x86: work around MTRR mask setting

Joshua Hoblitt reported that only 3 GB of his 16 GB of RAM is
usable. Booting with mtrr_show showed us the BIOS-initialized
MTRR settings - which are all wrong.

So the root cause is that the BIOS has not set the mask correctly:

> [ 0.429971] MSR00000200: 00000000d0000000
> [ 0.433305] MSR00000201: 0000000ff0000800
> should be ==> [ 0.433305] MSR00000201: 0000003ff0000800
>
> [ 0.436638] MSR00000202: 00000000e0000000
> [ 0.439971] MSR00000203: 0000000fe0000800
> should be ==> [ 0.439971] MSR00000203: 0000003fe0000800
>
> [ 0.443304] MSR00000204: 0000000000000006
> [ 0.446637] MSR00000205: 0000000c00000800
> should be ==> [ 0.446637] MSR00000205: 0000003c00000800
>
> [ 0.449970] MSR00000206: 0000000400000006
> [ 0.453303] MSR00000207: 0000000fe0000800
> should be ==> [ 0.453303] MSR00000207: 0000003fe0000800
>
> [ 0.456636] MSR00000208: 0000000420000006
> [ 0.459970] MSR00000209: 0000000ff0000800
> should be ==> [ 0.459970] MSR00000209: 0000003ff0000800

So detect this borkage and add the prefix 111.

Signed-off-by: Yinghai Lu <yhlu.kernel@gmail.com>
Cc: <stable@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Yinghai Lu and committed by Ingo Molnar 38cc1c3d 8323444b

+13 -2
+13 -2
arch/x86/kernel/cpu/mtrr/generic.c
··· 379 379 unsigned long *size, mtrr_type *type) 380 380 { 381 381 unsigned int mask_lo, mask_hi, base_lo, base_hi; 382 + unsigned int tmp, hi; 382 383 383 384 rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi); 384 385 if ((mask_lo & 0x800) == 0) { ··· 393 392 rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); 394 393 395 394 /* Work out the shifted address mask. */ 396 - mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) 397 - | mask_lo >> PAGE_SHIFT; 395 + tmp = mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT; 396 + mask_lo = size_or_mask | tmp; 397 + /* Expand tmp with high bits to all 1s*/ 398 + hi = fls(tmp); 399 + if (hi > 0) { 400 + tmp |= ~((1<<(hi - 1)) - 1); 401 + 402 + if (tmp != mask_lo) { 403 + WARN_ON("mtrr: your BIOS has set up an incorrect mask, fixing it up.\n"); 404 + mask_lo = tmp; 405 + } 406 + } 398 407 399 408 /* This works correctly if size is a power of two, i.e. a 400 409 contiguous range. */