📄 mtrr.c
字号:
/* x86-64 MTRR (Memory Type Range Register) driver. Based largely upon arch/i386/kernel/mtrr.c Copyright (C) 1997-2000 Richard Gooch Copyright (C) 2002 Dave Jones. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. (For earlier history, see arch/i386/kernel/mtrr.c) v2.00 September 2001 Dave Jones <davej@suse.de> Initial rewrite for x86-64. Removal of non-Intel style MTRR code. v2.01 June 2002 Dave Jones <davej@suse.de> Removal of redundant abstraction layer. 64-bit fixes. v2.02 July 2002 Dave Jones <davej@suse.de> Fix gentry inconsistencies between kernel/userspace. More casts to clean up warnings.*/#include <linux/types.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/timer.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/wait.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/fs.h>#include <linux/ctype.h>#include <linux/proc_fs.h>#include <linux/devfs_fs_kernel.h>#include <linux/mm.h>#include <linux/module.h>#define MTRR_NEED_STRINGS#include <asm/mtrr.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/agp_backend.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/processor.h>#include <asm/system.h>#include <asm/pgtable.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/atomic.h>#include <asm/msr.h>#include <asm/hardirq.h>#include <linux/irq.h>#define MTRR_VERSION "2.02 (20020716)"#undef Dprintk#define Dprintk(...) #define TRUE 1#define FALSE 0#define MSR_MTRRphysBase(reg) (0x200 + 2 * (reg))#define MSR_MTRRphysMask(reg) (0x200 + 2 * (reg) + 1)#define NUM_FIXED_RANGES 88#define MTRR_CHANGE_MASK_FIXED 0x01#define MTRR_CHANGE_MASK_VARIABLE 0x02#define MTRR_CHANGE_MASK_DEFTYPE 0x04typedef u8 mtrr_type;#define LINE_SIZE 80#ifdef CONFIG_SMP#define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type)#else#define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type, TRUE)#endif#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS)#define USERSPACE_INTERFACE#endif#ifdef USERSPACE_INTERFACEstatic char *ascii_buffer;static unsigned int ascii_buf_bytes;static void compute_ascii (void);#else#define compute_ascii() while (0)#endifstatic unsigned int *usage_table;static DECLARE_MUTEX (mtrr_lock);struct set_mtrr_context { u32 deftype_lo; u32 deftype_hi; unsigned long flags; u64 cr4val;};/* Put the processor into a state where MTRRs can be safely set */static void set_mtrr_prepare (struct set_mtrr_context *ctxt){ u64 cr0; /* Disable interrupts locally */ __save_flags(ctxt->flags); __cli(); /* Save value of CR4 and clear Page Global Enable (bit 7) */ if (test_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability)) { ctxt->cr4val = read_cr4(); write_cr4(ctxt->cr4val & ~(1UL << 7)); } /* Disable and flush caches. Note that wbinvd flushes the TLBs as a side-effect */ cr0 = read_cr0() | 0x40000000; wbinvd(); write_cr0(cr0); wbinvd(); /* Disable MTRRs, and set the default type to uncached */ rdmsr(MSR_MTRRdefType, ctxt->deftype_lo, ctxt->deftype_hi); wrmsr(MSR_MTRRdefType, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);}/* Restore the processor after a set_mtrr_prepare */static void set_mtrr_done (struct set_mtrr_context *ctxt){ /* Flush caches and TLBs */ wbinvd(); /* Restore MTRRdefType */ wrmsr(MSR_MTRRdefType, ctxt->deftype_lo, ctxt->deftype_hi); /* Enable caches */ write_cr0(read_cr0() & 0xbfffffff); /* Restore value of CR4 */ if (test_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability)) write_cr4 (ctxt->cr4val); /* Re-enable interrupts locally (if enabled previously) */ __restore_flags(ctxt->flags);}/* This function returns the number of variable MTRRs */static unsigned int get_num_var_ranges (void){ u32 config, dummy; rdmsr (MSR_MTRRcap, config, dummy); return (config & 0xff);}/* Returns non-zero if we have the write-combining memory type */static int have_wrcomb (void){ u32 config, dummy; rdmsr (MSR_MTRRcap, config, dummy); return (config & (1 << 10));}static u64 size_or_mask, size_and_mask;static void get_mtrr (unsigned int reg, u64 *base, u32 *size, mtrr_type * type){ u32 mask_lo, mask_hi, base_lo, base_hi; u64 newsize; rdmsr (MSR_MTRRphysMask(reg), mask_lo, mask_hi); if ((mask_lo & 0x800) == 0) { /* Invalid (i.e. free) range */ *base = 0; *size = 0; *type = 0; return; } rdmsr (MSR_MTRRphysBase(reg), base_lo, base_hi); /* Work out the shifted address mask. */ newsize = (u64) mask_hi << 32 | (mask_lo & ~0x800); newsize = ~newsize+1; *size = (u32) newsize >> PAGE_SHIFT; *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. * <base> The base address of the region. * <size> The size of the region. If this is 0 the region is disabled. * <type> The type of the region. * <do_safe> If TRUE, do the change safely. If FALSE, safety measures should * be done externally. */static void set_mtrr_up (unsigned int reg, u64 base, u32 size, mtrr_type type, int do_safe){ struct set_mtrr_context ctxt; u64 base64; u64 size64; if (do_safe) set_mtrr_prepare (&ctxt); if (size == 0) { /* The invalid bit is kept in the mask, so we simply clear the 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 = ~((size << PAGE_SHIFT) - 1); size64 = size64 & size_and_mask; wrmsr (MSR_MTRRphysMask(reg), (u32) (size64 | 0x800), (u32) (size64 >> 32)); } if (do_safe) set_mtrr_done (&ctxt);}#ifdef CONFIG_SMPstruct mtrr_var_range { u32 base_lo; u32 base_hi; u32 mask_lo; u32 mask_hi;};/* Get the MSR pair relating to a var range */static void __init get_mtrr_var_range (unsigned int index, struct mtrr_var_range *vr){ rdmsr (MSR_MTRRphysBase(index), vr->base_lo, vr->base_hi); rdmsr (MSR_MTRRphysMask(index), vr->mask_lo, vr->mask_hi);}/* Set the MSR pair relating to a var range. Returns TRUE if changes are made */static int __init set_mtrr_var_range_testing (unsigned int index, struct mtrr_var_range *vr){ u32 lo, hi; int changed = FALSE; rdmsr (MSR_MTRRphysBase(index), lo, hi); if ((vr->base_lo & 0xfffff0ff) != (lo & 0xfffff0ff) || (vr->base_hi & 0x000fffff) != (hi & 0x000fffff)) { wrmsr (MSR_MTRRphysBase(index), vr->base_lo, vr->base_hi); changed = TRUE; } rdmsr (MSR_MTRRphysMask(index), lo, hi); if ((vr->mask_lo & 0xfffff800) != (lo & 0xfffff800) || (vr->mask_hi & 0x000fffff) != (hi & 0x000fffff)) { wrmsr (MSR_MTRRphysMask(index), vr->mask_lo, vr->mask_hi); changed = TRUE; } return changed;}static void __init get_fixed_ranges (mtrr_type * frs){ u32 *p = (u32 *) frs; int i; rdmsr (MSR_MTRRfix64K_00000, p[0], p[1]); for (i = 0; i < 2; i++) rdmsr (MSR_MTRRfix16K_80000 + i, p[2 + i * 2], p[3 + i * 2]); for (i = 0; i < 8; i++) rdmsr (MSR_MTRRfix4K_C0000 + i, p[6 + i * 2], p[7 + i * 2]);}static int __init set_fixed_ranges_testing (mtrr_type * frs){ u32 *p = (u32 *) frs; int changed = FALSE; int i; u32 lo, hi; Dprintk (KERN_INFO "mtrr: rdmsr 64K_00000\n"); rdmsr (MSR_MTRRfix64K_00000, lo, hi); if (p[0] != lo || p[1] != hi) { Dprintk (KERN_INFO "mtrr: Writing %x:%x to 64K MSR. lohi were %x:%x\n", p[0], p[1], lo, hi); wrmsr (MSR_MTRRfix64K_00000, p[0], p[1]); changed = TRUE; } Dprintk (KERN_INFO "mtrr: rdmsr 16K_80000\n"); for (i = 0; i < 2; i++) { rdmsr (MSR_MTRRfix16K_80000 + i, lo, hi); if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) { Dprintk (KERN_INFO "mtrr: Writing %x:%x to 16K MSR%d. lohi were %x:%x\n", p[2 + i * 2], p[3 + i * 2], i, lo, hi ); wrmsr (MSR_MTRRfix16K_80000 + i, p[2 + i * 2], p[3 + i * 2]); changed = TRUE; } } Dprintk (KERN_INFO "mtrr: rdmsr 4K_C0000\n"); for (i = 0; i < 8; i++) { rdmsr (MSR_MTRRfix4K_C0000 + i, lo, hi); Dprintk (KERN_INFO "mtrr: MTRRfix4K_C0000+%d = %x:%x\n", i, lo, hi); if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) { Dprintk (KERN_INFO "mtrr: Writing %x:%x to 4K MSR%d. lohi were %x:%x\n", p[6 + i * 2], p[7 + i * 2], i, lo, hi); wrmsr (MSR_MTRRfix4K_C0000 + i, p[6 + i * 2], p[7 + i * 2]); changed = TRUE; } } return changed;}struct mtrr_state { unsigned int num_var_ranges; struct mtrr_var_range *var_ranges; mtrr_type fixed_ranges[NUM_FIXED_RANGES]; mtrr_type def_type; unsigned char enabled;};/* Grab all of the MTRR state for this CPU into *state */static void __init get_mtrr_state (struct mtrr_state *state){ unsigned int nvrs, i; struct mtrr_var_range *vrs; u32 lo, dummy; nvrs = state->num_var_ranges = get_num_var_ranges(); vrs = state->var_ranges = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); if (vrs == NULL) nvrs = state->num_var_ranges = 0; for (i = 0; i < nvrs; i++) get_mtrr_var_range (i, &vrs[i]); get_fixed_ranges (state->fixed_ranges); rdmsr (MSR_MTRRdefType, lo, dummy); state->def_type = (lo & 0xff); state->enabled = (lo & 0xc00) >> 10;}/* Free resources associated with a struct mtrr_state */static void __init finalize_mtrr_state (struct mtrr_state *state){ if (state->var_ranges) kfree (state->var_ranges);}/* * Set the MTRR state for this CPU. * <state> The MTRR state information to read. * <ctxt> Some relevant CPU context. * [NOTE] The CPU must already be in a safe state for MTRR changes. * [RETURNS] 0 if no changes made, else a mask indication what was changed. */static u64 __init set_mtrr_state (struct mtrr_state *state, struct set_mtrr_context *ctxt){ unsigned int i; u64 change_mask = 0; for (i = 0; i < state->num_var_ranges; i++) if (set_mtrr_var_range_testing (i, &state->var_ranges[i])) change_mask |= MTRR_CHANGE_MASK_VARIABLE; if (set_fixed_ranges_testing (state->fixed_ranges)) change_mask |= MTRR_CHANGE_MASK_FIXED; /* Set_mtrr_restore restores the old value of MTRRdefType, so to set it we fiddle with the saved value */ if ((ctxt->deftype_lo & 0xff) != state->def_type || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) { ctxt->deftype_lo |= (state->def_type | state->enabled << 10); change_mask |= MTRR_CHANGE_MASK_DEFTYPE; } return change_mask;}static atomic_t undone_count;static volatile int wait_barrier_execute = FALSE;static volatile int wait_barrier_cache_enable = FALSE;struct set_mtrr_data { u64 smp_base; u32 smp_size; unsigned int smp_reg; mtrr_type smp_type;};/* * Synchronisation handler. Executed by "other" CPUs. */static void ipi_handler (void *info){ struct set_mtrr_data *data = info; struct set_mtrr_context ctxt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -