1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a1a499a3SJaswinder Singh Rajput /* 3a1a499a3SJaswinder Singh Rajput * This only handles 32bit MTRR on 32bit hosts. This is strictly wrong 4a1a499a3SJaswinder Singh Rajput * because MTRRs can span up to 40 bits (36bits on most modern x86) 5a1a499a3SJaswinder Singh Rajput */ 6a1a499a3SJaswinder Singh Rajput 7186f4360SPaul Gortmaker #include <linux/export.h> 82ec1df41SThomas Gleixner #include <linux/init.h> 9a1a499a3SJaswinder Singh Rajput #include <linux/io.h> 102ec1df41SThomas Gleixner #include <linux/mm.h> 1129055dc7SJuergen Gross #include <linux/cc_platform.h> 12a1a499a3SJaswinder Singh Rajput #include <asm/processor-flags.h> 13d5f66d5dSJuergen Gross #include <asm/cacheinfo.h> 14a1a499a3SJaswinder Singh Rajput #include <asm/cpufeature.h> 1529055dc7SJuergen Gross #include <asm/hypervisor.h> 1629055dc7SJuergen Gross #include <asm/mshyperv.h> 17a1a499a3SJaswinder Singh Rajput #include <asm/tlbflush.h> 182ec1df41SThomas Gleixner #include <asm/mtrr.h> 192ec1df41SThomas Gleixner #include <asm/msr.h> 20eb243d1dSIngo Molnar #include <asm/memtype.h> 21a1a499a3SJaswinder Singh Rajput 222ec1df41SThomas Gleixner #include "mtrr.h" 232ec1df41SThomas Gleixner 242ec1df41SThomas Gleixner struct fixed_range_block { 252ec1df41SThomas Gleixner int base_msr; /* start address of an MTRR block */ 262ec1df41SThomas Gleixner int ranges; /* number of MTRRs in this block */ 272ec1df41SThomas Gleixner }; 282ec1df41SThomas Gleixner 292ec1df41SThomas Gleixner static struct fixed_range_block fixed_range_blocks[] = { 30a036c7a3SJaswinder Singh Rajput { MSR_MTRRfix64K_00000, 1 }, /* one 64k MTRR */ 317d9d55e4SJaswinder Singh Rajput { MSR_MTRRfix16K_80000, 2 }, /* two 16k MTRRs */ 32ba5673ffSJaswinder Singh Rajput { MSR_MTRRfix4K_C0000, 8 }, /* eight 4k MTRRs */ 332ec1df41SThomas Gleixner {} 342ec1df41SThomas Gleixner }; 352ec1df41SThomas Gleixner 362ec1df41SThomas Gleixner static unsigned long smp_changes_mask; 372e5d9c85Svenkatesh.pallipadi@intel.com static int mtrr_state_set; 3895ffa243SYinghai Lu u64 mtrr_tom2; 392ec1df41SThomas Gleixner 40a1a499a3SJaswinder Singh Rajput struct mtrr_state_type mtrr_state; 41932d27a7SSheng Yang EXPORT_SYMBOL_GPL(mtrr_state); 42932d27a7SSheng Yang 43d053b481SJuergen Gross /* Reserved bits in the high portion of the MTRRphysBaseN MSR. */ 44d053b481SJuergen Gross u32 phys_hi_rsvd; 45f6b98064SJuergen Gross 46a1a499a3SJaswinder Singh Rajput /* 473ff42da5SAndreas Herrmann * BIOS is expected to clear MtrrFixDramModEn bit, see for example 483ff42da5SAndreas Herrmann * "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD 493ff42da5SAndreas Herrmann * Opteron Processors" (26094 Rev. 3.30 February 2006), section 503ff42da5SAndreas Herrmann * "13.2.1.2 SYSCFG Register": "The MtrrFixDramModEn bit should be set 516a6256f9SAdam Buchbinder * to 1 during BIOS initialization of the fixed MTRRs, then cleared to 523ff42da5SAndreas Herrmann * 0 for operation." 533ff42da5SAndreas Herrmann */ 543ff42da5SAndreas Herrmann static inline void k8_check_syscfg_dram_mod_en(void) 553ff42da5SAndreas Herrmann { 563ff42da5SAndreas Herrmann u32 lo, hi; 573ff42da5SAndreas Herrmann 583ff42da5SAndreas Herrmann if (!((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && 593ff42da5SAndreas Herrmann (boot_cpu_data.x86 >= 0x0f))) 603ff42da5SAndreas Herrmann return; 613ff42da5SAndreas Herrmann 62059e5c32SBrijesh Singh rdmsr(MSR_AMD64_SYSCFG, lo, hi); 633ff42da5SAndreas Herrmann if (lo & K8_MTRRFIXRANGE_DRAM_MODIFY) { 641b74dde7SChen Yucong pr_err(FW_WARN "MTRR: CPU %u: SYSCFG[MtrrFixDramModEn]" 653ff42da5SAndreas Herrmann " not cleared by BIOS, clearing this bit\n", 663ff42da5SAndreas Herrmann smp_processor_id()); 673ff42da5SAndreas Herrmann lo &= ~K8_MTRRFIXRANGE_DRAM_MODIFY; 68059e5c32SBrijesh Singh mtrr_wrmsr(MSR_AMD64_SYSCFG, lo, hi); 693ff42da5SAndreas Herrmann } 703ff42da5SAndreas Herrmann } 713ff42da5SAndreas Herrmann 72351e5a70SVenkatesh Pallipadi /* Get the size of contiguous MTRR range */ 73351e5a70SVenkatesh Pallipadi static u64 get_mtrr_size(u64 mask) 74351e5a70SVenkatesh Pallipadi { 75351e5a70SVenkatesh Pallipadi u64 size; 76351e5a70SVenkatesh Pallipadi 77d053b481SJuergen Gross mask |= (u64)phys_hi_rsvd << 32; 78351e5a70SVenkatesh Pallipadi size = -mask; 79d053b481SJuergen Gross 80351e5a70SVenkatesh Pallipadi return size; 81351e5a70SVenkatesh Pallipadi } 82351e5a70SVenkatesh Pallipadi 83*1ca12099SJuergen Gross static u8 get_effective_type(u8 type1, u8 type2) 84*1ca12099SJuergen Gross { 85*1ca12099SJuergen Gross if (type1 == MTRR_TYPE_UNCACHABLE || type2 == MTRR_TYPE_UNCACHABLE) 86*1ca12099SJuergen Gross return MTRR_TYPE_UNCACHABLE; 87*1ca12099SJuergen Gross 88*1ca12099SJuergen Gross if ((type1 == MTRR_TYPE_WRBACK && type2 == MTRR_TYPE_WRTHROUGH) || 89*1ca12099SJuergen Gross (type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK)) 90*1ca12099SJuergen Gross return MTRR_TYPE_WRTHROUGH; 91*1ca12099SJuergen Gross 92*1ca12099SJuergen Gross if (type1 != type2) 93*1ca12099SJuergen Gross return MTRR_TYPE_UNCACHABLE; 94*1ca12099SJuergen Gross 95*1ca12099SJuergen Gross return type1; 96*1ca12099SJuergen Gross } 97*1ca12099SJuergen Gross 982e5d9c85Svenkatesh.pallipadi@intel.com /* 99a7f07cfbSVenkatesh Pallipadi * Check and return the effective type for MTRR-MTRR type overlap. 100*1ca12099SJuergen Gross * Returns true if the effective type is UNCACHEABLE, else returns false 101a7f07cfbSVenkatesh Pallipadi */ 102*1ca12099SJuergen Gross static bool check_type_overlap(u8 *prev, u8 *curr) 103a7f07cfbSVenkatesh Pallipadi { 104*1ca12099SJuergen Gross *prev = *curr = get_effective_type(*curr, *prev); 105a7f07cfbSVenkatesh Pallipadi 106*1ca12099SJuergen Gross return *prev == MTRR_TYPE_UNCACHABLE; 107a7f07cfbSVenkatesh Pallipadi } 108a7f07cfbSVenkatesh Pallipadi 1090cc705f5SToshi Kani /** 1100cc705f5SToshi Kani * mtrr_type_lookup_fixed - look up memory type in MTRR fixed entries 1110cc705f5SToshi Kani * 1120cc705f5SToshi Kani * Return the MTRR fixed memory type of 'start'. 1130cc705f5SToshi Kani * 1140cc705f5SToshi Kani * MTRR fixed entries are divided into the following ways: 1150cc705f5SToshi Kani * 0x00000 - 0x7FFFF : This range is divided into eight 64KB sub-ranges 1160cc705f5SToshi Kani * 0x80000 - 0xBFFFF : This range is divided into sixteen 16KB sub-ranges 1170cc705f5SToshi Kani * 0xC0000 - 0xFFFFF : This range is divided into sixty-four 4KB sub-ranges 1180cc705f5SToshi Kani * 1190cc705f5SToshi Kani * Return Values: 1200cc705f5SToshi Kani * MTRR_TYPE_(type) - Matched memory type 1210cc705f5SToshi Kani * MTRR_TYPE_INVALID - Unmatched 1222e5d9c85Svenkatesh.pallipadi@intel.com */ 1230cc705f5SToshi Kani static u8 mtrr_type_lookup_fixed(u64 start, u64 end) 1240cc705f5SToshi Kani { 1250cc705f5SToshi Kani int idx; 1260cc705f5SToshi Kani 1270cc705f5SToshi Kani if (start >= 0x100000) 1280cc705f5SToshi Kani return MTRR_TYPE_INVALID; 1290cc705f5SToshi Kani 1300cc705f5SToshi Kani /* 0x0 - 0x7FFFF */ 1310cc705f5SToshi Kani if (start < 0x80000) { 1320cc705f5SToshi Kani idx = 0; 1330cc705f5SToshi Kani idx += (start >> 16); 1340cc705f5SToshi Kani return mtrr_state.fixed_ranges[idx]; 1350cc705f5SToshi Kani /* 0x80000 - 0xBFFFF */ 1360cc705f5SToshi Kani } else if (start < 0xC0000) { 1370cc705f5SToshi Kani idx = 1 * 8; 1380cc705f5SToshi Kani idx += ((start - 0x80000) >> 14); 1390cc705f5SToshi Kani return mtrr_state.fixed_ranges[idx]; 1400cc705f5SToshi Kani } 1410cc705f5SToshi Kani 1420cc705f5SToshi Kani /* 0xC0000 - 0xFFFFF */ 1430cc705f5SToshi Kani idx = 3 * 8; 1440cc705f5SToshi Kani idx += ((start - 0xC0000) >> 12); 1450cc705f5SToshi Kani return mtrr_state.fixed_ranges[idx]; 1460cc705f5SToshi Kani } 1470cc705f5SToshi Kani 1480cc705f5SToshi Kani /** 1490cc705f5SToshi Kani * mtrr_type_lookup_variable - look up memory type in MTRR variable entries 1500cc705f5SToshi Kani * 1510cc705f5SToshi Kani * Return Value: 1520cc705f5SToshi Kani * MTRR_TYPE_(type) - Matched memory type or default memory type (unmatched) 1530cc705f5SToshi Kani * 154b73522e0SToshi Kani * Output Arguments: 1550cc705f5SToshi Kani * repeat - Set to 1 when [start:end] spanned across MTRR range and type 1560cc705f5SToshi Kani * returned corresponds only to [start:*partial_end]. Caller has 1570cc705f5SToshi Kani * to lookup again for [*partial_end:end]. 158b73522e0SToshi Kani * 159b73522e0SToshi Kani * uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the 160b73522e0SToshi Kani * region is fully covered by a single MTRR entry or the default 161b73522e0SToshi Kani * type. 1620cc705f5SToshi Kani */ 1630cc705f5SToshi Kani static u8 mtrr_type_lookup_variable(u64 start, u64 end, u64 *partial_end, 164b73522e0SToshi Kani int *repeat, u8 *uniform) 1652e5d9c85Svenkatesh.pallipadi@intel.com { 1662e5d9c85Svenkatesh.pallipadi@intel.com int i; 1672e5d9c85Svenkatesh.pallipadi@intel.com u64 base, mask; 1682e5d9c85Svenkatesh.pallipadi@intel.com u8 prev_match, curr_match; 1692e5d9c85Svenkatesh.pallipadi@intel.com 170351e5a70SVenkatesh Pallipadi *repeat = 0; 171b73522e0SToshi Kani *uniform = 1; 1722e5d9c85Svenkatesh.pallipadi@intel.com 1733d3ca416SToshi Kani prev_match = MTRR_TYPE_INVALID; 1742e5d9c85Svenkatesh.pallipadi@intel.com for (i = 0; i < num_var_ranges; ++i) { 1757f0431e3SToshi Kani unsigned short start_state, end_state, inclusive; 1762e5d9c85Svenkatesh.pallipadi@intel.com 177d053b481SJuergen Gross if (!(mtrr_state.var_ranges[i].mask_lo & MTRR_PHYSMASK_V)) 1782e5d9c85Svenkatesh.pallipadi@intel.com continue; 1792e5d9c85Svenkatesh.pallipadi@intel.com 1802e5d9c85Svenkatesh.pallipadi@intel.com base = (((u64)mtrr_state.var_ranges[i].base_hi) << 32) + 1812e5d9c85Svenkatesh.pallipadi@intel.com (mtrr_state.var_ranges[i].base_lo & PAGE_MASK); 1822e5d9c85Svenkatesh.pallipadi@intel.com mask = (((u64)mtrr_state.var_ranges[i].mask_hi) << 32) + 1832e5d9c85Svenkatesh.pallipadi@intel.com (mtrr_state.var_ranges[i].mask_lo & PAGE_MASK); 1842e5d9c85Svenkatesh.pallipadi@intel.com 1852e5d9c85Svenkatesh.pallipadi@intel.com start_state = ((start & mask) == (base & mask)); 1862e5d9c85Svenkatesh.pallipadi@intel.com end_state = ((end & mask) == (base & mask)); 1877f0431e3SToshi Kani inclusive = ((start < base) && (end > base)); 188351e5a70SVenkatesh Pallipadi 1897f0431e3SToshi Kani if ((start_state != end_state) || inclusive) { 190351e5a70SVenkatesh Pallipadi /* 191351e5a70SVenkatesh Pallipadi * We have start:end spanning across an MTRR. 1927f0431e3SToshi Kani * We split the region into either 1937f0431e3SToshi Kani * 1947f0431e3SToshi Kani * - start_state:1 195351e5a70SVenkatesh Pallipadi * (start:mtrr_end)(mtrr_end:end) 1967f0431e3SToshi Kani * - end_state:1 197351e5a70SVenkatesh Pallipadi * (start:mtrr_start)(mtrr_start:end) 1987f0431e3SToshi Kani * - inclusive:1 1997f0431e3SToshi Kani * (start:mtrr_start)(mtrr_start:mtrr_end)(mtrr_end:end) 2007f0431e3SToshi Kani * 201351e5a70SVenkatesh Pallipadi * depending on kind of overlap. 2027f0431e3SToshi Kani * 2037f0431e3SToshi Kani * Return the type of the first region and a pointer 2047f0431e3SToshi Kani * to the start of next region so that caller will be 2057f0431e3SToshi Kani * advised to lookup again after having adjusted start 2067f0431e3SToshi Kani * and end. 2077f0431e3SToshi Kani * 2080cc705f5SToshi Kani * Note: This way we handle overlaps with multiple 2090cc705f5SToshi Kani * entries and the default type properly. 210351e5a70SVenkatesh Pallipadi */ 211351e5a70SVenkatesh Pallipadi if (start_state) 212351e5a70SVenkatesh Pallipadi *partial_end = base + get_mtrr_size(mask); 213351e5a70SVenkatesh Pallipadi else 214351e5a70SVenkatesh Pallipadi *partial_end = base; 215351e5a70SVenkatesh Pallipadi 216351e5a70SVenkatesh Pallipadi if (unlikely(*partial_end <= start)) { 217351e5a70SVenkatesh Pallipadi WARN_ON(1); 218351e5a70SVenkatesh Pallipadi *partial_end = start + PAGE_SIZE; 219351e5a70SVenkatesh Pallipadi } 220351e5a70SVenkatesh Pallipadi 221351e5a70SVenkatesh Pallipadi end = *partial_end - 1; /* end is inclusive */ 222351e5a70SVenkatesh Pallipadi *repeat = 1; 223b73522e0SToshi Kani *uniform = 0; 224351e5a70SVenkatesh Pallipadi } 2252e5d9c85Svenkatesh.pallipadi@intel.com 226a1a499a3SJaswinder Singh Rajput if ((start & mask) != (base & mask)) 2272e5d9c85Svenkatesh.pallipadi@intel.com continue; 2282e5d9c85Svenkatesh.pallipadi@intel.com 229d053b481SJuergen Gross curr_match = mtrr_state.var_ranges[i].base_lo & MTRR_PHYSBASE_TYPE; 2303d3ca416SToshi Kani if (prev_match == MTRR_TYPE_INVALID) { 2312e5d9c85Svenkatesh.pallipadi@intel.com prev_match = curr_match; 2322e5d9c85Svenkatesh.pallipadi@intel.com continue; 2332e5d9c85Svenkatesh.pallipadi@intel.com } 2342e5d9c85Svenkatesh.pallipadi@intel.com 235b73522e0SToshi Kani *uniform = 0; 236a7f07cfbSVenkatesh Pallipadi if (check_type_overlap(&prev_match, &curr_match)) 237a7f07cfbSVenkatesh Pallipadi return curr_match; 2382e5d9c85Svenkatesh.pallipadi@intel.com } 2392e5d9c85Svenkatesh.pallipadi@intel.com 2403d3ca416SToshi Kani if (prev_match != MTRR_TYPE_INVALID) 2412e5d9c85Svenkatesh.pallipadi@intel.com return prev_match; 2422e5d9c85Svenkatesh.pallipadi@intel.com 2432e5d9c85Svenkatesh.pallipadi@intel.com return mtrr_state.def_type; 2442e5d9c85Svenkatesh.pallipadi@intel.com } 2452e5d9c85Svenkatesh.pallipadi@intel.com 2460cc705f5SToshi Kani /** 24729055dc7SJuergen Gross * mtrr_overwrite_state - set static MTRR state 24829055dc7SJuergen Gross * 24929055dc7SJuergen Gross * Used to set MTRR state via different means (e.g. with data obtained from 25029055dc7SJuergen Gross * a hypervisor). 25129055dc7SJuergen Gross * Is allowed only for special cases when running virtualized. Must be called 25229055dc7SJuergen Gross * from the x86_init.hyper.init_platform() hook. It can be called only once. 25329055dc7SJuergen Gross * The MTRR state can't be changed afterwards. To ensure that, X86_FEATURE_MTRR 25429055dc7SJuergen Gross * is cleared. 25529055dc7SJuergen Gross */ 25629055dc7SJuergen Gross void mtrr_overwrite_state(struct mtrr_var_range *var, unsigned int num_var, 25729055dc7SJuergen Gross mtrr_type def_type) 25829055dc7SJuergen Gross { 25929055dc7SJuergen Gross unsigned int i; 26029055dc7SJuergen Gross 26129055dc7SJuergen Gross /* Only allowed to be called once before mtrr_bp_init(). */ 26229055dc7SJuergen Gross if (WARN_ON_ONCE(mtrr_state_set)) 26329055dc7SJuergen Gross return; 26429055dc7SJuergen Gross 26529055dc7SJuergen Gross /* Only allowed when running virtualized. */ 26629055dc7SJuergen Gross if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) 26729055dc7SJuergen Gross return; 26829055dc7SJuergen Gross 26929055dc7SJuergen Gross /* 27029055dc7SJuergen Gross * Only allowed for special virtualization cases: 27129055dc7SJuergen Gross * - when running as Hyper-V, SEV-SNP guest using vTOM 27229055dc7SJuergen Gross * - when running as Xen PV guest 27329055dc7SJuergen Gross * - when running as SEV-SNP or TDX guest to avoid unnecessary 27429055dc7SJuergen Gross * VMM communication/Virtualization exceptions (#VC, #VE) 27529055dc7SJuergen Gross */ 27629055dc7SJuergen Gross if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) && 27729055dc7SJuergen Gross !hv_is_isolation_supported() && 27829055dc7SJuergen Gross !cpu_feature_enabled(X86_FEATURE_XENPV) && 27929055dc7SJuergen Gross !cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) 28029055dc7SJuergen Gross return; 28129055dc7SJuergen Gross 28229055dc7SJuergen Gross /* Disable MTRR in order to disable MTRR modifications. */ 28329055dc7SJuergen Gross setup_clear_cpu_cap(X86_FEATURE_MTRR); 28429055dc7SJuergen Gross 28529055dc7SJuergen Gross if (var) { 28629055dc7SJuergen Gross if (num_var > MTRR_MAX_VAR_RANGES) { 28729055dc7SJuergen Gross pr_warn("Trying to overwrite MTRR state with %u variable entries\n", 28829055dc7SJuergen Gross num_var); 28929055dc7SJuergen Gross num_var = MTRR_MAX_VAR_RANGES; 29029055dc7SJuergen Gross } 29129055dc7SJuergen Gross for (i = 0; i < num_var; i++) 29229055dc7SJuergen Gross mtrr_state.var_ranges[i] = var[i]; 29329055dc7SJuergen Gross num_var_ranges = num_var; 29429055dc7SJuergen Gross } 29529055dc7SJuergen Gross 29629055dc7SJuergen Gross mtrr_state.def_type = def_type; 29729055dc7SJuergen Gross mtrr_state.enabled |= MTRR_STATE_MTRR_ENABLED; 29829055dc7SJuergen Gross 29929055dc7SJuergen Gross mtrr_state_set = 1; 30029055dc7SJuergen Gross } 30129055dc7SJuergen Gross 30229055dc7SJuergen Gross /** 3030cc705f5SToshi Kani * mtrr_type_lookup - look up memory type in MTRR 3040cc705f5SToshi Kani * 3050cc705f5SToshi Kani * Return Values: 3060cc705f5SToshi Kani * MTRR_TYPE_(type) - The effective MTRR type for the region 3070cc705f5SToshi Kani * MTRR_TYPE_INVALID - MTRR is disabled 308b73522e0SToshi Kani * 309b73522e0SToshi Kani * Output Argument: 310b73522e0SToshi Kani * uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the 311b73522e0SToshi Kani * region is fully covered by a single MTRR entry or the default 312b73522e0SToshi Kani * type. 313351e5a70SVenkatesh Pallipadi */ 314b73522e0SToshi Kani u8 mtrr_type_lookup(u64 start, u64 end, u8 *uniform) 315351e5a70SVenkatesh Pallipadi { 316b73522e0SToshi Kani u8 type, prev_type, is_uniform = 1, dummy; 317351e5a70SVenkatesh Pallipadi int repeat; 318351e5a70SVenkatesh Pallipadi u64 partial_end; 319351e5a70SVenkatesh Pallipadi 320cb7f4a8bSYing-Tsun Huang /* Make end inclusive instead of exclusive */ 321cb7f4a8bSYing-Tsun Huang end--; 322cb7f4a8bSYing-Tsun Huang 3230cc705f5SToshi Kani if (!mtrr_state_set) 3240cc705f5SToshi Kani return MTRR_TYPE_INVALID; 3250cc705f5SToshi Kani 3260cc705f5SToshi Kani if (!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED)) 3270cc705f5SToshi Kani return MTRR_TYPE_INVALID; 3280cc705f5SToshi Kani 3290cc705f5SToshi Kani /* 3300cc705f5SToshi Kani * Look up the fixed ranges first, which take priority over 3310cc705f5SToshi Kani * the variable ranges. 3320cc705f5SToshi Kani */ 3330cc705f5SToshi Kani if ((start < 0x100000) && 3340cc705f5SToshi Kani (mtrr_state.have_fixed) && 335b73522e0SToshi Kani (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) { 336b73522e0SToshi Kani is_uniform = 0; 337b73522e0SToshi Kani type = mtrr_type_lookup_fixed(start, end); 338b73522e0SToshi Kani goto out; 339b73522e0SToshi Kani } 3400cc705f5SToshi Kani 3410cc705f5SToshi Kani /* 3420cc705f5SToshi Kani * Look up the variable ranges. Look of multiple ranges matching 3430cc705f5SToshi Kani * this address and pick type as per MTRR precedence. 3440cc705f5SToshi Kani */ 345b73522e0SToshi Kani type = mtrr_type_lookup_variable(start, end, &partial_end, 346b73522e0SToshi Kani &repeat, &is_uniform); 347351e5a70SVenkatesh Pallipadi 348351e5a70SVenkatesh Pallipadi /* 349351e5a70SVenkatesh Pallipadi * Common path is with repeat = 0. 350351e5a70SVenkatesh Pallipadi * However, we can have cases where [start:end] spans across some 3510cc705f5SToshi Kani * MTRR ranges and/or the default type. Do repeated lookups for 3520cc705f5SToshi Kani * that case here. 353351e5a70SVenkatesh Pallipadi */ 354351e5a70SVenkatesh Pallipadi while (repeat) { 355351e5a70SVenkatesh Pallipadi prev_type = type; 356351e5a70SVenkatesh Pallipadi start = partial_end; 357b73522e0SToshi Kani is_uniform = 0; 358b73522e0SToshi Kani type = mtrr_type_lookup_variable(start, end, &partial_end, 359b73522e0SToshi Kani &repeat, &dummy); 360351e5a70SVenkatesh Pallipadi 361351e5a70SVenkatesh Pallipadi if (check_type_overlap(&prev_type, &type)) 362b73522e0SToshi Kani goto out; 363351e5a70SVenkatesh Pallipadi } 364351e5a70SVenkatesh Pallipadi 3650cc705f5SToshi Kani if (mtrr_tom2 && (start >= (1ULL<<32)) && (end < mtrr_tom2)) 366b73522e0SToshi Kani type = MTRR_TYPE_WRBACK; 3670cc705f5SToshi Kani 368b73522e0SToshi Kani out: 369b73522e0SToshi Kani *uniform = is_uniform; 370351e5a70SVenkatesh Pallipadi return type; 371351e5a70SVenkatesh Pallipadi } 372351e5a70SVenkatesh Pallipadi 3732ec1df41SThomas Gleixner /* Get the MSR pair relating to a var range */ 3742ec1df41SThomas Gleixner static void 3752ec1df41SThomas Gleixner get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr) 3762ec1df41SThomas Gleixner { 3772ec1df41SThomas Gleixner rdmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); 3782ec1df41SThomas Gleixner rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); 3792ec1df41SThomas Gleixner } 3802ec1df41SThomas Gleixner 381a1a499a3SJaswinder Singh Rajput /* Fill the MSR pair relating to a var range */ 38295ffa243SYinghai Lu void fill_mtrr_var_range(unsigned int index, 38395ffa243SYinghai Lu u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi) 38495ffa243SYinghai Lu { 38595ffa243SYinghai Lu struct mtrr_var_range *vr; 38695ffa243SYinghai Lu 38795ffa243SYinghai Lu vr = mtrr_state.var_ranges; 38895ffa243SYinghai Lu 38995ffa243SYinghai Lu vr[index].base_lo = base_lo; 39095ffa243SYinghai Lu vr[index].base_hi = base_hi; 39195ffa243SYinghai Lu vr[index].mask_lo = mask_lo; 39295ffa243SYinghai Lu vr[index].mask_hi = mask_hi; 39395ffa243SYinghai Lu } 39495ffa243SYinghai Lu 395a1a499a3SJaswinder Singh Rajput static void get_fixed_ranges(mtrr_type *frs) 3962ec1df41SThomas Gleixner { 3972ec1df41SThomas Gleixner unsigned int *p = (unsigned int *)frs; 3982ec1df41SThomas Gleixner int i; 3992ec1df41SThomas Gleixner 4003ff42da5SAndreas Herrmann k8_check_syscfg_dram_mod_en(); 4013ff42da5SAndreas Herrmann 402a036c7a3SJaswinder Singh Rajput rdmsr(MSR_MTRRfix64K_00000, p[0], p[1]); 4032ec1df41SThomas Gleixner 4042ec1df41SThomas Gleixner for (i = 0; i < 2; i++) 4057d9d55e4SJaswinder Singh Rajput rdmsr(MSR_MTRRfix16K_80000 + i, p[2 + i * 2], p[3 + i * 2]); 4062ec1df41SThomas Gleixner for (i = 0; i < 8; i++) 407ba5673ffSJaswinder Singh Rajput rdmsr(MSR_MTRRfix4K_C0000 + i, p[6 + i * 2], p[7 + i * 2]); 4082ec1df41SThomas Gleixner } 4092ec1df41SThomas Gleixner 4102ec1df41SThomas Gleixner void mtrr_save_fixed_ranges(void *info) 4112ec1df41SThomas Gleixner { 412362f924bSBorislav Petkov if (boot_cpu_has(X86_FEATURE_MTRR)) 4132ec1df41SThomas Gleixner get_fixed_ranges(mtrr_state.fixed_ranges); 4142ec1df41SThomas Gleixner } 4152ec1df41SThomas Gleixner 416d4c90e37SYinghai Lu static unsigned __initdata last_fixed_start; 417d4c90e37SYinghai Lu static unsigned __initdata last_fixed_end; 418d4c90e37SYinghai Lu static mtrr_type __initdata last_fixed_type; 419d4c90e37SYinghai Lu 420d4c90e37SYinghai Lu static void __init print_fixed_last(void) 421d4c90e37SYinghai Lu { 422d4c90e37SYinghai Lu if (!last_fixed_end) 423d4c90e37SYinghai Lu return; 424d4c90e37SYinghai Lu 425a1a499a3SJaswinder Singh Rajput pr_debug(" %05X-%05X %s\n", last_fixed_start, 426d4c90e37SYinghai Lu last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type)); 427d4c90e37SYinghai Lu 428d4c90e37SYinghai Lu last_fixed_end = 0; 429d4c90e37SYinghai Lu } 430d4c90e37SYinghai Lu 431d4c90e37SYinghai Lu static void __init update_fixed_last(unsigned base, unsigned end, 432d4c90e37SYinghai Lu mtrr_type type) 433d4c90e37SYinghai Lu { 434d4c90e37SYinghai Lu last_fixed_start = base; 435d4c90e37SYinghai Lu last_fixed_end = end; 436d4c90e37SYinghai Lu last_fixed_type = type; 437d4c90e37SYinghai Lu } 438d4c90e37SYinghai Lu 439a1a499a3SJaswinder Singh Rajput static void __init 440a1a499a3SJaswinder Singh Rajput print_fixed(unsigned base, unsigned step, const mtrr_type *types) 4412ec1df41SThomas Gleixner { 4422ec1df41SThomas Gleixner unsigned i; 4432ec1df41SThomas Gleixner 444d4c90e37SYinghai Lu for (i = 0; i < 8; ++i, ++types, base += step) { 445d4c90e37SYinghai Lu if (last_fixed_end == 0) { 446d4c90e37SYinghai Lu update_fixed_last(base, base + step, *types); 447d4c90e37SYinghai Lu continue; 448d4c90e37SYinghai Lu } 449d4c90e37SYinghai Lu if (last_fixed_end == base && last_fixed_type == *types) { 450d4c90e37SYinghai Lu last_fixed_end = base + step; 451d4c90e37SYinghai Lu continue; 452d4c90e37SYinghai Lu } 453d4c90e37SYinghai Lu /* new segments: gap or different type */ 454d4c90e37SYinghai Lu print_fixed_last(); 455d4c90e37SYinghai Lu update_fixed_last(base, base + step, *types); 456d4c90e37SYinghai Lu } 4572ec1df41SThomas Gleixner } 4582ec1df41SThomas Gleixner 4598ad97905SYinghai Lu static void __init print_mtrr_state(void) 4608ad97905SYinghai Lu { 4618ad97905SYinghai Lu unsigned int i; 4628ad97905SYinghai Lu int high_width; 4638ad97905SYinghai Lu 464a1a499a3SJaswinder Singh Rajput pr_debug("MTRR default type: %s\n", 465d4c90e37SYinghai Lu mtrr_attrib_to_str(mtrr_state.def_type)); 4668ad97905SYinghai Lu if (mtrr_state.have_fixed) { 467a1a499a3SJaswinder Singh Rajput pr_debug("MTRR fixed ranges %sabled:\n", 4689b3aca62SToshi Kani ((mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED) && 4699b3aca62SToshi Kani (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) ? 4709b3aca62SToshi Kani "en" : "dis"); 4718ad97905SYinghai Lu print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0); 4728ad97905SYinghai Lu for (i = 0; i < 2; ++i) 473a1a499a3SJaswinder Singh Rajput print_fixed(0x80000 + i * 0x20000, 0x04000, 474a1a499a3SJaswinder Singh Rajput mtrr_state.fixed_ranges + (i + 1) * 8); 4758ad97905SYinghai Lu for (i = 0; i < 8; ++i) 476a1a499a3SJaswinder Singh Rajput print_fixed(0xC0000 + i * 0x08000, 0x01000, 477a1a499a3SJaswinder Singh Rajput mtrr_state.fixed_ranges + (i + 3) * 8); 478d4c90e37SYinghai Lu 479d4c90e37SYinghai Lu /* tail */ 480d4c90e37SYinghai Lu print_fixed_last(); 4818ad97905SYinghai Lu } 482a1a499a3SJaswinder Singh Rajput pr_debug("MTRR variable ranges %sabled:\n", 4839b3aca62SToshi Kani mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED ? "en" : "dis"); 484f6b98064SJuergen Gross high_width = (boot_cpu_data.x86_phys_bits - (32 - PAGE_SHIFT) + 3) / 4; 485a1a499a3SJaswinder Singh Rajput 4868ad97905SYinghai Lu for (i = 0; i < num_var_ranges; ++i) { 487d053b481SJuergen Gross if (mtrr_state.var_ranges[i].mask_lo & MTRR_PHYSMASK_V) 488a1a499a3SJaswinder Singh Rajput pr_debug(" %u base %0*X%05X000 mask %0*X%05X000 %s\n", 4898ad97905SYinghai Lu i, 4908ad97905SYinghai Lu high_width, 4918ad97905SYinghai Lu mtrr_state.var_ranges[i].base_hi, 4928ad97905SYinghai Lu mtrr_state.var_ranges[i].base_lo >> 12, 4938ad97905SYinghai Lu high_width, 4948ad97905SYinghai Lu mtrr_state.var_ranges[i].mask_hi, 4958ad97905SYinghai Lu mtrr_state.var_ranges[i].mask_lo >> 12, 496d053b481SJuergen Gross mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 497d053b481SJuergen Gross MTRR_PHYSBASE_TYPE)); 4988ad97905SYinghai Lu else 499a1a499a3SJaswinder Singh Rajput pr_debug(" %u disabled\n", i); 5008ad97905SYinghai Lu } 501a1a499a3SJaswinder Singh Rajput if (mtrr_tom2) 502a1a499a3SJaswinder Singh Rajput pr_debug("TOM2: %016llx aka %lldM\n", mtrr_tom2, mtrr_tom2>>20); 5038ad97905SYinghai Lu } 5048ad97905SYinghai Lu 5052ec1df41SThomas Gleixner /* Grab all of the MTRR state for this CPU into *state */ 506f9626104SLuis R. Rodriguez bool __init get_mtrr_state(void) 5072ec1df41SThomas Gleixner { 5082ec1df41SThomas Gleixner struct mtrr_var_range *vrs; 509a1a499a3SJaswinder Singh Rajput unsigned lo, dummy; 510a1a499a3SJaswinder Singh Rajput unsigned int i; 5112ec1df41SThomas Gleixner 5122ec1df41SThomas Gleixner vrs = mtrr_state.var_ranges; 5132ec1df41SThomas Gleixner 514d9bcc01dSJaswinder Singh Rajput rdmsr(MSR_MTRRcap, lo, dummy); 515d053b481SJuergen Gross mtrr_state.have_fixed = lo & MTRR_CAP_FIX; 5162ec1df41SThomas Gleixner 5172ec1df41SThomas Gleixner for (i = 0; i < num_var_ranges; i++) 5182ec1df41SThomas Gleixner get_mtrr_var_range(i, &vrs[i]); 5192ec1df41SThomas Gleixner if (mtrr_state.have_fixed) 5202ec1df41SThomas Gleixner get_fixed_ranges(mtrr_state.fixed_ranges); 5212ec1df41SThomas Gleixner 52252650257SJaswinder Singh Rajput rdmsr(MSR_MTRRdefType, lo, dummy); 523d053b481SJuergen Gross mtrr_state.def_type = lo & MTRR_DEF_TYPE_TYPE; 524d053b481SJuergen Gross mtrr_state.enabled = (lo & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT; 5252ec1df41SThomas Gleixner 52635605a10SYinghai Lu if (amd_special_default_mtrr()) { 5270da72a4aSThomas Gleixner unsigned low, high; 528a1a499a3SJaswinder Singh Rajput 52935605a10SYinghai Lu /* TOP_MEM2 */ 5300da72a4aSThomas Gleixner rdmsr(MSR_K8_TOP_MEM2, low, high); 53195ffa243SYinghai Lu mtrr_tom2 = high; 53295ffa243SYinghai Lu mtrr_tom2 <<= 32; 53395ffa243SYinghai Lu mtrr_tom2 |= low; 5348004dd96SYinghai Lu mtrr_tom2 &= 0xffffff800000ULL; 53535605a10SYinghai Lu } 5362ec1df41SThomas Gleixner 5378ad97905SYinghai Lu print_mtrr_state(); 5388ad97905SYinghai Lu 5392e5d9c85Svenkatesh.pallipadi@intel.com mtrr_state_set = 1; 5402e5d9c85Svenkatesh.pallipadi@intel.com 541f9626104SLuis R. Rodriguez return !!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED); 5422ec1df41SThomas Gleixner } 5432ec1df41SThomas Gleixner 544a1a499a3SJaswinder Singh Rajput /* Some BIOS's are messed up and don't set all MTRRs the same! */ 5452ec1df41SThomas Gleixner void __init mtrr_state_warn(void) 5462ec1df41SThomas Gleixner { 5472ec1df41SThomas Gleixner unsigned long mask = smp_changes_mask; 5482ec1df41SThomas Gleixner 5492ec1df41SThomas Gleixner if (!mask) 5502ec1df41SThomas Gleixner return; 5512ec1df41SThomas Gleixner if (mask & MTRR_CHANGE_MASK_FIXED) 5521b74dde7SChen Yucong pr_warn("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); 5532ec1df41SThomas Gleixner if (mask & MTRR_CHANGE_MASK_VARIABLE) 5541b74dde7SChen Yucong pr_warn("mtrr: your CPUs had inconsistent variable MTRR settings\n"); 5552ec1df41SThomas Gleixner if (mask & MTRR_CHANGE_MASK_DEFTYPE) 5561b74dde7SChen Yucong pr_warn("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); 557a1a499a3SJaswinder Singh Rajput 5581b74dde7SChen Yucong pr_info("mtrr: probably your BIOS does not setup all CPUs.\n"); 5591b74dde7SChen Yucong pr_info("mtrr: corrected configuration.\n"); 5602ec1df41SThomas Gleixner } 5612ec1df41SThomas Gleixner 562a1a499a3SJaswinder Singh Rajput /* 563a1a499a3SJaswinder Singh Rajput * Doesn't attempt to pass an error out to MTRR users 564a1a499a3SJaswinder Singh Rajput * because it's quite complicated in some cases and probably not 565a1a499a3SJaswinder Singh Rajput * worth it because the best error handling is to ignore it. 566a1a499a3SJaswinder Singh Rajput */ 5672ec1df41SThomas Gleixner void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b) 5682ec1df41SThomas Gleixner { 569a1a499a3SJaswinder Singh Rajput if (wrmsr_safe(msr, a, b) < 0) { 5701b74dde7SChen Yucong pr_err("MTRR: CPU %u: Writing MSR %x to %x:%x failed\n", 5712ec1df41SThomas Gleixner smp_processor_id(), msr, a, b); 5722ec1df41SThomas Gleixner } 573a1a499a3SJaswinder Singh Rajput } 5742ec1df41SThomas Gleixner 5752ec1df41SThomas Gleixner /** 576a1a499a3SJaswinder Singh Rajput * set_fixed_range - checks & updates a fixed-range MTRR if it 577a1a499a3SJaswinder Singh Rajput * differs from the value it should have 5781d3381ebSRandy Dunlap * @msr: MSR address of the MTTR which should be checked and updated 5791d3381ebSRandy Dunlap * @changed: pointer which indicates whether the MTRR needed to be changed 5801d3381ebSRandy Dunlap * @msrwords: pointer to the MSR values which the MSR should have 5812ec1df41SThomas Gleixner */ 5822d2ee8deSPaul Jimenez static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) 5832ec1df41SThomas Gleixner { 5842ec1df41SThomas Gleixner unsigned lo, hi; 5852ec1df41SThomas Gleixner 5862ec1df41SThomas Gleixner rdmsr(msr, lo, hi); 5872ec1df41SThomas Gleixner 5882ec1df41SThomas Gleixner if (lo != msrwords[0] || hi != msrwords[1]) { 5892ec1df41SThomas Gleixner mtrr_wrmsr(msr, msrwords[0], msrwords[1]); 5902d2ee8deSPaul Jimenez *changed = true; 5912ec1df41SThomas Gleixner } 5922ec1df41SThomas Gleixner } 5932ec1df41SThomas Gleixner 5941d3381ebSRandy Dunlap /** 5951d3381ebSRandy Dunlap * generic_get_free_region - Get a free MTRR. 5961d3381ebSRandy Dunlap * @base: The starting (base) address of the region. 5971d3381ebSRandy Dunlap * @size: The size (in bytes) of the region. 5981d3381ebSRandy Dunlap * @replace_reg: mtrr index to be replaced; set to invalid value if none. 5991d3381ebSRandy Dunlap * 6001d3381ebSRandy Dunlap * Returns: The index of the region on success, else negative on error. 6012ec1df41SThomas Gleixner */ 602a1a499a3SJaswinder Singh Rajput int 603a1a499a3SJaswinder Singh Rajput generic_get_free_region(unsigned long base, unsigned long size, int replace_reg) 6042ec1df41SThomas Gleixner { 6052ec1df41SThomas Gleixner unsigned long lbase, lsize; 606a1a499a3SJaswinder Singh Rajput mtrr_type ltype; 607a1a499a3SJaswinder Singh Rajput int i, max; 6082ec1df41SThomas Gleixner 6092ec1df41SThomas Gleixner max = num_var_ranges; 6102ec1df41SThomas Gleixner if (replace_reg >= 0 && replace_reg < max) 6112ec1df41SThomas Gleixner return replace_reg; 612a1a499a3SJaswinder Singh Rajput 6132ec1df41SThomas Gleixner for (i = 0; i < max; ++i) { 6142ec1df41SThomas Gleixner mtrr_if->get(i, &lbase, &lsize, <ype); 6152ec1df41SThomas Gleixner if (lsize == 0) 6162ec1df41SThomas Gleixner return i; 6172ec1df41SThomas Gleixner } 618a1a499a3SJaswinder Singh Rajput 6192ec1df41SThomas Gleixner return -ENOSPC; 6202ec1df41SThomas Gleixner } 6212ec1df41SThomas Gleixner 6222ec1df41SThomas Gleixner static void generic_get_mtrr(unsigned int reg, unsigned long *base, 6232ec1df41SThomas Gleixner unsigned long *size, mtrr_type *type) 6242ec1df41SThomas Gleixner { 625d5c78673SYinghai Lu u32 mask_lo, mask_hi, base_lo, base_hi; 626d5c78673SYinghai Lu unsigned int hi; 627d5c78673SYinghai Lu u64 tmp, mask; 6282ec1df41SThomas Gleixner 6298ad97905SYinghai Lu /* 6308ad97905SYinghai Lu * get_mtrr doesn't need to update mtrr_state, also it could be called 6318ad97905SYinghai Lu * from any cpu, so try to print it out directly. 6328ad97905SYinghai Lu */ 633fa10ba64SAndi Kleen get_cpu(); 63463516ef6SYinghai Lu 6352ec1df41SThomas Gleixner rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi); 6368ad97905SYinghai Lu 637d053b481SJuergen Gross if (!(mask_lo & MTRR_PHYSMASK_V)) { 6382ec1df41SThomas Gleixner /* Invalid (i.e. free) range */ 6392ec1df41SThomas Gleixner *base = 0; 6402ec1df41SThomas Gleixner *size = 0; 6412ec1df41SThomas Gleixner *type = 0; 64263516ef6SYinghai Lu goto out_put_cpu; 6432ec1df41SThomas Gleixner } 6442ec1df41SThomas Gleixner 6452ec1df41SThomas Gleixner rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); 6462ec1df41SThomas Gleixner 64763516ef6SYinghai Lu /* Work out the shifted address mask: */ 648d053b481SJuergen Gross tmp = (u64)mask_hi << 32 | (mask_lo & PAGE_MASK); 649d053b481SJuergen Gross mask = (u64)phys_hi_rsvd << 32 | tmp; 65063516ef6SYinghai Lu 65163516ef6SYinghai Lu /* Expand tmp with high bits to all 1s: */ 652d5c78673SYinghai Lu hi = fls64(tmp); 65338cc1c3dSYinghai Lu if (hi > 0) { 654d5c78673SYinghai Lu tmp |= ~((1ULL<<(hi - 1)) - 1); 65538cc1c3dSYinghai Lu 656d5c78673SYinghai Lu if (tmp != mask) { 6571b74dde7SChen Yucong pr_warn("mtrr: your BIOS has configured an incorrect mask, fixing it.\n"); 658373d4d09SRusty Russell add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); 659d5c78673SYinghai Lu mask = tmp; 66038cc1c3dSYinghai Lu } 66138cc1c3dSYinghai Lu } 6622ec1df41SThomas Gleixner 66363516ef6SYinghai Lu /* 66463516ef6SYinghai Lu * This works correctly if size is a power of two, i.e. a 66563516ef6SYinghai Lu * contiguous range: 66663516ef6SYinghai Lu */ 667d053b481SJuergen Gross *size = -mask >> PAGE_SHIFT; 668d5c78673SYinghai Lu *base = (u64)base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; 669d053b481SJuergen Gross *type = base_lo & MTRR_PHYSBASE_TYPE; 6708ad97905SYinghai Lu 67163516ef6SYinghai Lu out_put_cpu: 67263516ef6SYinghai Lu put_cpu(); 6732ec1df41SThomas Gleixner } 6742ec1df41SThomas Gleixner 6752ec1df41SThomas Gleixner /** 676a1a499a3SJaswinder Singh Rajput * set_fixed_ranges - checks & updates the fixed-range MTRRs if they 677a1a499a3SJaswinder Singh Rajput * differ from the saved set 6781d3381ebSRandy Dunlap * @frs: pointer to fixed-range MTRR values, saved by get_fixed_ranges() 6792ec1df41SThomas Gleixner */ 6802ec1df41SThomas Gleixner static int set_fixed_ranges(mtrr_type *frs) 6812ec1df41SThomas Gleixner { 6822ec1df41SThomas Gleixner unsigned long long *saved = (unsigned long long *)frs; 6832d2ee8deSPaul Jimenez bool changed = false; 6842ec1df41SThomas Gleixner int block = -1, range; 6852ec1df41SThomas Gleixner 6863ff42da5SAndreas Herrmann k8_check_syscfg_dram_mod_en(); 6873ff42da5SAndreas Herrmann 688a1a499a3SJaswinder Singh Rajput while (fixed_range_blocks[++block].ranges) { 6892ec1df41SThomas Gleixner for (range = 0; range < fixed_range_blocks[block].ranges; range++) 6902ec1df41SThomas Gleixner set_fixed_range(fixed_range_blocks[block].base_msr + range, 6912ec1df41SThomas Gleixner &changed, (unsigned int *)saved++); 692a1a499a3SJaswinder Singh Rajput } 6932ec1df41SThomas Gleixner 6942ec1df41SThomas Gleixner return changed; 6952ec1df41SThomas Gleixner } 6962ec1df41SThomas Gleixner 697a1a499a3SJaswinder Singh Rajput /* 698a1a499a3SJaswinder Singh Rajput * Set the MSR pair relating to a var range. 699a1a499a3SJaswinder Singh Rajput * Returns true if changes are made. 700a1a499a3SJaswinder Singh Rajput */ 7012d2ee8deSPaul Jimenez static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr) 7022ec1df41SThomas Gleixner { 7032ec1df41SThomas Gleixner unsigned int lo, hi; 7042d2ee8deSPaul Jimenez bool changed = false; 7052ec1df41SThomas Gleixner 7062ec1df41SThomas Gleixner rdmsr(MTRRphysBase_MSR(index), lo, hi); 707d053b481SJuergen Gross if ((vr->base_lo & ~MTRR_PHYSBASE_RSVD) != (lo & ~MTRR_PHYSBASE_RSVD) 708d053b481SJuergen Gross || (vr->base_hi & ~phys_hi_rsvd) != (hi & ~phys_hi_rsvd)) { 709a1a499a3SJaswinder Singh Rajput 7102ec1df41SThomas Gleixner mtrr_wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); 7112d2ee8deSPaul Jimenez changed = true; 7122ec1df41SThomas Gleixner } 7132ec1df41SThomas Gleixner 7142ec1df41SThomas Gleixner rdmsr(MTRRphysMask_MSR(index), lo, hi); 7152ec1df41SThomas Gleixner 716d053b481SJuergen Gross if ((vr->mask_lo & ~MTRR_PHYSMASK_RSVD) != (lo & ~MTRR_PHYSMASK_RSVD) 717d053b481SJuergen Gross || (vr->mask_hi & ~phys_hi_rsvd) != (hi & ~phys_hi_rsvd)) { 7182ec1df41SThomas Gleixner mtrr_wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); 7192d2ee8deSPaul Jimenez changed = true; 7202ec1df41SThomas Gleixner } 7212ec1df41SThomas Gleixner return changed; 7222ec1df41SThomas Gleixner } 7232ec1df41SThomas Gleixner 7242ec1df41SThomas Gleixner static u32 deftype_lo, deftype_hi; 7252ec1df41SThomas Gleixner 7261d3381ebSRandy Dunlap /** 7271d3381ebSRandy Dunlap * set_mtrr_state - Set the MTRR state for this CPU. 7281d3381ebSRandy Dunlap * 72901c97c73SJuergen Gross * NOTE: The CPU must already be in a safe state for MTRR changes, including 73001c97c73SJuergen Gross * measures that only a single CPU can be active in set_mtrr_state() in 73101c97c73SJuergen Gross * order to not be subject to races for usage of deftype_lo. This is 732d5f66d5dSJuergen Gross * accomplished by taking cache_disable_lock. 7331d3381ebSRandy Dunlap * RETURNS: 0 if no changes made, else a mask indicating what was changed. 7342ec1df41SThomas Gleixner */ 7351d3381ebSRandy Dunlap static unsigned long set_mtrr_state(void) 7362ec1df41SThomas Gleixner { 7372ec1df41SThomas Gleixner unsigned long change_mask = 0; 738a1a499a3SJaswinder Singh Rajput unsigned int i; 7392ec1df41SThomas Gleixner 740a1a499a3SJaswinder Singh Rajput for (i = 0; i < num_var_ranges; i++) { 7412ec1df41SThomas Gleixner if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i])) 7422ec1df41SThomas Gleixner change_mask |= MTRR_CHANGE_MASK_VARIABLE; 743a1a499a3SJaswinder Singh Rajput } 7442ec1df41SThomas Gleixner 7452ec1df41SThomas Gleixner if (mtrr_state.have_fixed && set_fixed_ranges(mtrr_state.fixed_ranges)) 7462ec1df41SThomas Gleixner change_mask |= MTRR_CHANGE_MASK_FIXED; 7472ec1df41SThomas Gleixner 748a1a499a3SJaswinder Singh Rajput /* 749a1a499a3SJaswinder Singh Rajput * Set_mtrr_restore restores the old value of MTRRdefType, 750a1a499a3SJaswinder Singh Rajput * so to set it we fiddle with the saved value: 751a1a499a3SJaswinder Singh Rajput */ 752d053b481SJuergen Gross if ((deftype_lo & MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type || 753d053b481SJuergen Gross ((deftype_lo & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT) != mtrr_state.enabled) { 754a1a499a3SJaswinder Singh Rajput 755d053b481SJuergen Gross deftype_lo = (deftype_lo & MTRR_DEF_TYPE_DISABLE) | 756d053b481SJuergen Gross mtrr_state.def_type | 757d053b481SJuergen Gross (mtrr_state.enabled << MTRR_STATE_SHIFT); 7582ec1df41SThomas Gleixner change_mask |= MTRR_CHANGE_MASK_DEFTYPE; 7592ec1df41SThomas Gleixner } 7602ec1df41SThomas Gleixner 7612ec1df41SThomas Gleixner return change_mask; 7622ec1df41SThomas Gleixner } 7632ec1df41SThomas Gleixner 7644ad7149eSJuergen Gross void mtrr_disable(void) 7654ad7149eSJuergen Gross { 7664ad7149eSJuergen Gross /* Save MTRR state */ 7674ad7149eSJuergen Gross rdmsr(MSR_MTRRdefType, deftype_lo, deftype_hi); 7684ad7149eSJuergen Gross 7694ad7149eSJuergen Gross /* Disable MTRRs, and set the default type to uncached */ 770d053b481SJuergen Gross mtrr_wrmsr(MSR_MTRRdefType, deftype_lo & MTRR_DEF_TYPE_DISABLE, deftype_hi); 7714ad7149eSJuergen Gross } 7724ad7149eSJuergen Gross 7734ad7149eSJuergen Gross void mtrr_enable(void) 7744ad7149eSJuergen Gross { 7754ad7149eSJuergen Gross /* Intel (P6) standard MTRRs */ 7764ad7149eSJuergen Gross mtrr_wrmsr(MSR_MTRRdefType, deftype_lo, deftype_hi); 7774ad7149eSJuergen Gross } 7784ad7149eSJuergen Gross 7797d71db53SJuergen Gross void mtrr_generic_set_state(void) 7802ec1df41SThomas Gleixner { 7812ec1df41SThomas Gleixner unsigned long mask, count; 7822ec1df41SThomas Gleixner 7832ec1df41SThomas Gleixner /* Actually set the state */ 7842ec1df41SThomas Gleixner mask = set_mtrr_state(); 7852ec1df41SThomas Gleixner 7862ec1df41SThomas Gleixner /* Use the atomic bitops to update the global mask */ 7870e96f31eSJordan Borgner for (count = 0; count < sizeof(mask) * 8; ++count) { 7882ec1df41SThomas Gleixner if (mask & 0x01) 7892ec1df41SThomas Gleixner set_bit(count, &smp_changes_mask); 7902ec1df41SThomas Gleixner mask >>= 1; 7912ec1df41SThomas Gleixner } 7922ec1df41SThomas Gleixner } 7932ec1df41SThomas Gleixner 794a1a499a3SJaswinder Singh Rajput /** 795a1a499a3SJaswinder Singh Rajput * generic_set_mtrr - set variable MTRR register on the local CPU. 796a1a499a3SJaswinder Singh Rajput * 797a1a499a3SJaswinder Singh Rajput * @reg: The register to set. 798a1a499a3SJaswinder Singh Rajput * @base: The base address of the region. 799a1a499a3SJaswinder Singh Rajput * @size: The size of the region. If this is 0 the region is disabled. 800a1a499a3SJaswinder Singh Rajput * @type: The type of the region. 801a1a499a3SJaswinder Singh Rajput * 802a1a499a3SJaswinder Singh Rajput * Returns nothing. 803a1a499a3SJaswinder Singh Rajput */ 8042ec1df41SThomas Gleixner static void generic_set_mtrr(unsigned int reg, unsigned long base, 8052ec1df41SThomas Gleixner unsigned long size, mtrr_type type) 8062ec1df41SThomas Gleixner { 8072ec1df41SThomas Gleixner unsigned long flags; 8082ec1df41SThomas Gleixner struct mtrr_var_range *vr; 8092ec1df41SThomas Gleixner 8102ec1df41SThomas Gleixner vr = &mtrr_state.var_ranges[reg]; 8112ec1df41SThomas Gleixner 8122ec1df41SThomas Gleixner local_irq_save(flags); 813d5f66d5dSJuergen Gross cache_disable(); 8142ec1df41SThomas Gleixner 8152ec1df41SThomas Gleixner if (size == 0) { 816a1a499a3SJaswinder Singh Rajput /* 817a1a499a3SJaswinder Singh Rajput * The invalid bit is kept in the mask, so we simply 818a1a499a3SJaswinder Singh Rajput * clear the relevant mask register to disable a range. 819a1a499a3SJaswinder Singh Rajput */ 8202ec1df41SThomas Gleixner mtrr_wrmsr(MTRRphysMask_MSR(reg), 0, 0); 8212ec1df41SThomas Gleixner memset(vr, 0, sizeof(struct mtrr_var_range)); 8222ec1df41SThomas Gleixner } else { 8232ec1df41SThomas Gleixner vr->base_lo = base << PAGE_SHIFT | type; 824d053b481SJuergen Gross vr->base_hi = (base >> (32 - PAGE_SHIFT)) & ~phys_hi_rsvd; 825d053b481SJuergen Gross vr->mask_lo = -size << PAGE_SHIFT | MTRR_PHYSMASK_V; 826d053b481SJuergen Gross vr->mask_hi = (-size >> (32 - PAGE_SHIFT)) & ~phys_hi_rsvd; 8272ec1df41SThomas Gleixner 8282ec1df41SThomas Gleixner mtrr_wrmsr(MTRRphysBase_MSR(reg), vr->base_lo, vr->base_hi); 8292ec1df41SThomas Gleixner mtrr_wrmsr(MTRRphysMask_MSR(reg), vr->mask_lo, vr->mask_hi); 8302ec1df41SThomas Gleixner } 8312ec1df41SThomas Gleixner 832d5f66d5dSJuergen Gross cache_enable(); 8332ec1df41SThomas Gleixner local_irq_restore(flags); 8342ec1df41SThomas Gleixner } 8352ec1df41SThomas Gleixner 836a1a499a3SJaswinder Singh Rajput int generic_validate_add_page(unsigned long base, unsigned long size, 837a1a499a3SJaswinder Singh Rajput unsigned int type) 8382ec1df41SThomas Gleixner { 8392ec1df41SThomas Gleixner unsigned long lbase, last; 8402ec1df41SThomas Gleixner 841a1a499a3SJaswinder Singh Rajput /* 842a1a499a3SJaswinder Singh Rajput * For Intel PPro stepping <= 7 843a1a499a3SJaswinder Singh Rajput * must be 4 MiB aligned and not touch 0x70000000 -> 0x7003FFFF 844a1a499a3SJaswinder Singh Rajput */ 84503409069SJuergen Gross if (mtrr_if == &generic_mtrr_ops && boot_cpu_data.x86 == 6 && 8462ec1df41SThomas Gleixner boot_cpu_data.x86_model == 1 && 847b399151cSJia Zhang boot_cpu_data.x86_stepping <= 7) { 8482ec1df41SThomas Gleixner if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) { 8491b74dde7SChen Yucong pr_warn("mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); 8502ec1df41SThomas Gleixner return -EINVAL; 8512ec1df41SThomas Gleixner } 8522ec1df41SThomas Gleixner if (!(base + size < 0x70000 || base > 0x7003F) && 8532ec1df41SThomas Gleixner (type == MTRR_TYPE_WRCOMB 8542ec1df41SThomas Gleixner || type == MTRR_TYPE_WRBACK)) { 8551b74dde7SChen Yucong pr_warn("mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); 8562ec1df41SThomas Gleixner return -EINVAL; 8572ec1df41SThomas Gleixner } 8582ec1df41SThomas Gleixner } 8592ec1df41SThomas Gleixner 860a1a499a3SJaswinder Singh Rajput /* 861a1a499a3SJaswinder Singh Rajput * Check upper bits of base and last are equal and lower bits are 0 862a1a499a3SJaswinder Singh Rajput * for base and 1 for last 863a1a499a3SJaswinder Singh Rajput */ 8642ec1df41SThomas Gleixner last = base + size - 1; 8652ec1df41SThomas Gleixner for (lbase = base; !(lbase & 1) && (last & 1); 866a1a499a3SJaswinder Singh Rajput lbase = lbase >> 1, last = last >> 1) 867a1a499a3SJaswinder Singh Rajput ; 8682ec1df41SThomas Gleixner if (lbase != last) { 8691b74dde7SChen Yucong pr_warn("mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", base, size); 8702ec1df41SThomas Gleixner return -EINVAL; 8712ec1df41SThomas Gleixner } 8722ec1df41SThomas Gleixner return 0; 8732ec1df41SThomas Gleixner } 8742ec1df41SThomas Gleixner 8752ec1df41SThomas Gleixner static int generic_have_wrcomb(void) 8762ec1df41SThomas Gleixner { 8772ec1df41SThomas Gleixner unsigned long config, dummy; 878d9bcc01dSJaswinder Singh Rajput rdmsr(MSR_MTRRcap, config, dummy); 879d053b481SJuergen Gross return config & MTRR_CAP_WC; 8802ec1df41SThomas Gleixner } 8812ec1df41SThomas Gleixner 8822ec1df41SThomas Gleixner int positive_have_wrcomb(void) 8832ec1df41SThomas Gleixner { 8842ec1df41SThomas Gleixner return 1; 8852ec1df41SThomas Gleixner } 8862ec1df41SThomas Gleixner 887a1a499a3SJaswinder Singh Rajput /* 888a1a499a3SJaswinder Singh Rajput * Generic structure... 8892ec1df41SThomas Gleixner */ 8903b9cfc0aSEmese Revfy const struct mtrr_ops generic_mtrr_ops = { 8912ec1df41SThomas Gleixner .get = generic_get_mtrr, 8922ec1df41SThomas Gleixner .get_free_region = generic_get_free_region, 8932ec1df41SThomas Gleixner .set = generic_set_mtrr, 8942ec1df41SThomas Gleixner .validate_add_page = generic_validate_add_page, 8952ec1df41SThomas Gleixner .have_wrcomb = generic_have_wrcomb, 8962ec1df41SThomas Gleixner }; 897