Detect x2APIC mode on boot and obey it.
If BIOS performed hand-off to OS with BSP LAPIC in the x2APIC mode, system usually consumes such configuration without a notice, since x2APIC is turned on by OS if possible (nop). But if BIOS simultaneously requested OS to not use x2APIC, code assumption that that xAPIC is active breaks. In my opinion, we cannot safely turn off x2APIC if control is passed in this mode. Make madt.c ignore user or BIOS requests to turn x2APIC off, and do not check the x2APIC black list. Just trust the config and try to continue, giving a warning in dmesg. Reported and tested by: Slawa Olhovchenkov <slw@zxy.spb.ru> (previous version) Diagnosed by and discussed with: avg Sponsored by: The FreeBSD Foundation MFC after: 2 weeks
This commit is contained in:
parent
bf9c87c813
commit
36596c2a29
@ -135,10 +135,11 @@ madt_setup_local(void)
|
||||
const char *reason;
|
||||
char *hw_vendor;
|
||||
u_int p[4];
|
||||
int user_x2apic;
|
||||
bool bios_x2apic;
|
||||
|
||||
madt = pmap_mapbios(madt_physaddr, madt_length);
|
||||
if ((cpu_feature2 & CPUID2_X2APIC) != 0) {
|
||||
x2apic_mode = 1;
|
||||
reason = NULL;
|
||||
|
||||
/*
|
||||
@ -150,21 +151,17 @@ madt_setup_local(void)
|
||||
if (dmartbl_physaddr != 0) {
|
||||
dmartbl = acpi_map_table(dmartbl_physaddr,
|
||||
ACPI_SIG_DMAR);
|
||||
if ((dmartbl->Flags & ACPI_DMAR_X2APIC_OPT_OUT) != 0) {
|
||||
x2apic_mode = 0;
|
||||
if ((dmartbl->Flags & ACPI_DMAR_X2APIC_OPT_OUT) != 0)
|
||||
reason = "by DMAR table";
|
||||
}
|
||||
acpi_unmap_table(dmartbl);
|
||||
}
|
||||
if (vm_guest == VM_GUEST_VMWARE) {
|
||||
vmware_hvcall(VMW_HVCMD_GETVCPU_INFO, p);
|
||||
if ((p[0] & VMW_VCPUINFO_VCPU_RESERVED) != 0 ||
|
||||
(p[0] & VMW_VCPUINFO_LEGACY_X2APIC) == 0) {
|
||||
x2apic_mode = 0;
|
||||
reason = "inside VMWare without intr redirection";
|
||||
}
|
||||
(p[0] & VMW_VCPUINFO_LEGACY_X2APIC) == 0)
|
||||
reason =
|
||||
"inside VMWare without intr redirection";
|
||||
} else if (vm_guest == VM_GUEST_XEN) {
|
||||
x2apic_mode = 0;
|
||||
reason = "due to running under XEN";
|
||||
} else if (vm_guest == VM_GUEST_NO &&
|
||||
CPUID_TO_FAMILY(cpu_id) == 0x6 &&
|
||||
@ -184,16 +181,33 @@ madt_setup_local(void)
|
||||
if (!strcmp(hw_vendor, "LENOVO") ||
|
||||
!strcmp(hw_vendor,
|
||||
"ASUSTeK Computer Inc.")) {
|
||||
x2apic_mode = 0;
|
||||
reason =
|
||||
"for a suspected SandyBridge BIOS bug";
|
||||
}
|
||||
freeenv(hw_vendor);
|
||||
}
|
||||
}
|
||||
TUNABLE_INT_FETCH("hw.x2apic_enable", &x2apic_mode);
|
||||
if (!x2apic_mode && reason != NULL && bootverbose)
|
||||
bios_x2apic = lapic_is_x2apic();
|
||||
if (reason != NULL && bios_x2apic) {
|
||||
if (bootverbose)
|
||||
printf("x2APIC should be disabled %s but "
|
||||
"already enabled by BIOS; enabling.\n",
|
||||
reason);
|
||||
reason = NULL;
|
||||
}
|
||||
if (reason == NULL)
|
||||
x2apic_mode = 1;
|
||||
else if (bootverbose)
|
||||
printf("x2APIC available but disabled %s\n", reason);
|
||||
user_x2apic = x2apic_mode;
|
||||
TUNABLE_INT_FETCH("hw.x2apic_enable", &user_x2apic);
|
||||
if (user_x2apic != x2apic_mode) {
|
||||
if (bios_x2apic && !user_x2apic)
|
||||
printf("x2APIC disabled by tunable and "
|
||||
"enabled by BIOS; ignoring tunable.");
|
||||
else
|
||||
x2apic_mode = user_x2apic;
|
||||
}
|
||||
}
|
||||
|
||||
lapic_init(madt->Address);
|
||||
|
@ -206,6 +206,7 @@ struct apic_ops {
|
||||
void (*create)(u_int, int);
|
||||
void (*init)(vm_paddr_t);
|
||||
void (*xapic_mode)(void);
|
||||
bool (*is_x2apic)(void);
|
||||
void (*setup)(int);
|
||||
void (*dump)(const char *);
|
||||
void (*disable)(void);
|
||||
@ -268,6 +269,13 @@ lapic_xapic_mode(void)
|
||||
apic_ops.xapic_mode();
|
||||
}
|
||||
|
||||
static inline bool
|
||||
lapic_is_x2apic(void)
|
||||
{
|
||||
|
||||
return (apic_ops.is_x2apic());
|
||||
}
|
||||
|
||||
static inline void
|
||||
lapic_setup(int boot)
|
||||
{
|
||||
|
@ -269,6 +269,16 @@ native_lapic_enable_x2apic(void)
|
||||
wrmsr(MSR_APICBASE, apic_base);
|
||||
}
|
||||
|
||||
static bool
|
||||
native_lapic_is_x2apic(void)
|
||||
{
|
||||
uint64_t apic_base;
|
||||
|
||||
apic_base = rdmsr(MSR_APICBASE);
|
||||
return ((apic_base & (APICBASE_X2APIC | APICBASE_ENABLED)) ==
|
||||
(APICBASE_X2APIC | APICBASE_ENABLED));
|
||||
}
|
||||
|
||||
static void lapic_enable(void);
|
||||
static void lapic_resume(struct pic *pic, bool suspend_cancelled);
|
||||
static void lapic_timer_oneshot(struct lapic *);
|
||||
@ -329,6 +339,7 @@ struct apic_ops apic_ops = {
|
||||
.create = native_lapic_create,
|
||||
.init = native_lapic_init,
|
||||
.xapic_mode = native_lapic_xapic_mode,
|
||||
.is_x2apic = native_lapic_is_x2apic,
|
||||
.setup = native_lapic_setup,
|
||||
.dump = native_lapic_dump,
|
||||
.disable = native_lapic_disable,
|
||||
|
@ -139,6 +139,13 @@ xen_pv_lapic_disable(void)
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
xen_pv_lapic_is_x2apic(void)
|
||||
{
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void
|
||||
xen_pv_lapic_eoi(void)
|
||||
{
|
||||
@ -351,6 +358,7 @@ struct apic_ops xen_apic_ops = {
|
||||
.create = xen_pv_lapic_create,
|
||||
.init = xen_pv_lapic_init,
|
||||
.xapic_mode = xen_pv_lapic_disable,
|
||||
.is_x2apic = xen_pv_lapic_is_x2apic,
|
||||
.setup = xen_pv_lapic_setup,
|
||||
.dump = xen_pv_lapic_dump,
|
||||
.disable = xen_pv_lapic_disable,
|
||||
|
Loading…
Reference in New Issue
Block a user