Download raw body.
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] */
SEV-ES: vmm(4): Define GHCB for guest-host communication