mtrr.c

来自「适合KS8695X」· C语言 代码 · 共 868 行 · 第 1/2 页

C
868
字号
/****************************************************************************
*
*                   SciTech OS Portability Manager Library
*
*  ========================================================================
*
*    The contents of this file are subject to the SciTech MGL Public
*    License Version 1.0 (the "License"); you may not use this file
*    except in compliance with the License. You may obtain a copy of
*    the License at http://www.scitechsoft.com/mgl-license.txt
*
*    Software distributed under the License is distributed on an
*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
*    implied. See the License for the specific language governing
*    rights and limitations under the License.
*
*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
*
*    The Initial Developer of the Original Code is SciTech Software, Inc.
*    All Rights Reserved.
*
*  ========================================================================
*
*               Heavily based on code copyright (C) Richard Gooch
*
* Language:     ANSI C
* Environment:  32-bit Ring 0 device driver
*
* Description:  Generic Memory Type Range Register (MTRR) functions to
*               manipulate the MTRR registers on supported CPU's. This code
*               *must* run at ring 0, so you can't normally include this
*               code directly in normal applications (the except is DOS4GW
*               apps which run at ring 0 under real DOS). Thus this code
*               will normally be compiled into a ring 0 device driver for
*               the target operating system.
*
****************************************************************************/

#include "pmapi.h"
#include "ztimerc.h"
#include "mtrr.h"

#ifndef REALMODE

/*--------------------------- Global variables ----------------------------*/

/* Intel pre-defined MTRR registers */

#define NUM_FIXED_RANGES        88
#define INTEL_cap_MSR           0x0FE
#define INTEL_defType_MSR       0x2FF
#define INTEL_fix64K_00000_MSR  0x250
#define INTEL_fix16K_80000_MSR  0x258
#define INTEL_fix16K_A0000_MSR  0x259
#define INTEL_fix4K_C0000_MSR   0x268
#define INTEL_fix4K_C8000_MSR   0x269
#define INTEL_fix4K_D0000_MSR   0x26A
#define INTEL_fix4K_D8000_MSR   0x26B
#define INTEL_fix4K_E0000_MSR   0x26C
#define INTEL_fix4K_E8000_MSR   0x26D
#define INTEL_fix4K_F0000_MSR   0x26E
#define INTEL_fix4K_F8000_MSR   0x26F

/* Macros to find the address of a paricular MSR register */

#define INTEL_physBase_MSR(reg) (0x200 + 2 * (reg))
#define INTEL_physMask_MSR(reg) (0x200 + 2 * (reg) + 1)

/* Cyrix CPU configuration register indexes */
#define CX86_CCR0 0xC0
#define CX86_CCR1 0xC1
#define CX86_CCR2 0xC2
#define CX86_CCR3 0xC3
#define CX86_CCR4 0xE8
#define CX86_CCR5 0xE9
#define CX86_CCR6 0xEA
#define CX86_DIR0 0xFE
#define CX86_DIR1 0xFF
#define CX86_ARR_BASE 0xC4
#define CX86_RCR_BASE 0xDC

/* Structure to maintain machine state while updating MTRR registers */

typedef struct {
    ulong   flags;
    ulong   defTypeLo;
    ulong   defTypeHi;
    ulong   cr4Val;
    ulong   ccr3;
    } MTRRContext;

static int      numMTRR = -1;
static int      cpuFamily,cpuType,cpuStepping;
static void     (*getMTRR)(uint reg,ulong *base,ulong *size,int *type) = NULL;
static void     (*setMTRR)(uint reg,ulong base,ulong size,int type) = NULL;
static int      (*getFreeRegion)(ulong base,ulong size) = NULL;

/*----------------------------- Implementation ----------------------------*/

/****************************************************************************
RETURNS:
Returns non-zero if we have the write-combining memory type
****************************************************************************/
static int MTRR_haveWriteCombine(void)
{
    ulong   config,dummy;

    switch (cpuFamily) {
	case CPU_AMD:
	    if (cpuType < CPU_AMDAthlon) {
		/* AMD K6-2 stepping 8 and later support the MTRR registers.
		 * The earlier K6-2 steppings (300Mhz models) do not
		 * support MTRR's.
		 */
		if ((cpuType < CPU_AMDK6_2) || (cpuType == CPU_AMDK6_2 && cpuStepping < 8))
		    return 0;
		return 1;
		}
	    /* Fall through for AMD Athlon which uses P6 style MTRR's */
	case CPU_Intel:
	    _MTRR_readMSR(INTEL_cap_MSR,&config,&dummy);
	    return (config & (1 << 10));
	case CPU_Cyrix:
	    /* Cyrix 6x86 and later support the MTRR registers */
	    if (cpuType < CPU_Cyrix6x86)
		return 0;
	    return 1;
	}
    return 0;
}

/****************************************************************************
PARAMETERS:
base    - The starting physical base address of the region
size    - The size in bytes of the region

RETURNS:
The index of the region on success, else -1 on error.

REMARKS:
Generic function to find the location of a free MTRR register to be used
for creating a new mapping.
****************************************************************************/
static int GENERIC_getFreeRegion(
    ulong base,
    ulong size)
{
    int     i,ltype;
    ulong   lbase,lsize;

    for (i = 0; i < numMTRR; i++) {
	getMTRR(i,&lbase,&lsize,&ltype);
	if (lsize < 1)
	    return i;
	}
    (void)base;
    (void)size;
    return -1;
}

/****************************************************************************
PARAMETERS:
base    - The starting physical base address of the region
size    - The size in bytes of the region

RETURNS:
The index of the region on success, else -1 on error.

REMARKS:
Generic function to find the location of a free MTRR register to be used
for creating a new mapping.
****************************************************************************/
static int AMDK6_getFreeRegion(
    ulong base,
    ulong size)
{
    int     i,ltype;
    ulong   lbase,lsize;

    for (i = 0; i < numMTRR; i++) {
	getMTRR(i,&lbase,&lsize,&ltype);
	if (lsize < 1)
	    return i;
	}
    (void)base;
    (void)size;
    return -1;
}

/****************************************************************************
PARAMETERS:
base    - The starting physical base address of the region
size    - The size in bytes of the region

RETURNS:
The index of the region on success, else -1 on error.

REMARKS:
Cyrix specific function to find the location of a free MTRR register to be
used for creating a new mapping.
****************************************************************************/
static int CYRIX_getFreeRegion(
    ulong base,
    ulong size)
{
    int     i,ltype;
    ulong   lbase, lsize;

    if (size > 0x2000000UL) {
	/* If we are to set up a region >32M then look at ARR7 immediately */
	getMTRR(7,&lbase,&lsize,&ltype);
	if (lsize < 1)
	    return 7;
	}
    else {
	/* Check ARR0-6 registers */
	for (i = 0; i < 7; i++) {
	    getMTRR(i,&lbase,&lsize,&ltype);
	    if (lsize < 1)
		return i;
	    }
	/* Try ARR7 but its size must be at least 256K */
	getMTRR(7,&lbase,&lsize,&ltype);
	if ((lsize < 1) && (size >= 0x40000))
	    return i;
	}
    (void)base;
    return -1;
}

/****************************************************************************
PARAMETERS:
c   - Place to store the machine context across the call

REMARKS:
Puts the processor into a state where MTRRs can be safely updated
****************************************************************************/
static void MTRR_beginUpdate(
    MTRRContext *c)
{
    c->flags = _MTRR_disableInt();
    if (cpuFamily != CPU_AMD || (cpuFamily == CPU_AMD && cpuType >= CPU_AMDAthlon)) {
	switch (cpuFamily) {
	    case CPU_Intel:
	    case CPU_AMD:
		/* Disable MTRRs, and set the default type to uncached */
		c->cr4Val = _MTRR_saveCR4();
		_MTRR_readMSR(INTEL_defType_MSR,&c->defTypeLo,&c->defTypeHi);
		_MTRR_writeMSR(INTEL_defType_MSR,c->defTypeLo & 0xF300UL,c->defTypeHi);
		break;
	    case CPU_Cyrix:
		c->ccr3 = _MTRR_getCx86(CX86_CCR3);
		_MTRR_setCx86(CX86_CCR3, (uchar)((c->ccr3 & 0x0F) | 0x10));
		break;
	    }
	}
}

/****************************************************************************
PARAMETERS:
c   - Place to restore the machine context from

REMARKS:
Restores the processor after updating any of the registers
****************************************************************************/
static void MTRR_endUpdate(
    MTRRContext *c)
{
    if (cpuFamily != CPU_AMD || (cpuFamily == CPU_AMD && cpuType >= CPU_AMDAthlon)) {
	PM_flushTLB();
	switch (cpuFamily) {
	    case CPU_Intel:
	    case CPU_AMD:
		_MTRR_writeMSR(INTEL_defType_MSR,c->defTypeLo,c->defTypeHi);
		_MTRR_restoreCR4(c->cr4Val);
		break;
	    case CPU_Cyrix:
		_MTRR_setCx86(CX86_CCR3,(uchar)c->ccr3);
		break;
	    }
	}

    /* Re-enable interrupts (if enabled previously) */
    _MTRR_restoreInt(c->flags);
}

/****************************************************************************
PARAMETERS:
reg     - MTRR register to read
base    - Place to store the starting physical base address of the region
size    - Place to store the size in bytes of the region
type    - Place to store the type of the MTRR register

REMARKS:
Intel specific function to read the value of a specific MTRR register.
****************************************************************************/
static void INTEL_getMTRR(
    uint reg,
    ulong *base,
    ulong *size,
    int *type)
{
    ulong hi,maskLo,baseLo;

    _MTRR_readMSR(INTEL_physMask_MSR(reg),&maskLo,&hi);
    if ((maskLo & 0x800) == 0) {
	/* MTRR is disabled, so it is free */
	*base = 0;
	*size = 0;
	*type = 0;
	return;
	}
    _MTRR_readMSR(INTEL_physBase_MSR(reg),&baseLo,&hi);
    maskLo = (maskLo & 0xFFFFF000UL);
    *size = ~(maskLo - 1);
    *base = (baseLo & 0xFFFFF000UL);
    *type = (baseLo & 0xFF);
}

/****************************************************************************
PARAMETERS:
reg     - MTRR register to set
base    - The starting physical base address of the region
size    - The size in bytes of the region
type    - Type to place into the MTRR register

REMARKS:
Intel specific function to set the value of a specific MTRR register to
the passed in base, size and type.
****************************************************************************/
static void INTEL_setMTRR(
    uint reg,
    ulong base,
    ulong size,
    int type)
{
    MTRRContext c;

    MTRR_beginUpdate(&c);
    if (size == 0) {
	/* The invalid bit is kept in the mask, so we simply clear the
	 * relevant mask register to disable a range.
	 */
	_MTRR_writeMSR(INTEL_physMask_MSR(reg),0,0);
	}
    else {
	_MTRR_writeMSR(INTEL_physBase_MSR(reg),base | type,0);
	_MTRR_writeMSR(INTEL_physMask_MSR(reg),~(size - 1) | 0x800,0);
	}
    MTRR_endUpdate(&c);
}

/****************************************************************************
REMARKS:
Disabled banked write combing for Intel processors. We always disable this
because it invariably causes problems with older hardware.
****************************************************************************/
static void INTEL_disableBankedWriteCombine(void)
{
    MTRRContext c;

    MTRR_beginUpdate(&c);
    _MTRR_writeMSR(INTEL_fix16K_A0000_MSR,0,0);
    MTRR_endUpdate(&c);
}

/****************************************************************************
PARAMETERS:
reg     - MTRR register to set
base    - The starting physical base address of the region
size    - The size in bytes of the region
type    - Type to place into the MTRR register

REMARKS:
Intel specific function to set the value of a specific MTRR register to
the passed in base, size and type.
****************************************************************************/
static void AMD_getMTRR(
    uint reg,
    ulong *base,
    ulong *size,
    int *type)
{
    ulong   low,high;

    /*  Upper dword is region 1, lower is region 0  */
    _MTRR_readMSR(0xC0000085, &low, &high);
    if (reg == 1)
	low = high;

    /* Find the base and type for the region */
    *base = low & 0xFFFE0000;
    *type = 0;
    if (low & 1)
	*type = PM_MTRR_UNCACHABLE;
    if (low & 2)
	*type = PM_MTRR_WRCOMB;
    if ((low & 3) == 0) {
	*size = 0;
	return;
	}

    /* This needs a little explaining. The size is stored as an
     * inverted mask of bits of 128K granularity 15 bits long offset
     * 2 bits
     *
     * So to get a size we do invert the mask and add 1 to the lowest
     * mask bit (4 as its 2 bits in). This gives us a size we then shift
     * to turn into 128K blocks
     *
     *  eg              111 1111 1111 1100      is 512K
     *
     *  invert          000 0000 0000 0011
     *  +1              000 0000 0000 0100
     *  *128K   ...
     */
    low = (~low) & 0x0FFFC;
    *size = (low + 4) << 15;
}

/****************************************************************************
PARAMETERS:
reg     - MTRR register to set
base    - The starting physical base address of the region
size    - The size in bytes of the region
type    - Type to place into the MTRR register

REMARKS:
Intel specific function to set the value of a specific MTRR register to
the passed in base, size and type.
****************************************************************************/
static void AMD_setMTRR(
    uint reg,
    ulong base,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?