本文共 3350 字,大约阅读时间需要 11 分钟。
MTRR的全称是Memory Type Range Registers。
它们是一组组的MSR寄存器对(目前最多有96组),用来指定特定的系统内存段的类型。
这里说的类型,是指内存的CACHE类型,有如下的值:
对应的C代码中的实现:
//// Memory cache types//typedef enum { CacheUncacheable = 0, CacheWriteCombining = 1, CacheWriteThrough = 4, CacheWriteProtected = 5, CacheWriteBack = 6, CacheInvalid = 7} MTRR_MEMORY_CACHE_TYPE;
通过MTRR,系统可以优化RAM/ROM/MMIO等不同类型的内存的访问速度。
下面是一个设置MTRR的例子:
因为MTRR是MSR寄存器,因此它是跟CPU有关的,并不是所有的CPU都支持。
不过现在的x86平台CPU都支持该功能,事实上从P6型号开始就只是MTRR了。
要判断是否支持MTRR,需要使用CPUID命令,对应的是CPUID.EAX=01H,返回的结果在EDX:
当确定是否支持之后,还需要确定MTRR的支持情况,这对应于一个MSR寄存器(IA32_MTRRCAP):
它是一个只读的寄存器,具体的BIT位说明如下:
SMRR:确定是否支持System-Management Range Register;
FIX:确定是否支持Fixed Range MTRR(两种类型的MTRR,可变和固定);
WC:Write Combining的CACHE类型是否支持;
VCNT:可变MTRR的支持个数。
这里没有说固定MTRR的个数,是否意味着它的个数是固定的?(目前来看是固定的11个,后面再介绍)
对应判断MTRR是否支持的代码如下:
/** Checks if MTRR is supported. @retval TRUE MTRR is supported. @retval FALSE MTRR is not supported.**/BOOLEANEFIAPIIsMtrrSupported ( VOID ){ CPUID_VERSION_INFO_EDX Edx; MSR_IA32_MTRRCAP_REGISTER MtrrCap; // // Check CPUID(1).EDX[12] for MTRR capability // AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32); if (Edx.Bits.MTRR == 0) { return FALSE; } // // Check number of variable MTRRs and fixed MTRRs existence. // If number of variable MTRRs is zero, or fixed MTRRs do not // exist, return false. // MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP); if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) { return FALSE; } return TRUE;}
硬件复位之后,CPU会Disable掉所有的MTRR,此时所有的系统内存都是UNCACHEABLE的。
需要BIOS来完成MTRR,且在多处理器的系统中,各个处理器的设置必须是一致的。
MTRR的设置对应到3类寄存器,首先是一个全局的MSR(IA32_MTRR_DEF_TYPE):
E:MTRR的开关;
FE:固定MTRR的开关;
Type:系统内存的默认CACHE类型,对于没有被MTRR覆盖到的内存段,就使用默认的CACHE类型;
第二类是固定MTRR,因为它们对应的内存段是固定的,所以也比较好理解,下面是所有的固定MTRR:
每一个寄存器又将内存段分为8个小段,可以分别设置。
第三类是可变MTRR,它通过一组MSR寄存器来设置一段内存的属性,这组MSR如下所示:
第一个寄存器设定了内存的基址和类型,而第二个寄存器设置了它的大小以及使能与否。
这里还有一个概念,就是MAXPHYADDR,它表示的是最大支持的系统内存。
虽然我们说现在x86是64位的系统,但是CPU真正只是的系统内存却不是2^64,而是2^MAXPHYADDR,而这个MAXPHYADDR的大小不同系统有不同的值。关于这个值,可以通过CPUID.80000008H获取,下面是具体的代码:
/** Initializes the valid bits mask and valid address mask for MTRRs. This function initializes the valid bits mask and valid address mask for MTRRs. @param[out] MtrrValidBitsMask The mask for the valid bit of the MTRR @param[out] MtrrValidAddressMask The valid address mask for the MTRR**/VOIDMtrrLibInitializeMtrrMask ( OUT UINT64 *MtrrValidBitsMask, OUT UINT64 *MtrrValidAddressMask ){ UINT32 MaxExtendedFunction; CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize; AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL); if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) { AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL); } else { VirPhyAddressSize.Bits.PhysicalAddressBits = 36; } *MtrrValidBitsMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1; *MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL;}
当MAXPHYADDR确定之后,基址的设置没有什么特别好介绍的,不过大小需要说明下。
一个可变MTRR可以存放的内存大小通过如下的方式计算:
Address_Within_Range AND PhysMask = PhysBase AND PhysMask
其中Address_Within_Range就是可用的内存地址。
对应到代码如下:
Mask = ((~(Length - 1)) & MtrrValidAddressMask) | BIT11;
其中的MtrrValidAddressMask是可用的地址的掩码(即MAXPHYADDR个1,前面的MtrrLibInitializeMtrrMask()有计算方式)。
Linux下查看MTRR的方式:
转载地址:http://qgadz.baihongyu.com/