From: Miguel Landaeta 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 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 #include #include +#include #include #include @@ -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