aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBorislav Petkov <bp@suse.de>2015-06-04 12:55:10 -0400
committerIngo Molnar <mingo@kernel.org>2015-06-07 09:28:52 -0400
commit9cd25aac1f44f269de5ecea11f7d927f37f1d01c (patch)
tree00d865f9811556a50b0ece6d840d55ab08fb0ecf
parent9dac6290945142e6b87d9f027edfee676dcfbfda (diff)
x86/mm/pat: Emulate PAT when it is disabled
In the case when PAT is disabled on the command line with "nopat" or when virtualization doesn't support PAT (correctly) - see 9d34cfdf4796 ("x86: Don't rely on VMWare emulating PAT MSR correctly"). we emulate it using the PWT and PCD cache attribute bits. Get rid of boot_pat_state while at it. Based on a conglomerate patch from Toshi Kani. Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Toshi Kani <toshi.kani@hp.com> Acked-by: Juergen Gross <jgross@suse.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Elliott@hp.com Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Luis R. Rodriguez <mcgrof@suse.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: arnd@arndb.de Cc: hch@lst.de Cc: hmh@hmh.eng.br Cc: konrad.wilk@oracle.com Cc: linux-mm <linux-mm@kvack.org> Cc: linux-nvdimm@lists.01.org Cc: stefan.bader@canonical.com Cc: yigal@plexistor.com Link: http://lkml.kernel.org/r/1433436928-31903-3-git-send-email-bp@alien8.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/pat.h2
-rw-r--r--arch/x86/mm/init.c6
-rw-r--r--arch/x86/mm/pat.c81
-rw-r--r--arch/x86/xen/enlighten.c5
4 files changed, 60 insertions, 34 deletions
diff --git a/arch/x86/include/asm/pat.h b/arch/x86/include/asm/pat.h
index cdcff7f7f694..ca6c228d5e62 100644
--- a/arch/x86/include/asm/pat.h
+++ b/arch/x86/include/asm/pat.h
@@ -6,7 +6,7 @@
6 6
7bool pat_enabled(void); 7bool pat_enabled(void);
8extern void pat_init(void); 8extern void pat_init(void);
9void pat_init_cache_modes(void); 9void pat_init_cache_modes(u64);
10 10
11extern int reserve_memtype(u64 start, u64 end, 11extern int reserve_memtype(u64 start, u64 end,
12 enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm); 12 enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm);
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 1d553186c434..8533b46e6bee 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -40,7 +40,7 @@
40 */ 40 */
41uint16_t __cachemode2pte_tbl[_PAGE_CACHE_MODE_NUM] = { 41uint16_t __cachemode2pte_tbl[_PAGE_CACHE_MODE_NUM] = {
42 [_PAGE_CACHE_MODE_WB ] = 0 | 0 , 42 [_PAGE_CACHE_MODE_WB ] = 0 | 0 ,
43 [_PAGE_CACHE_MODE_WC ] = _PAGE_PWT | 0 , 43 [_PAGE_CACHE_MODE_WC ] = 0 | _PAGE_PCD,
44 [_PAGE_CACHE_MODE_UC_MINUS] = 0 | _PAGE_PCD, 44 [_PAGE_CACHE_MODE_UC_MINUS] = 0 | _PAGE_PCD,
45 [_PAGE_CACHE_MODE_UC ] = _PAGE_PWT | _PAGE_PCD, 45 [_PAGE_CACHE_MODE_UC ] = _PAGE_PWT | _PAGE_PCD,
46 [_PAGE_CACHE_MODE_WT ] = 0 | _PAGE_PCD, 46 [_PAGE_CACHE_MODE_WT ] = 0 | _PAGE_PCD,
@@ -50,11 +50,11 @@ EXPORT_SYMBOL(__cachemode2pte_tbl);
50 50
51uint8_t __pte2cachemode_tbl[8] = { 51uint8_t __pte2cachemode_tbl[8] = {
52 [__pte2cm_idx( 0 | 0 | 0 )] = _PAGE_CACHE_MODE_WB, 52 [__pte2cm_idx( 0 | 0 | 0 )] = _PAGE_CACHE_MODE_WB,
53 [__pte2cm_idx(_PAGE_PWT | 0 | 0 )] = _PAGE_CACHE_MODE_WC, 53 [__pte2cm_idx(_PAGE_PWT | 0 | 0 )] = _PAGE_CACHE_MODE_UC_MINUS,
54 [__pte2cm_idx( 0 | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC_MINUS, 54 [__pte2cm_idx( 0 | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC_MINUS,
55 [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC, 55 [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC,
56 [__pte2cm_idx( 0 | 0 | _PAGE_PAT)] = _PAGE_CACHE_MODE_WB, 56 [__pte2cm_idx( 0 | 0 | _PAGE_PAT)] = _PAGE_CACHE_MODE_WB,
57 [__pte2cm_idx(_PAGE_PWT | 0 | _PAGE_PAT)] = _PAGE_CACHE_MODE_WC, 57 [__pte2cm_idx(_PAGE_PWT | 0 | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS,
58 [__pte2cm_idx(0 | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS, 58 [__pte2cm_idx(0 | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS,
59 [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC, 59 [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC,
60}; 60};
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index 476d0780560f..6dc7826e4797 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -68,8 +68,6 @@ static int __init pat_debug_setup(char *str)
68} 68}
69__setup("debugpat", pat_debug_setup); 69__setup("debugpat", pat_debug_setup);
70 70
71static u64 __read_mostly boot_pat_state;
72
73#ifdef CONFIG_X86_PAT 71#ifdef CONFIG_X86_PAT
74/* 72/*
75 * X86 PAT uses page flags WC and Uncached together to keep track of 73 * X86 PAT uses page flags WC and Uncached together to keep track of
@@ -177,14 +175,12 @@ static enum page_cache_mode pat_get_cache_mode(unsigned pat_val, char *msg)
177 * configuration. 175 * configuration.
178 * Using lower indices is preferred, so we start with highest index. 176 * Using lower indices is preferred, so we start with highest index.
179 */ 177 */
180void pat_init_cache_modes(void) 178void pat_init_cache_modes(u64 pat)
181{ 179{
182 int i;
183 enum page_cache_mode cache; 180 enum page_cache_mode cache;
184 char pat_msg[33]; 181 char pat_msg[33];
185 u64 pat; 182 int i;
186 183
187 rdmsrl(MSR_IA32_CR_PAT, pat);
188 pat_msg[32] = 0; 184 pat_msg[32] = 0;
189 for (i = 7; i >= 0; i--) { 185 for (i = 7; i >= 0; i--) {
190 cache = pat_get_cache_mode((pat >> (i * 8)) & 7, 186 cache = pat_get_cache_mode((pat >> (i * 8)) & 7,
@@ -198,24 +194,33 @@ void pat_init_cache_modes(void)
198 194
199static void pat_bsp_init(u64 pat) 195static void pat_bsp_init(u64 pat)
200{ 196{
197 u64 tmp_pat;
198
201 if (!cpu_has_pat) { 199 if (!cpu_has_pat) {
202 pat_disable("PAT not supported by CPU."); 200 pat_disable("PAT not supported by CPU.");
203 return; 201 return;
204 } 202 }
205 203
206 rdmsrl(MSR_IA32_CR_PAT, boot_pat_state); 204 if (!pat_enabled())
207 if (!boot_pat_state) { 205 goto done;
206
207 rdmsrl(MSR_IA32_CR_PAT, tmp_pat);
208 if (!tmp_pat) {
208 pat_disable("PAT MSR is 0, disabled."); 209 pat_disable("PAT MSR is 0, disabled.");
209 return; 210 return;
210 } 211 }
211 212
212 wrmsrl(MSR_IA32_CR_PAT, pat); 213 wrmsrl(MSR_IA32_CR_PAT, pat);
213 214
214 pat_init_cache_modes(); 215done:
216 pat_init_cache_modes(pat);
215} 217}
216 218
217static void pat_ap_init(u64 pat) 219static void pat_ap_init(u64 pat)
218{ 220{
221 if (!pat_enabled())
222 return;
223
219 if (!cpu_has_pat) { 224 if (!cpu_has_pat) {
220 /* 225 /*
221 * If this happens we are on a secondary CPU, but switched to 226 * If this happens we are on a secondary CPU, but switched to
@@ -231,25 +236,45 @@ void pat_init(void)
231{ 236{
232 u64 pat; 237 u64 pat;
233 238
234 if (!pat_enabled()) 239 if (!pat_enabled()) {
235 return; 240 /*
236 241 * No PAT. Emulate the PAT table that corresponds to the two
237 /* 242 * cache bits, PWT (Write Through) and PCD (Cache Disable). This
238 * Set PWT to Write-Combining. All other bits stay the same: 243 * setup is the same as the BIOS default setup when the system
239 * 244 * has PAT but the "nopat" boot option has been specified. This
240 * PTE encoding used in Linux: 245 * emulated PAT table is used when MSR_IA32_CR_PAT returns 0.
241 * PAT 246 *
242 * |PCD 247 * PTE encoding used:
243 * ||PWT 248 *
244 * ||| 249 * PCD
245 * 000 WB _PAGE_CACHE_WB 250 * |PWT PAT
246 * 001 WC _PAGE_CACHE_WC 251 * || slot
247 * 010 UC- _PAGE_CACHE_UC_MINUS 252 * 00 0 WB : _PAGE_CACHE_MODE_WB
248 * 011 UC _PAGE_CACHE_UC 253 * 01 1 WT : _PAGE_CACHE_MODE_WT
249 * PAT bit unused 254 * 10 2 UC-: _PAGE_CACHE_MODE_UC_MINUS
250 */ 255 * 11 3 UC : _PAGE_CACHE_MODE_UC
251 pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) | 256 *
252 PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC); 257 * NOTE: When WC or WP is used, it is redirected to UC- per
258 * the default setup in __cachemode2pte_tbl[].
259 */
260 pat = PAT(0, WB) | PAT(1, WT) | PAT(2, UC_MINUS) | PAT(3, UC) |
261 PAT(4, WB) | PAT(5, WT) | PAT(6, UC_MINUS) | PAT(7, UC);
262 } else {
263 /*
264 * PTE encoding used in Linux:
265 * PAT
266 * |PCD
267 * ||PWT
268 * |||
269 * 000 WB _PAGE_CACHE_WB
270 * 001 WC _PAGE_CACHE_WC
271 * 010 UC- _PAGE_CACHE_UC_MINUS
272 * 011 UC _PAGE_CACHE_UC
273 * PAT bit unused
274 */
275 pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
276 PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC);
277 }
253 278
254 if (!boot_cpu_done) { 279 if (!boot_cpu_done) {
255 pat_bsp_init(pat); 280 pat_bsp_init(pat);
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 46957ead3060..53233a9beea9 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -1467,6 +1467,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
1467{ 1467{
1468 struct physdev_set_iopl set_iopl; 1468 struct physdev_set_iopl set_iopl;
1469 unsigned long initrd_start = 0; 1469 unsigned long initrd_start = 0;
1470 u64 pat;
1470 int rc; 1471 int rc;
1471 1472
1472 if (!xen_start_info) 1473 if (!xen_start_info)
@@ -1574,8 +1575,8 @@ asmlinkage __visible void __init xen_start_kernel(void)
1574 * Modify the cache mode translation tables to match Xen's PAT 1575 * Modify the cache mode translation tables to match Xen's PAT
1575 * configuration. 1576 * configuration.
1576 */ 1577 */
1577 1578 rdmsrl(MSR_IA32_CR_PAT, pat);
1578 pat_init_cache_modes(); 1579 pat_init_cache_modes(pat);
1579 1580
1580 /* keep using Xen gdt for now; no urgent need to change it */ 1581 /* keep using Xen gdt for now; no urgent need to change it */
1581 1582