Index | Thread | Search

From:
hshoexer <hshoexer@yerbouti.franken.de>
Subject:
SEV-SNP: Register GHCB and handle #VC for unvalidated
To:
tech@openbsd.org
Date:
Thu, 24 Sep 2026 18:17:22 +0200

Download raw body.

Thread
  • hshoexer:

    SEV-SNP: Register GHCB and handle #VC for unvalidated

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