ACPI相关(8)- ACPI Platform Error Interfaces

一、简介

平台上的硬件使用多样的方式向上层软件报告硬件错误,有的通过 PCI-E 总线传递错误消息,有的需要读写特定的寄存器组来得到错误信息,还有的通过产生特定的中断或者异常来报告错误状态。在这些各式各样方法的背后,是硬件设计人员和软件开发人员耗费大量的时间用来定义接口以及接口实现。这样做的直接后果是增加了太多不必要的开销。因此,一个统一高效的接口无疑是非常有必要的,APEI(Advanced Platform Error Interfaces)的出现,正是为了解决这一长久存在的问题。新的 APEI 规范统一了软硬件之间的接口,降低了软硬件开发人员的开发复杂度。不但如此,新的 APEI 接口更加灵活,便于扩展。譬如说,APEI 的规范定义中大量使用了 UEFI 中已有的结构定义,这样做极大提高了 APEI 和未来的 UEFI BIOS 的兼容性。

APEI 的结构组成并不复杂,简单而言,就是 4 张表。他们分别是:

BOOT Error Record Table (BERT)

Error Record Serialization Table (ERST)

Error Injection Table (EINJ)

Hardware Error Source Table (HEST)

以下将对这 4 张表的功能以及在 Linux 中的实现分别加以介绍。

 

二、APEI相关ACPI表

1、BERT (Boot Error Record Table)

在 OS 未接管平台的控制权限之前 firmware(如 BIOS 或者 UEFI)检测到错误,导致系统无法继续启动,可以通过 BIOS/FIRMWARE 将这种类型的错误写入到特定的存储位置。这样一来,在下一次的正常启动过程中,OS 可以通过特定的方法将之前保存的错误读取出来分析并处理。

当系统运行过程中 firmware 检测到了致命错误,以至于 firmware 决定不通知 OS 而是直接重启(如CPU风扇突然坏了,瞬间过热,如果不立刻重启会烧毁 CPU),在重启前 firmware 可以记录下相关的错误信息以便之后分析出错原因。

注:只有 BIOS/FIRMWARE 才有能力对 BERT 执行写入操作;对于 OS 而言,BERT 仅仅是一个只读的表。BERT 出现的意义在于希望采用一种统一的接口来记录特定类型的硬件错误(主要是一些致命的),从而简化 BIOS/FIRMWARE 和 OS 的实现。

2、ERST (Error Record Serialization Table)

ERST 本质上是一个用来永久存储错误的抽象接口。软件可以通过 ERST表 将各种错误信息保存到 ERST 中,再由 ERST 写入到可用于永久存储的物理介质中。ERST 并没有一个严格的定义来界定什么是“错误”,换言之,软件可以保存任何信息到 ERST 中,只要软件认为是有意义,有价值的信息就可以。

物理介质未必一定是flash或NVRAM,可以是网络存储或者其他。

ERST 的主要作用就是用来存储各种硬件或者平台相关的错误,错误类型包括 Corrected Error(CE),Uncorrected Recoverable Error(UCR),以及 Uncorrected Non-Recoverable Error,或者说 Fatal Error。换言之,只要是软件可以记录的错误,都可以将其保存到 ERST 当中。加上之前谈到的 BERT 表,这样一来,无论系统运行在哪个阶段,当出现硬件或平台相关的错误时,通过 APEI 接口,都有办法将错误保存下来。这样一来就可以在之后通过适当的方法将错误读取出来进行分析,从而加快定位产生错误的原因并加以解决。

3、HEST(Hardware Error Source Table

在 HEST 中定义了很多硬件相关的错误源和错误类型。定义这些硬件错误源的目的在于标准化软硬件错误接口的实现。有了 HEST,当发生特定类型的硬件错误,如 PCI-E 设备产生了一个Uncorrected Recoverable类型的错误时,BIOS/FIRMWARE 有统一的方法更新特定的寄存器和内部状态,软件有统一的方法去处理和解析错误。HEST 中定义了很多硬件错误源,如 MCE、PCI-E、GHES 等等。

其中最为特殊也是最为重要的硬件错误源类型就是 GHES(Generic Hardware Error Source)。GHES 是一个通用硬件错误源,换言之,任何类型的硬件错误都可以使用 GHES 来定义,而无需使用之前提到的特定硬件错误源,如内存控制器错误等。

当前无论是软件还是 BIOS/FIRMWARE 的实现,基本上都是只使用 GHES 来实现 HEST 的功能,至于其他特定的硬件错误源,基本上都没有使用(PCI-E AER 的部分代码检测了 PCI-E 类型的硬件错误源)。

在 FFM 使能的情况下,一般而言,所有 CE 类型的错误通过 SCI 中断报告给 OS,然后 OS 在 HEST/GHES 中查表,检测并处理可能的硬件错误;所有 UC 和 Fatal 类型的错误通过 NMI 报告给 OS,然后 OS 在 NMI 的 handler 中查表,检测并处理可能的硬件错误。这些规定并不是硬性要求的,平台设计者完全可以根据需要使用 NMI 来处理所有的错误类型,包括 CE, UC 和 Fatal 类型的错误,也可以只使用 NMI 来处理 UC 和 Fatal 类型的错误,而使用轮询的方式来处理 CE 类型的错误。

a、错误源类型

(1) IA-32架构相关的检测校验错误描述

(2) PCIE AER错误源(root port、设备以及桥的结构)

(3) Generic Hardware Error Source

OSPM使用Generic Hardware Error Source结构中的信息,从error status block(平台为记录错误状态信息(由谁来初始化)而预留的内存范围)读取错误数据来配置错误处理程序

由于Generic Hardware Error Source不是标准结构,OSPM不实现对配置和控制操作的内置支持。错误源必须在启动期间由系统固件配置。

 

b、HEST数据结构

 

1> Generic Hardware Error Source中描述了错误源的如下信息:

(1)是否支持FFM(Firmware-First handling)

(2)错误信息长度

(3)记录错误源信息寄存器的物理地址

(4)指定错误源如何通知OSPM发生了错误(Polled、External Interrupt、NMI、SCI等)

2> Generic Error Data

(1)Error Status Block包含给定的通用错误源的错误状态信息。OSPM提供了一个错误处理程序,可以根据特定操作系统的需要格式化这些块中的一个或多个

(2)包含信息:

a、顶层是一个通用的错误状态块结构包括:

1)下面描述的错误信息的类型和状态

2)数据长度及错误严重级别

b、在通用错误状态块结构之后是一个或多个通用错误数据输入结构,包括:

1)错误数据类型和严重级别

2)错误格式

d、通知事件

对于可纠正的错误,建议使用事件通知,其中处理错误报告中的延迟对于系统正常运行而言并不关键。事件通知的实现需要平台在ACPI命名空间中定义一个带有PNP ID PNP0C33的设备,称为错误设备。该设备用于通知OSPM一个通用错误源正在报告一个错误。由于多个通用错误源可以使用事件通知,所以OSPM负责扫描这些通用错误源的列表并检查block status字段,以确定报告错误的源。

平台负责提供一种控制方法,该方法在错误设备(PNP0C33)上发出通知,通知代码的类型为0x80。对于传统的ACPI平台,事件通知遵循GPE事件通知模型。平台为错误通知实现了一个通用事件(GPE), GPE有一个相关的控制方法。

错误通知的GPE控制方法示例如下:

Method (\_GPE._L08) { // GPE 8 level error notification

Notify ( error_device , 0x80)

}

平台使用事件通知时的总体流程为:

平台使用表18-379和表18-380中的格式枚举带有事件的错误源作为通知方法

平台将一个错误设备PNP ID PNP0C33呈现给OSPM

当平台准备报告错误时,平台填充错误状态块,包括块状态字段(表18-380)

传统的ACPI平台使用SCI在适当的GPE上发出错误信号:

•OSPM评估与此事件相关的GPE控制方法,如上所示。

•OSPM通过检查具有SCI通用通知类型的所有通用错误源的错误状态块来识别此报告错误的源,从而响应此通知。

e、Firmware First Error Handling

硬件故障触发的中断可以不直接通知OS,而先通过SMI进入BIOS/firmware,让BIOS/firmware可以先行处理硬件故障,视情况再决定是否通知OS继续处理。这种模式称为Firmware First Model。

4、EINJ(Error Injection Table)

EINJ 的主要作用是用来注入错误并触发错误,或者说,EINJ 是一个用来测试的表。EINJ 可以注入各种类型的硬件错误,这些注入的错误不是模拟的,而是通过 EINJ 和底层 firmware 以及硬件配合真实产生的。通过 EINJ 注入的硬件错误是真正的错误,和硬件真实发生的错误没有差别。这样一来,平台设计者和软件开发人员可以使用 EINJ 在软硬件发布之前测试平台的软硬件环境是否可靠,是否具有足够的容错性以及完备性,而不必等到在平台发布之后的使用过程中出现错误时再来检测系统是否可靠。

EINJ 支持的错误注入方式非常丰富。从错误类型上划分,和 ERST 一样,包括 Corrected Error(CE),Uncorrected Recoverable Error(UCR),以及 Uncorrected Non-Recoverable Error,或者说 Fatal Error。从错误来源划分,可以分为 Processor,Memory,以及 PCI-E 设备等类型。通过交叉组合,至少有 9 种可以注入的错误。

0x00000001 Processor Correctable      0x00000002 Processor Uncorrectable non-fatal

0x00000004 Processor Uncorrectable fatal      0x00000008 Memory Correctable

0x00000010 Memory Uncorrectable non-fatal   0x00000020 Memory Uncorrectable fatal

0x00000040 PCI Express Correctable     0x00000080 PCI Express Uncorrectable fatal

0x00000100 PCI Express Uncorrectable non-fatal   0x00000200 Platform Correctable

0x00000400 Platform Uncorrectable non-fatal   0x00000800 Platform Uncorrectable fatal

使用 EINJ 进行错误注入有两个步骤:

第一步根据需要产生错误注入需要的 trigger 表(trigger action table),这个 trigger 表是 BIOS/FIRMWARE 根据用户需要注入的错误类型动态生成的,不能人为手工构造;

第二步是触发这个 trigger 表,让其在合适的位置产生需要的错误。至于产生了错误之后,如何处理错误,如何修复错误之类的事情,和 EINJ 无关。

EINJ 的注入过程基本上是一个 2 步操作:

1、使用 SET_ERROR_TYPE 这个 ACTION 向 EINJ 表中注入一个错误

2、根据第一步设定的错误,与 EINJ 相关的 firmware 会动态生成一个 Trigger Error Action 表,使用 GET_TRIGGER_ERROR_ACTION_TABLE 这个动作可以得到这个 trigger 表,然后操作这个 trigger 表可以触发之前注入的错误,从而达到测试特定错误类型的目的。

在整个过程当中,关键点是从 GET_TRIGGER_ERROR_ACTION_TABLE 动作完成后得到的 trigger 表的基地址。这个 trigger 表的基地址作为入口参数被 __einj_error_trigger 函数调用,最终完成错误触发。简单来说,__einj_error_trigger 需要完成两件事:

  1. 根据 GET_TRIGGER_ERROR_ACTION_TABLE 返回的 trigger 表的地址(本质上是一个复合结构)进行相应的 IO 资源分配(这里的 IO 资源主要由 GAS 提供)
  2. 调用 ACPI_EINJ_TRIGGER_ERROR 动作完成错误触发。

EINJ注入错误例:

# cd /sys/kernel/debug/apei/einj

# cat available_error_type # See which errors can be injected

 

显示为目前支持注入的硬件错误。

0x00000002 Processor Uncorrectable non-fatal

0x00000008 Memory Correctable

0x00000010 Memory Uncorrectable non-fatal

 

# echo 0x12345000 > param1 # Set memory address for injection

# echo $((-1 << 12)) > param2 # Mask 0xfffffffffffff000 - anywhere in this page

# echo 0x8 > error_type # Choose correctable memory error

# echo 1 > error_inject # Inject now

memory_address = param1;

memory_address_range = param2;

apicid = param3;

pcie_sbdf = param4;

 

对于内存错误(类型0x8、0x10和0x20),使用param1在param2中带有掩码(0x0等于所有1)。对于PCI表达段(总线,设备和)的错误(类型0x40、0x80和0x100)函数是使用param1指定的:

 

         31     24 23    16 15    11 10      8  7        0

+-------------------------------------------------+

| segment |   bus  | device | function | reserved |

+-------------------------------------------------+

 

ACPI 5.0 BIOS也可能允许注入特定于供应商的错误。在这种情况下,名为vendor的文件将包含标识信息,从BIOS中希望可以允许希望使用的应用程序使用特定于供应商的扩展,以告知他们正在BIOS上运行支持它。

所有供应商扩展都在其中设置了0x80000000位error_type。文件vendor_flags控制对param1的解释和param2(1 =处理器,2 =内存,4 = PCI)。

三、内核处理错误流程

ghes_probe函数中,根据HEST表中传递的检测错误类型,查看相关kernel配置选项是否支持。包括arm相关的SEA错误,NMI,本地中断。

调用ghes_new函数,初始化struct ghes结构。初始化ghes结构,映射表中Error Status Address。为存放错误信息数据申请内存。

根据上报错误方式,注册不同的处理流程,包括如下:

(1)轮询方式,根据表中传递的poll_interval时间,创建定时器。在定时器处理函数ghes_poll_func中,调用ghes_proc。在这个函数中:

a、读取GHES结构中传递的Error Status Address。首先将读到的struct acpi_hest_generic_status结构拷贝到前面申请的内存中,检测相关错误信息长度是否合法。然后将后面的错误信息拷贝。

b、如上报错误的严重级别大于GHES_SEV_PANIC,则将错误信息打印,清除错误状态以及记录错误信息的内存块。然后进入kernel panic。
  c、调用ghes_do_proc函数处理错误。这个函数中,获取错误数据块中各section中section_type以及error_severity。同时判断fru_id和fru_text字段是否有效。 1>如为内存相关错误,以下为错误类型分类

 * @HW_EVENT_ERR_CORRECTED: Corrected Error - 表示检测到ECC纠正的错误

 * @HW_EVENT_ERR_UNCORRECTED : 表示ECC无法纠正的错误,但不是致命的错误(可能是在未使用的内存区域,或者内存控制器可以从中恢复,例如,通过重新尝试操作)

 * @HW_EVENT_ERR_DEFERRED:  Deferred Error - 表示处理不紧急的不可纠正的错误。这可能是由于硬件数据中毒,系统可以继续操作,直到中毒的数据被消耗。也可以采取主动的措施,例如,offlining页面等。

 * @HW_EVENT_ERR_FATAL:致命错误-无法恢复的不可更正错误。

 * @HW_EVENT_ERR_INFO:规范定义了第四种类型的错误:信息日志。

 

首先获取struct cper_sec_mem_err数据块。调用

ghes_edac_report_mem_error函数,这个函数中,将错误信息中包括错误类型,错误地址,内存颗粒,错误内存位置填充到错误报告buffer中(struct edac_raw_error_desc)。错误类型如下:

 

把错误写到ftrace的一个跟踪项中,最后调用edac_raw_mc_handle_error,

分别通过edac_ce_error处理ECC类型的错误以及调用edac_ue_error处理其他错误。

架构相关处理过程,在x86中,调用MCE相关处理函数,记录log。

最后调用ghes_handle_memory_failure,如果(1)错误级别为可修复级别且CPER_SEC_ERROR_THRESHOLD_EXCEEDED置位(表示内核中止使用这个资源)。

(2)错误是可恢复的类型。满足上述条件之一则调用memory_failure_queue函数。这个函数在检测到页面的硬件内存损坏时由硬件错误处理程序调用。它调度错误页面的恢复,包括删除页面,杀死进程等。

2> 如为pcie aer错误

PCIe AER错误需要发送到AER驱动程序进行报告和恢复。GHES的严重程度与以下AER严重程度相对应,并需要进行以下处理:

 GHES_SEV_CORRECTABLE -> AER_CORRECTABLE  --- 需要由AER驱动报告,但不需要恢复。

GHES_SEV_RECOVERABLE -> AER_NONFATAL

GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL

这两种情况都需要AER驱动报告和恢复。

GHES_SEV_PANIC不会进行这种处理,因为内核必须将进入panic状态。

3> ARM结构相关的错误

把错误写到ftrace的一个跟踪项中

4>其他错误

使用trace机制,拔错误写到ftrace的一个跟踪项。

(2)如果为外部中断,则通过GHES中传递的中断号,申请中断处理函数,在中断处理函数ghes_irq_func调用ghes_proc。下面的处理流程和上面轮询一致。

(3)同样的如果为SCI中断,调用notifier_call回调函数ghes_notify_hed,遍历ghes_hed链表,分别执行ghes_proc函数,处理错误。

(4)如果为NMI中断,处理错误级别大于GHES_SEV_PANIC,则直接kernel panic。否则如配置CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG,则调到工作队列处理函数ghes_proc_in_irq中。执行相当于下半部的处理过程。最终调用ghes_do_proc函数执行上述相关错误的处理。

 

四、x86错误寄存器

1、MCA

MACHINE-CHECK ARCHITECTURE(MCA) 和 machine-check exception(#MC) 机制用来对服务器硬件进行自检,并在发现硬件错误的时候发出中断或异常。系统软件收到中断或异常后,会对其进行响应,进行相应的修复、告警或其他策略等动作。通过Intel的这个RAS特性,保证在发生crash等错误前,服务器可以有机会做一些容错处理,大大提升了Intel在数据中心高可靠服务器领域的竞争实力。

通过MCA,系统可以探测硬件错误,如系统总线错误,ECC错误,奇偶校验错误,cache错误,TLB错误等。MCA机制使用到了一组相关的MSR,通过这些MSR,MCA可以对硬件错误进行检测并记录检测到的错误信息。注意这里有一个bank的概念,可以简单理解为组。如bank1里面有MSR1_STATUS, MSR1_CTL, MSR1_ADDR;bank2里面有MSR2_STATUS, MSR2_CTL, MSR2_ADDR;

2、MSR

Machine check使用了一组相关的MSR。这些MSR有两部分组成,第一部分是全局控制和状态寄存器(global),另外一部分是一些记录错误信息的banks.

每一个错误报告的bank都同处理器中一个特定的硬件单元(或一组硬件单元)相关联。使用RDMSR或WRMSR来对这些寄存器进行读写。

全局相关的寄存器组定义了如何开启 MCA 的能力。每一个 BANK 则具体对应一类错误源,如 CPU,MEMORY,CACHE,CHIPSET 等等。每一个 BANK 都可以进行单独的控制,这样软件就能够针对每一个 BANK 使用特定的方式进行处理。由于 MCA 以时间窗口为单位对错误进行采样,因此在每一个采样结束时有可能会发现有不止一个的错误产生,但是只会触发一次中断或者异常,因此当软件进行处理时必要要轮询所有的 BANK 以确保每一个产生的错误都可以被处理。

(1)全局寄存器

IA32_MCG_CAP MSR寄存器

 

* 0~7bit 包含当前处理器错误信息的banks数量

* MCG_ELOG_P (extended error logging) flag, bit 26 — Indicates (when set) that the processor allows platform firmware to be invoked when an error is detected so that it may provide additional platform specific information in an ACPI format “Generic Error Data Entry” that augments the data included in machine check bank registers.

(2)banks

每个错误报告寄存器块(bank)包含IA32_MCi_CTL, IA32_MCi_STATUS, IA32_MCi_ADDR和IA32_MCi_MISC MSRs四个寄存器。

a、IA32_MCi_CTL MSRs

IA32_MCi_CTL MSR 控制着某个(某组)硬件单元产生的error的 error reporting。64个标志位 (EEj)的每一位代表一种可能的error,设定EEj的相应标志来使能相关error的 reporting,清除了就关闭相应error的 reporting功能。如果处理器没有实现某些error,那么是不会改变相应位的。

b、IA32_MCi_STATUS

如果VAL(vaild flag)被设置了,IA32_MCi_STATUS MSR都包含了一个 machine-check error的相关信息。

 

MCA (machine-check architecture) error code field, bits 15:0 — 当 machine-check error被探测到,这里就写入了通用架构相关的 machine-check error code。既然是架构相关,那么所有种类的CPU都会理解这些error code的含义。

MSCOD Model Specific Error Code 31:16 — 指定了 model-specific error code,这种error code是每种CPU都有不同解释的,唯一的标识了一种错误

Reserved, Error Status, and Other Information fields, bits 56:32 — 主要是Other Information用来提供额外信息和error发生的counter累加器用来判断是否超过阀值需要告警。

PCC (processor context corrupt) flag, bit 57 — 当设定了,就意味着整个处理器都被探测到的错误污染了,没法进行修复或者重新执行指令。当没有设置,表明处理器当前还没有被错误污染,软件有可能通过恢复动作让系统正常运行

ADDRV (IA32_MCi_ADDR register valid) flag, bit 58 — 当设定了,就代表 IA32_MCi_ADDR寄存器中含有发生错误时候的内存地址

MISCV (IA32_MCi_MISC register valid) flag, bit 59 — 当设定了,表明 IA32_MCi_MISC寄存器中包含了错误的额外信息

EN (error enabled) flag, bit 60 — 当设定了,就说明了该错误在 IA32_MCi_CTL寄存器中对应的 EEj位被设定了

UC (error uncorrected) flag, bit 61 — 当设定了,表明处理器不能依靠硬件来矫正这个错误(如多bit的内存错误),当清除则该错误可以被处理器硬件纠正(如单bit内存错误)

OVER (machine check overflow) flag, bit 62 — 当设定了,就表明发生了MC嵌套。

VAL (IA32_MCi_STATUS register valid) flag, bit 63 — 当设定了,就意味着 IA32_MCi_STATUS中的错误信息是有效的,处理器负责设定VAL,系统软件负责清除这个位(当处理完成)。

c、IA32_MCi_ADDR

当 IA32_MCi_STATUS. ADDRV = 1的时候,IA32_MCi_ADDR中含有产生本次错误的指令或数据内存的内存地址。根据遇到的错误不同,返回的地址可能是一个段内偏移量、线性地址或物理地址。可以通过写入全0来清除该寄存器。

d、IA32_MCi_MISC MSRs

当 IA32_MCi_STATUS. MISCV = 1时, IA32_MCi_MISC MSR中包含了本次 machine-check error的额外信息,软件通过显示的写入全0来清除该寄存器。额外信息包括IA32_MCi_ADDR记录的内存类型

如果IA32_MCi_STATUS.MISCV =1 且  IA32_MCG_CAP[24] = 1, IA32_MCi_MISC_MSR的定义如下图所示,用来支持软件恢复 uncorrected errors

 

(3)扩展寄存器

当IA32_MCG_CAP.MCG_EXT_P = 1 时,实现了更多的扩展machine-check state MSRs,可以记录更加详细的错误信息。

  • 10
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值