diff options
Diffstat (limited to 'arch/x86/kernel/cpu/mtrr')
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/cleanup.c | 1101 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/generic.c | 199 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/if.c | 10 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/main.c | 1069 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/mtrr.h | 4 |
6 files changed, 1245 insertions, 1140 deletions
diff --git a/arch/x86/kernel/cpu/mtrr/Makefile b/arch/x86/kernel/cpu/mtrr/Makefile index 191fc0533649..f4361b56f8e9 100644 --- a/arch/x86/kernel/cpu/mtrr/Makefile +++ b/arch/x86/kernel/cpu/mtrr/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | obj-y := main.o if.o generic.o state.o | 1 | obj-y := main.o if.o generic.o state.o cleanup.o |
2 | obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o | 2 | obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o |
3 | 3 | ||
diff --git a/arch/x86/kernel/cpu/mtrr/cleanup.c b/arch/x86/kernel/cpu/mtrr/cleanup.c new file mode 100644 index 000000000000..ce0fe4b5c04f --- /dev/null +++ b/arch/x86/kernel/cpu/mtrr/cleanup.c | |||
@@ -0,0 +1,1101 @@ | |||
1 | /* MTRR (Memory Type Range Register) cleanup | ||
2 | |||
3 | Copyright (C) 2009 Yinghai Lu | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Library General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Library General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Library General Public | ||
16 | License along with this library; if not, write to the Free | ||
17 | Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/smp.h> | ||
24 | #include <linux/cpu.h> | ||
25 | #include <linux/mutex.h> | ||
26 | #include <linux/sort.h> | ||
27 | |||
28 | #include <asm/e820.h> | ||
29 | #include <asm/mtrr.h> | ||
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/processor.h> | ||
32 | #include <asm/msr.h> | ||
33 | #include <asm/kvm_para.h> | ||
34 | #include "mtrr.h" | ||
35 | |||
36 | /* should be related to MTRR_VAR_RANGES nums */ | ||
37 | #define RANGE_NUM 256 | ||
38 | |||
39 | struct res_range { | ||
40 | unsigned long start; | ||
41 | unsigned long end; | ||
42 | }; | ||
43 | |||
44 | static int __init | ||
45 | add_range(struct res_range *range, int nr_range, unsigned long start, | ||
46 | unsigned long end) | ||
47 | { | ||
48 | /* out of slots */ | ||
49 | if (nr_range >= RANGE_NUM) | ||
50 | return nr_range; | ||
51 | |||
52 | range[nr_range].start = start; | ||
53 | range[nr_range].end = end; | ||
54 | |||
55 | nr_range++; | ||
56 | |||
57 | return nr_range; | ||
58 | } | ||
59 | |||
60 | static int __init | ||
61 | add_range_with_merge(struct res_range *range, int nr_range, unsigned long start, | ||
62 | unsigned long end) | ||
63 | { | ||
64 | int i; | ||
65 | |||
66 | /* try to merge it with old one */ | ||
67 | for (i = 0; i < nr_range; i++) { | ||
68 | unsigned long final_start, final_end; | ||
69 | unsigned long common_start, common_end; | ||
70 | |||
71 | if (!range[i].end) | ||
72 | continue; | ||
73 | |||
74 | common_start = max(range[i].start, start); | ||
75 | common_end = min(range[i].end, end); | ||
76 | if (common_start > common_end + 1) | ||
77 | continue; | ||
78 | |||
79 | final_start = min(range[i].start, start); | ||
80 | final_end = max(range[i].end, end); | ||
81 | |||
82 | range[i].start = final_start; | ||
83 | range[i].end = final_end; | ||
84 | return nr_range; | ||
85 | } | ||
86 | |||
87 | /* need to add that */ | ||
88 | return add_range(range, nr_range, start, end); | ||
89 | } | ||
90 | |||
91 | static void __init | ||
92 | subtract_range(struct res_range *range, unsigned long start, unsigned long end) | ||
93 | { | ||
94 | int i, j; | ||
95 | |||
96 | for (j = 0; j < RANGE_NUM; j++) { | ||
97 | if (!range[j].end) | ||
98 | continue; | ||
99 | |||
100 | if (start <= range[j].start && end >= range[j].end) { | ||
101 | range[j].start = 0; | ||
102 | range[j].end = 0; | ||
103 | continue; | ||
104 | } | ||
105 | |||
106 | if (start <= range[j].start && end < range[j].end && | ||
107 | range[j].start < end + 1) { | ||
108 | range[j].start = end + 1; | ||
109 | continue; | ||
110 | } | ||
111 | |||
112 | |||
113 | if (start > range[j].start && end >= range[j].end && | ||
114 | range[j].end > start - 1) { | ||
115 | range[j].end = start - 1; | ||
116 | continue; | ||
117 | } | ||
118 | |||
119 | if (start > range[j].start && end < range[j].end) { | ||
120 | /* find the new spare */ | ||
121 | for (i = 0; i < RANGE_NUM; i++) { | ||
122 | if (range[i].end == 0) | ||
123 | break; | ||
124 | } | ||
125 | if (i < RANGE_NUM) { | ||
126 | range[i].end = range[j].end; | ||
127 | range[i].start = end + 1; | ||
128 | } else { | ||
129 | printk(KERN_ERR "run of slot in ranges\n"); | ||
130 | } | ||
131 | range[j].end = start - 1; | ||
132 | continue; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static int __init cmp_range(const void *x1, const void *x2) | ||
138 | { | ||
139 | const struct res_range *r1 = x1; | ||
140 | const struct res_range *r2 = x2; | ||
141 | long start1, start2; | ||
142 | |||
143 | start1 = r1->start; | ||
144 | start2 = r2->start; | ||
145 | |||
146 | return start1 - start2; | ||
147 | } | ||
148 | |||
149 | struct var_mtrr_range_state { | ||
150 | unsigned long base_pfn; | ||
151 | unsigned long size_pfn; | ||
152 | mtrr_type type; | ||
153 | }; | ||
154 | |||
155 | static struct var_mtrr_range_state __initdata range_state[RANGE_NUM]; | ||
156 | static int __initdata debug_print; | ||
157 | |||
158 | static int __init | ||
159 | x86_get_mtrr_mem_range(struct res_range *range, int nr_range, | ||
160 | unsigned long extra_remove_base, | ||
161 | unsigned long extra_remove_size) | ||
162 | { | ||
163 | unsigned long base, size; | ||
164 | mtrr_type type; | ||
165 | int i; | ||
166 | |||
167 | for (i = 0; i < num_var_ranges; i++) { | ||
168 | type = range_state[i].type; | ||
169 | if (type != MTRR_TYPE_WRBACK) | ||
170 | continue; | ||
171 | base = range_state[i].base_pfn; | ||
172 | size = range_state[i].size_pfn; | ||
173 | nr_range = add_range_with_merge(range, nr_range, base, | ||
174 | base + size - 1); | ||
175 | } | ||
176 | if (debug_print) { | ||
177 | printk(KERN_DEBUG "After WB checking\n"); | ||
178 | for (i = 0; i < nr_range; i++) | ||
179 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
180 | range[i].start, range[i].end + 1); | ||
181 | } | ||
182 | |||
183 | /* take out UC ranges */ | ||
184 | for (i = 0; i < num_var_ranges; i++) { | ||
185 | type = range_state[i].type; | ||
186 | if (type != MTRR_TYPE_UNCACHABLE && | ||
187 | type != MTRR_TYPE_WRPROT) | ||
188 | continue; | ||
189 | size = range_state[i].size_pfn; | ||
190 | if (!size) | ||
191 | continue; | ||
192 | base = range_state[i].base_pfn; | ||
193 | if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed && | ||
194 | (mtrr_state.enabled & 1)) { | ||
195 | /* Var MTRR contains UC entry below 1M? Skip it: */ | ||
196 | printk(KERN_WARNING "WARNING: BIOS bug: VAR MTRR %d " | ||
197 | "contains strange UC entry under 1M, check " | ||
198 | "with your system vendor!\n", i); | ||
199 | if (base + size <= (1<<(20-PAGE_SHIFT))) | ||
200 | continue; | ||
201 | size -= (1<<(20-PAGE_SHIFT)) - base; | ||
202 | base = 1<<(20-PAGE_SHIFT); | ||
203 | } | ||
204 | subtract_range(range, base, base + size - 1); | ||
205 | } | ||
206 | if (extra_remove_size) | ||
207 | subtract_range(range, extra_remove_base, | ||
208 | extra_remove_base + extra_remove_size - 1); | ||
209 | |||
210 | /* get new range num */ | ||
211 | nr_range = 0; | ||
212 | for (i = 0; i < RANGE_NUM; i++) { | ||
213 | if (!range[i].end) | ||
214 | continue; | ||
215 | nr_range++; | ||
216 | } | ||
217 | if (debug_print) { | ||
218 | printk(KERN_DEBUG "After UC checking\n"); | ||
219 | for (i = 0; i < nr_range; i++) | ||
220 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
221 | range[i].start, range[i].end + 1); | ||
222 | } | ||
223 | |||
224 | /* sort the ranges */ | ||
225 | sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL); | ||
226 | if (debug_print) { | ||
227 | printk(KERN_DEBUG "After sorting\n"); | ||
228 | for (i = 0; i < nr_range; i++) | ||
229 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
230 | range[i].start, range[i].end + 1); | ||
231 | } | ||
232 | |||
233 | /* clear those is not used */ | ||
234 | for (i = nr_range; i < RANGE_NUM; i++) | ||
235 | memset(&range[i], 0, sizeof(range[i])); | ||
236 | |||
237 | return nr_range; | ||
238 | } | ||
239 | |||
240 | static struct res_range __initdata range[RANGE_NUM]; | ||
241 | static int __initdata nr_range; | ||
242 | |||
243 | #ifdef CONFIG_MTRR_SANITIZER | ||
244 | |||
245 | static unsigned long __init sum_ranges(struct res_range *range, int nr_range) | ||
246 | { | ||
247 | unsigned long sum; | ||
248 | int i; | ||
249 | |||
250 | sum = 0; | ||
251 | for (i = 0; i < nr_range; i++) | ||
252 | sum += range[i].end + 1 - range[i].start; | ||
253 | |||
254 | return sum; | ||
255 | } | ||
256 | |||
257 | static int enable_mtrr_cleanup __initdata = | ||
258 | CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT; | ||
259 | |||
260 | static int __init disable_mtrr_cleanup_setup(char *str) | ||
261 | { | ||
262 | enable_mtrr_cleanup = 0; | ||
263 | return 0; | ||
264 | } | ||
265 | early_param("disable_mtrr_cleanup", disable_mtrr_cleanup_setup); | ||
266 | |||
267 | static int __init enable_mtrr_cleanup_setup(char *str) | ||
268 | { | ||
269 | enable_mtrr_cleanup = 1; | ||
270 | return 0; | ||
271 | } | ||
272 | early_param("enable_mtrr_cleanup", enable_mtrr_cleanup_setup); | ||
273 | |||
274 | static int __init mtrr_cleanup_debug_setup(char *str) | ||
275 | { | ||
276 | debug_print = 1; | ||
277 | return 0; | ||
278 | } | ||
279 | early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup); | ||
280 | |||
281 | struct var_mtrr_state { | ||
282 | unsigned long range_startk; | ||
283 | unsigned long range_sizek; | ||
284 | unsigned long chunk_sizek; | ||
285 | unsigned long gran_sizek; | ||
286 | unsigned int reg; | ||
287 | }; | ||
288 | |||
289 | static void __init | ||
290 | set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, | ||
291 | unsigned char type, unsigned int address_bits) | ||
292 | { | ||
293 | u32 base_lo, base_hi, mask_lo, mask_hi; | ||
294 | u64 base, mask; | ||
295 | |||
296 | if (!sizek) { | ||
297 | fill_mtrr_var_range(reg, 0, 0, 0, 0); | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | mask = (1ULL << address_bits) - 1; | ||
302 | mask &= ~((((u64)sizek) << 10) - 1); | ||
303 | |||
304 | base = ((u64)basek) << 10; | ||
305 | |||
306 | base |= type; | ||
307 | mask |= 0x800; | ||
308 | |||
309 | base_lo = base & ((1ULL<<32) - 1); | ||
310 | base_hi = base >> 32; | ||
311 | |||
312 | mask_lo = mask & ((1ULL<<32) - 1); | ||
313 | mask_hi = mask >> 32; | ||
314 | |||
315 | fill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi); | ||
316 | } | ||
317 | |||
318 | static void __init | ||
319 | save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, | ||
320 | unsigned char type) | ||
321 | { | ||
322 | range_state[reg].base_pfn = basek >> (PAGE_SHIFT - 10); | ||
323 | range_state[reg].size_pfn = sizek >> (PAGE_SHIFT - 10); | ||
324 | range_state[reg].type = type; | ||
325 | } | ||
326 | |||
327 | static void __init | ||
328 | set_var_mtrr_all(unsigned int address_bits) | ||
329 | { | ||
330 | unsigned long basek, sizek; | ||
331 | unsigned char type; | ||
332 | unsigned int reg; | ||
333 | |||
334 | for (reg = 0; reg < num_var_ranges; reg++) { | ||
335 | basek = range_state[reg].base_pfn << (PAGE_SHIFT - 10); | ||
336 | sizek = range_state[reg].size_pfn << (PAGE_SHIFT - 10); | ||
337 | type = range_state[reg].type; | ||
338 | |||
339 | set_var_mtrr(reg, basek, sizek, type, address_bits); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | static unsigned long to_size_factor(unsigned long sizek, char *factorp) | ||
344 | { | ||
345 | char factor; | ||
346 | unsigned long base = sizek; | ||
347 | |||
348 | if (base & ((1<<10) - 1)) { | ||
349 | /* not MB alignment */ | ||
350 | factor = 'K'; | ||
351 | } else if (base & ((1<<20) - 1)) { | ||
352 | factor = 'M'; | ||
353 | base >>= 10; | ||
354 | } else { | ||
355 | factor = 'G'; | ||
356 | base >>= 20; | ||
357 | } | ||
358 | |||
359 | *factorp = factor; | ||
360 | |||
361 | return base; | ||
362 | } | ||
363 | |||
364 | static unsigned int __init | ||
365 | range_to_mtrr(unsigned int reg, unsigned long range_startk, | ||
366 | unsigned long range_sizek, unsigned char type) | ||
367 | { | ||
368 | if (!range_sizek || (reg >= num_var_ranges)) | ||
369 | return reg; | ||
370 | |||
371 | while (range_sizek) { | ||
372 | unsigned long max_align, align; | ||
373 | unsigned long sizek; | ||
374 | |||
375 | /* Compute the maximum size I can make a range */ | ||
376 | if (range_startk) | ||
377 | max_align = ffs(range_startk) - 1; | ||
378 | else | ||
379 | max_align = 32; | ||
380 | align = fls(range_sizek) - 1; | ||
381 | if (align > max_align) | ||
382 | align = max_align; | ||
383 | |||
384 | sizek = 1 << align; | ||
385 | if (debug_print) { | ||
386 | char start_factor = 'K', size_factor = 'K'; | ||
387 | unsigned long start_base, size_base; | ||
388 | |||
389 | start_base = to_size_factor(range_startk, | ||
390 | &start_factor), | ||
391 | size_base = to_size_factor(sizek, &size_factor), | ||
392 | |||
393 | printk(KERN_DEBUG "Setting variable MTRR %d, " | ||
394 | "base: %ld%cB, range: %ld%cB, type %s\n", | ||
395 | reg, start_base, start_factor, | ||
396 | size_base, size_factor, | ||
397 | (type == MTRR_TYPE_UNCACHABLE) ? "UC" : | ||
398 | ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other") | ||
399 | ); | ||
400 | } | ||
401 | save_var_mtrr(reg++, range_startk, sizek, type); | ||
402 | range_startk += sizek; | ||
403 | range_sizek -= sizek; | ||
404 | if (reg >= num_var_ranges) | ||
405 | break; | ||
406 | } | ||
407 | return reg; | ||
408 | } | ||
409 | |||
410 | static unsigned __init | ||
411 | range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, | ||
412 | unsigned long sizek) | ||
413 | { | ||
414 | unsigned long hole_basek, hole_sizek; | ||
415 | unsigned long second_basek, second_sizek; | ||
416 | unsigned long range0_basek, range0_sizek; | ||
417 | unsigned long range_basek, range_sizek; | ||
418 | unsigned long chunk_sizek; | ||
419 | unsigned long gran_sizek; | ||
420 | |||
421 | hole_basek = 0; | ||
422 | hole_sizek = 0; | ||
423 | second_basek = 0; | ||
424 | second_sizek = 0; | ||
425 | chunk_sizek = state->chunk_sizek; | ||
426 | gran_sizek = state->gran_sizek; | ||
427 | |||
428 | /* align with gran size, prevent small block used up MTRRs */ | ||
429 | range_basek = ALIGN(state->range_startk, gran_sizek); | ||
430 | if ((range_basek > basek) && basek) | ||
431 | return second_sizek; | ||
432 | state->range_sizek -= (range_basek - state->range_startk); | ||
433 | range_sizek = ALIGN(state->range_sizek, gran_sizek); | ||
434 | |||
435 | while (range_sizek > state->range_sizek) { | ||
436 | range_sizek -= gran_sizek; | ||
437 | if (!range_sizek) | ||
438 | return 0; | ||
439 | } | ||
440 | state->range_sizek = range_sizek; | ||
441 | |||
442 | /* try to append some small hole */ | ||
443 | range0_basek = state->range_startk; | ||
444 | range0_sizek = ALIGN(state->range_sizek, chunk_sizek); | ||
445 | |||
446 | /* no increase */ | ||
447 | if (range0_sizek == state->range_sizek) { | ||
448 | if (debug_print) | ||
449 | printk(KERN_DEBUG "rangeX: %016lx - %016lx\n", | ||
450 | range0_basek<<10, | ||
451 | (range0_basek + state->range_sizek)<<10); | ||
452 | state->reg = range_to_mtrr(state->reg, range0_basek, | ||
453 | state->range_sizek, MTRR_TYPE_WRBACK); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | /* only cut back, when it is not the last */ | ||
458 | if (sizek) { | ||
459 | while (range0_basek + range0_sizek > (basek + sizek)) { | ||
460 | if (range0_sizek >= chunk_sizek) | ||
461 | range0_sizek -= chunk_sizek; | ||
462 | else | ||
463 | range0_sizek = 0; | ||
464 | |||
465 | if (!range0_sizek) | ||
466 | break; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | second_try: | ||
471 | range_basek = range0_basek + range0_sizek; | ||
472 | |||
473 | /* one hole in the middle */ | ||
474 | if (range_basek > basek && range_basek <= (basek + sizek)) | ||
475 | second_sizek = range_basek - basek; | ||
476 | |||
477 | if (range0_sizek > state->range_sizek) { | ||
478 | |||
479 | /* one hole in middle or at end */ | ||
480 | hole_sizek = range0_sizek - state->range_sizek - second_sizek; | ||
481 | |||
482 | /* hole size should be less than half of range0 size */ | ||
483 | if (hole_sizek >= (range0_sizek >> 1) && | ||
484 | range0_sizek >= chunk_sizek) { | ||
485 | range0_sizek -= chunk_sizek; | ||
486 | second_sizek = 0; | ||
487 | hole_sizek = 0; | ||
488 | |||
489 | goto second_try; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | if (range0_sizek) { | ||
494 | if (debug_print) | ||
495 | printk(KERN_DEBUG "range0: %016lx - %016lx\n", | ||
496 | range0_basek<<10, | ||
497 | (range0_basek + range0_sizek)<<10); | ||
498 | state->reg = range_to_mtrr(state->reg, range0_basek, | ||
499 | range0_sizek, MTRR_TYPE_WRBACK); | ||
500 | } | ||
501 | |||
502 | if (range0_sizek < state->range_sizek) { | ||
503 | /* need to handle left over */ | ||
504 | range_sizek = state->range_sizek - range0_sizek; | ||
505 | |||
506 | if (debug_print) | ||
507 | printk(KERN_DEBUG "range: %016lx - %016lx\n", | ||
508 | range_basek<<10, | ||
509 | (range_basek + range_sizek)<<10); | ||
510 | state->reg = range_to_mtrr(state->reg, range_basek, | ||
511 | range_sizek, MTRR_TYPE_WRBACK); | ||
512 | } | ||
513 | |||
514 | if (hole_sizek) { | ||
515 | hole_basek = range_basek - hole_sizek - second_sizek; | ||
516 | if (debug_print) | ||
517 | printk(KERN_DEBUG "hole: %016lx - %016lx\n", | ||
518 | hole_basek<<10, | ||
519 | (hole_basek + hole_sizek)<<10); | ||
520 | state->reg = range_to_mtrr(state->reg, hole_basek, | ||
521 | hole_sizek, MTRR_TYPE_UNCACHABLE); | ||
522 | } | ||
523 | |||
524 | return second_sizek; | ||
525 | } | ||
526 | |||
527 | static void __init | ||
528 | set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn, | ||
529 | unsigned long size_pfn) | ||
530 | { | ||
531 | unsigned long basek, sizek; | ||
532 | unsigned long second_sizek = 0; | ||
533 | |||
534 | if (state->reg >= num_var_ranges) | ||
535 | return; | ||
536 | |||
537 | basek = base_pfn << (PAGE_SHIFT - 10); | ||
538 | sizek = size_pfn << (PAGE_SHIFT - 10); | ||
539 | |||
540 | /* See if I can merge with the last range */ | ||
541 | if ((basek <= 1024) || | ||
542 | (state->range_startk + state->range_sizek == basek)) { | ||
543 | unsigned long endk = basek + sizek; | ||
544 | state->range_sizek = endk - state->range_startk; | ||
545 | return; | ||
546 | } | ||
547 | /* Write the range mtrrs */ | ||
548 | if (state->range_sizek != 0) | ||
549 | second_sizek = range_to_mtrr_with_hole(state, basek, sizek); | ||
550 | |||
551 | /* Allocate an msr */ | ||
552 | state->range_startk = basek + second_sizek; | ||
553 | state->range_sizek = sizek - second_sizek; | ||
554 | } | ||
555 | |||
556 | /* mininum size of mtrr block that can take hole */ | ||
557 | static u64 mtrr_chunk_size __initdata = (256ULL<<20); | ||
558 | |||
559 | static int __init parse_mtrr_chunk_size_opt(char *p) | ||
560 | { | ||
561 | if (!p) | ||
562 | return -EINVAL; | ||
563 | mtrr_chunk_size = memparse(p, &p); | ||
564 | return 0; | ||
565 | } | ||
566 | early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt); | ||
567 | |||
568 | /* granity of mtrr of block */ | ||
569 | static u64 mtrr_gran_size __initdata; | ||
570 | |||
571 | static int __init parse_mtrr_gran_size_opt(char *p) | ||
572 | { | ||
573 | if (!p) | ||
574 | return -EINVAL; | ||
575 | mtrr_gran_size = memparse(p, &p); | ||
576 | return 0; | ||
577 | } | ||
578 | early_param("mtrr_gran_size", parse_mtrr_gran_size_opt); | ||
579 | |||
580 | static int nr_mtrr_spare_reg __initdata = | ||
581 | CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT; | ||
582 | |||
583 | static int __init parse_mtrr_spare_reg(char *arg) | ||
584 | { | ||
585 | if (arg) | ||
586 | nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0); | ||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg); | ||
591 | |||
592 | static int __init | ||
593 | x86_setup_var_mtrrs(struct res_range *range, int nr_range, | ||
594 | u64 chunk_size, u64 gran_size) | ||
595 | { | ||
596 | struct var_mtrr_state var_state; | ||
597 | int i; | ||
598 | int num_reg; | ||
599 | |||
600 | var_state.range_startk = 0; | ||
601 | var_state.range_sizek = 0; | ||
602 | var_state.reg = 0; | ||
603 | var_state.chunk_sizek = chunk_size >> 10; | ||
604 | var_state.gran_sizek = gran_size >> 10; | ||
605 | |||
606 | memset(range_state, 0, sizeof(range_state)); | ||
607 | |||
608 | /* Write the range etc */ | ||
609 | for (i = 0; i < nr_range; i++) | ||
610 | set_var_mtrr_range(&var_state, range[i].start, | ||
611 | range[i].end - range[i].start + 1); | ||
612 | |||
613 | /* Write the last range */ | ||
614 | if (var_state.range_sizek != 0) | ||
615 | range_to_mtrr_with_hole(&var_state, 0, 0); | ||
616 | |||
617 | num_reg = var_state.reg; | ||
618 | /* Clear out the extra MTRR's */ | ||
619 | while (var_state.reg < num_var_ranges) { | ||
620 | save_var_mtrr(var_state.reg, 0, 0, 0); | ||
621 | var_state.reg++; | ||
622 | } | ||
623 | |||
624 | return num_reg; | ||
625 | } | ||
626 | |||
627 | struct mtrr_cleanup_result { | ||
628 | unsigned long gran_sizek; | ||
629 | unsigned long chunk_sizek; | ||
630 | unsigned long lose_cover_sizek; | ||
631 | unsigned int num_reg; | ||
632 | int bad; | ||
633 | }; | ||
634 | |||
635 | /* | ||
636 | * gran_size: 64K, 128K, 256K, 512K, 1M, 2M, ..., 2G | ||
637 | * chunk size: gran_size, ..., 2G | ||
638 | * so we need (1+16)*8 | ||
639 | */ | ||
640 | #define NUM_RESULT 136 | ||
641 | #define PSHIFT (PAGE_SHIFT - 10) | ||
642 | |||
643 | static struct mtrr_cleanup_result __initdata result[NUM_RESULT]; | ||
644 | static unsigned long __initdata min_loss_pfn[RANGE_NUM]; | ||
645 | |||
646 | static void __init print_out_mtrr_range_state(void) | ||
647 | { | ||
648 | int i; | ||
649 | char start_factor = 'K', size_factor = 'K'; | ||
650 | unsigned long start_base, size_base; | ||
651 | mtrr_type type; | ||
652 | |||
653 | for (i = 0; i < num_var_ranges; i++) { | ||
654 | |||
655 | size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10); | ||
656 | if (!size_base) | ||
657 | continue; | ||
658 | |||
659 | size_base = to_size_factor(size_base, &size_factor), | ||
660 | start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10); | ||
661 | start_base = to_size_factor(start_base, &start_factor), | ||
662 | type = range_state[i].type; | ||
663 | |||
664 | printk(KERN_DEBUG "reg %d, base: %ld%cB, range: %ld%cB, type %s\n", | ||
665 | i, start_base, start_factor, | ||
666 | size_base, size_factor, | ||
667 | (type == MTRR_TYPE_UNCACHABLE) ? "UC" : | ||
668 | ((type == MTRR_TYPE_WRPROT) ? "WP" : | ||
669 | ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other")) | ||
670 | ); | ||
671 | } | ||
672 | } | ||
673 | |||
674 | static int __init mtrr_need_cleanup(void) | ||
675 | { | ||
676 | int i; | ||
677 | mtrr_type type; | ||
678 | unsigned long size; | ||
679 | /* extra one for all 0 */ | ||
680 | int num[MTRR_NUM_TYPES + 1]; | ||
681 | |||
682 | /* check entries number */ | ||
683 | memset(num, 0, sizeof(num)); | ||
684 | for (i = 0; i < num_var_ranges; i++) { | ||
685 | type = range_state[i].type; | ||
686 | size = range_state[i].size_pfn; | ||
687 | if (type >= MTRR_NUM_TYPES) | ||
688 | continue; | ||
689 | if (!size) | ||
690 | type = MTRR_NUM_TYPES; | ||
691 | if (type == MTRR_TYPE_WRPROT) | ||
692 | type = MTRR_TYPE_UNCACHABLE; | ||
693 | num[type]++; | ||
694 | } | ||
695 | |||
696 | /* check if we got UC entries */ | ||
697 | if (!num[MTRR_TYPE_UNCACHABLE]) | ||
698 | return 0; | ||
699 | |||
700 | /* check if we only had WB and UC */ | ||
701 | if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != | ||
702 | num_var_ranges - num[MTRR_NUM_TYPES]) | ||
703 | return 0; | ||
704 | |||
705 | return 1; | ||
706 | } | ||
707 | |||
708 | static unsigned long __initdata range_sums; | ||
709 | static void __init mtrr_calc_range_state(u64 chunk_size, u64 gran_size, | ||
710 | unsigned long extra_remove_base, | ||
711 | unsigned long extra_remove_size, | ||
712 | int i) | ||
713 | { | ||
714 | int num_reg; | ||
715 | static struct res_range range_new[RANGE_NUM]; | ||
716 | static int nr_range_new; | ||
717 | unsigned long range_sums_new; | ||
718 | |||
719 | /* convert ranges to var ranges state */ | ||
720 | num_reg = x86_setup_var_mtrrs(range, nr_range, | ||
721 | chunk_size, gran_size); | ||
722 | |||
723 | /* we got new setting in range_state, check it */ | ||
724 | memset(range_new, 0, sizeof(range_new)); | ||
725 | nr_range_new = x86_get_mtrr_mem_range(range_new, 0, | ||
726 | extra_remove_base, extra_remove_size); | ||
727 | range_sums_new = sum_ranges(range_new, nr_range_new); | ||
728 | |||
729 | result[i].chunk_sizek = chunk_size >> 10; | ||
730 | result[i].gran_sizek = gran_size >> 10; | ||
731 | result[i].num_reg = num_reg; | ||
732 | if (range_sums < range_sums_new) { | ||
733 | result[i].lose_cover_sizek = | ||
734 | (range_sums_new - range_sums) << PSHIFT; | ||
735 | result[i].bad = 1; | ||
736 | } else | ||
737 | result[i].lose_cover_sizek = | ||
738 | (range_sums - range_sums_new) << PSHIFT; | ||
739 | |||
740 | /* double check it */ | ||
741 | if (!result[i].bad && !result[i].lose_cover_sizek) { | ||
742 | if (nr_range_new != nr_range || | ||
743 | memcmp(range, range_new, sizeof(range))) | ||
744 | result[i].bad = 1; | ||
745 | } | ||
746 | |||
747 | if (!result[i].bad && (range_sums - range_sums_new < | ||
748 | min_loss_pfn[num_reg])) { | ||
749 | min_loss_pfn[num_reg] = | ||
750 | range_sums - range_sums_new; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | static void __init mtrr_print_out_one_result(int i) | ||
755 | { | ||
756 | char gran_factor, chunk_factor, lose_factor; | ||
757 | unsigned long gran_base, chunk_base, lose_base; | ||
758 | |||
759 | gran_base = to_size_factor(result[i].gran_sizek, &gran_factor), | ||
760 | chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor), | ||
761 | lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor), | ||
762 | printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t", | ||
763 | result[i].bad ? "*BAD*" : " ", | ||
764 | gran_base, gran_factor, chunk_base, chunk_factor); | ||
765 | printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n", | ||
766 | result[i].num_reg, result[i].bad ? "-" : "", | ||
767 | lose_base, lose_factor); | ||
768 | } | ||
769 | |||
770 | static int __init mtrr_search_optimal_index(void) | ||
771 | { | ||
772 | int i; | ||
773 | int num_reg_good; | ||
774 | int index_good; | ||
775 | |||
776 | if (nr_mtrr_spare_reg >= num_var_ranges) | ||
777 | nr_mtrr_spare_reg = num_var_ranges - 1; | ||
778 | num_reg_good = -1; | ||
779 | for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) { | ||
780 | if (!min_loss_pfn[i]) | ||
781 | num_reg_good = i; | ||
782 | } | ||
783 | |||
784 | index_good = -1; | ||
785 | if (num_reg_good != -1) { | ||
786 | for (i = 0; i < NUM_RESULT; i++) { | ||
787 | if (!result[i].bad && | ||
788 | result[i].num_reg == num_reg_good && | ||
789 | !result[i].lose_cover_sizek) { | ||
790 | index_good = i; | ||
791 | break; | ||
792 | } | ||
793 | } | ||
794 | } | ||
795 | |||
796 | return index_good; | ||
797 | } | ||
798 | |||
799 | |||
800 | int __init mtrr_cleanup(unsigned address_bits) | ||
801 | { | ||
802 | unsigned long extra_remove_base, extra_remove_size; | ||
803 | unsigned long base, size, def, dummy; | ||
804 | mtrr_type type; | ||
805 | u64 chunk_size, gran_size; | ||
806 | int index_good; | ||
807 | int i; | ||
808 | |||
809 | if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1) | ||
810 | return 0; | ||
811 | rdmsr(MTRRdefType_MSR, def, dummy); | ||
812 | def &= 0xff; | ||
813 | if (def != MTRR_TYPE_UNCACHABLE) | ||
814 | return 0; | ||
815 | |||
816 | /* get it and store it aside */ | ||
817 | memset(range_state, 0, sizeof(range_state)); | ||
818 | for (i = 0; i < num_var_ranges; i++) { | ||
819 | mtrr_if->get(i, &base, &size, &type); | ||
820 | range_state[i].base_pfn = base; | ||
821 | range_state[i].size_pfn = size; | ||
822 | range_state[i].type = type; | ||
823 | } | ||
824 | |||
825 | /* check if we need handle it and can handle it */ | ||
826 | if (!mtrr_need_cleanup()) | ||
827 | return 0; | ||
828 | |||
829 | /* print original var MTRRs at first, for debugging: */ | ||
830 | printk(KERN_DEBUG "original variable MTRRs\n"); | ||
831 | print_out_mtrr_range_state(); | ||
832 | |||
833 | memset(range, 0, sizeof(range)); | ||
834 | extra_remove_size = 0; | ||
835 | extra_remove_base = 1 << (32 - PAGE_SHIFT); | ||
836 | if (mtrr_tom2) | ||
837 | extra_remove_size = | ||
838 | (mtrr_tom2 >> PAGE_SHIFT) - extra_remove_base; | ||
839 | nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base, | ||
840 | extra_remove_size); | ||
841 | /* | ||
842 | * [0, 1M) should always be coverred by var mtrr with WB | ||
843 | * and fixed mtrrs should take effective before var mtrr for it | ||
844 | */ | ||
845 | nr_range = add_range_with_merge(range, nr_range, 0, | ||
846 | (1ULL<<(20 - PAGE_SHIFT)) - 1); | ||
847 | /* sort the ranges */ | ||
848 | sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL); | ||
849 | |||
850 | range_sums = sum_ranges(range, nr_range); | ||
851 | printk(KERN_INFO "total RAM coverred: %ldM\n", | ||
852 | range_sums >> (20 - PAGE_SHIFT)); | ||
853 | |||
854 | if (mtrr_chunk_size && mtrr_gran_size) { | ||
855 | i = 0; | ||
856 | mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size, | ||
857 | extra_remove_base, extra_remove_size, i); | ||
858 | |||
859 | mtrr_print_out_one_result(i); | ||
860 | |||
861 | if (!result[i].bad) { | ||
862 | set_var_mtrr_all(address_bits); | ||
863 | printk(KERN_DEBUG "New variable MTRRs\n"); | ||
864 | print_out_mtrr_range_state(); | ||
865 | return 1; | ||
866 | } | ||
867 | printk(KERN_INFO "invalid mtrr_gran_size or mtrr_chunk_size, " | ||
868 | "will find optimal one\n"); | ||
869 | } | ||
870 | |||
871 | i = 0; | ||
872 | memset(min_loss_pfn, 0xff, sizeof(min_loss_pfn)); | ||
873 | memset(result, 0, sizeof(result)); | ||
874 | for (gran_size = (1ULL<<16); gran_size < (1ULL<<32); gran_size <<= 1) { | ||
875 | |||
876 | for (chunk_size = gran_size; chunk_size < (1ULL<<32); | ||
877 | chunk_size <<= 1) { | ||
878 | |||
879 | if (i >= NUM_RESULT) | ||
880 | continue; | ||
881 | |||
882 | mtrr_calc_range_state(chunk_size, gran_size, | ||
883 | extra_remove_base, extra_remove_size, i); | ||
884 | if (debug_print) { | ||
885 | mtrr_print_out_one_result(i); | ||
886 | printk(KERN_INFO "\n"); | ||
887 | } | ||
888 | |||
889 | i++; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* try to find the optimal index */ | ||
894 | index_good = mtrr_search_optimal_index(); | ||
895 | |||
896 | if (index_good != -1) { | ||
897 | printk(KERN_INFO "Found optimal setting for mtrr clean up\n"); | ||
898 | i = index_good; | ||
899 | mtrr_print_out_one_result(i); | ||
900 | |||
901 | /* convert ranges to var ranges state */ | ||
902 | chunk_size = result[i].chunk_sizek; | ||
903 | chunk_size <<= 10; | ||
904 | gran_size = result[i].gran_sizek; | ||
905 | gran_size <<= 10; | ||
906 | x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size); | ||
907 | set_var_mtrr_all(address_bits); | ||
908 | printk(KERN_DEBUG "New variable MTRRs\n"); | ||
909 | print_out_mtrr_range_state(); | ||
910 | return 1; | ||
911 | } else { | ||
912 | /* print out all */ | ||
913 | for (i = 0; i < NUM_RESULT; i++) | ||
914 | mtrr_print_out_one_result(i); | ||
915 | } | ||
916 | |||
917 | printk(KERN_INFO "mtrr_cleanup: can not find optimal value\n"); | ||
918 | printk(KERN_INFO "please specify mtrr_gran_size/mtrr_chunk_size\n"); | ||
919 | |||
920 | return 0; | ||
921 | } | ||
922 | #else | ||
923 | int __init mtrr_cleanup(unsigned address_bits) | ||
924 | { | ||
925 | return 0; | ||
926 | } | ||
927 | #endif | ||
928 | |||
929 | static int disable_mtrr_trim; | ||
930 | |||
931 | static int __init disable_mtrr_trim_setup(char *str) | ||
932 | { | ||
933 | disable_mtrr_trim = 1; | ||
934 | return 0; | ||
935 | } | ||
936 | early_param("disable_mtrr_trim", disable_mtrr_trim_setup); | ||
937 | |||
938 | /* | ||
939 | * Newer AMD K8s and later CPUs have a special magic MSR way to force WB | ||
940 | * for memory >4GB. Check for that here. | ||
941 | * Note this won't check if the MTRRs < 4GB where the magic bit doesn't | ||
942 | * apply to are wrong, but so far we don't know of any such case in the wild. | ||
943 | */ | ||
944 | #define Tom2Enabled (1U << 21) | ||
945 | #define Tom2ForceMemTypeWB (1U << 22) | ||
946 | |||
947 | int __init amd_special_default_mtrr(void) | ||
948 | { | ||
949 | u32 l, h; | ||
950 | |||
951 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||
952 | return 0; | ||
953 | if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11) | ||
954 | return 0; | ||
955 | /* In case some hypervisor doesn't pass SYSCFG through */ | ||
956 | if (rdmsr_safe(MSR_K8_SYSCFG, &l, &h) < 0) | ||
957 | return 0; | ||
958 | /* | ||
959 | * Memory between 4GB and top of mem is forced WB by this magic bit. | ||
960 | * Reserved before K8RevF, but should be zero there. | ||
961 | */ | ||
962 | if ((l & (Tom2Enabled | Tom2ForceMemTypeWB)) == | ||
963 | (Tom2Enabled | Tom2ForceMemTypeWB)) | ||
964 | return 1; | ||
965 | return 0; | ||
966 | } | ||
967 | |||
968 | static u64 __init real_trim_memory(unsigned long start_pfn, | ||
969 | unsigned long limit_pfn) | ||
970 | { | ||
971 | u64 trim_start, trim_size; | ||
972 | trim_start = start_pfn; | ||
973 | trim_start <<= PAGE_SHIFT; | ||
974 | trim_size = limit_pfn; | ||
975 | trim_size <<= PAGE_SHIFT; | ||
976 | trim_size -= trim_start; | ||
977 | |||
978 | return e820_update_range(trim_start, trim_size, E820_RAM, | ||
979 | E820_RESERVED); | ||
980 | } | ||
981 | /** | ||
982 | * mtrr_trim_uncached_memory - trim RAM not covered by MTRRs | ||
983 | * @end_pfn: ending page frame number | ||
984 | * | ||
985 | * Some buggy BIOSes don't setup the MTRRs properly for systems with certain | ||
986 | * memory configurations. This routine checks that the highest MTRR matches | ||
987 | * the end of memory, to make sure the MTRRs having a write back type cover | ||
988 | * all of the memory the kernel is intending to use. If not, it'll trim any | ||
989 | * memory off the end by adjusting end_pfn, removing it from the kernel's | ||
990 | * allocation pools, warning the user with an obnoxious message. | ||
991 | */ | ||
992 | int __init mtrr_trim_uncached_memory(unsigned long end_pfn) | ||
993 | { | ||
994 | unsigned long i, base, size, highest_pfn = 0, def, dummy; | ||
995 | mtrr_type type; | ||
996 | u64 total_trim_size; | ||
997 | |||
998 | /* extra one for all 0 */ | ||
999 | int num[MTRR_NUM_TYPES + 1]; | ||
1000 | /* | ||
1001 | * Make sure we only trim uncachable memory on machines that | ||
1002 | * support the Intel MTRR architecture: | ||
1003 | */ | ||
1004 | if (!is_cpu(INTEL) || disable_mtrr_trim) | ||
1005 | return 0; | ||
1006 | rdmsr(MTRRdefType_MSR, def, dummy); | ||
1007 | def &= 0xff; | ||
1008 | if (def != MTRR_TYPE_UNCACHABLE) | ||
1009 | return 0; | ||
1010 | |||
1011 | /* get it and store it aside */ | ||
1012 | memset(range_state, 0, sizeof(range_state)); | ||
1013 | for (i = 0; i < num_var_ranges; i++) { | ||
1014 | mtrr_if->get(i, &base, &size, &type); | ||
1015 | range_state[i].base_pfn = base; | ||
1016 | range_state[i].size_pfn = size; | ||
1017 | range_state[i].type = type; | ||
1018 | } | ||
1019 | |||
1020 | /* Find highest cached pfn */ | ||
1021 | for (i = 0; i < num_var_ranges; i++) { | ||
1022 | type = range_state[i].type; | ||
1023 | if (type != MTRR_TYPE_WRBACK) | ||
1024 | continue; | ||
1025 | base = range_state[i].base_pfn; | ||
1026 | size = range_state[i].size_pfn; | ||
1027 | if (highest_pfn < base + size) | ||
1028 | highest_pfn = base + size; | ||
1029 | } | ||
1030 | |||
1031 | /* kvm/qemu doesn't have mtrr set right, don't trim them all */ | ||
1032 | if (!highest_pfn) { | ||
1033 | printk(KERN_INFO "CPU MTRRs all blank - virtualized system.\n"); | ||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | /* check entries number */ | ||
1038 | memset(num, 0, sizeof(num)); | ||
1039 | for (i = 0; i < num_var_ranges; i++) { | ||
1040 | type = range_state[i].type; | ||
1041 | if (type >= MTRR_NUM_TYPES) | ||
1042 | continue; | ||
1043 | size = range_state[i].size_pfn; | ||
1044 | if (!size) | ||
1045 | type = MTRR_NUM_TYPES; | ||
1046 | num[type]++; | ||
1047 | } | ||
1048 | |||
1049 | /* no entry for WB? */ | ||
1050 | if (!num[MTRR_TYPE_WRBACK]) | ||
1051 | return 0; | ||
1052 | |||
1053 | /* check if we only had WB and UC */ | ||
1054 | if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != | ||
1055 | num_var_ranges - num[MTRR_NUM_TYPES]) | ||
1056 | return 0; | ||
1057 | |||
1058 | memset(range, 0, sizeof(range)); | ||
1059 | nr_range = 0; | ||
1060 | if (mtrr_tom2) { | ||
1061 | range[nr_range].start = (1ULL<<(32 - PAGE_SHIFT)); | ||
1062 | range[nr_range].end = (mtrr_tom2 >> PAGE_SHIFT) - 1; | ||
1063 | if (highest_pfn < range[nr_range].end + 1) | ||
1064 | highest_pfn = range[nr_range].end + 1; | ||
1065 | nr_range++; | ||
1066 | } | ||
1067 | nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0); | ||
1068 | |||
1069 | total_trim_size = 0; | ||
1070 | /* check the head */ | ||
1071 | if (range[0].start) | ||
1072 | total_trim_size += real_trim_memory(0, range[0].start); | ||
1073 | /* check the holes */ | ||
1074 | for (i = 0; i < nr_range - 1; i++) { | ||
1075 | if (range[i].end + 1 < range[i+1].start) | ||
1076 | total_trim_size += real_trim_memory(range[i].end + 1, | ||
1077 | range[i+1].start); | ||
1078 | } | ||
1079 | /* check the top */ | ||
1080 | i = nr_range - 1; | ||
1081 | if (range[i].end + 1 < end_pfn) | ||
1082 | total_trim_size += real_trim_memory(range[i].end + 1, | ||
1083 | end_pfn); | ||
1084 | |||
1085 | if (total_trim_size) { | ||
1086 | printk(KERN_WARNING "WARNING: BIOS bug: CPU MTRRs don't cover" | ||
1087 | " all of memory, losing %lluMB of RAM.\n", | ||
1088 | total_trim_size >> 20); | ||
1089 | |||
1090 | if (!changed_by_mtrr_cleanup) | ||
1091 | WARN_ON(1); | ||
1092 | |||
1093 | printk(KERN_INFO "update e820 for mtrr\n"); | ||
1094 | update_e820(); | ||
1095 | |||
1096 | return 1; | ||
1097 | } | ||
1098 | |||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c index 0c0a455fe95c..0b776c09aff3 100644 --- a/arch/x86/kernel/cpu/mtrr/generic.c +++ b/arch/x86/kernel/cpu/mtrr/generic.c | |||
@@ -33,13 +33,31 @@ u64 mtrr_tom2; | |||
33 | struct mtrr_state_type mtrr_state = {}; | 33 | struct mtrr_state_type mtrr_state = {}; |
34 | EXPORT_SYMBOL_GPL(mtrr_state); | 34 | EXPORT_SYMBOL_GPL(mtrr_state); |
35 | 35 | ||
36 | static int __initdata mtrr_show; | 36 | /** |
37 | static int __init mtrr_debug(char *opt) | 37 | * BIOS is expected to clear MtrrFixDramModEn bit, see for example |
38 | * "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD | ||
39 | * Opteron Processors" (26094 Rev. 3.30 February 2006), section | ||
40 | * "13.2.1.2 SYSCFG Register": "The MtrrFixDramModEn bit should be set | ||
41 | * to 1 during BIOS initalization of the fixed MTRRs, then cleared to | ||
42 | * 0 for operation." | ||
43 | */ | ||
44 | static inline void k8_check_syscfg_dram_mod_en(void) | ||
38 | { | 45 | { |
39 | mtrr_show = 1; | 46 | u32 lo, hi; |
40 | return 0; | 47 | |
48 | if (!((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && | ||
49 | (boot_cpu_data.x86 >= 0x0f))) | ||
50 | return; | ||
51 | |||
52 | rdmsr(MSR_K8_SYSCFG, lo, hi); | ||
53 | if (lo & K8_MTRRFIXRANGE_DRAM_MODIFY) { | ||
54 | printk(KERN_ERR FW_WARN "MTRR: CPU %u: SYSCFG[MtrrFixDramModEn]" | ||
55 | " not cleared by BIOS, clearing this bit\n", | ||
56 | smp_processor_id()); | ||
57 | lo &= ~K8_MTRRFIXRANGE_DRAM_MODIFY; | ||
58 | mtrr_wrmsr(MSR_K8_SYSCFG, lo, hi); | ||
59 | } | ||
41 | } | 60 | } |
42 | early_param("mtrr.show", mtrr_debug); | ||
43 | 61 | ||
44 | /* | 62 | /* |
45 | * Returns the effective MTRR type for the region | 63 | * Returns the effective MTRR type for the region |
@@ -174,6 +192,8 @@ get_fixed_ranges(mtrr_type * frs) | |||
174 | unsigned int *p = (unsigned int *) frs; | 192 | unsigned int *p = (unsigned int *) frs; |
175 | int i; | 193 | int i; |
176 | 194 | ||
195 | k8_check_syscfg_dram_mod_en(); | ||
196 | |||
177 | rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); | 197 | rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); |
178 | 198 | ||
179 | for (i = 0; i < 2; i++) | 199 | for (i = 0; i < 2; i++) |
@@ -188,18 +208,94 @@ void mtrr_save_fixed_ranges(void *info) | |||
188 | get_fixed_ranges(mtrr_state.fixed_ranges); | 208 | get_fixed_ranges(mtrr_state.fixed_ranges); |
189 | } | 209 | } |
190 | 210 | ||
191 | static void print_fixed(unsigned base, unsigned step, const mtrr_type*types) | 211 | static unsigned __initdata last_fixed_start; |
212 | static unsigned __initdata last_fixed_end; | ||
213 | static mtrr_type __initdata last_fixed_type; | ||
214 | |||
215 | static void __init print_fixed_last(void) | ||
216 | { | ||
217 | if (!last_fixed_end) | ||
218 | return; | ||
219 | |||
220 | printk(KERN_DEBUG " %05X-%05X %s\n", last_fixed_start, | ||
221 | last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type)); | ||
222 | |||
223 | last_fixed_end = 0; | ||
224 | } | ||
225 | |||
226 | static void __init update_fixed_last(unsigned base, unsigned end, | ||
227 | mtrr_type type) | ||
228 | { | ||
229 | last_fixed_start = base; | ||
230 | last_fixed_end = end; | ||
231 | last_fixed_type = type; | ||
232 | } | ||
233 | |||
234 | static void __init print_fixed(unsigned base, unsigned step, | ||
235 | const mtrr_type *types) | ||
192 | { | 236 | { |
193 | unsigned i; | 237 | unsigned i; |
194 | 238 | ||
195 | for (i = 0; i < 8; ++i, ++types, base += step) | 239 | for (i = 0; i < 8; ++i, ++types, base += step) { |
196 | printk(KERN_INFO "MTRR %05X-%05X %s\n", | 240 | if (last_fixed_end == 0) { |
197 | base, base + step - 1, mtrr_attrib_to_str(*types)); | 241 | update_fixed_last(base, base + step, *types); |
242 | continue; | ||
243 | } | ||
244 | if (last_fixed_end == base && last_fixed_type == *types) { | ||
245 | last_fixed_end = base + step; | ||
246 | continue; | ||
247 | } | ||
248 | /* new segments: gap or different type */ | ||
249 | print_fixed_last(); | ||
250 | update_fixed_last(base, base + step, *types); | ||
251 | } | ||
198 | } | 252 | } |
199 | 253 | ||
200 | static void prepare_set(void); | 254 | static void prepare_set(void); |
201 | static void post_set(void); | 255 | static void post_set(void); |
202 | 256 | ||
257 | static void __init print_mtrr_state(void) | ||
258 | { | ||
259 | unsigned int i; | ||
260 | int high_width; | ||
261 | |||
262 | printk(KERN_DEBUG "MTRR default type: %s\n", | ||
263 | mtrr_attrib_to_str(mtrr_state.def_type)); | ||
264 | if (mtrr_state.have_fixed) { | ||
265 | printk(KERN_DEBUG "MTRR fixed ranges %sabled:\n", | ||
266 | mtrr_state.enabled & 1 ? "en" : "dis"); | ||
267 | print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0); | ||
268 | for (i = 0; i < 2; ++i) | ||
269 | print_fixed(0x80000 + i * 0x20000, 0x04000, mtrr_state.fixed_ranges + (i + 1) * 8); | ||
270 | for (i = 0; i < 8; ++i) | ||
271 | print_fixed(0xC0000 + i * 0x08000, 0x01000, mtrr_state.fixed_ranges + (i + 3) * 8); | ||
272 | |||
273 | /* tail */ | ||
274 | print_fixed_last(); | ||
275 | } | ||
276 | printk(KERN_DEBUG "MTRR variable ranges %sabled:\n", | ||
277 | mtrr_state.enabled & 2 ? "en" : "dis"); | ||
278 | high_width = ((size_or_mask ? ffs(size_or_mask) - 1 : 32) - (32 - PAGE_SHIFT) + 3) / 4; | ||
279 | for (i = 0; i < num_var_ranges; ++i) { | ||
280 | if (mtrr_state.var_ranges[i].mask_lo & (1 << 11)) | ||
281 | printk(KERN_DEBUG " %u base %0*X%05X000 mask %0*X%05X000 %s\n", | ||
282 | i, | ||
283 | high_width, | ||
284 | mtrr_state.var_ranges[i].base_hi, | ||
285 | mtrr_state.var_ranges[i].base_lo >> 12, | ||
286 | high_width, | ||
287 | mtrr_state.var_ranges[i].mask_hi, | ||
288 | mtrr_state.var_ranges[i].mask_lo >> 12, | ||
289 | mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 0xff)); | ||
290 | else | ||
291 | printk(KERN_DEBUG " %u disabled\n", i); | ||
292 | } | ||
293 | if (mtrr_tom2) { | ||
294 | printk(KERN_DEBUG "TOM2: %016llx aka %lldM\n", | ||
295 | mtrr_tom2, mtrr_tom2>>20); | ||
296 | } | ||
297 | } | ||
298 | |||
203 | /* Grab all of the MTRR state for this CPU into *state */ | 299 | /* Grab all of the MTRR state for this CPU into *state */ |
204 | void __init get_mtrr_state(void) | 300 | void __init get_mtrr_state(void) |
205 | { | 301 | { |
@@ -231,41 +327,9 @@ void __init get_mtrr_state(void) | |||
231 | mtrr_tom2 |= low; | 327 | mtrr_tom2 |= low; |
232 | mtrr_tom2 &= 0xffffff800000ULL; | 328 | mtrr_tom2 &= 0xffffff800000ULL; |
233 | } | 329 | } |
234 | if (mtrr_show) { | 330 | |
235 | int high_width; | 331 | print_mtrr_state(); |
236 | 332 | ||
237 | printk(KERN_INFO "MTRR default type: %s\n", mtrr_attrib_to_str(mtrr_state.def_type)); | ||
238 | if (mtrr_state.have_fixed) { | ||
239 | printk(KERN_INFO "MTRR fixed ranges %sabled:\n", | ||
240 | mtrr_state.enabled & 1 ? "en" : "dis"); | ||
241 | print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0); | ||
242 | for (i = 0; i < 2; ++i) | ||
243 | print_fixed(0x80000 + i * 0x20000, 0x04000, mtrr_state.fixed_ranges + (i + 1) * 8); | ||
244 | for (i = 0; i < 8; ++i) | ||
245 | print_fixed(0xC0000 + i * 0x08000, 0x01000, mtrr_state.fixed_ranges + (i + 3) * 8); | ||
246 | } | ||
247 | printk(KERN_INFO "MTRR variable ranges %sabled:\n", | ||
248 | mtrr_state.enabled & 2 ? "en" : "dis"); | ||
249 | high_width = ((size_or_mask ? ffs(size_or_mask) - 1 : 32) - (32 - PAGE_SHIFT) + 3) / 4; | ||
250 | for (i = 0; i < num_var_ranges; ++i) { | ||
251 | if (mtrr_state.var_ranges[i].mask_lo & (1 << 11)) | ||
252 | printk(KERN_INFO "MTRR %u base %0*X%05X000 mask %0*X%05X000 %s\n", | ||
253 | i, | ||
254 | high_width, | ||
255 | mtrr_state.var_ranges[i].base_hi, | ||
256 | mtrr_state.var_ranges[i].base_lo >> 12, | ||
257 | high_width, | ||
258 | mtrr_state.var_ranges[i].mask_hi, | ||
259 | mtrr_state.var_ranges[i].mask_lo >> 12, | ||
260 | mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 0xff)); | ||
261 | else | ||
262 | printk(KERN_INFO "MTRR %u disabled\n", i); | ||
263 | } | ||
264 | if (mtrr_tom2) { | ||
265 | printk(KERN_INFO "TOM2: %016llx aka %lldM\n", | ||
266 | mtrr_tom2, mtrr_tom2>>20); | ||
267 | } | ||
268 | } | ||
269 | mtrr_state_set = 1; | 333 | mtrr_state_set = 1; |
270 | 334 | ||
271 | /* PAT setup for BP. We need to go through sync steps here */ | 335 | /* PAT setup for BP. We need to go through sync steps here */ |
@@ -308,27 +372,10 @@ void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b) | |||
308 | } | 372 | } |
309 | 373 | ||
310 | /** | 374 | /** |
311 | * Enable and allow read/write of extended fixed-range MTRR bits on K8 CPUs | ||
312 | * see AMD publication no. 24593, chapter 3.2.1 for more information | ||
313 | */ | ||
314 | static inline void k8_enable_fixed_iorrs(void) | ||
315 | { | ||
316 | unsigned lo, hi; | ||
317 | |||
318 | rdmsr(MSR_K8_SYSCFG, lo, hi); | ||
319 | mtrr_wrmsr(MSR_K8_SYSCFG, lo | ||
320 | | K8_MTRRFIXRANGE_DRAM_ENABLE | ||
321 | | K8_MTRRFIXRANGE_DRAM_MODIFY, hi); | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * set_fixed_range - checks & updates a fixed-range MTRR if it differs from the value it should have | 375 | * set_fixed_range - checks & updates a fixed-range MTRR if it differs from the value it should have |
326 | * @msr: MSR address of the MTTR which should be checked and updated | 376 | * @msr: MSR address of the MTTR which should be checked and updated |
327 | * @changed: pointer which indicates whether the MTRR needed to be changed | 377 | * @changed: pointer which indicates whether the MTRR needed to be changed |
328 | * @msrwords: pointer to the MSR values which the MSR should have | 378 | * @msrwords: pointer to the MSR values which the MSR should have |
329 | * | ||
330 | * If K8 extentions are wanted, update the K8 SYSCFG MSR also. | ||
331 | * See AMD publication no. 24593, chapter 7.8.1, page 233 for more information. | ||
332 | */ | 379 | */ |
333 | static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) | 380 | static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) |
334 | { | 381 | { |
@@ -337,10 +384,6 @@ static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) | |||
337 | rdmsr(msr, lo, hi); | 384 | rdmsr(msr, lo, hi); |
338 | 385 | ||
339 | if (lo != msrwords[0] || hi != msrwords[1]) { | 386 | if (lo != msrwords[0] || hi != msrwords[1]) { |
340 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && | ||
341 | (boot_cpu_data.x86 >= 0x0f && boot_cpu_data.x86 <= 0x11) && | ||
342 | ((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK)) | ||
343 | k8_enable_fixed_iorrs(); | ||
344 | mtrr_wrmsr(msr, msrwords[0], msrwords[1]); | 387 | mtrr_wrmsr(msr, msrwords[0], msrwords[1]); |
345 | *changed = true; | 388 | *changed = true; |
346 | } | 389 | } |
@@ -376,22 +419,31 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, | |||
376 | { | 419 | { |
377 | unsigned int mask_lo, mask_hi, base_lo, base_hi; | 420 | unsigned int mask_lo, mask_hi, base_lo, base_hi; |
378 | unsigned int tmp, hi; | 421 | unsigned int tmp, hi; |
422 | int cpu; | ||
423 | |||
424 | /* | ||
425 | * get_mtrr doesn't need to update mtrr_state, also it could be called | ||
426 | * from any cpu, so try to print it out directly. | ||
427 | */ | ||
428 | cpu = get_cpu(); | ||
379 | 429 | ||
380 | rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi); | 430 | rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi); |
431 | |||
381 | if ((mask_lo & 0x800) == 0) { | 432 | if ((mask_lo & 0x800) == 0) { |
382 | /* Invalid (i.e. free) range */ | 433 | /* Invalid (i.e. free) range */ |
383 | *base = 0; | 434 | *base = 0; |
384 | *size = 0; | 435 | *size = 0; |
385 | *type = 0; | 436 | *type = 0; |
386 | return; | 437 | goto out_put_cpu; |
387 | } | 438 | } |
388 | 439 | ||
389 | rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); | 440 | rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); |
390 | 441 | ||
391 | /* Work out the shifted address mask. */ | 442 | /* Work out the shifted address mask: */ |
392 | tmp = mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT; | 443 | tmp = mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT; |
393 | mask_lo = size_or_mask | tmp; | 444 | mask_lo = size_or_mask | tmp; |
394 | /* Expand tmp with high bits to all 1s*/ | 445 | |
446 | /* Expand tmp with high bits to all 1s: */ | ||
395 | hi = fls(tmp); | 447 | hi = fls(tmp); |
396 | if (hi > 0) { | 448 | if (hi > 0) { |
397 | tmp |= ~((1<<(hi - 1)) - 1); | 449 | tmp |= ~((1<<(hi - 1)) - 1); |
@@ -402,11 +454,16 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, | |||
402 | } | 454 | } |
403 | } | 455 | } |
404 | 456 | ||
405 | /* This works correctly if size is a power of two, i.e. a | 457 | /* |
406 | contiguous range. */ | 458 | * This works correctly if size is a power of two, i.e. a |
459 | * contiguous range: | ||
460 | */ | ||
407 | *size = -mask_lo; | 461 | *size = -mask_lo; |
408 | *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; | 462 | *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; |
409 | *type = base_lo & 0xff; | 463 | *type = base_lo & 0xff; |
464 | |||
465 | out_put_cpu: | ||
466 | put_cpu(); | ||
410 | } | 467 | } |
411 | 468 | ||
412 | /** | 469 | /** |
@@ -419,6 +476,8 @@ static int set_fixed_ranges(mtrr_type * frs) | |||
419 | bool changed = false; | 476 | bool changed = false; |
420 | int block=-1, range; | 477 | int block=-1, range; |
421 | 478 | ||
479 | k8_check_syscfg_dram_mod_en(); | ||
480 | |||
422 | while (fixed_range_blocks[++block].ranges) | 481 | while (fixed_range_blocks[++block].ranges) |
423 | for (range=0; range < fixed_range_blocks[block].ranges; range++) | 482 | for (range=0; range < fixed_range_blocks[block].ranges; range++) |
424 | set_fixed_range(fixed_range_blocks[block].base_msr + range, | 483 | set_fixed_range(fixed_range_blocks[block].base_msr + range, |
diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c index 4c4214690dd1..fb73a52913a4 100644 --- a/arch/x86/kernel/cpu/mtrr/if.c +++ b/arch/x86/kernel/cpu/mtrr/if.c | |||
@@ -377,10 +377,6 @@ static const struct file_operations mtrr_fops = { | |||
377 | .release = mtrr_close, | 377 | .release = mtrr_close, |
378 | }; | 378 | }; |
379 | 379 | ||
380 | |||
381 | static struct proc_dir_entry *proc_root_mtrr; | ||
382 | |||
383 | |||
384 | static int mtrr_seq_show(struct seq_file *seq, void *offset) | 380 | static int mtrr_seq_show(struct seq_file *seq, void *offset) |
385 | { | 381 | { |
386 | char factor; | 382 | char factor; |
@@ -423,11 +419,7 @@ static int __init mtrr_if_init(void) | |||
423 | (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) | 419 | (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) |
424 | return -ENODEV; | 420 | return -ENODEV; |
425 | 421 | ||
426 | proc_root_mtrr = | 422 | proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); |
427 | proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); | ||
428 | |||
429 | if (proc_root_mtrr) | ||
430 | proc_root_mtrr->owner = THIS_MODULE; | ||
431 | return 0; | 423 | return 0; |
432 | } | 424 | } |
433 | 425 | ||
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index 236a401b8259..03cda01f57c7 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c | |||
@@ -574,7 +574,7 @@ struct mtrr_value { | |||
574 | unsigned long lsize; | 574 | unsigned long lsize; |
575 | }; | 575 | }; |
576 | 576 | ||
577 | static struct mtrr_value mtrr_state[MTRR_MAX_VAR_RANGES]; | 577 | static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES]; |
578 | 578 | ||
579 | static int mtrr_save(struct sys_device * sysdev, pm_message_t state) | 579 | static int mtrr_save(struct sys_device * sysdev, pm_message_t state) |
580 | { | 580 | { |
@@ -582,9 +582,9 @@ static int mtrr_save(struct sys_device * sysdev, pm_message_t state) | |||
582 | 582 | ||
583 | for (i = 0; i < num_var_ranges; i++) { | 583 | for (i = 0; i < num_var_ranges; i++) { |
584 | mtrr_if->get(i, | 584 | mtrr_if->get(i, |
585 | &mtrr_state[i].lbase, | 585 | &mtrr_value[i].lbase, |
586 | &mtrr_state[i].lsize, | 586 | &mtrr_value[i].lsize, |
587 | &mtrr_state[i].ltype); | 587 | &mtrr_value[i].ltype); |
588 | } | 588 | } |
589 | return 0; | 589 | return 0; |
590 | } | 590 | } |
@@ -594,11 +594,11 @@ static int mtrr_restore(struct sys_device * sysdev) | |||
594 | int i; | 594 | int i; |
595 | 595 | ||
596 | for (i = 0; i < num_var_ranges; i++) { | 596 | for (i = 0; i < num_var_ranges; i++) { |
597 | if (mtrr_state[i].lsize) | 597 | if (mtrr_value[i].lsize) |
598 | set_mtrr(i, | 598 | set_mtrr(i, |
599 | mtrr_state[i].lbase, | 599 | mtrr_value[i].lbase, |
600 | mtrr_state[i].lsize, | 600 | mtrr_value[i].lsize, |
601 | mtrr_state[i].ltype); | 601 | mtrr_value[i].ltype); |
602 | } | 602 | } |
603 | return 0; | 603 | return 0; |
604 | } | 604 | } |
@@ -610,1058 +610,7 @@ static struct sysdev_driver mtrr_sysdev_driver = { | |||
610 | .resume = mtrr_restore, | 610 | .resume = mtrr_restore, |
611 | }; | 611 | }; |
612 | 612 | ||
613 | /* should be related to MTRR_VAR_RANGES nums */ | 613 | int __initdata changed_by_mtrr_cleanup; |
614 | #define RANGE_NUM 256 | ||
615 | |||
616 | struct res_range { | ||
617 | unsigned long start; | ||
618 | unsigned long end; | ||
619 | }; | ||
620 | |||
621 | static int __init | ||
622 | add_range(struct res_range *range, int nr_range, unsigned long start, | ||
623 | unsigned long end) | ||
624 | { | ||
625 | /* out of slots */ | ||
626 | if (nr_range >= RANGE_NUM) | ||
627 | return nr_range; | ||
628 | |||
629 | range[nr_range].start = start; | ||
630 | range[nr_range].end = end; | ||
631 | |||
632 | nr_range++; | ||
633 | |||
634 | return nr_range; | ||
635 | } | ||
636 | |||
637 | static int __init | ||
638 | add_range_with_merge(struct res_range *range, int nr_range, unsigned long start, | ||
639 | unsigned long end) | ||
640 | { | ||
641 | int i; | ||
642 | |||
643 | /* try to merge it with old one */ | ||
644 | for (i = 0; i < nr_range; i++) { | ||
645 | unsigned long final_start, final_end; | ||
646 | unsigned long common_start, common_end; | ||
647 | |||
648 | if (!range[i].end) | ||
649 | continue; | ||
650 | |||
651 | common_start = max(range[i].start, start); | ||
652 | common_end = min(range[i].end, end); | ||
653 | if (common_start > common_end + 1) | ||
654 | continue; | ||
655 | |||
656 | final_start = min(range[i].start, start); | ||
657 | final_end = max(range[i].end, end); | ||
658 | |||
659 | range[i].start = final_start; | ||
660 | range[i].end = final_end; | ||
661 | return nr_range; | ||
662 | } | ||
663 | |||
664 | /* need to add that */ | ||
665 | return add_range(range, nr_range, start, end); | ||
666 | } | ||
667 | |||
668 | static void __init | ||
669 | subtract_range(struct res_range *range, unsigned long start, unsigned long end) | ||
670 | { | ||
671 | int i, j; | ||
672 | |||
673 | for (j = 0; j < RANGE_NUM; j++) { | ||
674 | if (!range[j].end) | ||
675 | continue; | ||
676 | |||
677 | if (start <= range[j].start && end >= range[j].end) { | ||
678 | range[j].start = 0; | ||
679 | range[j].end = 0; | ||
680 | continue; | ||
681 | } | ||
682 | |||
683 | if (start <= range[j].start && end < range[j].end && | ||
684 | range[j].start < end + 1) { | ||
685 | range[j].start = end + 1; | ||
686 | continue; | ||
687 | } | ||
688 | |||
689 | |||
690 | if (start > range[j].start && end >= range[j].end && | ||
691 | range[j].end > start - 1) { | ||
692 | range[j].end = start - 1; | ||
693 | continue; | ||
694 | } | ||
695 | |||
696 | if (start > range[j].start && end < range[j].end) { | ||
697 | /* find the new spare */ | ||
698 | for (i = 0; i < RANGE_NUM; i++) { | ||
699 | if (range[i].end == 0) | ||
700 | break; | ||
701 | } | ||
702 | if (i < RANGE_NUM) { | ||
703 | range[i].end = range[j].end; | ||
704 | range[i].start = end + 1; | ||
705 | } else { | ||
706 | printk(KERN_ERR "run of slot in ranges\n"); | ||
707 | } | ||
708 | range[j].end = start - 1; | ||
709 | continue; | ||
710 | } | ||
711 | } | ||
712 | } | ||
713 | |||
714 | static int __init cmp_range(const void *x1, const void *x2) | ||
715 | { | ||
716 | const struct res_range *r1 = x1; | ||
717 | const struct res_range *r2 = x2; | ||
718 | long start1, start2; | ||
719 | |||
720 | start1 = r1->start; | ||
721 | start2 = r2->start; | ||
722 | |||
723 | return start1 - start2; | ||
724 | } | ||
725 | |||
726 | struct var_mtrr_range_state { | ||
727 | unsigned long base_pfn; | ||
728 | unsigned long size_pfn; | ||
729 | mtrr_type type; | ||
730 | }; | ||
731 | |||
732 | static struct var_mtrr_range_state __initdata range_state[RANGE_NUM]; | ||
733 | static int __initdata debug_print; | ||
734 | |||
735 | static int __init | ||
736 | x86_get_mtrr_mem_range(struct res_range *range, int nr_range, | ||
737 | unsigned long extra_remove_base, | ||
738 | unsigned long extra_remove_size) | ||
739 | { | ||
740 | unsigned long i, base, size; | ||
741 | mtrr_type type; | ||
742 | |||
743 | for (i = 0; i < num_var_ranges; i++) { | ||
744 | type = range_state[i].type; | ||
745 | if (type != MTRR_TYPE_WRBACK) | ||
746 | continue; | ||
747 | base = range_state[i].base_pfn; | ||
748 | size = range_state[i].size_pfn; | ||
749 | nr_range = add_range_with_merge(range, nr_range, base, | ||
750 | base + size - 1); | ||
751 | } | ||
752 | if (debug_print) { | ||
753 | printk(KERN_DEBUG "After WB checking\n"); | ||
754 | for (i = 0; i < nr_range; i++) | ||
755 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
756 | range[i].start, range[i].end + 1); | ||
757 | } | ||
758 | |||
759 | /* take out UC ranges */ | ||
760 | for (i = 0; i < num_var_ranges; i++) { | ||
761 | type = range_state[i].type; | ||
762 | if (type != MTRR_TYPE_UNCACHABLE && | ||
763 | type != MTRR_TYPE_WRPROT) | ||
764 | continue; | ||
765 | size = range_state[i].size_pfn; | ||
766 | if (!size) | ||
767 | continue; | ||
768 | base = range_state[i].base_pfn; | ||
769 | subtract_range(range, base, base + size - 1); | ||
770 | } | ||
771 | if (extra_remove_size) | ||
772 | subtract_range(range, extra_remove_base, | ||
773 | extra_remove_base + extra_remove_size - 1); | ||
774 | |||
775 | /* get new range num */ | ||
776 | nr_range = 0; | ||
777 | for (i = 0; i < RANGE_NUM; i++) { | ||
778 | if (!range[i].end) | ||
779 | continue; | ||
780 | nr_range++; | ||
781 | } | ||
782 | if (debug_print) { | ||
783 | printk(KERN_DEBUG "After UC checking\n"); | ||
784 | for (i = 0; i < nr_range; i++) | ||
785 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
786 | range[i].start, range[i].end + 1); | ||
787 | } | ||
788 | |||
789 | /* sort the ranges */ | ||
790 | sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL); | ||
791 | if (debug_print) { | ||
792 | printk(KERN_DEBUG "After sorting\n"); | ||
793 | for (i = 0; i < nr_range; i++) | ||
794 | printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n", | ||
795 | range[i].start, range[i].end + 1); | ||
796 | } | ||
797 | |||
798 | /* clear those is not used */ | ||
799 | for (i = nr_range; i < RANGE_NUM; i++) | ||
800 | memset(&range[i], 0, sizeof(range[i])); | ||
801 | |||
802 | return nr_range; | ||
803 | } | ||
804 | |||
805 | static struct res_range __initdata range[RANGE_NUM]; | ||
806 | static int __initdata nr_range; | ||
807 | |||
808 | #ifdef CONFIG_MTRR_SANITIZER | ||
809 | |||
810 | static unsigned long __init sum_ranges(struct res_range *range, int nr_range) | ||
811 | { | ||
812 | unsigned long sum; | ||
813 | int i; | ||
814 | |||
815 | sum = 0; | ||
816 | for (i = 0; i < nr_range; i++) | ||
817 | sum += range[i].end + 1 - range[i].start; | ||
818 | |||
819 | return sum; | ||
820 | } | ||
821 | |||
822 | static int enable_mtrr_cleanup __initdata = | ||
823 | CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT; | ||
824 | |||
825 | static int __init disable_mtrr_cleanup_setup(char *str) | ||
826 | { | ||
827 | enable_mtrr_cleanup = 0; | ||
828 | return 0; | ||
829 | } | ||
830 | early_param("disable_mtrr_cleanup", disable_mtrr_cleanup_setup); | ||
831 | |||
832 | static int __init enable_mtrr_cleanup_setup(char *str) | ||
833 | { | ||
834 | enable_mtrr_cleanup = 1; | ||
835 | return 0; | ||
836 | } | ||
837 | early_param("enable_mtrr_cleanup", enable_mtrr_cleanup_setup); | ||
838 | |||
839 | static int __init mtrr_cleanup_debug_setup(char *str) | ||
840 | { | ||
841 | debug_print = 1; | ||
842 | return 0; | ||
843 | } | ||
844 | early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup); | ||
845 | |||
846 | struct var_mtrr_state { | ||
847 | unsigned long range_startk; | ||
848 | unsigned long range_sizek; | ||
849 | unsigned long chunk_sizek; | ||
850 | unsigned long gran_sizek; | ||
851 | unsigned int reg; | ||
852 | }; | ||
853 | |||
854 | static void __init | ||
855 | set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, | ||
856 | unsigned char type, unsigned int address_bits) | ||
857 | { | ||
858 | u32 base_lo, base_hi, mask_lo, mask_hi; | ||
859 | u64 base, mask; | ||
860 | |||
861 | if (!sizek) { | ||
862 | fill_mtrr_var_range(reg, 0, 0, 0, 0); | ||
863 | return; | ||
864 | } | ||
865 | |||
866 | mask = (1ULL << address_bits) - 1; | ||
867 | mask &= ~((((u64)sizek) << 10) - 1); | ||
868 | |||
869 | base = ((u64)basek) << 10; | ||
870 | |||
871 | base |= type; | ||
872 | mask |= 0x800; | ||
873 | |||
874 | base_lo = base & ((1ULL<<32) - 1); | ||
875 | base_hi = base >> 32; | ||
876 | |||
877 | mask_lo = mask & ((1ULL<<32) - 1); | ||
878 | mask_hi = mask >> 32; | ||
879 | |||
880 | fill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi); | ||
881 | } | ||
882 | |||
883 | static void __init | ||
884 | save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, | ||
885 | unsigned char type) | ||
886 | { | ||
887 | range_state[reg].base_pfn = basek >> (PAGE_SHIFT - 10); | ||
888 | range_state[reg].size_pfn = sizek >> (PAGE_SHIFT - 10); | ||
889 | range_state[reg].type = type; | ||
890 | } | ||
891 | |||
892 | static void __init | ||
893 | set_var_mtrr_all(unsigned int address_bits) | ||
894 | { | ||
895 | unsigned long basek, sizek; | ||
896 | unsigned char type; | ||
897 | unsigned int reg; | ||
898 | |||
899 | for (reg = 0; reg < num_var_ranges; reg++) { | ||
900 | basek = range_state[reg].base_pfn << (PAGE_SHIFT - 10); | ||
901 | sizek = range_state[reg].size_pfn << (PAGE_SHIFT - 10); | ||
902 | type = range_state[reg].type; | ||
903 | |||
904 | set_var_mtrr(reg, basek, sizek, type, address_bits); | ||
905 | } | ||
906 | } | ||
907 | |||
908 | static unsigned long to_size_factor(unsigned long sizek, char *factorp) | ||
909 | { | ||
910 | char factor; | ||
911 | unsigned long base = sizek; | ||
912 | |||
913 | if (base & ((1<<10) - 1)) { | ||
914 | /* not MB alignment */ | ||
915 | factor = 'K'; | ||
916 | } else if (base & ((1<<20) - 1)){ | ||
917 | factor = 'M'; | ||
918 | base >>= 10; | ||
919 | } else { | ||
920 | factor = 'G'; | ||
921 | base >>= 20; | ||
922 | } | ||
923 | |||
924 | *factorp = factor; | ||
925 | |||
926 | return base; | ||
927 | } | ||
928 | |||
929 | static unsigned int __init | ||
930 | range_to_mtrr(unsigned int reg, unsigned long range_startk, | ||
931 | unsigned long range_sizek, unsigned char type) | ||
932 | { | ||
933 | if (!range_sizek || (reg >= num_var_ranges)) | ||
934 | return reg; | ||
935 | |||
936 | while (range_sizek) { | ||
937 | unsigned long max_align, align; | ||
938 | unsigned long sizek; | ||
939 | |||
940 | /* Compute the maximum size I can make a range */ | ||
941 | if (range_startk) | ||
942 | max_align = ffs(range_startk) - 1; | ||
943 | else | ||
944 | max_align = 32; | ||
945 | align = fls(range_sizek) - 1; | ||
946 | if (align > max_align) | ||
947 | align = max_align; | ||
948 | |||
949 | sizek = 1 << align; | ||
950 | if (debug_print) { | ||
951 | char start_factor = 'K', size_factor = 'K'; | ||
952 | unsigned long start_base, size_base; | ||
953 | |||
954 | start_base = to_size_factor(range_startk, &start_factor), | ||
955 | size_base = to_size_factor(sizek, &size_factor), | ||
956 | |||
957 | printk(KERN_DEBUG "Setting variable MTRR %d, " | ||
958 | "base: %ld%cB, range: %ld%cB, type %s\n", | ||
959 | reg, start_base, start_factor, | ||
960 | size_base, size_factor, | ||
961 | (type == MTRR_TYPE_UNCACHABLE)?"UC": | ||
962 | ((type == MTRR_TYPE_WRBACK)?"WB":"Other") | ||
963 | ); | ||
964 | } | ||
965 | save_var_mtrr(reg++, range_startk, sizek, type); | ||
966 | range_startk += sizek; | ||
967 | range_sizek -= sizek; | ||
968 | if (reg >= num_var_ranges) | ||
969 | break; | ||
970 | } | ||
971 | return reg; | ||
972 | } | ||
973 | |||
974 | static unsigned __init | ||
975 | range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, | ||
976 | unsigned long sizek) | ||
977 | { | ||
978 | unsigned long hole_basek, hole_sizek; | ||
979 | unsigned long second_basek, second_sizek; | ||
980 | unsigned long range0_basek, range0_sizek; | ||
981 | unsigned long range_basek, range_sizek; | ||
982 | unsigned long chunk_sizek; | ||
983 | unsigned long gran_sizek; | ||
984 | |||
985 | hole_basek = 0; | ||
986 | hole_sizek = 0; | ||
987 | second_basek = 0; | ||
988 | second_sizek = 0; | ||
989 | chunk_sizek = state->chunk_sizek; | ||
990 | gran_sizek = state->gran_sizek; | ||
991 | |||
992 | /* align with gran size, prevent small block used up MTRRs */ | ||
993 | range_basek = ALIGN(state->range_startk, gran_sizek); | ||
994 | if ((range_basek > basek) && basek) | ||
995 | return second_sizek; | ||
996 | state->range_sizek -= (range_basek - state->range_startk); | ||
997 | range_sizek = ALIGN(state->range_sizek, gran_sizek); | ||
998 | |||
999 | while (range_sizek > state->range_sizek) { | ||
1000 | range_sizek -= gran_sizek; | ||
1001 | if (!range_sizek) | ||
1002 | return 0; | ||
1003 | } | ||
1004 | state->range_sizek = range_sizek; | ||
1005 | |||
1006 | /* try to append some small hole */ | ||
1007 | range0_basek = state->range_startk; | ||
1008 | range0_sizek = ALIGN(state->range_sizek, chunk_sizek); | ||
1009 | |||
1010 | /* no increase */ | ||
1011 | if (range0_sizek == state->range_sizek) { | ||
1012 | if (debug_print) | ||
1013 | printk(KERN_DEBUG "rangeX: %016lx - %016lx\n", | ||
1014 | range0_basek<<10, | ||
1015 | (range0_basek + state->range_sizek)<<10); | ||
1016 | state->reg = range_to_mtrr(state->reg, range0_basek, | ||
1017 | state->range_sizek, MTRR_TYPE_WRBACK); | ||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | /* only cut back, when it is not the last */ | ||
1022 | if (sizek) { | ||
1023 | while (range0_basek + range0_sizek > (basek + sizek)) { | ||
1024 | if (range0_sizek >= chunk_sizek) | ||
1025 | range0_sizek -= chunk_sizek; | ||
1026 | else | ||
1027 | range0_sizek = 0; | ||
1028 | |||
1029 | if (!range0_sizek) | ||
1030 | break; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | second_try: | ||
1035 | range_basek = range0_basek + range0_sizek; | ||
1036 | |||
1037 | /* one hole in the middle */ | ||
1038 | if (range_basek > basek && range_basek <= (basek + sizek)) | ||
1039 | second_sizek = range_basek - basek; | ||
1040 | |||
1041 | if (range0_sizek > state->range_sizek) { | ||
1042 | |||
1043 | /* one hole in middle or at end */ | ||
1044 | hole_sizek = range0_sizek - state->range_sizek - second_sizek; | ||
1045 | |||
1046 | /* hole size should be less than half of range0 size */ | ||
1047 | if (hole_sizek >= (range0_sizek >> 1) && | ||
1048 | range0_sizek >= chunk_sizek) { | ||
1049 | range0_sizek -= chunk_sizek; | ||
1050 | second_sizek = 0; | ||
1051 | hole_sizek = 0; | ||
1052 | |||
1053 | goto second_try; | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | if (range0_sizek) { | ||
1058 | if (debug_print) | ||
1059 | printk(KERN_DEBUG "range0: %016lx - %016lx\n", | ||
1060 | range0_basek<<10, | ||
1061 | (range0_basek + range0_sizek)<<10); | ||
1062 | state->reg = range_to_mtrr(state->reg, range0_basek, | ||
1063 | range0_sizek, MTRR_TYPE_WRBACK); | ||
1064 | } | ||
1065 | |||
1066 | if (range0_sizek < state->range_sizek) { | ||
1067 | /* need to handle left over */ | ||
1068 | range_sizek = state->range_sizek - range0_sizek; | ||
1069 | |||
1070 | if (debug_print) | ||
1071 | printk(KERN_DEBUG "range: %016lx - %016lx\n", | ||
1072 | range_basek<<10, | ||
1073 | (range_basek + range_sizek)<<10); | ||
1074 | state->reg = range_to_mtrr(state->reg, range_basek, | ||
1075 | range_sizek, MTRR_TYPE_WRBACK); | ||
1076 | } | ||
1077 | |||
1078 | if (hole_sizek) { | ||
1079 | hole_basek = range_basek - hole_sizek - second_sizek; | ||
1080 | if (debug_print) | ||
1081 | printk(KERN_DEBUG "hole: %016lx - %016lx\n", | ||
1082 | hole_basek<<10, | ||
1083 | (hole_basek + hole_sizek)<<10); | ||
1084 | state->reg = range_to_mtrr(state->reg, hole_basek, | ||
1085 | hole_sizek, MTRR_TYPE_UNCACHABLE); | ||
1086 | } | ||
1087 | |||
1088 | return second_sizek; | ||
1089 | } | ||
1090 | |||
1091 | static void __init | ||
1092 | set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn, | ||
1093 | unsigned long size_pfn) | ||
1094 | { | ||
1095 | unsigned long basek, sizek; | ||
1096 | unsigned long second_sizek = 0; | ||
1097 | |||
1098 | if (state->reg >= num_var_ranges) | ||
1099 | return; | ||
1100 | |||
1101 | basek = base_pfn << (PAGE_SHIFT - 10); | ||
1102 | sizek = size_pfn << (PAGE_SHIFT - 10); | ||
1103 | |||
1104 | /* See if I can merge with the last range */ | ||
1105 | if ((basek <= 1024) || | ||
1106 | (state->range_startk + state->range_sizek == basek)) { | ||
1107 | unsigned long endk = basek + sizek; | ||
1108 | state->range_sizek = endk - state->range_startk; | ||
1109 | return; | ||
1110 | } | ||
1111 | /* Write the range mtrrs */ | ||
1112 | if (state->range_sizek != 0) | ||
1113 | second_sizek = range_to_mtrr_with_hole(state, basek, sizek); | ||
1114 | |||
1115 | /* Allocate an msr */ | ||
1116 | state->range_startk = basek + second_sizek; | ||
1117 | state->range_sizek = sizek - second_sizek; | ||
1118 | } | ||
1119 | |||
1120 | /* mininum size of mtrr block that can take hole */ | ||
1121 | static u64 mtrr_chunk_size __initdata = (256ULL<<20); | ||
1122 | |||
1123 | static int __init parse_mtrr_chunk_size_opt(char *p) | ||
1124 | { | ||
1125 | if (!p) | ||
1126 | return -EINVAL; | ||
1127 | mtrr_chunk_size = memparse(p, &p); | ||
1128 | return 0; | ||
1129 | } | ||
1130 | early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt); | ||
1131 | |||
1132 | /* granity of mtrr of block */ | ||
1133 | static u64 mtrr_gran_size __initdata; | ||
1134 | |||
1135 | static int __init parse_mtrr_gran_size_opt(char *p) | ||
1136 | { | ||
1137 | if (!p) | ||
1138 | return -EINVAL; | ||
1139 | mtrr_gran_size = memparse(p, &p); | ||
1140 | return 0; | ||
1141 | } | ||
1142 | early_param("mtrr_gran_size", parse_mtrr_gran_size_opt); | ||
1143 | |||
1144 | static int nr_mtrr_spare_reg __initdata = | ||
1145 | CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT; | ||
1146 | |||
1147 | static int __init parse_mtrr_spare_reg(char *arg) | ||
1148 | { | ||
1149 | if (arg) | ||
1150 | nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0); | ||
1151 | return 0; | ||
1152 | } | ||
1153 | |||
1154 | early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg); | ||
1155 | |||
1156 | static int __init | ||
1157 | x86_setup_var_mtrrs(struct res_range *range, int nr_range, | ||
1158 | u64 chunk_size, u64 gran_size) | ||
1159 | { | ||
1160 | struct var_mtrr_state var_state; | ||
1161 | int i; | ||
1162 | int num_reg; | ||
1163 | |||
1164 | var_state.range_startk = 0; | ||
1165 | var_state.range_sizek = 0; | ||
1166 | var_state.reg = 0; | ||
1167 | var_state.chunk_sizek = chunk_size >> 10; | ||
1168 | var_state.gran_sizek = gran_size >> 10; | ||
1169 | |||
1170 | memset(range_state, 0, sizeof(range_state)); | ||
1171 | |||
1172 | /* Write the range etc */ | ||
1173 | for (i = 0; i < nr_range; i++) | ||
1174 | set_var_mtrr_range(&var_state, range[i].start, | ||
1175 | range[i].end - range[i].start + 1); | ||
1176 | |||
1177 | /* Write the last range */ | ||
1178 | if (var_state.range_sizek != 0) | ||
1179 | range_to_mtrr_with_hole(&var_state, 0, 0); | ||
1180 | |||
1181 | num_reg = var_state.reg; | ||
1182 | /* Clear out the extra MTRR's */ | ||
1183 | while (var_state.reg < num_var_ranges) { | ||
1184 | save_var_mtrr(var_state.reg, 0, 0, 0); | ||
1185 | var_state.reg++; | ||
1186 | } | ||
1187 | |||
1188 | return num_reg; | ||
1189 | } | ||
1190 | |||
1191 | struct mtrr_cleanup_result { | ||
1192 | unsigned long gran_sizek; | ||
1193 | unsigned long chunk_sizek; | ||
1194 | unsigned long lose_cover_sizek; | ||
1195 | unsigned int num_reg; | ||
1196 | int bad; | ||
1197 | }; | ||
1198 | |||
1199 | /* | ||
1200 | * gran_size: 64K, 128K, 256K, 512K, 1M, 2M, ..., 2G | ||
1201 | * chunk size: gran_size, ..., 2G | ||
1202 | * so we need (1+16)*8 | ||
1203 | */ | ||
1204 | #define NUM_RESULT 136 | ||
1205 | #define PSHIFT (PAGE_SHIFT - 10) | ||
1206 | |||
1207 | static struct mtrr_cleanup_result __initdata result[NUM_RESULT]; | ||
1208 | static unsigned long __initdata min_loss_pfn[RANGE_NUM]; | ||
1209 | |||
1210 | static void __init print_out_mtrr_range_state(void) | ||
1211 | { | ||
1212 | int i; | ||
1213 | char start_factor = 'K', size_factor = 'K'; | ||
1214 | unsigned long start_base, size_base; | ||
1215 | mtrr_type type; | ||
1216 | |||
1217 | for (i = 0; i < num_var_ranges; i++) { | ||
1218 | |||
1219 | size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10); | ||
1220 | if (!size_base) | ||
1221 | continue; | ||
1222 | |||
1223 | size_base = to_size_factor(size_base, &size_factor), | ||
1224 | start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10); | ||
1225 | start_base = to_size_factor(start_base, &start_factor), | ||
1226 | type = range_state[i].type; | ||
1227 | |||
1228 | printk(KERN_DEBUG "reg %d, base: %ld%cB, range: %ld%cB, type %s\n", | ||
1229 | i, start_base, start_factor, | ||
1230 | size_base, size_factor, | ||
1231 | (type == MTRR_TYPE_UNCACHABLE) ? "UC" : | ||
1232 | ((type == MTRR_TYPE_WRPROT) ? "WP" : | ||
1233 | ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other")) | ||
1234 | ); | ||
1235 | } | ||
1236 | } | ||
1237 | |||
1238 | static int __init mtrr_need_cleanup(void) | ||
1239 | { | ||
1240 | int i; | ||
1241 | mtrr_type type; | ||
1242 | unsigned long size; | ||
1243 | /* extra one for all 0 */ | ||
1244 | int num[MTRR_NUM_TYPES + 1]; | ||
1245 | |||
1246 | /* check entries number */ | ||
1247 | memset(num, 0, sizeof(num)); | ||
1248 | for (i = 0; i < num_var_ranges; i++) { | ||
1249 | type = range_state[i].type; | ||
1250 | size = range_state[i].size_pfn; | ||
1251 | if (type >= MTRR_NUM_TYPES) | ||
1252 | continue; | ||
1253 | if (!size) | ||
1254 | type = MTRR_NUM_TYPES; | ||
1255 | if (type == MTRR_TYPE_WRPROT) | ||
1256 | type = MTRR_TYPE_UNCACHABLE; | ||
1257 | num[type]++; | ||
1258 | } | ||
1259 | |||
1260 | /* check if we got UC entries */ | ||
1261 | if (!num[MTRR_TYPE_UNCACHABLE]) | ||
1262 | return 0; | ||
1263 | |||
1264 | /* check if we only had WB and UC */ | ||
1265 | if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != | ||
1266 | num_var_ranges - num[MTRR_NUM_TYPES]) | ||
1267 | return 0; | ||
1268 | |||
1269 | return 1; | ||
1270 | } | ||
1271 | |||
1272 | static unsigned long __initdata range_sums; | ||
1273 | static void __init mtrr_calc_range_state(u64 chunk_size, u64 gran_size, | ||
1274 | unsigned long extra_remove_base, | ||
1275 | unsigned long extra_remove_size, | ||
1276 | int i) | ||
1277 | { | ||
1278 | int num_reg; | ||
1279 | static struct res_range range_new[RANGE_NUM]; | ||
1280 | static int nr_range_new; | ||
1281 | unsigned long range_sums_new; | ||
1282 | |||
1283 | /* convert ranges to var ranges state */ | ||
1284 | num_reg = x86_setup_var_mtrrs(range, nr_range, | ||
1285 | chunk_size, gran_size); | ||
1286 | |||
1287 | /* we got new setting in range_state, check it */ | ||
1288 | memset(range_new, 0, sizeof(range_new)); | ||
1289 | nr_range_new = x86_get_mtrr_mem_range(range_new, 0, | ||
1290 | extra_remove_base, extra_remove_size); | ||
1291 | range_sums_new = sum_ranges(range_new, nr_range_new); | ||
1292 | |||
1293 | result[i].chunk_sizek = chunk_size >> 10; | ||
1294 | result[i].gran_sizek = gran_size >> 10; | ||
1295 | result[i].num_reg = num_reg; | ||
1296 | if (range_sums < range_sums_new) { | ||
1297 | result[i].lose_cover_sizek = | ||
1298 | (range_sums_new - range_sums) << PSHIFT; | ||
1299 | result[i].bad = 1; | ||
1300 | } else | ||
1301 | result[i].lose_cover_sizek = | ||
1302 | (range_sums - range_sums_new) << PSHIFT; | ||
1303 | |||
1304 | /* double check it */ | ||
1305 | if (!result[i].bad && !result[i].lose_cover_sizek) { | ||
1306 | if (nr_range_new != nr_range || | ||
1307 | memcmp(range, range_new, sizeof(range))) | ||
1308 | result[i].bad = 1; | ||
1309 | } | ||
1310 | |||
1311 | if (!result[i].bad && (range_sums - range_sums_new < | ||
1312 | min_loss_pfn[num_reg])) { | ||
1313 | min_loss_pfn[num_reg] = | ||
1314 | range_sums - range_sums_new; | ||
1315 | } | ||
1316 | } | ||
1317 | |||
1318 | static void __init mtrr_print_out_one_result(int i) | ||
1319 | { | ||
1320 | char gran_factor, chunk_factor, lose_factor; | ||
1321 | unsigned long gran_base, chunk_base, lose_base; | ||
1322 | |||
1323 | gran_base = to_size_factor(result[i].gran_sizek, &gran_factor), | ||
1324 | chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor), | ||
1325 | lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor), | ||
1326 | printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t", | ||
1327 | result[i].bad ? "*BAD*" : " ", | ||
1328 | gran_base, gran_factor, chunk_base, chunk_factor); | ||
1329 | printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n", | ||
1330 | result[i].num_reg, result[i].bad ? "-" : "", | ||
1331 | lose_base, lose_factor); | ||
1332 | } | ||
1333 | |||
1334 | static int __init mtrr_search_optimal_index(void) | ||
1335 | { | ||
1336 | int i; | ||
1337 | int num_reg_good; | ||
1338 | int index_good; | ||
1339 | |||
1340 | if (nr_mtrr_spare_reg >= num_var_ranges) | ||
1341 | nr_mtrr_spare_reg = num_var_ranges - 1; | ||
1342 | num_reg_good = -1; | ||
1343 | for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) { | ||
1344 | if (!min_loss_pfn[i]) | ||
1345 | num_reg_good = i; | ||
1346 | } | ||
1347 | |||
1348 | index_good = -1; | ||
1349 | if (num_reg_good != -1) { | ||
1350 | for (i = 0; i < NUM_RESULT; i++) { | ||
1351 | if (!result[i].bad && | ||
1352 | result[i].num_reg == num_reg_good && | ||
1353 | !result[i].lose_cover_sizek) { | ||
1354 | index_good = i; | ||
1355 | break; | ||
1356 | } | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | return index_good; | ||
1361 | } | ||
1362 | |||
1363 | |||
1364 | static int __init mtrr_cleanup(unsigned address_bits) | ||
1365 | { | ||
1366 | unsigned long extra_remove_base, extra_remove_size; | ||
1367 | unsigned long base, size, def, dummy; | ||
1368 | mtrr_type type; | ||
1369 | u64 chunk_size, gran_size; | ||
1370 | int index_good; | ||
1371 | int i; | ||
1372 | |||
1373 | if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1) | ||
1374 | return 0; | ||
1375 | rdmsr(MTRRdefType_MSR, def, dummy); | ||
1376 | def &= 0xff; | ||
1377 | if (def != MTRR_TYPE_UNCACHABLE) | ||
1378 | return 0; | ||
1379 | |||
1380 | /* get it and store it aside */ | ||
1381 | memset(range_state, 0, sizeof(range_state)); | ||
1382 | for (i = 0; i < num_var_ranges; i++) { | ||
1383 | mtrr_if->get(i, &base, &size, &type); | ||
1384 | range_state[i].base_pfn = base; | ||
1385 | range_state[i].size_pfn = size; | ||
1386 | range_state[i].type = type; | ||
1387 | } | ||
1388 | |||
1389 | /* check if we need handle it and can handle it */ | ||
1390 | if (!mtrr_need_cleanup()) | ||
1391 | return 0; | ||
1392 | |||
1393 | /* print original var MTRRs at first, for debugging: */ | ||
1394 | printk(KERN_DEBUG "original variable MTRRs\n"); | ||
1395 | print_out_mtrr_range_state(); | ||
1396 | |||
1397 | memset(range, 0, sizeof(range)); | ||
1398 | extra_remove_size = 0; | ||
1399 | extra_remove_base = 1 << (32 - PAGE_SHIFT); | ||
1400 | if (mtrr_tom2) | ||
1401 | extra_remove_size = | ||
1402 | (mtrr_tom2 >> PAGE_SHIFT) - extra_remove_base; | ||
1403 | nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base, | ||
1404 | extra_remove_size); | ||
1405 | /* | ||
1406 | * [0, 1M) should always be coverred by var mtrr with WB | ||
1407 | * and fixed mtrrs should take effective before var mtrr for it | ||
1408 | */ | ||
1409 | nr_range = add_range_with_merge(range, nr_range, 0, | ||
1410 | (1ULL<<(20 - PAGE_SHIFT)) - 1); | ||
1411 | /* sort the ranges */ | ||
1412 | sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL); | ||
1413 | |||
1414 | range_sums = sum_ranges(range, nr_range); | ||
1415 | printk(KERN_INFO "total RAM coverred: %ldM\n", | ||
1416 | range_sums >> (20 - PAGE_SHIFT)); | ||
1417 | |||
1418 | if (mtrr_chunk_size && mtrr_gran_size) { | ||
1419 | i = 0; | ||
1420 | mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size, | ||
1421 | extra_remove_base, extra_remove_size, i); | ||
1422 | |||
1423 | mtrr_print_out_one_result(i); | ||
1424 | |||
1425 | if (!result[i].bad) { | ||
1426 | set_var_mtrr_all(address_bits); | ||
1427 | return 1; | ||
1428 | } | ||
1429 | printk(KERN_INFO "invalid mtrr_gran_size or mtrr_chunk_size, " | ||
1430 | "will find optimal one\n"); | ||
1431 | } | ||
1432 | |||
1433 | i = 0; | ||
1434 | memset(min_loss_pfn, 0xff, sizeof(min_loss_pfn)); | ||
1435 | memset(result, 0, sizeof(result)); | ||
1436 | for (gran_size = (1ULL<<16); gran_size < (1ULL<<32); gran_size <<= 1) { | ||
1437 | |||
1438 | for (chunk_size = gran_size; chunk_size < (1ULL<<32); | ||
1439 | chunk_size <<= 1) { | ||
1440 | |||
1441 | if (i >= NUM_RESULT) | ||
1442 | continue; | ||
1443 | |||
1444 | mtrr_calc_range_state(chunk_size, gran_size, | ||
1445 | extra_remove_base, extra_remove_size, i); | ||
1446 | if (debug_print) { | ||
1447 | mtrr_print_out_one_result(i); | ||
1448 | printk(KERN_INFO "\n"); | ||
1449 | } | ||
1450 | |||
1451 | i++; | ||
1452 | } | ||
1453 | } | ||
1454 | |||
1455 | /* try to find the optimal index */ | ||
1456 | index_good = mtrr_search_optimal_index(); | ||
1457 | |||
1458 | if (index_good != -1) { | ||
1459 | printk(KERN_INFO "Found optimal setting for mtrr clean up\n"); | ||
1460 | i = index_good; | ||
1461 | mtrr_print_out_one_result(i); | ||
1462 | |||
1463 | /* convert ranges to var ranges state */ | ||
1464 | chunk_size = result[i].chunk_sizek; | ||
1465 | chunk_size <<= 10; | ||
1466 | gran_size = result[i].gran_sizek; | ||
1467 | gran_size <<= 10; | ||
1468 | x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size); | ||
1469 | set_var_mtrr_all(address_bits); | ||
1470 | printk(KERN_DEBUG "New variable MTRRs\n"); | ||
1471 | print_out_mtrr_range_state(); | ||
1472 | return 1; | ||
1473 | } else { | ||
1474 | /* print out all */ | ||
1475 | for (i = 0; i < NUM_RESULT; i++) | ||
1476 | mtrr_print_out_one_result(i); | ||
1477 | } | ||
1478 | |||
1479 | printk(KERN_INFO "mtrr_cleanup: can not find optimal value\n"); | ||
1480 | printk(KERN_INFO "please specify mtrr_gran_size/mtrr_chunk_size\n"); | ||
1481 | |||
1482 | return 0; | ||
1483 | } | ||
1484 | #else | ||
1485 | static int __init mtrr_cleanup(unsigned address_bits) | ||
1486 | { | ||
1487 | return 0; | ||
1488 | } | ||
1489 | #endif | ||
1490 | |||
1491 | static int __initdata changed_by_mtrr_cleanup; | ||
1492 | |||
1493 | static int disable_mtrr_trim; | ||
1494 | |||
1495 | static int __init disable_mtrr_trim_setup(char *str) | ||
1496 | { | ||
1497 | disable_mtrr_trim = 1; | ||
1498 | return 0; | ||
1499 | } | ||
1500 | early_param("disable_mtrr_trim", disable_mtrr_trim_setup); | ||
1501 | |||
1502 | /* | ||
1503 | * Newer AMD K8s and later CPUs have a special magic MSR way to force WB | ||
1504 | * for memory >4GB. Check for that here. | ||
1505 | * Note this won't check if the MTRRs < 4GB where the magic bit doesn't | ||
1506 | * apply to are wrong, but so far we don't know of any such case in the wild. | ||
1507 | */ | ||
1508 | #define Tom2Enabled (1U << 21) | ||
1509 | #define Tom2ForceMemTypeWB (1U << 22) | ||
1510 | |||
1511 | int __init amd_special_default_mtrr(void) | ||
1512 | { | ||
1513 | u32 l, h; | ||
1514 | |||
1515 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||
1516 | return 0; | ||
1517 | if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11) | ||
1518 | return 0; | ||
1519 | /* In case some hypervisor doesn't pass SYSCFG through */ | ||
1520 | if (rdmsr_safe(MSR_K8_SYSCFG, &l, &h) < 0) | ||
1521 | return 0; | ||
1522 | /* | ||
1523 | * Memory between 4GB and top of mem is forced WB by this magic bit. | ||
1524 | * Reserved before K8RevF, but should be zero there. | ||
1525 | */ | ||
1526 | if ((l & (Tom2Enabled | Tom2ForceMemTypeWB)) == | ||
1527 | (Tom2Enabled | Tom2ForceMemTypeWB)) | ||
1528 | return 1; | ||
1529 | return 0; | ||
1530 | } | ||
1531 | |||
1532 | static u64 __init real_trim_memory(unsigned long start_pfn, | ||
1533 | unsigned long limit_pfn) | ||
1534 | { | ||
1535 | u64 trim_start, trim_size; | ||
1536 | trim_start = start_pfn; | ||
1537 | trim_start <<= PAGE_SHIFT; | ||
1538 | trim_size = limit_pfn; | ||
1539 | trim_size <<= PAGE_SHIFT; | ||
1540 | trim_size -= trim_start; | ||
1541 | |||
1542 | return e820_update_range(trim_start, trim_size, E820_RAM, | ||
1543 | E820_RESERVED); | ||
1544 | } | ||
1545 | /** | ||
1546 | * mtrr_trim_uncached_memory - trim RAM not covered by MTRRs | ||
1547 | * @end_pfn: ending page frame number | ||
1548 | * | ||
1549 | * Some buggy BIOSes don't setup the MTRRs properly for systems with certain | ||
1550 | * memory configurations. This routine checks that the highest MTRR matches | ||
1551 | * the end of memory, to make sure the MTRRs having a write back type cover | ||
1552 | * all of the memory the kernel is intending to use. If not, it'll trim any | ||
1553 | * memory off the end by adjusting end_pfn, removing it from the kernel's | ||
1554 | * allocation pools, warning the user with an obnoxious message. | ||
1555 | */ | ||
1556 | int __init mtrr_trim_uncached_memory(unsigned long end_pfn) | ||
1557 | { | ||
1558 | unsigned long i, base, size, highest_pfn = 0, def, dummy; | ||
1559 | mtrr_type type; | ||
1560 | u64 total_trim_size; | ||
1561 | |||
1562 | /* extra one for all 0 */ | ||
1563 | int num[MTRR_NUM_TYPES + 1]; | ||
1564 | /* | ||
1565 | * Make sure we only trim uncachable memory on machines that | ||
1566 | * support the Intel MTRR architecture: | ||
1567 | */ | ||
1568 | if (!is_cpu(INTEL) || disable_mtrr_trim) | ||
1569 | return 0; | ||
1570 | rdmsr(MTRRdefType_MSR, def, dummy); | ||
1571 | def &= 0xff; | ||
1572 | if (def != MTRR_TYPE_UNCACHABLE) | ||
1573 | return 0; | ||
1574 | |||
1575 | /* get it and store it aside */ | ||
1576 | memset(range_state, 0, sizeof(range_state)); | ||
1577 | for (i = 0; i < num_var_ranges; i++) { | ||
1578 | mtrr_if->get(i, &base, &size, &type); | ||
1579 | range_state[i].base_pfn = base; | ||
1580 | range_state[i].size_pfn = size; | ||
1581 | range_state[i].type = type; | ||
1582 | } | ||
1583 | |||
1584 | /* Find highest cached pfn */ | ||
1585 | for (i = 0; i < num_var_ranges; i++) { | ||
1586 | type = range_state[i].type; | ||
1587 | if (type != MTRR_TYPE_WRBACK) | ||
1588 | continue; | ||
1589 | base = range_state[i].base_pfn; | ||
1590 | size = range_state[i].size_pfn; | ||
1591 | if (highest_pfn < base + size) | ||
1592 | highest_pfn = base + size; | ||
1593 | } | ||
1594 | |||
1595 | /* kvm/qemu doesn't have mtrr set right, don't trim them all */ | ||
1596 | if (!highest_pfn) { | ||
1597 | printk(KERN_INFO "CPU MTRRs all blank - virtualized system.\n"); | ||
1598 | return 0; | ||
1599 | } | ||
1600 | |||
1601 | /* check entries number */ | ||
1602 | memset(num, 0, sizeof(num)); | ||
1603 | for (i = 0; i < num_var_ranges; i++) { | ||
1604 | type = range_state[i].type; | ||
1605 | if (type >= MTRR_NUM_TYPES) | ||
1606 | continue; | ||
1607 | size = range_state[i].size_pfn; | ||
1608 | if (!size) | ||
1609 | type = MTRR_NUM_TYPES; | ||
1610 | num[type]++; | ||
1611 | } | ||
1612 | |||
1613 | /* no entry for WB? */ | ||
1614 | if (!num[MTRR_TYPE_WRBACK]) | ||
1615 | return 0; | ||
1616 | |||
1617 | /* check if we only had WB and UC */ | ||
1618 | if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != | ||
1619 | num_var_ranges - num[MTRR_NUM_TYPES]) | ||
1620 | return 0; | ||
1621 | |||
1622 | memset(range, 0, sizeof(range)); | ||
1623 | nr_range = 0; | ||
1624 | if (mtrr_tom2) { | ||
1625 | range[nr_range].start = (1ULL<<(32 - PAGE_SHIFT)); | ||
1626 | range[nr_range].end = (mtrr_tom2 >> PAGE_SHIFT) - 1; | ||
1627 | if (highest_pfn < range[nr_range].end + 1) | ||
1628 | highest_pfn = range[nr_range].end + 1; | ||
1629 | nr_range++; | ||
1630 | } | ||
1631 | nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0); | ||
1632 | |||
1633 | total_trim_size = 0; | ||
1634 | /* check the head */ | ||
1635 | if (range[0].start) | ||
1636 | total_trim_size += real_trim_memory(0, range[0].start); | ||
1637 | /* check the holes */ | ||
1638 | for (i = 0; i < nr_range - 1; i++) { | ||
1639 | if (range[i].end + 1 < range[i+1].start) | ||
1640 | total_trim_size += real_trim_memory(range[i].end + 1, | ||
1641 | range[i+1].start); | ||
1642 | } | ||
1643 | /* check the top */ | ||
1644 | i = nr_range - 1; | ||
1645 | if (range[i].end + 1 < end_pfn) | ||
1646 | total_trim_size += real_trim_memory(range[i].end + 1, | ||
1647 | end_pfn); | ||
1648 | |||
1649 | if (total_trim_size) { | ||
1650 | printk(KERN_WARNING "WARNING: BIOS bug: CPU MTRRs don't cover" | ||
1651 | " all of memory, losing %lluMB of RAM.\n", | ||
1652 | total_trim_size >> 20); | ||
1653 | |||
1654 | if (!changed_by_mtrr_cleanup) | ||
1655 | WARN_ON(1); | ||
1656 | |||
1657 | printk(KERN_INFO "update e820 for mtrr\n"); | ||
1658 | update_e820(); | ||
1659 | |||
1660 | return 1; | ||
1661 | } | ||
1662 | |||
1663 | return 0; | ||
1664 | } | ||
1665 | 614 | ||
1666 | /** | 615 | /** |
1667 | * mtrr_bp_init - initialize mtrrs on the boot CPU | 616 | * mtrr_bp_init - initialize mtrrs on the boot CPU |
diff --git a/arch/x86/kernel/cpu/mtrr/mtrr.h b/arch/x86/kernel/cpu/mtrr/mtrr.h index ffd60409cc6d..77f67f7b347a 100644 --- a/arch/x86/kernel/cpu/mtrr/mtrr.h +++ b/arch/x86/kernel/cpu/mtrr/mtrr.h | |||
@@ -79,6 +79,7 @@ extern struct mtrr_ops * mtrr_if; | |||
79 | 79 | ||
80 | extern unsigned int num_var_ranges; | 80 | extern unsigned int num_var_ranges; |
81 | extern u64 mtrr_tom2; | 81 | extern u64 mtrr_tom2; |
82 | extern struct mtrr_state_type mtrr_state; | ||
82 | 83 | ||
83 | void mtrr_state_warn(void); | 84 | void mtrr_state_warn(void); |
84 | const char *mtrr_attrib_to_str(int x); | 85 | const char *mtrr_attrib_to_str(int x); |
@@ -88,3 +89,6 @@ void mtrr_wrmsr(unsigned, unsigned, unsigned); | |||
88 | int amd_init_mtrr(void); | 89 | int amd_init_mtrr(void); |
89 | int cyrix_init_mtrr(void); | 90 | int cyrix_init_mtrr(void); |
90 | int centaur_init_mtrr(void); | 91 | int centaur_init_mtrr(void); |
92 | |||
93 | extern int changed_by_mtrr_cleanup; | ||
94 | extern int mtrr_cleanup(unsigned address_bits); | ||