Index | Thread | Search

From:
Miguel Landaeta <miguel@miguel.cc>
Subject:
[1/5] vmm: handle MSR_APICBASE and x2APIC MSR reads without injecting #GP
To:
tech@openbsd.org
Date:
Tue, 9 Jun 2026 13:26:10 +0000

Download raw body.

Thread
  • Miguel Landaeta:

    [1/5] vmm: handle MSR_APICBASE and x2APIC MSR reads without injecting #GP

This is a series of commits to expose ACPI tables to vmd guests.
These were committed and tested individually.

They can be reviewed in their entirety here:

https://github.com/openbsd/src/compare/master...nomadium:src:add-support-for-acpi-in-vmd.patch

---
 sys/arch/amd64/amd64/vmm_machdep.c | 60 ++++++++++++++++++++++++++++++
 sys/arch/amd64/include/vmmvar.h    |  3 +-
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/sys/arch/amd64/amd64/vmm_machdep.c b/sys/arch/amd64/amd64/vmm_machdep.c
index 787b65e29e1..cd1544db9bf 100644
--- a/sys/arch/amd64/amd64/vmm_machdep.c
+++ b/sys/arch/amd64/amd64/vmm_machdep.c
@@ -39,6 +39,7 @@
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
 #include <machine/ghcb.h>
+#include <machine/i82489reg.h>
 #include <machine/vmmvar.h>
 
 #include <dev/isa/isareg.h>
@@ -5852,11 +5853,40 @@ vmx_handle_rdmsr(struct vcpu *vcpu)
 		*rax = 0;
 		*rdx = 0;
 		break;
+	case MSR_APICBASE:
+		/*
+		 * Return the LAPIC base address with the global enable bit.
+		 * APICBASE_ENABLE_X2APIC is intentionally omitted: guests
+		 * that read this MSR before enabling x2APIC (e.g. Linux)
+		 * will see x2APIC as not yet enabled, remain in xAPIC mode,
+		 * and fall back gracefully when their WRMSR to enable it is
+		 * silently discarded. OpenBSD guests synthesize the x2APIC
+		 * flag from CPUID when CPUIDECX_HV and CPUIDECX_X2APIC are
+		 * both set, skipping rdmsr(MSR_APICBASE), so they are
+		 * unaffected. The BSP flag is set only for VCPU 0.
+		 */
+		*rax = LAPIC_BASE | APICBASE_GLOBAL_ENABLE;
+		if (vcpu->vc_id == 0)
+			*rax |= APICBASE_BSP;
+		*rdx = 0;
+		break;
 	case MSR_CR_PAT:
 		*rax = (vcpu->vc_shadow_pat & 0xFFFFFFFFULL);
 		*rdx = (vcpu->vc_shadow_pat >> 32);
 		break;
 	default:
+		/*
+		 * x2APIC MSRs (0x800-0x8ff): return the VCPU id for the LAPIC
+		 * ID register and 0 for everything else. Returning 0 for the
+		 * current-count register (0x839) causes lapic_calibrate_timer()
+		 * to see lapic_per_second == 0 and fall back to the PIT.
+		 */
+		if (*rcx >= MSR_X2APIC_BASE &&
+		    *rcx < MSR_X2APIC_BASE + 0x100) {
+			*rax = (*rcx == MSR_X2APIC_ID) ? vcpu->vc_id : 0;
+			*rdx = 0;
+			break;
+		}
 		/* Unsupported MSRs causes #GP exception, don't advance %rip */
 		DPRINTF("%s: unsupported rdmsr (msr=0x%llx), injecting #GP\n",
 		    __func__, *rcx);
@@ -6163,6 +6193,23 @@ svm_handle_msr(struct vcpu *vcpu)
 			*rax = 0;
 			*rdx = 0;
 			break;
+		case MSR_APICBASE:
+			/*
+			 * Return the LAPIC base address with the global enable bit.
+			 * APICBASE_ENABLE_X2APIC is intentionally omitted: guests
+			 * that read this MSR before enabling x2APIC (e.g. Linux)
+			 * will see x2APIC as not yet enabled, remain in xAPIC mode,
+			 * and fall back gracefully when their WRMSR to enable it is
+			 * silently discarded. OpenBSD guests synthesize the x2APIC
+			 * flag from CPUID when CPUIDECX_HV and CPUIDECX_X2APIC are
+			 * both set, skipping rdmsr(MSR_APICBASE), so they are
+			 * unaffected. The BSP flag is set only for VCPU 0.
+			 */
+			*rax = LAPIC_BASE | APICBASE_GLOBAL_ENABLE;
+			if (vcpu->vc_id == 0)
+				*rax |= APICBASE_BSP;
+			*rdx = 0;
+			break;
 		case MSR_CR_PAT:
 			*rax = (vcpu->vc_shadow_pat & 0xFFFFFFFFULL);
 			*rdx = (vcpu->vc_shadow_pat >> 32);
@@ -6173,6 +6220,19 @@ svm_handle_msr(struct vcpu *vcpu)
 			*rdx = 0;
 			break;
 		default:
+			/*
+			 * x2APIC MSRs (0x800-0x8ff): return the VCPU id for
+			 * the LAPIC ID register and 0 for everything else.
+			 * Returning 0 for the current-count register (0x839)
+			 * causes lapic_calibrate_timer() to see
+			 * lapic_per_second == 0 and fall back to the PIT.
+			 */
+			if (*rcx >= MSR_X2APIC_BASE &&
+			    *rcx < MSR_X2APIC_BASE + 0x100) {
+				*rax = (*rcx == MSR_X2APIC_ID) ? vcpu->vc_id : 0;
+				*rdx = 0;
+				break;
+			}
 			/*
 			 * Unsupported MSRs causes #GP exception, don't advance
 			 * %rip
diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h
index 25b1618ad1f..5ba454d5cc4 100644
--- a/sys/arch/amd64/include/vmmvar.h
+++ b/sys/arch/amd64/include/vmmvar.h
@@ -520,7 +520,6 @@ struct vm_rwregs_params {
  *  perf/debug (CPUIDECX_PDCM)
  *  pcid (CPUIDECX_PCID)
  *  direct cache access (CPUIDECX_DCA)
- *  x2APIC (CPUIDECX_X2APIC)
  *  apic deadline (CPUIDECX_DEADLINE)
  *  apic (CPUID_APIC)
  *  psn (CPUID_PSN)
@@ -534,7 +533,7 @@ struct vm_rwregs_params {
     CPUIDECX_PDCM | CPUIDECX_VMX | CPUIDECX_DTES64 | \
     CPUIDECX_DSCPL | CPUIDECX_SMX | CPUIDECX_CNXTID | \
     CPUIDECX_SDBG | CPUIDECX_XTPR | CPUIDECX_PCID | \
-    CPUIDECX_DCA | CPUIDECX_X2APIC | CPUIDECX_DEADLINE)
+    CPUIDECX_DCA | CPUIDECX_DEADLINE)
 #define VMM_ECPUIDECX_MASK ~(CPUIDECX_SVM | CPUIDECX_MWAITX)
 #define VMM_CPUIDEDX_MASK ~(CPUID_ACPI | CPUID_TM | \
     CPUID_HTT | CPUID_DS | CPUID_APIC | \
-- 
2.54.0