diff options
Diffstat (limited to 'src/cpu')
-rw-r--r-- | src/cpu/x86/mtrr/mtrr.c | 116 |
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) |