mtrr.c
来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 1,361 行 · 第 1/3 页
C
1,361 行
/* 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) September 2001 Dave Jones <davej@suse.de> Initial rewrite for x86-64.*/#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 <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.00 (20020207)"#define TRUE 1#define FALSE 0#define MTRRcap_MSR 0x0fe#define MTRRdefType_MSR 0x2ff#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg))#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1)#define NUM_FIXED_RANGES 88#define MTRRfix64K_00000_MSR 0x250#define MTRRfix16K_80000_MSR 0x258#define MTRRfix16K_A0000_MSR 0x259#define MTRRfix4K_C0000_MSR 0x268#define MTRRfix4K_C8000_MSR 0x269#define MTRRfix4K_D0000_MSR 0x26a#define MTRRfix4K_D8000_MSR 0x26b#define MTRRfix4K_E0000_MSR 0x26c#define MTRRfix4K_E8000_MSR 0x26d#define MTRRfix4K_F0000_MSR 0x26e#define MTRRfix4K_F8000_MSR 0x26f#ifdef CONFIG_SMP#define MTRR_CHANGE_MASK_FIXED 0x01#define MTRR_CHANGE_MASK_VARIABLE 0x02#define MTRR_CHANGE_MASK_DEFTYPE 0x04#endiftypedef 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#ifndef USERSPACE_INTERFACE#define compute_ascii() while (0)#endif#ifdef USERSPACE_INTERFACEstatic char *ascii_buffer;static unsigned int ascii_buf_bytes;#endifstatic unsigned int *usage_table;static DECLARE_MUTEX (main_lock);/* Private functions */#ifdef USERSPACE_INTERFACEstatic void compute_ascii (void);#endifstruct set_mtrr_context { unsigned long flags; unsigned long deftype_lo; unsigned long deftype_hi; unsigned long cr4val;};/* Put the processor into a state where MTRRs can be safely set */static void set_mtrr_prepare (struct set_mtrr_context *ctxt){ unsigned long 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(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); wrmsr(MTRRdefType_MSR, 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(MTRRdefType_MSR, 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){ unsigned long config, dummy; rdmsr (MTRRcap_MSR, config, dummy); return (config & 0xff);}/* Returns non-zero if we have the write-combining memory type */static int have_wrcomb (void){ unsigned long config, dummy; rdmsr (MTRRcap_MSR, config, dummy); return (config & (1 << 10));}static u32 size_or_mask, size_and_mask;static void get_mtrr (unsigned int reg, unsigned long *base, unsigned long *size, mtrr_type * type){ unsigned long mask_lo, mask_hi, base_lo, base_hi; rdmsr (MTRRphysMask_MSR (reg), mask_lo, mask_hi); if ((mask_lo & 0x800) == 0) { /* Invalid (i.e. free) range */ *base = 0; *size = 0; *type = 0; return; } rdmsr (MTRRphysBase_MSR (reg), base_lo, base_hi); /* Work out the shifted address mask. */ mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) | mask_lo >> PAGE_SHIFT; /* This works correctly if size is a power of two, i.e. a contiguous range. */ *size = -mask_lo; *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; *type = base_lo & 0xff;}static void 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. <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. [RETURNS] Nothing.*/{ struct set_mtrr_context ctxt; 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 (MTRRphysMask_MSR (reg), 0, 0); } else { wrmsr (MTRRphysBase_MSR (reg), base << PAGE_SHIFT | type, (base & size_and_mask) >> (32 - PAGE_SHIFT)); wrmsr (MTRRphysMask_MSR (reg), -size << PAGE_SHIFT | 0x800, (-size & size_and_mask) >> (32 - PAGE_SHIFT)); } if (do_safe) set_mtrr_done (&ctxt);}#ifdef CONFIG_SMPstruct mtrr_var_range { unsigned long base_lo; unsigned long base_hi; unsigned long mask_lo; unsigned long 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 (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi); rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi);}/* Set the MSR pair relating to a var range. Returns TRUE if changes are made */static int __initset_mtrr_var_range_testing (unsigned int index, struct mtrr_var_range *vr){ unsigned int lo, hi; int changed = FALSE; rdmsr (MTRRphysBase_MSR (index), lo, hi); if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) { wrmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi); changed = TRUE; } rdmsr (MTRRphysMask_MSR (index), lo, hi); if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) { wrmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi); changed = TRUE; } return changed;}static void __init get_fixed_ranges (mtrr_type * frs){ unsigned long *p = (unsigned long *) frs; int i; rdmsr (MTRRfix64K_00000_MSR, p[0], p[1]); for (i = 0; i < 2; i++) rdmsr (MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]); for (i = 0; i < 8; i++) rdmsr (MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]);}static int __init set_fixed_ranges_testing (mtrr_type * frs){ unsigned long *p = (unsigned long *) frs; int changed = FALSE; int i; unsigned long lo, hi; rdmsr (MTRRfix64K_00000_MSR, lo, hi); if (p[0] != lo || p[1] != hi) { wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]); changed = TRUE; } for (i = 0; i < 2; i++) { rdmsr (MTRRfix16K_80000_MSR + i, lo, hi); if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) { wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]); changed = TRUE; } } for (i = 0; i < 8; i++) { rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi); if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) { wrmsr (MTRRfix4K_C0000_MSR + 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]; unsigned char enabled; mtrr_type def_type;};/* 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; unsigned long 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 (MTRRdefType_MSR, 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);}static unsigned long __init set_mtrr_state (struct mtrr_state *state, struct set_mtrr_context *ctxt)/* [SUMMARY] 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.*/{ unsigned int i; unsigned long 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 { unsigned long smp_base; unsigned long smp_size; unsigned int smp_reg; mtrr_type smp_type;};static void ipi_handler (void *info)/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. [RETURNS] Nothing.*/{ struct set_mtrr_data *data = info; struct set_mtrr_context ctxt; set_mtrr_prepare (&ctxt); /* Notify master that I've flushed and disabled my cache */ atomic_dec (&undone_count); while (wait_barrier_execute) barrier (); /* The master has cleared me to execute */ (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, FALSE);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?