summaryrefslogtreecommitdiff
path: root/src/cpu/x86
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/x86')
-rw-r--r--src/cpu/x86/mtrr/mtrr.c116
1 files changed, 78 insertions, 38 deletions
diff --git a/src/cpu/x86/mtrr/mtrr.c b/src/cpu/x86/mtrr/mtrr.c
index b19b853196..e0392f7978 100644
--- a/src/cpu/x86/mtrr/mtrr.c
+++ b/src/cpu/x86/mtrr/mtrr.c
@@ -50,6 +50,12 @@
#define BIOS_MTRRS 6
#define OS_MTRRS 2
#define MTRRS (BIOS_MTRRS + OS_MTRRS)
+/*
+ * Static storage size for variable MTRRs. Its sized sufficiently large to
+ * handle different types of CPUs. Empiricially, 16 variable MTRRs has not
+ * yet been observed.
+ */
+#define NUM_MTRR_STATIC_STORAGE 16
static int total_mtrrs = MTRRS;
static int bios_mtrrs = BIOS_MTRRS;
@@ -61,6 +67,13 @@ static void detect_var_mtrrs(void)
msr = rdmsr(MTRRcap_MSR);
total_mtrrs = msr.lo & 0xff;
+
+ if (total_mtrrs > NUM_MTRR_STATIC_STORAGE) {
+ printk(BIOS_WARNING,
+ "MTRRs detected (%d) > NUM_MTRR_STATIC_STORAGE (%d)\n",
+ total_mtrrs, NUM_MTRR_STATIC_STORAGE);
+ total_mtrrs = NUM_MTRR_STATIC_STORAGE;
+ }
bios_mtrrs = total_mtrrs - OS_MTRRS;
}
@@ -314,8 +327,6 @@ static void commit_fixed_mtrrs(void)
memset(&fixed_msrs, 0, sizeof(fixed_msrs));
- disable_cache();
-
msr_num = 0;
type_index = 0;
for (i = 0; i < ARRAY_SIZE(fixed_mtrr_desc); i++) {
@@ -347,12 +358,13 @@ static void commit_fixed_mtrrs(void)
}
}
- for (i = 0; i < ARRAY_SIZE(fixed_msrs); i++) {
+ for (i = 0; i < ARRAY_SIZE(fixed_msrs); i++)
printk(BIOS_DEBUG, "MTRR: Fixed MSR 0x%lx 0x%08x%08x\n",
msr_index[i], fixed_msrs[i].hi, fixed_msrs[i].lo);
- wrmsr(msr_index[i], fixed_msrs[i]);
- }
+ disable_cache();
+ for (i = 0; i < ARRAY_SIZE(fixed_msrs); i++)
+ wrmsr(msr_index[i], fixed_msrs[i]);
enable_cache();
}
@@ -370,13 +382,28 @@ void x86_setup_fixed_mtrrs(void)
enable_fixed_mtrr();
}
+struct var_mtrr_regs {
+ msr_t base;
+ msr_t mask;
+};
+
+struct var_mtrr_solution {
+ int mtrr_default_type;
+ int num_used;
+ struct var_mtrr_regs regs[NUM_MTRR_STATIC_STORAGE];
+};
+
+/* Global storage for variable MTRR solution. */
+static struct var_mtrr_solution mtrr_global_solution;
+
struct var_mtrr_state {
struct memranges *addr_space;
int above4gb;
int address_bits;
- int commit_mtrrs;
+ int prepare_msrs;
int mtrr_index;
int def_mtrr_type;
+ struct var_mtrr_regs *regs;
};
static void clear_var_mtrr(int index)
@@ -388,11 +415,10 @@ static void clear_var_mtrr(int index)
wrmsr(MTRRphysMask_MSR(index), msr_val);
}
-static void write_var_mtrr(struct var_mtrr_state *var_state,
- uint32_t base, uint32_t size, int mtrr_type)
+static void prep_var_mtrr(struct var_mtrr_state *var_state,
+ uint32_t base, uint32_t size, int mtrr_type)
{
- msr_t msr_val;
- unsigned long msr_index;
+ struct var_mtrr_regs *regs;
resource_t rbase;
resource_t rsize;
resource_t mask;
@@ -420,18 +446,15 @@ static void write_var_mtrr(struct var_mtrr_state *var_state,
printk(BIOS_DEBUG, "MTRR: %d base 0x%016llx mask 0x%016llx type %d\n",
var_state->mtrr_index, rbase, rsize, mtrr_type);
- msr_val.lo = rbase;
- msr_val.lo |= mtrr_type;
+ regs = &var_state->regs[var_state->mtrr_index];
- msr_val.hi = rbase >> 32;
- msr_index = MTRRphysBase_MSR(var_state->mtrr_index);
- wrmsr(msr_index, msr_val);
+ regs->base.lo = rbase;
+ regs->base.lo |= mtrr_type;
+ regs->base.hi = rbase >> 32;
- msr_val.lo = rsize;
- msr_val.lo |= MTRRphysMaskValid;
- msr_val.hi = rsize >> 32;
- msr_index = MTRRphysMask_MSR(var_state->mtrr_index);
- wrmsr(msr_index, msr_val);
+ regs->mask.lo = rsize;
+ regs->mask.lo |= MTRRphysMaskValid;
+ regs->mask.hi = rsize >> 32;
}
static void calc_var_mtrr_range(struct var_mtrr_state *var_state,
@@ -453,8 +476,8 @@ static void calc_var_mtrr_range(struct var_mtrr_state *var_state,
else
mtrr_size = 1 << addr_lsb;
- if (var_state->commit_mtrrs)
- write_var_mtrr(var_state, base, mtrr_size, mtrr_type);
+ if (var_state->prepare_msrs)
+ prep_var_mtrr(var_state, base, mtrr_size, mtrr_type);
size -= mtrr_size;
base += mtrr_size;
@@ -614,7 +637,7 @@ static void __calc_var_mtrrs(struct memranges *addr_space,
var_state.addr_space = addr_space;
var_state.above4gb = above4gb;
var_state.address_bits = address_bits;
- var_state.commit_mtrrs = 0;
+ var_state.prepare_msrs = 0;
wb_deftype_count = 0;
uc_deftype_count = 0;
@@ -711,20 +734,21 @@ static int calc_var_mtrrs(struct memranges *addr_space,
return MTRR_TYPE_UNCACHEABLE;
}
-static void commit_var_mtrrs(struct memranges *addr_space, int def_type,
- int above4gb, int address_bits)
+static void prepare_var_mtrrs(struct memranges *addr_space, int def_type,
+ int above4gb, int address_bits,
+ struct var_mtrr_solution *sol)
{
struct range_entry *r;
struct var_mtrr_state var_state;
- int i;
var_state.addr_space = addr_space;
var_state.above4gb = above4gb;
var_state.address_bits = address_bits;
- /* Write the MSRs. */
- var_state.commit_mtrrs = 1;
+ /* Prepare the MSRs. */
+ var_state.prepare_msrs = 1;
var_state.mtrr_index = 0;
var_state.def_mtrr_type = def_type;
+ var_state.regs = &sol->regs[0];
memranges_each_entry(r, var_state.addr_space) {
if (range_entry_mtrr_type(r) == def_type)
@@ -737,30 +761,46 @@ static void commit_var_mtrrs(struct memranges *addr_space, int def_type,
calc_var_mtrrs_without_hole(&var_state, r);
}
- /* Clear all remaining variable MTRRs. */
- for (i = var_state.mtrr_index; i < total_mtrrs; i++)
+ /* Update the solution. */
+ sol->num_used = var_state.mtrr_index;
+}
+
+static void commit_var_mtrrs(const struct var_mtrr_solution *sol)
+{
+ int i;
+
+ /* Write out the variable MTTRs. */
+ disable_cache();
+ for (i = 0; i < sol->num_used; i++) {
+ wrmsr(MTRRphysBase_MSR(i), sol->regs[i].base);
+ wrmsr(MTRRphysMask_MSR(i), sol->regs[i].mask);
+ }
+ /* Clear the ones that are unused. */
+ for (; i < total_mtrrs; i++)
clear_var_mtrr(i);
+ enable_cache();
+
}
void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb)
{
- static int mtrr_default_type = -1;
+ static struct var_mtrr_solution *sol = NULL;
struct memranges *addr_space;
addr_space = get_physical_address_space();
- if (mtrr_default_type == -1) {
+ if (sol == NULL) {
if (above4gb == 2)
detect_var_mtrrs();
- mtrr_default_type =
+ sol = &mtrr_global_solution;
+ sol->mtrr_default_type =
calc_var_mtrrs(addr_space, !!above4gb, address_bits);
+ prepare_var_mtrrs(addr_space, sol->mtrr_default_type,
+ !!above4gb, address_bits, sol);
}
- disable_cache();
- commit_var_mtrrs(addr_space, mtrr_default_type, !!above4gb,
- address_bits);
- enable_var_mtrr(mtrr_default_type);
- enable_cache();
+ commit_var_mtrrs(sol);
+ enable_var_mtrr(sol->mtrr_default_type);
}
void x86_setup_mtrrs(void)