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

Merge branch 'kvm-6.11-sev-attestation' into HEAD

The GHCB 2.0 specification defines 2 GHCB request types to allow SNP guests
to send encrypted messages/requests to firmware: SNP Guest Requests and SNP
Extended Guest Requests. These encrypted messages are used for things like
servicing attestation requests issued by the guest. Implementing support for
these is required to be fully GHCB-compliant.

For the most part, KVM only needs to handle forwarding these requests to
firmware (to be issued via the SNP_GUEST_REQUEST firmware command defined
in the SEV-SNP Firmware ABI), and then forwarding the encrypted response to
the guest.

However, in the case of SNP Extended Guest Requests, the host is also
able to provide the certificate data corresponding to the endorsement key
used by firmware to sign attestation report requests. This certificate data
is provided by userspace because:

1) It allows for different keys/key types to be used for each particular
guest with requiring any sort of KVM API to configure the certificate
table in advance on a per-guest basis.

2) It provides additional flexibility with how attestation requests might
be handled during live migration where the certificate data for
source/dest might be different.

3) It allows all synchronization between certificates and firmware/signing
key updates to be handled purely by userspace rather than requiring
some in-kernel mechanism to facilitate it. [1]

To support fetching certificate data from userspace, a new KVM exit type will
be needed to handle fetching the certificate from userspace. An attempt to
define a new KVM_EXIT_COCO/KVM_EXIT_COCO_REQ_CERTS exit type to handle this
was introduced in v1 of this patchset, but is still being discussed by
community, so for now this patchset only implements a stub version of SNP
Extended Guest Requests that does not provide certificate data, but is still
enough to provide compliance with the GHCB 2.0 spec.

+244 -65
+48
arch/x86/include/asm/sev.h
··· 119 119 unsigned int data_npages; 120 120 }; 121 121 122 + #define MAX_AUTHTAG_LEN 32 123 + 124 + /* See SNP spec SNP_GUEST_REQUEST section for the structure */ 125 + enum msg_type { 126 + SNP_MSG_TYPE_INVALID = 0, 127 + SNP_MSG_CPUID_REQ, 128 + SNP_MSG_CPUID_RSP, 129 + SNP_MSG_KEY_REQ, 130 + SNP_MSG_KEY_RSP, 131 + SNP_MSG_REPORT_REQ, 132 + SNP_MSG_REPORT_RSP, 133 + SNP_MSG_EXPORT_REQ, 134 + SNP_MSG_EXPORT_RSP, 135 + SNP_MSG_IMPORT_REQ, 136 + SNP_MSG_IMPORT_RSP, 137 + SNP_MSG_ABSORB_REQ, 138 + SNP_MSG_ABSORB_RSP, 139 + SNP_MSG_VMRK_REQ, 140 + SNP_MSG_VMRK_RSP, 141 + 142 + SNP_MSG_TYPE_MAX 143 + }; 144 + 145 + enum aead_algo { 146 + SNP_AEAD_INVALID, 147 + SNP_AEAD_AES_256_GCM, 148 + }; 149 + 150 + struct snp_guest_msg_hdr { 151 + u8 authtag[MAX_AUTHTAG_LEN]; 152 + u64 msg_seqno; 153 + u8 rsvd1[8]; 154 + u8 algo; 155 + u8 hdr_version; 156 + u16 hdr_sz; 157 + u8 msg_type; 158 + u8 msg_version; 159 + u16 msg_sz; 160 + u32 rsvd2; 161 + u8 msg_vmpck; 162 + u8 rsvd3[35]; 163 + } __packed; 164 + 165 + struct snp_guest_msg { 166 + struct snp_guest_msg_hdr hdr; 167 + u8 payload[4000]; 168 + } __packed; 169 + 122 170 struct sev_guest_platform_data { 123 171 u64 secrets_gpa; 124 172 };
+190
arch/x86/kvm/svm/sev.c
··· 19 19 #include <linux/misc_cgroup.h> 20 20 #include <linux/processor.h> 21 21 #include <linux/trace_events.h> 22 + #include <uapi/linux/sev-guest.h> 22 23 23 24 #include <asm/pkru.h> 24 25 #include <asm/trapnr.h> ··· 327 326 sev_decommission(handle); 328 327 } 329 328 329 + /* 330 + * This sets up bounce buffers/firmware pages to handle SNP Guest Request 331 + * messages (e.g. attestation requests). See "SNP Guest Request" in the GHCB 332 + * 2.0 specification for more details. 333 + * 334 + * Technically, when an SNP Guest Request is issued, the guest will provide its 335 + * own request/response pages, which could in theory be passed along directly 336 + * to firmware rather than using bounce pages. However, these pages would need 337 + * special care: 338 + * 339 + * - Both pages are from shared guest memory, so they need to be protected 340 + * from migration/etc. occurring while firmware reads/writes to them. At a 341 + * minimum, this requires elevating the ref counts and potentially needing 342 + * an explicit pinning of the memory. This places additional restrictions 343 + * on what type of memory backends userspace can use for shared guest 344 + * memory since there is some reliance on using refcounted pages. 345 + * 346 + * - The response page needs to be switched to Firmware-owned[1] state 347 + * before the firmware can write to it, which can lead to potential 348 + * host RMP #PFs if the guest is misbehaved and hands the host a 349 + * guest page that KVM might write to for other reasons (e.g. virtio 350 + * buffers/etc.). 351 + * 352 + * Both of these issues can be avoided completely by using separately-allocated 353 + * bounce pages for both the request/response pages and passing those to 354 + * firmware instead. So that's what is being set up here. 355 + * 356 + * Guest requests rely on message sequence numbers to ensure requests are 357 + * issued to firmware in the order the guest issues them, so concurrent guest 358 + * requests generally shouldn't happen. But a misbehaved guest could issue 359 + * concurrent guest requests in theory, so a mutex is used to serialize 360 + * access to the bounce buffers. 361 + * 362 + * [1] See the "Page States" section of the SEV-SNP Firmware ABI for more 363 + * details on Firmware-owned pages, along with "RMP and VMPL Access Checks" 364 + * in the APM for details on the related RMP restrictions. 365 + */ 366 + static int snp_guest_req_init(struct kvm *kvm) 367 + { 368 + struct kvm_sev_info *sev = to_kvm_sev_info(kvm); 369 + struct page *req_page; 370 + 371 + req_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); 372 + if (!req_page) 373 + return -ENOMEM; 374 + 375 + sev->guest_resp_buf = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); 376 + if (!sev->guest_resp_buf) { 377 + __free_page(req_page); 378 + return -EIO; 379 + } 380 + 381 + sev->guest_req_buf = page_address(req_page); 382 + mutex_init(&sev->guest_req_mutex); 383 + 384 + return 0; 385 + } 386 + 387 + static void snp_guest_req_cleanup(struct kvm *kvm) 388 + { 389 + struct kvm_sev_info *sev = to_kvm_sev_info(kvm); 390 + 391 + if (sev->guest_resp_buf) 392 + snp_free_firmware_page(sev->guest_resp_buf); 393 + 394 + if (sev->guest_req_buf) 395 + __free_page(virt_to_page(sev->guest_req_buf)); 396 + 397 + sev->guest_req_buf = NULL; 398 + sev->guest_resp_buf = NULL; 399 + } 400 + 330 401 static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, 331 402 struct kvm_sev_init *data, 332 403 unsigned long vm_type) ··· 447 374 init_args.probe = false; 448 375 ret = sev_platform_init(&init_args); 449 376 if (ret) 377 + goto e_free; 378 + 379 + /* This needs to happen after SEV/SNP firmware initialization. */ 380 + if (vm_type == KVM_X86_SNP_VM && snp_guest_req_init(kvm)) 450 381 goto e_free; 451 382 452 383 INIT_LIST_HEAD(&sev->regions_list); ··· 2927 2850 } 2928 2851 2929 2852 if (sev_snp_guest(kvm)) { 2853 + snp_guest_req_cleanup(kvm); 2854 + 2930 2855 /* 2931 2856 * Decomission handles unbinding of the ASID. If it fails for 2932 2857 * some unexpected reason, just leak the ASID. ··· 3398 3319 break; 3399 3320 case SVM_VMGEXIT_PSC: 3400 3321 if (!sev_snp_guest(vcpu->kvm) || !kvm_ghcb_sw_scratch_is_valid(svm)) 3322 + goto vmgexit_err; 3323 + break; 3324 + case SVM_VMGEXIT_GUEST_REQUEST: 3325 + case SVM_VMGEXIT_EXT_GUEST_REQUEST: 3326 + if (!sev_snp_guest(vcpu->kvm) || 3327 + !PAGE_ALIGNED(control->exit_info_1) || 3328 + !PAGE_ALIGNED(control->exit_info_2) || 3329 + control->exit_info_1 == control->exit_info_2) 3401 3330 goto vmgexit_err; 3402 3331 break; 3403 3332 default: ··· 4026 3939 return ret; 4027 3940 } 4028 3941 3942 + static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_gpa) 3943 + { 3944 + struct sev_data_snp_guest_request data = {0}; 3945 + struct kvm *kvm = svm->vcpu.kvm; 3946 + struct kvm_sev_info *sev = to_kvm_sev_info(kvm); 3947 + sev_ret_code fw_err = 0; 3948 + int ret; 3949 + 3950 + if (!sev_snp_guest(kvm)) 3951 + return -EINVAL; 3952 + 3953 + mutex_lock(&sev->guest_req_mutex); 3954 + 3955 + if (kvm_read_guest(kvm, req_gpa, sev->guest_req_buf, PAGE_SIZE)) { 3956 + ret = -EIO; 3957 + goto out_unlock; 3958 + } 3959 + 3960 + data.gctx_paddr = __psp_pa(sev->snp_context); 3961 + data.req_paddr = __psp_pa(sev->guest_req_buf); 3962 + data.res_paddr = __psp_pa(sev->guest_resp_buf); 3963 + 3964 + /* 3965 + * Firmware failures are propagated on to guest, but any other failure 3966 + * condition along the way should be reported to userspace. E.g. if 3967 + * the PSP is dead and commands are timing out. 3968 + */ 3969 + ret = sev_issue_cmd(kvm, SEV_CMD_SNP_GUEST_REQUEST, &data, &fw_err); 3970 + if (ret && !fw_err) 3971 + goto out_unlock; 3972 + 3973 + if (kvm_write_guest(kvm, resp_gpa, sev->guest_resp_buf, PAGE_SIZE)) { 3974 + ret = -EIO; 3975 + goto out_unlock; 3976 + } 3977 + 3978 + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, SNP_GUEST_ERR(0, fw_err)); 3979 + 3980 + ret = 1; /* resume guest */ 3981 + 3982 + out_unlock: 3983 + mutex_unlock(&sev->guest_req_mutex); 3984 + return ret; 3985 + } 3986 + 3987 + static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_gpa) 3988 + { 3989 + struct kvm *kvm = svm->vcpu.kvm; 3990 + u8 msg_type; 3991 + 3992 + if (!sev_snp_guest(kvm)) 3993 + return -EINVAL; 3994 + 3995 + if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type), 3996 + &msg_type, 1)) 3997 + return -EIO; 3998 + 3999 + /* 4000 + * As per GHCB spec, requests of type MSG_REPORT_REQ also allow for 4001 + * additional certificate data to be provided alongside the attestation 4002 + * report via the guest-provided data pages indicated by RAX/RBX. The 4003 + * certificate data is optional and requires additional KVM enablement 4004 + * to provide an interface for userspace to provide it, but KVM still 4005 + * needs to be able to handle extended guest requests either way. So 4006 + * provide a stub implementation that will always return an empty 4007 + * certificate table in the guest-provided data pages. 4008 + */ 4009 + if (msg_type == SNP_MSG_REPORT_REQ) { 4010 + struct kvm_vcpu *vcpu = &svm->vcpu; 4011 + u64 data_npages; 4012 + gpa_t data_gpa; 4013 + 4014 + if (!kvm_ghcb_rax_is_valid(svm) || !kvm_ghcb_rbx_is_valid(svm)) 4015 + goto request_invalid; 4016 + 4017 + data_gpa = vcpu->arch.regs[VCPU_REGS_RAX]; 4018 + data_npages = vcpu->arch.regs[VCPU_REGS_RBX]; 4019 + 4020 + if (!PAGE_ALIGNED(data_gpa)) 4021 + goto request_invalid; 4022 + 4023 + /* 4024 + * As per GHCB spec (see "SNP Extended Guest Request"), the 4025 + * certificate table is terminated by 24-bytes of zeroes. 4026 + */ 4027 + if (data_npages && kvm_clear_guest(kvm, data_gpa, 24)) 4028 + return -EIO; 4029 + } 4030 + 4031 + return snp_handle_guest_req(svm, req_gpa, resp_gpa); 4032 + 4033 + request_invalid: 4034 + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); 4035 + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_INPUT); 4036 + return 1; /* resume guest */ 4037 + } 4038 + 4029 4039 static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) 4030 4040 { 4031 4041 struct vmcb_control_area *control = &svm->vmcb->control; ··· 4396 4212 } 4397 4213 4398 4214 ret = 1; 4215 + break; 4216 + case SVM_VMGEXIT_GUEST_REQUEST: 4217 + ret = snp_handle_guest_req(svm, control->exit_info_1, control->exit_info_2); 4218 + break; 4219 + case SVM_VMGEXIT_EXT_GUEST_REQUEST: 4220 + ret = snp_handle_ext_guest_req(svm, control->exit_info_1, control->exit_info_2); 4399 4221 break; 4400 4222 case SVM_VMGEXIT_UNSUPPORTED_EVENT: 4401 4223 vcpu_unimpl(vcpu,
+3
arch/x86/kvm/svm/svm.h
··· 95 95 struct misc_cg *misc_cg; /* For misc cgroup accounting */ 96 96 atomic_t migration_in_progress; 97 97 void *snp_context; /* SNP guest context page */ 98 + void *guest_req_buf; /* Bounce buffer for SNP Guest Request input */ 99 + void *guest_resp_buf; /* Bounce buffer for SNP Guest Request output */ 100 + struct mutex guest_req_mutex; /* Must acquire before using bounce buffers */ 98 101 }; 99 102 100 103 struct kvm_svm {
-2
drivers/virt/coco/sev-guest/sev-guest.c
··· 29 29 #include <asm/svm.h> 30 30 #include <asm/sev.h> 31 31 32 - #include "sev-guest.h" 33 - 34 32 #define DEVICE_NAME "sev-guest" 35 33 #define AAD_LEN 48 36 34 #define MSG_HDR_VER 1
-63
drivers/virt/coco/sev-guest/sev-guest.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0-only */ 2 - /* 3 - * Copyright (C) 2021 Advanced Micro Devices, Inc. 4 - * 5 - * Author: Brijesh Singh <brijesh.singh@amd.com> 6 - * 7 - * SEV-SNP API spec is available at https://developer.amd.com/sev 8 - */ 9 - 10 - #ifndef __VIRT_SEVGUEST_H__ 11 - #define __VIRT_SEVGUEST_H__ 12 - 13 - #include <linux/types.h> 14 - 15 - #define MAX_AUTHTAG_LEN 32 16 - 17 - /* See SNP spec SNP_GUEST_REQUEST section for the structure */ 18 - enum msg_type { 19 - SNP_MSG_TYPE_INVALID = 0, 20 - SNP_MSG_CPUID_REQ, 21 - SNP_MSG_CPUID_RSP, 22 - SNP_MSG_KEY_REQ, 23 - SNP_MSG_KEY_RSP, 24 - SNP_MSG_REPORT_REQ, 25 - SNP_MSG_REPORT_RSP, 26 - SNP_MSG_EXPORT_REQ, 27 - SNP_MSG_EXPORT_RSP, 28 - SNP_MSG_IMPORT_REQ, 29 - SNP_MSG_IMPORT_RSP, 30 - SNP_MSG_ABSORB_REQ, 31 - SNP_MSG_ABSORB_RSP, 32 - SNP_MSG_VMRK_REQ, 33 - SNP_MSG_VMRK_RSP, 34 - 35 - SNP_MSG_TYPE_MAX 36 - }; 37 - 38 - enum aead_algo { 39 - SNP_AEAD_INVALID, 40 - SNP_AEAD_AES_256_GCM, 41 - }; 42 - 43 - struct snp_guest_msg_hdr { 44 - u8 authtag[MAX_AUTHTAG_LEN]; 45 - u64 msg_seqno; 46 - u8 rsvd1[8]; 47 - u8 algo; 48 - u8 hdr_version; 49 - u16 hdr_sz; 50 - u8 msg_type; 51 - u8 msg_version; 52 - u16 msg_sz; 53 - u32 rsvd2; 54 - u8 msg_vmpck; 55 - u8 rsvd3[35]; 56 - } __packed; 57 - 58 - struct snp_guest_msg { 59 - struct snp_guest_msg_hdr hdr; 60 - u8 payload[4000]; 61 - } __packed; 62 - 63 - #endif /* __VIRT_SEVGUEST_H__ */
+3
include/uapi/linux/sev-guest.h
··· 89 89 #define SNP_GUEST_FW_ERR_MASK GENMASK_ULL(31, 0) 90 90 #define SNP_GUEST_VMM_ERR_SHIFT 32 91 91 #define SNP_GUEST_VMM_ERR(x) (((u64)x) << SNP_GUEST_VMM_ERR_SHIFT) 92 + #define SNP_GUEST_FW_ERR(x) ((x) & SNP_GUEST_FW_ERR_MASK) 93 + #define SNP_GUEST_ERR(vmm_err, fw_err) (SNP_GUEST_VMM_ERR(vmm_err) | \ 94 + SNP_GUEST_FW_ERR(fw_err)) 92 95 93 96 #define SNP_GUEST_VMM_ERR_INVALID_LEN 1 94 97 #define SNP_GUEST_VMM_ERR_BUSY 2