* [PATCH] x86: Fix /proc/mtrr with base/size more than 44bits
@ 2013-06-13 18:53 Yinghai Lu
2013-06-13 20:47 ` H. Peter Anvin
0 siblings, 1 reply; 3+ messages in thread
From: Yinghai Lu @ 2013-06-13 18:53 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Thomas Gleixner, Ingo Molnar, linux-kernel, Yinghai Lu, stable
On one sytem that mtrr range is more then 44bits, in dmesg we have
[ 0.000000] MTRR default type: write-back
[ 0.000000] MTRR fixed ranges enabled:
[ 0.000000] 00000-9FFFF write-back
[ 0.000000] A0000-BFFFF uncachable
[ 0.000000] C0000-DFFFF write-through
[ 0.000000] E0000-FFFFF write-protect
[ 0.000000] MTRR variable ranges enabled:
[ 0.000000] 0 [000080000000-0000FFFFFFFF] mask 3FFF80000000 uncachable
[ 0.000000] 1 [380000000000-38FFFFFFFFFF] mask 3F0000000000 uncachable
[ 0.000000] 2 [000099000000-000099FFFFFF] mask 3FFFFF000000 write-through
[ 0.000000] 3 [00009A000000-00009AFFFFFF] mask 3FFFFF000000 write-through
[ 0.000000] 4 [381FFA000000-381FFBFFFFFF] mask 3FFFFE000000 write-through
[ 0.000000] 5 [381FFC000000-381FFC0FFFFF] mask 3FFFFFF00000 write-through
[ 0.000000] 6 [0000AD000000-0000ADFFFFFF] mask 3FFFFF000000 write-through
[ 0.000000] 7 [0000BD000000-0000BDFFFFFF] mask 3FFFFF000000 write-through
[ 0.000000] 8 disabled
[ 0.000000] 9 disabled
but /proc/mtrr report wrong:
reg00: base=0x080000000 ( 2048MB), size= 2048MB, count=1: uncachable
reg01: base=0x80000000000 (8388608MB), size=1048576MB, count=1: uncachable
reg02: base=0x099000000 ( 2448MB), size= 16MB, count=1: write-through
reg03: base=0x09a000000 ( 2464MB), size= 16MB, count=1: write-through
reg04: base=0x81ffa000000 (8519584MB), size= 32MB, count=1: write-through
reg05: base=0x81ffc000000 (8519616MB), size= 1MB, count=1: write-through
reg06: base=0x0ad000000 ( 2768MB), size= 16MB, count=1: write-through
reg07: base=0x0bd000000 ( 3024MB), size= 16MB, count=1: write-through
reg08: base=0x09b000000 ( 2480MB), size= 16MB, count=1: write-combining
so bit 44 and bit 45 get cut off.
We have problems in arch/x86/kernel/cpu/mtrr/generic.c::generic_get_mtrr().
1. for base, we miss cast base_lo to 64bit before shifting.
Fix that by adding u64 casting.
2. for size, it only can handle 44 bits aka 32bits + page_shift
Fix that with 64bit mask instead of 32bit mask_lo, then range could be
more than 44bits.
At the same time, we need to update size_or_mask for old cpus that does
support cpuid 0x80000008 to get phys_addr. Need to set high 32bits
to all 1s, otherwise will not get correct size for them.
Also fix mtrr_add_page as it should not use size_or_mask to do the
boundary checking. Use boot_cpu_data.x86_phys_bits instead.
So When are we going to have size more than 44bits? that is 16TiB.
after patch we have right ouput:
reg00: base=0x080000000 ( 2048MB), size= 2048MB, count=1: uncachable
reg01: base=0x380000000000 (58720256MB), size=1048576MB, count=1: uncachable
reg02: base=0x099000000 ( 2448MB), size= 16MB, count=1: write-through
reg03: base=0x09a000000 ( 2464MB), size= 16MB, count=1: write-through
reg04: base=0x381ffa000000 (58851232MB), size= 32MB, count=1: write-through
reg05: base=0x381ffc000000 (58851264MB), size= 1MB, count=1: write-through
reg06: base=0x0ad000000 ( 2768MB), size= 16MB, count=1: write-through
reg07: base=0x0bd000000 ( 3024MB), size= 16MB, count=1: write-through
reg08: base=0x09b000000 ( 2480MB), size= 16MB, count=1: write-combining
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Cc: <stable@vger.kernel.org>
---
arch/x86/kernel/cpu/mtrr/generic.c | 21 +++++++++++----------
arch/x86/kernel/cpu/mtrr/main.c | 17 ++++++++++-------
2 files changed, 21 insertions(+), 17 deletions(-)
Index: linux-2.6/arch/x86/kernel/cpu/mtrr/generic.c
===================================================================
--- linux-2.6.orig/arch/x86/kernel/cpu/mtrr/generic.c
+++ linux-2.6/arch/x86/kernel/cpu/mtrr/generic.c
@@ -510,8 +510,9 @@ generic_get_free_region(unsigned long ba
static void generic_get_mtrr(unsigned int reg, unsigned long *base,
unsigned long *size, mtrr_type *type)
{
- unsigned int mask_lo, mask_hi, base_lo, base_hi;
- unsigned int tmp, hi;
+ u32 mask_lo, mask_hi, base_lo, base_hi;
+ unsigned int hi;
+ u64 tmp, mask;
/*
* get_mtrr doesn't need to update mtrr_state, also it could be called
@@ -532,18 +533,18 @@ static void generic_get_mtrr(unsigned in
rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi);
/* Work out the shifted address mask: */
- tmp = mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT;
- mask_lo = size_or_mask | tmp;
+ tmp = (u64)mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT;
+ mask = size_or_mask | tmp;
/* Expand tmp with high bits to all 1s: */
- hi = fls(tmp);
+ hi = fls64(tmp);
if (hi > 0) {
- tmp |= ~((1<<(hi - 1)) - 1);
+ tmp |= ~((1ULL<<(hi - 1)) - 1);
- if (tmp != mask_lo) {
+ if (tmp != mask) {
printk(KERN_WARNING "mtrr: your BIOS has configured an incorrect mask, fixing it.\n");
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
- mask_lo = tmp;
+ mask = tmp;
}
}
@@ -551,8 +552,8 @@ static void generic_get_mtrr(unsigned in
* This works correctly if size is a power of two, i.e. a
* contiguous range:
*/
- *size = -mask_lo;
- *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
+ *size = -mask;
+ *base = (u64)base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
*type = base_lo & 0xff;
out_put_cpu:
Index: linux-2.6/arch/x86/kernel/cpu/mtrr/main.c
===================================================================
--- linux-2.6.orig/arch/x86/kernel/cpu/mtrr/main.c
+++ linux-2.6/arch/x86/kernel/cpu/mtrr/main.c
@@ -305,7 +305,9 @@ int mtrr_add_page(unsigned long base, un
return -EINVAL;
}
- if (base & size_or_mask || size & size_or_mask) {
+ if (base >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT) ||
+ base > (base + size) ||
+ (base + size - 1) >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT)) {
pr_warning("mtrr: base or size exceeds the MTRR width\n");
return -EINVAL;
}
@@ -583,6 +585,7 @@ static struct syscore_ops mtrr_syscore_o
int __initdata changed_by_mtrr_cleanup;
+#define SIZE_OR_MASK_BITS(n) (~((1ULL << ((n) - PAGE_SHIFT)) - 1))
/**
* mtrr_bp_init - initialize mtrrs on the boot CPU
*
@@ -600,7 +603,7 @@ void __init mtrr_bp_init(void)
if (cpu_has_mtrr) {
mtrr_if = &generic_mtrr_ops;
- size_or_mask = 0xff000000; /* 36 bits */
+ size_or_mask = SIZE_OR_MASK_BITS(36);
size_and_mask = 0x00f00000;
phys_addr = 36;
@@ -619,7 +622,7 @@ void __init mtrr_bp_init(void)
boot_cpu_data.x86_mask == 0x4))
phys_addr = 36;
- size_or_mask = ~((1ULL << (phys_addr - PAGE_SHIFT)) - 1);
+ size_or_mask = SIZE_OR_MASK_BITS(phys_addr);
size_and_mask = ~size_or_mask & 0xfffff00000ULL;
} else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
boot_cpu_data.x86 == 6) {
@@ -627,7 +630,7 @@ void __init mtrr_bp_init(void)
* VIA C* family have Intel style MTRRs,
* but don't support PAE
*/
- size_or_mask = 0xfff00000; /* 32 bits */
+ size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
phys_addr = 32;
}
@@ -637,21 +640,21 @@ void __init mtrr_bp_init(void)
if (cpu_has_k6_mtrr) {
/* Pre-Athlon (K6) AMD CPU MTRRs */
mtrr_if = mtrr_ops[X86_VENDOR_AMD];
- size_or_mask = 0xfff00000; /* 32 bits */
+ size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
case X86_VENDOR_CENTAUR:
if (cpu_has_centaur_mcr) {
mtrr_if = mtrr_ops[X86_VENDOR_CENTAUR];
- size_or_mask = 0xfff00000; /* 32 bits */
+ size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
case X86_VENDOR_CYRIX:
if (cpu_has_cyrix_arr) {
mtrr_if = mtrr_ops[X86_VENDOR_CYRIX];
- size_or_mask = 0xfff00000; /* 32 bits */
+ size_or_mask = SIZE_OR_MASK_BITS(32);
size_and_mask = 0;
}
break;
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] x86: Fix /proc/mtrr with base/size more than 44bits
2013-06-13 18:53 [PATCH] x86: Fix /proc/mtrr with base/size more than 44bits Yinghai Lu
@ 2013-06-13 20:47 ` H. Peter Anvin
2013-06-13 22:11 ` Yinghai Lu
0 siblings, 1 reply; 3+ messages in thread
From: H. Peter Anvin @ 2013-06-13 20:47 UTC (permalink / raw)
To: Yinghai Lu; +Cc: Thomas Gleixner, Ingo Molnar, linux-kernel, stable
On 06/13/2013 11:53 AM, Yinghai Lu wrote:
>
> - if (base & size_or_mask || size & size_or_mask) {
> + if (base >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT) ||
> + base > (base + size) ||
> + (base + size - 1) >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT)) {
> pr_warning("mtrr: base or size exceeds the MTRR width\n");
> return -EINVAL;
> }
Most of this patch looks good as far as being a minimal patch, but I'm
really confused about this bit. Could you explain the reason for why
the original doesn't work? (To be fair: I am not even sure the original
does anything useful so it could just be a "this is just too broken to
live" kind of thing.)
The first and third clause of the test can be simplified, however:
(base | (base + size - 1)) >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT)
... although it would be cleaner to put boot_cpu_data.x86_phys_bits -
PAGE_SHIFT into a variable.
A lot of the mask_hi/mask_lo stuff should just get removed by using
rdmsrl/wrmsrl, but that is not stable material obviously.
-hpa
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] x86: Fix /proc/mtrr with base/size more than 44bits
2013-06-13 20:47 ` H. Peter Anvin
@ 2013-06-13 22:11 ` Yinghai Lu
0 siblings, 0 replies; 3+ messages in thread
From: Yinghai Lu @ 2013-06-13 22:11 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Thomas Gleixner, Ingo Molnar, Linux Kernel Mailing List,
stable@vger.kernel.org
On Thu, Jun 13, 2013 at 1:47 PM, H. Peter Anvin <hpa@zytor.com> wrote:
> On 06/13/2013 11:53 AM, Yinghai Lu wrote:
>>
>> - if (base & size_or_mask || size & size_or_mask) {
>> + if (base >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT) ||
>> + base > (base + size) ||
>> + (base + size - 1) >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT)) {
>> pr_warning("mtrr: base or size exceeds the MTRR width\n");
>> return -EINVAL;
>> }
>
> Most of this patch looks good as far as being a minimal patch, but I'm
> really confused about this bit. Could you explain the reason for why
> the original doesn't work? (To be fair: I am not even sure the original
> does anything useful so it could just be a "this is just too broken to
> live" kind of thing.)
all because I update size_of_mask for old cpus that does not have
cpuid 80000008.
by make high 32bits to be all 1s. otherwise size = -mask trick will not work.
then check those range size_or_mask using, found that is not right.
as base and size could be all small, but base + size -1 could be big enough.
then the original will not detect that is out of boundary.
also we could even use x86_phys_bits directly.
>
> The first and third clause of the test can be simplified, however:
>
> (base | (base + size - 1)) >> (boot_cpu_data.x86_phys_bits - PAGE_SHIFT)
>
> ... although it would be cleaner to put boot_cpu_data.x86_phys_bits -
> PAGE_SHIFT into a variable.
Yes.
also we can drop base > (base + size) checking, as
base and size are already shifted with PAGE_SHIFT to pfn.
so base+size can not be wrapped.
>
> A lot of the mask_hi/mask_lo stuff should just get removed by using
> rdmsrl/wrmsrl, but that is not stable material obviously.
yes.
will send updated version shortly.
Thanks
Yinghai
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2013-06-13 22:11 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-06-13 18:53 [PATCH] x86: Fix /proc/mtrr with base/size more than 44bits Yinghai Lu
2013-06-13 20:47 ` H. Peter Anvin
2013-06-13 22:11 ` Yinghai Lu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox