Skip to content

Commit 9cd25aa

Browse files
suryasaimadhuIngo Molnar
authored andcommitted
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 9d34cfd ("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 <[email protected]> Reviewed-by: Toshi Kani <[email protected]> Acked-by: Juergen Gross <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: [email protected] Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Luis R. Rodriguez <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: linux-mm <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 9dac629 commit 9cd25aa

File tree

4 files changed

+60
-34
lines changed

4 files changed

+60
-34
lines changed

arch/x86/include/asm/pat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
bool pat_enabled(void);
88
extern void pat_init(void);
9-
void pat_init_cache_modes(void);
9+
void pat_init_cache_modes(u64);
1010

1111
extern int reserve_memtype(u64 start, u64 end,
1212
enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm);

arch/x86/mm/init.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
*/
4141
uint16_t __cachemode2pte_tbl[_PAGE_CACHE_MODE_NUM] = {
4242
[_PAGE_CACHE_MODE_WB ] = 0 | 0 ,
43-
[_PAGE_CACHE_MODE_WC ] = _PAGE_PWT | 0 ,
43+
[_PAGE_CACHE_MODE_WC ] = 0 | _PAGE_PCD,
4444
[_PAGE_CACHE_MODE_UC_MINUS] = 0 | _PAGE_PCD,
4545
[_PAGE_CACHE_MODE_UC ] = _PAGE_PWT | _PAGE_PCD,
4646
[_PAGE_CACHE_MODE_WT ] = 0 | _PAGE_PCD,
@@ -50,11 +50,11 @@ EXPORT_SYMBOL(__cachemode2pte_tbl);
5050

5151
uint8_t __pte2cachemode_tbl[8] = {
5252
[__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,
5454
[__pte2cm_idx( 0 | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC_MINUS,
5555
[__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | 0 )] = _PAGE_CACHE_MODE_UC,
5656
[__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,
5858
[__pte2cm_idx(0 | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS,
5959
[__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC,
6060
};

arch/x86/mm/pat.c

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ static int __init pat_debug_setup(char *str)
6868
}
6969
__setup("debugpat", pat_debug_setup);
7070

71-
static u64 __read_mostly boot_pat_state;
72-
7371
#ifdef CONFIG_X86_PAT
7472
/*
7573
* 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)
177175
* configuration.
178176
* Using lower indices is preferred, so we start with highest index.
179177
*/
180-
void pat_init_cache_modes(void)
178+
void pat_init_cache_modes(u64 pat)
181179
{
182-
int i;
183180
enum page_cache_mode cache;
184181
char pat_msg[33];
185-
u64 pat;
182+
int i;
186183

187-
rdmsrl(MSR_IA32_CR_PAT, pat);
188184
pat_msg[32] = 0;
189185
for (i = 7; i >= 0; i--) {
190186
cache = pat_get_cache_mode((pat >> (i * 8)) & 7,
@@ -198,24 +194,33 @@ void pat_init_cache_modes(void)
198194

199195
static void pat_bsp_init(u64 pat)
200196
{
197+
u64 tmp_pat;
198+
201199
if (!cpu_has_pat) {
202200
pat_disable("PAT not supported by CPU.");
203201
return;
204202
}
205203

206-
rdmsrl(MSR_IA32_CR_PAT, boot_pat_state);
207-
if (!boot_pat_state) {
204+
if (!pat_enabled())
205+
goto done;
206+
207+
rdmsrl(MSR_IA32_CR_PAT, tmp_pat);
208+
if (!tmp_pat) {
208209
pat_disable("PAT MSR is 0, disabled.");
209210
return;
210211
}
211212

212213
wrmsrl(MSR_IA32_CR_PAT, pat);
213214

214-
pat_init_cache_modes();
215+
done:
216+
pat_init_cache_modes(pat);
215217
}
216218

217219
static void pat_ap_init(u64 pat)
218220
{
221+
if (!pat_enabled())
222+
return;
223+
219224
if (!cpu_has_pat) {
220225
/*
221226
* If this happens we are on a secondary CPU, but switched to
@@ -231,25 +236,45 @@ void pat_init(void)
231236
{
232237
u64 pat;
233238

234-
if (!pat_enabled())
235-
return;
236-
237-
/*
238-
* Set PWT to Write-Combining. All other bits stay the same:
239-
*
240-
* PTE encoding used in Linux:
241-
* PAT
242-
* |PCD
243-
* ||PWT
244-
* |||
245-
* 000 WB _PAGE_CACHE_WB
246-
* 001 WC _PAGE_CACHE_WC
247-
* 010 UC- _PAGE_CACHE_UC_MINUS
248-
* 011 UC _PAGE_CACHE_UC
249-
* PAT bit unused
250-
*/
251-
pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
252-
PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC);
239+
if (!pat_enabled()) {
240+
/*
241+
* No PAT. Emulate the PAT table that corresponds to the two
242+
* cache bits, PWT (Write Through) and PCD (Cache Disable). This
243+
* setup is the same as the BIOS default setup when the system
244+
* has PAT but the "nopat" boot option has been specified. This
245+
* emulated PAT table is used when MSR_IA32_CR_PAT returns 0.
246+
*
247+
* PTE encoding used:
248+
*
249+
* PCD
250+
* |PWT PAT
251+
* || slot
252+
* 00 0 WB : _PAGE_CACHE_MODE_WB
253+
* 01 1 WT : _PAGE_CACHE_MODE_WT
254+
* 10 2 UC-: _PAGE_CACHE_MODE_UC_MINUS
255+
* 11 3 UC : _PAGE_CACHE_MODE_UC
256+
*
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+
}
253278

254279
if (!boot_cpu_done) {
255280
pat_bsp_init(pat);

arch/x86/xen/enlighten.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
14671467
{
14681468
struct physdev_set_iopl set_iopl;
14691469
unsigned long initrd_start = 0;
1470+
u64 pat;
14701471
int rc;
14711472

14721473
if (!xen_start_info)
@@ -1574,8 +1575,8 @@ asmlinkage __visible void __init xen_start_kernel(void)
15741575
* Modify the cache mode translation tables to match Xen's PAT
15751576
* configuration.
15761577
*/
1577-
1578-
pat_init_cache_modes();
1578+
rdmsrl(MSR_IA32_CR_PAT, pat);
1579+
pat_init_cache_modes(pat);
15791580

15801581
/* keep using Xen gdt for now; no urgent need to change it */
15811582

0 commit comments

Comments
 (0)