x86架构——MCA

来源:互联网 发布:网络帐号交易平台 编辑:程序博客网 时间:2024/05/16 11:08

简述

Intel从奔腾4开始的CPU中增加了一种机制,称为MCA——Machine Check Architecture,它用来检测硬件(这里的Machine表示的就是硬件)错误,比如系统总线错误、ECC错误等等。

这套系统通过一定数量的MSR(Model Specific Register)来实现,这些MSR分为两个部分,一部分用来进行设置,另一部分用来描述发生的硬件错误。

当CPU检测到不可纠正的MCE(Machine Check Error)时,就会触发#MC(Machine Check Exception),通常软件会注册相关的函数来处理#MC,在这个函数中会通过读取MSR来收集MCE的错误信息,然后重启系统。当然由于发生的MCE可能是非常致命的,CPU直接重启了,没有办法完成MCE处理函数;甚至有可能在MCE处理函数中又触发了不可纠正的MCE,也会导致系统直接重启。

当然CPU还会检测到可纠正的MCE,当可纠正的MCE数量超过一定的阈值时,会触发CMCI(Corrected Machine Check Error Interrupt),此时软件可以捕捉到该中断并进行相应的处理。CMCI是在MCA之后才加入的,算是对MCA的一个增强,在此之前软件只能通过轮询可纠正MCE相关的MSR才能实现相关的操作。


Machine Check MSR

前面已经说过,MCA是通过一系列的MSR来实现,这里介绍下这些MSR寄存器,首先看下面的图:


上图基本包含了MCA相关的所有MSR。

它分为左右两个部分,左边的是全局的寄存器,右边表示的是多组寄存器。

i表示的是各个组的Index。这里的组有一个称呼是Error Reporting Register Bank。

MCA通过若干Bank的MSR寄存器来表示各种类型的MCE。

下面简单介绍一下这些寄存器。


IA32_MCG_CAP MSR

这个MSR描述了当前CPU处理MCA的能力,具体每个位的作用如下所示:


BIT0-7:表示的是CPU支持的Bank的个数;

BIT8:1表示IA32_MCG_CTL有效,如果是0的话表示无效,读取该IA32_MCG_CTL这个MSR可能发生Exception(至少在UEFI下是这样);

BIT9:1表示IA32_MCG_EXT_CTL有效,反之无效,这个与BIT8的作用类似;

BIT10:1表示支持CMCI,但是CMCI是否能用还需要通过IA32_MCi_CTL2这个MSR的BIT30来使能;

BIT11:1表示IA32_MCi_STATUS这个MSR的BIT56-55是保留的,BIT54-53是用来上报Threshold-based Error状态的;

BIT16-23:表示存在的Extended Machine Check State寄存器的个数;

BIT24:1表示CPU支持Software Error Recovery;

BIT25:1表示CPU支持增强版的MCA;

BIT26:1表示支持更多的错误记录(需要UEFI、ACPI的支持);

BIT27:1表示支持Local Machine Check Exception;


IA32_MCG_STATUS MSR

该MSR记录了MCE发生时CPU的状态,主要的BIT位介绍如下:


这里的IP指的是Instruction Pointer,指向当前的CPU指令;

EIPV为1时表示当前的指令与导致MCE的原因相关;RIPV为1表示当前CPU从当前指令继续执行并不会有什么问题;

但是还是不知道这两个BIT有什么用处...


IA32_MCG_CTL MSR

这个寄存器的存在依赖于IA32_MCG_CAP这个MSR的BIT8。

这个寄存器主要用来Disable(写1)或者Enable(写全0)MCA功能。


IA32_MCG_EXT_CTL MSR

这个寄存器同样依赖于IA32_MCA_CAP这个MSR,这次依赖的是BIT9。该MSR的BIT位说明如下图所示:


目前有就BIT0有用,用来Disable(写1)或者Enable(写0)LMCE,这个LMCE的功能就是使硬件能够将某些MCE发送给单个的逻辑处理器,为什么要这样做目前还不是很 清楚。


以上都是全局的MSR,下面介绍每个Bank对应的MSR,

这些寄存器的第一个是IA32_MC0_CTL,它的地址一般都是400H。之后接着的是IA32_MC0_STATUS,IA32_MC0_ADDR,IA32_MC0_MISC,但是在之后并不是IA32_MC0_CTL2,而是IA32_MC1_CTL;对于IA32_MCi_CTL2来说,它的地址跟上面的这些不在一起,第一个IA32_MC0_CTL2是在280H,之后是IA32_MC1_CTL2在281H,以此类推。


IA32_MCi_CTL MSRs

每个Bank的CTL的作用是用来控制在发生哪些MCA的时候来触发#MC:


这里的64个BIT位,设置某个BIT位就会使对应BIT位的MCA类型在发生时触发#MC。


IA32_MCi_STATUS MSRS

这类MSR的作用就是显示MCE信息:


注意只有当VAL这个BIT位(BIT63)为1时才表示发生了对应这个Bank的MCE。当MCE发生了,软件需要给这个VAL位写0来清零(如果有可能的话,因为对于不可纠正的MCE可能软件会 来不及写),不能往这位写1,会出现Exception。

BIT0-15BIT16-31:这个两个部分都表示MCE的错误类型,前者是通用的,后者是跟CPU有关的;

BIT58:1表示IA32_MCi_ADDR这个MSR是有效的,反之无效;

BIT59:1表示IA32_MCi_MISC这个MSR是有效的,反之无效;这两个BIT是因为不同MCE错误并不是都需要ADDR和MSIC这样的MSR;

BIT60:这个位于IA32_MCi_CTL中的位是对应的,那边使能了,这里就是1;

BIT61:表示MCE是不可纠正的;

BIT62:表示发生了二次的MCE,这个时候到底这个Bank表示的是哪一次的MCE信息,需要根据一定的规则来确定:


这个可以先不关注。

另外还有一些寄存器在这里不介绍,具体还是看手册。


IA32_MCi_ADDR MSRs

这个MSR并没有特别好介绍的:


这个地址指向内存中导致MCE的代码或者数据。

注意这个地址在不同的内存模型下可以是偏移地址,虚拟地址和物理地址中的一种,这个需要MISC这个MSR来确定,下面胡讲到。

这个MSR也可以手动清零,写1会出错。


IA32_MCi_MISC MSRs

这个寄存器的BIT位说明如下:


这里的Address Mode说明如下:



IA32_MCi_CTL2 MSRs

这个寄存器就是为CMCI使用的,BIT位说明如下:


一个是用于使能CMCI,另一个是用来设置CMCI的阈值。


除了上述的MSR之外,在IA32_MCG_CAP这个MSR的说明中还提到过它的BIT16-23还提到了额外的MSR,它们称为Extended Machine Check State,这些MSR的描述如下:


上图实际上只展示了非64位CPU的MSR,还有一个64位CPU的MSR,这里就不再多说。


需要注意,实际上上面的这些寄存器并不需要自己一个个去对比和解析,Intel提供了一个工具叫做MCE Decoder,可以用来解析MCE。

另外在Intel的开发者手册中有专门的一个章节解析MCE错误:《CHAPTER 16 INTERPRETING MACHINE-CHECK ERROR CODES》。


CMCI

前面以及提到,CMCI是后期加入到MCA的一种机制,它将错误上报的阈值操作从原始的软件轮询变成了硬件中断触发。

一个CPU是否支持CMCI需要查看IA32_MCG_CAP的BIT10,如果该位是1就表示支持。

另外CMCI默认是关闭的,需要通过IA32_MCi_CTL2的BIT30来打开,并设置BIT0-14的阈值,注意每个Bank都要设置。

设置的时候首先写1到IA32_MCi_CTL2的BIT30,再读取这个值,如果值变成了1,说明CMCI使能了,否则就是CPU不支持CMCI;之后再写阈值到BIT0-14,如果读出来的值是0,表示不支持阈值,否则就是成功设置了阈值。

CMCI是通过Local ACPI来实现的,具体的示意图如下:


在Local ACPI Table中有专门处理CMCI的寄存器,称为LVT CMCI Register (FEE0 02F0H) :


BIT0-7:中断向量;

BIT8-10:Delivery Mode,比如SMI,NMI等;

BIT12:Delivery Status,0表示没有中断,1表示中断正在发生;

BIT17:Interrupt Mask,0表示接收中断,1表示屏蔽中断;


关于CMCI的初始化和CMCI处理函数的实现,手册上有部分的介绍,不过没有什么源代码可以借鉴,这个不展开了。


MCA的初始化

手册上有一个伪代码可供参考:

IF CPU supports MCETHENIF CPU supports MCATHENIF (IA32_MCG_CAP.MCG_CTL_P = 1)(* IA32_MCG_CTL register is present *)THENIA32_MCG_CTL ← FFFFFFFFFFFFFFFFH;(* enables all MCA features *)FIIF (IA32_MCG_CAP.MCG_LMCE_P = 1 and IA32_FEATURE_CONTROL.LOCK = 1 and IA32_FEATURE_CONTROL.LMCE_ON= 1)(* IA32_MCG_EXT_CTL register is present and platform has enabled LMCE to permit system software to use LMCE *)THENIA32_MCG_EXT_CTL ← IA32_MCG_EXT_CTL | 01H;(* System software enables LMCE capability for hardware to signal MCE to a single logical processor*)FI(* Determine number of error-reporting banks supported *)COUNT← IA32_MCG_CAP.Count;MAX_BANK_NUMBER ← COUNT - 1;IF (Processor Family is 6H and Processor EXTMODEL:MODEL is less than 1AH)THEN(* Enable logging of all errors except for MC0_CTL register *)FOR error-reporting banks (1 through MAX_BANK_NUMBER)DOIA32_MCi_CTL ← 0FFFFFFFFFFFFFFFFH;ODELSE(* Enable logging of all errors including MC0_CTL register *)FOR error-reporting banks (0 through MAX_BANK_NUMBER)DOIA32_MCi_CTL ← 0FFFFFFFFFFFFFFFFH;ODFI(* BIOS clears all errors only on power-on reset *)IF (BIOS detects Power-on reset)THENFOR error-reporting banks (0 through MAX_BANK_NUMBER)DOIA32_MCi_STATUS ← 0;ODELSEFOR error-reporting banks (0 through MAX_BANK_NUMBER)DO(Optional for BIOS and OS) Log valid errors(OS only) IA32_MCi_STATUS ← 0;ODFIFISetup the Machine Check Exception (#MC) handler for vector 18 in IDTSet the MCE bit (bit 6) in CR4 register to enable Machine-Check ExceptionsFI


MSR的读写

x86平台读写MSR有专门的指令,分别是rdmsr和wrmsr。下面是MSR读写的一个基本实现:

/**  Returns a 64-bit Machine Specific Register(MSR).  Reads and returns the 64-bit MSR specified by Index. No parameter checking is  performed on Index, and some Index values may cause CPU exceptions. The  caller must either guarantee that Index is valid, or the caller must set up  exception handlers to catch the exceptions. This function is only available  on IA-32 and X64.  @param  Index The 32-bit MSR index to read.  @return The value of the MSR identified by Index.**/UINT64EFIAPIAsmReadMsr64 (  IN      UINT32                    Index  ){  UINT32 LowData;  UINT32 HighData;    __asm__ __volatile__ (    "rdmsr"    : "=a" (LowData),   // %0      "=d" (HighData)   // %1    : "c"  (Index)      // %2    );      return (((UINT64)HighData) << 32) | LowData;}/**  Writes a 64-bit value to a Machine Specific Register(MSR), and returns the  value.  Writes the 64-bit value specified by Value to the MSR specified by Index. The  64-bit value written to the MSR is returned. No parameter checking is  performed on Index or Value, and some of these may cause CPU exceptions. The  caller must either guarantee that Index and Value are valid, or the caller  must establish proper exception handlers. This function is only available on  IA-32 and X64.  @param  Index The 32-bit MSR index to write.  @param  Value The 64-bit value to write to the MSR.  @return Value**/UINT64EFIAPIAsmWriteMsr64 (  IN      UINT32                    Index,  IN      UINT64                    Value  ){  UINT32 LowData;  UINT32 HighData;  LowData  = (UINT32)(Value);  HighData = (UINT32)(Value >> 32);    __asm__ __volatile__ (    "wrmsr"    :    : "c" (Index),      "a" (LowData),      "d" (HighData)    );      return Value;}
上面是GCC版本的,还有汇编版本的:

;-------------------------------------------------------; UINT64; EFIAPI; AsmReadMsr64 (;   IN UINT64  Index;   );;-------------------------------------------------------AsmReadMsr64    PROC    mov     ecx, [esp + 4]    rdmsr    retAsmReadMsr64    ENDP;-------------------------------------------------------; UINT64; EFIAPI; AsmWriteMsr64 (;   IN UINT32  Index,;   IN UINT64  Value;   );;--------------------------------------------------------AsmWriteMsr64   PROC    mov     edx, [esp + 12]    mov     eax, [esp + 8]    mov     ecx, [esp + 4]    wrmsr    retAsmWriteMsr64   ENDP


0 0