diff --git a/arch/x64/apic.cc b/arch/x64/apic.cc index 2bcd227f9178d474feb960aaaa8b5415cfbf6031..904e3bfac8c5e0b089518cdce9170947541686bb 100644 --- a/arch/x64/apic.cc +++ b/arch/x64/apic.cc @@ -1,5 +1,6 @@ #include "apic.hh" #include "msr.hh" +#include "xen.hh" #include <osv/percpu.hh> #include <cpuid.hh> #include <processor.hh> @@ -118,7 +119,24 @@ void x2apic::write(apicreg reg, u32 value) u32 x2apic::id() { - return processor::rdmsr(msr::X2APIC_ID); + u32 id = processor::rdmsr(msr::X2APIC_ID); + if (!is_xen()) + return id; + + // The x2APIC specification says that reading from the X2APIC_ID MSR should + // return the physical apic id of the current processor. However, the Xen + // implementation (as of 4.2.2) is broken, and reads actually return old + // style xAPIC id. Even if they fix it, we still have HVs deployed around + // that will return the wrong ID. We can work around this by testing if the + // returned APIC id is in the form (id << 24), since in that case, the + // first 24 bits will all be zeroed. Then at least we can get this working + // everywhere. This may pose a problem if we want to ever support more than + // 1 << 24 vCPUs (or if any other HV has some random x2apic ids), but that + // is highly unlikely anyway. + if (((id & 0xffffff) == 0) && ((id >> 24) != 0)) { + id = (id >> 24); + } + return id; } apic_driver* create_apic_driver() diff --git a/arch/x64/xen.hh b/arch/x64/xen.hh index 6c090d436fea5f1689173db159f37d5d245fa82e..0704a7fd621473898c686a3ac51dd8eabc97e983 100644 --- a/arch/x64/xen.hh +++ b/arch/x64/xen.hh @@ -12,8 +12,10 @@ extern char hypercall_page[]; extern uint8_t xen_features[]; extern struct start_info* xen_start_info; +extern "C" shared_info_t *HYPERVISOR_shared_info; #define XENPV_ALTERNATIVE(x, y) ALTERNATIVE((xen_start_info != nullptr), x, y) +#define is_xen() (HYPERVISOR_shared_info != nullptr) // We don't support 32 bit struct xen_vcpu_info {