From: Hans-Jörg Höxer Subject: SEV-ES: vmm(4): GHCB MSR protocol for CPUID To: Date: Tue, 20 May 2025 14:05:28 +0200 Hi, my last two diffs for vmm(4) and SEV-ES: When a SEV-ES guest is in locore, it will not be able yet to use a GHCB to communicate with vmm(4). Therefore, AMD specifies a "GHCB MSR protocol" that uses the lowere 12 bits of the GHCB MSR to request services from vmm(4). Guest writes to the GHCB MSR will show up in the v_ghcb_gpa member of the VMCB and are thus accesible by vmm(4). The response of vmm(4) can be provided by writing it to the VMCB. In locore a SEV-ES guest will need to request CPUID from vmm(4) using the GHCB MSR protocol. This diff provides this service. Take care, HJ. --------------------------------------------------------------------------- commit 8278f4051b90ee94e19db25d019100a736ee7317 Author: Hans-Joerg Hoexer Date: Mon Nov 18 13:59:19 2024 +0100 vmm(4): GHCB MSR protocol for CPUID When a SEV-ES guest is in locore, it will not be able yet to use a GHCB to communicate with vmm(4). Therefore, AMD specifies a "GHCB MSR protocol" that uses the lowere 12 bits of the GHCB MSR to request services from vmm(4). Guest writes to the GHCB MSR will show up in the v_ghcb_gpa member of the VMCB and are thus accesible by vmm(4). The response of vmm(4) can be provided by writing it to the VMCB. In locore a SEV-ES guest will need to request CPUID from vmm(4) using the GHCB MSR protocol. This diff provides this service. diff --git a/sys/arch/amd64/amd64/vmm_machdep.c b/sys/arch/amd64/amd64/vmm_machdep.c index 9049bc6340d..2e90b7340f2 100644 --- a/sys/arch/amd64/amd64/vmm_machdep.c +++ b/sys/arch/amd64/amd64/vmm_machdep.c @@ -4514,13 +4514,67 @@ svm_handle_gexit(struct vcpu *vcpu) struct vm *vm = vcpu->vc_parent; struct ghcb_sa *ghcb; paddr_t ghcb_gpa, ghcb_hpa; + uint32_t req, resp; + uint64_t result; int syncout, error = 0; - if (vcpu->vc_svm_ghcb_va == 0) { + if (vcpu->vc_svm_ghcb_va == 0 && (vmcb->v_ghcb_gpa & ~PG_FRAME) == 0 && + (vmcb->v_ghcb_gpa & PG_FRAME) != 0) { + /* + * Guest provides a valid guest physcial address + * for GHCB and it is not set yet -> assign it. + * + * We only accept a GHCB once; we decline re-definition. + */ ghcb_gpa = vmcb->v_ghcb_gpa & PG_FRAME; if (!pmap_extract(vm->vm_map->pmap, ghcb_gpa, &ghcb_hpa)) return (EINVAL); vcpu->vc_svm_ghcb_va = (vaddr_t)PMAP_DIRECT_MAP(ghcb_hpa); + } else if ((vmcb->v_ghcb_gpa & ~PG_FRAME) != 0) { + /* + * Low bits in use, thus must be a MSR protocol + * request. + */ + req = (vmcb->v_ghcb_gpa & 0xffffffff); + + /* we only support cpuid */ + if ((req & ~PG_FRAME) != MSR_PROTO_CPUID_REQ) + return (EINVAL); + + /* Emulate CPUID */ + vmcb->v_exitcode = SVM_VMEXIT_CPUID; + vmcb->v_rax = vmcb->v_ghcb_gpa >> 32; + vcpu->vc_gueststate.vg_rax = 0; + vcpu->vc_gueststate.vg_rbx = 0; + vcpu->vc_gueststate.vg_rcx = 0; + vcpu->vc_gueststate.vg_rdx = 0; + error = vmm_handle_cpuid(vcpu); + if (error) + goto out; + + switch (req >> 30) { + case 0: /* eax: emulate cpuid and return eax */ + result = vmcb->v_rax; + break; + case 1: /* return ebx */ + result = vcpu->vc_gueststate.vg_rbx; + break; + case 2: /* return ecx */ + result = vcpu->vc_gueststate.vg_rcx; + break; + case 3: /* return edx */ + result = vcpu->vc_gueststate.vg_rdx; + break; + default: + DPRINTF("%s: unknown request 0x%x\n", __func__, req); + return (EINVAL); + } + + /* build response */ + resp = MSR_PROTO_CPUID_RESP | (req & 0xc0000000); + vmcb->v_ghcb_gpa = (result << 32) | resp; + + return (0); } /* Verify GHCB and synchronize guest state information. */ diff --git a/sys/arch/amd64/include/ghcb.h b/sys/arch/amd64/include/ghcb.h index 802bf7f015e..954e1fa3e3b 100644 --- a/sys/arch/amd64/include/ghcb.h +++ b/sys/arch/amd64/include/ghcb.h @@ -101,6 +101,12 @@ struct ghcb_sync { }; +/* Definitions used with the MSR protocol */ +#define MSR_PROTO_CPUID_REQ 0x4 +#define MSR_PROTO_CPUID_RESP 0x5 +#define MSR_PROTO_TERMINATE 0x100 + + void ghcb_clear(struct ghcb_sa *); int ghcb_valbm_set(uint8_t *, int); int ghcb_valbm_isset(uint8_t *, int);