Message ID | 20250106124633.1418972-6-nikunj@amd.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Add Secure TSC support for SNP guests | expand |
On Mon, Jan 06, 2025 at 06:16:25PM +0530, Nikunj A Dadhania wrote: > diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c > index 0f81f70aca82..715c2c09582f 100644 > --- a/arch/x86/coco/core.c > +++ b/arch/x86/coco/core.c > @@ -97,6 +97,10 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) > case CC_ATTR_GUEST_SEV_SNP: > return sev_status & MSR_AMD64_SEV_SNP_ENABLED; > > + case CC_ATTR_GUEST_SNP_SECURE_TSC: > + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && This is new here? > + (sev_status & MSR_AMD64_SNP_SECURE_TSC); > +
On 1/7/2025 4:12 PM, Borislav Petkov wrote: > On Mon, Jan 06, 2025 at 06:16:25PM +0530, Nikunj A Dadhania wrote: >> diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c >> index 0f81f70aca82..715c2c09582f 100644 >> --- a/arch/x86/coco/core.c >> +++ b/arch/x86/coco/core.c >> @@ -97,6 +97,10 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) >> case CC_ATTR_GUEST_SEV_SNP: >> return sev_status & MSR_AMD64_SEV_SNP_ENABLED; >> >> + case CC_ATTR_GUEST_SNP_SECURE_TSC: >> + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && > > This is new here? Yes, this was suggested by Tom here [1] >> + (sev_status & MSR_AMD64_SNP_SECURE_TSC); >> + > Regards Nikunj 1. https://lore.kernel.org/all/bad7406d-4a0b-d871-cc02-3ffb9e9185ba@amd.com
On Tue, Jan 07, 2025 at 05:13:03PM +0530, Nikunj A. Dadhania wrote: > >> + case CC_ATTR_GUEST_SNP_SECURE_TSC: > >> + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && > > > > This is new here? > > Yes, this was suggested by Tom here [1] Either of you care to explain why this is needed? > >> + (sev_status & MSR_AMD64_SNP_SECURE_TSC); I would strongly assume that whatever sets MSR_AMD64_SNP_SECURE_TSC will have checked/set MSR_AMD64_SEV_SNP_ENABLED already.
On 1/7/25 06:37, Borislav Petkov wrote: > On Tue, Jan 07, 2025 at 05:13:03PM +0530, Nikunj A. Dadhania wrote: >>>> + case CC_ATTR_GUEST_SNP_SECURE_TSC: >>>> + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && >>> >>> This is new here? >> >> Yes, this was suggested by Tom here [1] > > Either of you care to explain why this is needed? > >>>> + (sev_status & MSR_AMD64_SNP_SECURE_TSC); > > I would strongly assume that whatever sets MSR_AMD64_SNP_SECURE_TSC will have > checked/set MSR_AMD64_SEV_SNP_ENABLED already. Yes, but from a readability point of view this makes it perfectly clear that Secure TSC is only for SNP guests. It's not on a fast path, so I don't think it hurts anything by having the extra check. But if you prefer the previous check, I'm ok with that. Thanks, Tom >
On Tue, Jan 07, 2025 at 12:53:26PM -0600, Tom Lendacky wrote: > Yes, but from a readability point of view this makes it perfectly clear > that Secure TSC is only for SNP guests. That would mean that we need to check SNP with every SNP-specific feature which would be just silly. And all SNP feature bits have "SNP" in the same so...
On 1/6/25 06:46, Nikunj A Dadhania wrote: > 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). > > Signed-off-by: Nikunj A Dadhania <nikunj@amd.com> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> Although if you have to re-spin, it might make sense to use the __free() support to reduce the number of gotos and labels in snp_get_tsc_info(). You could also use kfree_sensitive() to automatically do the memzero_explicit(), too. > --- > arch/x86/include/asm/sev-common.h | 1 + > arch/x86/include/asm/sev.h | 21 ++++++ > arch/x86/include/asm/svm.h | 6 +- > include/linux/cc_platform.h | 8 +++ > arch/x86/coco/core.c | 4 ++ > arch/x86/coco/sev/core.c | 107 ++++++++++++++++++++++++++++++ > arch/x86/mm/mem_encrypt.c | 2 + > 7 files changed, 147 insertions(+), 2 deletions(-) > > 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 0937ac7a96db..bdcdaac4df1c 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,21 @@ struct snp_guest_msg { > u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)]; > } __packed; > > +#define SNP_TSC_INFO_REQ_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; > @@ -463,6 +481,8 @@ void snp_msg_free(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); > > +void __init snp_secure_tsc_prepare(void); > + > #else /* !CONFIG_AMD_MEM_ENCRYPT */ > > #define snp_vmpl 0 > @@ -503,6 +523,7 @@ static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; } > static inline void snp_msg_free(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 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..0bf7d33a1048 100644 > --- a/include/linux/cc_platform.h > +++ b/include/linux/cc_platform.h > @@ -81,6 +81,14 @@ enum cc_attr { > */ > CC_ATTR_GUEST_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, > + > /** > * @CC_ATTR_HOST_SEV_SNP: AMD SNP enabled on the host. > * > diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c > index 0f81f70aca82..715c2c09582f 100644 > --- a/arch/x86/coco/core.c > +++ b/arch/x86/coco/core.c > @@ -97,6 +97,10 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) > case CC_ATTR_GUEST_SEV_SNP: > return sev_status & MSR_AMD64_SEV_SNP_ENABLED; > > + case CC_ATTR_GUEST_SNP_SECURE_TSC: > + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && > + (sev_status & MSR_AMD64_SNP_SECURE_TSC); > + > case CC_ATTR_HOST_SEV_SNP: > return cc_flags.host_sev_snp; > > diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c > index feb145df6bf7..00a0ac3baab7 100644 > --- a/arch/x86/coco/sev/core.c > +++ b/arch/x86/coco/sev/core.c > @@ -96,6 +96,14 @@ static u64 sev_hv_features __ro_after_init; > /* Secrets page physical address from the CC blob */ > static u64 secrets_pa __ro_after_init; > > +/* > + * For Secure TSC guests, the BSP fetches TSC_INFO using SNP guest messaging and > + * initializes snp_tsc_scale and snp_tsc_offset. These values are replicated > + * across the APs VMSA fields (TSC_SCALE and TSC_OFFSET). > + */ > +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; > @@ -1272,6 +1280,12 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip) > vmsa->vmpl = snp_vmpl; > vmsa->sev_features = sev_status >> 2; > > + /* Populate AP's TSC scale/offset to get accurate TSC values. */ > + 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) { > @@ -3121,3 +3135,96 @@ 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) > +{ > + 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 = -ENOMEM; > + > + tsc_req = kzalloc(sizeof(*tsc_req), GFP_KERNEL); > + if (!tsc_req) > + return rc; > + > + /* > + * The intermediate response buffer is used while decrypting the > + * response payload. Make sure that it has enough space to cover > + * the authtag. > + */ > + tsc_resp = kzalloc(sizeof(*tsc_resp) + AUTHTAG_LEN, GFP_KERNEL); > + if (!tsc_resp) > + goto e_free_tsc_req; > + > + req = kzalloc(sizeof(*req), GFP_KERNEL); > + if (!req) > + goto e_free_tsc_resp; > + > + rio = kzalloc(sizeof(*rio), GFP_KERNEL); > + if (!rio) > + goto e_free_req; > + > + mdesc = snp_msg_alloc(); > + if (IS_ERR_OR_NULL(mdesc)) > + goto e_free_rio; > + > + rc = snp_msg_init(mdesc, snp_vmpl); > + if (rc) > + goto e_free_mdesc; > + > + 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 = (void *)tsc_resp; > + 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 e_request; > + > + pr_debug("%s: response status 0x%x scale 0x%llx offset 0x%llx factor 0x%x\n", > + __func__, tsc_resp->status, tsc_resp->tsc_scale, tsc_resp->tsc_offset, > + tsc_resp->tsc_factor); > + > + if (!tsc_resp->status) { > + snp_tsc_scale = tsc_resp->tsc_scale; > + snp_tsc_offset = tsc_resp->tsc_offset; > + } else { > + pr_err("Failed to get TSC info, response status 0x%x\n", tsc_resp->status); > + rc = -EIO; > + } > + > +e_request: > + /* The response buffer contains sensitive data, explicitly clear it. */ > + memzero_explicit(tsc_resp, sizeof(*tsc_resp) + AUTHTAG_LEN); > +e_free_mdesc: > + snp_msg_free(mdesc); > +e_free_rio: > + kfree(rio); > +e_free_req: > + kfree(req); > + e_free_tsc_resp: > + kfree(tsc_resp); > +e_free_tsc_req: > + kfree(tsc_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..95bae74fdab2 100644 > --- a/arch/x86/mm/mem_encrypt.c > +++ b/arch/x86/mm/mem_encrypt.c > @@ -94,6 +94,8 @@ void __init mem_encrypt_init(void) > /* Call into SWIOTLB to update the SWIOTLB DMA buffers */ > swiotlb_update_mem_attributes(); > > + snp_secure_tsc_prepare(); > + > print_mem_encrypt_feature_info(); > } >
On Tue, Jan 07, 2025 at 01:46:49PM -0600, Tom Lendacky wrote: > Although if you have to re-spin, it might make sense to use the __free() > support to reduce the number of gotos and labels in snp_get_tsc_info(). > You could also use kfree_sensitive() to automatically do the > memzero_explicit(), too. Let's keep that for future cleanups, pls, when there's time. Thx.
On 1/8/2025 12:48 AM, Borislav Petkov wrote: > On Tue, Jan 07, 2025 at 12:53:26PM -0600, Tom Lendacky wrote: >> Yes, but from a readability point of view this makes it perfectly clear >> that Secure TSC is only for SNP guests. > > That would mean that we need to check SNP with every SNP-specific feature > which would be just silly. And all SNP feature bits have "SNP" in the same > so... > Right, here is the updated diff: diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c index 715c2c09582f..d6647953590b 100644 --- a/arch/x86/coco/core.c +++ b/arch/x86/coco/core.c @@ -98,8 +98,7 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) return sev_status & MSR_AMD64_SEV_SNP_ENABLED; case CC_ATTR_GUEST_SNP_SECURE_TSC: - return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && - (sev_status & MSR_AMD64_SNP_SECURE_TSC); + return sev_status & MSR_AMD64_SNP_SECURE_TSC; case CC_ATTR_HOST_SEV_SNP: return cc_flags.host_sev_snp; diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index 00a0ac3baab7..763cfeb65b2f 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -3218,7 +3218,8 @@ static int __init snp_get_tsc_info(void) void __init snp_secure_tsc_prepare(void) { - if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) || + !cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) return; if (snp_get_tsc_info()) {
On Wed, Jan 08, 2025 at 01:17:11PM +0530, Nikunj A. Dadhania wrote: > diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c > index 00a0ac3baab7..763cfeb65b2f 100644 > --- a/arch/x86/coco/sev/core.c > +++ b/arch/x86/coco/sev/core.c > @@ -3218,7 +3218,8 @@ static int __init snp_get_tsc_info(void) > > void __init snp_secure_tsc_prepare(void) > { > - if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) > + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) || > + !cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) So how is moving the CC_ATTR_GUEST_SEV_SNP check here make any sense? I simply zapped the MSR_AMD64_SEV_SNP_ENABLED check above locally.
On 1/8/2025 1:35 PM, Borislav Petkov wrote: > On Wed, Jan 08, 2025 at 01:17:11PM +0530, Nikunj A. Dadhania wrote: >> diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c >> index 00a0ac3baab7..763cfeb65b2f 100644 >> --- a/arch/x86/coco/sev/core.c >> +++ b/arch/x86/coco/sev/core.c >> @@ -3218,7 +3218,8 @@ static int __init snp_get_tsc_info(void) >> >> void __init snp_secure_tsc_prepare(void) >> { >> - if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) >> + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) || >> + !cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) > > So how is moving the CC_ATTR_GUEST_SEV_SNP check here make any sense? In the comment that you gave here[1], I understood that this check has to be pushed to snp_secure_tsc_prepare(). diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c index 996ca27f0b72..95bae74fdab2 100644 --- a/arch/x86/mm/mem_encrypt.c +++ b/arch/x86/mm/mem_encrypt.c @@ -94,9 +94,7 @@ 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(); + snp_secure_tsc_prepare(); print_mem_encrypt_feature_info(); } > I simply zapped the MSR_AMD64_SEV_SNP_ENABLED check above locally. For SEV and SEV-ES this SecureTSC bit should not be set, I think we should be fine without MSR_AMD64_SEV_SNP_ENABLED check. Regards Nikunj 1. https://lore.kernel.org/kvm/20241111103434.GAZzHduouKi4LBwbg8@fat_crate.local/
On Wed, Jan 08, 2025 at 02:07:07PM +0530, Nikunj A. Dadhania wrote: > >> void __init snp_secure_tsc_prepare(void) > >> { > >> - if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) > >> + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) || > >> + !cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) > > > > So how is moving the CC_ATTR_GUEST_SEV_SNP check here make any sense? > > In the comment that you gave here[1], I understood that this check has > to be pushed to snp_secure_tsc_prepare(). The check should be pushed there but you should not check CC_ATTR_GUEST_SEV_SNP *and* CC_ATTR_GUEST_SNP_SECURE_TSC if all you wanna do is check if you're running a STSC guest. That was the whole point.
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 0937ac7a96db..bdcdaac4df1c 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,21 @@ struct snp_guest_msg { u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)]; } __packed; +#define SNP_TSC_INFO_REQ_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; @@ -463,6 +481,8 @@ void snp_msg_free(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); +void __init snp_secure_tsc_prepare(void); + #else /* !CONFIG_AMD_MEM_ENCRYPT */ #define snp_vmpl 0 @@ -503,6 +523,7 @@ static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; } static inline void snp_msg_free(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 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..0bf7d33a1048 100644 --- a/include/linux/cc_platform.h +++ b/include/linux/cc_platform.h @@ -81,6 +81,14 @@ enum cc_attr { */ CC_ATTR_GUEST_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, + /** * @CC_ATTR_HOST_SEV_SNP: AMD SNP enabled on the host. * diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c index 0f81f70aca82..715c2c09582f 100644 --- a/arch/x86/coco/core.c +++ b/arch/x86/coco/core.c @@ -97,6 +97,10 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr) case CC_ATTR_GUEST_SEV_SNP: return sev_status & MSR_AMD64_SEV_SNP_ENABLED; + case CC_ATTR_GUEST_SNP_SECURE_TSC: + return (sev_status & MSR_AMD64_SEV_SNP_ENABLED) && + (sev_status & MSR_AMD64_SNP_SECURE_TSC); + case CC_ATTR_HOST_SEV_SNP: return cc_flags.host_sev_snp; diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index feb145df6bf7..00a0ac3baab7 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -96,6 +96,14 @@ static u64 sev_hv_features __ro_after_init; /* Secrets page physical address from the CC blob */ static u64 secrets_pa __ro_after_init; +/* + * For Secure TSC guests, the BSP fetches TSC_INFO using SNP guest messaging and + * initializes snp_tsc_scale and snp_tsc_offset. These values are replicated + * across the APs VMSA fields (TSC_SCALE and TSC_OFFSET). + */ +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; @@ -1272,6 +1280,12 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip) vmsa->vmpl = snp_vmpl; vmsa->sev_features = sev_status >> 2; + /* Populate AP's TSC scale/offset to get accurate TSC values. */ + 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) { @@ -3121,3 +3135,96 @@ 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) +{ + 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 = -ENOMEM; + + tsc_req = kzalloc(sizeof(*tsc_req), GFP_KERNEL); + if (!tsc_req) + return rc; + + /* + * The intermediate response buffer is used while decrypting the + * response payload. Make sure that it has enough space to cover + * the authtag. + */ + tsc_resp = kzalloc(sizeof(*tsc_resp) + AUTHTAG_LEN, GFP_KERNEL); + if (!tsc_resp) + goto e_free_tsc_req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + goto e_free_tsc_resp; + + rio = kzalloc(sizeof(*rio), GFP_KERNEL); + if (!rio) + goto e_free_req; + + mdesc = snp_msg_alloc(); + if (IS_ERR_OR_NULL(mdesc)) + goto e_free_rio; + + rc = snp_msg_init(mdesc, snp_vmpl); + if (rc) + goto e_free_mdesc; + + 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 = (void *)tsc_resp; + 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 e_request; + + pr_debug("%s: response status 0x%x scale 0x%llx offset 0x%llx factor 0x%x\n", + __func__, tsc_resp->status, tsc_resp->tsc_scale, tsc_resp->tsc_offset, + tsc_resp->tsc_factor); + + if (!tsc_resp->status) { + snp_tsc_scale = tsc_resp->tsc_scale; + snp_tsc_offset = tsc_resp->tsc_offset; + } else { + pr_err("Failed to get TSC info, response status 0x%x\n", tsc_resp->status); + rc = -EIO; + } + +e_request: + /* The response buffer contains sensitive data, explicitly clear it. */ + memzero_explicit(tsc_resp, sizeof(*tsc_resp) + AUTHTAG_LEN); +e_free_mdesc: + snp_msg_free(mdesc); +e_free_rio: + kfree(rio); +e_free_req: + kfree(req); + e_free_tsc_resp: + kfree(tsc_resp); +e_free_tsc_req: + kfree(tsc_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..95bae74fdab2 100644 --- a/arch/x86/mm/mem_encrypt.c +++ b/arch/x86/mm/mem_encrypt.c @@ -94,6 +94,8 @@ void __init mem_encrypt_init(void) /* Call into SWIOTLB to update the SWIOTLB DMA buffers */ swiotlb_update_mem_attributes(); + snp_secure_tsc_prepare(); + print_mem_encrypt_feature_info(); }
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). Signed-off-by: Nikunj A Dadhania <nikunj@amd.com> --- arch/x86/include/asm/sev-common.h | 1 + arch/x86/include/asm/sev.h | 21 ++++++ arch/x86/include/asm/svm.h | 6 +- include/linux/cc_platform.h | 8 +++ arch/x86/coco/core.c | 4 ++ arch/x86/coco/sev/core.c | 107 ++++++++++++++++++++++++++++++ arch/x86/mm/mem_encrypt.c | 2 + 7 files changed, 147 insertions(+), 2 deletions(-)