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.c90
1 files changed, 79 insertions, 11 deletions
diff --git a/src/cpu/x86/mtrr/mtrr.c b/src/cpu/x86/mtrr/mtrr.c
index 8033e792bc..3ee907f96a 100644
--- a/src/cpu/x86/mtrr/mtrr.c
+++ b/src/cpu/x86/mtrr/mtrr.c
@@ -154,6 +154,24 @@ static int filter_vga_wrcomb(struct device *dev, struct resource *res)
return 1;
}
+static void print_physical_address_space(const struct memranges *addr_space,
+ const char *identifier)
+{
+ const struct range_entry *r;
+
+ if (identifier)
+ printk(BIOS_DEBUG, "MTRR: %s Physical address space:\n",
+ identifier);
+ else
+ printk(BIOS_DEBUG, "MTRR: Physical address space:\n");
+
+ memranges_each_entry(r, addr_space)
+ printk(BIOS_DEBUG,
+ "0x%016llx - 0x%016llx size 0x%08llx type %ld\n",
+ range_entry_base(r), range_entry_end(r),
+ range_entry_size(r), range_entry_tag(r));
+}
+
static struct memranges *get_physical_address_space(void)
{
static struct memranges *addr_space;
@@ -163,7 +181,6 @@ static struct memranges *get_physical_address_space(void)
* uncacheable ranges, such as graphics memory, at resource insertion
* time remove uncacheable regions from the cacheable ones. */
if (addr_space == NULL) {
- struct range_entry *r;
unsigned long mask;
unsigned long match;
@@ -193,12 +210,7 @@ static struct memranges *get_physical_address_space(void)
RANGE_TO_PHYS_ADDR(RANGE_4GB),
MTRR_TYPE_UNCACHEABLE);
- printk(BIOS_DEBUG, "MTRR: Physical address space:\n");
- memranges_each_entry(r, addr_space)
- printk(BIOS_DEBUG,
- "0x%016llx - 0x%016llx size 0x%08llx type %ld\n",
- range_entry_base(r), range_entry_end(r),
- range_entry_size(r), range_entry_tag(r));
+ print_physical_address_space(addr_space, NULL);
}
return addr_space;
@@ -380,11 +392,10 @@ struct var_mtrr_state {
static void clear_var_mtrr(int index)
{
- msr_t msr_val;
+ msr_t msr = { .lo = 0, .hi = 0 };
- msr_val = rdmsr(MTRR_PHYS_MASK(index));
- msr_val.lo &= ~MTRR_PHYS_MASK_VALID;
- wrmsr(MTRR_PHYS_MASK(index), msr_val);
+ wrmsr(MTRR_PHYS_BASE(index), msr);
+ wrmsr(MTRR_PHYS_MASK(index), msr);
}
static void prep_var_mtrr(struct var_mtrr_state *var_state,
@@ -817,3 +828,60 @@ void x86_mtrr_check(void)
post_code(0x93);
}
+
+static bool put_back_original_solution;
+
+void mtrr_use_temp_range(uintptr_t begin, size_t size, int type)
+{
+ const struct range_entry *r;
+ const struct memranges *orig;
+ struct var_mtrr_solution sol;
+ struct memranges addr_space;
+ const int above4gb = 1; /* Cover above 4GiB by default. */
+ int address_bits;
+
+ /* Make a copy of the original address space and tweak it with the
+ * provided range. */
+ memranges_init_empty(&addr_space, NULL, 0);
+ orig = get_physical_address_space();
+ memranges_each_entry(r, orig) {
+ unsigned long tag = range_entry_tag(r);
+
+ /* Remove any special tags from original solution. */
+ tag &= ~MTRR_RANGE_UC_USE_HOLE;
+
+ /* Remove any write combining MTRRs from the temporary
+ * solution as it just fragments the address space. */
+ if (tag == MTRR_TYPE_WRCOMB)
+ tag = MTRR_TYPE_UNCACHEABLE;
+
+ memranges_insert(&addr_space, range_entry_base(r),
+ range_entry_size(r), tag);
+ }
+
+ /* Place new range into the address space. */
+ memranges_insert(&addr_space, begin, size, type);
+
+ print_physical_address_space(&addr_space, "TEMPORARY");
+
+ /* Calculate a new solution with the updated address space. */
+ address_bits = cpu_phys_address_size();
+ memset(&sol, 0, sizeof(sol));
+ 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);
+ commit_var_mtrrs(&sol);
+
+ memranges_teardown(&addr_space);
+ put_back_original_solution = true;
+}
+
+static void remove_temp_solution(void *unused)
+{
+ if (put_back_original_solution)
+ commit_var_mtrrs(&mtrr_global_solution);
+}
+
+BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, remove_temp_solution, NULL);
+BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, remove_temp_solution, NULL);