|
@@ -20,6 +20,18 @@ struct mtrr_state {
|
|
|
mtrr_type def_type;
|
|
|
};
|
|
|
|
|
|
+struct fixed_range_block {
|
|
|
+ int base_msr; /* start address of an MTRR block */
|
|
|
+ int ranges; /* number of MTRRs in this block */
|
|
|
+};
|
|
|
+
|
|
|
+static struct fixed_range_block fixed_range_blocks[] = {
|
|
|
+ { MTRRfix64K_00000_MSR, 1 }, /* one 64k MTRR */
|
|
|
+ { MTRRfix16K_80000_MSR, 2 }, /* two 16k MTRRs */
|
|
|
+ { MTRRfix4K_C0000_MSR, 8 }, /* eight 4k MTRRs */
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
static unsigned long smp_changes_mask;
|
|
|
static struct mtrr_state mtrr_state = {};
|
|
|
|
|
@@ -152,6 +164,44 @@ void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b)
|
|
|
smp_processor_id(), msr, a, b);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Enable and allow read/write of extended fixed-range MTRR bits on K8 CPUs
|
|
|
+ * see AMD publication no. 24593, chapter 3.2.1 for more information
|
|
|
+ */
|
|
|
+static inline void k8_enable_fixed_iorrs(void)
|
|
|
+{
|
|
|
+ unsigned lo, hi;
|
|
|
+
|
|
|
+ rdmsr(MSR_K8_SYSCFG, lo, hi);
|
|
|
+ mtrr_wrmsr(MSR_K8_SYSCFG, lo
|
|
|
+ | K8_MTRRFIXRANGE_DRAM_ENABLE
|
|
|
+ | K8_MTRRFIXRANGE_DRAM_MODIFY, hi);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Checks and updates an fixed-range MTRR if it differs from the value it
|
|
|
+ * should have. If K8 extenstions are wanted, update the K8 SYSCFG MSR also.
|
|
|
+ * see AMD publication no. 24593, chapter 7.8.1, page 233 for more information
|
|
|
+ * \param msr MSR address of the MTTR which should be checked and updated
|
|
|
+ * \param changed pointer which indicates whether the MTRR needed to be changed
|
|
|
+ * \param msrwords pointer to the MSR values which the MSR should have
|
|
|
+ */
|
|
|
+static void set_fixed_range(int msr, int * changed, unsigned int * msrwords)
|
|
|
+{
|
|
|
+ unsigned lo, hi;
|
|
|
+
|
|
|
+ rdmsr(msr, lo, hi);
|
|
|
+
|
|
|
+ if (lo != msrwords[0] || hi != msrwords[1]) {
|
|
|
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
|
|
|
+ boot_cpu_data.x86 == 15 &&
|
|
|
+ ((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK))
|
|
|
+ k8_enable_fixed_iorrs();
|
|
|
+ mtrr_wrmsr(msr, msrwords[0], msrwords[1]);
|
|
|
+ *changed = TRUE;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg)
|
|
|
/* [SUMMARY] Get a free MTRR.
|
|
|
<base> The starting (base) address of the region.
|
|
@@ -201,36 +251,21 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
|
|
|
*type = base_lo & 0xff;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Checks and updates the fixed-range MTRRs if they differ from the saved set
|
|
|
+ * \param frs pointer to fixed-range MTRR values, saved by get_fixed_ranges()
|
|
|
+ */
|
|
|
static int set_fixed_ranges(mtrr_type * frs)
|
|
|
{
|
|
|
- unsigned int *p = (unsigned int *) frs;
|
|
|
+ unsigned long long *saved = (unsigned long long *) frs;
|
|
|
int changed = FALSE;
|
|
|
- int i;
|
|
|
- unsigned int lo, hi;
|
|
|
+ int block=-1, range;
|
|
|
|
|
|
- rdmsr(MTRRfix64K_00000_MSR, lo, hi);
|
|
|
- if (p[0] != lo || p[1] != hi) {
|
|
|
- mtrr_wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
|
|
|
- changed = TRUE;
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
- rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
|
|
|
- if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) {
|
|
|
- mtrr_wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2],
|
|
|
- p[3 + i * 2]);
|
|
|
- changed = TRUE;
|
|
|
- }
|
|
|
- }
|
|
|
+ while (fixed_range_blocks[++block].ranges)
|
|
|
+ for (range=0; range < fixed_range_blocks[block].ranges; range++)
|
|
|
+ set_fixed_range(fixed_range_blocks[block].base_msr + range,
|
|
|
+ &changed, (unsigned int *) saved++);
|
|
|
|
|
|
- for (i = 0; i < 8; i++) {
|
|
|
- rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
|
|
|
- if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) {
|
|
|
- mtrr_wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2],
|
|
|
- p[7 + i * 2]);
|
|
|
- changed = TRUE;
|
|
|
- }
|
|
|
- }
|
|
|
return changed;
|
|
|
}
|
|
|
|