x86: Enable PAT to use cache mode translation tables
authorJuergen Gross <jgross@suse.com>
Mon, 3 Nov 2014 13:02:03 +0000 (14:02 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 16 Nov 2014 10:04:26 +0000 (11:04 +0100)
Update the translation tables from cache mode to pgprot values
according to the PAT settings. This enables changing the cache
attributes of a PAT index in just one place without having to change
at the users side.

With this change it is possible to use the same kernel with different
PAT configurations, e.g. supporting Xen.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Toshi Kani <toshi.kani@hp.com>
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stefan.bader@canonical.com
Cc: xen-devel@lists.xensource.com
Cc: ville.syrjala@linux.intel.com
Cc: david.vrabel@citrix.com
Cc: jbeulich@suse.com
Cc: plagnioj@jcrosoft.com
Cc: tomi.valkeinen@ti.com
Cc: bhelgaas@google.com
Link: http://lkml.kernel.org/r/1415019724-4317-18-git-send-email-jgross@suse.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/include/asm/pat.h
arch/x86/include/asm/pgtable_types.h
arch/x86/mm/init.c
arch/x86/mm/mm_internal.h
arch/x86/mm/pat.c

index 150407a7234dad561e2cdc70ef5cd1ab34529731..91bc4ba95f919e90c3a398469f2dd52ca40e3cdd 100644 (file)
@@ -11,6 +11,7 @@ static const int pat_enabled;
 #endif
 
 extern void pat_init(void);
+void pat_init_cache_modes(void);
 
 extern int reserve_memtype(u64 start, u64 end,
                enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm);
index 6d5f6d15421588914d36dc5f746edb8c1d36bf08..af447f95e3bed803a843c6990dee02fc6cc76821 100644 (file)
@@ -351,6 +351,10 @@ extern uint8_t __pte2cachemode_tbl[8];
        ((((cb) >> (_PAGE_BIT_PAT - 2)) & 4) |          \
         (((cb) >> (_PAGE_BIT_PCD - 1)) & 2) |          \
         (((cb) >> _PAGE_BIT_PWT) & 1))
+#define __cm_idx2pte(i)                                        \
+       ((((i) & 4) << (_PAGE_BIT_PAT - 2)) |           \
+        (((i) & 2) << (_PAGE_BIT_PCD - 1)) |           \
+        (((i) & 1) << _PAGE_BIT_PWT))
 
 static inline unsigned long cachemode2protval(enum page_cache_mode pcm)
 {
index a9776ba475d462f013836a35a754356912226bdc..82b41d56bb9893f2318048daa7c2b305bcc078ba 100644 (file)
@@ -716,3 +716,11 @@ void __init zone_sizes_init(void)
        free_area_init_nodes(max_zone_pfns);
 }
 
+void update_cache_mode_entry(unsigned entry, enum page_cache_mode cache)
+{
+       /* entry 0 MUST be WB (hardwired to speed up translations) */
+       BUG_ON(!entry && cache != _PAGE_CACHE_MODE_WB);
+
+       __cachemode2pte_tbl[cache] = __cm_idx2pte(entry);
+       __pte2cachemode_tbl[entry] = cache;
+}
index 6b563a11889148f4356a0197ee202a6437b4d598..62474ba66c8e03d3cfefe3e9e8e4d32e7cdce2a9 100644 (file)
@@ -16,4 +16,6 @@ void zone_sizes_init(void);
 
 extern int after_bootmem;
 
+void update_cache_mode_entry(unsigned entry, enum page_cache_mode cache);
+
 #endif /* __X86_MM_INTERNAL_H */
index ef75f3f89810231e32ae92e341e4d29e1feccce8..4c601276a5563e38bc8b7310cdb15684f6e61a2a 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/io.h>
 
 #include "pat_internal.h"
+#include "mm_internal.h"
 
 #ifdef CONFIG_X86_PAT
 int __read_mostly pat_enabled = 1;
@@ -75,6 +76,52 @@ enum {
        PAT_UC_MINUS = 7,       /* UC, but can be overriden by MTRR */
 };
 
+#define CM(c) (_PAGE_CACHE_MODE_ ## c)
+
+static enum page_cache_mode pat_get_cache_mode(unsigned pat_val, char *msg)
+{
+       enum page_cache_mode cache;
+       char *cache_mode;
+
+       switch (pat_val) {
+       case PAT_UC:       cache = CM(UC);       cache_mode = "UC  "; break;
+       case PAT_WC:       cache = CM(WC);       cache_mode = "WC  "; break;
+       case PAT_WT:       cache = CM(WT);       cache_mode = "WT  "; break;
+       case PAT_WP:       cache = CM(WP);       cache_mode = "WP  "; break;
+       case PAT_WB:       cache = CM(WB);       cache_mode = "WB  "; break;
+       case PAT_UC_MINUS: cache = CM(UC_MINUS); cache_mode = "UC- "; break;
+       default:           cache = CM(WB);       cache_mode = "WB  "; break;
+       }
+
+       memcpy(msg, cache_mode, 4);
+
+       return cache;
+}
+
+#undef CM
+
+/*
+ * Update the cache mode to pgprot translation tables according to PAT
+ * configuration.
+ * Using lower indices is preferred, so we start with highest index.
+ */
+void pat_init_cache_modes(void)
+{
+       int i;
+       enum page_cache_mode cache;
+       char pat_msg[33];
+       u64 pat;
+
+       rdmsrl(MSR_IA32_CR_PAT, pat);
+       pat_msg[32] = 0;
+       for (i = 7; i >= 0; i--) {
+               cache = pat_get_cache_mode((pat >> (i * 8)) & 7,
+                                          pat_msg + 4 * i);
+               update_cache_mode_entry(i, cache);
+       }
+       pr_info("PAT configuration [0-7]: %s\n", pat_msg);
+}
+
 #define PAT(x, y)      ((u64)PAT_ ## y << ((x)*8))
 
 void pat_init(void)
@@ -124,8 +171,7 @@ void pat_init(void)
        wrmsrl(MSR_IA32_CR_PAT, pat);
 
        if (boot_cpu)
-               printk(KERN_INFO "x86 PAT enabled: cpu %d, old 0x%Lx, new 0x%Lx\n",
-                      smp_processor_id(), boot_pat_state, pat);
+               pat_init_cache_modes();
 }
 
 #undef PAT