From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934295AbcCKDxW (ORCPT ); Thu, 10 Mar 2016 22:53:22 -0500 Received: from g4t3427.houston.hp.com ([15.201.208.55]:50956 "EHLO g4t3427.houston.hp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934007AbcCKDxS (ORCPT ); Thu, 10 Mar 2016 22:53:18 -0500 From: Toshi Kani To: mingo@kernel.org, bp@suse.de, hpa@zytor.com, tglx@linutronix.de Cc: mcgrof@suse.com, jgross@suse.com, paul.gortmaker@windriver.com, x86@kernel.org, linux-kernel@vger.kernel.org, Toshi Kani Subject: [PATCH 1/2] x86/mm/pat: Change pat_disable() to emulate PAT table Date: Thu, 10 Mar 2016 21:45:45 -0700 Message-Id: <1457671546-13486-2-git-send-email-toshi.kani@hpe.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1457671546-13486-1-git-send-email-toshi.kani@hpe.com> References: <1457671546-13486-1-git-send-email-toshi.kani@hpe.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Since 'commit 9cd25aac1f44 ("x86/mm/pat: Emulate PAT when it is disabled")', we emulate a PAT table when PAT is disabled. This requires pat_init() be called even if PAT is disabled, which revealed a long standing issue that PAT is left enabled without calling pat_init() at all. pat_init() is called from MTRR code since it relies on MTRR's rendezvous handler to initialize PAT for all APs . However, when CPU does not support MTRR, ex. qemu32's virtual CPU, MTRR is set disabled and does not call pat_init(). There is no interface available for MTRR to disable PAT, either. Change pat_disable() to a regular function (from an inline func) so that MTRR can call it to disable PAT when MTRR is disabled. pat_disable() sets PAT disabled, and calls pat_disable_init() to emulate the PAT table. link: https://lkml.org/lkml/2016/3/10/402 Reported-by: Paul Gortmaker Signed-off-by: Toshi Kani Cc: Borislav Petkov Cc: Luis R. Rodriguez Cc: Juergen Gross Cc: Ingo Molnar Cc: H. Peter Anvin Cc: Thomas Gleixner --- arch/x86/include/asm/pat.h | 1 + arch/x86/mm/pat.c | 84 +++++++++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/arch/x86/include/asm/pat.h b/arch/x86/include/asm/pat.h index ca6c228..016142b 100644 --- a/arch/x86/include/asm/pat.h +++ b/arch/x86/include/asm/pat.h @@ -5,6 +5,7 @@ #include bool pat_enabled(void); +void pat_disable(const char *reason); extern void pat_init(void); void pat_init_cache_modes(u64); diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index f4ae536..1ff8aa9 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c @@ -40,11 +40,19 @@ static bool boot_cpu_done; static int __read_mostly __pat_enabled = IS_ENABLED(CONFIG_X86_PAT); +static void pat_disable_init(void); -static inline void pat_disable(const char *reason) +void pat_disable(const char *reason) { + if (boot_cpu_done) { + pr_info("x86/PAT: PAT cannot be disabled after initialized\n"); + return; + } + __pat_enabled = 0; pr_info("x86/PAT: %s\n", reason); + + pat_disable_init(); } static int __init nopat(char *str) @@ -207,9 +215,6 @@ static void pat_bsp_init(u64 pat) return; } - if (!pat_enabled()) - goto done; - rdmsrl(MSR_IA32_CR_PAT, tmp_pat); if (!tmp_pat) { pat_disable("PAT MSR is 0, disabled."); @@ -218,15 +223,11 @@ static void pat_bsp_init(u64 pat) wrmsrl(MSR_IA32_CR_PAT, pat); -done: pat_init_cache_modes(pat); } static void pat_ap_init(u64 pat) { - if (!pat_enabled()) - return; - if (!cpu_has_pat) { /* * If this happens we are on a secondary CPU, but switched to @@ -238,38 +239,55 @@ static void pat_ap_init(u64 pat) wrmsrl(MSR_IA32_CR_PAT, pat); } +static void pat_disable_init(void) +{ + u64 pat; + static int disable_init_done; + + if (disable_init_done) + return; + + /* + * No PAT. Emulate the PAT table that corresponds to the two + * cache bits, PWT (Write Through) and PCD (Cache Disable). This + * setup is the same as the BIOS default setup when the system + * has PAT but the "nopat" boot option has been specified. This + * emulated PAT table is used when MSR_IA32_CR_PAT returns 0. + * + * PTE encoding: + * + * PCD + * |PWT PAT + * || slot + * 00 0 WB : _PAGE_CACHE_MODE_WB + * 01 1 WT : _PAGE_CACHE_MODE_WT + * 10 2 UC-: _PAGE_CACHE_MODE_UC_MINUS + * 11 3 UC : _PAGE_CACHE_MODE_UC + * + * NOTE: When WC or WP is used, it is redirected to UC- per + * the default setup in __cachemode2pte_tbl[]. + */ + pat = PAT(0, WB) | PAT(1, WT) | PAT(2, UC_MINUS) | PAT(3, UC) | + PAT(4, WB) | PAT(5, WT) | PAT(6, UC_MINUS) | PAT(7, UC); + + pat_init_cache_modes(pat); + + disable_init_done = 1; +} + void pat_init(void) { u64 pat; struct cpuinfo_x86 *c = &boot_cpu_data; if (!pat_enabled()) { - /* - * No PAT. Emulate the PAT table that corresponds to the two - * cache bits, PWT (Write Through) and PCD (Cache Disable). This - * setup is the same as the BIOS default setup when the system - * has PAT but the "nopat" boot option has been specified. This - * emulated PAT table is used when MSR_IA32_CR_PAT returns 0. - * - * PTE encoding: - * - * PCD - * |PWT PAT - * || slot - * 00 0 WB : _PAGE_CACHE_MODE_WB - * 01 1 WT : _PAGE_CACHE_MODE_WT - * 10 2 UC-: _PAGE_CACHE_MODE_UC_MINUS - * 11 3 UC : _PAGE_CACHE_MODE_UC - * - * NOTE: When WC or WP is used, it is redirected to UC- per - * the default setup in __cachemode2pte_tbl[]. - */ - pat = PAT(0, WB) | PAT(1, WT) | PAT(2, UC_MINUS) | PAT(3, UC) | - PAT(4, WB) | PAT(5, WT) | PAT(6, UC_MINUS) | PAT(7, UC); + pat_disable_init(); + return; + } - } else if ((c->x86_vendor == X86_VENDOR_INTEL) && - (((c->x86 == 0x6) && (c->x86_model <= 0xd)) || - ((c->x86 == 0xf) && (c->x86_model <= 0x6)))) { + if ((c->x86_vendor == X86_VENDOR_INTEL) && + (((c->x86 == 0x6) && (c->x86_model <= 0xd)) || + ((c->x86 == 0xf) && (c->x86_model <= 0x6)))) { /* * PAT support with the lower four entries. Intel Pentium 2, * 3, M, and 4 are affected by PAT errata, which makes the