Index | Thread | Search

From:
Hans-Jörg Höxer <hshoexer@genua.de>
Subject:
SEV-ES: vmm(4): GHCB MSR protocol for CPUID
To:
<tech@openbsd.org>
Date:
Tue, 20 May 2025 14:05:28 +0200

Download raw body.

Thread
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 <hshoexer@genua.de>
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);