--- 2.3.30.mtrr.c	Wed Dec  8 23:32:09 1999
+++ linux/arch/i386/kernel/mtrr.c	Wed Dec  8 23:29:47 1999
@@ -474,11 +474,113 @@
     return 0;
 }   /*  End Function have_wrcomb  */
 
+static void intel_get_fixed_mtrr (unsigned int reg, unsigned long *base,
+		      unsigned long *size, mtrr_type *type)
+{
+    unsigned long lo, hi, calc_base, calc_size;
+    unsigned long msr_num;
+
+    reg-=get_num_var_ranges();
+
+    if (reg>=NUM_FIXED_RANGES) {
+	/* Invalid num. */
+	*base = 0;
+	*size = 0;
+	*type = 0;
+	return;
+    }
+
+    switch(reg>>3){
+       case 0:
+          msr_num=MTRRfix64K_00000_MSR;
+          calc_base=0;
+          calc_size=65536;
+          break;
+       case 1:
+          msr_num=MTRRfix16K_80000_MSR;
+          calc_base=0x80000;
+          calc_size=16384;
+          break;
+       case 2:
+          msr_num=MTRRfix16K_A0000_MSR;
+          calc_base=0xA0000;
+          calc_size=16384;
+          break;
+       case 3:
+          msr_num=MTRRfix4K_C0000_MSR;
+          calc_base=0xC0000;
+          calc_size=4096;
+          break;
+       case 4:
+          msr_num=MTRRfix4K_C8000_MSR;
+          calc_base=0xC8000;
+          calc_size=4096;
+          break;
+       case 5:
+          msr_num=MTRRfix4K_D0000_MSR;
+          calc_base=0xD0000;
+          calc_size=4096;
+          break;
+       case 6:
+          msr_num=MTRRfix4K_D8000_MSR;
+          calc_base=0xD8000;
+          calc_size=4096;
+          break;
+       case 7:
+          msr_num=MTRRfix4K_E0000_MSR;
+          calc_base=0xE0000;
+          calc_size=4096;
+          break;
+       case 8:
+          msr_num=MTRRfix4K_E8000_MSR;
+          calc_base=0xE8000;
+          calc_size=4096;
+          break;
+       case 9:
+          msr_num=MTRRfix4K_F0000_MSR;
+          calc_base=0xF0000;
+          calc_size=4096;
+          break;
+       case 10:
+          msr_num=MTRRfix4K_F8000_MSR;
+          calc_base=0xF8000;
+          calc_size=4096;
+          break;
+       default:
+          msr_num=0;
+          calc_base=0;
+          calc_size=0;
+          break;
+    };
+
+    if (msr_num==0) {
+	/* Invalid num. */
+	*base = 0;
+	*size = 0;
+	*type = 0;
+	return;
+    }
+
+    rdmsr(msr_num, lo, hi);
+
+    if(reg&4)lo=hi;
+    lo>>=8*(reg&3);
+
+    *type = (lo & 0xff);
+    *size=calc_size;
+    *base=calc_base+(reg&7)*calc_size;
+}   
+
 static void intel_get_mtrr (unsigned int reg, unsigned long *base,
 			    unsigned long *size, mtrr_type *type)
 {
     unsigned long dummy, mask_lo, base_lo;
 
+    if(reg>=get_num_var_ranges()){
+        intel_get_fixed_mtrr(reg,base,size,type);
+        return;
+    };
+
     rdmsr (MTRRphysMask_MSR(reg), mask_lo, dummy);
     if ((mask_lo & 0x800) == 0) {
 	/* Invalid (i.e. free) range. */
@@ -611,6 +713,71 @@
 static void (*get_mtrr) (unsigned int reg, unsigned long *base,
 			 unsigned long *size, mtrr_type *type) = NULL;
 
+static int intel_set_fixed_mtrr (unsigned int reg, unsigned long base,
+		      unsigned long size, mtrr_type type)
+/* currently, ignore size, only set the fixed mtrr that includes base */ 
+{
+    unsigned long lo, hi, mask, calc_size;
+    unsigned long msr_num, tmp_base;
+    int place;
+
+    if((size==0)&&(base==0)&&(type==0)) {
+        int max=get_num_var_ranges ();
+        intel_get_fixed_mtrr(reg,&base,&size,&type);
+        if(reg<max+16)type=6;
+        if(reg>=max+16)type=0;
+    };
+
+    if(size==0) {
+          size=0x1000;
+          type=0;
+    };
+
+    while(size>0)
+    {
+        if (base>=0x100000) return -EFAULT;
+        tmp_base = base >> 12 ;
+    
+        if(tmp_base<128) {
+              msr_num=MTRRfix64K_00000_MSR;
+              place=tmp_base>>4;
+              calc_size=65536;
+        } else
+        if(tmp_base<192) {
+              msr_num=MTRRfix16K_80000_MSR+(tmp_base>=160);
+              place=(tmp_base>>2)&7l;
+              calc_size=16384;
+        } else {
+              msr_num=MTRRfix4K_C0000_MSR+((tmp_base&0x3f)>>3);
+              place=tmp_base&7;
+              calc_size=4096;
+        };
+    
+        rdmsr(msr_num, lo, hi);
+    
+        mask=0xff<<((place&3)<<3);
+    
+        if(place&4) {
+           hi&=~mask;
+           hi|=type<<((place&3)<<3);
+        } else {
+           lo &=~mask;
+           lo|=type<<(place<<3);
+        };
+        
+        wrmsr(msr_num, lo, hi);
+        
+        if(size<calc_size)size=0; 
+        else 
+        {
+           size -= calc_size;
+           base += calc_size;
+        };
+    };
+    
+    return 0;
+}   
+
 static void intel_set_mtrr_up (unsigned int reg, unsigned long base,
 			       unsigned long size, mtrr_type type, int do_safe)
 /*  [SUMMARY] Set variable MTRR register on the local CPU.
@@ -624,15 +791,21 @@
 */
 {
     struct set_mtrr_context ctxt;
+    int max=get_num_var_ranges ();
 
     if (do_safe) set_mtrr_prepare (&ctxt);
-    if (size == 0)
+    if ((size == 0) && (reg<=max))
     {
 	/* The invalid bit is kept in the mask, so we simply clear the
 	   relevant mask register to disable a range. */
 	wrmsr (MTRRphysMask_MSR (reg), 0, 0);
     }
     else
+    if(base<0x100000)
+    {
+        intel_set_fixed_mtrr(reg,base,size,type);
+    }
+    else
     {
 	wrmsr (MTRRphysBase_MSR (reg), base | type, 0);
 	wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0);
@@ -1010,15 +1183,6 @@
 	return;
     }
     for (i = 0; i < max; i++) usage_table[i] = 1;
-#ifdef CONFIG_PROC_FS
-    if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL )
-    {
-	printk ("mtrr: could not allocate\n");
-	return;
-    }
-    ascii_buf_bytes = 0;
-    compute_ascii ();
-#endif
 }   /*  End Function init_table  */
 
 static int generic_get_free_region (unsigned long base, unsigned long size)
@@ -1087,7 +1251,7 @@
     [NOTE] This routine uses a spinlock.
 */
 {
-    int i, max;
+    int i=0, max, fixed=0;
     mtrr_type ltype;
     unsigned long lbase, lsize, last;
 
@@ -1112,6 +1276,12 @@
 	/*  Double check for Intel, we may run on Athlon. */
 	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
 	  /*  For Intel PPro stepping <= 7, must be 4 MiB aligned  */
+          if (base + size < 0x100000)
+          {
+              fixed=1;
+              i=get_num_var_ranges ();
+          }
+          else 
 	  if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
 	       (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) )
 	  {
@@ -1136,7 +1306,7 @@
 		return -EINVAL;
 	    }
 	}
-	else if (base + size < 0x100000)
+	else if (!fixed && (base + size < 0x100000))
 	{
 	    printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
 		    base, size);
@@ -1173,6 +1343,8 @@
     max = get_num_var_ranges ();
     /*  Search for existing MTRR  */
     down(&main_lock);
+    if(!fixed)
+    {
     for (i = 0; i < max; ++i)
     {
 	(*get_mtrr) (i, &lbase, &lsize, &ltype);
@@ -1196,21 +1368,23 @@
 	    return -EINVAL;
 	}
 	if (increment) ++usage_table[i];
-	compute_ascii ();
 	up(&main_lock);
 	return i;
     }
     /*  Search for an empty MTRR  */
     i = (*get_free_region) (base, size);
-    if (i < 0)
+    if ((i < 0) && (base + size >= 0x100000))
     {
-	spin_unlock (&main_lock);
+	up(&main_lock);
 	printk ("mtrr: no more MTRRs available\n");
 	return i;
     }
+    };
     set_mtrr (i, base, size, type);
-    usage_table[i] = 1;
-    compute_ascii ();
+    if(!fixed)
+    {
+       usage_table[i] = 1;
+    };
     up(&main_lock);
     return i;
 }   /*  End Function mtrr_add  */
@@ -1281,7 +1455,6 @@
 	return -EINVAL;
     }
     if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0);
-    compute_ascii ();
     up(&main_lock);
     return reg;
 }   /*  End Function mtrr_del  */
@@ -1326,11 +1499,30 @@
 
 static ssize_t mtrr_read (struct file *file, char *buf, size_t len,
 			  loff_t *ppos)
-{
-    if (*ppos >= ascii_buf_bytes) return 0;
+{   int size;
+
+    size=get_num_var_ranges()+NUM_FIXED_RANGES;
+    if ( ( ascii_buffer = kmalloc (size * LINE_SIZE, GFP_KERNEL) ) == NULL )
+    {
+	printk ("mtrr: could not allocate\n");
+	return 0;
+    }
+    ascii_buf_bytes = 0;
+    compute_ascii ();
+
+    if (*ppos >= ascii_buf_bytes) 
+    {
+        kfree(ascii_buffer);
+        return 0;
+    };
     if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos;
-    if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT;
+    if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) 
+    {
+        kfree(ascii_buffer);
+        return -EFAULT;
+    };
     *ppos += len;
+    kfree(ascii_buffer);
     return len;
 }   /*  End Function mtrr_read  */
 
@@ -1436,7 +1628,9 @@
       case MTRRIOC_GET_ENTRY:
 	if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) )
 	    return -EFAULT;
+/* This check should be done by get_mtrr.
 	if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL;
+*/ 
 	(*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type);
 	gentry.type = type;
 	if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) )
@@ -1523,6 +1717,19 @@
 		 "reg%02i: base=0x%08lx (%4liMB), size=%4li%cB: %s, count=%d\n",
 		 i, base, base>>20, size, factor,
 		 attrib_to_str (type), usage_table[i]);
+	    ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes);
+	}
+    }
+    if(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+    for (i = max; i < max+NUM_FIXED_RANGES; i++)
+    {
+	(*get_mtrr) (i, &base, &size, &type);
+	if(((i<max+16)&&(type!=6))||((i>=max+16)&&(type!=0)))
+        {
+	    sprintf
+		(ascii_buffer + ascii_buf_bytes,
+		 "reg%02i: base=0x%08lx (%4likB), size=%4likB: %s, count=%d\n",
+		 i, base, base>>10, size>>10, attrib_to_str (type), 1);
 	    ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes);
 	}
     }
