[PATCH] x86_64: Backport 2.6 MTRR algorithms

Linux Kernel Mailing List <[email protected]> Thu, 07 Apr 2005 12:05:33 +0000
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
ChangeSet 1.1496, 2005/04/07 09:05:33-03:00, [email protected]

	[PATCH] x86_64: Backport 2.6 MTRR algorithms
	
	Backport 2.6 MTRR algorithms
	
	Don't assume 40bits of address space anymore .
	This avoids various problems on Xeons which currently have only 36bits.
	
	We use the tested code from 2.6 as replacement, so this should
	be fairly safe.
	
	Thanks to Philip Pokorny for a lot of investigation on this issue.
	I ended up redoing his patch so all blame to me.
	
	Cc: Philip Pokorny <[email protected]>
	Signed-off-by: Andi Kleen <[email protected]>
	
	Index: linux/arch/x86_64/kernel/mtrr.c
	===================================================================



 mtrr.c |   58 +++++++++++++++++++++++++++-------------------------------
 1 files changed, 27 insertions(+), 31 deletions(-)


diff -Nru a/arch/x86_64/kernel/mtrr.c b/arch/x86_64/kernel/mtrr.c
--- a/arch/x86_64/kernel/mtrr.c	2005-04-07 11:03:49 -07:00
+++ b/arch/x86_64/kernel/mtrr.c	2005-04-07 11:03:49 -07:00
@@ -198,8 +198,7 @@
 
 static void get_mtrr (unsigned int reg, u64 *base, u32 *size, mtrr_type * type)
 {
-	u32 count, tmp, mask_lo, mask_hi;
-	int i;
+	u32 mask_lo, mask_hi;
 	u32 base_lo, base_hi;
 
 	rdmsr (MSR_MTRRphysMask(reg), mask_lo, mask_hi);
@@ -213,22 +212,17 @@
 
 	rdmsr (MSR_MTRRphysBase(reg), base_lo, base_hi);
 
-	count = 0;
-	tmp = mask_lo >> MTRR_BEG_BIT;
-	for (i = MTRR_BEG_BIT; i <= 31; i++, tmp = tmp >> 1)
-		count = (count << (~tmp & 1)) | (~tmp & 1);
-	
-	tmp = mask_hi;
-	for (i = 0; i <= MTRR_END_BIT; i++, tmp = tmp >> 1)
-		count = (count << (~tmp & 1)) | (~tmp & 1);
-	
-	*size = (count+1); 
-	*base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
-	*type = base_lo & 0xff;
+	/* Work out the shifted address mask */
+	mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) | 
+		  mask_lo >> PAGE_SHIFT; 
+
+	/* This works correctly if size is a power of two, i.e. a
+	   continguous range. */
+	*size = -mask_lo;
+	*base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; 
+	*type = base_lo & 0xff; 
 }
 
-
-
 /*
  * Set variable MTRR register on the local CPU.
  *  <reg> The register to set.
@@ -242,8 +236,6 @@
 		   u32 size, mtrr_type type, int do_safe)
 {
 	struct set_mtrr_context ctxt;
-	u64 base64;
-	u64 size64;
 
 	if (do_safe) { 
 		set_mtrr_prepare (&ctxt);
@@ -255,12 +247,10 @@
 		   relevant mask register to disable a range. */
 		wrmsr (MSR_MTRRphysMask(reg), 0, 0);
 	} else {
-		base64 = (base << PAGE_SHIFT) & size_and_mask;
-		wrmsr (MSR_MTRRphysBase(reg), base64 | type, base64 >> 32);
-
-		size64 = ~(((u64)size << PAGE_SHIFT) - 1);
-		size64 = size64 & size_and_mask;
-		wrmsr (MSR_MTRRphysMask(reg), (u32) (size64 | 0x800), (u32) (size64 >> 32));
+		wrmsr (MSR_MTRRphysBase(reg), base << PAGE_SHIFT | type, 
+			(base & size_and_mask) >> (32 - PAGE_SHIFT));
+		wrmsr(MSR_MTRRphysMask(reg), -size << PAGE_SHIFT | 0x800,
+			(-size & size_and_mask) >> (32 - PAGE_SHIFT));
 	}
 	if (do_safe)
 		set_mtrr_done (&ctxt);
@@ -691,13 +681,13 @@
 		return -ENOSYS;
 	}
 
-	if (base & (size_or_mask>>PAGE_SHIFT)) {
+	if (base & size_or_mask) {
 		printk (KERN_WARNING "mtrr: base(%Lx) exceeds the MTRR width(%Lx)\n",
-				base, (size_or_mask>>PAGE_SHIFT));
+				base, size_or_mask);
 		return -EINVAL;
 	}
 
-	if (size & (size_or_mask>>PAGE_SHIFT)) {
+	if (size & size_or_mask) {
 		printk (KERN_WARNING "mtrr: size exceeds the MTRR width\n");
 		return -EINVAL;
 	}
@@ -1281,16 +1271,22 @@
 
 	if (test_bit (X86_FEATURE_MTRR, boot_cpu_data.x86_capability)) {
 		/* Query the width (in bits) of the physical
-		   addressable memory on the Hammer family. */
-		if ((cpuid_eax (0x80000000) >= 0x80000008)) {
+		   addressable memory. This is an AMD specific MSR,
+		   but we assume(hope?) Intel will implement it too
+		   when they extend the width of the Xeon address bus. */
+		if (cpuid_eax (0x80000000) >= 0x80000008) {
 			u32 phys_addr;
 			phys_addr = cpuid_eax (0x80000008) & 0xff;
-			size_or_mask = ~((1L << phys_addr) - 1);
+			size_or_mask = ~((1L << (phys_addr - PAGE_SHIFT)) - 1);
 			/*
 			 * top bits MBZ as its beyond the addressable range.
 			 * bottom bits MBZ as we don't care about lower 12 bits of addr.
 			 */
-			size_and_mask = (~size_or_mask) & 0x000ffffffffff000L;
+			size_and_mask = ~size_or_mask &  0xfff00000;
+		} else {
+			/* 36bit fallback */
+			size_or_mask = 0xff000000;
+			size_and_mask = 0x00f00000;
 		}
 	}
 }