1
2
3#include <linux/init.h>
4#include <linux/slab.h>
5#include <linux/mm.h>
6#include <asm/io.h>
7#include <asm/mtrr.h>
8#include <asm/msr.h>
9#include <asm/system.h>
10#include <asm/cpufeature.h>
11#include <asm/tlbflush.h>
12#include "mtrr.h"
13
14struct mtrr_state {
15 struct mtrr_var_range *var_ranges;
16 mtrr_type fixed_ranges[NUM_FIXED_RANGES];
17 unsigned char enabled;
18 mtrr_type def_type;
19};
20
21static unsigned long smp_changes_mask;
22static struct mtrr_state mtrr_state = {};
23
24
25static void __init
26get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr)
27{
28 rdmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
29 rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
30}
31
32static void __init
33get_fixed_ranges(mtrr_type * frs)
34{
35 unsigned int *p = (unsigned int *) frs;
36 int i;
37
38 rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
39
40 for (i = 0; i < 2; i++)
41 rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]);
42 for (i = 0; i < 8; i++)
43 rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]);
44}
45
46
47void __init get_mtrr_state(void)
48{
49 unsigned int i;
50 struct mtrr_var_range *vrs;
51 unsigned lo, dummy;
52
53 if (!mtrr_state.var_ranges) {
54 mtrr_state.var_ranges = kmalloc(num_var_ranges * sizeof (struct mtrr_var_range),
55 GFP_KERNEL);
56 if (!mtrr_state.var_ranges)
57 return;
58 }
59 vrs = mtrr_state.var_ranges;
60
61 for (i = 0; i < num_var_ranges; i++)
62 get_mtrr_var_range(i, &vrs[i]);
63 get_fixed_ranges(mtrr_state.fixed_ranges);
64
65 rdmsr(MTRRdefType_MSR, lo, dummy);
66 mtrr_state.def_type = (lo & 0xff);
67 mtrr_state.enabled = (lo & 0xc00) >> 10;
68}
69
70
71void __init finalize_mtrr_state(void)
72{
73 if (mtrr_state.var_ranges)
74 kfree(mtrr_state.var_ranges);
75 mtrr_state.var_ranges = NULL;
76}
77
78
79void __init mtrr_state_warn(void)
80{
81 unsigned long mask = smp_changes_mask;
82
83 if (!mask)
84 return;
85 if (mask & MTRR_CHANGE_MASK_FIXED)
86 printk(KERN_WARNING "mtrr: your CPUs had inconsistent fixed MTRR settings\n");
87 if (mask & MTRR_CHANGE_MASK_VARIABLE)
88 printk(KERN_WARNING "mtrr: your CPUs had inconsistent variable MTRR settings\n");
89 if (mask & MTRR_CHANGE_MASK_DEFTYPE)
90 printk(KERN_WARNING "mtrr: your CPUs had inconsistent MTRRdefType settings\n");
91 printk(KERN_INFO "mtrr: probably your BIOS does not setup all CPUs.\n");
92 printk(KERN_INFO "mtrr: corrected configuration.\n");
93}
94
95
96
97
98void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b)
99{
100 if (wrmsr_safe(msr, a, b) < 0)
101 printk(KERN_ERR
102 "MTRR: CPU %u: Writing MSR %x to %x:%x failed\n",
103 smp_processor_id(), msr, a, b);
104}
105
106int generic_get_free_region(unsigned long base, unsigned long size)
107
108
109
110
111
112{
113 int i, max;
114 mtrr_type ltype;
115 unsigned long lbase;
116 unsigned lsize;
117
118 max = num_var_ranges;
119 for (i = 0; i < max; ++i) {
120 mtrr_if->get(i, &lbase, &lsize, <ype);
121 if (lsize == 0)
122 return i;
123 }
124 return -ENOSPC;
125}
126
127static void generic_get_mtrr(unsigned int reg, unsigned long *base,
128 unsigned int *size, mtrr_type * type)
129{
130 unsigned int mask_lo, mask_hi, base_lo, base_hi;
131
132 rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi);
133 if ((mask_lo & 0x800) == 0) {
134
135 *base = 0;
136 *size = 0;
137 *type = 0;
138 return;
139 }
140
141 rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi);
142
143
144 mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT)
145 | mask_lo >> PAGE_SHIFT;
146
147
148
149 *size = -mask_lo;
150 *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
151 *type = base_lo & 0xff;
152}
153
154static int set_fixed_ranges(mtrr_type * frs)
155{
156 unsigned int *p = (unsigned int *) frs;
157 int changed = FALSE;
158 int i;
159 unsigned int lo, hi;
160
161 rdmsr(MTRRfix64K_00000_MSR, lo, hi);
162 if (p[0] != lo || p[1] != hi) {
163 mtrr_wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
164 changed = TRUE;
165 }
166
167 for (i = 0; i < 2; i++) {
168 rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
169 if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) {
170 mtrr_wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2],
171 p[3 + i * 2]);
172 changed = TRUE;
173 }
174 }
175
176 for (i = 0; i < 8; i++) {
177 rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
178 if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) {
179 mtrr_wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2],
180 p[7 + i * 2]);
181 changed = TRUE;
182 }
183 }
184 return changed;
185}
186
187
188
189static int set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
190{
191 unsigned int lo, hi;
192 int changed = FALSE;
193
194 rdmsr(MTRRphysBase_MSR(index), lo, hi);
195 if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
196 || (vr->base_hi & (size_and_mask >> (32 - PAGE_SHIFT))) !=
197 (hi & (size_and_mask >> (32 - PAGE_SHIFT)))) {
198 mtrr_wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
199 changed = TRUE;
200 }
201
202 rdmsr(MTRRphysMask_MSR(index), lo, hi);
203
204 if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
205 || (vr->mask_hi & (size_and_mask >> (32 - PAGE_SHIFT))) !=
206 (hi & (size_and_mask >> (32 - PAGE_SHIFT)))) {
207 mtrr_wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
208 changed = TRUE;
209 }
210 return changed;
211}
212
213static unsigned long set_mtrr_state(u32 deftype_lo, u32 deftype_hi)
214
215
216
217
218
219
220{
221 unsigned int i;
222 unsigned long change_mask = 0;
223
224 for (i = 0; i < num_var_ranges; i++)
225 if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i]))
226 change_mask |= MTRR_CHANGE_MASK_VARIABLE;
227
228 if (set_fixed_ranges(mtrr_state.fixed_ranges))
229 change_mask |= MTRR_CHANGE_MASK_FIXED;
230
231
232
233 if ((deftype_lo & 0xff) != mtrr_state.def_type
234 || ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) {
235 deftype_lo |= (mtrr_state.def_type | mtrr_state.enabled << 10);
236 change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
237 }
238
239 return change_mask;
240}
241
242
243static unsigned long cr4 = 0;
244static u32 deftype_lo, deftype_hi;
245static DEFINE_SPINLOCK(set_atomicity_lock);
246
247
248
249
250
251
252
253
254static void prepare_set(void)
255{
256 unsigned long cr0;
257
258
259
260
261
262 spin_lock(&set_atomicity_lock);
263
264
265 cr0 = read_cr0() | 0x40000000;
266 write_cr0(cr0);
267 wbinvd();
268
269
270 if ( cpu_has_pge ) {
271 cr4 = read_cr4();
272 write_cr4(cr4 & ~X86_CR4_PGE);
273 }
274
275
276 __flush_tlb();
277
278
279 rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);
280
281
282 mtrr_wrmsr(MTRRdefType_MSR, deftype_lo & 0xf300UL, deftype_hi);
283}
284
285static void post_set(void)
286{
287
288 __flush_tlb();
289
290
291 mtrr_wrmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);
292
293
294 write_cr0(read_cr0() & 0xbfffffff);
295
296
297 if ( cpu_has_pge )
298 write_cr4(cr4);
299 spin_unlock(&set_atomicity_lock);
300}
301
302static void generic_set_all(void)
303{
304 unsigned long mask, count;
305 unsigned long flags;
306
307 local_irq_save(flags);
308 prepare_set();
309
310
311 mask = set_mtrr_state(deftype_lo,deftype_hi);
312
313 post_set();
314 local_irq_restore(flags);
315
316
317 for (count = 0; count < sizeof mask * 8; ++count) {
318 if (mask & 0x01)
319 set_bit(count, &smp_changes_mask);
320 mask >>= 1;
321 }
322
323}
324
325static void generic_set_mtrr(unsigned int reg, unsigned long base,
326 unsigned long size, mtrr_type type)
327
328
329
330
331
332
333
334
335
336{
337 unsigned long flags;
338
339 local_irq_save(flags);
340 prepare_set();
341
342 if (size == 0) {
343
344
345 mtrr_wrmsr(MTRRphysMask_MSR(reg), 0, 0);
346 } else {
347 mtrr_wrmsr(MTRRphysBase_MSR(reg), base << PAGE_SHIFT | type,
348 (base & size_and_mask) >> (32 - PAGE_SHIFT));
349 mtrr_wrmsr(MTRRphysMask_MSR(reg), -size << PAGE_SHIFT | 0x800,
350 (-size & size_and_mask) >> (32 - PAGE_SHIFT));
351 }
352
353 post_set();
354 local_irq_restore(flags);
355}
356
357int generic_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
358{
359 unsigned long lbase, last;
360
361
362
363 if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 &&
364 boot_cpu_data.x86_model == 1 &&
365 boot_cpu_data.x86_mask <= 7) {
366 if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) {
367 printk(KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base);
368 return -EINVAL;
369 }
370 if (!(base + size < 0x70000000 || base > 0x7003FFFF) &&
371 (type == MTRR_TYPE_WRCOMB
372 || type == MTRR_TYPE_WRBACK)) {
373 printk(KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n");
374 return -EINVAL;
375 }
376 }
377
378 if (base + size < 0x100) {
379 printk(KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n",
380 base, size);
381 return -EINVAL;
382 }
383
384
385 last = base + size - 1;
386 for (lbase = base; !(lbase & 1) && (last & 1);
387 lbase = lbase >> 1, last = last >> 1) ;
388 if (lbase != last) {
389 printk(KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n",
390 base, size);
391 return -EINVAL;
392 }
393 return 0;
394}
395
396
397static int generic_have_wrcomb(void)
398{
399 unsigned long config, dummy;
400 rdmsr(MTRRcap_MSR, config, dummy);
401 return (config & (1 << 10));
402}
403
404int positive_have_wrcomb(void)
405{
406 return 1;
407}
408
409
410
411struct mtrr_ops generic_mtrr_ops = {
412 .use_intel_if = 1,
413 .set_all = generic_set_all,
414 .get = generic_get_mtrr,
415 .get_free_region = generic_get_free_region,
416 .set = generic_set_mtrr,
417 .validate_add_page = generic_validate_add_page,
418 .have_wrcomb = generic_have_wrcomb,
419};
420