Author: kib
Date: Mon Sep 19 15:58:45 2016
New Revision: 305978
URL: https://svnweb.freebsd.org/changeset/base/305978

Log:
  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 <s...@zxy.spb.ru> (previous 
version)
  Diagnosed by and discussed with:      avg
  Sponsored by: The FreeBSD Foundation
  MFC after:    2 weeks

Modified:
  head/sys/x86/acpica/madt.c
  head/sys/x86/include/apicvar.h
  head/sys/x86/x86/local_apic.c
  head/sys/x86/xen/xen_apic.c

Modified: head/sys/x86/acpica/madt.c
==============================================================================
--- head/sys/x86/acpica/madt.c  Mon Sep 19 15:58:33 2016        (r305977)
+++ head/sys/x86/acpica/madt.c  Mon Sep 19 15:58:45 2016        (r305978)
@@ -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);

Modified: head/sys/x86/include/apicvar.h
==============================================================================
--- head/sys/x86/include/apicvar.h      Mon Sep 19 15:58:33 2016        
(r305977)
+++ head/sys/x86/include/apicvar.h      Mon Sep 19 15:58:45 2016        
(r305978)
@@ -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)
 {

Modified: head/sys/x86/x86/local_apic.c
==============================================================================
--- head/sys/x86/x86/local_apic.c       Mon Sep 19 15:58:33 2016        
(r305977)
+++ head/sys/x86/x86/local_apic.c       Mon Sep 19 15:58:45 2016        
(r305978)
@@ -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,

Modified: head/sys/x86/xen/xen_apic.c
==============================================================================
--- head/sys/x86/xen/xen_apic.c Mon Sep 19 15:58:33 2016        (r305977)
+++ head/sys/x86/xen/xen_apic.c Mon Sep 19 15:58:45 2016        (r305978)
@@ -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,
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to