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>
This commit is contained in:
Duncan Laurie 2011-12-22 10:59:40 -08:00 committed by Stefan Reinauer
parent 67e6c2aaf4
commit 7389fa945f
1 changed files with 45 additions and 7 deletions

View File

@ -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