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

Miguel Landaeta <[email protected]>
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.