From: hshoexer Subject: SEV-SNP: Register GHCB and handle #VC for unvalidated To: tech@openbsd.org Date: Thu, 24 Sep 2026 18:17:22 +0200 Hi, that's the final diff with required "glue" to enable SEV-SNP guest support. With SNP enabled, the GHCB page needs to be registered with the hypervisor through the GHCB MSR protocol. When we are an 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. vctrap() now takes the faulting address as an additional argument, so vctrap_early() has to pass %cr2 in %r8. While there, also zero the arguments sig and code: vctrap_early() is always in kernel mode, so user is zero and they are never used. ok? Take care, HJ. --- sys/arch/amd64/amd64/machdep.c | 16 ++++++++++++++-- sys/arch/amd64/amd64/trap.c | 21 +++++++++++++++++---- sys/arch/amd64/amd64/vector.S | 3 +++ sys/arch/amd64/include/vmmvar.h | 1 + 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index ed3aed4d361..066281cb181 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -1345,6 +1345,7 @@ void cpu_init_early_vctrap(paddr_t addr) { struct region_descriptor region; + uint64_t request, resp; extern void Xvctrap_early(void); @@ -1364,8 +1365,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 68cdf4c1b23..5cb25f5720c 100644 --- a/sys/arch/amd64/amd64/trap.c +++ b/sys/arch/amd64/amd64/trap.c @@ -98,7 +98,7 @@ int upageflttrap(struct trapframe *, uint64_t); int kpageflttrap(struct trapframe *, uint64_t); #ifdef AMDSEV -int vctrap(struct trapframe *, int, int *, int *); +int vctrap(struct trapframe *, int, int *, int *, uint64_t); #endif void kerntrap(struct trapframe *); void usertrap(struct trapframe *); @@ -305,7 +305,7 @@ kpageflttrap(struct trapframe *frame, uint64_t cr2) #ifdef AMDSEV int -vctrap(struct trapframe *frame, int user, int *sig, int *code) +vctrap(struct trapframe *frame, int user, int *sig, int *code, uint64_t cr2) { uint8_t *rip = (uint8_t *)(frame->tf_rip); uint64_t port; @@ -466,6 +466,19 @@ vctrap(struct trapframe *frame, int user, int *sig, int *code) else frame->tf_rip += 2; break; + case SVM_VMEXIT_PAGE_NOT_VALIDATED: { + struct pmap *pm; + paddr_t pa; + vaddr_t va = (vaddr_t)cr2; + + pm = user ? curproc->p_vmspace->vm_map.pmap : pmap_kernel(); + if (!pmap_extract(pm, va, &pa)) + panic("page not validated: rip 0x%llx va 0x%lx " + "(unmapped)", frame->tf_rip, va); + panic("page not validated: rip 0x%llx va 0x%lx pa 0x%lx", + frame->tf_rip, va, pa); + /* NOTREACHED */ + } default: panic("invalid exit code 0x%llx", ghcb_regs.exitcode); } @@ -551,7 +564,7 @@ kerntrap(struct trapframe *frame) #ifdef AMDSEV case T_VC: - if (vctrap(frame, 0, NULL, NULL)) + if (vctrap(frame, 0, NULL, NULL, cr2)) return; goto we_re_toast; #endif @@ -636,7 +649,7 @@ usertrap(struct trapframe *frame) break; #ifdef AMDSEV case T_VC: - if (vctrap(frame, 1, &sig, &code)) + if (vctrap(frame, 1, &sig, &code, cr2)) goto out; break; #endif diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S index ff7be54de50..3c21002d298 100644 --- a/sys/arch/amd64/amd64/vector.S +++ b/sys/arch/amd64/amd64/vector.S @@ -560,6 +560,9 @@ IDTVEC(vctrap_early) cld movq %rsp, %rdi movq $0x0, %rsi + movq $0x0, %rdx + movq $0x0, %rcx + movq %cr2, %r8 call vctrap movq $0,-8(%rsp) INTRFASTEXIT diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h index 287632bb574..df99e6b7a37 100644 --- a/sys/arch/amd64/include/vmmvar.h +++ b/sys/arch/amd64/include/vmmvar.h @@ -268,6 +268,7 @@ struct vm; #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.47.3