]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/i386/mtrr.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / osfmk / i386 / mtrr.c
index fe718c39d6fd78ffc30194980f97122d64ed06ce..30df3db47345794cf8a11c6949c3b32d6e1cee74 100644 (file)
@@ -1,33 +1,40 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2011 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 #include <mach/kern_return.h>
 #include <kern/kalloc.h>
 #include <kern/cpu_number.h>
 #include <kern/cpu_data.h>
-#include <i386/mp.h>
 #include <i386/cpuid.h>
+#include <i386/mp.h>
 #include <i386/proc_reg.h>
 #include <i386/mtrr.h>
+#include <i386/machine_check.h>
 
 struct mtrr_var_range {
        uint64_t  base;         /* in IA32_MTRR_PHYSBASE format */
@@ -56,6 +63,7 @@ decl_simple_lock_data(static, mtrr_lock);
 #define MTRR_LOCK()    simple_lock(&mtrr_lock);
 #define MTRR_UNLOCK()  simple_unlock(&mtrr_lock);
 
+//#define MTRR_DEBUG 1
 #if    MTRR_DEBUG
 #define DBG(x...)      kprintf(x)
 #else
@@ -100,7 +108,7 @@ static int  var_range_overlap(mtrr_var_range_t * range, addr64_t address,
 static uint64_t mtrr_phys_mask = PHYS_BITS_TO_MASK(36);
 
 #define IA32_MTRR_PHYMASK_VALID                0x0000000000000800ULL
-#define IA32_MTRR_PHYSBASE_MASK                (mtrr_phys_mask & ~0xFFF)
+#define IA32_MTRR_PHYSBASE_MASK                (mtrr_phys_mask & ~0x0000000000000FFFULL)
 #define IA32_MTRR_PHYSBASE_TYPE                0x00000000000000FFULL
 
 /*
@@ -180,6 +188,42 @@ mtrr_set_fix_ranges(const struct mtrr_fix_range * range)
                wrmsr64(MSR_IA32_MTRR_FIX4K_C0000 + i, range[3 + i].types);
 }
 
+static boolean_t
+mtrr_check_fix_ranges(const struct mtrr_fix_range * range)
+{
+       int             i;
+       boolean_t       match = TRUE;
+
+       DBG("CPU%d: %s\n", get_cpu_number(), __FUNCTION__);
+
+       /* assume 11 fix range registers */
+       match = range[0].types == rdmsr64(MSR_IA32_MTRR_FIX64K_00000) &&
+               range[1].types == rdmsr64(MSR_IA32_MTRR_FIX16K_80000) &&
+               range[2].types == rdmsr64(MSR_IA32_MTRR_FIX16K_A0000);
+       for (i = 0; match && i < 8; i++) {
+               match = range[3 + i].types ==
+                       rdmsr64(MSR_IA32_MTRR_FIX4K_C0000 + i);
+       }
+
+       return match;
+}
+
+static boolean_t
+mtrr_check_var_ranges(mtrr_var_range_t * range, int count)
+{
+       int             i;
+       boolean_t       match = TRUE;
+       DBG("CPU%d: %s\n", get_cpu_number(), __FUNCTION__);
+
+       for (i = 0; match && i < count; i++) {
+               match = range[i].base == rdmsr64(MSR_IA32_MTRR_PHYSBASE(i)) &&
+                       range[i].mask == rdmsr64(MSR_IA32_MTRR_PHYSMASK(i));
+       }
+
+       return match;
+}
+
 #if MTRR_DEBUG
 static void
 mtrr_msr_dump(void)
@@ -221,8 +265,6 @@ mtrr_msr_dump(void)
 void
 mtrr_init(void)
 {
-       i386_cpu_info_t * infop = cpuid_info();
-
        /* no reason to init more than once */
        if (mtrr_initialized == TRUE)
                return;
@@ -231,48 +273,13 @@ mtrr_init(void)
        if ((cpuid_features() & CPUID_FEATURE_MTRR) == 0)
                return;  /* no MTRR feature */
 
-       /* cpu vendor/model specific handling */
-       if (!strncmp(infop->cpuid_vendor, CPUID_VID_AMD, sizeof(CPUID_VID_AMD)))
-       {
-               /* Check for AMD Athlon 64 and Opteron */
-               if (cpuid_family() == 0xF)
-               {
-                       uint32_t cpuid_result[4];
-
-                       /* check if cpu support Address Sizes function */
-                       do_cpuid(0x80000000, cpuid_result);
-                       if (cpuid_result[0] >= 0x80000008)
-                       {
-                               int bits;
-
-                               do_cpuid(0x80000008, cpuid_result);
-                               DBG("MTRR: AMD 8000_0008 EAX = %08x\n",
-                                       cpuid_result[0]);
-
-                               /*
-                                * Function 8000_0008 (Address Sizes) EAX
-                                * Bits  7-0 : phys address size
-                                * Bits 15-8 : virt address size
-                                */
-                               bits = cpuid_result[0] & 0xFF;
-                               if ((bits < 36) || (bits > 64))
-                               {
-                                       printf("MTRR: bad address size\n");
-                                       return; /* bogus size */
-                               }
-
-                               mtrr_phys_mask = PHYS_BITS_TO_MASK(bits);
-                       }
-               }
-       }
-
        /* use a lock to serialize MTRR changes */
        bzero((void *)&mtrr_state, sizeof(mtrr_state));
        simple_lock_init(&mtrr_lock, 0);
 
        mtrr_state.MTRRcap     = rdmsr64(MSR_IA32_MTRRCAP);
        mtrr_state.MTRRdefType = rdmsr64(MSR_IA32_MTRR_DEF_TYPE);
-       mtrr_state.var_count   = mtrr_state.MTRRcap & IA32_MTRRCAP_VCNT;
+       mtrr_state.var_count   = (unsigned int)(mtrr_state.MTRRcap & IA32_MTRRCAP_VCNT);
 
        /* allocate storage for variable ranges (can block?) */
        if (mtrr_state.var_count) {
@@ -297,6 +304,7 @@ mtrr_init(void)
 #if MTRR_DEBUG
        mtrr_msr_dump();        /* dump firmware settings */
 #endif
+
 }
 
 /*
@@ -308,8 +316,8 @@ mtrr_init(void)
 static void
 mtrr_update_action(void * cache_control_type)
 {
-       uint32_t cr0, cr4;
-       uint32_t tmp;
+       uintptr_t cr0, cr4;
+       uintptr_t tmp;
 
        cr0 = get_cr0();
        cr4 = get_cr4();
@@ -327,7 +335,7 @@ mtrr_update_action(void * cache_control_type)
                set_cr4(cr4 & ~CR4_PGE);
 
        /* flush TLBs */
-       flush_tlb();   
+       flush_tlb_raw();
 
        if (CACHE_CONTROL_PAT == cache_control_type) {
                /* Change PA6 attribute field to WC */
@@ -359,7 +367,7 @@ mtrr_update_action(void * cache_control_type)
 
        /* flush all caches and TLBs a second time */
        wbinvd();
-       flush_tlb();
+       flush_tlb_raw();
 
        /* restore normal cache mode */
        set_cr0(cr0);
@@ -406,20 +414,55 @@ mtrr_update_all_cpus(void)
 }
 
 /*
- * Update a single CPU with the current MTRR settings. Can be called
- * during slave processor initialization to mirror the MTRR settings
+ * Verify that a processor has been set with the BSP's MTRR settings. Called
+ * during slave processor initialization to check and set MTRR settings
  * discovered on the boot processor by mtrr_init().
  */
 kern_return_t
 mtrr_update_cpu(void)
 {
+       boolean_t       match = TRUE;
+
        if (mtrr_initialized == FALSE)
                return KERN_NOT_SUPPORTED;
 
+       DBG("CPU%d: %s\n", get_cpu_number(), __FUNCTION__);
+
        MTRR_LOCK();
-       mtrr_update_setup(NULL);
-       mtrr_update_action(NULL);
-       mtrr_update_teardown(NULL);
+
+       /* Check MSR_IA32_MTRR_DEF_TYPE MSR */
+       match = mtrr_state.MTRRdefType == rdmsr64(MSR_IA32_MTRR_DEF_TYPE);
+
+       /* Check MSR_IA32_MTRRCAP MSR */
+       if (match) {
+               match = mtrr_state.MTRRcap == rdmsr64(MSR_IA32_MTRRCAP);
+       }
+
+       /* Check variable ranges */
+       if (match && mtrr_state.var_count) {
+               match = mtrr_check_var_ranges(mtrr_state.var_range,
+                                             mtrr_state.var_count);
+       }
+
+       /* Check fixed ranges */
+       if (match && (mtrr_state.MTRRcap & IA32_MTRRCAP_FIX)) {
+               match = mtrr_check_fix_ranges(mtrr_state.fix_range);
+       }
+
+#if MTRR_DEBUG
+       if (!match)
+               mtrr_msr_dump();
+#endif
+       if (!match) {
+               DBG("mtrr_update_cpu() setting MTRR for cpu %d\n",
+                       get_cpu_number());
+               mtrr_update_action(NULL);
+       }
+#if MTRR_DEBUG
+       if (!match)
+               mtrr_msr_dump();
+#endif
+
        MTRR_UNLOCK();
 
        return KERN_SUCCESS;
@@ -585,7 +628,7 @@ var_range_encode(mtrr_var_range_t * range, addr64_t address,
                 uint64_t length, uint32_t type, int valid)
 {
        range->base = (address & IA32_MTRR_PHYSBASE_MASK) |
-                     (type    & IA32_MTRR_PHYSBASE_TYPE);
+                     (type    & (uint32_t)IA32_MTRR_PHYSBASE_TYPE);
 
        range->mask = LEN_TO_MASK(length) |
                      (valid ? IA32_MTRR_PHYMASK_VALID : 0);
@@ -600,7 +643,7 @@ var_range_overlap(mtrr_var_range_t * range, addr64_t address,
        int       result = 0;  /* no overlap, or overlap ok */
 
        v_address = range->base & IA32_MTRR_PHYSBASE_MASK;
-       v_type    = range->base & IA32_MTRR_PHYSBASE_TYPE;
+       v_type    = (uint32_t)(range->base & IA32_MTRR_PHYSBASE_TYPE);
        v_length  = MASK_TO_LEN(range->mask);
 
        /* detect range overlap */
@@ -634,10 +677,20 @@ var_range_overlap(mtrr_var_range_t * range, addr64_t address,
 void
 pat_init(void)
 {
-       if (cpuid_features() & CPUID_FEATURE_PAT)
-       {
-               boolean_t istate = ml_set_interrupts_enabled(FALSE);
+       boolean_t       istate;
+       uint64_t        pat;
+
+       if (!(cpuid_features() & CPUID_FEATURE_PAT))
+               return;
+
+       istate = ml_set_interrupts_enabled(FALSE);
+
+       pat = rdmsr64(MSR_IA32_CR_PAT);
+       DBG("CPU%d PAT: was 0x%016llx\n", get_cpu_number(), pat);
+
+       /* Change PA6 attribute field to WC if required */
+       if ((pat & ~(0x0FULL << 48)) != (0x01ULL << 48)) {
                mtrr_update_action(CACHE_CONTROL_PAT);
-               ml_set_interrupts_enabled(istate);
        }
+       ml_set_interrupts_enabled(istate);
 }