【x86架构】MTRR
介绍x86架构中的MTRR。
MTRR是什么
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的支持情况
因为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.
**/
BOOLEAN
EFIAPI
IsMtrrSupported (
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;
}
MTRR的设置
硬件复位之后,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
**/
VOID
MtrrLibInitializeMtrrMask (
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()有计算方式)。
MTRR查看
Linux下查看MTRR的方式:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)