1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#define DEBUG
35
36#include <linux/types.h>
37
38#include <linux/stop_machine.h>
39#include <linux/kvm_para.h>
40#include <linux/uaccess.h>
41#include <linux/module.h>
42#include <linux/mutex.h>
43#include <linux/init.h>
44#include <linux/sort.h>
45#include <linux/cpu.h>
46#include <linux/pci.h>
47#include <linux/smp.h>
48#include <linux/syscore_ops.h>
49
50#include <asm/processor.h>
51#include <asm/e820.h>
52#include <asm/mtrr.h>
53#include <asm/msr.h>
54
55#include "mtrr.h"
56
57u32 num_var_ranges;
58
59unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
60static DEFINE_MUTEX(mtrr_mutex);
61
62u64 size_or_mask, size_and_mask;
63static bool mtrr_aps_delayed_init;
64
65static const struct mtrr_ops *mtrr_ops[X86_VENDOR_NUM];
66
67const struct mtrr_ops *mtrr_if;
68
69static void set_mtrr(unsigned int reg, unsigned long base,
70 unsigned long size, mtrr_type type);
71
72void set_mtrr_ops(const struct mtrr_ops *ops)
73{
74 if (ops->vendor && ops->vendor < X86_VENDOR_NUM)
75 mtrr_ops[ops->vendor] = ops;
76}
77
78
79static int have_wrcomb(void)
80{
81 struct pci_dev *dev;
82
83 dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, NULL);
84 if (dev != NULL) {
85
86
87
88
89
90 if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
91 dev->device == PCI_DEVICE_ID_SERVERWORKS_LE &&
92 dev->revision <= 5) {
93 pr_info("mtrr: Serverworks LE rev < 6 detected. Write-combining disabled.\n");
94 pci_dev_put(dev);
95 return 0;
96 }
97
98
99
100
101 if (dev->vendor == PCI_VENDOR_ID_INTEL &&
102 dev->device == PCI_DEVICE_ID_INTEL_82451NX) {
103 pr_info("mtrr: Intel 450NX MMC detected. Write-combining disabled.\n");
104 pci_dev_put(dev);
105 return 0;
106 }
107 pci_dev_put(dev);
108 }
109 return mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0;
110}
111
112
113static void __init set_num_var_ranges(void)
114{
115 unsigned long config = 0, dummy;
116
117 if (use_intel())
118 rdmsr(MSR_MTRRcap, config, dummy);
119 else if (is_cpu(AMD))
120 config = 2;
121 else if (is_cpu(CYRIX) || is_cpu(CENTAUR))
122 config = 8;
123
124 num_var_ranges = config & 0xff;
125}
126
127static void __init init_table(void)
128{
129 int i, max;
130
131 max = num_var_ranges;
132 for (i = 0; i < max; i++)
133 mtrr_usage_table[i] = 1;
134}
135
136struct set_mtrr_data {
137 unsigned long smp_base;
138 unsigned long smp_size;
139 unsigned int smp_reg;
140 mtrr_type smp_type;
141};
142
143
144
145
146
147
148
149
150static int mtrr_rendezvous_handler(void *info)
151{
152 struct set_mtrr_data *data = info;
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 if (data->smp_reg != ~0U) {
168 mtrr_if->set(data->smp_reg, data->smp_base,
169 data->smp_size, data->smp_type);
170 } else if (mtrr_aps_delayed_init || !cpu_online(smp_processor_id())) {
171 mtrr_if->set_all();
172 }
173 return 0;
174}
175
176static inline int types_compatible(mtrr_type type1, mtrr_type type2)
177{
178 return type1 == MTRR_TYPE_UNCACHABLE ||
179 type2 == MTRR_TYPE_UNCACHABLE ||
180 (type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK) ||
181 (type1 == MTRR_TYPE_WRBACK && type2 == MTRR_TYPE_WRTHROUGH);
182}
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218static void
219set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)
220{
221 struct set_mtrr_data data = { .smp_reg = reg,
222 .smp_base = base,
223 .smp_size = size,
224 .smp_type = type
225 };
226
227 stop_machine(mtrr_rendezvous_handler, &data, cpu_online_mask);
228}
229
230static void set_mtrr_from_inactive_cpu(unsigned int reg, unsigned long base,
231 unsigned long size, mtrr_type type)
232{
233 struct set_mtrr_data data = { .smp_reg = reg,
234 .smp_base = base,
235 .smp_size = size,
236 .smp_type = type
237 };
238
239 stop_machine_from_inactive_cpu(mtrr_rendezvous_handler, &data,
240 cpu_callout_mask);
241}
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278int mtrr_add_page(unsigned long base, unsigned long size,
279 unsigned int type, bool increment)
280{
281 unsigned long lbase, lsize;
282 int i, replace, error;
283 mtrr_type ltype;
284
285 if (!mtrr_if)
286 return -ENXIO;
287
288 error = mtrr_if->validate_add_page(base, size, type);
289 if (error)
290 return error;
291
292 if (type >= MTRR_NUM_TYPES) {
293 pr_warning("mtrr: type: %u invalid\n", type);
294 return -EINVAL;
295 }
296
297
298 if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb()) {
299 pr_warning("mtrr: your processor doesn't support write-combining\n");
300 return -ENOSYS;
301 }
302
303 if (!size) {
304 pr_warning("mtrr: zero sized request\n");
305 return -EINVAL;
306 }
307
308 if (base & size_or_mask || size & size_or_mask) {
309 pr_warning("mtrr: base or size exceeds the MTRR width\n");
310 return -EINVAL;
311 }
312
313 error = -EINVAL;
314 replace = -1;
315
316
317 get_online_cpus();
318
319
320 mutex_lock(&mtrr_mutex);
321 for (i = 0; i < num_var_ranges; ++i) {
322 mtrr_if->get(i, &lbase, &lsize, <ype);
323 if (!lsize || base > lbase + lsize - 1 ||
324 base + size - 1 < lbase)
325 continue;
326
327
328
329
330 if (base < lbase || base + size - 1 > lbase + lsize - 1) {
331 if (base <= lbase &&
332 base + size - 1 >= lbase + lsize - 1) {
333
334 if (type == ltype) {
335 replace = replace == -1 ? i : -2;
336 continue;
337 } else if (types_compatible(type, ltype))
338 continue;
339 }
340 pr_warning("mtrr: 0x%lx000,0x%lx000 overlaps existing"
341 " 0x%lx000,0x%lx000\n", base, size, lbase,
342 lsize);
343 goto out;
344 }
345
346 if (ltype != type) {
347 if (types_compatible(type, ltype))
348 continue;
349 pr_warning("mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n",
350 base, size, mtrr_attrib_to_str(ltype),
351 mtrr_attrib_to_str(type));
352 goto out;
353 }
354 if (increment)
355 ++mtrr_usage_table[i];
356 error = i;
357 goto out;
358 }
359
360 i = mtrr_if->get_free_region(base, size, replace);
361 if (i >= 0) {
362 set_mtrr(i, base, size, type);
363 if (likely(replace < 0)) {
364 mtrr_usage_table[i] = 1;
365 } else {
366 mtrr_usage_table[i] = mtrr_usage_table[replace];
367 if (increment)
368 mtrr_usage_table[i]++;
369 if (unlikely(replace != i)) {
370 set_mtrr(replace, 0, 0, 0);
371 mtrr_usage_table[replace] = 0;
372 }
373 }
374 } else {
375 pr_info("mtrr: no more MTRRs available\n");
376 }
377 error = i;
378 out:
379 mutex_unlock(&mtrr_mutex);
380 put_online_cpus();
381 return error;
382}
383
384static int mtrr_check(unsigned long base, unsigned long size)
385{
386 if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
387 pr_warning("mtrr: size and base must be multiples of 4 kiB\n");
388 pr_debug("mtrr: size: 0x%lx base: 0x%lx\n", size, base);
389 dump_stack();
390 return -1;
391 }
392 return 0;
393}
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430int mtrr_add(unsigned long base, unsigned long size, unsigned int type,
431 bool increment)
432{
433 if (mtrr_check(base, size))
434 return -EINVAL;
435 return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
436 increment);
437}
438EXPORT_SYMBOL(mtrr_add);
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454int mtrr_del_page(int reg, unsigned long base, unsigned long size)
455{
456 int i, max;
457 mtrr_type ltype;
458 unsigned long lbase, lsize;
459 int error = -EINVAL;
460
461 if (!mtrr_if)
462 return -ENXIO;
463
464 max = num_var_ranges;
465
466 get_online_cpus();
467 mutex_lock(&mtrr_mutex);
468 if (reg < 0) {
469
470 for (i = 0; i < max; ++i) {
471 mtrr_if->get(i, &lbase, &lsize, <ype);
472 if (lbase == base && lsize == size) {
473 reg = i;
474 break;
475 }
476 }
477 if (reg < 0) {
478 pr_debug("mtrr: no MTRR for %lx000,%lx000 found\n",
479 base, size);
480 goto out;
481 }
482 }
483 if (reg >= max) {
484 pr_warning("mtrr: register: %d too big\n", reg);
485 goto out;
486 }
487 mtrr_if->get(reg, &lbase, &lsize, <ype);
488 if (lsize < 1) {
489 pr_warning("mtrr: MTRR %d not used\n", reg);
490 goto out;
491 }
492 if (mtrr_usage_table[reg] < 1) {
493 pr_warning("mtrr: reg: %d has count=0\n", reg);
494 goto out;
495 }
496 if (--mtrr_usage_table[reg] < 1)
497 set_mtrr(reg, 0, 0, 0);
498 error = reg;
499 out:
500 mutex_unlock(&mtrr_mutex);
501 put_online_cpus();
502 return error;
503}
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519int mtrr_del(int reg, unsigned long base, unsigned long size)
520{
521 if (mtrr_check(base, size))
522 return -EINVAL;
523 return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
524}
525EXPORT_SYMBOL(mtrr_del);
526
527
528
529
530
531
532static void __init init_ifs(void)
533{
534#ifndef CONFIG_X86_64
535 amd_init_mtrr();
536 cyrix_init_mtrr();
537 centaur_init_mtrr();
538#endif
539}
540
541
542
543
544struct mtrr_value {
545 mtrr_type ltype;
546 unsigned long lbase;
547 unsigned long lsize;
548};
549
550static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES];
551
552static int mtrr_save(void)
553{
554 int i;
555
556 for (i = 0; i < num_var_ranges; i++) {
557 mtrr_if->get(i, &mtrr_value[i].lbase,
558 &mtrr_value[i].lsize,
559 &mtrr_value[i].ltype);
560 }
561 return 0;
562}
563
564static void mtrr_restore(void)
565{
566 int i;
567
568 for (i = 0; i < num_var_ranges; i++) {
569 if (mtrr_value[i].lsize) {
570 set_mtrr(i, mtrr_value[i].lbase,
571 mtrr_value[i].lsize,
572 mtrr_value[i].ltype);
573 }
574 }
575}
576
577
578
579static struct syscore_ops mtrr_syscore_ops = {
580 .suspend = mtrr_save,
581 .resume = mtrr_restore,
582};
583
584int __initdata changed_by_mtrr_cleanup;
585
586
587
588
589
590
591
592
593void __init mtrr_bp_init(void)
594{
595 u32 phys_addr;
596
597 init_ifs();
598
599 phys_addr = 32;
600
601 if (cpu_has_mtrr) {
602 mtrr_if = &generic_mtrr_ops;
603 size_or_mask = 0xff000000;
604 size_and_mask = 0x00f00000;
605 phys_addr = 36;
606
607
608
609
610
611
612 if (cpuid_eax(0x80000000) >= 0x80000008) {
613 phys_addr = cpuid_eax(0x80000008) & 0xff;
614
615 if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
616 boot_cpu_data.x86 == 0xF &&
617 boot_cpu_data.x86_model == 0x3 &&
618 (boot_cpu_data.x86_mask == 0x3 ||
619 boot_cpu_data.x86_mask == 0x4))
620 phys_addr = 36;
621
622 size_or_mask = ~((1ULL << (phys_addr - PAGE_SHIFT)) - 1);
623 size_and_mask = ~size_or_mask & 0xfffff00000ULL;
624 } else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
625 boot_cpu_data.x86 == 6) {
626
627
628
629
630 size_or_mask = 0xfff00000;
631 size_and_mask = 0;
632 phys_addr = 32;
633 }
634 } else {
635 switch (boot_cpu_data.x86_vendor) {
636 case X86_VENDOR_AMD:
637 if (cpu_has_k6_mtrr) {
638
639 mtrr_if = mtrr_ops[X86_VENDOR_AMD];
640 size_or_mask = 0xfff00000;
641 size_and_mask = 0;
642 }
643 break;
644 case X86_VENDOR_CENTAUR:
645 if (cpu_has_centaur_mcr) {
646 mtrr_if = mtrr_ops[X86_VENDOR_CENTAUR];
647 size_or_mask = 0xfff00000;
648 size_and_mask = 0;
649 }
650 break;
651 case X86_VENDOR_CYRIX:
652 if (cpu_has_cyrix_arr) {
653 mtrr_if = mtrr_ops[X86_VENDOR_CYRIX];
654 size_or_mask = 0xfff00000;
655 size_and_mask = 0;
656 }
657 break;
658 default:
659 break;
660 }
661 }
662
663 if (mtrr_if) {
664 set_num_var_ranges();
665 init_table();
666 if (use_intel()) {
667 get_mtrr_state();
668
669 if (mtrr_cleanup(phys_addr)) {
670 changed_by_mtrr_cleanup = 1;
671 mtrr_if->set_all();
672 }
673 }
674 }
675}
676
677void mtrr_ap_init(void)
678{
679 if (!use_intel() || mtrr_aps_delayed_init)
680 return;
681
682
683
684
685
686
687
688
689
690
691
692
693
694 set_mtrr_from_inactive_cpu(~0U, 0, 0, 0);
695}
696
697
698
699
700void mtrr_save_state(void)
701{
702 smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1);
703}
704
705void set_mtrr_aps_delayed_init(void)
706{
707 if (!use_intel())
708 return;
709
710 mtrr_aps_delayed_init = true;
711}
712
713
714
715
716void mtrr_aps_init(void)
717{
718 if (!use_intel())
719 return;
720
721
722
723
724
725
726 if (!mtrr_aps_delayed_init)
727 return;
728
729 set_mtrr(~0U, 0, 0, 0);
730 mtrr_aps_delayed_init = false;
731}
732
733void mtrr_bp_restore(void)
734{
735 if (!use_intel())
736 return;
737
738 mtrr_if->set_all();
739}
740
741static int __init mtrr_init_finialize(void)
742{
743 if (!mtrr_if)
744 return 0;
745
746 if (use_intel()) {
747 if (!changed_by_mtrr_cleanup)
748 mtrr_state_warn();
749 return 0;
750 }
751
752
753
754
755
756
757
758
759
760 register_syscore_ops(&mtrr_syscore_ops);
761
762 return 0;
763}
764subsys_initcall(mtrr_init_finialize);
765