From: Hans-Jörg Höxer Subject: SEV-SNP: Register GHCB with hypervisor and handle #VC for unvalidated pages 4/4 To: Date: Tue, 11 Aug 2026 11:37:50 +0200 Hi, this is patch 4/4. With this OpenBSD can run as SEV-SNP enabled guest on qemu/KVM. With SNP enabled, the GHCB page need to be registered with the hypervisor through the GHCB MSR protocol. When we are SNP enabled guest and touch a page that has not been validated by us the CPU delivers a #VC with error code SVM_VMEXIT_PAGE_NOT_VALIDATED. In that case we panic. While there, clean up GHCB MSR protocol definitions. ok? comments? Take care, HJ. --- sys/arch/amd64/amd64/locore0.S | 2 +- sys/arch/amd64/amd64/machdep.c | 16 ++++++++++++++-- sys/arch/amd64/amd64/trap.c | 8 ++++++++ sys/arch/amd64/amd64/vmm_machdep.c | 2 +- sys/arch/amd64/include/ghcb.h | 21 +++++++++++++-------- sys/arch/amd64/include/vmmvar.h | 1 + 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/sys/arch/amd64/amd64/locore0.S b/sys/arch/amd64/amd64/locore0.S index f8ba97805ce..1bc71845b19 100644 --- a/sys/arch/amd64/amd64/locore0.S +++ b/sys/arch/amd64/amd64/locore0.S @@ -872,7 +872,7 @@ locore_vc_trap32: iret .Lterminate32: - movl $MSR_PROTO_TERMINATE, %eax + movl $MSR_PROTO_TERMINATION_REQ, %eax movl $MSR_SEV_GHCB, %ecx wrmsr rep vmmcall diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index ad44bea3162..bf6aed8f6c1 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -1342,6 +1342,7 @@ void cpu_init_early_vctrap(paddr_t addr) { struct region_descriptor region; + uint64_t request, resp; extern void Xvctrap_early(void); @@ -1361,8 +1362,19 @@ cpu_init_early_vctrap(paddr_t addr) GSEL(GCODE_SEL, SEL_KPL)); cpu_init_idt(); - /* Tell the hypervisor about our GHCB. */ - ghcb_paddr = addr; + /* For SEV-SNP we have to register GHCB. */ + if (ISSET(cpu_sev_guestmode, SEV_STAT_SNP_ACTIVE)) { + request = (addr & PG_FRAME) | MSR_PROTO_REGISTER_GHCB_PA_REQ; + wrmsr(MSR_SEV_GHCB, request); + vmgexit(); + resp = rdmsr(MSR_SEV_GHCB); + if (((resp & ~PG_FRAME) != MSR_PROTO_REGISTER_GHCB_PA_RESP) || + ((resp & PG_FRAME) != (addr & PG_FRAME))) + panic("failed to register GHCB"); + } + + /* Tell vmm(4) about our GHCB. */ + ghcb_paddr = addr & PG_FRAME; ghcb_vaddr = addr + KERNBASE; memset((void *)ghcb_vaddr, 0, 2 * PAGE_SIZE); wrmsr(MSR_SEV_GHCB, ghcb_paddr); diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c index ccd0315186c..23a247accda 100644 --- a/sys/arch/amd64/amd64/trap.c +++ b/sys/arch/amd64/amd64/trap.c @@ -463,6 +463,14 @@ vctrap(struct trapframe *frame, int user, int *sig, int *code) else frame->tf_rip += 2; break; + case SVM_VMEXIT_PAGE_NOT_VALIDATED: { + paddr_t pa; + vaddr_t va = rcr2(); + pmap_extract(pmap_kernel(), va, &pa); + panic("page not validated access at 0x%llx on 0x%lx/0x%lx", + frame->tf_rip, va, pa); + /* NOTREACHED */ + } default: panic("invalid exit code 0x%llx", ghcb_regs.exitcode); } diff --git a/sys/arch/amd64/amd64/vmm_machdep.c b/sys/arch/amd64/amd64/vmm_machdep.c index 787b65e29e1..850010abb90 100644 --- a/sys/arch/amd64/amd64/vmm_machdep.c +++ b/sys/arch/amd64/amd64/vmm_machdep.c @@ -4523,7 +4523,7 @@ svm_handle_vmgexit(struct vcpu *vcpu) req = (vmcb->v_ghcb_gpa & 0xffffffff); /* We only support cpuid and terminate. */ - if ((req & ~PG_FRAME) == MSR_PROTO_TERMINATE) { + if ((req & ~PG_FRAME) == MSR_PROTO_TERMINATION_REQ) { DPRINTF("%s: guest requests termination\n", __func__); return (1); } else if ((req & ~PG_FRAME) != MSR_PROTO_CPUID_REQ) diff --git a/sys/arch/amd64/include/ghcb.h b/sys/arch/amd64/include/ghcb.h index a821abdfb23..86d602363da 100644 --- a/sys/arch/amd64/include/ghcb.h +++ b/sys/arch/amd64/include/ghcb.h @@ -42,6 +42,19 @@ #define GHCB_MAX 0xFFF +#endif /* !_LOCORE */ + +/* Definitions used with the MSR protocol */ +#define MSR_PROTO_CPUID_REQ 0x4 +#define MSR_PROTO_CPUID_RESP 0x5 +#define MSR_PROTO_PREFERRED_GHCB_PA_REQ 0x10 +#define MSR_PROTO_PREFERRED_GHCB_PA_RESP 0x11 +#define MSR_PROTO_REGISTER_GHCB_PA_REQ 0x12 +#define MSR_PROTO_REGISTER_GHCB_PA_RESP 0x13 +#define MSR_PROTO_TERMINATION_REQ 0x100 + +#ifndef _LOCORE + struct ghcb_sa { uint8_t v_pad0[0xcb]; /* 000h-0CAh */ uint8_t v_cpl; /* 0CBh */ @@ -102,14 +115,6 @@ struct ghcb_sync { int sz_c; int sz_d; }; -#endif /* !_LOCORE */ - -/* Definitions used with the MSR protocol */ -#define MSR_PROTO_CPUID_REQ 0x4 -#define MSR_PROTO_CPUID_RESP 0x5 -#define MSR_PROTO_TERMINATE 0x100 - -#ifndef _LOCORE struct ghcb_psc { struct { diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h index 215ee20988f..72ed597bf88 100644 --- a/sys/arch/amd64/include/vmmvar.h +++ b/sys/arch/amd64/include/vmmvar.h @@ -264,6 +264,7 @@ #define SVM_AVIC_INCOMPLETE_IPI 0x401 #define SVM_AVIC_NOACCEL 0x402 #define SVM_VMEXIT_VMGEXIT 0x403 +#define SVM_VMEXIT_PAGE_NOT_VALIDATED 0x404 #define SVM_VMEXIT_INVALID -1 /* -- 2.53.0