Intel MCA与MCE硬件机制概述

admin 2022年1月6日01:11:19评论386 views字数 11650阅读38分50秒阅读模式

简述

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寄存器,首先看下面的图:

img

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

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

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

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

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

IA32_MCG_CAP MSR

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

img

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位介绍如下:

img

这里的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位说明如下图所示:

img

目前有就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:

img

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

IA32_MCi_STATUS MSRS

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

img

注意只有当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信息,需要根据一定的规则来确定:

img

这个可以先不关注。

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

IA32_MCi_ADDR MSRs

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

img

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

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

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

IA32_MCi_MISC MSRs

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

img

这里的Address Mode说明如下:

img

IA32_MCi_CTL2 MSRs

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

img

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

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

img

上图实际上只展示了非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来实现的,具体的示意图如下:

img

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

img

BIT0-7:中断向量;

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

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

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

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

MCA的初始化

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
IF CPU supports MCE
THEN
IF CPU supports MCA
THEN
IF (IA32_MCG_CAP.MCG_CTL_P = 1)
(* IA32_MCG_CTL register is present *)
THEN
IA32_MCG_CTL ← FFFFFFFFFFFFFFFFH;
(* enables all MCA features *)
FI
IF (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 *)
THEN
IA32_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)
DO
IA32_MCi_CTL ← 0FFFFFFFFFFFFFFFFH;
OD
ELSE
(* Enable logging of all errors including MC0_CTL register *)
FOR error-reporting banks (0 through MAX_BANK_NUMBER)
DO
IA32_MCi_CTL ← 0FFFFFFFFFFFFFFFFH;
OD
FI
(* BIOS clears all errors only on power-on reset *)
IF (BIOS detects Power-on reset)
THEN
FOR error-reporting banks (0 through MAX_BANK_NUMBER)
DO
IA32_MCi_STATUS ← 0;
OD
ELSE
FOR error-reporting banks (0 through MAX_BANK_NUMBER)
DO
(Optional for BIOS and OS) Log valid errors
(OS only) IA32_MCi_STATUS ← 0;
OD
FI
FI
Setup the Machine Check Exception (#MC) handler for vector 18 in IDT
Set the MCE bit (bit 6) in CR4 register to enable Machine-Check Exceptions
FI

MSR的读写

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

gcc版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
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.
**/
UINT64
EFIAPI
AsmReadMsr64 (
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
**/
UINT64
EFIAPI
AsmWriteMsr64 (
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;
}

汇编版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;-------------------------------------------------------
; UINT64
; EFIAPI
; AsmReadMsr64 (
; IN UINT64 Index
; );
;-------------------------------------------------------
AsmReadMsr64 PROC
mov ecx, [esp + 4]
rdmsr
ret
AsmReadMsr64 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
ret
AsmWriteMsr64 ENDP

MCE

Machine Check Exception (MCE) 是CPU发现硬件错误时触发的异常(exception),中断号是18,异常的类型是abort:

image-20210122150211454

导致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 中,如下图黄色框所示:

image-20210122150307976

分析MCE的方法,就是根据Intel手册解读上述寄存器中记录的错误信息。Linux内核把MCE的信息保存在下面的结构体中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/arch/x86/include/asm/mce.h :

0067 struct mce {
0068 __u64 status; /* 对应 IA32_MCi_STATUS MSR */
0069 __u64 misc;
0070 __u64 addr;
0071 __u64 mcgstatus;/*对应 IA32_MCG_STATUS MSR */
0072 __u64 ip;
0073 __u64 tsc; /* cpu time stamp counter */
0074 __u64 time; /* wall time_t when error was detected */
0075 __u8 cpuvendor; /* cpu vendor as encoded in system.h */
0076 __u8 inject_flags; /* software inject flags */
0077 __u16 pad;
0078 __u32 cpuid; /* CPUID 1 EAX */
0079 __u8 cs; /* code segment */
0080 __u8 bank; /* machine check bank */
0081 __u8 cpu; /* cpu number; obsolete; use extcpu now */
0082 __u8 finished; /* entry is valid */
0083 __u32 extcpu; /* linux cpu number that detected the error */
0084 __u32 socketid; /* CPU socket ID */
0085 __u32 apicid; /* CPU initial apic ID */
0086 __u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */
0087 };

下面的MCE错误信息截取自一台因MCE而crash的机器,我们以此为例来解读一下MCE信息。
注:产生以下信息的内核函数是:
static void print_mce(struct mce *m)
源程序:arch/x86/kernel/cpu/mcheck/mce.c
如果需要的话,阅读源程序可以理解输出的信息与原始数据的对应关系。

1
2
3
4
5
[Hardware Error]: CPU 3: Machine Check Exception: 4 Bank 5: be00000000800400
[Hardware Error]: TSC 128727f47a97 ADDR 3f628bd69349 MISC 1
[Hardware Error]: PROCESSOR 0:106a5 TIME 1450279905 SOCKET 1 APIC 14
[Hardware Error]: Machine check: Processor context corrupt
Kernel panic - not syncing: Fatal Machine check

其中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。

image-20210122150512960

  • 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字段)。

ia32-mci

be00000000800400 的二进制位如下:

1
2
3
4
5
6
7
8
Bit 63: VAL. 表示本寄存器中包含有效的错误码
Bit 61: UC. 表示是无法纠正的MCE
Bit 60: EN. 表示处于允许报告错误的状态
Bit 59: MISCV. 表示MCi_MISC寄存器中含有对该错误的补充信息
Bit 58: ADDRV. 表示MCi_ADDR寄存器含有发生错误的内存地址
Bit 57: PCC. 表示该CPU的上下文状态已被该错误破坏,无法恢复软件代码的运行
Bits [16:31] 包含特定CPU型号相关的扩展错误码. 本例中是0x0080.
Bits [0:15] 包含MCE错误码,该错误码是所有CPU型号通用的,分为两类:simple error codes(简单错误码) 和 compound error codes(复合错误码)

本例中0x0400表示Internal timer error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
– Simple Error Codes:
0000 0000 0000 0000 – 没有错误.
0000 0000 0000 0001 – Unclassified. 未分类的错误类型.
0000 0000 0000 0010 – ROM微码校验错
0000 0000 0000 0011 – MCE是由于别的CPU的BINT# 引起的.
0000 0000 0000 0100 – Functional redundancy check (FRC) master/slave error.
0000 0000 0000 0101 – Internal parity error.
0000 0100 0000 0000 – Internal timer error.
0000 01xx xxxx xxxx – Internal unclassified error. 至少有一个x等于1

– Compound Error Codes:
000F 0000 0000 11LL – Generic cache hierarchy errors.
000F 0000 0001 TTLL – TLB errors.
000F 0000 1MMM CCCC – Memory controller errors (Intel-only).
000F 0001 RRRR TTLL – Memory errors in the cache hierarchy.
000F 1PPT RRRR IILL – Bus and interconnect errors.

下一步,由于Bits [16:31] 是非零值0x0080,包含的是特定CPU型号相关的扩展错误码,我们要参考Intel手册第三卷第16章。首先确定CPU型号,我们需要的是CPU faimily和model,从/proc/cpuinfo中可以找到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# less /proc/cpuinfo
...
processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 26
model name : Intel(R) Xeon(R) CPU L5520 @ 2.27GHz
stepping : 5
cpu MHz : 2266.700
cache size : 8192 KB
physical id : 1
siblings : 8
core id : 2
cpu cores : 4
apicid : 20
...

根据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

http://linuxperf.com/?p=105

FROM :ol4three.com | Author:ol4three

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:11:19
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Intel MCA与MCE硬件机制概述http://cn-sec.com/archives/721319.html

发表评论

匿名网友 填写信息