summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
67e6c2a)
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>
#endif
#define MTRRS (BIOS_MTRRS + OS_MTRRS)
#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)
{
static void set_fixed_mtrrs(unsigned int first, unsigned int last, unsigned char type)
{
unsigned long next_range_startk, unsigned char type,
unsigned int address_bits, unsigned int above4gb)
{
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.
if (!range_sizek) {
/* If there's no MTRR hole, this function will bail out
* here when called for the hole.
- 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":
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":
+ 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;
while(range_sizek) {
unsigned long max_align, align;
unsigned long sizek;
set_var_mtrr(reg++, range_startk, sizek, type, address_bits);
range_startk += sizek;
range_sizek -= sizek;
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;
}
}
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);
+ }
+
{
struct var_mtrr_state *state = gp;
unsigned long basek, sizek;
{
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);
return;
basek = resk(res->base);
sizek = resk(res->size);
/* Write the range mtrrs */
if (state->range_sizek != 0) {
#if CONFIG_VAR_MTRR_HOLE
/* 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;
/* We need to put that on to hole */
unsigned long endk = basek + sizek;
state->hole_startk = state->range_startk + state->range_sizek;
var_state.address_bits = address_bits;
var_state.above4gb = 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);
search_global_resources(
IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE,
set_var_mtrr_resource, &var_state);
} else {
#if CONFIG_VAR_MTRR_HOLE
// Increase the base range and set up UMA as an UC hole instead
} 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);
var_state.hole_startk = (uma_memory_base >> 10);
var_state.hole_sizek = (uma_memory_size >> 10);
printk(BIOS_DEBUG, "DONE variable MTRRs\n");
printk(BIOS_DEBUG, "Clear out the extra MTRR's\n");
/* Clear out the extra MTRR's */
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);
}
set_var_mtrr(var_state.reg++, 0, 0, 0, var_state.address_bits);
}
* complete ROM now that we actually have RAM.
*/
if (boot_cpu() && (acpi_slp_type != 3)) {
* 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
MTRR_TYPE_WRPROT, address_bits);
}
#endif