APEI Error INJection

EINJ 提供硬件错误注入机制。它对于调试和测试 APEI 和 RAS 功能非常有用。

您需要先检查您的 BIOS 是否支持 EINJ。为此,请查找类似于以下的早期启动消息:

ACPI: EINJ 0x000000007370A000 000150 (v01 INTEL           00000001 INTL 00000001)

这表明 BIOS 正在公开一个 EINJ 表 - 这是注入的机制。

或者,在 /sys/firmware/acpi/tables 中查找“EINJ”文件,它是同一事物的不同表示。

如果上述内容不存在,并不一定意味着不支持 EINJ:在放弃之前,请进入 BIOS 设置,查看 BIOS 是否有启用错误注入的选项。查找名为 WHEA 或类似的东西。通常,您需要先启用 ACPI5 支持选项,以便查看 BIOS 菜单支持和公开的 APEI、EINJ、... 功能。

要使用 EINJ,请确保在内核配置中启用了以下选项:

CONFIG_DEBUG_FS
CONFIG_ACPI_APEI
CONFIG_ACPI_APEI_EINJ

...并且(可选)启用 CXL 协议错误注入集:

CONFIG_ACPI_APEI_EINJ_CXL

EINJ 用户界面位于 <debugfs 挂载点>/apei/einj。

其中包括以下文件:

  • available_error_type

  该文件显示支持哪些错误类型:

   文件内容的格式如上,但仅显示可用的错误类型。

  • error_type

  设置注入的错误类型的值。可能的错误类型在上面的 available_error_type 文件中定义。

  • error_inject

  将任意整数写入此文件以触发错误注入。请确保您已指定所有必要的错误参数,即此写入应该是注入错误的最后一步。

  • flags

  内核版本 3.13 及以上版本中存在。用于指定哪些 param{1..4} 有效,并应由固件在注入期间使用。值是 ACPI5.0 规范中为 SET_ERROR_TYPE_WITH_ADDRESS 数据结构指定的位掩码:

    BIT 0
      处理器 APIC 字段有效(请参阅下面的 param3)。

    BIT 1
      内存地址和掩码有效(param1 和 param2)。

    BIT 2
      PCIe (seg,bus,dev,fn) 有效(请参阅下面的 param4)。

  如果设置为零,则模仿旧行为,其中注入类型仅指定一个位集,并且 param1 被多路复用。

  • param1

  此文件用于设置第一个错误参数值。其效果取决于 error_type 中指定的错误类型。例如,如果错误类型是内存相关类型,则 param1 应为有效的物理内存地址。[除非设置了“flag” - 参见上文]。

  • param2

  与上面的 param1 用途相同。例如,如果错误类型是内存相关类型,则 param2 应该是物理内存地址掩码。Linux 要求页面或更窄的粒度,例如 0xffffffffffffff000。

  • param3

  当“flags”中的 0x1 位被设置时用于指定 APIC ID。

  • param4 

  当“标志”中设置 0x4 位来指定目标 PCIe 设备时使用。

  • notrigger

  错误注入机制分为两个步骤。首先注入错误,然后执行一些操作来触发错误。将“notrigger”设置为 1 会跳过触发阶段,这可能允许用户通过简单访问 CPU、内存位置或错误注入目标设备来在其他上下文中引发错误。这是否真的有效取决于 BIOS 在触发阶段实际包含哪些操作。

从 ACPI 6.5 开始支持 CXL 错误类型(假设存在 CXL 端口)。CXL 错误类型的 EINJ 用户界面位于 <debugfs 挂载点>/cxl。以下文件属于它:

  • einj_types:

  提供与上述 available_error_types 相同的功能,但针对的是 CXL 错误类型。

  • $dport_dev/einj_inject:

  将 CXL 错误类型注入由 $dport_dev 表示的 CXL 端口,其中 $dport_dev 是 CXL 端口的名称(通常是 PCIe 设备名称)。针对 CXL 2.0+ 端口的错误注入可以使用 <debugfs 挂载点>/apei/einj 下的旧接口,而 CXL 1.1/1.0 端口注入必须使用此文件。

基于 ACPI 4.0 规范的 BIOS 版本在控制错误注入位置方面具有有限的选项。您的 BIOS 可能支持扩展(使用 param_extension=1 模块参数或启动命令行 einj.param_extension=1 启用)。这允许通过 apei/einj 中的 param1 和 param2 文件指定内存注入的地址和掩码。

基于 ACPI 5.0 规范的 BIOS 版本对注入目标具有更多控制权。对于与处理器相关的错误(类型 0x1、0x2 和 0x4),您可以将标志设置为 0x3(param3 代表位 0,param1 和 param2 代表位 1),以便将更多信息添加到要注入的错误签名中。传递的实际数据如下:

memory_address = param1;
memory_address_range = param2;
apicid = param3;
pcie_sbdf = param4;

对于内存错误(类型 0x8、0x10 和 0x20),使用 param1 和 param2 中的掩码设置地址(0x0 相当于全 1)。对于 PCI express 错误(类型 0x40、0x80 和 0x100),使用 param1 指定段、总线、设备和功能:

 31     24 23    16 15    11 10      8  7        0
+-------------------------------------------------+
| segment |   bus  | device | function | reserved |
+-------------------------------------------------+

无论如何,您已经明白了,如果有疑问,只需查看 drivers/acpi/apei/einj.c 中的代码即可。

ACPI 5.0 BIOS 也可能允许注入特定于供应商的错误。在这种情况下,名为 vendor 的文件将包含来自 BIOS 的识别信息,希望这些信息能够让希望使用特定于供应商的扩展的应用程序知道它们正在支持它的 BIOS 上运行。所有供应商扩展都在 error_type 中设置了 0x80000000 位。文件 vendor_flags 控制 param1 和 param2 的解释(1 = 处理器、2 = 内存、4 = PCI)。有关详细信息,请参阅您的 BIOS 供应商文档(如果供应商使用此功能的创造力超出我们的预期,则需要更改此 API)。

错误注入示例:

# 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 0xfffffffffffff000 > param2            # Mask - anywhere in this page
# echo 0x8 > error_type                       # Choose correctable memory error
# echo 1 > error_inject                       # Inject now

您应该在 dmesg 中看到类似这样的内容:

[22715.830801] EDAC sbridge MC3: HANDLING MCE MEMORY ERROR
[22715.834759] EDAC sbridge MC3: CPU 0: Machine Check Event: 0 Bank 7: 8c00004000010090
[22715.834759] EDAC sbridge MC3: TSC 0
[22715.834759] EDAC sbridge MC3: ADDR 12345000 EDAC sbridge MC3: MISC 144780c86
[22715.834759] EDAC sbridge MC3: PROCESSOR 0:306e7 TIME 1422553404 SOCKET 0 APIC 0
[22716.616173] EDAC MC3: 1 CE memory read error on CPU_SrcID#0_Channel#0_DIMM#0 (channel:0 slot:0 page:0x12345 offset:0x0 grain:32 syndrome:0x0 -  area:DRAM err_code:0001:0090 socket:0 channel_mask:1 rank:0)

其中 $dport_dev=0000:e0:01.1 的 CXL 错误注入示例:

# cd /sys/kernel/debug/cxl/
# ls
0000:e0:01.1 0000:0c:00.0
# cat einj_types                # See which errors can be injected
    0x00008000  CXL.mem Protocol Correctable
    0x00010000  CXL.mem Protocol Uncorrectable non-fatal
    0x00020000  CXL.mem Protocol Uncorrectable fatal
# cd 0000:e0:01.1               # Navigate to dport to inject into
# echo 0x8000 > einj_inject     # Inject error

注入 SGX enclave 的特别注意事项:

可能有一个单独的 BIOS 设置选项来启用 SGX 注入。

注入过程包括设置一些特殊的内存控制器触发器,该触发器将在下次写入目标地址时注入错误。但硬件会阻止 SGX enclave 之外的任何软件访问enclave页面(即使是 BIOS SMM 模式)。

可以使用以下顺序:

  1. 确定 enclave 页面的物理地址
  2. 使用“notrigger=1”模式进行注入(这将设置注入地址,但实际上不会注入)
  3. 进入 enclave
  4. 将数据存储到与步骤 1 中的物理地址匹配的虚拟地址
  5. 对该虚拟地址执行 CLFLUSH
  6. 旋转延迟 250 毫秒
  7. 从虚拟地址读取。这将触发错误

有关 EINJ 的更多信息,请参阅 ACPI 规范版本 4.0 第 17.5 节和 ACPI 5.0 第 18.6 节。

posted @ 2025-03-20 18:30  闹闹爸爸  阅读(529)  评论(0)    收藏  举报