简述
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-15,BIT16-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的初始化
手册上有一个伪代码可供参考:
1 |
IF CPU supports MCE |
MSR的读写
x86平台读写MSR有专门的指令,分别是rdmsr和wrmsr。下面是MSR读写的一个基本实现:
gcc版本:
1 |
/** |
汇编版:
1 |
;------------------------------------------------------- |
MCE
Machine Check Exception (MCE) 是CPU发现硬件错误时触发的异常(exception),中断号是18,异常的类型是abort:
导致MCE的原因
导致mce的原因主要有:总线故障、内存ECC校验错、cache错误、TLB错误、内部时钟错误,等等。不仅硬件故障会引起MCE,不恰当的BIOS配置、firmware bug、软件bug也有可能引起MCE。
在 Linux 系统上,如果发生的MCE错误属于可以自动纠正的类型,那么系统保持继续运行,MCE错误日志会记录在一个ring buffer中(这个ring buffer通过设备文件/dev/mcelog来访问),用 mcelog(8) 命令可以读取MCE日志,系统通常会通过cron任务或者mcelog.service把ring buffer中的MCE日志写入/var/log/mcelog文件中。如果发生的MCE错误属于无法恢复的类型,那么系统会panic,错误信息会输出在终端上和message buffer里。
分析MCE
分析MCE需要参考Intel手册第3卷,15章Machine-Check Architecture和16章Interpreting Machine-Check Error Codes。由于MCE在不同型号的CPU上有差异,解读的方法也有不同,第16章是专门解释在不同的CPU型号上如何解读MCE错误码。
每个CPU上有一组寄存器称为 Machine-Check MSR (Model-Specific Register),用于Machine-Check的控制与记录,分为全局寄存器和若干Bank寄存器(CPU的硬件单元分成若干组,每一组称为一个Bank)。当发生MCE时,错误信息记录在全局状态寄存器 MCG_STATUS MSR 和Bank寄存器 MCi_STATUS MSR 中,如下图黄色框所示:
分析MCE的方法,就是根据Intel手册解读上述寄存器中记录的错误信息。Linux内核把MCE的信息保存在下面的结构体中:
1 |
/arch/x86/include/asm/mce.h : |
下面的MCE错误信息截取自一台因MCE而crash的机器,我们以此为例来解读一下MCE信息。
注:产生以下信息的内核函数是:
static void print_mce(struct mce *m)
源程序:arch/x86/kernel/cpu/mcheck/mce.c
如果需要的话,阅读源程序可以理解输出的信息与原始数据的对应关系。
1 |
[Hardware Error]: CPU 3: Machine Check Exception: 4 Bank 5: be00000000800400 |
其中CPU和Bank是MCE的接收者:
- CPU 3 – 表示检测到MCE错误的是3号CPU,对应struct mce的extcpu字段;
- Bank 5 – 一组硬件单元称为一个bank,每个bank对应一组machine-check寄存器;
MCE的错误代码包括两部分:
- Machine Check Exception: 4 – 表示 IA32_MCG_STATUS MSR寄存器的状态码是4(含义见后文),对应 mcgstatus字段;
- be00000000800400 – 表示 IA32_MCi_STATUS MSR寄存器中的错误码(含义见后文),对应status字段。
Machine Check Excheption: 4 的含义
它来自全局状态寄存器 IA32_MCG_STATUS MSR,(对应struct mce的 mcgstatus字段),只用到三个bit,如下所示。4表示machine-check in progress。
- Bit 0: Restart IP Valid. 表示程序的执行是否可以在被异常中断的指令处重新开始。
- Bit 1: Error IP Valid. 表示被中断的指令是否与MCE错误直接相关。
- Bit 2: Machine Check In Progress. 表示 machine check 正在进行中。
be00000000800400 的含义
它来自bank寄存器IA32_MCi_STATUS MSR,(对应struct mce的status字段)。
be00000000800400 的二进制位如下:
1 |
Bit 63: VAL. 表示本寄存器中包含有效的错误码 |
本例中0x0400表示Internal timer error:
1 |
– Simple Error Codes: |
下一步,由于Bits [16:31] 是非零值0x0080,包含的是特定CPU型号相关的扩展错误码,我们要参考Intel手册第三卷第16章。首先确定CPU型号,我们需要的是CPU faimily和model,从/proc/cpuinfo中可以找到:
1 |
# less /proc/cpuinfo |
根据cpu family/model,即06_1AH,找到Intel手册中对应的章节,但是没找到匹配Internal timer error和0x0080的条目。所以只能到此为止了。
所以MCE检测的错误为:
CPU 3 发现了无法纠正的MCE,是Internal timer error,被中断的指令与MCE不相关,被中断的程序指令不能恢复运行,CPU的上下文已被MCE破坏
如何禁用MCE
可以在 /boot/grub/grub.conf 中加入以下内容:mce=off
还有其它的MCE选项,比如禁用CMCI(Corrected Machine Check Interrupt),或者禁用MCE日志,等等,例如:
mce=off — Disable machine check
mce=no_cmci — Disable CMCI(Corrected Machine Check Interrupt)
参见文档:Documentation/x86/x86_64/boot-options.txt
每个 CPU 都有一个sysfs接口:
/sys/devices/system/machinecheck/machinecheckN
注:(N = CPU number)
其中包含的可调参数参见:Documentation/x86/x86_64/machinecheck
参考资料
Intel 64 and IA-32 Architectures Software Developer’s Manual
AMD64 Architecture Programmer’s Manual Volume 2: System Programming
FROM :ol4three.com | Author:ol4three
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论