diff options
author | Duncan Laurie <dlaurie@chromium.org> | 2011-12-22 10:59:40 -0800 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2012-03-30 17:53:39 +0200 |
commit | 7389fa945f48c6da6e5f39d871c0efd9dfba93a1 (patch) | |
tree | 6698b2500dc28d8270122eee3abf9e4333edfc5a /src/cpu | |
parent | 67e6c2aaf4d4194dac824014197cc684af9750f5 (diff) |
MTRR: add alternate allocation method for odd memory maps
With >= 4GB memory installed we get a memory map split in the middle
due to remap that has boundaries that are inconveniently aligned for
MTRRs due to the various UMA regions.
0000MB-2780MB 2780MB RAM (writeback)
2780MB-2782MB 2MB TSEG (uncached/SMRR)
2782MB-2784MB 2MB GFX GTT (uncached)
2784MB-2816MB 32MB GFX UMA (uncached)
2816MB-4096MB 1280MB EMPTY (N/A)
4096MB-5368MB 1272MB RAM (writeback)
5368MB-5376MB 8MB ME UMA (uncached)
The default MTRR allocation method of trying to cover everything
with one MTRR and then carve out a single uncached region does
not work for the GPU aperture which needs write-combining type,
and it also has issues trying to cover the uneven boundaries
in the avaiable variable MTRRs.
My goal was to make a minimal set of changes and avoid modifying
behavior on existing systems with an algorithm that is not always
optimal for a typical memory layout. So the flag 'above4gb=2'
will change these allocation behaviors:
1) Detect the number of available variable MTRRs rather than
limiting to hardcoded value. We need every last MTRR.
2) Don't try to cover all RAM with one MTRR, instead let each
RAM region get covered independently.
3) Don't assume uma_memory_base is part of the last region
and increase the size of that region. In this case the UMA
region is carved out from the lower memory region and it is
already declared as part of the ram region.
4) If a memory region can't be covered with MTRRs >= 16MB then
instead make a larger region and trim it with uncached MTRRs.
Change-Id: I5a60a44ab6d3ae2f46ea6ffa9e3677aaad2485eb
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Reviewed-on: http://review.coreboot.org/761
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/cpu')
-rw-r--r-- | src/cpu/x86/mtrr/mtrr.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/src/cpu/x86/mtrr/mtrr.c b/src/cpu/x86/mtrr/mtrr.c index 9015ad4d97..8dccfefa04 100644 --- a/src/cpu/x86/mtrr/mtrr.c +++ b/src/cpu/x86/mtrr/mtrr.c @@ -179,6 +179,18 @@ static inline unsigned int fls(unsigned int x) #endif #define MTRRS (BIOS_MTRRS + OS_MTRRS) +static int total_mtrrs = MTRRS; +static int bios_mtrrs = BIOS_MTRRS; + +static void detect_var_mtrrs(void) +{ + msr_t msr; + + msr = rdmsr(MTRRcap_MSR); + + total_mtrrs = msr.lo & 0xff; + bios_mtrrs = total_mtrrs - 2; +} static void set_fixed_mtrrs(unsigned int first, unsigned int last, unsigned char type) { @@ -235,6 +247,8 @@ static unsigned int range_to_mtrr(unsigned int reg, unsigned long next_range_startk, unsigned char type, unsigned int address_bits, unsigned int above4gb) { + unsigned long hole_startk = 0, hole_sizek = 0; + if (!range_sizek) { /* If there's no MTRR hole, this function will bail out * here when called for the hole. @@ -243,7 +257,7 @@ static unsigned int range_to_mtrr(unsigned int reg, return reg; } - if (reg >= BIOS_MTRRS) { + if (reg >= bios_mtrrs) { printk(BIOS_ERR, "Warning: Out of MTRRs for base: %4ldMB, range: %ldMB, type %s\n", range_startk >>10, range_sizek >> 10, (type==MTRR_TYPE_UNCACHEABLE)?"UC": @@ -251,6 +265,16 @@ static unsigned int range_to_mtrr(unsigned int reg, return reg; } + if (above4gb == 2 && type == MTRR_TYPE_WRBACK && range_sizek % 0x4000) { + /* + * If this range is not divisible by 16MB then instead + * make a larger range and carve out an uncached hole. + */ + hole_startk = range_startk + range_sizek; + hole_sizek = 0x4000 - (range_sizek % 0x4000); + range_sizek += hole_sizek; + } + while(range_sizek) { unsigned long max_align, align; unsigned long sizek; @@ -274,11 +298,20 @@ static unsigned int range_to_mtrr(unsigned int reg, set_var_mtrr(reg++, range_startk, sizek, type, address_bits); range_startk += sizek; range_sizek -= sizek; - if (reg >= BIOS_MTRRS) { + if (reg >= bios_mtrrs) { printk(BIOS_ERR, "Running out of variable MTRRs!\n"); break; } } + + if (hole_sizek) { + printk(BIOS_DEBUG, "Adding hole at %ldMB-%ldMB\n", + hole_startk, hole_startk + hole_sizek); + reg = range_to_mtrr(reg, hole_startk, hole_sizek, + next_range_startk, MTRR_TYPE_UNCACHEABLE, + address_bits, above4gb); + } + return reg; } @@ -325,7 +358,7 @@ void set_var_mtrr_resource(void *gp, struct device *dev, struct resource *res) { struct var_mtrr_state *state = gp; unsigned long basek, sizek; - if (state->reg >= BIOS_MTRRS) + if (state->reg >= bios_mtrrs) return; basek = resk(res->base); sizek = resk(res->size); @@ -341,7 +374,7 @@ void set_var_mtrr_resource(void *gp, struct device *dev, struct resource *res) /* Write the range mtrrs */ if (state->range_sizek != 0) { #if CONFIG_VAR_MTRR_HOLE - if (state->hole_sizek == 0) { + if (state->hole_sizek == 0 && state->above4gb != 2) { /* We need to put that on to hole */ unsigned long endk = basek + sizek; state->hole_startk = state->range_startk + state->range_sizek; @@ -424,6 +457,10 @@ void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb) var_state.address_bits = address_bits; var_state.above4gb = above4gb; + /* Detect number of variable MTRRs */ + if (above4gb == 2) + detect_var_mtrrs(); + search_global_resources( IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE, set_var_mtrr_resource, &var_state); @@ -435,7 +472,8 @@ void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb) } else { #if CONFIG_VAR_MTRR_HOLE // Increase the base range and set up UMA as an UC hole instead - var_state.range_sizek += (uma_memory_size >> 10); + if (above4gb != 2) + var_state.range_sizek += (uma_memory_size >> 10); var_state.hole_startk = (uma_memory_base >> 10); var_state.hole_sizek = (uma_memory_size >> 10); @@ -454,7 +492,7 @@ void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb) printk(BIOS_DEBUG, "DONE variable MTRRs\n"); printk(BIOS_DEBUG, "Clear out the extra MTRR's\n"); /* Clear out the extra MTRR's */ - while(var_state.reg < MTRRS) { + while(var_state.reg < total_mtrrs) { set_var_mtrr(var_state.reg++, 0, 0, 0, var_state.address_bits); } @@ -463,7 +501,7 @@ void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb) * complete ROM now that we actually have RAM. */ if (boot_cpu() && (acpi_slp_type != 3)) { - set_var_mtrr(7, (4096-4)*1024, 4*1024, + set_var_mtrr(total_mtrrs-1, (4096-4)*1024, 4*1024, MTRR_TYPE_WRPROT, address_bits); } #endif |