patch-2.0.36 linux/arch/i386/kernel/mtrr.c
Next file: linux/arch/i386/kernel/setup.c
Previous file: linux/arch/i386/kernel/ksyms.c
Back to the patch index
Back to the overall index
- Lines: 603
- Date:
Sun Nov 15 10:32:46 1998
- Orig file:
v2.0.35/linux/arch/i386/kernel/mtrr.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
@@ -0,0 +1,602 @@
+/*
+ * Read and write Memory Type Range Registers (MTRRs)
+ *
+ * These machine specific registers contain information about
+ * caching of memory regions on Intel processors.
+ *
+ * This code has been derived from pform_mod.c by M. Tisch"auser
+ * (email martin@ikcbarka.fzk.de). Special thanks to mingo for
+ * his hint.
+ *
+ * (c) 1997 M. Ohlenroth <moh@informatik.tu-chemnitz.de>
+ * NO WARRANTY: use this code at your own risk!
+ *
+ * This code is released under the GNU public license version 2 or
+ * later.
+ *
+ * modified to have a /proc/mtrr interface by M. Fr"ohlich, Jan. 1998
+ * <frohlich@na.uni-tuebingen.de>
+ * the user Interface is partly taken form mtrr-patch-v1.5
+ * Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ * The postal address is:
+ * Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mtrr.h>
+
+#define MTRR_CAP 0x0fe /* MTRRcap register */
+#define MTRR_VARIABLE 0x200 /* variable length registers */
+#define MTRR_FIXED64K 0x250 /* fixed size registers 64k */
+#define MTRR_FIXED16K 0x258 /* fixed size registers 16K */
+#define MTRR_FIXED4K 0x268 /* fixed size registers 4K */
+#define MTRR_DEFTYPE 0x2ff /* MTRRdefType register */
+
+/*
+ * data type for the MTRRcap register
+ */
+typedef struct {
+ __u64 VCNT : 8 __attribute__ ((packed)),
+ FIX : 1 __attribute__ ((packed)),
+ __reserved_2 : 1 __attribute__ ((packed)),
+ WC : 1 __attribute__ ((packed)),
+ __reserved_1 : 53 __attribute__ ((packed));
+} MTRRcap_t __attribute__ ((packed));
+
+
+/*
+ * data type for the MTRRdefType register
+ */
+typedef struct {
+ __u64 Type : 8 __attribute__ ((packed)),
+ __reserved_1 : 2 __attribute__ ((packed)),
+ FE : 1 __attribute__ ((packed)),
+ E : 1 __attribute__ ((packed)),
+ __reserved_2 : 52 __attribute__ ((packed));
+} MTRRdefType_t __attribute__ ((packed));
+
+/* FIXME implement the entry struct */
+typedef struct MTRRfix64K_t {
+ __u64 raw;
+} MTRRfix64K_t __attribute__ ((packed));
+
+typedef struct MTRRfix16K_t {
+ __u64 raw;
+} MTRRfix16K_t __attribute__ ((packed));
+
+typedef struct MTRRfix4K_t {
+ __u64 raw;
+} MTRRfix4K_t __attribute__ ((packed));
+
+/*
+ * data type for a pair of variable MTRR registers
+ */
+typedef struct {
+ struct {
+ __u64 Type : 8 __attribute__ ((packed)),
+ __reserved_1 : 4 __attribute__ ((packed)),
+ PhysBase : 24 __attribute__ ((packed)),
+ __reserved_2 : 28 __attribute__ ((packed));
+ } MTRRphysBase __attribute__ ((packed));
+
+ struct {
+ __u64 __reserved_3 : 11 __attribute__ ((packed)),
+ V : 1 __attribute__ ((packed)),
+ PhysMask : 24 __attribute__ ((packed)),
+ __reserved_4 : 28 __attribute__ ((packed));
+ } MTRRphysMask __attribute__ ((packed));
+} MTRRvar_t __attribute__ ((packed));
+
+#define RAW_ACCESS64(data) (*(unsigned long long *)(&(data)))
+
+/*
+ * MTRR configuration struct
+ */
+struct mtrr_cntl_t {
+ MTRRcap_t MTRRcap; /* MTRR capability register */
+ MTRRdefType_t MTRRdefType; /* MTRR default type register */
+ MTRRfix64K_t fixed64; /* fixed length entries (raw data) */
+ MTRRfix16K_t fixed16[2];
+ MTRRfix4K_t fixed4[8];
+ MTRRvar_t variable[0]; /* variable type entries */
+};
+
+static struct mtrr_cntl_t *mtrrcntl = NULL;
+
+/*
+ * Deletes the variable MTRR *MTRRvar
+ */
+static inline void MTRRvar_delete(MTRRvar_t *MTRRvar)
+{
+ RAW_ACCESS64(MTRRvar->MTRRphysBase) = 0;
+ RAW_ACCESS64(MTRRvar->MTRRphysMask) = 0;
+}
+
+
+/*
+ * Sets the variable MTRR *MTRRvar
+ */
+static inline void MTRRvar_set(MTRRvar_t *MTRRvar, unsigned int type,
+ unsigned long base, unsigned long size)
+{
+ unsigned long val;
+ base >>= 12;
+ size >>= 12;
+
+ MTRRvar->MTRRphysBase.Type = type;
+ MTRRvar->MTRRphysBase.PhysBase = base;
+
+ MTRRvar->MTRRphysMask.V = 1;
+ val = 1<<25;
+ while (0 == (val & size)) val |= (val>>1);
+ MTRRvar->MTRRphysMask.PhysMask = val;
+}
+
+
+/*
+ * returns 1 if the variable MTRR entry *MTRRvar is valid, 0 otherwise
+ */
+static inline int MTRRvar_is_valid(const MTRRvar_t *MTRRvar)
+{
+ return MTRRvar->MTRRphysMask.V;
+}
+
+/*
+ * returns the type of the variable MTRR entry *MTRRvar
+ */
+static inline int MTRRvar_get_type(const MTRRvar_t *MTRRvar)
+{
+ return MTRRvar->MTRRphysBase.Type;
+}
+
+/*
+ * returns the base of the variable MTRR entry *MTRRvar
+ */
+static inline unsigned long long MTRRvar_get_base(const MTRRvar_t *MTRRvar)
+{
+ return ((unsigned long long)MTRRvar->MTRRphysBase.PhysBase) << 12;
+}
+
+/*
+ * returns the size of the variable MTRR entry *MTRRvar
+ */
+static inline unsigned long long MTRRvar_get_size(const MTRRvar_t *MTRRvar)
+{
+ if (MTRRvar->MTRRphysMask.PhysMask == 0) {
+ return 0;
+ } else {
+ unsigned long size = 1;
+ const unsigned long Mask = MTRRvar->MTRRphysMask.PhysMask;
+ while (0 == (Mask & size)) size <<= 1;
+ return ((unsigned long long)size) << 12;
+ }
+}
+
+/*
+ * returns the eflags register
+ */
+static inline int read_eflags(void)
+{
+ int ret;
+ asm volatile (
+ "pushfl\n\t"
+ "popl %%eax\n\t"
+ :"=a" (ret)
+ :
+ );
+ return ret;
+}
+
+/*
+ * writes the eflags register
+ */
+static inline void write_eflags(int flag)
+{
+ asm volatile (
+ "pushl %%eax\n\t"
+ "popfl\n\t"
+ :
+ :"a" (flag)
+ );
+}
+
+/*
+ * returns 1 if the mtrr's are supported by the current processor, 0 otherwise
+ */
+static inline int mtrr_detect(void) {
+ unsigned long flags;
+ int eflags;
+ int val;
+
+#define MSR_MASK 0x20
+#define MTRR_MASK 0x1000
+#define CPUID_MASK 0x200000
+ /* this function may be called before the cpu_data array has
+ been initialized */
+ save_flags(flags); sti();
+ eflags = read_eflags();
+ write_eflags(eflags ^ CPUID_MASK);
+ if (!((eflags ^ read_eflags()) & CPUID_MASK)) {
+ write_eflags(eflags);
+ restore_flags(flags);
+ return 0;
+ }
+ write_eflags(eflags);
+ restore_flags(flags);
+
+ /* get the cpuid level */
+ asm volatile (
+ "xorl %%eax,%%eax\n\t"
+ "cpuid"
+ :"=a"(val)::"ebx","ecx","edx"
+ );
+ if (val < 1) return 0;
+ /* get the x86_capability value */
+ asm volatile (
+ "movl $1,%%eax\n\t"
+ "cpuid"
+ :"=d"(val)::"ebx","ecx","eax"
+ );
+ if (!(val & MSR_MASK)) return 0;
+ if (!(val & MTRR_MASK)) return 0;
+
+ return 1;
+#undef MSR_MASK
+#undef MTRR_MASK
+#undef CPUID_MASK
+}
+
+
+/*
+ * reads the mtrr configuration of the actual processor and returns
+ * this configuration on sucess. returns NULL if an error occured or
+ * if mtrr's are not supported.
+ */
+static struct mtrr_cntl_t *read_mtrr_configuration (void) {
+ struct mtrr_cntl_t *mtrrcntl;
+ int i;
+ size_t size;
+ MTRRcap_t MTRRcap;
+
+ if (!mtrr_detect()) {
+ printk("/proc/mtrr: MTRR's are NOT supported\n");
+ return NULL;
+ }
+
+ RAW_ACCESS64(MTRRcap) = rdmsr(MTRR_CAP);
+
+/* #define DUMP_MTRR */
+#ifdef DUMP_MTRR
+ {
+ /* Written for a bugreport to Gigabyte ... */
+ inline void print_msr(int num) {
+ unsigned long long tmp = rdmsr(num);
+ printk("MSR #%#06x: 0x%08lx%08lx\n",
+ num , (unsigned long)(tmp >> 32),
+ (unsigned long)tmp);
+ }
+
+ print_msr(MTRR_CAP);
+ /* all variable type */
+ for (i=0;i < MTRRcap.VCNT;i++) {
+ print_msr(MTRR_VARIABLE+2*i);
+ print_msr(MTRR_VARIABLE+2*i+1);
+ }
+ /* all fixed type */
+ print_msr(MTRR_FIXED64K);
+ print_msr(MTRR_FIXED16K);
+ print_msr(MTRR_FIXED16K+1);
+ for (i=0;i<8;i++)
+ print_msr(MTRR_FIXED4K+i);
+ print_msr(MTRR_DEFTYPE);
+ }
+#endif
+
+ size = sizeof(struct mtrr_cntl_t) + sizeof(MTRRvar_t)*MTRRcap.VCNT;
+ if (NULL == (mtrrcntl = kmalloc(size, GFP_KERNEL))) return NULL;
+ memset(mtrrcntl, 0, size);
+
+ /* read MTRRcap register */
+ mtrrcntl->MTRRcap = MTRRcap;
+
+ /* read MTRRdefType register */
+ RAW_ACCESS64(mtrrcntl->MTRRdefType) = rdmsr(MTRR_DEFTYPE);
+
+ /* read fixed length entries */
+ if (mtrrcntl->MTRRdefType.E && mtrrcntl->MTRRdefType.FE) {
+ mtrrcntl->fixed64.raw = rdmsr(MTRR_FIXED64K);
+ mtrrcntl->fixed16[0].raw = rdmsr(MTRR_FIXED16K);
+ mtrrcntl->fixed16[1].raw = rdmsr(MTRR_FIXED16K+1);
+ for (i=0;i<8;i++)
+ mtrrcntl->fixed4[i].raw = rdmsr(MTRR_FIXED4K+i);
+ }
+
+ /* read variable length entries */
+ if (mtrrcntl->MTRRdefType.E) {
+ const int vcnt = mtrrcntl->MTRRcap.VCNT;
+ for (i = 0 ; i < vcnt ; i++) {
+ RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) =
+ rdmsr(MTRR_VARIABLE + 2*i);
+ RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) =
+ rdmsr(MTRR_VARIABLE + 2*i + 1);
+ }
+ }
+
+ return mtrrcntl;
+}
+
+/*
+ * initializes the global mtrr configuration
+ */
+/*__init_function(void init_mtrr_config(void)) FIXME*/
+void init_mtrr_config(void)
+{
+ mtrrcntl = read_mtrr_configuration();
+}
+
+
+/* write back and invalidate cache */
+static inline void wbinvd(void)
+{
+ asm volatile("wbinvd");
+}
+
+/* flush tlb's */
+static inline void flush__tlb(void)
+{
+ asm volatile (
+ "movl %%cr3, %%eax\n\t"
+ "movl %%eax, %%cr3\n\t"
+ :
+ :
+ : "memory", "eax");
+}
+
+/* clear page global enable and return previous value */
+static inline unsigned long clear_pge(void)
+{
+ unsigned long ret;
+ asm volatile (
+ "movl %%cr4, %%eax\n\t"
+ "movl %%eax, %%edx\n\t"
+ "andl $0x7f, %%edx\n\t"
+ "movl %%edx, %%cr4\n\t"
+ : "=a" (ret)
+ :
+ : "memory", "cc", "eax", "edx");
+ return ret;
+}
+
+/* restores page global enable bit */
+static inline void restore_pge(unsigned long cr4)
+{
+ asm volatile (
+ "movl %0, %%cr4\n\t"
+ :
+ : "r" (cr4)
+ : "memory");
+}
+
+/* ... */
+static inline void disable_cache(void)
+{
+ asm volatile (
+ "movl %%cr0, %%eax\n\t"
+ "orl $0x40000000, %%eax\n\t"
+ "movl %%eax, %%cr0\n\t"
+ :
+ :
+ :"memory", "cc", "eax");
+}
+
+/* ... */
+static inline void enable_cache(void)
+{
+ asm volatile (
+ "movl %%cr0, %%eax\n\t"
+ "andl $0xbfffffff, %%eax\n\t"
+ "movl %%eax, %%cr0"
+ :
+ :
+ :"memory", "cc", "eax");
+}
+
+/* clear the MTRRdefType.E and MTRRdefType.FE flag to disable these MTRR's */
+static inline void disable_mtrr(void)
+{
+ MTRRdefType_t MTRRdefType;
+
+ RAW_ACCESS64(MTRRdefType) = rdmsr(MTRR_DEFTYPE);
+ MTRRdefType.E = 0;
+ MTRRdefType.FE = 0;
+ wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
+}
+
+/*
+ * written from pseudocode from intel
+ * (PentiumPro Family Developers manual Volume 3, P 322)
+ *
+ */
+static inline unsigned long pre_mtrr_change(void)
+{
+ unsigned long cr4;
+
+ cr4 = clear_pge();
+
+ wbinvd();
+
+ disable_cache();
+
+ wbinvd();
+
+ flush__tlb();
+
+ disable_mtrr();
+
+ return cr4;
+}
+
+/*
+ * written from pseudocode from intel
+ * (PentiumPro Family Developers manual Volume 3, P 322)
+ */
+static inline void post_mtrr_change(MTRRdefType_t MTRRdefType,unsigned long cr4)
+{
+ wbinvd();
+
+ flush__tlb();
+
+ wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
+
+ enable_cache();
+
+ restore_pge(cr4);
+}
+
+/*
+ * writes all fixed mtrr's
+ */
+static inline void set_mtrr_fixed(void) {
+ int i;
+
+ wrmsr(MTRR_FIXED64K,mtrrcntl->fixed64.raw);
+ wrmsr(MTRR_FIXED16K+0,mtrrcntl->fixed16[0].raw);
+ wrmsr(MTRR_FIXED16K+1,mtrrcntl->fixed16[1].raw);
+ for (i=0;i<8;i++)
+ wrmsr(MTRR_FIXED4K+i,mtrrcntl->fixed4[i].raw);
+}
+
+/*
+ * writes all variable mtrr's
+ */
+static inline void set_mtrr_variable(void) {
+ int i;
+ const int vcnt = mtrrcntl->MTRRcap.VCNT;
+
+ for (i = 0 ; i < vcnt ; i++ ) {
+ wrmsr(MTRR_VARIABLE +2*i,
+ RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase));
+ wrmsr(MTRR_VARIABLE +2*i+1,
+ RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask));
+ }
+}
+
+
+/*
+ * compares the mtrr_cntl_t structure second with that set by
+ * the boot processor,
+ * returns 0 if equal,
+ * -1 if the structs are not initialized,
+ * 1 if they are different
+ *
+ * the *second struct is assumed to be local, it is not locked!
+ */
+static inline int compare_mtrr_configuration(struct mtrr_cntl_t *second) {
+ int i, result = 0;
+
+ if (NULL == mtrrcntl) { result = -1; goto end; }
+ if (NULL == second) { result = -1; goto end; }
+ if (RAW_ACCESS64(mtrrcntl->MTRRcap)
+ != RAW_ACCESS64(second->MTRRcap)) {
+ result = 1; goto end;
+ }
+
+ if (RAW_ACCESS64(mtrrcntl->MTRRdefType)
+ != RAW_ACCESS64(second->MTRRdefType)) {
+ result = 1; goto end;
+ }
+
+ if (mtrrcntl->fixed64.raw != second->fixed64.raw) {
+ result = 1; goto end;
+ }
+ if (mtrrcntl->fixed16[0].raw != second->fixed16[0].raw) {
+ result = 1; goto end;
+ }
+ if (mtrrcntl->fixed16[1].raw != second->fixed16[1].raw) {
+ result = 1; goto end;
+ }
+
+ for (i=0;i<8;i++)
+ if (mtrrcntl->fixed4[i].raw != second->fixed4[i].raw) {
+ result = 1; goto end;
+ }
+ {
+ const int vcnt = mtrrcntl->MTRRcap.VCNT;
+ for (i = 0; i < vcnt; i++) {
+ if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) !=
+ RAW_ACCESS64(second->variable[i].MTRRphysBase)) {
+ result = 1; goto end;
+ }
+ if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) !=
+ RAW_ACCESS64(second->variable[i].MTRRphysMask)) {
+ result = 1; goto end;
+ }
+ }
+ }
+
+ end:
+
+ return result;
+}
+
+/*
+ * compares the mtrr configuration of the current processor with the
+ * main configuration and overwrites the mtrr's in the processor if they
+ * differ. (fixes a bug in the GA-686DX mainboard BIOS)
+ */
+void check_mtrr_config(void) {
+ unsigned long cr4;
+ unsigned long flags;
+ struct mtrr_cntl_t *this_cpu_setting;
+ int result;
+
+ save_flags(flags); sti();
+
+ /* if global struct is not initialized return */
+ if (mtrrcntl == NULL) {
+ restore_flags(flags);
+ return;
+ }
+
+ /* disable MTRR feature if this_cpu_setting == NULL */
+ /* read mtrr configuration of this cpu */
+ this_cpu_setting = read_mtrr_configuration();
+ if (this_cpu_setting == NULL) {
+ printk("/proc/mtrr: MTRR's are NOT supported by cpu %i.\n",
+ smp_processor_id());
+ restore_flags(flags);
+
+ return;
+ }
+ /* compare mtrr configuration */
+ result = compare_mtrr_configuration(this_cpu_setting);
+ kfree(this_cpu_setting);
+ /* return if mtrr setting is correct */
+ if (0 >= result) {
+ restore_flags(flags);
+ return;
+ }
+
+ /* prepare cpu's for setting mtrr's */
+ cr4 = pre_mtrr_change();
+
+ /* set all mtrr's */
+ set_mtrr_fixed();
+ set_mtrr_variable();
+
+ /* prepare cpu's for running */
+ post_mtrr_change(mtrrcntl->MTRRdefType, cr4);
+
+ restore_flags(flags);
+
+ printk("\nBIOS bug workaround: MTRR configuration changed on cpu "
+ "%i.\n", smp_processor_id());
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov