diff options
Diffstat (limited to 'arch/i386/kernel/cpu/mtrr/generic.c')
-rw-r--r-- | arch/i386/kernel/cpu/mtrr/generic.c | 101 |
1 files changed, 71 insertions, 30 deletions
diff --git a/arch/i386/kernel/cpu/mtrr/generic.c b/arch/i386/kernel/cpu/mtrr/generic.c index f77fc53db654..5367e32e0403 100644 --- a/arch/i386/kernel/cpu/mtrr/generic.c +++ b/arch/i386/kernel/cpu/mtrr/generic.c | |||
@@ -20,13 +20,25 @@ struct mtrr_state { | |||
20 | mtrr_type def_type; | 20 | mtrr_type def_type; |
21 | }; | 21 | }; |
22 | 22 | ||
23 | struct fixed_range_block { | ||
24 | int base_msr; /* start address of an MTRR block */ | ||
25 | int ranges; /* number of MTRRs in this block */ | ||
26 | }; | ||
27 | |||
28 | static struct fixed_range_block fixed_range_blocks[] = { | ||
29 | { MTRRfix64K_00000_MSR, 1 }, /* one 64k MTRR */ | ||
30 | { MTRRfix16K_80000_MSR, 2 }, /* two 16k MTRRs */ | ||
31 | { MTRRfix4K_C0000_MSR, 8 }, /* eight 4k MTRRs */ | ||
32 | {} | ||
33 | }; | ||
34 | |||
23 | static unsigned long smp_changes_mask; | 35 | static unsigned long smp_changes_mask; |
24 | static struct mtrr_state mtrr_state = {}; | 36 | static struct mtrr_state mtrr_state = {}; |
25 | 37 | ||
26 | #undef MODULE_PARAM_PREFIX | 38 | #undef MODULE_PARAM_PREFIX |
27 | #define MODULE_PARAM_PREFIX "mtrr." | 39 | #define MODULE_PARAM_PREFIX "mtrr." |
28 | 40 | ||
29 | static __initdata int mtrr_show; | 41 | static int mtrr_show; |
30 | module_param_named(show, mtrr_show, bool, 0); | 42 | module_param_named(show, mtrr_show, bool, 0); |
31 | 43 | ||
32 | /* Get the MSR pair relating to a var range */ | 44 | /* Get the MSR pair relating to a var range */ |
@@ -37,7 +49,7 @@ get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr) | |||
37 | rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); | 49 | rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); |
38 | } | 50 | } |
39 | 51 | ||
40 | static void __init | 52 | static void |
41 | get_fixed_ranges(mtrr_type * frs) | 53 | get_fixed_ranges(mtrr_type * frs) |
42 | { | 54 | { |
43 | unsigned int *p = (unsigned int *) frs; | 55 | unsigned int *p = (unsigned int *) frs; |
@@ -51,12 +63,18 @@ get_fixed_ranges(mtrr_type * frs) | |||
51 | rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]); | 63 | rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]); |
52 | } | 64 | } |
53 | 65 | ||
54 | static void __init print_fixed(unsigned base, unsigned step, const mtrr_type*types) | 66 | void mtrr_save_fixed_ranges(void *info) |
67 | { | ||
68 | get_fixed_ranges(mtrr_state.fixed_ranges); | ||
69 | } | ||
70 | |||
71 | static void __cpuinit print_fixed(unsigned base, unsigned step, const mtrr_type*types) | ||
55 | { | 72 | { |
56 | unsigned i; | 73 | unsigned i; |
57 | 74 | ||
58 | for (i = 0; i < 8; ++i, ++types, base += step) | 75 | for (i = 0; i < 8; ++i, ++types, base += step) |
59 | printk(KERN_INFO "MTRR %05X-%05X %s\n", base, base + step - 1, mtrr_attrib_to_str(*types)); | 76 | printk(KERN_INFO "MTRR %05X-%05X %s\n", |
77 | base, base + step - 1, mtrr_attrib_to_str(*types)); | ||
60 | } | 78 | } |
61 | 79 | ||
62 | /* Grab all of the MTRR state for this CPU into *state */ | 80 | /* Grab all of the MTRR state for this CPU into *state */ |
@@ -147,6 +165,44 @@ void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b) | |||
147 | smp_processor_id(), msr, a, b); | 165 | smp_processor_id(), msr, a, b); |
148 | } | 166 | } |
149 | 167 | ||
168 | /** | ||
169 | * Enable and allow read/write of extended fixed-range MTRR bits on K8 CPUs | ||
170 | * see AMD publication no. 24593, chapter 3.2.1 for more information | ||
171 | */ | ||
172 | static inline void k8_enable_fixed_iorrs(void) | ||
173 | { | ||
174 | unsigned lo, hi; | ||
175 | |||
176 | rdmsr(MSR_K8_SYSCFG, lo, hi); | ||
177 | mtrr_wrmsr(MSR_K8_SYSCFG, lo | ||
178 | | K8_MTRRFIXRANGE_DRAM_ENABLE | ||
179 | | K8_MTRRFIXRANGE_DRAM_MODIFY, hi); | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * Checks and updates an fixed-range MTRR if it differs from the value it | ||
184 | * should have. If K8 extenstions are wanted, update the K8 SYSCFG MSR also. | ||
185 | * see AMD publication no. 24593, chapter 7.8.1, page 233 for more information | ||
186 | * \param msr MSR address of the MTTR which should be checked and updated | ||
187 | * \param changed pointer which indicates whether the MTRR needed to be changed | ||
188 | * \param msrwords pointer to the MSR values which the MSR should have | ||
189 | */ | ||
190 | static void set_fixed_range(int msr, int * changed, unsigned int * msrwords) | ||
191 | { | ||
192 | unsigned lo, hi; | ||
193 | |||
194 | rdmsr(msr, lo, hi); | ||
195 | |||
196 | if (lo != msrwords[0] || hi != msrwords[1]) { | ||
197 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && | ||
198 | boot_cpu_data.x86 == 15 && | ||
199 | ((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK)) | ||
200 | k8_enable_fixed_iorrs(); | ||
201 | mtrr_wrmsr(msr, msrwords[0], msrwords[1]); | ||
202 | *changed = TRUE; | ||
203 | } | ||
204 | } | ||
205 | |||
150 | int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg) | 206 | int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg) |
151 | /* [SUMMARY] Get a free MTRR. | 207 | /* [SUMMARY] Get a free MTRR. |
152 | <base> The starting (base) address of the region. | 208 | <base> The starting (base) address of the region. |
@@ -196,36 +252,21 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, | |||
196 | *type = base_lo & 0xff; | 252 | *type = base_lo & 0xff; |
197 | } | 253 | } |
198 | 254 | ||
255 | /** | ||
256 | * Checks and updates the fixed-range MTRRs if they differ from the saved set | ||
257 | * \param frs pointer to fixed-range MTRR values, saved by get_fixed_ranges() | ||
258 | */ | ||
199 | static int set_fixed_ranges(mtrr_type * frs) | 259 | static int set_fixed_ranges(mtrr_type * frs) |
200 | { | 260 | { |
201 | unsigned int *p = (unsigned int *) frs; | 261 | unsigned long long *saved = (unsigned long long *) frs; |
202 | int changed = FALSE; | 262 | int changed = FALSE; |
203 | int i; | 263 | int block=-1, range; |
204 | unsigned int lo, hi; | ||
205 | 264 | ||
206 | rdmsr(MTRRfix64K_00000_MSR, lo, hi); | 265 | while (fixed_range_blocks[++block].ranges) |
207 | if (p[0] != lo || p[1] != hi) { | 266 | for (range=0; range < fixed_range_blocks[block].ranges; range++) |
208 | mtrr_wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]); | 267 | set_fixed_range(fixed_range_blocks[block].base_msr + range, |
209 | changed = TRUE; | 268 | &changed, (unsigned int *) saved++); |
210 | } | ||
211 | 269 | ||
212 | for (i = 0; i < 2; i++) { | ||
213 | rdmsr(MTRRfix16K_80000_MSR + i, lo, hi); | ||
214 | if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) { | ||
215 | mtrr_wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2], | ||
216 | p[3 + i * 2]); | ||
217 | changed = TRUE; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | for (i = 0; i < 8; i++) { | ||
222 | rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi); | ||
223 | if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) { | ||
224 | mtrr_wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], | ||
225 | p[7 + i * 2]); | ||
226 | changed = TRUE; | ||
227 | } | ||
228 | } | ||
229 | return changed; | 270 | return changed; |
230 | } | 271 | } |
231 | 272 | ||
@@ -428,7 +469,7 @@ int generic_validate_add_page(unsigned long base, unsigned long size, unsigned i | |||
428 | } | 469 | } |
429 | } | 470 | } |
430 | 471 | ||
431 | if (base + size < 0x100) { | 472 | if (base < 0x100) { |
432 | printk(KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", | 473 | printk(KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", |
433 | base, size); | 474 | base, size); |
434 | return -EINVAL; | 475 | return -EINVAL; |