Index | Thread | Search

From:
Hans-Jörg Höxer <hshoexer@genua.de>
Subject:
SEV-ES guest: Implement CPUID #VC, IOIO #VC, MSR #VC 3/3
To:
<tech@openbsd.org>
Date:
Thu, 26 Jun 2025 14:20:20 +0200

Download raw body.

Thread
Hi,

this is change 3/3.  With this change, SEV-ES enabled guest can be run
on vmm(4)/vmd(8).
    
Fill in the handling of cpuid, in/out and rdmsr/wrmsr.  For the
in/out and rdmsr/wrmsr we have to decode the actual instructions.
Use the GHCB to request emulation of these instructions from vmm(4).

Take care,
Hans-Joerg

-- 
commit dbe7557824d97848daf41c65ea0cef92a153ad50
Author: Hans-Joerg Hoexer <hshoexer@genua.de>
Date:   Thu Nov 21 15:57:33 2024 +0100

    SEV-ES guest: Implement CPUID #VC, IOIO #VC, MSR #VC
    
    Fill in the handling of cpuid, in/out and rdmsr/wrmsr.  For the
    in/out and rdmsr/wrmsr we have to decode the actual instructions.
    Use the GHCB to request emulation of these instructions from vmm(4).
    
    With this change, SEV-ES enabled guest are able to run on vmm(4)/vmd(8).

diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c
index bb445a60894..37b0dfbe8ba 100644
--- a/sys/arch/amd64/amd64/trap.c
+++ b/sys/arch/amd64/amd64/trap.c
@@ -305,6 +305,8 @@ int
 vctrap(struct trapframe *frame)
 {
 	uint64_t	 sw_exitcode, sw_exitinfo1, sw_exitinfo2;
+	uint8_t		*rip = (uint8_t *)(frame->tf_rip);
+	uint16_t	 port;
 	struct ghcb_sync syncout, syncin;
 	struct ghcb_sa	*ghcb;
 
@@ -318,6 +320,99 @@ vctrap(struct trapframe *frame)
 	sw_exitinfo2 = 0;
 
 	switch (sw_exitcode) {
+	case SVM_VMEXIT_CPUID:
+		ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncout);
+		ghcb_sync_val(GHCB_RCX, GHCB_SZ32, &syncout);
+		ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncin);
+		ghcb_sync_val(GHCB_RBX, GHCB_SZ32, &syncin);
+		ghcb_sync_val(GHCB_RCX, GHCB_SZ32, &syncin);
+		ghcb_sync_val(GHCB_RDX, GHCB_SZ32, &syncin);
+		frame->tf_rip += 2;
+		break;
+	case SVM_VMEXIT_MSR: {
+		if (*rip == 0x0f && *(rip + 1) == 0x30) {
+			/* WRMSR */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncout);
+			ghcb_sync_val(GHCB_RCX, GHCB_SZ32, &syncout);
+			ghcb_sync_val(GHCB_RDX, GHCB_SZ32, &syncout);
+			sw_exitinfo1 = 1;
+		} else if (*rip == 0x0f && *(rip + 1) == 0x32) {
+			/* RDMSR */
+			ghcb_sync_val(GHCB_RCX, GHCB_SZ32, &syncout);
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncin);
+			ghcb_sync_val(GHCB_RDX, GHCB_SZ32, &syncin);
+		} else
+			panic("failed to decode MSR");
+		frame->tf_rip += 2;
+		break;
+	    }
+	case SVM_VMEXIT_IOIO: {
+		switch (*rip) {
+		case 0x66: {
+			switch (*(rip + 1)) {
+			case 0xef:	/* out %ax,(%dx) */
+				ghcb_sync_val(GHCB_RAX, GHCB_SZ16, &syncout);
+				port = (uint16_t)frame->tf_rdx;
+				sw_exitinfo1 = (port << 16) |
+				    (1ULL << 5);
+				frame->tf_rip += 2;
+				break;
+			case 0xed:	/* in (%dx),%ax */
+				ghcb_sync_val(GHCB_RAX, GHCB_SZ16, &syncin);
+				port = (uint16_t)frame->tf_rdx;
+				sw_exitinfo1 = (port << 16) |
+				    (1ULL << 5) | (1ULL << 0);
+				frame->tf_rip += 2;
+				break;
+			default:
+				panic("failed to decode prefixed IOIO");
+			}
+			break;
+		    }
+		case 0xe4:	/* in $port,%al */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ8, &syncin);
+			port = *(rip + 1);
+			sw_exitinfo1 = (port << 16) | (1ULL << 4) |
+			    (1ULL << 0);
+			frame->tf_rip += 2;
+			break;
+		case 0xe6:	/* outb %al,$port */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ8, &syncout);
+			port = *(rip + 1);
+			sw_exitinfo1 = (port << 16) | (1ULL << 4);
+			frame->tf_rip += 2;
+			break;
+		case 0xec:	/* in (%dx),%al */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ8, &syncin);
+			port = (uint16_t)frame->tf_rdx;
+			sw_exitinfo1 = (port << 16) | (1ULL << 4) |
+			    (1ULL << 0);
+			frame->tf_rip += 1;
+			break;
+		case 0xed:	/* in (%dx),%eax */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncin);
+			port = (uint16_t)frame->tf_rdx;
+			sw_exitinfo1 = (port << 16) | (1ULL << 6) |
+			    (1ULL << 0);
+			frame->tf_rip += 1;
+			break;
+		case 0xee:	/* out %al,(%dx) */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ8, &syncout);
+			port = (uint16_t)frame->tf_rdx;
+			sw_exitinfo1 = (port << 16) | (1ULL << 4);
+			frame->tf_rip += 1;
+			break;
+		case 0xef:	/* out %eax,(%dx) */
+			ghcb_sync_val(GHCB_RAX, GHCB_SZ32, &syncout);
+			port = (uint16_t)frame->tf_rdx;
+			sw_exitinfo1 = (port << 16) | (1ULL << 6);
+			frame->tf_rip += 1;
+			break;
+		default:
+			panic("failed to decode IOIO");
+		}
+		break;
+	    }
 	default:
 		panic("invalid exit code 0x%llx", sw_exitcode);
 	}