linux/arch/i386/kernel/cpu/mtrr/generic.c
<<
>>
Prefs
   1/* This only handles 32bit MTRR on 32bit hosts. This is strictly wrong
   2   because MTRRs can span upto 40 bits (36bits on most modern x86) */ 
   3#include <linux/init.h>
   4#include <linux/slab.h>
   5#include <linux/mm.h>
   6#include <asm/io.h>
   7#include <asm/mtrr.h>
   8#include <asm/msr.h>
   9#include <asm/system.h>
  10#include <asm/cpufeature.h>
  11#include <asm/tlbflush.h>
  12#include "mtrr.h"
  13
  14struct mtrr_state {
  15        struct mtrr_var_range *var_ranges;
  16        mtrr_type fixed_ranges[NUM_FIXED_RANGES];
  17        unsigned char enabled;
  18        mtrr_type def_type;
  19};
  20
  21static unsigned long smp_changes_mask;
  22struct mtrr_state mtrr_state = {};
  23
  24
  25/*  Get the MSR pair relating to a var range  */
  26static void __init
  27get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr)
  28{
  29        rdmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
  30        rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
  31}
  32
  33static void __init
  34get_fixed_ranges(mtrr_type * frs)
  35{
  36        unsigned int *p = (unsigned int *) frs;
  37        int i;
  38
  39        rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
  40
  41        for (i = 0; i < 2; i++)
  42                rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]);
  43        for (i = 0; i < 8; i++)
  44                rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]);
  45}
  46
  47/*  Grab all of the MTRR state for this CPU into *state  */
  48void __init get_mtrr_state(void)
  49{
  50        unsigned int i;
  51        struct mtrr_var_range *vrs;
  52        unsigned lo, dummy;
  53
  54        if (!mtrr_state.var_ranges) {
  55                mtrr_state.var_ranges = kmalloc(num_var_ranges * sizeof (struct mtrr_var_range), 
  56                                                GFP_KERNEL);
  57                if (!mtrr_state.var_ranges)
  58                        return;
  59        } 
  60        vrs = mtrr_state.var_ranges;
  61
  62        for (i = 0; i < num_var_ranges; i++)
  63                get_mtrr_var_range(i, &vrs[i]);
  64        get_fixed_ranges(mtrr_state.fixed_ranges);
  65
  66        rdmsr(MTRRdefType_MSR, lo, dummy);
  67        mtrr_state.def_type = (lo & 0xff);
  68        mtrr_state.enabled = (lo & 0xc00) >> 10;
  69}
  70
  71/*  Free resources associated with a struct mtrr_state  */
  72void __init finalize_mtrr_state(void)
  73{
  74        if (mtrr_state.var_ranges)
  75                kfree(mtrr_state.var_ranges);
  76        mtrr_state.var_ranges = NULL;
  77}
  78
  79/*  Some BIOS's are fucked and don't set all MTRRs the same!  */
  80void __init mtrr_state_warn(void)
  81{
  82        unsigned long mask = smp_changes_mask;
  83
  84        if (!mask)
  85                return;
  86        if (mask & MTRR_CHANGE_MASK_FIXED)
  87                printk(KERN_WARNING "mtrr: your CPUs had inconsistent fixed MTRR settings\n");
  88        if (mask & MTRR_CHANGE_MASK_VARIABLE)
  89                printk(KERN_WARNING "mtrr: your CPUs had inconsistent variable MTRR settings\n");
  90        if (mask & MTRR_CHANGE_MASK_DEFTYPE)
  91                printk(KERN_WARNING "mtrr: your CPUs had inconsistent MTRRdefType settings\n");
  92        printk(KERN_INFO "mtrr: probably your BIOS does not setup all CPUs.\n");
  93        printk(KERN_INFO "mtrr: corrected configuration.\n");
  94}
  95
  96
  97int generic_get_free_region(unsigned long base, unsigned long size)
  98/*  [SUMMARY] Get a free MTRR.
  99    <base> The starting (base) address of the region.
 100    <size> The size (in bytes) of the region.
 101    [RETURNS] The index of the region on success, else -1 on error.
 102*/
 103{
 104        int i, max;
 105        mtrr_type ltype;
 106        unsigned long lbase;
 107        unsigned lsize;
 108
 109        max = num_var_ranges;
 110        for (i = 0; i < max; ++i) {
 111                mtrr_if->get(i, &lbase, &lsize, &ltype);
 112                if (lsize == 0)
 113                        return i;
 114        }
 115        return -ENOSPC;
 116}
 117
 118void generic_get_mtrr(unsigned int reg, unsigned long *base,
 119                      unsigned int *size, mtrr_type * type)
 120{
 121        unsigned int mask_lo, mask_hi, base_lo, base_hi;
 122
 123        rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi);
 124        if ((mask_lo & 0x800) == 0) {
 125                /*  Invalid (i.e. free) range  */
 126                *base = 0;
 127                *size = 0;
 128                *type = 0;
 129                return;
 130        }
 131
 132        rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi);
 133
 134        /* Work out the shifted address mask. */
 135        mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT)
 136            | mask_lo >> PAGE_SHIFT;
 137
 138        /* This works correctly if size is a power of two, i.e. a
 139           contiguous range. */
 140        *size = -mask_lo;
 141        *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
 142        *type = base_lo & 0xff;
 143}
 144
 145static int set_fixed_ranges(mtrr_type * frs)
 146{
 147        unsigned int *p = (unsigned int *) frs;
 148        int changed = FALSE;
 149        int i;
 150        unsigned int lo, hi;
 151
 152        rdmsr(MTRRfix64K_00000_MSR, lo, hi);
 153        if (p[0] != lo || p[1] != hi) {
 154                wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
 155                changed = TRUE;
 156        }
 157
 158        for (i = 0; i < 2; i++) {
 159                rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
 160                if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) {
 161                        wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2],
 162                              p[3 + i * 2]);
 163                        changed = TRUE;
 164                }
 165        }
 166
 167        for (i = 0; i < 8; i++) {
 168                rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
 169                if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) {
 170                        wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2],
 171                              p[7 + i * 2]);
 172                        changed = TRUE;
 173                }
 174        }
 175        return changed;
 176}
 177
 178/*  Set the MSR pair relating to a var range. Returns TRUE if
 179    changes are made  */
 180static int set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
 181{
 182        unsigned int lo, hi;
 183        int changed = FALSE;
 184
 185        rdmsr(MTRRphysBase_MSR(index), lo, hi);
 186        if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
 187            || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
 188                wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
 189                changed = TRUE;
 190        }
 191
 192        rdmsr(MTRRphysMask_MSR(index), lo, hi);
 193
 194        if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
 195            || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
 196                wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
 197                changed = TRUE;
 198        }
 199        return changed;
 200}
 201
 202static unsigned long set_mtrr_state(u32 deftype_lo, u32 deftype_hi)
 203/*  [SUMMARY] Set the MTRR state for this CPU.
 204    <state> The MTRR state information to read.
 205    <ctxt> Some relevant CPU context.
 206    [NOTE] The CPU must already be in a safe state for MTRR changes.
 207    [RETURNS] 0 if no changes made, else a mask indication what was changed.
 208*/
 209{
 210        unsigned int i;
 211        unsigned long change_mask = 0;
 212
 213        for (i = 0; i < num_var_ranges; i++)
 214                if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i]))
 215                        change_mask |= MTRR_CHANGE_MASK_VARIABLE;
 216
 217        if (set_fixed_ranges(mtrr_state.fixed_ranges))
 218                change_mask |= MTRR_CHANGE_MASK_FIXED;
 219
 220        /*  Set_mtrr_restore restores the old value of MTRRdefType,
 221           so to set it we fiddle with the saved value  */
 222        if ((deftype_lo & 0xff) != mtrr_state.def_type
 223            || ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) {
 224                deftype_lo |= (mtrr_state.def_type | mtrr_state.enabled << 10);
 225                change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
 226        }
 227
 228        return change_mask;
 229}
 230
 231
 232static unsigned long cr4 = 0;
 233static u32 deftype_lo, deftype_hi;
 234static DEFINE_SPINLOCK(set_atomicity_lock);
 235
 236/*
 237 * Since we are disabling the cache don't allow any interrupts - they
 238 * would run extremely slow and would only increase the pain.  The caller must
 239 * ensure that local interrupts are disabled and are reenabled after post_set()
 240 * has been called.
 241 */
 242
 243static void prepare_set(void)
 244{
 245        unsigned long cr0;
 246
 247        /*  Note that this is not ideal, since the cache is only flushed/disabled
 248           for this CPU while the MTRRs are changed, but changing this requires
 249           more invasive changes to the way the kernel boots  */
 250
 251        spin_lock(&set_atomicity_lock);
 252
 253        /*  Enter the no-fill (CD=1, NW=0) cache mode and flush caches. */
 254        cr0 = read_cr0() | 0x40000000;  /* set CD flag */
 255        write_cr0(cr0);
 256        wbinvd();
 257
 258        /*  Save value of CR4 and clear Page Global Enable (bit 7)  */
 259        if ( cpu_has_pge ) {
 260                cr4 = read_cr4();
 261                write_cr4(cr4 & ~X86_CR4_PGE);
 262        }
 263
 264        /* Flush all TLBs via a mov %cr3, %reg; mov %reg, %cr3 */
 265        __flush_tlb();
 266
 267        /*  Save MTRR state */
 268        rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);
 269
 270        /*  Disable MTRRs, and set the default type to uncached  */
 271        wrmsr(MTRRdefType_MSR, deftype_lo & 0xf300UL, deftype_hi);
 272}
 273
 274static void post_set(void)
 275{
 276        /*  Flush TLBs (no need to flush caches - they are disabled)  */
 277        __flush_tlb();
 278
 279        /* Intel (P6) standard MTRRs */
 280        wrmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);
 281                
 282        /*  Enable caches  */
 283        write_cr0(read_cr0() & 0xbfffffff);
 284
 285        /*  Restore value of CR4  */
 286        if ( cpu_has_pge )
 287                write_cr4(cr4);
 288        spin_unlock(&set_atomicity_lock);
 289}
 290
 291static void generic_set_all(void)
 292{
 293        unsigned long mask, count;
 294        unsigned long flags;
 295
 296        local_irq_save(flags);
 297        prepare_set();
 298
 299        /* Actually set the state */
 300        mask = set_mtrr_state(deftype_lo,deftype_hi);
 301
 302        post_set();
 303        local_irq_restore(flags);
 304
 305        /*  Use the atomic bitops to update the global mask  */
 306        for (count = 0; count < sizeof mask * 8; ++count) {
 307                if (mask & 0x01)
 308                        set_bit(count, &smp_changes_mask);
 309                mask >>= 1;
 310        }
 311        
 312}
 313
 314static void generic_set_mtrr(unsigned int reg, unsigned long base,
 315                             unsigned long size, mtrr_type type)
 316/*  [SUMMARY] Set variable MTRR register on the local CPU.
 317    <reg> The register to set.
 318    <base> The base address of the region.
 319    <size> The size of the region. If this is 0 the region is disabled.
 320    <type> The type of the region.
 321    <do_safe> If TRUE, do the change safely. If FALSE, safety measures should
 322    be done externally.
 323    [RETURNS] Nothing.
 324*/
 325{
 326        unsigned long flags;
 327
 328        local_irq_save(flags);
 329        prepare_set();
 330
 331        if (size == 0) {
 332                /* The invalid bit is kept in the mask, so we simply clear the
 333                   relevant mask register to disable a range. */
 334                wrmsr(MTRRphysMask_MSR(reg), 0, 0);
 335        } else {
 336                wrmsr(MTRRphysBase_MSR(reg), base << PAGE_SHIFT | type,
 337                      (base & size_and_mask) >> (32 - PAGE_SHIFT));
 338                wrmsr(MTRRphysMask_MSR(reg), -size << PAGE_SHIFT | 0x800,
 339                      (-size & size_and_mask) >> (32 - PAGE_SHIFT));
 340        }
 341
 342        post_set();
 343        local_irq_restore(flags);
 344}
 345
 346int generic_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
 347{
 348        unsigned long lbase, last;
 349
 350        /*  For Intel PPro stepping <= 7, must be 4 MiB aligned 
 351            and not touch 0x70000000->0x7003FFFF */
 352        if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 &&
 353            boot_cpu_data.x86_model == 1 &&
 354            boot_cpu_data.x86_mask <= 7) {
 355                if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) {
 356                        printk(KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base);
 357                        return -EINVAL;
 358                }
 359                if (!(base + size < 0x70000000 || base > 0x7003FFFF) &&
 360                    (type == MTRR_TYPE_WRCOMB
 361                     || type == MTRR_TYPE_WRBACK)) {
 362                        printk(KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n");
 363                        return -EINVAL;
 364                }
 365        }
 366
 367        if (base + size < 0x100) {
 368                printk(KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n",
 369                       base, size);
 370                return -EINVAL;
 371        }
 372        /*  Check upper bits of base and last are equal and lower bits are 0
 373            for base and 1 for last  */
 374        last = base + size - 1;
 375        for (lbase = base; !(lbase & 1) && (last & 1);
 376             lbase = lbase >> 1, last = last >> 1) ;
 377        if (lbase != last) {
 378                printk(KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n",
 379                       base, size);
 380                return -EINVAL;
 381        }
 382        return 0;
 383}
 384
 385
 386int generic_have_wrcomb(void)
 387{
 388        unsigned long config, dummy;
 389        rdmsr(MTRRcap_MSR, config, dummy);
 390        return (config & (1 << 10));
 391}
 392
 393int positive_have_wrcomb(void)
 394{
 395        return 1;
 396}
 397
 398/* generic structure...
 399 */
 400struct mtrr_ops generic_mtrr_ops = {
 401        .use_intel_if      = 1,
 402        .set_all           = generic_set_all,
 403        .get               = generic_get_mtrr,
 404        .get_free_region   = generic_get_free_region,
 405        .set               = generic_set_mtrr,
 406        .validate_add_page = generic_validate_add_page,
 407        .have_wrcomb       = generic_have_wrcomb,
 408};
 409
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.