[PATCH 2/2] x86/mtrr: Refactor PAT initialization code

From: Toshi Kani
Date: Thu Mar 10 2016 - 22:53:26 EST


MTRR manages PAT initialization as it implements a rendezvous
handler that initializes PAT as part of MTRR initialization.

When CPU does not support MTRR, ex. qemu32 virtual CPU, MTRR
simply skips PAT init, which causes PAT left enabled without
initialization. Also, get_mtrr_state() calls pat_init() on
BSP even if MTRR is disabled by its MSR. This causes pat_init()
be called on BSP only.

The following changes are made to address these issues:
- Move BSP's PAT init code to mtrr_bp_pat_init(), and change
mtrr_bp_init() to call it if MTRR is enabled. This keeps
the init condition consistent with mtrr_ap_init() and
mtrr_aps_init() for APs.
- Change mtrr_bp_init() to call pat_disable() when MTRR is
disabled.
- Change mtrr_bp_init() stub function to call pat_disable()
when CONFIG_MTRR is unset.

The table below discribes how PAT is initialized in all possible
cases.

Legend
----------------------------
E Enabled in CPU feature and MSR
D Disabled in CPU feature or MSR
nopat "nopat" boot option specified
!PAT CONFIG_X86_PAT unset
!MTRR CONFIG_MTRR unset

MTRR PAT ACTION
====================================================================
E E MTRR calls pat_init() -> PAT enabled
E D MTRR calls pat_init() -> PAT disabled per cpu_has_pat
D E MTRR calls pat_disable() -> PAT disabled (*)
D D MTRR calls pat_disable() -> PAT disabled
E nopat nopat() calls pat_disable() -> PAT disabled
D nopat nopat() calls pat_disable() -> PAT disabled
E !PAT MTRR calls pat_init() -> PAT disabled per __pat_enabled
D !PAT MTRR calls pat_disable() -> PAT disabled
!MTRR !PAT mtrr_bp_init() stub calls pat_disable() -> PAT disabled

(*) Further enhancement may enable PAT if needed

Signed-off-by: Toshi Kani <toshi.kani@xxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxx>
Cc: Luis R. Rodriguez <mcgrof@xxxxxxxx>
Cc: Juergen Gross <jgross@xxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: H. Peter Anvin <hpa@xxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
arch/x86/include/asm/mtrr.h | 6 +++++-
arch/x86/kernel/cpu/mtrr/generic.c | 24 ++++++++++++++----------
arch/x86/kernel/cpu/mtrr/main.c | 13 ++++++++++++-
arch/x86/kernel/cpu/mtrr/mtrr.h | 1 +
4 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h
index b94f6f6..a965e74 100644
--- a/arch/x86/include/asm/mtrr.h
+++ b/arch/x86/include/asm/mtrr.h
@@ -24,6 +24,7 @@
#define _ASM_X86_MTRR_H

#include <uapi/asm/mtrr.h>
+#include <asm/pat.h>


/*
@@ -83,9 +84,12 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
{
}
+static inline void mtrr_bp_init(void)
+{
+ pat_disable("PAT disabled by MTRR");
+}

#define mtrr_ap_init() do {} while (0)
-#define mtrr_bp_init() do {} while (0)
#define set_mtrr_aps_delayed_init() do {} while (0)
#define mtrr_aps_init() do {} while (0)
#define mtrr_bp_restore() do {} while (0)
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index c870af1..136ae86 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -444,11 +444,24 @@ static void __init print_mtrr_state(void)
pr_debug("TOM2: %016llx aka %lldM\n", mtrr_tom2, mtrr_tom2>>20);
}

+/* PAT setup for BP. We need to go through sync steps here */
+void __init mtrr_bp_pat_init(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ prepare_set();
+
+ pat_init();
+
+ post_set();
+ local_irq_restore(flags);
+}
+
/* Grab all of the MTRR state for this CPU into *state */
bool __init get_mtrr_state(void)
{
struct mtrr_var_range *vrs;
- unsigned long flags;
unsigned lo, dummy;
unsigned int i;

@@ -481,15 +494,6 @@ bool __init get_mtrr_state(void)

mtrr_state_set = 1;

- /* PAT setup for BP. We need to go through sync steps here */
- local_irq_save(flags);
- prepare_set();
-
- pat_init();
-
- post_set();
- local_irq_restore(flags);
-
return !!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED);
}

diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c
index 5c3d149..d9e91f1 100644
--- a/arch/x86/kernel/cpu/mtrr/main.c
+++ b/arch/x86/kernel/cpu/mtrr/main.c
@@ -752,6 +752,9 @@ void __init mtrr_bp_init(void)
/* BIOS may override */
__mtrr_enabled = get_mtrr_state();

+ if (mtrr_enabled())
+ mtrr_bp_pat_init();
+
if (mtrr_cleanup(phys_addr)) {
changed_by_mtrr_cleanup = 1;
mtrr_if->set_all();
@@ -759,8 +762,16 @@ void __init mtrr_bp_init(void)
}
}

- if (!mtrr_enabled())
+ if (!mtrr_enabled()) {
pr_info("MTRR: Disabled\n");
+
+ /*
+ * PAT initialization relies on MTRR's rendezvous handler.
+ * Disable PAT until the handler can initialize both features
+ * independently.
+ */
+ pat_disable("PAT disabled by MTRR");
+ }
}

void mtrr_ap_init(void)
diff --git a/arch/x86/kernel/cpu/mtrr/mtrr.h b/arch/x86/kernel/cpu/mtrr/mtrr.h
index 951884d..6c7ced0 100644
--- a/arch/x86/kernel/cpu/mtrr/mtrr.h
+++ b/arch/x86/kernel/cpu/mtrr/mtrr.h
@@ -52,6 +52,7 @@ void set_mtrr_prepare_save(struct set_mtrr_context *ctxt);
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);
+void mtrr_bp_pat_init(void);

extern void set_mtrr_ops(const struct mtrr_ops *ops);