x86: MTRR workaround for system with stange var MTRRs
authorYinghai Lu <yinghai@kernel.org>
Mon, 16 Mar 2009 23:33:59 +0000 (16:33 -0700)
committerIngo Molnar <mingo@elte.hu>
Tue, 17 Mar 2009 09:47:47 +0000 (10:47 +0100)
Impact: don't trim e820 according to wrong mtrr

Ozan reports that his server emits strange warning.
it turns out the BIOS sets the MTRRs incorrectly.

Ignore those strange ranges, and don't trim e820,
just emit one warning about BIOS

Reported-by: Ozan Çağlayan <ozan@pardus.org.tr>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
LKML-Reference: <49BEE1E7.7020706@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/cpu/mtrr/cleanup.c
arch/x86/kernel/cpu/mtrr/main.c
arch/x86/kernel/cpu/mtrr/mtrr.h

index 58b58bbf7eb3d62a969e592d3d25da834256a379..f4f89fbc228f167badceffb7499c00f43454d6af 100644 (file)
@@ -189,6 +189,17 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
                if (!size)
                        continue;
                base = range_state[i].base_pfn;
+               if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed &&
+                   (mtrr_state.enabled & 1)) {
+                       /* Var MTRR contains UC entry below 1M? Skip it: */
+                       printk(KERN_WARNING "WARNING: BIOS bug: VAR MTRR %d "
+                               "contains strange UC entry under 1M, check "
+                               "with your system vendor!\n", i);
+                       if (base + size <= (1<<(20-PAGE_SHIFT)))
+                               continue;
+                       size -= (1<<(20-PAGE_SHIFT)) - base;
+                       base = 1<<(20-PAGE_SHIFT);
+               }
                subtract_range(range, base, base + size - 1);
        }
        if (extra_remove_size)
index 5c2e266f41d73faa7299dcbac2d4d477477717af..03cda01f57c7125173a8b348d92105d5bcd5dd28 100644 (file)
@@ -574,7 +574,7 @@ struct mtrr_value {
        unsigned long   lsize;
 };
 
-static struct mtrr_value mtrr_state[MTRR_MAX_VAR_RANGES];
+static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES];
 
 static int mtrr_save(struct sys_device * sysdev, pm_message_t state)
 {
@@ -582,9 +582,9 @@ static int mtrr_save(struct sys_device * sysdev, pm_message_t state)
 
        for (i = 0; i < num_var_ranges; i++) {
                mtrr_if->get(i,
-                            &mtrr_state[i].lbase,
-                            &mtrr_state[i].lsize,
-                            &mtrr_state[i].ltype);
+                            &mtrr_value[i].lbase,
+                            &mtrr_value[i].lsize,
+                            &mtrr_value[i].ltype);
        }
        return 0;
 }
@@ -594,11 +594,11 @@ static int mtrr_restore(struct sys_device * sysdev)
        int i;
 
        for (i = 0; i < num_var_ranges; i++) {
-               if (mtrr_state[i].lsize) 
+               if (mtrr_value[i].lsize)
                        set_mtrr(i,
-                                mtrr_state[i].lbase,
-                                mtrr_state[i].lsize,
-                                mtrr_state[i].ltype);
+                                mtrr_value[i].lbase,
+                                mtrr_value[i].lsize,
+                                mtrr_value[i].ltype);
        }
        return 0;
 }
index 6710e93021a77691e01e21ce8c8624b053f2a691..77f67f7b347a3a97a093b1094c5da86a2ca9ad35 100644 (file)
@@ -79,6 +79,7 @@ extern struct mtrr_ops * mtrr_if;
 
 extern unsigned int num_var_ranges;
 extern u64 mtrr_tom2;
+extern struct mtrr_state_type mtrr_state;
 
 void mtrr_state_warn(void);
 const char *mtrr_attrib_to_str(int x);