Commit dc43fc75 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_mtrr_for_v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 mtrr updates from Borislav Petkov:
 "A serious scrubbing of the MTRR code including adding a new map
  mechanism in order to look up the memory type of a region easily.

  Also address memory range lookup issues like returning an invalid
  memory type. Furthermore, this handles the decoupling of PAT from MTRR
  more naturally.

  All work by Juergen Gross"

* tag 'x86_mtrr_for_v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/xen: Set default memory type for PV guests to WB
  x86/mtrr: Unify debugging printing
  x86/mtrr: Remove unused code
  x86/mm: Only check uniform after calling mtrr_type_lookup()
  x86/mtrr: Don't let mtrr_type_lookup() return MTRR_TYPE_INVALID
  x86/mtrr: Use new cache_map in mtrr_type_lookup()
  x86/mtrr: Add mtrr=debug command line option
  x86/mtrr: Construct a memory map with cache modes
  x86/mtrr: Add get_effective_type() service function
  x86/mtrr: Allocate mtrr_value array dynamically
  x86/mtrr: Move 32-bit code from mtrr.c to legacy.c
  x86/mtrr: Have only one set_mtrr() variant
  x86/mtrr: Replace vendor tests in MTRR code
  x86/xen: Set MTRR state when running as Xen PV initial domain
  x86/hyperv: Set MTRR state when running as SEV-SNP Hyper-V guest
  x86/mtrr: Support setting MTRR state for software defined MTRRs
  x86/mtrr: Replace size_or_mask and size_and_mask with a much easier concept
  x86/mtrr: Remove physical address size calculation
parents 4baa098a 30d65d1b
......@@ -3433,6 +3433,10 @@
[HW] Make the MicroTouch USB driver use raw coordinates
('y', default) or cooked coordinates ('n')
mtrr=debug [X86]
Enable printing debug information related to MTRR
registers at boot time.
mtrr_chunk_size=nn[KMG] [X86]
used for mtrr cleanup. It is largest continuous chunk
that could hold holes aka. UC entries.
......
......@@ -17,6 +17,7 @@
#include <asm/mem_encrypt.h>
#include <asm/mshyperv.h>
#include <asm/hypervisor.h>
#include <asm/mtrr.h>
#ifdef CONFIG_AMD_MEM_ENCRYPT
......@@ -372,6 +373,9 @@ void __init hv_vtom_init(void)
x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
/* Set WB as the default cache mode. */
mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);
}
#endif /* CONFIG_AMD_MEM_ENCRYPT */
......
......@@ -23,14 +23,43 @@
#ifndef _ASM_X86_MTRR_H
#define _ASM_X86_MTRR_H
#include <linux/bits.h>
#include <uapi/asm/mtrr.h>
/* Defines for hardware MTRR registers. */
#define MTRR_CAP_VCNT GENMASK(7, 0)
#define MTRR_CAP_FIX BIT_MASK(8)
#define MTRR_CAP_WC BIT_MASK(10)
#define MTRR_DEF_TYPE_TYPE GENMASK(7, 0)
#define MTRR_DEF_TYPE_FE BIT_MASK(10)
#define MTRR_DEF_TYPE_E BIT_MASK(11)
#define MTRR_DEF_TYPE_ENABLE (MTRR_DEF_TYPE_FE | MTRR_DEF_TYPE_E)
#define MTRR_DEF_TYPE_DISABLE ~(MTRR_DEF_TYPE_TYPE | MTRR_DEF_TYPE_ENABLE)
#define MTRR_PHYSBASE_TYPE GENMASK(7, 0)
#define MTRR_PHYSBASE_RSVD GENMASK(11, 8)
#define MTRR_PHYSMASK_RSVD GENMASK(10, 0)
#define MTRR_PHYSMASK_V BIT_MASK(11)
struct mtrr_state_type {
struct mtrr_var_range var_ranges[MTRR_MAX_VAR_RANGES];
mtrr_type fixed_ranges[MTRR_NUM_FIXED_RANGES];
unsigned char enabled;
bool have_fixed;
mtrr_type def_type;
};
/*
* The following functions are for use by other drivers that cannot use
* arch_phys_wc_add and arch_phys_wc_del.
*/
# ifdef CONFIG_MTRR
void mtrr_bp_init(void);
void mtrr_overwrite_state(struct mtrr_var_range *var, unsigned int num_var,
mtrr_type def_type);
extern u8 mtrr_type_lookup(u64 addr, u64 end, u8 *uniform);
extern void mtrr_save_fixed_ranges(void *);
extern void mtrr_save_state(void);
......@@ -40,7 +69,6 @@ extern int mtrr_add_page(unsigned long base, unsigned long size,
unsigned int type, bool increment);
extern int mtrr_del(int reg, unsigned long base, unsigned long size);
extern int mtrr_del_page(int reg, unsigned long base, unsigned long size);
extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi);
extern void mtrr_bp_restore(void);
extern int mtrr_trim_uncached_memory(unsigned long end_pfn);
extern int amd_special_default_mtrr(void);
......@@ -48,12 +76,21 @@ void mtrr_disable(void);
void mtrr_enable(void);
void mtrr_generic_set_state(void);
# else
static inline void mtrr_overwrite_state(struct mtrr_var_range *var,
unsigned int num_var,
mtrr_type def_type)
{
}
static inline u8 mtrr_type_lookup(u64 addr, u64 end, u8 *uniform)
{
/*
* Return no-MTRRs:
* Return the default MTRR type, without any known other types in
* that range.
*/
return MTRR_TYPE_INVALID;
*uniform = 1;
return MTRR_TYPE_UNCACHABLE;
}
#define mtrr_save_fixed_ranges(arg) do {} while (0)
#define mtrr_save_state() do {} while (0)
......@@ -79,9 +116,6 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
{
return 0;
}
static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
{
}
#define mtrr_bp_init() do {} while (0)
#define mtrr_bp_restore() do {} while (0)
#define mtrr_disable() do {} while (0)
......@@ -121,7 +155,8 @@ struct mtrr_gentry32 {
#endif /* CONFIG_COMPAT */
/* Bit fields for enabled in struct mtrr_state_type */
#define MTRR_STATE_MTRR_FIXED_ENABLED 0x01
#define MTRR_STATE_MTRR_ENABLED 0x02
#define MTRR_STATE_SHIFT 10
#define MTRR_STATE_MTRR_FIXED_ENABLED (MTRR_DEF_TYPE_FE >> MTRR_STATE_SHIFT)
#define MTRR_STATE_MTRR_ENABLED (MTRR_DEF_TYPE_E >> MTRR_STATE_SHIFT)
#endif /* _ASM_X86_MTRR_H */
......@@ -81,14 +81,6 @@ typedef __u8 mtrr_type;
#define MTRR_NUM_FIXED_RANGES 88
#define MTRR_MAX_VAR_RANGES 256
struct mtrr_state_type {
struct mtrr_var_range var_ranges[MTRR_MAX_VAR_RANGES];
mtrr_type fixed_ranges[MTRR_NUM_FIXED_RANGES];
unsigned char enabled;
unsigned char have_fixed;
mtrr_type def_type;
};
#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg))
#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1)
......@@ -115,9 +107,9 @@ struct mtrr_state_type {
#define MTRR_NUM_TYPES 7
/*
* Invalid MTRR memory type. mtrr_type_lookup() returns this value when
* MTRRs are disabled. Note, this value is allocated from the reserved
* values (0x7-0xff) of the MTRR memory types.
* Invalid MTRR memory type. No longer used outside of MTRR code.
* Note, this value is allocated from the reserved values (0x7-0xff) of
* the MTRR memory types.
*/
#define MTRR_TYPE_INVALID 0xff
......
# SPDX-License-Identifier: GPL-2.0-only
obj-y := mtrr.o if.o generic.o cleanup.o
obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o
obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o legacy.o
......@@ -110,7 +110,7 @@ amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
}
const struct mtrr_ops amd_mtrr_ops = {
.vendor = X86_VENDOR_AMD,
.var_regs = 2,
.set = amd_set_mtrr,
.get = amd_get_mtrr,
.get_free_region = generic_get_free_region,
......
......@@ -45,15 +45,6 @@ centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg)
return -ENOSPC;
}
/*
* Report boot time MCR setups
*/
void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
{
centaur_mcr[mcr].low = lo;
centaur_mcr[mcr].high = hi;
}
static void
centaur_get_mcr(unsigned int reg, unsigned long *base,
unsigned long *size, mtrr_type * type)
......@@ -112,7 +103,7 @@ centaur_validate_add_page(unsigned long base, unsigned long size, unsigned int t
}
const struct mtrr_ops centaur_mtrr_ops = {
.vendor = X86_VENDOR_CENTAUR,
.var_regs = 8,
.set = centaur_set_mcr,
.get = centaur_get_mcr,
.get_free_region = centaur_get_free_region,
......
......@@ -55,9 +55,6 @@ static int __initdata nr_range;
static struct var_mtrr_range_state __initdata range_state[RANGE_NUM];
static int __initdata debug_print;
#define Dprintk(x...) do { if (debug_print) pr_debug(x); } while (0)
#define BIOS_BUG_MSG \
"WARNING: BIOS bug: VAR MTRR %d contains strange UC entry under 1M, check with your system vendor!\n"
......@@ -79,12 +76,11 @@ x86_get_mtrr_mem_range(struct range *range, int nr_range,
nr_range = add_range_with_merge(range, RANGE_NUM, nr_range,
base, base + size);
}
if (debug_print) {
pr_debug("After WB checking\n");
for (i = 0; i < nr_range; i++)
pr_debug("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
}
Dprintk("After WB checking\n");
for (i = 0; i < nr_range; i++)
Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
/* Take out UC ranges: */
for (i = 0; i < num_var_ranges; i++) {
......@@ -112,24 +108,22 @@ x86_get_mtrr_mem_range(struct range *range, int nr_range,
subtract_range(range, RANGE_NUM, extra_remove_base,
extra_remove_base + extra_remove_size);
if (debug_print) {
pr_debug("After UC checking\n");
for (i = 0; i < RANGE_NUM; i++) {
if (!range[i].end)
continue;
pr_debug("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
}
Dprintk("After UC checking\n");
for (i = 0; i < RANGE_NUM; i++) {
if (!range[i].end)
continue;
Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
}
/* sort the ranges */
nr_range = clean_sort_range(range, RANGE_NUM);
if (debug_print) {
pr_debug("After sorting\n");
for (i = 0; i < nr_range; i++)
pr_debug("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
}
Dprintk("After sorting\n");
for (i = 0; i < nr_range; i++)
Dprintk("MTRR MAP PFN: %016llx - %016llx\n",
range[i].start, range[i].end);
return nr_range;
}
......@@ -164,16 +158,9 @@ static int __init enable_mtrr_cleanup_setup(char *str)
}
early_param("enable_mtrr_cleanup", enable_mtrr_cleanup_setup);
static int __init mtrr_cleanup_debug_setup(char *str)
{
debug_print = 1;
return 0;
}
early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup);
static void __init
set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
unsigned char type, unsigned int address_bits)
unsigned char type)
{
u32 base_lo, base_hi, mask_lo, mask_hi;
u64 base, mask;
......@@ -183,7 +170,7 @@ set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
return;
}
mask = (1ULL << address_bits) - 1;
mask = (1ULL << boot_cpu_data.x86_phys_bits) - 1;
mask &= ~((((u64)sizek) << 10) - 1);
base = ((u64)basek) << 10;
......@@ -209,7 +196,7 @@ save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
range_state[reg].type = type;
}
static void __init set_var_mtrr_all(unsigned int address_bits)
static void __init set_var_mtrr_all(void)
{
unsigned long basek, sizek;
unsigned char type;
......@@ -220,7 +207,7 @@ static void __init set_var_mtrr_all(unsigned int address_bits)
sizek = range_state[reg].size_pfn << (PAGE_SHIFT - 10);
type = range_state[reg].type;
set_var_mtrr(reg, basek, sizek, type, address_bits);
set_var_mtrr(reg, basek, sizek, type);
}
}
......@@ -267,7 +254,7 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk,
align = max_align;
sizek = 1UL << align;
if (debug_print) {
if (mtrr_debug) {
char start_factor = 'K', size_factor = 'K';
unsigned long start_base, size_base;
......@@ -542,7 +529,7 @@ static void __init print_out_mtrr_range_state(void)
start_base = to_size_factor(start_base, &start_factor);
type = range_state[i].type;
pr_debug("reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
Dprintk("reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
i, start_base, start_factor,
size_base, size_factor,
(type == MTRR_TYPE_UNCACHABLE) ? "UC" :
......@@ -680,7 +667,7 @@ static int __init mtrr_search_optimal_index(void)
return index_good;
}
int __init mtrr_cleanup(unsigned address_bits)
int __init mtrr_cleanup(void)
{
unsigned long x_remove_base, x_remove_size;
unsigned long base, size, def, dummy;
......@@ -689,7 +676,10 @@ int __init mtrr_cleanup(unsigned address_bits)
int index_good;
int i;
if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
if (!mtrr_enabled())
return 0;
if (!cpu_feature_enabled(X86_FEATURE_MTRR) || enable_mtrr_cleanup < 1)
return 0;
rdmsr(MSR_MTRRdefType, def, dummy);
......@@ -711,7 +701,7 @@ int __init mtrr_cleanup(unsigned address_bits)
return 0;
/* Print original var MTRRs at first, for debugging: */
pr_debug("original variable MTRRs\n");
Dprintk("original variable MTRRs\n");
print_out_mtrr_range_state();
memset(range, 0, sizeof(range));
......@@ -742,8 +732,8 @@ int __init mtrr_cleanup(unsigned address_bits)
mtrr_print_out_one_result(i);
if (!result[i].bad) {
set_var_mtrr_all(address_bits);
pr_debug("New variable MTRRs\n");
set_var_mtrr_all();
Dprintk("New variable MTRRs\n");
print_out_mtrr_range_state();
return 1;
}
......@@ -763,7 +753,7 @@ int __init mtrr_cleanup(unsigned address_bits)
mtrr_calc_range_state(chunk_size, gran_size,
x_remove_base, x_remove_size, i);
if (debug_print) {
if (mtrr_debug) {
mtrr_print_out_one_result(i);
pr_info("\n");
}
......@@ -786,8 +776,8 @@ int __init mtrr_cleanup(unsigned address_bits)
gran_size = result[i].gran_sizek;
gran_size <<= 10;
x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
set_var_mtrr_all(address_bits);
pr_debug("New variable MTRRs\n");
set_var_mtrr_all();
Dprintk("New variable MTRRs\n");
print_out_mtrr_range_state();
return 1;
} else {
......@@ -802,7 +792,7 @@ int __init mtrr_cleanup(unsigned address_bits)
return 0;
}
#else
int __init mtrr_cleanup(unsigned address_bits)
int __init mtrr_cleanup(void)
{
return 0;
}
......@@ -882,15 +872,18 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
/* extra one for all 0 */
int num[MTRR_NUM_TYPES + 1];
if (!mtrr_enabled())
return 0;
/*
* Make sure we only trim uncachable memory on machines that
* support the Intel MTRR architecture:
*/
if (!is_cpu(INTEL) || disable_mtrr_trim)
if (!cpu_feature_enabled(X86_FEATURE_MTRR) || disable_mtrr_trim)
return 0;
rdmsr(MSR_MTRRdefType, def, dummy);
def &= 0xff;
def &= MTRR_DEF_TYPE_TYPE;
if (def != MTRR_TYPE_UNCACHABLE)
return 0;
......
......@@ -235,7 +235,7 @@ static void cyrix_set_arr(unsigned int reg, unsigned long base,
}
const struct mtrr_ops cyrix_mtrr_ops = {
.vendor = X86_VENDOR_CYRIX,
.var_regs = 8,
.set = cyrix_set_arr,
.get = cyrix_get_arr,
.get_free_region = cyrix_get_free_region,
......
......@@ -8,10 +8,12 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/cc_platform.h>
#include <asm/processor-flags.h>
#include <asm/cacheinfo.h>
#include <asm/cpufeature.h>
#include <asm/hypervisor.h>
#include <asm/mshyperv.h>
#include <asm/tlbflush.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
......@@ -31,6 +33,55 @@ static struct fixed_range_block fixed_range_blocks[] = {
{}
};
struct cache_map {
u64 start;
u64 end;
u64 flags;
u64 type:8;
u64 fixed:1;
};
bool mtrr_debug;
static int __init mtrr_param_setup(char *str)
{
int rc = 0;
if (!str)
return -EINVAL;
if (!strcmp(str, "debug"))
mtrr_debug = true;
else
rc = -EINVAL;
return rc;
}
early_param("mtrr", mtrr_param_setup);
/*
* CACHE_MAP_MAX is the maximum number of memory ranges in cache_map, where
* no 2 adjacent ranges have the same cache mode (those would be merged).
* The number is based on the worst case:
* - no two adjacent fixed MTRRs share the same cache mode
* - one variable MTRR is spanning a huge area with mode WB
* - 255 variable MTRRs with mode UC all overlap with the WB MTRR, creating 2
* additional ranges each (result like "ababababa...aba" with a = WB, b = UC),
* accounting for MTRR_MAX_VAR_RANGES * 2 - 1 range entries
* - a TOP_MEM2 area (even with overlapping an UC MTRR can't add 2 range entries
* to the possible maximum, as it always starts at 4GB, thus it can't be in
* the middle of that MTRR, unless that MTRR starts at 0, which would remove
* the initial "a" from the "abababa" pattern above)
* The map won't contain ranges with no matching MTRR (those fall back to the
* default cache mode).
*/
#define CACHE_MAP_MAX (MTRR_NUM_FIXED_RANGES + MTRR_MAX_VAR_RANGES * 2)
static struct cache_map init_cache_map[CACHE_MAP_MAX] __initdata;
static struct cache_map *cache_map __refdata = init_cache_map;
static unsigned int cache_map_size = CACHE_MAP_MAX;
static unsigned int cache_map_n;
static unsigned int cache_map_fixed;
static unsigned long smp_changes_mask;
static int mtrr_state_set;
u64 mtrr_tom2;
......@@ -38,6 +89,9 @@ u64 mtrr_tom2;
struct mtrr_state_type mtrr_state;
EXPORT_SYMBOL_GPL(mtrr_state);
/* Reserved bits in the high portion of the MTRRphysBaseN MSR. */
u32 phys_hi_rsvd;
/*
* BIOS is expected to clear MtrrFixDramModEn bit, see for example
* "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
......@@ -69,175 +123,370 @@ static u64 get_mtrr_size(u64 mask)
{
u64 size;
mask >>= PAGE_SHIFT;
mask |= size_or_mask;
mask |= (u64)phys_hi_rsvd << 32;
size = -mask;
size <<= PAGE_SHIFT;
return size;
}
static u8 get_var_mtrr_state(unsigned int reg, u64 *start, u64 *size)
{
struct mtrr_var_range *mtrr = mtrr_state.var_ranges + reg;
if (!(mtrr->mask_lo & MTRR_PHYSMASK_V))
return MTRR_TYPE_INVALID;
*start = (((u64)mtrr->base_hi) << 32) + (mtrr->base_lo & PAGE_MASK);
*size = get_mtrr_size((((u64)mtrr->mask_hi) << 32) +
(mtrr->mask_lo & PAGE_MASK));
return mtrr->base_lo & MTRR_PHYSBASE_TYPE;
}
static u8 get_effective_type(u8 type1, u8 type2)
{
if (type1 == MTRR_TYPE_UNCACHABLE || type2 == MTRR_TYPE_UNCACHABLE)
return MTRR_TYPE_UNCACHABLE;
if ((type1 == MTRR_TYPE_WRBACK && type2 == MTRR_TYPE_WRTHROUGH) ||
(type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK))
return MTRR_TYPE_WRTHROUGH;
if (type1 != type2)
return MTRR_TYPE_UNCACHABLE;
return type1;
}
static void rm_map_entry_at(int idx)
{
cache_map_n--;
if (cache_map_n > idx) {
memmove(cache_map + idx, cache_map + idx + 1,
sizeof(*cache_map) * (cache_map_n - idx));
}
}
/*
* Check and return the effective type for MTRR-MTRR type overlap.
* Returns 1 if the effective type is UNCACHEABLE, else returns 0
* Add an entry into cache_map at a specific index. Merges adjacent entries if
* appropriate. Return the number of merges for correcting the scan index
* (this is needed as merging will reduce the number of entries, which will
* result in skipping entries in future iterations if the scan index isn't
* corrected).
* Note that the corrected index can never go below -1 (resulting in being 0 in
* the next scan iteration), as "2" is returned only if the current index is
* larger than zero.
*/
static int check_type_overlap(u8 *prev, u8 *curr)
static int add_map_entry_at(u64 start, u64 end, u8 type, int idx)
{
if (*prev == MTRR_TYPE_UNCACHABLE || *curr == MTRR_TYPE_UNCACHABLE) {
*prev = MTRR_TYPE_UNCACHABLE;
*curr = MTRR_TYPE_UNCACHABLE;
return 1;
bool merge_prev = false, merge_next = false;
if (start >= end)
return 0;
if (idx > 0) {
struct cache_map *prev = cache_map + idx - 1;
if (!prev->fixed && start == prev->end && type == prev->type)
merge_prev = true;
}
if ((*prev == MTRR_TYPE_WRBACK && *curr == MTRR_TYPE_WRTHROUGH) ||
(*prev == MTRR_TYPE_WRTHROUGH && *curr == MTRR_TYPE_WRBACK)) {
*prev = MTRR_TYPE_WRTHROUGH;
*curr = MTRR_TYPE_WRTHROUGH;
if (idx < cache_map_n) {
struct cache_map *next = cache_map + idx;
if (!next->fixed && end == next->start && type == next->type)
merge_next = true;
}
if (*prev != *curr) {
*prev = MTRR_TYPE_UNCACHABLE;
*curr = MTRR_TYPE_UNCACHABLE;
if (merge_prev && merge_next) {
cache_map[idx - 1].end = cache_map[idx].end;
rm_map_entry_at(idx);
return 2;
}
if (merge_prev) {
cache_map[idx - 1].end = end;
return 1;
}
if (merge_next) {
cache_map[idx].start = start;
return 1;
}
/* Sanity check: the array should NEVER be too small! */
if (cache_map_n == cache_map_size) {
WARN(1, "MTRR cache mode memory map exhausted!\n");
cache_map_n = cache_map_fixed;
return 0;
}
if (cache_map_n > idx) {
memmove(cache_map + idx + 1, cache_map + idx,
sizeof(*cache_map) * (cache_map_n - idx));
}
cache_map[idx].start = start;
cache_map[idx].end = end;
cache_map[idx].type = type;
cache_map[idx].fixed = 0;
cache_map_n++;
return 0;
}
/**
* mtrr_type_lookup_fixed - look up memory type in MTRR fixed entries
*
* Return the MTRR fixed memory type of 'start'.
*
* MTRR fixed entries are divided into the following ways:
* 0x00000 - 0x7FFFF : This range is divided into eight 64KB sub-ranges
* 0x80000 - 0xBFFFF : This range is divided into sixteen 16KB sub-ranges
* 0xC0000 - 0xFFFFF : This range is divided into sixty-four 4KB sub-ranges
*
* Return Values:
* MTRR_TYPE_(type) - Matched memory type
* MTRR_TYPE_INVALID - Unmatched
/* Clear a part of an entry. Return 1 if start of entry is still valid. */
static int clr_map_range_at(u64 start, u64 end, int idx)
{
int ret = start != cache_map[idx].start;
u64 tmp;
if (start == cache_map[idx].start && end == cache_map[idx].end) {
rm_map_entry_at(idx);
} else if (start == cache_map[idx].start) {
cache_map[idx].start = end;
} else if (end == cache_map[idx].end) {
cache_map[idx].end = start;
} else {
tmp = cache_map[idx].end;
cache_map[idx].end = start;
add_map_entry_at(end, tmp, cache_map[idx].type, idx + 1);
}
return ret;
}
/*
* Add MTRR to the map. The current map is scanned and each part of the MTRR
* either overlapping with an existing entry or with a hole in the map is
* handled separately.
*/
static u8 mtrr_type_lookup_fixed(u64 start, u64 end)
static void add_map_entry(u64 start, u64 end, u8 type)
{
int idx;
u8 new_type, old_type;
u64 tmp;
int i;
if (start >= 0x100000)
return MTRR_TYPE_INVALID;
for (i = 0; i < cache_map_n && start < end; i++) {
if (start >= cache_map[i].end)
continue;
if (start < cache_map[i].start) {
/* Region start has no overlap. */
tmp = min(end, cache_map[i].start);
i -= add_map_entry_at(start, tmp, type, i);
start = tmp;
continue;
}
/* 0x0 - 0x7FFFF */
if (start < 0x80000) {
idx = 0;
idx += (start >> 16);
return mtrr_state.fixed_ranges[idx];
/* 0x80000 - 0xBFFFF */
} else if (start < 0xC0000) {
idx = 1 * 8;
idx += ((start - 0x80000) >> 14);
return mtrr_state.fixed_ranges[idx];
new_type = get_effective_type(type, cache_map[i].type);
old_type = cache_map[i].type;
if (cache_map[i].fixed || new_type == old_type) {
/* Cut off start of new entry. */
start = cache_map[i].end;
continue;
}
/* Handle only overlapping part of region. */
tmp = min(end, cache_map[i].end);
i += clr_map_range_at(start, tmp, i);
i -= add_map_entry_at(start, tmp, new_type, i);
start = tmp;
}
/* 0xC0000 - 0xFFFFF */
idx = 3 * 8;
idx += ((start - 0xC0000) >> 12);
return mtrr_state.fixed_ranges[idx];
/* Add rest of region after last map entry (rest might be empty). */
add_map_entry_at(start, end, type, i);
}
/**
* mtrr_type_lookup_variable - look up memory type in MTRR variable entries
*
* Return Value:
* MTRR_TYPE_(type) - Matched memory type or default memory type (unmatched)
*
* Output Arguments:
* repeat - Set to 1 when [start:end] spanned across MTRR range and type
* returned corresponds only to [start:*partial_end]. Caller has
* to lookup again for [*partial_end:end].
*
* uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the
* region is fully covered by a single MTRR entry or the default
* type.
/* Add variable MTRRs to cache map. */
static void map_add_var(void)
{
u64 start, size;
unsigned int i;
u8 type;
/*
* Add AMD TOP_MEM2 area. Can't be added in mtrr_build_map(), as it
* needs to be added again when rebuilding the map due to potentially
* having moved as a result of variable MTRRs for memory below 4GB.
*/
if (mtrr_tom2) {
add_map_entry(BIT_ULL(32), mtrr_tom2, MTRR_TYPE_WRBACK);
cache_map[cache_map_n - 1].fixed = 1;
}
for (i = 0; i < num_var_ranges; i++) {
type = get_var_mtrr_state(i, &start, &size);
if (type != MTRR_TYPE_INVALID)
add_map_entry(start, start + size, type);
}
}
/*
* Rebuild map by replacing variable entries. Needs to be called when MTRR
* registers are being changed after boot, as such changes could include
* removals of registers, which are complicated to handle without rebuild of
* the map.
*/
static u8 mtrr_type_lookup_variable(u64 start, u64 end, u64 *partial_end,
int *repeat, u8 *uniform)
void generic_rebuild_map(void)
{
int i;
u64 base, mask;
u8 prev_match, curr_match;
if (mtrr_if != &generic_mtrr_ops)
return;
*repeat = 0;
*uniform = 1;
cache_map_n = cache_map_fixed;
prev_match = MTRR_TYPE_INVALID;
for (i = 0; i < num_var_ranges; ++i) {
unsigned short start_state, end_state, inclusive;
map_add_var();
}
if (!(mtrr_state.var_ranges[i].mask_lo & (1 << 11)))
continue;
static unsigned int __init get_cache_map_size(void)
{
return cache_map_fixed + 2 * num_var_ranges + (mtrr_tom2 != 0);
}
base = (((u64)mtrr_state.var_ranges[i].base_hi) << 32) +
(mtrr_state.var_ranges[i].base_lo & PAGE_MASK);
mask = (((u64)mtrr_state.var_ranges[i].mask_hi) << 32) +
(mtrr_state.var_ranges[i].mask_lo & PAGE_MASK);
start_state = ((start & mask) == (base & mask));
end_state = ((end & mask) == (base & mask));
inclusive = ((start < base) && (end > base));
if ((start_state != end_state) || inclusive) {
/*
* We have start:end spanning across an MTRR.
* We split the region into either
*
* - start_state:1
* (start:mtrr_end)(mtrr_end:end)
* - end_state:1
* (start:mtrr_start)(mtrr_start:end)
* - inclusive:1
* (start:mtrr_start)(mtrr_start:mtrr_end)(mtrr_end:end)
*
* depending on kind of overlap.
*
* Return the type of the first region and a pointer
* to the start of next region so that caller will be
* advised to lookup again after having adjusted start
* and end.
*
* Note: This way we handle overlaps with multiple
* entries and the default type properly.
*/
if (start_state)
*partial_end = base + get_mtrr_size(mask);
else
*partial_end = base;
if (unlikely(*partial_end <= start)) {
WARN_ON(1);
*partial_end = start + PAGE_SIZE;
}
/* Build the cache_map containing the cache modes per memory range. */
void __init mtrr_build_map(void)
{
u64 start, end, size;
unsigned int i;
u8 type;
end = *partial_end - 1; /* end is inclusive */
*repeat = 1;
*uniform = 0;
/* Add fixed MTRRs, optimize for adjacent entries with same type. */
if (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED) {
/*
* Start with 64k size fixed entries, preset 1st one (hence the
* loop below is starting with index 1).
*/
start = 0;
end = size = 0x10000;
type = mtrr_state.fixed_ranges[0];
for (i = 1; i < MTRR_NUM_FIXED_RANGES; i++) {
/* 8 64k entries, then 16 16k ones, rest 4k. */
if (i == 8 || i == 24)
size >>= 2;
if (mtrr_state.fixed_ranges[i] != type) {
add_map_entry(start, end, type);
start = end;
type = mtrr_state.fixed_ranges[i];
}
end += size;
}
add_map_entry(start, end, type);
}
if ((start & mask) != (base & mask))
continue;
/* Mark fixed, they take precedence. */
for (i = 0; i < cache_map_n; i++)
cache_map[i].fixed = 1;
cache_map_fixed = cache_map_n;
curr_match = mtrr_state.var_ranges[i].base_lo & 0xff;
if (prev_match == MTRR_TYPE_INVALID) {
prev_match = curr_match;
continue;
map_add_var();
pr_info("MTRR map: %u entries (%u fixed + %u variable; max %u), built from %u variable MTRRs\n",
cache_map_n, cache_map_fixed, cache_map_n - cache_map_fixed,
get_cache_map_size(), num_var_ranges + (mtrr_tom2 != 0));
if (mtrr_debug) {
for (i = 0; i < cache_map_n; i++) {
pr_info("%3u: %016llx-%016llx %s\n", i,
cache_map[i].start, cache_map[i].end - 1,
mtrr_attrib_to_str(cache_map[i].type));
}
}
}
*uniform = 0;
if (check_type_overlap(&prev_match, &curr_match))
return curr_match;
/* Copy the cache_map from __initdata memory to dynamically allocated one. */
void __init mtrr_copy_map(void)
{
unsigned int new_size = get_cache_map_size();
if (!mtrr_state.enabled || !new_size) {
cache_map = NULL;
return;
}
mutex_lock(&mtrr_mutex);
cache_map = kcalloc(new_size, sizeof(*cache_map), GFP_KERNEL);
if (cache_map) {
memmove(cache_map, init_cache_map,
cache_map_n * sizeof(*cache_map));
cache_map_size = new_size;
} else {
mtrr_state.enabled = 0;
pr_err("MTRRs disabled due to allocation failure for lookup map.\n");
}
mutex_unlock(&mtrr_mutex);
}
/**
* mtrr_overwrite_state - set static MTRR state
*
* Used to set MTRR state via different means (e.g. with data obtained from
* a hypervisor).
* Is allowed only for special cases when running virtualized. Must be called
* from the x86_init.hyper.init_platform() hook. It can be called only once.
* The MTRR state can't be changed afterwards. To ensure that, X86_FEATURE_MTRR
* is cleared.
*/
void mtrr_overwrite_state(struct mtrr_var_range *var, unsigned int num_var,
mtrr_type def_type)
{
unsigned int i;
/* Only allowed to be called once before mtrr_bp_init(). */
if (WARN_ON_ONCE(mtrr_state_set))
return;
/* Only allowed when running virtualized. */
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
return;
/*
* Only allowed for special virtualization cases:
* - when running as Hyper-V, SEV-SNP guest using vTOM
* - when running as Xen PV guest
* - when running as SEV-SNP or TDX guest to avoid unnecessary
* VMM communication/Virtualization exceptions (#VC, #VE)
*/
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) &&
!hv_is_isolation_supported() &&
!cpu_feature_enabled(X86_FEATURE_XENPV) &&
!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return;
/* Disable MTRR in order to disable MTRR modifications. */
setup_clear_cpu_cap(X86_FEATURE_MTRR);
if (var) {
if (num_var > MTRR_MAX_VAR_RANGES) {
pr_warn("Trying to overwrite MTRR state with %u variable entries\n",
num_var);
num_var = MTRR_MAX_VAR_RANGES;
}
for (i = 0; i < num_var; i++)
mtrr_state.var_ranges[i] = var[i];
num_var_ranges = num_var;
}
if (prev_match != MTRR_TYPE_INVALID)
return prev_match;
mtrr_state.def_type = def_type;
mtrr_state.enabled |= MTRR_STATE_MTRR_ENABLED;
return mtrr_state.def_type;
mtrr_state_set = 1;
}
static u8 type_merge(u8 type, u8 new_type, u8 *uniform)
{
u8 effective_type;
if (type == MTRR_TYPE_INVALID)
return new_type;
effective_type = get_effective_type(type, new_type);
if (type != effective_type)
*uniform = 0;
return effective_type;
}
/**
......@@ -248,66 +497,49 @@ static u8 mtrr_type_lookup_variable(u64 start, u64 end, u64 *partial_end,
* MTRR_TYPE_INVALID - MTRR is disabled
*
* Output Argument:
* uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the
* region is fully covered by a single MTRR entry or the default
* type.
* uniform - Set to 1 when the returned MTRR type is valid for the whole
* region, set to 0 else.
*/
u8 mtrr_type_lookup(u64 start, u64 end, u8 *uniform)
{
u8 type, prev_type, is_uniform = 1, dummy;
int repeat;
u64 partial_end;
u8 type = MTRR_TYPE_INVALID;
unsigned int i;
/* Make end inclusive instead of exclusive */
end--;
if (!mtrr_state_set) {
/* Uniformity is unknown. */
*uniform = 0;
return MTRR_TYPE_UNCACHABLE;
}
if (!mtrr_state_set)
return MTRR_TYPE_INVALID;
*uniform = 1;
if (!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED))
return MTRR_TYPE_INVALID;
return MTRR_TYPE_UNCACHABLE;
/*
* Look up the fixed ranges first, which take priority over
* the variable ranges.
*/
if ((start < 0x100000) &&
(mtrr_state.have_fixed) &&
(mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) {
is_uniform = 0;
type = mtrr_type_lookup_fixed(start, end);
goto out;
}
for (i = 0; i < cache_map_n && start < end; i++) {
/* Region after current map entry? -> continue with next one. */
if (start >= cache_map[i].end)
continue;
/*
* Look up the variable ranges. Look of multiple ranges matching
* this address and pick type as per MTRR precedence.
*/
type = mtrr_type_lookup_variable(start, end, &partial_end,
&repeat, &is_uniform);
/* Start of region not covered by current map entry? */
if (start < cache_map[i].start) {
/* At least some part of region has default type. */
type = type_merge(type, mtrr_state.def_type, uniform);
/* End of region not covered, too? -> lookup done. */
if (end <= cache_map[i].start)
return type;
}
/*
* Common path is with repeat = 0.
* However, we can have cases where [start:end] spans across some
* MTRR ranges and/or the default type. Do repeated lookups for
* that case here.
*/
while (repeat) {
prev_type = type;
start = partial_end;
is_uniform = 0;
type = mtrr_type_lookup_variable(start, end, &partial_end,
&repeat, &dummy);
/* At least part of region covered by map entry. */
type = type_merge(type, cache_map[i].type, uniform);
if (check_type_overlap(&prev_type, &type))
goto out;
start = cache_map[i].end;
}
if (mtrr_tom2 && (start >= (1ULL<<32)) && (end < mtrr_tom2))
type = MTRR_TYPE_WRBACK;
/* End of region past last entry in map? -> use default type. */
if (start < end)
type = type_merge(type, mtrr_state.def_type, uniform);
out:
*uniform = is_uniform;
return type;
}
......@@ -363,8 +595,8 @@ static void __init print_fixed_last(void)
if (!last_fixed_end)
return;
pr_debug(" %05X-%05X %s\n", last_fixed_start,
last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type));
pr_info(" %05X-%05X %s\n", last_fixed_start,
last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type));
last_fixed_end = 0;
}
......@@ -402,10 +634,10 @@ static void __init print_mtrr_state(void)
unsigned int i;
int high_width;
pr_debug("MTRR default type: %s\n",
mtrr_attrib_to_str(mtrr_state.def_type));
pr_info("MTRR default type: %s\n",
mtrr_attrib_to_str(mtrr_state.def_type));
if (mtrr_state.have_fixed) {
pr_debug("MTRR fixed ranges %sabled:\n",
pr_info("MTRR fixed ranges %sabled:\n",
((mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED) &&
(mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) ?
"en" : "dis");
......@@ -420,26 +652,27 @@ static void __init print_mtrr_state(void)
/* tail */
print_fixed_last();
}
pr_debug("MTRR variable ranges %sabled:\n",
mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED ? "en" : "dis");
high_width = (__ffs64(size_or_mask) - (32 - PAGE_SHIFT) + 3) / 4;
pr_info("MTRR variable ranges %sabled:\n",
mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED ? "en" : "dis");
high_width = (boot_cpu_data.x86_phys_bits - (32 - PAGE_SHIFT) + 3) / 4;
for (i = 0; i < num_var_ranges; ++i) {
if (mtrr_state.var_ranges[i].mask_lo & (1 << 11))
pr_debug(" %u base %0*X%05X000 mask %0*X%05X000 %s\n",
i,
high_width,
mtrr_state.var_ranges[i].base_hi,
mtrr_state.var_ranges[i].base_lo >> 12,
high_width,
mtrr_state.var_ranges[i].mask_hi,
mtrr_state.var_ranges[i].mask_lo >> 12,
mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 0xff));
if (mtrr_state.var_ranges[i].mask_lo & MTRR_PHYSMASK_V)
pr_info(" %u base %0*X%05X000 mask %0*X%05X000 %s\n",
i,
high_width,
mtrr_state.var_ranges[i].base_hi,
mtrr_state.var_ranges[i].base_lo >> 12,
high_width,
mtrr_state.var_ranges[i].mask_hi,
mtrr_state.var_ranges[i].mask_lo >> 12,
mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo &
MTRR_PHYSBASE_TYPE));
else
pr_debug(" %u disabled\n", i);
pr_info(" %u disabled\n", i);
}
if (mtrr_tom2)
pr_debug("TOM2: %016llx aka %lldM\n", mtrr_tom2, mtrr_tom2>>20);
pr_info("TOM2: %016llx aka %lldM\n", mtrr_tom2, mtrr_tom2>>20);
}
/* Grab all of the MTRR state for this CPU into *state */
......@@ -452,7 +685,7 @@ bool __init get_mtrr_state(void)
vrs = mtrr_state.var_ranges;
rdmsr(MSR_MTRRcap, lo, dummy);
mtrr_state.have_fixed = (lo >> 8) & 1;
mtrr_state.have_fixed = lo & MTRR_CAP_FIX;
for (i = 0; i < num_var_ranges; i++)
get_mtrr_var_range(i, &vrs[i]);
......@@ -460,8 +693,8 @@ bool __init get_mtrr_state(void)
get_fixed_ranges(mtrr_state.fixed_ranges);
rdmsr(MSR_MTRRdefType, lo, dummy);
mtrr_state.def_type = (lo & 0xff);
mtrr_state.enabled = (lo & 0xc00) >> 10;
mtrr_state.def_type = lo & MTRR_DEF_TYPE_TYPE;
mtrr_state.enabled = (lo & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT;
if (amd_special_default_mtrr()) {
unsigned low, high;
......@@ -474,7 +707,8 @@ bool __init get_mtrr_state(void)
mtrr_tom2 &= 0xffffff800000ULL;
}
print_mtrr_state();
if (mtrr_debug)
print_mtrr_state();
mtrr_state_set = 1;
......@@ -574,7 +808,7 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi);
if ((mask_lo & 0x800) == 0) {
if (!(mask_lo & MTRR_PHYSMASK_V)) {
/* Invalid (i.e. free) range */
*base = 0;
*size = 0;
......@@ -585,8 +819,8 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi);
/* Work out the shifted address mask: */
tmp = (u64)mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT;
mask = size_or_mask | tmp;
tmp = (u64)mask_hi << 32 | (mask_lo & PAGE_MASK);
mask = (u64)phys_hi_rsvd << 32 | tmp;
/* Expand tmp with high bits to all 1s: */
hi = fls64(tmp);
......@@ -604,9 +838,9 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
* This works correctly if size is a power of two, i.e. a
* contiguous range:
*/
*size = -mask;
*size = -mask >> PAGE_SHIFT;
*base = (u64)base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
*type = base_lo & 0xff;
*type = base_lo & MTRR_PHYSBASE_TYPE;
out_put_cpu:
put_cpu();
......@@ -644,9 +878,8 @@ static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
bool changed = false;
rdmsr(MTRRphysBase_MSR(index), lo, hi);
if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
|| (vr->base_hi & (size_and_mask >> (32 - PAGE_SHIFT))) !=
(hi & (size_and_mask >> (32 - PAGE_SHIFT)))) {
if ((vr->base_lo & ~MTRR_PHYSBASE_RSVD) != (lo & ~MTRR_PHYSBASE_RSVD)
|| (vr->base_hi & ~phys_hi_rsvd) != (hi & ~phys_hi_rsvd)) {
mtrr_wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
changed = true;
......@@ -654,9 +887,8 @@ static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
rdmsr(MTRRphysMask_MSR(index), lo, hi);
if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
|| (vr->mask_hi & (size_and_mask >> (32 - PAGE_SHIFT))) !=
(hi & (size_and_mask >> (32 - PAGE_SHIFT)))) {
if ((vr->mask_lo & ~MTRR_PHYSMASK_RSVD) != (lo & ~MTRR_PHYSMASK_RSVD)
|| (vr->mask_hi & ~phys_hi_rsvd) != (hi & ~phys_hi_rsvd)) {
mtrr_wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
changed = true;
}
......@@ -691,11 +923,12 @@ static unsigned long set_mtrr_state(void)
* Set_mtrr_restore restores the old value of MTRRdefType,
* so to set it we fiddle with the saved value:
*/
if ((deftype_lo & 0xff) != mtrr_state.def_type
|| ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) {
if ((deftype_lo & MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||
((deftype_lo & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT) != mtrr_state.enabled) {
deftype_lo = (deftype_lo & ~0xcff) | mtrr_state.def_type |
(mtrr_state.enabled << 10);
deftype_lo = (deftype_lo & MTRR_DEF_TYPE_DISABLE) |
mtrr_state.def_type |
(mtrr_state.enabled << MTRR_STATE_SHIFT);
change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
}
......@@ -708,7 +941,7 @@ void mtrr_disable(void)
rdmsr(MSR_MTRRdefType, deftype_lo, deftype_hi);
/* Disable MTRRs, and set the default type to uncached */
mtrr_wrmsr(MSR_MTRRdefType, deftype_lo & ~0xcff, deftype_hi);
mtrr_wrmsr(MSR_MTRRdefType, deftype_lo & MTRR_DEF_TYPE_DISABLE, deftype_hi);
}
void mtrr_enable(void)
......@@ -762,9 +995,9 @@ static void generic_set_mtrr(unsigned int reg, unsigned long base,
memset(vr, 0, sizeof(struct mtrr_var_range));
} else {
vr->base_lo = base << PAGE_SHIFT | type;
vr->base_hi = (base & size_and_mask) >> (32 - PAGE_SHIFT);
vr->mask_lo = -size << PAGE_SHIFT | 0x800;
vr->mask_hi = (-size & size_and_mask) >> (32 - PAGE_SHIFT);
vr->base_hi = (base >> (32 - PAGE_SHIFT)) & ~phys_hi_rsvd;
vr->mask_lo = -size << PAGE_SHIFT | MTRR_PHYSMASK_V;
vr->mask_hi = (-size >> (32 - PAGE_SHIFT)) & ~phys_hi_rsvd;
mtrr_wrmsr(MTRRphysBase_MSR(reg), vr->base_lo, vr->base_hi);
mtrr_wrmsr(MTRRphysMask_MSR(reg), vr->mask_lo, vr->mask_hi);
......@@ -783,7 +1016,7 @@ int generic_validate_add_page(unsigned long base, unsigned long size,
* For Intel PPro stepping <= 7
* must be 4 MiB aligned and not touch 0x70000000 -> 0x7003FFFF
*/
if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 &&
if (mtrr_if == &generic_mtrr_ops && boot_cpu_data.x86 == 6 &&
boot_cpu_data.x86_model == 1 &&
boot_cpu_data.x86_stepping <= 7) {
if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) {
......@@ -817,7 +1050,7 @@ static int generic_have_wrcomb(void)
{
unsigned long config, dummy;
rdmsr(MSR_MTRRcap, config, dummy);
return config & (1 << 10);
return config & MTRR_CAP_WC;
}
int positive_have_wrcomb(void)
......
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/syscore_ops.h>
#include <asm/cpufeature.h>
#include <asm/mtrr.h>
#include <asm/processor.h>
#include "mtrr.h"
void mtrr_set_if(void)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
/* Pre-Athlon (K6) AMD CPU MTRRs */
if (cpu_feature_enabled(X86_FEATURE_K6_MTRR))
mtrr_if = &amd_mtrr_ops;
break;
case X86_VENDOR_CENTAUR:
if (cpu_feature_enabled(X86_FEATURE_CENTAUR_MCR))
mtrr_if = &centaur_mtrr_ops;
break;
case X86_VENDOR_CYRIX:
if (cpu_feature_enabled(X86_FEATURE_CYRIX_ARR))
mtrr_if = &cyrix_mtrr_ops;
break;
default:
break;
}
}
/*
* The suspend/resume methods are only for CPUs without MTRR. CPUs using generic
* MTRR driver don't require this.
*/
struct mtrr_value {
mtrr_type ltype;
unsigned long lbase;
unsigned long lsize;
};
static struct mtrr_value *mtrr_value;
static int mtrr_save(void)
{
int i;
if (!mtrr_value)
return -ENOMEM;
for (i = 0; i < num_var_ranges; i++) {
mtrr_if->get(i, &mtrr_value[i].lbase,
&mtrr_value[i].lsize,
&mtrr_value[i].ltype);
}
return 0;
}
static void mtrr_restore(void)
{
int i;
for (i = 0; i < num_var_ranges; i++) {
if (mtrr_value[i].lsize) {
mtrr_if->set(i, mtrr_value[i].lbase,
mtrr_value[i].lsize,
mtrr_value[i].ltype);
}
}
}
static struct syscore_ops mtrr_syscore_ops = {
.suspend = mtrr_save,
.resume = mtrr_restore,
};
void mtrr_register_syscore(void)
{
mtrr_value = kcalloc(num_var_ranges, sizeof(*mtrr_value), GFP_KERNEL);
/*
* The CPU has no MTRR and seems to not support SMP. They have
* specific drivers, we use a tricky method to support
* suspend/resume for them.
*
* TBD: is there any system with such CPU which supports
* suspend/resume? If no, we should remove the code.
*/
register_syscore_ops(&mtrr_syscore_ops);
}
......@@ -59,15 +59,9 @@
#define MTRR_TO_PHYS_WC_OFFSET 1000
u32 num_var_ranges;
static bool mtrr_enabled(void)
{
return !!mtrr_if;
}
unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
static DEFINE_MUTEX(mtrr_mutex);
u64 size_or_mask, size_and_mask;
DEFINE_MUTEX(mtrr_mutex);
const struct mtrr_ops *mtrr_if;
......@@ -105,21 +99,6 @@ static int have_wrcomb(void)
return mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0;
}
/* This function returns the number of variable MTRRs */
static void __init set_num_var_ranges(bool use_generic)
{
unsigned long config = 0, dummy;
if (use_generic)
rdmsr(MSR_MTRRcap, config, dummy);
else if (is_cpu(AMD) || is_cpu(HYGON))
config = 2;
else if (is_cpu(CYRIX) || is_cpu(CENTAUR))
config = 8;
num_var_ranges = config & 0xff;
}
static void __init init_table(void)
{
int i, max;
......@@ -194,20 +173,8 @@ static inline int types_compatible(mtrr_type type1, mtrr_type type2)
* Note that the mechanism is the same for UP systems, too; all the SMP stuff
* becomes nops.
*/
static void
set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)
{
struct set_mtrr_data data = { .smp_reg = reg,
.smp_base = base,
.smp_size = size,
.smp_type = type
};
stop_machine(mtrr_rendezvous_handler, &data, cpu_online_mask);
}
static void set_mtrr_cpuslocked(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type)
static void set_mtrr(unsigned int reg, unsigned long base, unsigned long size,
mtrr_type type)
{
struct set_mtrr_data data = { .smp_reg = reg,
.smp_base = base,
......@@ -216,6 +183,8 @@ static void set_mtrr_cpuslocked(unsigned int reg, unsigned long base,
};
stop_machine_cpuslocked(mtrr_rendezvous_handler, &data, cpu_online_mask);
generic_rebuild_map();
}
/**
......@@ -337,7 +306,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
/* Search for an empty MTRR */
i = mtrr_if->get_free_region(base, size, replace);
if (i >= 0) {
set_mtrr_cpuslocked(i, base, size, type);
set_mtrr(i, base, size, type);
if (likely(replace < 0)) {
mtrr_usage_table[i] = 1;
} else {
......@@ -345,7 +314,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
if (increment)
mtrr_usage_table[i]++;
if (unlikely(replace != i)) {
set_mtrr_cpuslocked(replace, 0, 0, 0);
set_mtrr(replace, 0, 0, 0);
mtrr_usage_table[replace] = 0;
}
}
......@@ -363,7 +332,7 @@ static int mtrr_check(unsigned long base, unsigned long size)
{
if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
pr_warn("size and base must be multiples of 4 kiB\n");
pr_debug("size: 0x%lx base: 0x%lx\n", size, base);
Dprintk("size: 0x%lx base: 0x%lx\n", size, base);
dump_stack();
return -1;
}
......@@ -454,8 +423,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
}
}
if (reg < 0) {
pr_debug("no MTRR for %lx000,%lx000 found\n",
base, size);
Dprintk("no MTRR for %lx000,%lx000 found\n", base, size);
goto out;
}
}
......@@ -473,7 +441,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
goto out;
}
if (--mtrr_usage_table[reg] < 1)
set_mtrr_cpuslocked(reg, 0, 0, 0);
set_mtrr(reg, 0, 0, 0);
error = reg;
out:
mutex_unlock(&mtrr_mutex);
......@@ -574,136 +542,54 @@ int arch_phys_wc_index(int handle)
}
EXPORT_SYMBOL_GPL(arch_phys_wc_index);
/* The suspend/resume methods are only for CPU without MTRR. CPU using generic
* MTRR driver doesn't require this
*/
struct mtrr_value {
mtrr_type ltype;
unsigned long lbase;
unsigned long lsize;
};
static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES];
static int mtrr_save(void)
{
int i;
for (i = 0; i < num_var_ranges; i++) {
mtrr_if->get(i, &mtrr_value[i].lbase,
&mtrr_value[i].lsize,
&mtrr_value[i].ltype);
}
return 0;
}
static void mtrr_restore(void)
{
int i;
for (i = 0; i < num_var_ranges; i++) {
if (mtrr_value[i].lsize) {
set_mtrr(i, mtrr_value[i].lbase,
mtrr_value[i].lsize,
mtrr_value[i].ltype);
}
}
}
static struct syscore_ops mtrr_syscore_ops = {
.suspend = mtrr_save,
.resume = mtrr_restore,
};
int __initdata changed_by_mtrr_cleanup;
#define SIZE_OR_MASK_BITS(n) (~((1ULL << ((n) - PAGE_SHIFT)) - 1))
/**
* mtrr_bp_init - initialize mtrrs on the boot CPU
* mtrr_bp_init - initialize MTRRs on the boot CPU
*
* This needs to be called early; before any of the other CPUs are
* initialized (i.e. before smp_init()).
*
*/
void __init mtrr_bp_init(void)
{
bool generic_mtrrs = cpu_feature_enabled(X86_FEATURE_MTRR);
const char *why = "(not available)";
u32 phys_addr;
phys_addr = 32;
unsigned long config, dummy;
if (boot_cpu_has(X86_FEATURE_MTRR)) {
mtrr_if = &generic_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(36);
size_and_mask = 0x00f00000;
phys_addr = 36;
phys_hi_rsvd = GENMASK(31, boot_cpu_data.x86_phys_bits - 32);
if (!generic_mtrrs && mtrr_state.enabled) {
/*
* This is an AMD specific MSR, but we assume(hope?) that
* Intel will implement it too when they extend the address
* bus of the Xeon.
* Software overwrite of MTRR state, only for generic case.
* Note that X86_FEATURE_MTRR has been reset in this case.
*/
if (cpuid_eax(0x80000000) >= 0x80000008) {
phys_addr = cpuid_eax(0x80000008) & 0xff;
/* CPUID workaround for Intel 0F33/0F34 CPU */
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
boot_cpu_data.x86 == 0xF &&
boot_cpu_data.x86_model == 0x3 &&
(boot_cpu_data.x86_stepping == 0x3 ||
boot_cpu_data.x86_stepping == 0x4))
phys_addr = 36;
size_or_mask = SIZE_OR_MASK_BITS(phys_addr);
size_and_mask = ~size_or_mask & 0xfffff00000ULL;
} else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
boot_cpu_data.x86 == 6) {
/*
* VIA C* family have Intel style MTRRs,
* but don't support PAE
*/
size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
phys_addr = 32;
}
} else {
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
if (cpu_feature_enabled(X86_FEATURE_K6_MTRR)) {
/* Pre-Athlon (K6) AMD CPU MTRRs */
mtrr_if = &amd_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
case X86_VENDOR_CENTAUR:
if (cpu_feature_enabled(X86_FEATURE_CENTAUR_MCR)) {
mtrr_if = &centaur_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
case X86_VENDOR_CYRIX:
if (cpu_feature_enabled(X86_FEATURE_CYRIX_ARR)) {
mtrr_if = &cyrix_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
default:
break;
}
init_table();
mtrr_build_map();
pr_info("MTRRs set to read-only\n");
return;
}
if (generic_mtrrs)
mtrr_if = &generic_mtrr_ops;
else
mtrr_set_if();
if (mtrr_enabled()) {
set_num_var_ranges(mtrr_if == &generic_mtrr_ops);
/* Get the number of variable MTRR ranges. */
if (mtrr_if == &generic_mtrr_ops)
rdmsr(MSR_MTRRcap, config, dummy);
else
config = mtrr_if->var_regs;
num_var_ranges = config & MTRR_CAP_VCNT;
init_table();
if (mtrr_if == &generic_mtrr_ops) {
/* BIOS may override */
if (get_mtrr_state()) {
memory_caching_control |= CACHE_MTRR;
changed_by_mtrr_cleanup = mtrr_cleanup(phys_addr);
changed_by_mtrr_cleanup = mtrr_cleanup();
mtrr_build_map();
} else {
mtrr_if = NULL;
why = "by BIOS";
......@@ -730,8 +616,14 @@ void mtrr_save_state(void)
smp_call_function_single(first_cpu, mtrr_save_fixed_ranges, NULL, 1);
}
static int __init mtrr_init_finialize(void)
static int __init mtrr_init_finalize(void)
{
/*
* Map might exist if mtrr_overwrite_state() has been called or if
* mtrr_enabled() returns true.
*/
mtrr_copy_map();
if (!mtrr_enabled())
return 0;
......@@ -741,16 +633,8 @@ static int __init mtrr_init_finialize(void)
return 0;
}
/*
* The CPU has no MTRR and seems to not support SMP. They have
* specific drivers, we use a tricky method to support
* suspend/resume for them.
*
* TBD: is there any system with such CPU which supports
* suspend/resume? If no, we should remove the code.
*/
register_syscore_ops(&mtrr_syscore_ops);
mtrr_register_syscore();
return 0;
}
subsys_initcall(mtrr_init_finialize);
subsys_initcall(mtrr_init_finalize);
......@@ -10,10 +10,13 @@
#define MTRR_CHANGE_MASK_VARIABLE 0x02
#define MTRR_CHANGE_MASK_DEFTYPE 0x04
extern bool mtrr_debug;
#define Dprintk(x...) do { if (mtrr_debug) pr_info(x); } while (0)
extern unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
struct mtrr_ops {
u32 vendor;
u32 var_regs;
void (*set)(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type);
void (*get)(unsigned int reg, unsigned long *base,
......@@ -51,18 +54,26 @@ void fill_mtrr_var_range(unsigned int index,
u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi);
bool get_mtrr_state(void);
extern u64 size_or_mask, size_and_mask;
extern const struct mtrr_ops *mtrr_if;
#define is_cpu(vnd) (mtrr_if && mtrr_if->vendor == X86_VENDOR_##vnd)
extern struct mutex mtrr_mutex;
extern unsigned int num_var_ranges;
extern u64 mtrr_tom2;
extern struct mtrr_state_type mtrr_state;
extern u32 phys_hi_rsvd;
void mtrr_state_warn(void);
const char *mtrr_attrib_to_str(int x);
void mtrr_wrmsr(unsigned, unsigned, unsigned);
#ifdef CONFIG_X86_32
void mtrr_set_if(void);
void mtrr_register_syscore(void);
#else
static inline void mtrr_set_if(void) { }
static inline void mtrr_register_syscore(void) { }
#endif
void mtrr_build_map(void);
void mtrr_copy_map(void);
/* CPU specific mtrr_ops vectors. */
extern const struct mtrr_ops amd_mtrr_ops;
......@@ -70,4 +81,14 @@ extern const struct mtrr_ops cyrix_mtrr_ops;
extern const struct mtrr_ops centaur_mtrr_ops;
extern int changed_by_mtrr_cleanup;
extern int mtrr_cleanup(unsigned address_bits);
extern int mtrr_cleanup(void);
/*
* Must be used by code which uses mtrr_if to call platform-specific
* MTRR manipulation functions.
*/
static inline bool mtrr_enabled(void)
{
return !!mtrr_if;
}
void generic_rebuild_map(void);
......@@ -1037,6 +1037,8 @@ void __init setup_arch(char **cmdline_p)
/*
* VMware detection requires dmi to be available, so this
* needs to be done after dmi_setup(), for the boot CPU.
* For some guest types (Xen PV, SEV-SNP, TDX) it is required to be
* called before cache_bp_init() for setting up MTRR state.
*/
init_hypervisor_platform();
......
......@@ -702,14 +702,8 @@ void p4d_clear_huge(p4d_t *p4d)
* pud_set_huge - setup kernel PUD mapping
*
* MTRRs can override PAT memory types with 4KiB granularity. Therefore, this
* function sets up a huge page only if any of the following conditions are met:
*
* - MTRRs are disabled, or
*
* - MTRRs are enabled and the range is completely covered by a single MTRR, or
*
* - MTRRs are enabled and the corresponding MTRR memory type is WB, which
* has no effect on the requested PAT memory type.
* function sets up a huge page only if the complete range has the same MTRR
* caching mode.
*
* Callers should try to decrease page size (1GB -> 2MB -> 4K) if the bigger
* page mapping attempt fails.
......@@ -718,11 +712,10 @@ void p4d_clear_huge(p4d_t *p4d)
*/
int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
{
u8 mtrr, uniform;
u8 uniform;
mtrr = mtrr_type_lookup(addr, addr + PUD_SIZE, &uniform);
if ((mtrr != MTRR_TYPE_INVALID) && (!uniform) &&
(mtrr != MTRR_TYPE_WRBACK))
mtrr_type_lookup(addr, addr + PUD_SIZE, &uniform);
if (!uniform)
return 0;
/* Bail out if we are we on a populated non-leaf entry: */
......@@ -745,11 +738,10 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
*/
int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot)
{
u8 mtrr, uniform;
u8 uniform;
mtrr = mtrr_type_lookup(addr, addr + PMD_SIZE, &uniform);
if ((mtrr != MTRR_TYPE_INVALID) && (!uniform) &&
(mtrr != MTRR_TYPE_WRBACK)) {
mtrr_type_lookup(addr, addr + PMD_SIZE, &uniform);
if (!uniform) {
pr_warn_once("%s: Cannot satisfy [mem %#010llx-%#010llx] with a huge-page mapping due to MTRR override.\n",
__func__, addr, addr + PMD_SIZE);
return 0;
......
......@@ -68,6 +68,7 @@
#include <asm/reboot.h>
#include <asm/hypervisor.h>
#include <asm/mach_traps.h>
#include <asm/mtrr.h>
#include <asm/mwait.h>
#include <asm/pci_x86.h>
#include <asm/cpu.h>
......@@ -119,6 +120,54 @@ static int __init parse_xen_msr_safe(char *str)
}
early_param("xen_msr_safe", parse_xen_msr_safe);
/* Get MTRR settings from Xen and put them into mtrr_state. */
static void __init xen_set_mtrr_data(void)
{
#ifdef CONFIG_MTRR
struct xen_platform_op op = {
.cmd = XENPF_read_memtype,
.interface_version = XENPF_INTERFACE_VERSION,
};
unsigned int reg;
unsigned long mask;
uint32_t eax, width;
static struct mtrr_var_range var[MTRR_MAX_VAR_RANGES] __initdata;
/* Get physical address width (only 64-bit cpus supported). */
width = 36;
eax = cpuid_eax(0x80000000);
if ((eax >> 16) == 0x8000 && eax >= 0x80000008) {
eax = cpuid_eax(0x80000008);
width = eax & 0xff;
}
for (reg = 0; reg < MTRR_MAX_VAR_RANGES; reg++) {
op.u.read_memtype.reg = reg;
if (HYPERVISOR_platform_op(&op))
break;
/*
* Only called in dom0, which has all RAM PFNs mapped at
* RAM MFNs, and all PCI space etc. is identity mapped.
* This means we can treat MFN == PFN regarding MTRR settings.
*/
var[reg].base_lo = op.u.read_memtype.type;
var[reg].base_lo |= op.u.read_memtype.mfn << PAGE_SHIFT;
var[reg].base_hi = op.u.read_memtype.mfn >> (32 - PAGE_SHIFT);
mask = ~((op.u.read_memtype.nr_mfns << PAGE_SHIFT) - 1);
mask &= (1UL << width) - 1;
if (mask)
mask |= MTRR_PHYSMASK_V;
var[reg].mask_lo = mask;
var[reg].mask_hi = mask >> 32;
}
/* Only overwrite MTRR state if any MTRR could be got from Xen. */
if (reg)
mtrr_overwrite_state(var, reg, MTRR_TYPE_UNCACHABLE);
#endif
}
static void __init xen_pv_init_platform(void)
{
/* PV guests can't operate virtio devices without grants. */
......@@ -135,6 +184,11 @@ static void __init xen_pv_init_platform(void)
/* pvclock is in shared info area */
xen_init_time_ops();
if (xen_initial_domain())
xen_set_mtrr_data();
else
mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);
}
static void __init xen_pv_guest_late_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment