aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-04-24 17:51:56 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-04-24 17:51:56 +0100
commit522b335995907366ff995a36a8098bc6b1e4cdf1 (patch)
tree25af4b64dd99441bb98c2f6d5ba9ee119ef6a764
parent33264198067a646d4fb6e5016e785638a5646d42 (diff)
downloadxen-522b335995907366ff995a36a8098bc6b1e4cdf1.tar.gz
xen-522b335995907366ff995a36a8098bc6b1e4cdf1.tar.bz2
xen-522b335995907366ff995a36a8098bc6b1e4cdf1.zip
x86: Detect bogus BIOS e820 tables which are not fully covered as
cacheable (WB) by MTRRs. Fix up by truncating the memory map. This fixes performance issues on certain systems. Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
-rw-r--r--xen/arch/x86/e820.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c
index 125c8ff5f4..d402faa6e1 100644
--- a/xen/arch/x86/e820.c
+++ b/xen/arch/x86/e820.c
@@ -6,6 +6,9 @@
#include <xen/dmi.h>
#include <asm/e820.h>
#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/mtrr.h>
+#include <asm/msr.h>
/* opt_mem: Limit of physical RAM. Any RAM beyond this point is ignored. */
unsigned long long opt_mem;
@@ -345,6 +348,55 @@ static void __init clip_to_limit(uint64_t limit, char *warnmsg)
}
}
+/* Conservative estimate of top-of-RAM by looking for MTRR WB regions. */
+#define MSR_MTRRphysBase(reg) (0x200 + 2 * (reg))
+#define MSR_MTRRphysMask(reg) (0x200 + 2 * (reg) + 1)
+static uint64_t mtrr_top_of_ram(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ uint64_t mtrr_cap, mtrr_def, addr_mask, base, mask, top;
+ unsigned int i, phys_bits = 36;
+
+ /* Does the CPU support architectural MTRRs? */
+ cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
+ if ( !test_bit(X86_FEATURE_MTRR & 31, &edx) )
+ return 0;
+
+ /* Find the physical address size for this CPU. */
+ cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
+ if ( eax >= 0x80000008 )
+ {
+ cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
+ phys_bits = (uint8_t)eax;
+ }
+ addr_mask = ((1ull << phys_bits) - 1) & ~((1ull << 12) - 1);
+
+ rdmsrl(MSR_MTRRcap, mtrr_cap);
+ rdmsrl(MSR_MTRRdefType, mtrr_def);
+
+ /* MTRRs enabled, and default memory type is not writeback? */
+ if ( !test_bit(11, &mtrr_def) || ((uint8_t)mtrr_def == MTRR_TYPE_WRBACK) )
+ return 0;
+
+ /*
+ * Find end of highest WB-type range. This is a conservative estimate
+ * of the highest WB address since overlapping UC/WT ranges dominate.
+ */
+ top = 0;
+ for ( i = 0; i < (uint8_t)mtrr_cap; i++ )
+ {
+ rdmsrl(MSR_MTRRphysBase(i), base);
+ rdmsrl(MSR_MTRRphysMask(i), mask);
+ if ( !test_bit(11, &mask) || ((uint8_t)base != MTRR_TYPE_WRBACK) )
+ continue;
+ base &= addr_mask;
+ mask &= addr_mask;
+ top = max_t(uint64_t, top, ((base | ~mask) & addr_mask) + PAGE_SIZE);
+ }
+
+ return top;
+}
+
static void __init reserve_dmi_region(void)
{
u32 base, len;
@@ -357,6 +409,8 @@ static void __init reserve_dmi_region(void)
static void __init machine_specific_memory_setup(
struct e820entry *raw, int *raw_nr)
{
+ uint64_t top_of_ram;
+
char nr = (char)*raw_nr;
sanitize_e820_map(raw, &nr);
*raw_nr = nr;
@@ -389,6 +443,10 @@ static void __init machine_specific_memory_setup(
#endif
reserve_dmi_region();
+
+ top_of_ram = mtrr_top_of_ram();
+ if ( top_of_ram )
+ clip_to_limit(top_of_ram, "MTRRs do not cover all of memory.");
}
int __init e820_change_range_type(