| /**************************************************************************** |
| * |
| * 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,<ype); |
| 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,<ype); |
| 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,<ype); |
| if (lsize < 1) |
| return 7; |
| } |
| else { |
| /* Check ARR0-6 registers */ |
| for (i = 0; i < 7; i++) { |
| getMTRR(i,&lbase,&lsize,<ype); |
| if (lsize < 1) |
| return i; |
| } |
| /* Try ARR7 but its size must be at least 256K */ |
| getMTRR(7,&lbase,&lsize,<ype); |
| 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, |
| ulong size, |
| int type) |
| { |
| ulong low,high,newVal; |
| MTRRContext c; |
| |
| MTRR_beginUpdate(&c); |
| _MTRR_readMSR(0xC0000085, &low, &high); |
| if (size == 0) { |
| /* Clear register to disable */ |
| if (reg) |
| high = 0; |
| else |
| low = 0; |
| } |
| else { |
| /* Set the register to the base (already shifted for us), the |
| * type (off by one) and an inverted bitmask of the size |
| * The size is the only odd bit. We are fed say 512K |
| * We invert this and we get 111 1111 1111 1011 but |
| * if you subtract one and invert you get the desired |
| * 111 1111 1111 1100 mask |
| */ |
| newVal = (((~(size-1)) >> 15) & 0x0001FFFC) | base | (type+1); |
| if (reg) |
| high = newVal; |
| else |
| low = newVal; |
| } |
| |
| /* The writeback rule is quite specific. See the manual. Its |
| * disable local interrupts, write back the cache, set the MTRR |
| */ |
| PM_flushTLB(); |
| _MTRR_writeMSR(0xC0000085, low, high); |
| 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 CYRIX_getMTRR( |
| uint reg, |
| ulong *base, |
| ulong *size, |
| int *type) |
| { |
| MTRRContext c; |
| uchar arr = CX86_ARR_BASE + reg*3; |
| uchar rcr,shift; |
| |
| /* Save flags and disable interrupts */ |
| MTRR_beginUpdate(&c); |
| ((uchar*)base)[3] = _MTRR_getCx86(arr); |
| ((uchar*)base)[2] = _MTRR_getCx86((uchar)(arr+1)); |
| ((uchar*)base)[1] = _MTRR_getCx86((uchar)(arr+2)); |
| rcr = _MTRR_getCx86((uchar)(CX86_RCR_BASE + reg)); |
| MTRR_endUpdate(&c); |
| |
| /* Enable interrupts if it was enabled previously */ |
| shift = ((uchar*)base)[1] & 0x0f; |
| *base &= 0xFFFFF000UL; |
| |
| /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 |
| * Note: shift==0xF means 4G, this is unsupported. |
| */ |
| if (shift) |
| *size = (reg < 7 ? 0x800UL : 0x20000UL) << shift; |
| else |
| *size = 0; |
| |
| /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ |
| if (reg < 7) { |
| switch (rcr) { |
| case 1: *type = PM_MTRR_UNCACHABLE; break; |
| case 8: *type = PM_MTRR_WRBACK; break; |
| case 9: *type = PM_MTRR_WRCOMB; break; |
| case 24: |
| default: *type = PM_MTRR_WRTHROUGH; break; |
| } |
| } |
| else { |
| switch (rcr) { |
| case 0: *type = PM_MTRR_UNCACHABLE; break; |
| case 8: *type = PM_MTRR_WRCOMB; break; |
| case 9: *type = PM_MTRR_WRBACK; break; |
| case 25: |
| default: *type = PM_MTRR_WRTHROUGH; break; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| 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 CYRIX_setMTRR( |
| uint reg, |
| ulong base, |
| ulong size, |
| int type) |
| { |
| MTRRContext c; |
| uchar arr = CX86_ARR_BASE + reg*3; |
| uchar arr_type,arr_size; |
| |
| /* Count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ |
| size >>= (reg < 7 ? 12 : 18); |
| size &= 0x7FFF; /* Make sure arr_size <= 14 */ |
| for (arr_size = 0; size; arr_size++, size >>= 1) |
| ; |
| if (reg < 7) { |
| switch (type) { |
| case PM_MTRR_UNCACHABLE: arr_type = 1; break; |
| case PM_MTRR_WRCOMB: arr_type = 9; break; |
| case PM_MTRR_WRTHROUGH: arr_type = 24; break; |
| default: arr_type = 8; break; |
| } |
| } |
| else { |
| switch (type) { |
| case PM_MTRR_UNCACHABLE: arr_type = 0; break; |
| case PM_MTRR_WRCOMB: arr_type = 8; break; |
| case PM_MTRR_WRTHROUGH: arr_type = 25; break; |
| default: arr_type = 9; break; |
| } |
| } |
| MTRR_beginUpdate(&c); |
| _MTRR_setCx86((uchar)arr, ((uchar*)&base)[3]); |
| _MTRR_setCx86((uchar)(arr+1), ((uchar*)&base)[2]); |
| _MTRR_setCx86((uchar)(arr+2), (uchar)((((uchar*)&base)[1]) | arr_size)); |
| _MTRR_setCx86((uchar)(CX86_RCR_BASE + reg), (uchar)arr_type); |
| MTRR_endUpdate(&c); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| On Cyrix 6x86(MX) and MII the ARR3 is special: it has connection |
| with the SMM (System Management Mode) mode. So we need the following: |
| Check whether SMI_LOCK (CCR3 bit 0) is set |
| if it is set, ARR3 cannot be changed (it cannot be changed until the |
| next processor reset) |
| if it is reset, then we can change it, set all the needed bits: |
| - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) |
| - disable access to SMM memory (CCR1 bit 2 reset) |
| - disable SMM mode (CCR1 bit 1 reset) |
| - disable write protection of ARR3 (CCR6 bit 1 reset) |
| - (maybe) disable ARR3 |
| Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) |
| ****************************************************************************/ |
| static void CYRIX_initARR(void) |
| { |
| MTRRContext c; |
| uchar ccr[7]; |
| int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; |
| |
| /* Begin updating */ |
| MTRR_beginUpdate(&c); |
| |
| /* Save all CCRs locally */ |
| ccr[0] = _MTRR_getCx86(CX86_CCR0); |
| ccr[1] = _MTRR_getCx86(CX86_CCR1); |
| ccr[2] = _MTRR_getCx86(CX86_CCR2); |
| ccr[3] = (uchar)c.ccr3; |
| ccr[4] = _MTRR_getCx86(CX86_CCR4); |
| ccr[5] = _MTRR_getCx86(CX86_CCR5); |
| ccr[6] = _MTRR_getCx86(CX86_CCR6); |
| if (ccr[3] & 1) |
| ccrc[3] = 1; |
| else { |
| /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and |
| * access to SMM memory through ARR3 (bit 7). |
| */ |
| if (ccr[6] & 0x02) { |
| ccr[6] &= 0xFD; |
| ccrc[6] = 1; /* Disable write protection of ARR3. */ |
| _MTRR_setCx86(CX86_CCR6,ccr[6]); |
| } |
| } |
| |
| /* If we changed CCR1 in memory, change it in the processor, too. */ |
| if (ccrc[1]) |
| _MTRR_setCx86(CX86_CCR1,ccr[1]); |
| |
| /* Enable ARR usage by the processor */ |
| if (!(ccr[5] & 0x20)) { |
| ccr[5] |= 0x20; |
| ccrc[5] = 1; |
| _MTRR_setCx86(CX86_CCR5,ccr[5]); |
| } |
| |
| /* We are finished updating */ |
| MTRR_endUpdate(&c); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Initialise the MTRR module, by detecting the processor type and determining |
| if the processor supports the MTRR functionality. |
| ****************************************************************************/ |
| void MTRR_init(void) |
| { |
| int i,cpu,ltype; |
| ulong eax,edx,lbase,lsize; |
| |
| /* Check that we have a compatible CPU */ |
| if (numMTRR == -1) { |
| numMTRR = 0; |
| if (!_MTRR_isRing0()) |
| return; |
| cpu = CPU_getProcessorType(); |
| cpuFamily = cpu & CPU_familyMask; |
| cpuType = cpu & CPU_mask; |
| cpuStepping = (cpu & CPU_steppingMask) >> CPU_steppingShift; |
| switch (cpuFamily) { |
| case CPU_Intel: |
| /* Intel Pentium Pro and later support the MTRR registers */ |
| if (cpuType < CPU_PentiumPro) |
| return; |
| _MTRR_readMSR(INTEL_cap_MSR,&eax,&edx); |
| numMTRR = eax & 0xFF; |
| getMTRR = INTEL_getMTRR; |
| setMTRR = INTEL_setMTRR; |
| getFreeRegion = GENERIC_getFreeRegion; |
| INTEL_disableBankedWriteCombine(); |
| break; |
| case CPU_AMD: |
| /* AMD K6-2 and later support the MTRR registers */ |
| if ((cpuType < CPU_AMDK6_2) || (cpuType == CPU_AMDK6_2 && cpuStepping < 8)) |
| return; |
| if (cpuType < CPU_AMDAthlon) { |
| numMTRR = 2; /* AMD CPU's have 2 MTRR's */ |
| getMTRR = AMD_getMTRR; |
| setMTRR = AMD_setMTRR; |
| getFreeRegion = AMDK6_getFreeRegion; |
| |
| /* For some reason some IBM systems with K6-2 processors |
| * have write combined enabled for the system BIOS |
| * region from 0xE0000 to 0xFFFFFF. We need *both* MTRR's |
| * for our own graphics drivers, so if we detect any |
| * regions below the 1Meg boundary, we remove them |
| * so we can use this MTRR register ourselves. |
| */ |
| for (i = 0; i < numMTRR; i++) { |
| getMTRR(i,&lbase,&lsize,<ype); |
| if (lbase < 0x100000) |
| setMTRR(i,0,0,0); |
| } |
| } |
| else { |
| /* AMD Athlon uses P6 style MTRR's */ |
| _MTRR_readMSR(INTEL_cap_MSR,&eax,&edx); |
| numMTRR = eax & 0xFF; |
| getMTRR = INTEL_getMTRR; |
| setMTRR = INTEL_setMTRR; |
| getFreeRegion = GENERIC_getFreeRegion; |
| INTEL_disableBankedWriteCombine(); |
| } |
| break; |
| case CPU_Cyrix: |
| /* Cyrix 6x86 and later support the MTRR registers */ |
| if (cpuType < CPU_Cyrix6x86 || cpuType >= CPU_CyrixMediaGX) |
| return; |
| numMTRR = 8; /* Cyrix CPU's have 8 ARR's */ |
| getMTRR = CYRIX_getMTRR; |
| setMTRR = CYRIX_setMTRR; |
| getFreeRegion = CYRIX_getFreeRegion; |
| CYRIX_initARR(); |
| break; |
| default: |
| return; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| 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 |
| |
| RETURNS: |
| Error code describing the result. |
| |
| REMARKS: |
| Function to enable write combining for the specified region of memory. |
| ****************************************************************************/ |
| int MTRR_enableWriteCombine( |
| ulong base, |
| ulong size, |
| uint type) |
| { |
| int i; |
| int ltype; |
| ulong lbase,lsize,last; |
| |
| /* Check that we have a CPU that supports MTRR's and type is valid */ |
| if (numMTRR <= 0) { |
| if (!_MTRR_isRing0()) |
| return PM_MTRR_ERR_NO_OS_SUPPORT; |
| return PM_MTRR_NOT_SUPPORTED; |
| } |
| if (type >= PM_MTRR_MAX) |
| return PM_MTRR_ERR_PARAMS; |
| |
| /* If the type is WC, check that this processor supports it */ |
| if (!MTRR_haveWriteCombine()) |
| return PM_MTRR_ERR_NOWRCOMB; |
| |
| /* Adjust the boundaries depending on the CPU type */ |
| switch (cpuFamily) { |
| case CPU_AMD: |
| if (cpuType < CPU_AMDAthlon) { |
| /* Apply the K6 block alignment and size rules. In order: |
| * o Uncached or gathering only |
| * o 128K or bigger block |
| * o Power of 2 block |
| * o base suitably aligned to the power |
| */ |
| if (type > PM_MTRR_WRCOMB && (size < (1 << 17) || (size & ~(size-1))-size || (base & (size-1)))) |
| return PM_MTRR_ERR_NOT_ALIGNED; |
| break; |
| } |
| /* Fall through for AMD Athlon which uses P6 style MTRR's */ |
| case CPU_Intel: |
| case CPU_Cyrix: |
| if ((base & 0xFFF) || (size & 0xFFF)) { |
| /* Base and size must be multiples of 4Kb */ |
| return PM_MTRR_ERR_NOT_4KB_ALIGNED; |
| } |
| if (base < 0x100000) { |
| /* Base must be >= 1Mb */ |
| return PM_MTRR_ERR_BELOW_1MB; |
| } |
| |
| /* Check upper bits of base and last are equal and lower bits |
| * are 0 for base and 1 for last |
| */ |
| last = base + size - 1; |
| for (lbase = base; !(lbase & 1) && (last & 1); lbase = lbase >> 1, last = last >> 1) |
| ; |
| if (lbase != last) { |
| /* Base is not aligned on the correct boundary */ |
| return PM_MTRR_ERR_NOT_ALIGNED; |
| } |
| break; |
| default: |
| return PM_MTRR_NOT_SUPPORTED; |
| } |
| |
| /* Search for existing MTRR */ |
| for (i = 0; i < numMTRR; ++i) { |
| getMTRR(i,&lbase,&lsize,<ype); |
| if (lbase == 0 && lsize == 0) |
| continue; |
| if (base > lbase + (lsize-1)) |
| continue; |
| if ((base < lbase) && (base+size-1 < lbase)) |
| continue; |
| |
| /* Check that we don't overlap an existing region */ |
| if (type != PM_MTRR_UNCACHABLE) { |
| if ((base < lbase) || (base+size-1 > lbase+lsize-1)) |
| return PM_MTRR_ERR_OVERLAP; |
| } |
| else if (base == lbase && size == lsize) { |
| /* The region already exists so leave it alone */ |
| return PM_MTRR_ERR_OK; |
| } |
| |
| /* New region is enclosed by an existing region, so only allow |
| * a new type to be created if we are setting a region to be |
| * uncacheable (such as MMIO registers within a framebuffer). |
| */ |
| if (ltype != (int)type) { |
| if (type == PM_MTRR_UNCACHABLE) |
| continue; |
| return PM_MTRR_ERR_TYPE_MISMATCH; |
| } |
| return PM_MTRR_ERR_OK; |
| } |
| |
| /* Search for an empty MTRR */ |
| if ((i = getFreeRegion(base,size)) < 0) |
| return PM_MTRR_ERR_NONE_FREE; |
| setMTRR(i,base,size,type); |
| return PM_MTRR_ERR_OK; |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| callback - Function to callback with write combine information |
| |
| REMARKS: |
| Function to enumerate all write combine regions currently enabled for the |
| processor. |
| ****************************************************************************/ |
| int PMAPI PM_enumWriteCombine( |
| PM_enumWriteCombine_t callback) |
| { |
| int i,ltype; |
| ulong lbase,lsize; |
| |
| /* Check that we have a CPU that supports MTRR's and type is valid */ |
| if (numMTRR <= 0) { |
| if (!_MTRR_isRing0()) |
| return PM_MTRR_ERR_NO_OS_SUPPORT; |
| return PM_MTRR_NOT_SUPPORTED; |
| } |
| |
| /* Enumerate all existing MTRR's */ |
| for (i = 0; i < numMTRR; ++i) { |
| getMTRR(i,&lbase,&lsize,<ype); |
| callback(lbase,lsize,ltype); |
| } |
| return PM_MTRR_ERR_OK; |
| } |
| #endif |