Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/sev: Add Secure TSC support for SNP guests

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).

[ bp: Zap a stray newline over amd_cc_platform_has() while at it,
simplify CC_ATTR_GUEST_SNP_SECURE_TSC check ]

Signed-off-by: Nikunj A Dadhania <nikunj@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20250106124633.1418972-6-nikunj@amd.com

authored by

Nikunj A Dadhania and committed by
Borislav Petkov (AMD)
85b60ca9 1e0b23b5

+146 -3
+3 -1
arch/x86/coco/core.c
··· 65 65 * up under SME the trampoline area cannot be encrypted, whereas under SEV 66 66 * the trampoline area must be encrypted. 67 67 */ 68 - 69 68 static bool noinstr amd_cc_platform_has(enum cc_attr attr) 70 69 { 71 70 #ifdef CONFIG_AMD_MEM_ENCRYPT ··· 95 96 96 97 case CC_ATTR_GUEST_SEV_SNP: 97 98 return sev_status & MSR_AMD64_SEV_SNP_ENABLED; 99 + 100 + case CC_ATTR_GUEST_SNP_SECURE_TSC: 101 + return sev_status & MSR_AMD64_SNP_SECURE_TSC; 98 102 99 103 case CC_ATTR_HOST_SEV_SNP: 100 104 return cc_flags.host_sev_snp;
+107
arch/x86/coco/sev/core.c
··· 96 96 /* Secrets page physical address from the CC blob */ 97 97 static u64 secrets_pa __ro_after_init; 98 98 99 + /* 100 + * For Secure TSC guests, the BSP fetches TSC_INFO using SNP guest messaging and 101 + * initializes snp_tsc_scale and snp_tsc_offset. These values are replicated 102 + * across the APs VMSA fields (TSC_SCALE and TSC_OFFSET). 103 + */ 104 + static u64 snp_tsc_scale __ro_after_init; 105 + static u64 snp_tsc_offset __ro_after_init; 106 + 99 107 /* #VC handler runtime per-CPU data */ 100 108 struct sev_es_runtime_data { 101 109 struct ghcb ghcb_page; ··· 1284 1276 */ 1285 1277 vmsa->vmpl = snp_vmpl; 1286 1278 vmsa->sev_features = sev_status >> 2; 1279 + 1280 + /* Populate AP's TSC scale/offset to get accurate TSC values. */ 1281 + if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) { 1282 + vmsa->tsc_scale = snp_tsc_scale; 1283 + vmsa->tsc_offset = snp_tsc_offset; 1284 + } 1287 1285 1288 1286 /* Switch the page over to a VMSA page now that it is initialized */ 1289 1287 ret = snp_set_vmsa(vmsa, caa, apic_id, true); ··· 3140 3126 return 0; 3141 3127 } 3142 3128 EXPORT_SYMBOL_GPL(snp_send_guest_request); 3129 + 3130 + static int __init snp_get_tsc_info(void) 3131 + { 3132 + struct snp_guest_request_ioctl *rio; 3133 + struct snp_tsc_info_resp *tsc_resp; 3134 + struct snp_tsc_info_req *tsc_req; 3135 + struct snp_msg_desc *mdesc; 3136 + struct snp_guest_req *req; 3137 + int rc = -ENOMEM; 3138 + 3139 + tsc_req = kzalloc(sizeof(*tsc_req), GFP_KERNEL); 3140 + if (!tsc_req) 3141 + return rc; 3142 + 3143 + /* 3144 + * The intermediate response buffer is used while decrypting the 3145 + * response payload. Make sure that it has enough space to cover 3146 + * the authtag. 3147 + */ 3148 + tsc_resp = kzalloc(sizeof(*tsc_resp) + AUTHTAG_LEN, GFP_KERNEL); 3149 + if (!tsc_resp) 3150 + goto e_free_tsc_req; 3151 + 3152 + req = kzalloc(sizeof(*req), GFP_KERNEL); 3153 + if (!req) 3154 + goto e_free_tsc_resp; 3155 + 3156 + rio = kzalloc(sizeof(*rio), GFP_KERNEL); 3157 + if (!rio) 3158 + goto e_free_req; 3159 + 3160 + mdesc = snp_msg_alloc(); 3161 + if (IS_ERR_OR_NULL(mdesc)) 3162 + goto e_free_rio; 3163 + 3164 + rc = snp_msg_init(mdesc, snp_vmpl); 3165 + if (rc) 3166 + goto e_free_mdesc; 3167 + 3168 + req->msg_version = MSG_HDR_VER; 3169 + req->msg_type = SNP_MSG_TSC_INFO_REQ; 3170 + req->vmpck_id = snp_vmpl; 3171 + req->req_buf = tsc_req; 3172 + req->req_sz = sizeof(*tsc_req); 3173 + req->resp_buf = (void *)tsc_resp; 3174 + req->resp_sz = sizeof(*tsc_resp) + AUTHTAG_LEN; 3175 + req->exit_code = SVM_VMGEXIT_GUEST_REQUEST; 3176 + 3177 + rc = snp_send_guest_request(mdesc, req, rio); 3178 + if (rc) 3179 + goto e_request; 3180 + 3181 + pr_debug("%s: response status 0x%x scale 0x%llx offset 0x%llx factor 0x%x\n", 3182 + __func__, tsc_resp->status, tsc_resp->tsc_scale, tsc_resp->tsc_offset, 3183 + tsc_resp->tsc_factor); 3184 + 3185 + if (!tsc_resp->status) { 3186 + snp_tsc_scale = tsc_resp->tsc_scale; 3187 + snp_tsc_offset = tsc_resp->tsc_offset; 3188 + } else { 3189 + pr_err("Failed to get TSC info, response status 0x%x\n", tsc_resp->status); 3190 + rc = -EIO; 3191 + } 3192 + 3193 + e_request: 3194 + /* The response buffer contains sensitive data, explicitly clear it. */ 3195 + memzero_explicit(tsc_resp, sizeof(*tsc_resp) + AUTHTAG_LEN); 3196 + e_free_mdesc: 3197 + snp_msg_free(mdesc); 3198 + e_free_rio: 3199 + kfree(rio); 3200 + e_free_req: 3201 + kfree(req); 3202 + e_free_tsc_resp: 3203 + kfree(tsc_resp); 3204 + e_free_tsc_req: 3205 + kfree(tsc_req); 3206 + 3207 + return rc; 3208 + } 3209 + 3210 + void __init snp_secure_tsc_prepare(void) 3211 + { 3212 + if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) 3213 + return; 3214 + 3215 + if (snp_get_tsc_info()) { 3216 + pr_alert("Unable to retrieve Secure TSC info from ASP\n"); 3217 + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECURE_TSC); 3218 + } 3219 + 3220 + pr_debug("SecureTSC enabled"); 3221 + }
+1
arch/x86/include/asm/sev-common.h
··· 206 206 #define GHCB_TERM_NO_SVSM 7 /* SVSM is not advertised in the secrets page */ 207 207 #define GHCB_TERM_SVSM_VMPL0 8 /* SVSM is present but has set VMPL to 0 */ 208 208 #define GHCB_TERM_SVSM_CAA 9 /* SVSM is present but CAA is not page aligned */ 209 + #define GHCB_TERM_SECURE_TSC 10 /* Secure TSC initialization failed */ 209 210 210 211 #define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK) 211 212
+21
arch/x86/include/asm/sev.h
··· 146 146 SNP_MSG_VMRK_REQ, 147 147 SNP_MSG_VMRK_RSP, 148 148 149 + SNP_MSG_TSC_INFO_REQ = 17, 150 + SNP_MSG_TSC_INFO_RSP, 151 + 149 152 SNP_MSG_TYPE_MAX 150 153 }; 151 154 ··· 175 172 struct snp_guest_msg { 176 173 struct snp_guest_msg_hdr hdr; 177 174 u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)]; 175 + } __packed; 176 + 177 + #define SNP_TSC_INFO_REQ_SZ 128 178 + 179 + struct snp_tsc_info_req { 180 + u8 rsvd[SNP_TSC_INFO_REQ_SZ]; 181 + } __packed; 182 + 183 + struct snp_tsc_info_resp { 184 + u32 status; 185 + u32 rsvd1; 186 + u64 tsc_scale; 187 + u64 tsc_offset; 188 + u32 tsc_factor; 189 + u8 rsvd2[100]; 178 190 } __packed; 179 191 180 192 struct snp_guest_req { ··· 481 463 int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, 482 464 struct snp_guest_request_ioctl *rio); 483 465 466 + void __init snp_secure_tsc_prepare(void); 467 + 484 468 #else /* !CONFIG_AMD_MEM_ENCRYPT */ 485 469 486 470 #define snp_vmpl 0 ··· 523 503 static inline void snp_msg_free(struct snp_msg_desc *mdesc) { } 524 504 static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, 525 505 struct snp_guest_request_ioctl *rio) { return -ENODEV; } 506 + static inline void __init snp_secure_tsc_prepare(void) { } 526 507 527 508 #endif /* CONFIG_AMD_MEM_ENCRYPT */ 528 509
+4 -2
arch/x86/include/asm/svm.h
··· 417 417 u8 reserved_0x298[80]; 418 418 u32 pkru; 419 419 u32 tsc_aux; 420 - u8 reserved_0x2f0[24]; 420 + u64 tsc_scale; 421 + u64 tsc_offset; 422 + u8 reserved_0x300[8]; 421 423 u64 rcx; 422 424 u64 rdx; 423 425 u64 rbx; ··· 566 564 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0); 567 565 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248); 568 566 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298); 569 - BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0); 567 + BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300); 570 568 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320); 571 569 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380); 572 570 BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
+2
arch/x86/mm/mem_encrypt.c
··· 94 94 /* Call into SWIOTLB to update the SWIOTLB DMA buffers */ 95 95 swiotlb_update_mem_attributes(); 96 96 97 + snp_secure_tsc_prepare(); 98 + 97 99 print_mem_encrypt_feature_info(); 98 100 } 99 101
+8
include/linux/cc_platform.h
··· 82 82 CC_ATTR_GUEST_SEV_SNP, 83 83 84 84 /** 85 + * @CC_ATTR_GUEST_SNP_SECURE_TSC: SNP Secure TSC is active. 86 + * 87 + * The platform/OS is running as a guest/virtual machine and actively 88 + * using AMD SEV-SNP Secure TSC feature. 89 + */ 90 + CC_ATTR_GUEST_SNP_SECURE_TSC, 91 + 92 + /** 85 93 * @CC_ATTR_HOST_SEV_SNP: AMD SNP enabled on the host. 86 94 * 87 95 * The host kernel is running with the necessary features