kernel - Add workaround for errata #721 on AMD cpus (found by Matt Dillon)
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 23 Mar 2012 22:49:54 +0000 (15:49 -0700)
committerVenkatesh Srinivas <me@endeavour.zapto.org>
Tue, 27 Mar 2012 13:47:17 +0000 (06:47 -0700)
* Official reference for errata 721:

    http://support.amd.com/us/Processor_TechDocs/41322_10h_Rev_Gd.pdf

* This is the cpu bug found by Matthew Dillon, present on all K10 cores
  (so far).  AMD has indicated that the bug is not present on Bulldozer
  cpus.

* The MSR workaround is now reported and programmed during kernel boot.

* No discernable difference in performance was detected running GCC
  with and without the MSR adjustment.

* Verified to have fixed our test case w/GCC.  It no longer seg-faults.

sys/platform/pc64/x86_64/initcpu.c

index 643342f..165173a 100644 (file)
@@ -146,6 +146,28 @@ initializecpu(void)
                cpu_fxsr = hw_instruction_sse = 1;
        }
 
+       if (cpu_vendor_id == CPU_VENDOR_AMD) {
+               switch((cpu_id & 0xFF0000)) {
+               case 0x100000:
+               case 0x120000:
+                       /*
+                        * Errata 721 is the cpu bug found by your's truly
+                        * (Matthew Dillon).  It is a bug where a sequence
+                        * of 5 or more popq's + a retq, under involved
+                        * deep recursion circumstances, can cause the %rsp
+                        * to not be properly updated, almost always
+                        * resulting in a seg-fault soon after.
+                        */
+                       msr = rdmsr(0xc0011029);
+                       if ((msr & 1) == 0) {
+                               kprintf("Errata 721 workaround installed\n");
+                               msr |= 1;
+                               wrmsr(0xc0011029, msr);
+                       }
+                       break;
+               }
+       }
+
        if ((amd_feature & AMDID_NX) != 0) {
                msr = rdmsr(MSR_EFER) | EFER_NXE;
                wrmsr(MSR_EFER, msr);