Message ID | 20241021055156.2342564-4-nikunj@amd.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Add Secure TSC support for SNP guests | expand |
Le 21/10/2024 à 07:51, Nikunj A Dadhania a écrit : > Add support for Secure TSC in SNP-enabled guests. Secure TSC allows guests > to securely use RDTSC/RDTSCP instructions, ensuring that the parameters > used cannot be altered by the hypervisor once the guest is launched. > > Secure TSC-enabled guests need to query TSC information from the AMD > Security Processor. This communication channel is encrypted between the AMD > Security Processor and the guest, with the hypervisor acting merely as a > conduit to deliver the guest messages to the AMD Security Processor. Each > message is protected with AEAD (AES-256 GCM). Use a minimal AES GCM library > to encrypt and decrypt SNP guest messages for communication with the PSP. > > Use mem_encrypt_init() to fetch SNP TSC information from the AMD Security > Processor and initialize snp_tsc_scale and snp_tsc_offset. During secondary > CPU initialization, set the VMSA fields GUEST_TSC_SCALE (offset 2F0h) and > GUEST_TSC_OFFSET (offset 2F8h) with snp_tsc_scale and snp_tsc_offset, > respectively. > > Add confidential compute platform attribute CC_ATTR_GUEST_SNP_SECURE_TSC > that can be used by the guest to query whether the Secure TSC feature is > active. > > Since handle_guest_request() is common routine used by both the SEV guest > driver and Secure TSC code, move it to the SEV header file. > > Signed-off-by: Nikunj A Dadhania <nikunj@amd.com> > Tested-by: Peter Gonda <pgonda@google.com> > Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> > --- .. > +static int __init snp_get_tsc_info(void) > +{ > + static u8 buf[SNP_TSC_INFO_RESP_SZ + AUTHTAG_LEN]; > + struct snp_guest_request_ioctl rio; > + struct snp_tsc_info_resp tsc_resp; > + struct snp_tsc_info_req *tsc_req; > + struct snp_msg_desc *mdesc; > + struct snp_guest_req req; > + int rc; > + > + /* > + * The intermediate response buffer is used while decrypting the > + * response payload. Make sure that it has enough space to cover the > + * authtag. > + */ > + BUILD_BUG_ON(sizeof(buf) < (sizeof(tsc_resp) + AUTHTAG_LEN)); > + > + mdesc = snp_msg_alloc(); > + if (IS_ERR_OR_NULL(mdesc)) > + return -ENOMEM; > + > + rc = snp_msg_init(mdesc, snp_vmpl); > + if (rc) > + return rc; > + > + tsc_req = kzalloc(sizeof(struct snp_tsc_info_req), GFP_KERNEL); > + if (!tsc_req) > + return -ENOMEM; > + > + memset(&req, 0, sizeof(req)); > + memset(&rio, 0, sizeof(rio)); > + memset(buf, 0, sizeof(buf)); > + > + req.msg_version = MSG_HDR_VER; > + req.msg_type = SNP_MSG_TSC_INFO_REQ; > + req.vmpck_id = snp_vmpl; > + req.req_buf = tsc_req; > + req.req_sz = sizeof(*tsc_req); > + req.resp_buf = buf; > + req.resp_sz = sizeof(tsc_resp) + AUTHTAG_LEN; > + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST; > + > + rc = snp_send_guest_request(mdesc, &req, &rio); > + if (rc) > + goto err_req; > + > + memcpy(&tsc_resp, buf, sizeof(tsc_resp)); > + pr_debug("%s: response status %x scale %llx offset %llx factor %x\n", > + __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset, > + tsc_resp.tsc_factor); > + > + if (tsc_resp.status == 0) { > + snp_tsc_scale = tsc_resp.tsc_scale; > + snp_tsc_offset = tsc_resp.tsc_offset; > + } else { > + pr_err("Failed to get TSC info, response status %x\n", tsc_resp.status); > + rc = -EIO; > + } > + > +err_req: > + /* The response buffer contains the sensitive data, explicitly clear it. */ > + memzero_explicit(buf, sizeof(buf)); > + memzero_explicit(&tsc_resp, sizeof(tsc_resp)); > + memzero_explicit(&req, sizeof(req)); req does not seem to hold sensitive data. Is it needed, or maybe should it be tsc_req? > + > + return rc; > +} ... CJ
On 10/21/2024 1:42 PM, Christophe JAILLET wrote: > Le 21/10/2024 à 07:51, Nikunj A Dadhania a écrit : > > .. > >> +static int __init snp_get_tsc_info(void) >> +{ >> + static u8 buf[SNP_TSC_INFO_RESP_SZ + AUTHTAG_LEN]; >> + struct snp_guest_request_ioctl rio; >> + struct snp_tsc_info_resp tsc_resp; >> + struct snp_tsc_info_req *tsc_req; >> + struct snp_msg_desc *mdesc; >> + struct snp_guest_req req; >> + int rc; >> + >> + /* >> + * The intermediate response buffer is used while decrypting the >> + * response payload. Make sure that it has enough space to cover the >> + * authtag. >> + */ >> + BUILD_BUG_ON(sizeof(buf) < (sizeof(tsc_resp) + AUTHTAG_LEN)); >> + >> + mdesc = snp_msg_alloc(); >> + if (IS_ERR_OR_NULL(mdesc)) >> + return -ENOMEM; >> + >> + rc = snp_msg_init(mdesc, snp_vmpl); >> + if (rc) >> + return rc; >> + >> + tsc_req = kzalloc(sizeof(struct snp_tsc_info_req), GFP_KERNEL); >> + if (!tsc_req) >> + return -ENOMEM; >> + >> + memset(&req, 0, sizeof(req)); >> + memset(&rio, 0, sizeof(rio)); >> + memset(buf, 0, sizeof(buf)); >> + >> + req.msg_version = MSG_HDR_VER; >> + req.msg_type = SNP_MSG_TSC_INFO_REQ; >> + req.vmpck_id = snp_vmpl; >> + req.req_buf = tsc_req; >> + req.req_sz = sizeof(*tsc_req); >> + req.resp_buf = buf; >> + req.resp_sz = sizeof(tsc_resp) + AUTHTAG_LEN; >> + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST; >> + >> + rc = snp_send_guest_request(mdesc, &req, &rio); >> + if (rc) >> + goto err_req; >> + >> + memcpy(&tsc_resp, buf, sizeof(tsc_resp)); >> + pr_debug("%s: response status %x scale %llx offset %llx factor %x\n", >> + __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset, >> + tsc_resp.tsc_factor); >> + >> + if (tsc_resp.status == 0) { >> + snp_tsc_scale = tsc_resp.tsc_scale; >> + snp_tsc_offset = tsc_resp.tsc_offset; >> + } else { >> + pr_err("Failed to get TSC info, response status %x\n", tsc_resp.status); >> + rc = -EIO; >> + } >> + >> +err_req: >> + /* The response buffer contains the sensitive data, explicitly clear it. */ >> + memzero_explicit(buf, sizeof(buf)); >> + memzero_explicit(&tsc_resp, sizeof(tsc_resp)); >> + memzero_explicit(&req, sizeof(req)); > > req does not seem to hold sensitive data. That is correct, I will remove that. > Is it needed, or maybe should it be tsc_req? No, and tsc_req is zeroed by the caller and is not updated by AMD security processor. Regards Nikunj
diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h index 50f5666938c0..6ef92432a5ce 100644 --- a/arch/x86/include/asm/sev-common.h +++ b/arch/x86/include/asm/sev-common.h @@ -206,6 +206,7 @@ struct snp_psc_desc { #define GHCB_TERM_NO_SVSM 7 /* SVSM is not advertised in the secrets page */ #define GHCB_TERM_SVSM_VMPL0 8 /* SVSM is present but has set VMPL to 0 */ #define GHCB_TERM_SVSM_CAA 9 /* SVSM is present but CAA is not page aligned */ +#define GHCB_TERM_SECURE_TSC 10 /* Secure TSC initialization failed */ #define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK) diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index d6ad5f6b1ff3..9169b18eeb78 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -146,6 +146,9 @@ enum msg_type { SNP_MSG_VMRK_REQ, SNP_MSG_VMRK_RSP, + SNP_MSG_TSC_INFO_REQ = 17, + SNP_MSG_TSC_INFO_RSP, + SNP_MSG_TYPE_MAX }; @@ -174,6 +177,22 @@ struct snp_guest_msg { u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)]; } __packed; +#define SNP_TSC_INFO_REQ_SZ 128 +#define SNP_TSC_INFO_RESP_SZ 128 + +struct snp_tsc_info_req { + u8 rsvd[SNP_TSC_INFO_REQ_SZ]; +} __packed; + +struct snp_tsc_info_resp { + u32 status; + u32 rsvd1; + u64 tsc_scale; + u64 tsc_offset; + u32 tsc_factor; + u8 rsvd2[100]; +} __packed; + struct snp_guest_req { void *req_buf; size_t req_sz; @@ -497,6 +516,27 @@ static inline void snp_msg_cleanup(struct snp_msg_desc *mdesc) int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, struct snp_guest_request_ioctl *rio); +static inline int handle_guest_request(struct snp_msg_desc *mdesc, u64 exit_code, + struct snp_guest_request_ioctl *rio, u8 type, + void *req_buf, size_t req_sz, void *resp_buf, + u32 resp_sz) +{ + struct snp_guest_req req = { + .msg_version = rio->msg_version, + .msg_type = type, + .vmpck_id = mdesc->vmpck_id, + .req_buf = req_buf, + .req_sz = req_sz, + .resp_buf = resp_buf, + .resp_sz = resp_sz, + .exit_code = exit_code, + }; + + return snp_send_guest_request(mdesc, &req, rio); +} + +void __init snp_secure_tsc_prepare(void); + #else /* !CONFIG_AMD_MEM_ENCRYPT */ #define snp_vmpl 0 @@ -538,6 +578,12 @@ static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; } static inline void snp_msg_cleanup(struct snp_msg_desc *mdesc) { } static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, struct snp_guest_request_ioctl *rio) { return -ENODEV; } +static inline int handle_guest_request(struct snp_msg_desc *mdesc, u64 exit_code, + struct snp_guest_request_ioctl *rio, u8 type, + void *req_buf, size_t req_sz, void *resp_buf, + u32 resp_sz) { return -ENODEV; } + +static inline void __init snp_secure_tsc_prepare(void) { } #endif /* CONFIG_AMD_MEM_ENCRYPT */ diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index 2b59b9951c90..92e18798f197 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -417,7 +417,9 @@ struct sev_es_save_area { u8 reserved_0x298[80]; u32 pkru; u32 tsc_aux; - u8 reserved_0x2f0[24]; + u64 tsc_scale; + u64 tsc_offset; + u8 reserved_0x300[8]; u64 rcx; u64 rdx; u64 rbx; @@ -564,7 +566,7 @@ static inline void __unused_size_checks(void) BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0); BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248); BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298); - BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0); + BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300); BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320); BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380); BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0); diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h index caa4b4430634..cb7103dc124f 100644 --- a/include/linux/cc_platform.h +++ b/include/linux/cc_platform.h @@ -88,6 +88,14 @@ enum cc_attr { * enabled to run SEV-SNP guests. */ CC_ATTR_HOST_SEV_SNP, + + /** + * @CC_ATTR_GUEST_SNP_SECURE_TSC: SNP Secure TSC is active. + * + * The platform/OS is running as a guest/virtual machine and actively + * using AMD SEV-SNP Secure TSC feature. + */ + CC_ATTR_GUEST_SNP_SECURE_TSC, }; #ifdef CONFIG_ARCH_HAS_CC_PLATFORM diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c index 0f81f70aca82..5b9a358a3254 100644 --- a/arch/x86/coco/core.c +++ b/arch/x86/coco/core.c @@ -100,6 +100,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) case CC_ATTR_HOST_SEV_SNP: return cc_flags.host_sev_snp; + case CC_ATTR_GUEST_SNP_SECURE_TSC: + return sev_status & MSR_AMD64_SNP_SECURE_TSC; + default: return false; } diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index c96b742789c5..965209067f03 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -98,6 +98,10 @@ static u64 secrets_pa __ro_after_init; static struct snp_msg_desc *snp_mdesc; +/* Secure TSC values read using TSC_INFO SNP Guest request */ +static u64 snp_tsc_scale __ro_after_init; +static u64 snp_tsc_offset __ro_after_init; + /* #VC handler runtime per-CPU data */ struct sev_es_runtime_data { struct ghcb ghcb_page; @@ -1148,6 +1152,12 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip) vmsa->vmpl = snp_vmpl; vmsa->sev_features = sev_status >> 2; + /* Set Secure TSC parameters */ + if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) { + vmsa->tsc_scale = snp_tsc_scale; + vmsa->tsc_offset = snp_tsc_offset; + } + /* Switch the page over to a VMSA page now that it is initialized */ ret = snp_set_vmsa(vmsa, caa, apic_id, true); if (ret) { @@ -2942,3 +2952,84 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req return 0; } EXPORT_SYMBOL_GPL(snp_send_guest_request); + +static int __init snp_get_tsc_info(void) +{ + static u8 buf[SNP_TSC_INFO_RESP_SZ + AUTHTAG_LEN]; + struct snp_guest_request_ioctl rio; + struct snp_tsc_info_resp tsc_resp; + struct snp_tsc_info_req *tsc_req; + struct snp_msg_desc *mdesc; + struct snp_guest_req req; + int rc; + + /* + * The intermediate response buffer is used while decrypting the + * response payload. Make sure that it has enough space to cover the + * authtag. + */ + BUILD_BUG_ON(sizeof(buf) < (sizeof(tsc_resp) + AUTHTAG_LEN)); + + mdesc = snp_msg_alloc(); + if (IS_ERR_OR_NULL(mdesc)) + return -ENOMEM; + + rc = snp_msg_init(mdesc, snp_vmpl); + if (rc) + return rc; + + tsc_req = kzalloc(sizeof(struct snp_tsc_info_req), GFP_KERNEL); + if (!tsc_req) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + memset(&rio, 0, sizeof(rio)); + memset(buf, 0, sizeof(buf)); + + req.msg_version = MSG_HDR_VER; + req.msg_type = SNP_MSG_TSC_INFO_REQ; + req.vmpck_id = snp_vmpl; + req.req_buf = tsc_req; + req.req_sz = sizeof(*tsc_req); + req.resp_buf = buf; + req.resp_sz = sizeof(tsc_resp) + AUTHTAG_LEN; + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST; + + rc = snp_send_guest_request(mdesc, &req, &rio); + if (rc) + goto err_req; + + memcpy(&tsc_resp, buf, sizeof(tsc_resp)); + pr_debug("%s: response status %x scale %llx offset %llx factor %x\n", + __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset, + tsc_resp.tsc_factor); + + if (tsc_resp.status == 0) { + snp_tsc_scale = tsc_resp.tsc_scale; + snp_tsc_offset = tsc_resp.tsc_offset; + } else { + pr_err("Failed to get TSC info, response status %x\n", tsc_resp.status); + rc = -EIO; + } + +err_req: + /* The response buffer contains the sensitive data, explicitly clear it. */ + memzero_explicit(buf, sizeof(buf)); + memzero_explicit(&tsc_resp, sizeof(tsc_resp)); + memzero_explicit(&req, sizeof(req)); + + return rc; +} + +void __init snp_secure_tsc_prepare(void) +{ + if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) + return; + + if (snp_get_tsc_info()) { + pr_alert("Unable to retrieve Secure TSC info from ASP\n"); + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECURE_TSC); + } + + pr_debug("SecureTSC enabled"); +} diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c index 0a120d85d7bb..996ca27f0b72 100644 --- a/arch/x86/mm/mem_encrypt.c +++ b/arch/x86/mm/mem_encrypt.c @@ -94,6 +94,10 @@ void __init mem_encrypt_init(void) /* Call into SWIOTLB to update the SWIOTLB DMA buffers */ swiotlb_update_mem_attributes(); + /* Initialize SNP Secure TSC */ + if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) + snp_secure_tsc_prepare(); + print_mem_encrypt_feature_info(); }