Index | Thread | Search

From:
Hans-Jörg Höxer <hshoexer@genua.de>
Subject:
SEV-ES: vmm(4): Define GHCB for guest-host communication
To:
<tech@openbsd.org>
Date:
Tue, 20 May 2025 11:31:33 +0200

Download raw body.

Thread
  • Hans-Jörg Höxer:

    SEV-ES: vmm(4): Define GHCB for guest-host communication

Hi,

this change prepares the communication of SEV-ES enabled guests with
vmm(4):
    
    SEV-ES enabled guests will communicate with vmm(4) using a non-encrypted
    page, the Guest Host Communication Block (GHCB).  The GHCB layout
    is specified by AMD.  It includes all GPR, FPU and extended state
    and a buffer for other data.  Which members are valid are specified
    in a bitmap.  For now we will only use the GPRs A, B, C and D and
    the exit related members.
    
    Also provide functions for copying values to/from the GHCB.  For
    GPRs we respect the given operand size.  For example, when emulating
    an inb instruction we only want to copy the response from vmm(4)
    into %al (lowest byte) and not %rax (quad word).

Take care,
HJ.

-----------------------------------------------------------------------
commit 0c842c7fb6161823a5e8bba42639a65507c12555
Author: Hans-Joerg Hoexer <hshoexer@genua.de>
Date:   Wed Jan 22 11:43:03 2025 +0100

    vmm(4): Define GHCB for guest-host communication
    
    SEV-ES enabled guests will communicate with vmm(4) using a non-encrypted
    page, the Guest Host Communication Block (GHCB).  The GHCB layout
    is specified by AMD.  It includes all GPR, FPU and extended state
    and a buffer for other data.  Which members are valid are specified
    in a bitmap.  For now we will only use the GPRs A, B, C and D and
    the exit related members.
    
    Also provide functions for copying values to/from the GHCB.  For
    GPRs we respect the given operand size.  For example, when emulating
    an inb instruction we only want to copy the response from vmm(4)
    into %al (lowest byte) and not %rax (quad word).

diff --git a/sys/arch/amd64/amd64/ghcb.c b/sys/arch/amd64/amd64/ghcb.c
new file mode 100644
index 00000000000..227c58ea821
--- /dev/null
+++ b/sys/arch/amd64/amd64/ghcb.c
@@ -0,0 +1,225 @@
+/*	$OpenBSD:$	*/
+
+/*
+ * Copyright (c) 2024, 2025 Hans-Joerg Hoexer <hshoexer@genua.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+
+#include <machine/frame.h>
+#include <machine/ghcb.h>
+
+
+/* Mask for adjusting GPR sizes. */
+const uint64_t ghcb_sz_masks[] = {
+    0x00000000000000ffULL, 0x000000000000ffffULL,
+    0x00000000ffffffffULL, 0xffffffffffffffffULL
+};
+
+/*
+ * ghcb_clear
+ *
+ * Clear GHCB by setting to all 0.
+ * Used by host and guest.
+ */
+void
+ghcb_clear(struct ghcb_sa *ghcb)
+{
+	memset(ghcb, 0, sizeof(*ghcb));
+}
+
+/*
+ * ghcb_valbm_set
+ *
+ * Set the quad word position of qword in the GHCB valid bitmap.
+ * Used by host and guest.
+ */
+int
+ghcb_valbm_set(uint8_t *bm, int qword)
+{
+	if (qword > GHCB_MAX)
+		return (1);
+
+	bm[GHCB_IDX(qword)] |= (1 << GHCB_BIT(qword));
+
+	return (0);
+}
+
+/*
+ * ghcb_valbm_isset
+ *
+ * Indicate wether a specific quad word is set or not.
+ * Used by host and guest.
+ */
+int
+ghcb_valbm_isset(uint8_t *bm, int qword)
+{
+	if (qword > GHCB_MAX)
+		return (0);
+
+	return (bm[GHCB_IDX(qword)] & (1 << GHCB_BIT(qword)));
+}
+
+/*
+ * ghcb_valid
+ *
+ * To provide valid information, the exitcode, exitinfo1 and exitinfo2
+ * must be set in the GHCB.  Verify by checking valid_bitmap.
+ * Used by host only.
+ */
+int
+ghcb_valid(struct ghcb_sa *ghcb)
+{
+	uint8_t	*bm = ghcb->valid_bitmap;
+
+	return (ghcb_valbm_isset(bm, GHCB_SW_EXITCODE) &&
+	    ghcb_valbm_isset(bm, GHCB_SW_EXITINFO1) &&
+	    ghcb_valbm_isset(bm, GHCB_SW_EXITINFO2));
+}
+
+
+/*
+ * ghcb_verify_bm
+ *
+ * To be verified positive, the given expected bitmap must be at
+ * least a subset of the provided valid bitmap.
+ * Used by host and guest.
+ */
+int
+ghcb_verify_bm(uint8_t *valid_bm, uint8_t *expected_bm)
+{
+	return ((ghcb_valbm_isset(expected_bm, GHCB_RAX) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_RAX)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_RBX) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_RBX)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_RCX) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_RCX)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_RDX) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_RDX)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_SW_EXITCODE) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_SW_EXITCODE)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_SW_EXITINFO1) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_SW_EXITINFO1)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_SW_EXITINFO2) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_SW_EXITINFO2)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_SW_SCRATCH) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_SW_SCRATCH)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_XCR0) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_XCR0)) ||
+	    (ghcb_valbm_isset(expected_bm, GHCB_XSS) &&
+	    !ghcb_valbm_isset(valid_bm, GHCB_XSS)));
+}
+
+/*
+ * ghcb_sync_val
+ *
+ * Record a value for synchronization to GHCB in the valid bitmap.
+ * For GPRs A to D also record size.
+ */
+void
+ghcb_sync_val(int type, int size, struct ghcb_sync *gs)
+{
+	if (size > GHCB_SZ64)
+		panic("invalide size: %d", size);
+
+	switch (type) {
+	case GHCB_RAX:
+		gs->sz_a = size;
+		break;
+	case GHCB_RBX:
+		gs->sz_b = size;
+		break;
+	case GHCB_RCX:
+		gs->sz_c = size;
+		break;
+	case GHCB_RDX:
+		gs->sz_d = size;
+		break;
+	case GHCB_SW_EXITCODE:
+	case GHCB_SW_EXITINFO1:
+	case GHCB_SW_EXITINFO2:
+		break;
+
+	default:
+		panic("invalid type: %d", type);
+		/* NOTREACHED */
+	}
+
+	ghcb_valbm_set(gs->valid_bitmap, type);
+}
+
+/*
+ * ghcb_sync_out
+ *
+ * Copy values provided in trap frame (GPRs) and additional arguments
+ * according to valid bitmap to GHCB.  For GPRs respect given size.
+ * Used by guest only.
+ */
+void
+ghcb_sync_out(struct trapframe *frame, uint64_t exitcode, uint64_t exitinfo1,
+    uint64_t exitinfo2, struct ghcb_sa *ghcb, struct ghcb_sync *gsout)
+{
+	ghcb_clear(ghcb);
+
+	memcpy(ghcb->valid_bitmap, gsout->valid_bitmap,
+	    sizeof(ghcb->valid_bitmap));
+
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_RAX))
+		ghcb->v_rax = frame->tf_rax & ghcb_sz_masks[gsout->sz_a];
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_RBX))
+		ghcb->v_rbx = frame->tf_rbx & ghcb_sz_masks[gsout->sz_b];
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_RCX))
+		ghcb->v_rcx = frame->tf_rcx & ghcb_sz_masks[gsout->sz_c];
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_RDX))
+		ghcb->v_rdx = frame->tf_rdx & ghcb_sz_masks[gsout->sz_d];
+
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_SW_EXITCODE))
+		ghcb->v_sw_exitcode = exitcode;
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_SW_EXITINFO1))
+		ghcb->v_sw_exitinfo1 = exitinfo1;
+	if (ghcb_valbm_isset(gsout->valid_bitmap, GHCB_SW_EXITINFO2))
+		ghcb->v_sw_exitinfo2 = exitinfo2;
+}
+
+/*
+ * ghcb_sync_in
+ *
+ * Copy GPRs back to stack frame.  Respect provided GPR size.
+ * Used by guest only.
+ */
+void
+ghcb_sync_in(struct trapframe *frame, struct ghcb_sa *ghcb,
+    struct ghcb_sync *gsin)
+{
+	if (ghcb_valbm_isset(gsin->valid_bitmap, GHCB_RAX)) {
+		frame->tf_rax &= ~ghcb_sz_masks[gsin->sz_a];
+		frame->tf_rax |= (ghcb->v_rax & ghcb_sz_masks[gsin->sz_a]);
+	}
+	if (ghcb_valbm_isset(gsin->valid_bitmap, GHCB_RBX)) {
+		frame->tf_rbx &= ~ghcb_sz_masks[gsin->sz_b];
+		frame->tf_rbx |= (ghcb->v_rbx & ghcb_sz_masks[gsin->sz_b]);
+	}
+	if (ghcb_valbm_isset(gsin->valid_bitmap, GHCB_RCX)) {
+		frame->tf_rcx &= ~ghcb_sz_masks[gsin->sz_c];
+		frame->tf_rcx |= (ghcb->v_rcx & ghcb_sz_masks[gsin->sz_c]);
+	}
+	if (ghcb_valbm_isset(gsin->valid_bitmap, GHCB_RDX)) {
+		frame->tf_rdx &= ~ghcb_sz_masks[gsin->sz_d];
+		frame->tf_rdx |= (ghcb->v_rdx & ghcb_sz_masks[gsin->sz_d]);
+	}
+
+	ghcb_clear(ghcb);
+}
diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64
index 0f285927448..496753d9f8b 100644
--- a/sys/arch/amd64/conf/files.amd64
+++ b/sys/arch/amd64/conf/files.amd64
@@ -28,6 +28,7 @@ file	arch/amd64/amd64/vm_machdep.c
 file	arch/amd64/amd64/fpu.c
 file	arch/amd64/amd64/i8259.c
 file	arch/amd64/amd64/cacheinfo.c
+file	arch/amd64/amd64/ghcb.c
 file	arch/amd64/amd64/vector.S
 file	arch/amd64/amd64/copy.S
 file	arch/amd64/amd64/spl.S
diff --git a/sys/arch/amd64/include/ghcb.h b/sys/arch/amd64/include/ghcb.h
new file mode 100644
index 00000000000..802bf7f015e
--- /dev/null
+++ b/sys/arch/amd64/include/ghcb.h
@@ -0,0 +1,115 @@
+/*	$OpenBSD:$	*/
+
+/*
+ * Copyright (c) 2024, 2025 Hans-Joerg Hoexer <hshoexer@genua.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MACHINE_GHCB_H_
+#define _MACHINE_GHCB_H_
+
+#include <machine/frame.h>
+
+#define GHCB_OFFSET(m)			((m) / 8)
+#define GHCB_IDX(m)			(GHCB_OFFSET((m)) / 8)
+#define GHCB_BIT(m)			(GHCB_OFFSET((m)) % 8)
+
+#define GHCB_XSS			0x140
+#define GHCB_RAX			0x1F8
+#define GHCB_RBX			0x318
+#define GHCB_RCX			0x308
+#define GHCB_RDX			0x310
+#define GHCB_SW_EXITCODE		0x390
+#define GHCB_SW_EXITINFO1		0x398
+#define GHCB_SW_EXITINFO2		0x3A0
+#define GHCB_SW_SCRATCH			0x3A8
+#define GHCB_XCR0			0x3E8
+
+#define GHCB_MAX			0xFFF
+
+struct ghcb_sa {
+		uint8_t			v_pad0[0xcb];		/* 000h-0CAh */
+		uint8_t			v_cpl;			/* 0CBh */
+		uint8_t			v_pad1[0x74];		/* 0CCh-13Fh */
+		uint64_t		v_xss;			/* 140h */
+		uint8_t			v_pad2[0x18];		/* 148h-15Fh */
+		uint64_t		v_dr7;			/* 160h */
+		uint8_t			v_pad3[0x10];		/* 168h-177h */
+		uint64_t		v_rip;			/* 178h */
+		uint8_t			v_pad4[0x58];		/* 180h-1D7h */
+		uint64_t		v_rsp;			/* 1D8h */
+		uint8_t			v_pad5[0x18];		/* 1E0h-1F7h */
+		uint64_t		v_rax;			/* 1F8h */
+		uint8_t			v_pad6[0x108];		/* 200h-307h */
+		uint64_t		v_rcx;			/* 308h */
+		uint64_t		v_rdx;			/* 310h */
+		uint64_t		v_rbx;			/* 318h */
+		uint8_t			v_pad7[0x8];		/* 320h-327h */
+		uint64_t		v_rbp;			/* 328h */
+		uint64_t		v_rsi;			/* 330h */
+		uint64_t		v_rdi;			/* 338h */
+		uint64_t		v_r8;			/* 340h */
+		uint64_t		v_r9;			/* 348h */
+		uint64_t		v_r10;			/* 350h */
+		uint64_t		v_r11;			/* 358h */
+		uint64_t		v_r12;			/* 360h */
+		uint64_t		v_r13;			/* 368h */
+		uint64_t		v_r14;			/* 370h */
+		uint64_t		v_r15;			/* 378h */
+		uint8_t			v_pad8[0x10];		/* 380h-38Fh */
+		uint64_t		v_sw_exitcode;		/* 390h */
+		uint64_t		v_sw_exitinfo1;		/* 398h */
+		uint64_t		v_sw_exitinfo2;		/* 3a0h */
+		uint64_t		v_sw_scratch; 		/* 3a8h */
+		uint8_t			v_pad9[0x38];		/* 3B0h-3E7h */
+		uint64_t		v_xcr0;			/* 3E8h */
+#define GHCB_VB_SZ	0x10
+		uint8_t			valid_bitmap[GHCB_VB_SZ];
+								/* 3F0h-3FFh */
+		uint64_t		v_x87_state_gpa;	/* 400h */
+		uint8_t			v_pad10[0x3f8];		/* 408h-7FFh */
+		uint8_t			v_sharedbuf[0x7f0];	/* 800h-FEFh */
+		uint8_t			v_pad11[0xa];		/* FF0h-FF9h */
+		uint16_t		v_ghcb_proto_version;	/* FFAh-FFBh */
+		uint32_t		v_ghcb_usage;		/* FFCh-FFFh */
+};
+
+
+#define GHCB_SZ8	0
+#define GHCB_SZ16	1
+#define GHCB_SZ32	2
+#define GHCB_SZ64	3
+
+struct ghcb_sync {
+		uint8_t			valid_bitmap[GHCB_VB_SZ];
+
+		int			sz_a;
+		int			sz_b;
+		int			sz_c;
+		int			sz_d;
+};
+
+
+void	ghcb_clear(struct ghcb_sa *);
+int	ghcb_valbm_set(uint8_t *, int);
+int	ghcb_valbm_isset(uint8_t *, int);
+int	ghcb_verify_bm(uint8_t *, uint8_t *);
+int	ghcb_valid(struct ghcb_sa *);
+
+void	ghcb_sync_val(int, int, struct ghcb_sync *);
+void	ghcb_sync_out(struct trapframe *, uint64_t, uint64_t, uint64_t,
+	    struct ghcb_sa *, struct ghcb_sync *);
+void	ghcb_sync_in(struct trapframe *, struct ghcb_sa *, struct ghcb_sync *);
+
+#endif /* !_MACHINE_GHCB_H_ */
diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h
index 03d86639476..d7cdce1c148 100644
--- a/sys/arch/amd64/include/vmmvar.h
+++ b/sys/arch/amd64/include/vmmvar.h
@@ -661,7 +661,7 @@ struct vmcb {
 			uint64_t	v_exitintinfo;		/* 088h */
 			uint64_t	v_np_enable;		/* 090h */
 			uint64_t	v_avic_apic_bar;	/* 098h */
-			uint64_t	v_pad4;			/* 0A0h */
+			uint64_t	v_ghcb_gpa;		/* 0A0h */
 			uint64_t	v_eventinj;		/* 0A8h */
 			uint64_t	v_n_cr3;		/* 0B0h */
 			uint64_t	v_lbr_virt_enable;	/* 0B8h */
@@ -1014,6 +1014,8 @@ struct vcpu {
 	paddr_t vc_svm_hsa_pa;
 	vaddr_t vc_svm_vmsa_va;
 	paddr_t vc_svm_vmsa_pa;
+	vaddr_t vc_svm_ghcb_va;
+	paddr_t vc_svm_ghcb_pa;
 	vaddr_t vc_svm_ioio_va;
 	paddr_t vc_svm_ioio_pa;
 	int vc_sev;				/* [I] */