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