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:
kib 2016-09-19 15:58:45 +00:00
parent 800b88629e
commit 3f4c126b42
4 changed files with 53 additions and 12 deletions

View File

@ -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);

View File

@ -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)
{

View File

@ -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,

View File

@ -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,