From: Hans-Jörg Höxer Subject: SEV-ES: vmm(4): Define GHCB for guest-host communication To: Date: Tue, 20 May 2025 11:31:33 +0200 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 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 + * + * 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 +#include + +#include +#include + + +/* 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 + * + * 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 + +#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] */