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 + -
显示快捷键?