163f9600fSJaswinder Singh Rajput /*
263f9600fSJaswinder Singh Rajput * MTRR (Memory Type Range Register) cleanup
363f9600fSJaswinder Singh Rajput *
463f9600fSJaswinder Singh Rajput * Copyright (C) 2009 Yinghai Lu
563f9600fSJaswinder Singh Rajput *
663f9600fSJaswinder Singh Rajput * This library is free software; you can redistribute it and/or
763f9600fSJaswinder Singh Rajput * modify it under the terms of the GNU Library General Public
863f9600fSJaswinder Singh Rajput * License as published by the Free Software Foundation; either
963f9600fSJaswinder Singh Rajput * version 2 of the License, or (at your option) any later version.
1063f9600fSJaswinder Singh Rajput *
1163f9600fSJaswinder Singh Rajput * This library is distributed in the hope that it will be useful,
1263f9600fSJaswinder Singh Rajput * but WITHOUT ANY WARRANTY; without even the implied warranty of
1363f9600fSJaswinder Singh Rajput * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1463f9600fSJaswinder Singh Rajput * Library General Public License for more details.
1563f9600fSJaswinder Singh Rajput *
1663f9600fSJaswinder Singh Rajput * You should have received a copy of the GNU Library General Public
1763f9600fSJaswinder Singh Rajput * License along with this library; if not, write to the Free
1863f9600fSJaswinder Singh Rajput * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
190d890355SYinghai Lu */
200d890355SYinghai Lu #include <linux/init.h>
210d890355SYinghai Lu #include <linux/pci.h>
220d890355SYinghai Lu #include <linux/smp.h>
230d890355SYinghai Lu #include <linux/cpu.h>
2463f9600fSJaswinder Singh Rajput #include <linux/mutex.h>
2563f9600fSJaswinder Singh Rajput #include <linux/uaccess.h>
2663f9600fSJaswinder Singh Rajput #include <linux/kvm_para.h>
2727811d8cSYinghai Lu #include <linux/range.h>
280d890355SYinghai Lu
2963f9600fSJaswinder Singh Rajput #include <asm/processor.h>
3066441bd3SIngo Molnar #include <asm/e820/api.h>
310d890355SYinghai Lu #include <asm/mtrr.h>
320d890355SYinghai Lu #include <asm/msr.h>
3363f9600fSJaswinder Singh Rajput
340d890355SYinghai Lu #include "mtrr.h"
350d890355SYinghai Lu
36e3d0e692SIngo Molnar struct var_mtrr_range_state {
37e3d0e692SIngo Molnar unsigned long base_pfn;
38e3d0e692SIngo Molnar unsigned long size_pfn;
39e3d0e692SIngo Molnar mtrr_type type;
40e3d0e692SIngo Molnar };
41e3d0e692SIngo Molnar
42e3d0e692SIngo Molnar struct var_mtrr_state {
43e3d0e692SIngo Molnar unsigned long range_startk;
44e3d0e692SIngo Molnar unsigned long range_sizek;
45e3d0e692SIngo Molnar unsigned long chunk_sizek;
46e3d0e692SIngo Molnar unsigned long gran_sizek;
47e3d0e692SIngo Molnar unsigned int reg;
48e3d0e692SIngo Molnar };
49e3d0e692SIngo Molnar
50e3d0e692SIngo Molnar /* Should be related to MTRR_VAR_RANGES nums */
51e3d0e692SIngo Molnar #define RANGE_NUM 256
52e3d0e692SIngo Molnar
5327811d8cSYinghai Lu static struct range __initdata range[RANGE_NUM];
54e3d0e692SIngo Molnar static int __initdata nr_range;
55e3d0e692SIngo Molnar
56e3d0e692SIngo Molnar static struct var_mtrr_range_state __initdata range_state[RANGE_NUM];
57e3d0e692SIngo Molnar
581b74dde7SChen Yucong #define BIOS_BUG_MSG \
5963f9600fSJaswinder Singh Rajput "WARNING: BIOS bug: VAR MTRR %d contains strange UC entry under 1M, check with your system vendor!\n"
600d890355SYinghai Lu
610d890355SYinghai Lu static int __init
x86_get_mtrr_mem_range(struct range * range,int nr_range,unsigned long extra_remove_base,unsigned long extra_remove_size)6227811d8cSYinghai Lu x86_get_mtrr_mem_range(struct range *range, int nr_range,
630d890355SYinghai Lu unsigned long extra_remove_base,
640d890355SYinghai Lu unsigned long extra_remove_size)
650d890355SYinghai Lu {
664e16c888SJaswinder Singh Rajput unsigned long base, size;
670d890355SYinghai Lu mtrr_type type;
684e16c888SJaswinder Singh Rajput int i;
690d890355SYinghai Lu
700d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
710d890355SYinghai Lu type = range_state[i].type;
720d890355SYinghai Lu if (type != MTRR_TYPE_WRBACK)
730d890355SYinghai Lu continue;
740d890355SYinghai Lu base = range_state[i].base_pfn;
750d890355SYinghai Lu size = range_state[i].size_pfn;
7627811d8cSYinghai Lu nr_range = add_range_with_merge(range, RANGE_NUM, nr_range,
77e9a0064aSYinghai Lu base, base + size);
780d890355SYinghai Lu }
79*7c1dee73SBorislav Petkov (AMD)
80*7c1dee73SBorislav Petkov (AMD) Dprintk("After WB checking\n");
810d890355SYinghai Lu for (i = 0; i < nr_range; i++)
82*7c1dee73SBorislav Petkov (AMD) Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
83e9a0064aSYinghai Lu range[i].start, range[i].end);
840d890355SYinghai Lu
8563f9600fSJaswinder Singh Rajput /* Take out UC ranges: */
860d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
870d890355SYinghai Lu type = range_state[i].type;
880d890355SYinghai Lu if (type != MTRR_TYPE_UNCACHABLE &&
890d890355SYinghai Lu type != MTRR_TYPE_WRPROT)
900d890355SYinghai Lu continue;
910d890355SYinghai Lu size = range_state[i].size_pfn;
920d890355SYinghai Lu if (!size)
930d890355SYinghai Lu continue;
940d890355SYinghai Lu base = range_state[i].base_pfn;
95f0348c43SYinghai Lu if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed &&
969b3aca62SToshi Kani (mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED) &&
979b3aca62SToshi Kani (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) {
98f0348c43SYinghai Lu /* Var MTRR contains UC entry below 1M? Skip it: */
991b74dde7SChen Yucong pr_warn(BIOS_BUG_MSG, i);
100f0348c43SYinghai Lu if (base + size <= (1<<(20-PAGE_SHIFT)))
101f0348c43SYinghai Lu continue;
102f0348c43SYinghai Lu size -= (1<<(20-PAGE_SHIFT)) - base;
103f0348c43SYinghai Lu base = 1<<(20-PAGE_SHIFT);
104f0348c43SYinghai Lu }
105e9a0064aSYinghai Lu subtract_range(range, RANGE_NUM, base, base + size);
1060d890355SYinghai Lu }
1070d890355SYinghai Lu if (extra_remove_size)
10827811d8cSYinghai Lu subtract_range(range, RANGE_NUM, extra_remove_base,
109e9a0064aSYinghai Lu extra_remove_base + extra_remove_size);
1100d890355SYinghai Lu
111*7c1dee73SBorislav Petkov (AMD) Dprintk("After UC checking\n");
1120d890355SYinghai Lu for (i = 0; i < RANGE_NUM; i++) {
1130d890355SYinghai Lu if (!range[i].end)
1140d890355SYinghai Lu continue;
115*7c1dee73SBorislav Petkov (AMD)
116*7c1dee73SBorislav Petkov (AMD) Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
117e9a0064aSYinghai Lu range[i].start, range[i].end);
1180d890355SYinghai Lu }
1190d890355SYinghai Lu
1200d890355SYinghai Lu /* sort the ranges */
1215bf65b9bSYinghai Lu nr_range = clean_sort_range(range, RANGE_NUM);
122*7c1dee73SBorislav Petkov (AMD)
123*7c1dee73SBorislav Petkov (AMD) Dprintk("After sorting\n");
1240d890355SYinghai Lu for (i = 0; i < nr_range; i++)
125*7c1dee73SBorislav Petkov (AMD) Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
126e9a0064aSYinghai Lu range[i].start, range[i].end);
1270d890355SYinghai Lu
1280d890355SYinghai Lu return nr_range;
1290d890355SYinghai Lu }
1300d890355SYinghai Lu
1310d890355SYinghai Lu #ifdef CONFIG_MTRR_SANITIZER
1320d890355SYinghai Lu
sum_ranges(struct range * range,int nr_range)13327811d8cSYinghai Lu static unsigned long __init sum_ranges(struct range *range, int nr_range)
1340d890355SYinghai Lu {
13563f9600fSJaswinder Singh Rajput unsigned long sum = 0;
1360d890355SYinghai Lu int i;
1370d890355SYinghai Lu
1380d890355SYinghai Lu for (i = 0; i < nr_range; i++)
139e9a0064aSYinghai Lu sum += range[i].end - range[i].start;
1400d890355SYinghai Lu
1410d890355SYinghai Lu return sum;
1420d890355SYinghai Lu }
1430d890355SYinghai Lu
1440d890355SYinghai Lu static int enable_mtrr_cleanup __initdata =
1450d890355SYinghai Lu CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT;
1460d890355SYinghai Lu
disable_mtrr_cleanup_setup(char * str)1470d890355SYinghai Lu static int __init disable_mtrr_cleanup_setup(char *str)
1480d890355SYinghai Lu {
1490d890355SYinghai Lu enable_mtrr_cleanup = 0;
1500d890355SYinghai Lu return 0;
1510d890355SYinghai Lu }
1520d890355SYinghai Lu early_param("disable_mtrr_cleanup", disable_mtrr_cleanup_setup);
1530d890355SYinghai Lu
enable_mtrr_cleanup_setup(char * str)1540d890355SYinghai Lu static int __init enable_mtrr_cleanup_setup(char *str)
1550d890355SYinghai Lu {
1560d890355SYinghai Lu enable_mtrr_cleanup = 1;
1570d890355SYinghai Lu return 0;
1580d890355SYinghai Lu }
1590d890355SYinghai Lu early_param("enable_mtrr_cleanup", enable_mtrr_cleanup_setup);
1600d890355SYinghai Lu
1610d890355SYinghai Lu static void __init
set_var_mtrr(unsigned int reg,unsigned long basek,unsigned long sizek,unsigned char type)1620d890355SYinghai Lu set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
163f6b98064SJuergen Gross unsigned char type)
1640d890355SYinghai Lu {
1650d890355SYinghai Lu u32 base_lo, base_hi, mask_lo, mask_hi;
1660d890355SYinghai Lu u64 base, mask;
1670d890355SYinghai Lu
1680d890355SYinghai Lu if (!sizek) {
1690d890355SYinghai Lu fill_mtrr_var_range(reg, 0, 0, 0, 0);
1700d890355SYinghai Lu return;
1710d890355SYinghai Lu }
1720d890355SYinghai Lu
173f6b98064SJuergen Gross mask = (1ULL << boot_cpu_data.x86_phys_bits) - 1;
1740d890355SYinghai Lu mask &= ~((((u64)sizek) << 10) - 1);
1750d890355SYinghai Lu
1760d890355SYinghai Lu base = ((u64)basek) << 10;
1770d890355SYinghai Lu
1780d890355SYinghai Lu base |= type;
1790d890355SYinghai Lu mask |= 0x800;
1800d890355SYinghai Lu
1810d890355SYinghai Lu base_lo = base & ((1ULL<<32) - 1);
1820d890355SYinghai Lu base_hi = base >> 32;
1830d890355SYinghai Lu
1840d890355SYinghai Lu mask_lo = mask & ((1ULL<<32) - 1);
1850d890355SYinghai Lu mask_hi = mask >> 32;
1860d890355SYinghai Lu
1870d890355SYinghai Lu fill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi);
1880d890355SYinghai Lu }
1890d890355SYinghai Lu
1900d890355SYinghai Lu static void __init
save_var_mtrr(unsigned int reg,unsigned long basek,unsigned long sizek,unsigned char type)1910d890355SYinghai Lu save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
1920d890355SYinghai Lu unsigned char type)
1930d890355SYinghai Lu {
1940d890355SYinghai Lu range_state[reg].base_pfn = basek >> (PAGE_SHIFT - 10);
1950d890355SYinghai Lu range_state[reg].size_pfn = sizek >> (PAGE_SHIFT - 10);
1960d890355SYinghai Lu range_state[reg].type = type;
1970d890355SYinghai Lu }
1980d890355SYinghai Lu
set_var_mtrr_all(void)199f6b98064SJuergen Gross static void __init set_var_mtrr_all(void)
2000d890355SYinghai Lu {
2010d890355SYinghai Lu unsigned long basek, sizek;
2020d890355SYinghai Lu unsigned char type;
2030d890355SYinghai Lu unsigned int reg;
2040d890355SYinghai Lu
2050d890355SYinghai Lu for (reg = 0; reg < num_var_ranges; reg++) {
2060d890355SYinghai Lu basek = range_state[reg].base_pfn << (PAGE_SHIFT - 10);
2070d890355SYinghai Lu sizek = range_state[reg].size_pfn << (PAGE_SHIFT - 10);
2080d890355SYinghai Lu type = range_state[reg].type;
2090d890355SYinghai Lu
210f6b98064SJuergen Gross set_var_mtrr(reg, basek, sizek, type);
2110d890355SYinghai Lu }
2120d890355SYinghai Lu }
2130d890355SYinghai Lu
to_size_factor(unsigned long sizek,char * factorp)2140d890355SYinghai Lu static unsigned long to_size_factor(unsigned long sizek, char *factorp)
2150d890355SYinghai Lu {
2160d890355SYinghai Lu unsigned long base = sizek;
21763f9600fSJaswinder Singh Rajput char factor;
2180d890355SYinghai Lu
2190d890355SYinghai Lu if (base & ((1<<10) - 1)) {
22063f9600fSJaswinder Singh Rajput /* Not MB-aligned: */
2210d890355SYinghai Lu factor = 'K';
2220d890355SYinghai Lu } else if (base & ((1<<20) - 1)) {
2230d890355SYinghai Lu factor = 'M';
2240d890355SYinghai Lu base >>= 10;
2250d890355SYinghai Lu } else {
2260d890355SYinghai Lu factor = 'G';
2270d890355SYinghai Lu base >>= 20;
2280d890355SYinghai Lu }
2290d890355SYinghai Lu
2300d890355SYinghai Lu *factorp = factor;
2310d890355SYinghai Lu
2320d890355SYinghai Lu return base;
2330d890355SYinghai Lu }
2340d890355SYinghai Lu
2350d890355SYinghai Lu static unsigned int __init
range_to_mtrr(unsigned int reg,unsigned long range_startk,unsigned long range_sizek,unsigned char type)2360d890355SYinghai Lu range_to_mtrr(unsigned int reg, unsigned long range_startk,
2370d890355SYinghai Lu unsigned long range_sizek, unsigned char type)
2380d890355SYinghai Lu {
2390d890355SYinghai Lu if (!range_sizek || (reg >= num_var_ranges))
2400d890355SYinghai Lu return reg;
2410d890355SYinghai Lu
2420d890355SYinghai Lu while (range_sizek) {
2430d890355SYinghai Lu unsigned long max_align, align;
2440d890355SYinghai Lu unsigned long sizek;
2450d890355SYinghai Lu
24663f9600fSJaswinder Singh Rajput /* Compute the maximum size with which we can make a range: */
2470d890355SYinghai Lu if (range_startk)
2481ba9a294SJan Beulich max_align = __ffs(range_startk);
2490d890355SYinghai Lu else
2501ba9a294SJan Beulich max_align = BITS_PER_LONG - 1;
25163f9600fSJaswinder Singh Rajput
2521ba9a294SJan Beulich align = __fls(range_sizek);
2530d890355SYinghai Lu if (align > max_align)
2540d890355SYinghai Lu align = max_align;
2550d890355SYinghai Lu
2562da06af8Szhenzhong.duan sizek = 1UL << align;
257*7c1dee73SBorislav Petkov (AMD) if (mtrr_debug) {
2580d890355SYinghai Lu char start_factor = 'K', size_factor = 'K';
2590d890355SYinghai Lu unsigned long start_base, size_base;
2600d890355SYinghai Lu
26163f9600fSJaswinder Singh Rajput start_base = to_size_factor(range_startk, &start_factor);
26263f9600fSJaswinder Singh Rajput size_base = to_size_factor(sizek, &size_factor);
2630d890355SYinghai Lu
26463f9600fSJaswinder Singh Rajput Dprintk("Setting variable MTRR %d, "
2650d890355SYinghai Lu "base: %ld%cB, range: %ld%cB, type %s\n",
2660d890355SYinghai Lu reg, start_base, start_factor,
2670d890355SYinghai Lu size_base, size_factor,
2680d890355SYinghai Lu (type == MTRR_TYPE_UNCACHABLE) ? "UC" :
2690d890355SYinghai Lu ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other")
2700d890355SYinghai Lu );
2710d890355SYinghai Lu }
2720d890355SYinghai Lu save_var_mtrr(reg++, range_startk, sizek, type);
2730d890355SYinghai Lu range_startk += sizek;
2740d890355SYinghai Lu range_sizek -= sizek;
2750d890355SYinghai Lu if (reg >= num_var_ranges)
2760d890355SYinghai Lu break;
2770d890355SYinghai Lu }
2780d890355SYinghai Lu return reg;
2790d890355SYinghai Lu }
2800d890355SYinghai Lu
2810d890355SYinghai Lu static unsigned __init
range_to_mtrr_with_hole(struct var_mtrr_state * state,unsigned long basek,unsigned long sizek)2820d890355SYinghai Lu range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
2830d890355SYinghai Lu unsigned long sizek)
2840d890355SYinghai Lu {
2850d890355SYinghai Lu unsigned long hole_basek, hole_sizek;
286c81cd5c0SBo Yu unsigned long second_sizek;
2870d890355SYinghai Lu unsigned long range0_basek, range0_sizek;
2880d890355SYinghai Lu unsigned long range_basek, range_sizek;
2890d890355SYinghai Lu unsigned long chunk_sizek;
2900d890355SYinghai Lu unsigned long gran_sizek;
2910d890355SYinghai Lu
2920d890355SYinghai Lu hole_basek = 0;
2930d890355SYinghai Lu hole_sizek = 0;
2940d890355SYinghai Lu second_sizek = 0;
2950d890355SYinghai Lu chunk_sizek = state->chunk_sizek;
2960d890355SYinghai Lu gran_sizek = state->gran_sizek;
2970d890355SYinghai Lu
29863f9600fSJaswinder Singh Rajput /* Align with gran size, prevent small block used up MTRRs: */
2990d890355SYinghai Lu range_basek = ALIGN(state->range_startk, gran_sizek);
3000d890355SYinghai Lu if ((range_basek > basek) && basek)
3010d890355SYinghai Lu return second_sizek;
30263f9600fSJaswinder Singh Rajput
3030d890355SYinghai Lu state->range_sizek -= (range_basek - state->range_startk);
3040d890355SYinghai Lu range_sizek = ALIGN(state->range_sizek, gran_sizek);
3050d890355SYinghai Lu
3060d890355SYinghai Lu while (range_sizek > state->range_sizek) {
3070d890355SYinghai Lu range_sizek -= gran_sizek;
3080d890355SYinghai Lu if (!range_sizek)
3090d890355SYinghai Lu return 0;
3100d890355SYinghai Lu }
3110d890355SYinghai Lu state->range_sizek = range_sizek;
3120d890355SYinghai Lu
31363f9600fSJaswinder Singh Rajput /* Try to append some small hole: */
3140d890355SYinghai Lu range0_basek = state->range_startk;
3150d890355SYinghai Lu range0_sizek = ALIGN(state->range_sizek, chunk_sizek);
3160d890355SYinghai Lu
31763f9600fSJaswinder Singh Rajput /* No increase: */
3180d890355SYinghai Lu if (range0_sizek == state->range_sizek) {
31963f9600fSJaswinder Singh Rajput Dprintk("rangeX: %016lx - %016lx\n",
3200d890355SYinghai Lu range0_basek<<10,
3210d890355SYinghai Lu (range0_basek + state->range_sizek)<<10);
3220d890355SYinghai Lu state->reg = range_to_mtrr(state->reg, range0_basek,
3230d890355SYinghai Lu state->range_sizek, MTRR_TYPE_WRBACK);
3240d890355SYinghai Lu return 0;
3250d890355SYinghai Lu }
3260d890355SYinghai Lu
32763f9600fSJaswinder Singh Rajput /* Only cut back when it is not the last: */
3280d890355SYinghai Lu if (sizek) {
3290d890355SYinghai Lu while (range0_basek + range0_sizek > (basek + sizek)) {
3300d890355SYinghai Lu if (range0_sizek >= chunk_sizek)
3310d890355SYinghai Lu range0_sizek -= chunk_sizek;
3320d890355SYinghai Lu else
3330d890355SYinghai Lu range0_sizek = 0;
3340d890355SYinghai Lu
3350d890355SYinghai Lu if (!range0_sizek)
3360d890355SYinghai Lu break;
3370d890355SYinghai Lu }
3380d890355SYinghai Lu }
3390d890355SYinghai Lu
3400d890355SYinghai Lu second_try:
3410d890355SYinghai Lu range_basek = range0_basek + range0_sizek;
3420d890355SYinghai Lu
34363f9600fSJaswinder Singh Rajput /* One hole in the middle: */
3440d890355SYinghai Lu if (range_basek > basek && range_basek <= (basek + sizek))
3450d890355SYinghai Lu second_sizek = range_basek - basek;
3460d890355SYinghai Lu
3470d890355SYinghai Lu if (range0_sizek > state->range_sizek) {
3480d890355SYinghai Lu
34963f9600fSJaswinder Singh Rajput /* One hole in middle or at the end: */
3500d890355SYinghai Lu hole_sizek = range0_sizek - state->range_sizek - second_sizek;
3510d890355SYinghai Lu
35263f9600fSJaswinder Singh Rajput /* Hole size should be less than half of range0 size: */
3530d890355SYinghai Lu if (hole_sizek >= (range0_sizek >> 1) &&
3540d890355SYinghai Lu range0_sizek >= chunk_sizek) {
3550d890355SYinghai Lu range0_sizek -= chunk_sizek;
3560d890355SYinghai Lu second_sizek = 0;
3570d890355SYinghai Lu hole_sizek = 0;
3580d890355SYinghai Lu
3590d890355SYinghai Lu goto second_try;
3600d890355SYinghai Lu }
3610d890355SYinghai Lu }
3620d890355SYinghai Lu
3630d890355SYinghai Lu if (range0_sizek) {
36463f9600fSJaswinder Singh Rajput Dprintk("range0: %016lx - %016lx\n",
3650d890355SYinghai Lu range0_basek<<10,
3660d890355SYinghai Lu (range0_basek + range0_sizek)<<10);
3670d890355SYinghai Lu state->reg = range_to_mtrr(state->reg, range0_basek,
3680d890355SYinghai Lu range0_sizek, MTRR_TYPE_WRBACK);
3690d890355SYinghai Lu }
3700d890355SYinghai Lu
3710d890355SYinghai Lu if (range0_sizek < state->range_sizek) {
37263f9600fSJaswinder Singh Rajput /* Need to handle left over range: */
3730d890355SYinghai Lu range_sizek = state->range_sizek - range0_sizek;
3740d890355SYinghai Lu
37563f9600fSJaswinder Singh Rajput Dprintk("range: %016lx - %016lx\n",
3760d890355SYinghai Lu range_basek<<10,
3770d890355SYinghai Lu (range_basek + range_sizek)<<10);
37863f9600fSJaswinder Singh Rajput
3790d890355SYinghai Lu state->reg = range_to_mtrr(state->reg, range_basek,
3800d890355SYinghai Lu range_sizek, MTRR_TYPE_WRBACK);
3810d890355SYinghai Lu }
3820d890355SYinghai Lu
3830d890355SYinghai Lu if (hole_sizek) {
3840d890355SYinghai Lu hole_basek = range_basek - hole_sizek - second_sizek;
38563f9600fSJaswinder Singh Rajput Dprintk("hole: %016lx - %016lx\n",
3860d890355SYinghai Lu hole_basek<<10,
3870d890355SYinghai Lu (hole_basek + hole_sizek)<<10);
3880d890355SYinghai Lu state->reg = range_to_mtrr(state->reg, hole_basek,
3890d890355SYinghai Lu hole_sizek, MTRR_TYPE_UNCACHABLE);
3900d890355SYinghai Lu }
3910d890355SYinghai Lu
3920d890355SYinghai Lu return second_sizek;
3930d890355SYinghai Lu }
3940d890355SYinghai Lu
3950d890355SYinghai Lu static void __init
set_var_mtrr_range(struct var_mtrr_state * state,unsigned long base_pfn,unsigned long size_pfn)3960d890355SYinghai Lu set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn,
3970d890355SYinghai Lu unsigned long size_pfn)
3980d890355SYinghai Lu {
3990d890355SYinghai Lu unsigned long basek, sizek;
4000d890355SYinghai Lu unsigned long second_sizek = 0;
4010d890355SYinghai Lu
4020d890355SYinghai Lu if (state->reg >= num_var_ranges)
4030d890355SYinghai Lu return;
4040d890355SYinghai Lu
4050d890355SYinghai Lu basek = base_pfn << (PAGE_SHIFT - 10);
4060d890355SYinghai Lu sizek = size_pfn << (PAGE_SHIFT - 10);
4070d890355SYinghai Lu
40863f9600fSJaswinder Singh Rajput /* See if I can merge with the last range: */
4090d890355SYinghai Lu if ((basek <= 1024) ||
4100d890355SYinghai Lu (state->range_startk + state->range_sizek == basek)) {
4110d890355SYinghai Lu unsigned long endk = basek + sizek;
4120d890355SYinghai Lu state->range_sizek = endk - state->range_startk;
4130d890355SYinghai Lu return;
4140d890355SYinghai Lu }
41563f9600fSJaswinder Singh Rajput /* Write the range mtrrs: */
4160d890355SYinghai Lu if (state->range_sizek != 0)
4170d890355SYinghai Lu second_sizek = range_to_mtrr_with_hole(state, basek, sizek);
4180d890355SYinghai Lu
41963f9600fSJaswinder Singh Rajput /* Allocate an msr: */
4200d890355SYinghai Lu state->range_startk = basek + second_sizek;
4210d890355SYinghai Lu state->range_sizek = sizek - second_sizek;
4220d890355SYinghai Lu }
4230d890355SYinghai Lu
424d9f6e12fSIngo Molnar /* Minimum size of mtrr block that can take hole: */
4250d890355SYinghai Lu static u64 mtrr_chunk_size __initdata = (256ULL<<20);
4260d890355SYinghai Lu
parse_mtrr_chunk_size_opt(char * p)4270d890355SYinghai Lu static int __init parse_mtrr_chunk_size_opt(char *p)
4280d890355SYinghai Lu {
4290d890355SYinghai Lu if (!p)
4300d890355SYinghai Lu return -EINVAL;
4310d890355SYinghai Lu mtrr_chunk_size = memparse(p, &p);
4320d890355SYinghai Lu return 0;
4330d890355SYinghai Lu }
4340d890355SYinghai Lu early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt);
4350d890355SYinghai Lu
43663f9600fSJaswinder Singh Rajput /* Granularity of mtrr of block: */
4370d890355SYinghai Lu static u64 mtrr_gran_size __initdata;
4380d890355SYinghai Lu
parse_mtrr_gran_size_opt(char * p)4390d890355SYinghai Lu static int __init parse_mtrr_gran_size_opt(char *p)
4400d890355SYinghai Lu {
4410d890355SYinghai Lu if (!p)
4420d890355SYinghai Lu return -EINVAL;
4430d890355SYinghai Lu mtrr_gran_size = memparse(p, &p);
4440d890355SYinghai Lu return 0;
4450d890355SYinghai Lu }
4460d890355SYinghai Lu early_param("mtrr_gran_size", parse_mtrr_gran_size_opt);
4470d890355SYinghai Lu
44863f9600fSJaswinder Singh Rajput static unsigned long nr_mtrr_spare_reg __initdata =
4490d890355SYinghai Lu CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT;
4500d890355SYinghai Lu
parse_mtrr_spare_reg(char * arg)4510d890355SYinghai Lu static int __init parse_mtrr_spare_reg(char *arg)
4520d890355SYinghai Lu {
4530d890355SYinghai Lu if (arg)
4540d890355SYinghai Lu nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0);
4550d890355SYinghai Lu return 0;
4560d890355SYinghai Lu }
4570d890355SYinghai Lu early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg);
4580d890355SYinghai Lu
4590d890355SYinghai Lu static int __init
x86_setup_var_mtrrs(struct range * range,int nr_range,u64 chunk_size,u64 gran_size)46027811d8cSYinghai Lu x86_setup_var_mtrrs(struct range *range, int nr_range,
4610d890355SYinghai Lu u64 chunk_size, u64 gran_size)
4620d890355SYinghai Lu {
4630d890355SYinghai Lu struct var_mtrr_state var_state;
4640d890355SYinghai Lu int num_reg;
46563f9600fSJaswinder Singh Rajput int i;
4660d890355SYinghai Lu
4670d890355SYinghai Lu var_state.range_startk = 0;
4680d890355SYinghai Lu var_state.range_sizek = 0;
4690d890355SYinghai Lu var_state.reg = 0;
4700d890355SYinghai Lu var_state.chunk_sizek = chunk_size >> 10;
4710d890355SYinghai Lu var_state.gran_sizek = gran_size >> 10;
4720d890355SYinghai Lu
4730d890355SYinghai Lu memset(range_state, 0, sizeof(range_state));
4740d890355SYinghai Lu
47563f9600fSJaswinder Singh Rajput /* Write the range: */
47663f9600fSJaswinder Singh Rajput for (i = 0; i < nr_range; i++) {
4770d890355SYinghai Lu set_var_mtrr_range(&var_state, range[i].start,
478e9a0064aSYinghai Lu range[i].end - range[i].start);
47963f9600fSJaswinder Singh Rajput }
4800d890355SYinghai Lu
48163f9600fSJaswinder Singh Rajput /* Write the last range: */
4820d890355SYinghai Lu if (var_state.range_sizek != 0)
4830d890355SYinghai Lu range_to_mtrr_with_hole(&var_state, 0, 0);
4840d890355SYinghai Lu
4850d890355SYinghai Lu num_reg = var_state.reg;
48663f9600fSJaswinder Singh Rajput /* Clear out the extra MTRR's: */
4870d890355SYinghai Lu while (var_state.reg < num_var_ranges) {
4880d890355SYinghai Lu save_var_mtrr(var_state.reg, 0, 0, 0);
4890d890355SYinghai Lu var_state.reg++;
4900d890355SYinghai Lu }
4910d890355SYinghai Lu
4920d890355SYinghai Lu return num_reg;
4930d890355SYinghai Lu }
4940d890355SYinghai Lu
4950d890355SYinghai Lu struct mtrr_cleanup_result {
4960d890355SYinghai Lu unsigned long gran_sizek;
4970d890355SYinghai Lu unsigned long chunk_sizek;
4980d890355SYinghai Lu unsigned long lose_cover_sizek;
4990d890355SYinghai Lu unsigned int num_reg;
5000d890355SYinghai Lu int bad;
5010d890355SYinghai Lu };
5020d890355SYinghai Lu
5030d890355SYinghai Lu /*
5040d890355SYinghai Lu * gran_size: 64K, 128K, 256K, 512K, 1M, 2M, ..., 2G
5050d890355SYinghai Lu * chunk size: gran_size, ..., 2G
5060d890355SYinghai Lu * so we need (1+16)*8
5070d890355SYinghai Lu */
5080d890355SYinghai Lu #define NUM_RESULT 136
5090d890355SYinghai Lu #define PSHIFT (PAGE_SHIFT - 10)
5100d890355SYinghai Lu
5110d890355SYinghai Lu static struct mtrr_cleanup_result __initdata result[NUM_RESULT];
5120d890355SYinghai Lu static unsigned long __initdata min_loss_pfn[RANGE_NUM];
5130d890355SYinghai Lu
print_out_mtrr_range_state(void)5140d890355SYinghai Lu static void __init print_out_mtrr_range_state(void)
5150d890355SYinghai Lu {
5160d890355SYinghai Lu char start_factor = 'K', size_factor = 'K';
5170d890355SYinghai Lu unsigned long start_base, size_base;
5180d890355SYinghai Lu mtrr_type type;
51963f9600fSJaswinder Singh Rajput int i;
5200d890355SYinghai Lu
5210d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
5220d890355SYinghai Lu
5230d890355SYinghai Lu size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10);
5240d890355SYinghai Lu if (!size_base)
5250d890355SYinghai Lu continue;
5260d890355SYinghai Lu
5273052636aSZheng Yongjun size_base = to_size_factor(size_base, &size_factor);
5280d890355SYinghai Lu start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10);
5293052636aSZheng Yongjun start_base = to_size_factor(start_base, &start_factor);
5300d890355SYinghai Lu type = range_state[i].type;
5310d890355SYinghai Lu
532*7c1dee73SBorislav Petkov (AMD) Dprintk("reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
5330d890355SYinghai Lu i, start_base, start_factor,
5340d890355SYinghai Lu size_base, size_factor,
5350d890355SYinghai Lu (type == MTRR_TYPE_UNCACHABLE) ? "UC" :
5360d890355SYinghai Lu ((type == MTRR_TYPE_WRPROT) ? "WP" :
5370d890355SYinghai Lu ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other"))
5380d890355SYinghai Lu );
5390d890355SYinghai Lu }
5400d890355SYinghai Lu }
5410d890355SYinghai Lu
mtrr_need_cleanup(void)5420d890355SYinghai Lu static int __init mtrr_need_cleanup(void)
5430d890355SYinghai Lu {
5440d890355SYinghai Lu int i;
5450d890355SYinghai Lu mtrr_type type;
5460d890355SYinghai Lu unsigned long size;
54763f9600fSJaswinder Singh Rajput /* Extra one for all 0: */
5480d890355SYinghai Lu int num[MTRR_NUM_TYPES + 1];
5490d890355SYinghai Lu
55063f9600fSJaswinder Singh Rajput /* Check entries number: */
5510d890355SYinghai Lu memset(num, 0, sizeof(num));
5520d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
5530d890355SYinghai Lu type = range_state[i].type;
5540d890355SYinghai Lu size = range_state[i].size_pfn;
5550d890355SYinghai Lu if (type >= MTRR_NUM_TYPES)
5560d890355SYinghai Lu continue;
5570d890355SYinghai Lu if (!size)
5580d890355SYinghai Lu type = MTRR_NUM_TYPES;
5590d890355SYinghai Lu num[type]++;
5600d890355SYinghai Lu }
5610d890355SYinghai Lu
56263f9600fSJaswinder Singh Rajput /* Check if we got UC entries: */
5630d890355SYinghai Lu if (!num[MTRR_TYPE_UNCACHABLE])
5640d890355SYinghai Lu return 0;
5650d890355SYinghai Lu
56663f9600fSJaswinder Singh Rajput /* Check if we only had WB and UC */
5670d890355SYinghai Lu if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
5680d890355SYinghai Lu num_var_ranges - num[MTRR_NUM_TYPES])
5690d890355SYinghai Lu return 0;
5700d890355SYinghai Lu
5710d890355SYinghai Lu return 1;
5720d890355SYinghai Lu }
5730d890355SYinghai Lu
5740d890355SYinghai Lu static unsigned long __initdata range_sums;
57563f9600fSJaswinder Singh Rajput
57663f9600fSJaswinder Singh Rajput static void __init
mtrr_calc_range_state(u64 chunk_size,u64 gran_size,unsigned long x_remove_base,unsigned long x_remove_size,int i)57763f9600fSJaswinder Singh Rajput mtrr_calc_range_state(u64 chunk_size, u64 gran_size,
57863f9600fSJaswinder Singh Rajput unsigned long x_remove_base,
57963f9600fSJaswinder Singh Rajput unsigned long x_remove_size, int i)
5800d890355SYinghai Lu {
581c332813bSRasmus Villemoes /*
582c332813bSRasmus Villemoes * range_new should really be an automatic variable, but
583c332813bSRasmus Villemoes * putting 4096 bytes on the stack is frowned upon, to put it
584c332813bSRasmus Villemoes * mildly. It is safe to make it a static __initdata variable,
585c332813bSRasmus Villemoes * since mtrr_calc_range_state is only called during init and
586c332813bSRasmus Villemoes * there's no way it will call itself recursively.
587c332813bSRasmus Villemoes */
588c332813bSRasmus Villemoes static struct range range_new[RANGE_NUM] __initdata;
5890d890355SYinghai Lu unsigned long range_sums_new;
590c332813bSRasmus Villemoes int nr_range_new;
59163f9600fSJaswinder Singh Rajput int num_reg;
5920d890355SYinghai Lu
59363f9600fSJaswinder Singh Rajput /* Convert ranges to var ranges state: */
59463f9600fSJaswinder Singh Rajput num_reg = x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
5950d890355SYinghai Lu
59663f9600fSJaswinder Singh Rajput /* We got new setting in range_state, check it: */
5970d890355SYinghai Lu memset(range_new, 0, sizeof(range_new));
5980d890355SYinghai Lu nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
59963f9600fSJaswinder Singh Rajput x_remove_base, x_remove_size);
6000d890355SYinghai Lu range_sums_new = sum_ranges(range_new, nr_range_new);
6010d890355SYinghai Lu
6020d890355SYinghai Lu result[i].chunk_sizek = chunk_size >> 10;
6030d890355SYinghai Lu result[i].gran_sizek = gran_size >> 10;
6040d890355SYinghai Lu result[i].num_reg = num_reg;
60563f9600fSJaswinder Singh Rajput
6060d890355SYinghai Lu if (range_sums < range_sums_new) {
60763f9600fSJaswinder Singh Rajput result[i].lose_cover_sizek = (range_sums_new - range_sums) << PSHIFT;
6080d890355SYinghai Lu result[i].bad = 1;
60963f9600fSJaswinder Singh Rajput } else {
61063f9600fSJaswinder Singh Rajput result[i].lose_cover_sizek = (range_sums - range_sums_new) << PSHIFT;
61163f9600fSJaswinder Singh Rajput }
6120d890355SYinghai Lu
61363f9600fSJaswinder Singh Rajput /* Double check it: */
6140d890355SYinghai Lu if (!result[i].bad && !result[i].lose_cover_sizek) {
61563f9600fSJaswinder Singh Rajput if (nr_range_new != nr_range || memcmp(range, range_new, sizeof(range)))
6160d890355SYinghai Lu result[i].bad = 1;
6170d890355SYinghai Lu }
6180d890355SYinghai Lu
61963f9600fSJaswinder Singh Rajput if (!result[i].bad && (range_sums - range_sums_new < min_loss_pfn[num_reg]))
62063f9600fSJaswinder Singh Rajput min_loss_pfn[num_reg] = range_sums - range_sums_new;
6210d890355SYinghai Lu }
6220d890355SYinghai Lu
mtrr_print_out_one_result(int i)6230d890355SYinghai Lu static void __init mtrr_print_out_one_result(int i)
6240d890355SYinghai Lu {
6250d890355SYinghai Lu unsigned long gran_base, chunk_base, lose_base;
62663f9600fSJaswinder Singh Rajput char gran_factor, chunk_factor, lose_factor;
6270d890355SYinghai Lu
628b2691085SJoe Perches gran_base = to_size_factor(result[i].gran_sizek, &gran_factor);
629b2691085SJoe Perches chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor);
630b2691085SJoe Perches lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor);
63163f9600fSJaswinder Singh Rajput
63263f9600fSJaswinder Singh Rajput pr_info("%sgran_size: %ld%c \tchunk_size: %ld%c \t",
6330d890355SYinghai Lu result[i].bad ? "*BAD*" : " ",
6340d890355SYinghai Lu gran_base, gran_factor, chunk_base, chunk_factor);
63563f9600fSJaswinder Singh Rajput pr_cont("num_reg: %d \tlose cover RAM: %s%ld%c\n",
6360d890355SYinghai Lu result[i].num_reg, result[i].bad ? "-" : "",
6370d890355SYinghai Lu lose_base, lose_factor);
6380d890355SYinghai Lu }
6390d890355SYinghai Lu
mtrr_search_optimal_index(void)6400d890355SYinghai Lu static int __init mtrr_search_optimal_index(void)
6410d890355SYinghai Lu {
6420d890355SYinghai Lu int num_reg_good;
6430d890355SYinghai Lu int index_good;
64463f9600fSJaswinder Singh Rajput int i;
6450d890355SYinghai Lu
6460d890355SYinghai Lu if (nr_mtrr_spare_reg >= num_var_ranges)
6470d890355SYinghai Lu nr_mtrr_spare_reg = num_var_ranges - 1;
64863f9600fSJaswinder Singh Rajput
6490d890355SYinghai Lu num_reg_good = -1;
6500d890355SYinghai Lu for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) {
6510d890355SYinghai Lu if (!min_loss_pfn[i])
6520d890355SYinghai Lu num_reg_good = i;
6530d890355SYinghai Lu }
6540d890355SYinghai Lu
6550d890355SYinghai Lu index_good = -1;
6560d890355SYinghai Lu if (num_reg_good != -1) {
6570d890355SYinghai Lu for (i = 0; i < NUM_RESULT; i++) {
6580d890355SYinghai Lu if (!result[i].bad &&
6590d890355SYinghai Lu result[i].num_reg == num_reg_good &&
6600d890355SYinghai Lu !result[i].lose_cover_sizek) {
6610d890355SYinghai Lu index_good = i;
6620d890355SYinghai Lu break;
6630d890355SYinghai Lu }
6640d890355SYinghai Lu }
6650d890355SYinghai Lu }
6660d890355SYinghai Lu
6670d890355SYinghai Lu return index_good;
6680d890355SYinghai Lu }
6690d890355SYinghai Lu
mtrr_cleanup(void)670f6b98064SJuergen Gross int __init mtrr_cleanup(void)
6710d890355SYinghai Lu {
67263f9600fSJaswinder Singh Rajput unsigned long x_remove_base, x_remove_size;
6730d890355SYinghai Lu unsigned long base, size, def, dummy;
6740d890355SYinghai Lu u64 chunk_size, gran_size;
67563f9600fSJaswinder Singh Rajput mtrr_type type;
6760d890355SYinghai Lu int index_good;
6770d890355SYinghai Lu int i;
6780d890355SYinghai Lu
67903409069SJuergen Gross if (!mtrr_enabled())
68003409069SJuergen Gross return 0;
68103409069SJuergen Gross
68203409069SJuergen Gross if (!cpu_feature_enabled(X86_FEATURE_MTRR) || enable_mtrr_cleanup < 1)
6830d890355SYinghai Lu return 0;
68463f9600fSJaswinder Singh Rajput
68552650257SJaswinder Singh Rajput rdmsr(MSR_MTRRdefType, def, dummy);
6860d890355SYinghai Lu def &= 0xff;
6870d890355SYinghai Lu if (def != MTRR_TYPE_UNCACHABLE)
6880d890355SYinghai Lu return 0;
6890d890355SYinghai Lu
69063f9600fSJaswinder Singh Rajput /* Get it and store it aside: */
6910d890355SYinghai Lu memset(range_state, 0, sizeof(range_state));
6920d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
6930d890355SYinghai Lu mtrr_if->get(i, &base, &size, &type);
6940d890355SYinghai Lu range_state[i].base_pfn = base;
6950d890355SYinghai Lu range_state[i].size_pfn = size;
6960d890355SYinghai Lu range_state[i].type = type;
6970d890355SYinghai Lu }
6980d890355SYinghai Lu
69963f9600fSJaswinder Singh Rajput /* Check if we need handle it and can handle it: */
7000d890355SYinghai Lu if (!mtrr_need_cleanup())
7010d890355SYinghai Lu return 0;
7020d890355SYinghai Lu
70363f9600fSJaswinder Singh Rajput /* Print original var MTRRs at first, for debugging: */
704*7c1dee73SBorislav Petkov (AMD) Dprintk("original variable MTRRs\n");
7050d890355SYinghai Lu print_out_mtrr_range_state();
7060d890355SYinghai Lu
7070d890355SYinghai Lu memset(range, 0, sizeof(range));
70863f9600fSJaswinder Singh Rajput x_remove_size = 0;
70963f9600fSJaswinder Singh Rajput x_remove_base = 1 << (32 - PAGE_SHIFT);
7100d890355SYinghai Lu if (mtrr_tom2)
71163f9600fSJaswinder Singh Rajput x_remove_size = (mtrr_tom2 >> PAGE_SHIFT) - x_remove_base;
71263f9600fSJaswinder Singh Rajput
7130d890355SYinghai Lu /*
71463f9600fSJaswinder Singh Rajput * [0, 1M) should always be covered by var mtrr with WB
71563f9600fSJaswinder Singh Rajput * and fixed mtrrs should take effect before var mtrr for it:
7160d890355SYinghai Lu */
717d8d386c1SYinghai Lu nr_range = add_range_with_merge(range, RANGE_NUM, 0, 0,
718e9a0064aSYinghai Lu 1ULL<<(20 - PAGE_SHIFT));
719d8d386c1SYinghai Lu /* add from var mtrr at last */
720d8d386c1SYinghai Lu nr_range = x86_get_mtrr_mem_range(range, nr_range,
721d8d386c1SYinghai Lu x_remove_base, x_remove_size);
7220d890355SYinghai Lu
7230d890355SYinghai Lu range_sums = sum_ranges(range, nr_range);
7241b74dde7SChen Yucong pr_info("total RAM covered: %ldM\n",
7250d890355SYinghai Lu range_sums >> (20 - PAGE_SHIFT));
7260d890355SYinghai Lu
7270d890355SYinghai Lu if (mtrr_chunk_size && mtrr_gran_size) {
7280d890355SYinghai Lu i = 0;
7290d890355SYinghai Lu mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size,
73063f9600fSJaswinder Singh Rajput x_remove_base, x_remove_size, i);
7310d890355SYinghai Lu
7320d890355SYinghai Lu mtrr_print_out_one_result(i);
7330d890355SYinghai Lu
7340d890355SYinghai Lu if (!result[i].bad) {
735f6b98064SJuergen Gross set_var_mtrr_all();
736*7c1dee73SBorislav Petkov (AMD) Dprintk("New variable MTRRs\n");
7370d890355SYinghai Lu print_out_mtrr_range_state();
7380d890355SYinghai Lu return 1;
7390d890355SYinghai Lu }
7401b74dde7SChen Yucong pr_info("invalid mtrr_gran_size or mtrr_chunk_size, will find optimal one\n");
7410d890355SYinghai Lu }
7420d890355SYinghai Lu
7430d890355SYinghai Lu i = 0;
7440d890355SYinghai Lu memset(min_loss_pfn, 0xff, sizeof(min_loss_pfn));
7450d890355SYinghai Lu memset(result, 0, sizeof(result));
7460d890355SYinghai Lu for (gran_size = (1ULL<<16); gran_size < (1ULL<<32); gran_size <<= 1) {
7470d890355SYinghai Lu
7480d890355SYinghai Lu for (chunk_size = gran_size; chunk_size < (1ULL<<32);
7490d890355SYinghai Lu chunk_size <<= 1) {
7500d890355SYinghai Lu
7510d890355SYinghai Lu if (i >= NUM_RESULT)
7520d890355SYinghai Lu continue;
7530d890355SYinghai Lu
7540d890355SYinghai Lu mtrr_calc_range_state(chunk_size, gran_size,
75563f9600fSJaswinder Singh Rajput x_remove_base, x_remove_size, i);
756*7c1dee73SBorislav Petkov (AMD) if (mtrr_debug) {
7570d890355SYinghai Lu mtrr_print_out_one_result(i);
7581b74dde7SChen Yucong pr_info("\n");
7590d890355SYinghai Lu }
7600d890355SYinghai Lu
7610d890355SYinghai Lu i++;
7620d890355SYinghai Lu }
7630d890355SYinghai Lu }
7640d890355SYinghai Lu
76563f9600fSJaswinder Singh Rajput /* Try to find the optimal index: */
7660d890355SYinghai Lu index_good = mtrr_search_optimal_index();
7670d890355SYinghai Lu
7680d890355SYinghai Lu if (index_good != -1) {
7691b74dde7SChen Yucong pr_info("Found optimal setting for mtrr clean up\n");
7700d890355SYinghai Lu i = index_good;
7710d890355SYinghai Lu mtrr_print_out_one_result(i);
7720d890355SYinghai Lu
77363f9600fSJaswinder Singh Rajput /* Convert ranges to var ranges state: */
7740d890355SYinghai Lu chunk_size = result[i].chunk_sizek;
7750d890355SYinghai Lu chunk_size <<= 10;
7760d890355SYinghai Lu gran_size = result[i].gran_sizek;
7770d890355SYinghai Lu gran_size <<= 10;
7780d890355SYinghai Lu x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
779f6b98064SJuergen Gross set_var_mtrr_all();
780*7c1dee73SBorislav Petkov (AMD) Dprintk("New variable MTRRs\n");
7810d890355SYinghai Lu print_out_mtrr_range_state();
7820d890355SYinghai Lu return 1;
7830d890355SYinghai Lu } else {
7840d890355SYinghai Lu /* print out all */
7850d890355SYinghai Lu for (i = 0; i < NUM_RESULT; i++)
7860d890355SYinghai Lu mtrr_print_out_one_result(i);
7870d890355SYinghai Lu }
7880d890355SYinghai Lu
7891b74dde7SChen Yucong pr_info("mtrr_cleanup: can not find optimal value\n");
7901b74dde7SChen Yucong pr_info("please specify mtrr_gran_size/mtrr_chunk_size\n");
7910d890355SYinghai Lu
7920d890355SYinghai Lu return 0;
7930d890355SYinghai Lu }
7940d890355SYinghai Lu #else
mtrr_cleanup(void)795f6b98064SJuergen Gross int __init mtrr_cleanup(void)
7960d890355SYinghai Lu {
7970d890355SYinghai Lu return 0;
7980d890355SYinghai Lu }
7990d890355SYinghai Lu #endif
8000d890355SYinghai Lu
8010d890355SYinghai Lu static int disable_mtrr_trim;
8020d890355SYinghai Lu
disable_mtrr_trim_setup(char * str)8030d890355SYinghai Lu static int __init disable_mtrr_trim_setup(char *str)
8040d890355SYinghai Lu {
8050d890355SYinghai Lu disable_mtrr_trim = 1;
8060d890355SYinghai Lu return 0;
8070d890355SYinghai Lu }
8080d890355SYinghai Lu early_param("disable_mtrr_trim", disable_mtrr_trim_setup);
8090d890355SYinghai Lu
8100d890355SYinghai Lu /*
8110d890355SYinghai Lu * Newer AMD K8s and later CPUs have a special magic MSR way to force WB
8120d890355SYinghai Lu * for memory >4GB. Check for that here.
8130d890355SYinghai Lu * Note this won't check if the MTRRs < 4GB where the magic bit doesn't
8140d890355SYinghai Lu * apply to are wrong, but so far we don't know of any such case in the wild.
8150d890355SYinghai Lu */
8160d890355SYinghai Lu #define Tom2Enabled (1U << 21)
8170d890355SYinghai Lu #define Tom2ForceMemTypeWB (1U << 22)
8180d890355SYinghai Lu
amd_special_default_mtrr(void)8190d890355SYinghai Lu int __init amd_special_default_mtrr(void)
8200d890355SYinghai Lu {
8210d890355SYinghai Lu u32 l, h;
8220d890355SYinghai Lu
82339dc6f15SPu Wen if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
82439dc6f15SPu Wen boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
8250d890355SYinghai Lu return 0;
8263fdbf004SAndreas Herrmann if (boot_cpu_data.x86 < 0xf)
8270d890355SYinghai Lu return 0;
82863f9600fSJaswinder Singh Rajput /* In case some hypervisor doesn't pass SYSCFG through: */
829059e5c32SBrijesh Singh if (rdmsr_safe(MSR_AMD64_SYSCFG, &l, &h) < 0)
8300d890355SYinghai Lu return 0;
8310d890355SYinghai Lu /*
8320d890355SYinghai Lu * Memory between 4GB and top of mem is forced WB by this magic bit.
8330d890355SYinghai Lu * Reserved before K8RevF, but should be zero there.
8340d890355SYinghai Lu */
8350d890355SYinghai Lu if ((l & (Tom2Enabled | Tom2ForceMemTypeWB)) ==
8360d890355SYinghai Lu (Tom2Enabled | Tom2ForceMemTypeWB))
8370d890355SYinghai Lu return 1;
8380d890355SYinghai Lu return 0;
8390d890355SYinghai Lu }
8400d890355SYinghai Lu
84163f9600fSJaswinder Singh Rajput static u64 __init
real_trim_memory(unsigned long start_pfn,unsigned long limit_pfn)84263f9600fSJaswinder Singh Rajput real_trim_memory(unsigned long start_pfn, unsigned long limit_pfn)
8430d890355SYinghai Lu {
8440d890355SYinghai Lu u64 trim_start, trim_size;
84563f9600fSJaswinder Singh Rajput
8460d890355SYinghai Lu trim_start = start_pfn;
8470d890355SYinghai Lu trim_start <<= PAGE_SHIFT;
84863f9600fSJaswinder Singh Rajput
8490d890355SYinghai Lu trim_size = limit_pfn;
8500d890355SYinghai Lu trim_size <<= PAGE_SHIFT;
8510d890355SYinghai Lu trim_size -= trim_start;
8520d890355SYinghai Lu
85309821ff1SIngo Molnar return e820__range_update(trim_start, trim_size, E820_TYPE_RAM, E820_TYPE_RESERVED);
8540d890355SYinghai Lu }
85563f9600fSJaswinder Singh Rajput
8560d890355SYinghai Lu /**
8570d890355SYinghai Lu * mtrr_trim_uncached_memory - trim RAM not covered by MTRRs
8580d890355SYinghai Lu * @end_pfn: ending page frame number
8590d890355SYinghai Lu *
8600d890355SYinghai Lu * Some buggy BIOSes don't setup the MTRRs properly for systems with certain
8610d890355SYinghai Lu * memory configurations. This routine checks that the highest MTRR matches
8620d890355SYinghai Lu * the end of memory, to make sure the MTRRs having a write back type cover
8630d890355SYinghai Lu * all of the memory the kernel is intending to use. If not, it'll trim any
8640d890355SYinghai Lu * memory off the end by adjusting end_pfn, removing it from the kernel's
8650d890355SYinghai Lu * allocation pools, warning the user with an obnoxious message.
8660d890355SYinghai Lu */
mtrr_trim_uncached_memory(unsigned long end_pfn)8670d890355SYinghai Lu int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
8680d890355SYinghai Lu {
8690d890355SYinghai Lu unsigned long i, base, size, highest_pfn = 0, def, dummy;
8700d890355SYinghai Lu mtrr_type type;
8710d890355SYinghai Lu u64 total_trim_size;
8720d890355SYinghai Lu /* extra one for all 0 */
8730d890355SYinghai Lu int num[MTRR_NUM_TYPES + 1];
87463f9600fSJaswinder Singh Rajput
87503409069SJuergen Gross if (!mtrr_enabled())
87603409069SJuergen Gross return 0;
87703409069SJuergen Gross
8780d890355SYinghai Lu /*
8790d890355SYinghai Lu * Make sure we only trim uncachable memory on machines that
8800d890355SYinghai Lu * support the Intel MTRR architecture:
8810d890355SYinghai Lu */
88203409069SJuergen Gross if (!cpu_feature_enabled(X86_FEATURE_MTRR) || disable_mtrr_trim)
8830d890355SYinghai Lu return 0;
88463f9600fSJaswinder Singh Rajput
88552650257SJaswinder Singh Rajput rdmsr(MSR_MTRRdefType, def, dummy);
886d053b481SJuergen Gross def &= MTRR_DEF_TYPE_TYPE;
8870d890355SYinghai Lu if (def != MTRR_TYPE_UNCACHABLE)
8880d890355SYinghai Lu return 0;
8890d890355SYinghai Lu
89063f9600fSJaswinder Singh Rajput /* Get it and store it aside: */
8910d890355SYinghai Lu memset(range_state, 0, sizeof(range_state));
8920d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
8930d890355SYinghai Lu mtrr_if->get(i, &base, &size, &type);
8940d890355SYinghai Lu range_state[i].base_pfn = base;
8950d890355SYinghai Lu range_state[i].size_pfn = size;
8960d890355SYinghai Lu range_state[i].type = type;
8970d890355SYinghai Lu }
8980d890355SYinghai Lu
89963f9600fSJaswinder Singh Rajput /* Find highest cached pfn: */
9000d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
9010d890355SYinghai Lu type = range_state[i].type;
9020d890355SYinghai Lu if (type != MTRR_TYPE_WRBACK)
9030d890355SYinghai Lu continue;
9040d890355SYinghai Lu base = range_state[i].base_pfn;
9050d890355SYinghai Lu size = range_state[i].size_pfn;
9060d890355SYinghai Lu if (highest_pfn < base + size)
9070d890355SYinghai Lu highest_pfn = base + size;
9080d890355SYinghai Lu }
9090d890355SYinghai Lu
91063f9600fSJaswinder Singh Rajput /* kvm/qemu doesn't have mtrr set right, don't trim them all: */
9110d890355SYinghai Lu if (!highest_pfn) {
9121b74dde7SChen Yucong pr_info("CPU MTRRs all blank - virtualized system.\n");
9130d890355SYinghai Lu return 0;
9140d890355SYinghai Lu }
9150d890355SYinghai Lu
91663f9600fSJaswinder Singh Rajput /* Check entries number: */
9170d890355SYinghai Lu memset(num, 0, sizeof(num));
9180d890355SYinghai Lu for (i = 0; i < num_var_ranges; i++) {
9190d890355SYinghai Lu type = range_state[i].type;
9200d890355SYinghai Lu if (type >= MTRR_NUM_TYPES)
9210d890355SYinghai Lu continue;
9220d890355SYinghai Lu size = range_state[i].size_pfn;
9230d890355SYinghai Lu if (!size)
9240d890355SYinghai Lu type = MTRR_NUM_TYPES;
9250d890355SYinghai Lu num[type]++;
9260d890355SYinghai Lu }
9270d890355SYinghai Lu
92863f9600fSJaswinder Singh Rajput /* No entry for WB? */
9290d890355SYinghai Lu if (!num[MTRR_TYPE_WRBACK])
9300d890355SYinghai Lu return 0;
9310d890355SYinghai Lu
93263f9600fSJaswinder Singh Rajput /* Check if we only had WB and UC: */
9330d890355SYinghai Lu if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
9340d890355SYinghai Lu num_var_ranges - num[MTRR_NUM_TYPES])
9350d890355SYinghai Lu return 0;
9360d890355SYinghai Lu
9370d890355SYinghai Lu memset(range, 0, sizeof(range));
9380d890355SYinghai Lu nr_range = 0;
9390d890355SYinghai Lu if (mtrr_tom2) {
9400d890355SYinghai Lu range[nr_range].start = (1ULL<<(32 - PAGE_SHIFT));
941e9a0064aSYinghai Lu range[nr_range].end = mtrr_tom2 >> PAGE_SHIFT;
942e9a0064aSYinghai Lu if (highest_pfn < range[nr_range].end)
943e9a0064aSYinghai Lu highest_pfn = range[nr_range].end;
9440d890355SYinghai Lu nr_range++;
9450d890355SYinghai Lu }
9460d890355SYinghai Lu nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0);
9470d890355SYinghai Lu
94863f9600fSJaswinder Singh Rajput /* Check the head: */
9490d890355SYinghai Lu total_trim_size = 0;
9500d890355SYinghai Lu if (range[0].start)
9510d890355SYinghai Lu total_trim_size += real_trim_memory(0, range[0].start);
95263f9600fSJaswinder Singh Rajput
95363f9600fSJaswinder Singh Rajput /* Check the holes: */
9540d890355SYinghai Lu for (i = 0; i < nr_range - 1; i++) {
955e9a0064aSYinghai Lu if (range[i].end < range[i+1].start)
956e9a0064aSYinghai Lu total_trim_size += real_trim_memory(range[i].end,
9570d890355SYinghai Lu range[i+1].start);
9580d890355SYinghai Lu }
95963f9600fSJaswinder Singh Rajput
96063f9600fSJaswinder Singh Rajput /* Check the top: */
9610d890355SYinghai Lu i = nr_range - 1;
962e9a0064aSYinghai Lu if (range[i].end < end_pfn)
963e9a0064aSYinghai Lu total_trim_size += real_trim_memory(range[i].end,
9640d890355SYinghai Lu end_pfn);
9650d890355SYinghai Lu
9660d890355SYinghai Lu if (total_trim_size) {
9671b74dde7SChen Yucong pr_warn("WARNING: BIOS bug: CPU MTRRs don't cover all of memory, losing %lluMB of RAM.\n",
9681b74dde7SChen Yucong total_trim_size >> 20);
9690d890355SYinghai Lu
9700d890355SYinghai Lu if (!changed_by_mtrr_cleanup)
9710d890355SYinghai Lu WARN_ON(1);
9720d890355SYinghai Lu
97363f9600fSJaswinder Singh Rajput pr_info("update e820 for mtrr\n");
9746464d294SIngo Molnar e820__update_table_print();
9750d890355SYinghai Lu
9760d890355SYinghai Lu return 1;
9770d890355SYinghai Lu }
9780d890355SYinghai Lu
9790d890355SYinghai Lu return 0;
9800d890355SYinghai Lu }
981