xref: /openbmc/linux/arch/x86/kernel/cpu/mtrr/cleanup.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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