diff options
author | Borislav Petkov <bp@suse.de> | 2015-06-04 12:55:10 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-06-07 09:28:52 -0400 |
commit | 9cd25aac1f44f269de5ecea11f7d927f37f1d01c (patch) | |
tree | 00d865f9811556a50b0ece6d840d55ab08fb0ecf | |
parent | 9dac6290945142e6b87d9f027edfee676dcfbfda (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.h | 2 | ||||
-rw-r--r-- | arch/x86/mm/init.c | 6 | ||||
-rw-r--r-- | arch/x86/mm/pat.c | 81 | ||||
-rw-r--r-- | arch/x86/xen/enlighten.c | 5 |
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 | ||
7 | bool pat_enabled(void); | 7 | bool pat_enabled(void); |
8 | extern void pat_init(void); | 8 | extern void pat_init(void); |
9 | void pat_init_cache_modes(void); | 9 | void pat_init_cache_modes(u64); |
10 | 10 | ||
11 | extern int reserve_memtype(u64 start, u64 end, | 11 | extern 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 | */ |
41 | uint16_t __cachemode2pte_tbl[_PAGE_CACHE_MODE_NUM] = { | 41 | uint16_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 | ||
51 | uint8_t __pte2cachemode_tbl[8] = { | 51 | uint8_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 | ||
71 | static 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 | */ |
180 | void pat_init_cache_modes(void) | 178 | void 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 | ||
199 | static void pat_bsp_init(u64 pat) | 195 | static 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(); | 215 | done: |
216 | pat_init_cache_modes(pat); | ||
215 | } | 217 | } |
216 | 218 | ||
217 | static void pat_ap_init(u64 pat) | 219 | static 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 | ||