aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorBernhard Kaindl <bk@suse.de>2007-05-02 13:27:17 -0400
committerAndi Kleen <andi@basil.nowhere.org>2007-05-02 13:27:17 -0400
commitde938c51d5fec4ae03af64b06beb15d4423ec611 (patch)
treeaabb457fda132a7d88e771427aa3da04c7944d4b /arch/i386
parent3ebad5905609476a4ff1151a66b21d9794009961 (diff)
[PATCH] i386: Enable support for fixed-range IORRs to keep RdMem & WrMem in sync
If our copy of the MTRRs of the BSP has RdMem or WrMem set, and we are running on an AMD64/K8 system, the boot CPU must have had MtrrFixDramEn and MtrrFixDramModEn set (otherwise our RDMSR would have copied these bits cleared), so we set them on this CPU as well. This allows us to keep the AMD64/K8 RdMem and WrMem bits in sync across the CPUs of SMP systems in order to fullfill the duty of system software to "initialize and maintain MTRR consistency across all processors." as written in the AMD and Intel manuals. If an WRMSR instruction fails because MtrrFixDramModEn is not set, I expect that also the Intel-style MTRR bits are not updated. AK: minor cleanup, moved MSR defines around Signed-off-by: Bernhard Kaindl <bk@suse.de> Signed-off-by: Andi Kleen <ak@suse.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andi Kleen <ak@suse.de> Cc: Dave Jones <davej@codemonkey.org.uk>
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/kernel/cpu/mtrr/generic.c85
1 files changed, 60 insertions, 25 deletions
diff --git a/arch/i386/kernel/cpu/mtrr/generic.c b/arch/i386/kernel/cpu/mtrr/generic.c
index 150cf5055a3c..000c07e86433 100644
--- a/arch/i386/kernel/cpu/mtrr/generic.c
+++ b/arch/i386/kernel/cpu/mtrr/generic.c
@@ -20,6 +20,18 @@ struct mtrr_state {
20 mtrr_type def_type; 20 mtrr_type def_type;
21}; 21};
22 22
23struct fixed_range_block {
24 int base_msr; /* start address of an MTRR block */
25 int ranges; /* number of MTRRs in this block */
26};
27
28static struct fixed_range_block fixed_range_blocks[] = {
29 { MTRRfix64K_00000_MSR, 1 }, /* one 64k MTRR */
30 { MTRRfix16K_80000_MSR, 2 }, /* two 16k MTRRs */
31 { MTRRfix4K_C0000_MSR, 8 }, /* eight 4k MTRRs */
32 {}
33};
34
23static unsigned long smp_changes_mask; 35static unsigned long smp_changes_mask;
24static struct mtrr_state mtrr_state = {}; 36static struct mtrr_state mtrr_state = {};
25 37
@@ -152,6 +164,44 @@ void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b)
152 smp_processor_id(), msr, a, b); 164 smp_processor_id(), msr, a, b);
153} 165}
154 166
167/**
168 * Enable and allow read/write of extended fixed-range MTRR bits on K8 CPUs
169 * see AMD publication no. 24593, chapter 3.2.1 for more information
170 */
171static inline void k8_enable_fixed_iorrs(void)
172{
173 unsigned lo, hi;
174
175 rdmsr(MSR_K8_SYSCFG, lo, hi);
176 mtrr_wrmsr(MSR_K8_SYSCFG, lo
177 | K8_MTRRFIXRANGE_DRAM_ENABLE
178 | K8_MTRRFIXRANGE_DRAM_MODIFY, hi);
179}
180
181/**
182 * Checks and updates an fixed-range MTRR if it differs from the value it
183 * should have. If K8 extenstions are wanted, update the K8 SYSCFG MSR also.
184 * see AMD publication no. 24593, chapter 7.8.1, page 233 for more information
185 * \param msr MSR address of the MTTR which should be checked and updated
186 * \param changed pointer which indicates whether the MTRR needed to be changed
187 * \param msrwords pointer to the MSR values which the MSR should have
188 */
189static void set_fixed_range(int msr, int * changed, unsigned int * msrwords)
190{
191 unsigned lo, hi;
192
193 rdmsr(msr, lo, hi);
194
195 if (lo != msrwords[0] || hi != msrwords[1]) {
196 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
197 boot_cpu_data.x86 == 15 &&
198 ((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK))
199 k8_enable_fixed_iorrs();
200 mtrr_wrmsr(msr, msrwords[0], msrwords[1]);
201 *changed = TRUE;
202 }
203}
204
155int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg) 205int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg)
156/* [SUMMARY] Get a free MTRR. 206/* [SUMMARY] Get a free MTRR.
157 <base> The starting (base) address of the region. 207 <base> The starting (base) address of the region.
@@ -201,36 +251,21 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
201 *type = base_lo & 0xff; 251 *type = base_lo & 0xff;
202} 252}
203 253
254/**
255 * Checks and updates the fixed-range MTRRs if they differ from the saved set
256 * \param frs pointer to fixed-range MTRR values, saved by get_fixed_ranges()
257 */
204static int set_fixed_ranges(mtrr_type * frs) 258static int set_fixed_ranges(mtrr_type * frs)
205{ 259{
206 unsigned int *p = (unsigned int *) frs; 260 unsigned long long *saved = (unsigned long long *) frs;
207 int changed = FALSE; 261 int changed = FALSE;
208 int i; 262 int block=-1, range;
209 unsigned int lo, hi;
210 263
211 rdmsr(MTRRfix64K_00000_MSR, lo, hi); 264 while (fixed_range_blocks[++block].ranges)
212 if (p[0] != lo || p[1] != hi) { 265 for (range=0; range < fixed_range_blocks[block].ranges; range++)
213 mtrr_wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]); 266 set_fixed_range(fixed_range_blocks[block].base_msr + range,
214 changed = TRUE; 267 &changed, (unsigned int *) saved++);
215 }
216
217 for (i = 0; i < 2; i++) {
218 rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
219 if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) {
220 mtrr_wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2],
221 p[3 + i * 2]);
222 changed = TRUE;
223 }
224 }
225 268
226 for (i = 0; i < 8; i++) {
227 rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
228 if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) {
229 mtrr_wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2],
230 p[7 + i * 2]);
231 changed = TRUE;
232 }
233 }
234 return changed; 269 return changed;
235} 270}
236 271