aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu/mtrr/generic.c
diff options
context:
space:
mode:
authorAndreas Herrmann <andreas.herrmann3@amd.com>2009-03-12 12:39:37 -0400
committerIngo Molnar <mingo@elte.hu>2009-03-13 05:19:27 -0400
commit3ff42da5048649503e343a32be37b14a6a4e8aaf (patch)
tree9d34746890b6d7aa46c9f5b464eab5b2e8df497a /arch/x86/kernel/cpu/mtrr/generic.c
parent0d890355bff25e1dc03a577a90ed80741489ca54 (diff)
x86: mtrr: don't modify RdDram/WrDram bits of fixed MTRRs
Impact: bug fix + BIOS workaround BIOS is expected to clear the SYSCFG[MtrrFixDramModEn] on AMD CPUs after fixed MTRRs are configured. Some BIOSes do not clear SYSCFG[MtrrFixDramModEn] on BP (and on APs). This can lead to obfuscation in Linux when this bit is not cleared on BP but cleared on APs. A consequence of this is that the saved fixed-MTRR state (from BP) differs from the fixed-MTRRs of APs -- because RdDram/WrDram bits are read as zero when SYSCFG[MtrrFixDramModEn] is cleared -- and Linux tries to sync fixed-MTRR state from BP to AP. This implies that Linux sets SYSCFG[MtrrFixDramEn] and activates those bits. More important is that (some) systems change these bits in SMM when ACPI is enabled. Hence it is racy if Linux modifies RdMem/WrMem bits, too. (1) The patch modifies an old fix from Bernhard Kaindl to get suspend/resume working on some Acer Laptops. Bernhard's patch tried to sync RdMem/WrMem bits of fixed MTRR registers and that helped on those old Laptops. (Don't ask me why -- can't test it myself). But this old problem was not the motivation for the patch. (See http://lkml.org/lkml/2007/4/3/110) (2) The more important effect is to fix issues on some more current systems. On those systems Linux panics or just freezes, see http://bugzilla.kernel.org/show_bug.cgi?id=11541 (and also duplicates of this bug: http://bugzilla.kernel.org/show_bug.cgi?id=11737 http://bugzilla.kernel.org/show_bug.cgi?id=11714) The affected systems boot only using acpi=ht, acpi=off or when the kernel is built with CONFIG_MTRR=n. The acpi options prevent full enablement of ACPI. Obviously when ACPI is enabled the BIOS/SMM modfies RdMem/WrMem bits. When CONFIG_MTRR=y Linux also accesses and modifies those bits when it needs to sync fixed-MTRRs across cores (Bernhard's fix, see (1)). How do you synchronize that? You can't. As a consequence Linux shouldn't touch those bits at all (Rationale are AMD's BKDGs which recommend to clear the bit that makes RdMem/WrMem accessible). This is the purpose of this patch. And (so far) this suffices to fix (1) and (2). I suggest not to touch RdDram/WrDram bits of fixed-MTRRs and SYSCFG[MtrrFixDramEn] and to clear SYSCFG[MtrrFixDramModEn] as suggested by AMD K8, and AMD family 10h/11h BKDGs. BIOS is expected to do this anyway. This should avoid that Linux and SMM tread on each other's toes ... Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com> Cc: trenn@suse.de Cc: Yinghai Lu <yinghai@kernel.org> LKML-Reference: <20090312163937.GH20716@alberich.amd.com> Cc: <stable@kernel.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/cpu/mtrr/generic.c')
-rw-r--r--arch/x86/kernel/cpu/mtrr/generic.c51
1 files changed, 30 insertions, 21 deletions
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 964403520100..950c434f793d 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -33,6 +33,32 @@ u64 mtrr_tom2;
33struct mtrr_state_type mtrr_state = {}; 33struct mtrr_state_type mtrr_state = {};
34EXPORT_SYMBOL_GPL(mtrr_state); 34EXPORT_SYMBOL_GPL(mtrr_state);
35 35
36/**
37 * BIOS is expected to clear MtrrFixDramModEn bit, see for example
38 * "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
39 * Opteron Processors" (26094 Rev. 3.30 February 2006), section
40 * "13.2.1.2 SYSCFG Register": "The MtrrFixDramModEn bit should be set
41 * to 1 during BIOS initalization of the fixed MTRRs, then cleared to
42 * 0 for operation."
43 */
44static inline void k8_check_syscfg_dram_mod_en(void)
45{
46 u32 lo, hi;
47
48 if (!((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) &&
49 (boot_cpu_data.x86 >= 0x0f)))
50 return;
51
52 rdmsr(MSR_K8_SYSCFG, lo, hi);
53 if (lo & K8_MTRRFIXRANGE_DRAM_MODIFY) {
54 printk(KERN_ERR FW_WARN "MTRR: CPU %u: SYSCFG[MtrrFixDramModEn]"
55 " not cleared by BIOS, clearing this bit\n",
56 smp_processor_id());
57 lo &= ~K8_MTRRFIXRANGE_DRAM_MODIFY;
58 mtrr_wrmsr(MSR_K8_SYSCFG, lo, hi);
59 }
60}
61
36/* 62/*
37 * Returns the effective MTRR type for the region 63 * Returns the effective MTRR type for the region
38 * Error returns: 64 * Error returns:
@@ -166,6 +192,8 @@ get_fixed_ranges(mtrr_type * frs)
166 unsigned int *p = (unsigned int *) frs; 192 unsigned int *p = (unsigned int *) frs;
167 int i; 193 int i;
168 194
195 k8_check_syscfg_dram_mod_en();
196
169 rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); 197 rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
170 198
171 for (i = 0; i < 2; i++) 199 for (i = 0; i < 2; i++)
@@ -306,27 +334,10 @@ void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b)
306} 334}
307 335
308/** 336/**
309 * Enable and allow read/write of extended fixed-range MTRR bits on K8 CPUs
310 * see AMD publication no. 24593, chapter 3.2.1 for more information
311 */
312static inline void k8_enable_fixed_iorrs(void)
313{
314 unsigned lo, hi;
315
316 rdmsr(MSR_K8_SYSCFG, lo, hi);
317 mtrr_wrmsr(MSR_K8_SYSCFG, lo
318 | K8_MTRRFIXRANGE_DRAM_ENABLE
319 | K8_MTRRFIXRANGE_DRAM_MODIFY, hi);
320}
321
322/**
323 * set_fixed_range - checks & updates a fixed-range MTRR if it differs from the value it should have 337 * set_fixed_range - checks & updates a fixed-range MTRR if it differs from the value it should have
324 * @msr: MSR address of the MTTR which should be checked and updated 338 * @msr: MSR address of the MTTR which should be checked and updated
325 * @changed: pointer which indicates whether the MTRR needed to be changed 339 * @changed: pointer which indicates whether the MTRR needed to be changed
326 * @msrwords: pointer to the MSR values which the MSR should have 340 * @msrwords: pointer to the MSR values which the MSR should have
327 *
328 * If K8 extentions are wanted, update the K8 SYSCFG MSR also.
329 * See AMD publication no. 24593, chapter 7.8.1, page 233 for more information.
330 */ 341 */
331static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) 342static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords)
332{ 343{
@@ -335,10 +346,6 @@ static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords)
335 rdmsr(msr, lo, hi); 346 rdmsr(msr, lo, hi);
336 347
337 if (lo != msrwords[0] || hi != msrwords[1]) { 348 if (lo != msrwords[0] || hi != msrwords[1]) {
338 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
339 (boot_cpu_data.x86 >= 0x0f && boot_cpu_data.x86 <= 0x11) &&
340 ((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK))
341 k8_enable_fixed_iorrs();
342 mtrr_wrmsr(msr, msrwords[0], msrwords[1]); 349 mtrr_wrmsr(msr, msrwords[0], msrwords[1]);
343 *changed = true; 350 *changed = true;
344 } 351 }
@@ -426,6 +433,8 @@ static int set_fixed_ranges(mtrr_type * frs)
426 bool changed = false; 433 bool changed = false;
427 int block=-1, range; 434 int block=-1, range;
428 435
436 k8_check_syscfg_dram_mod_en();
437
429 while (fixed_range_blocks[++block].ranges) 438 while (fixed_range_blocks[++block].ranges)
430 for (range=0; range < fixed_range_blocks[block].ranges; range++) 439 for (range=0; range < fixed_range_blocks[block].ranges; range++)
431 set_fixed_range(fixed_range_blocks[block].base_msr + range, 440 set_fixed_range(fixed_range_blocks[block].base_msr + range,