APEI 錯誤注入¶
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
此檔案顯示支援的錯誤型別。
錯誤型別值
錯誤描述
0x00000001
處理器可糾正錯誤
0x00000002
處理器不可糾正非致命錯誤
0x00000004
處理器不可糾正致命錯誤
0x00000008
記憶體可糾正錯誤
0x00000010
記憶體不可糾正非致命錯誤
0x00000020
記憶體不可糾正致命錯誤
0x00000040
PCI Express 可糾正錯誤
0x00000080
PCI Express 不可糾正非致命錯誤
0x00000100
PCI Express 不可糾正致命錯誤
0x00000200
平臺可糾正錯誤
0x00000400
平臺不可糾正非致命錯誤
0x00000800
平臺不可糾正致命錯誤
檔案內容的格式如上所述,但只顯示可用的錯誤型別。
error_type
設定要注入的錯誤型別的值。可能的錯誤型別在上面的 available_error_type 檔案中定義。
error_inject
向此檔案寫入任何整數以觸發錯誤注入。請確保您已指定所有必要的錯誤引數,即此寫入應該是注入錯誤的最後一步。
flags
適用於核心版本 3.13 及更高版本。用於指定 param{1..4} 中哪些是有效的,以及韌體在注入時應使用哪些。該值是 ACPI5.0 規範中 SET_ERROR_TYPE_WITH_ADDRESS 資料結構指定的位掩碼。
- 位 0
處理器 APIC 欄位有效(參見下面的 param3)。
- 位 1
記憶體地址和掩碼有效(param1 和 param2)。
- 位 2
PCIe (段、匯流排、裝置、功能) 有效(參見下面的 param4)。
如果設定為零,則模擬傳統行為,其中注入型別只指定一個設定位,並且 param1 被複用。
param1
此檔案用於設定第一個錯誤引數值。其效果取決於 error_type 中指定的錯誤型別。例如,如果錯誤型別是記憶體相關型別,則 param1 應該是一個有效的物理記憶體地址。[除非設定了“flag”——參見上文]
param2
與上面的 param1 用途相同。例如,如果錯誤型別是記憶體相關型別,則 param2 應該是一個物理記憶體地址掩碼。Linux 需要頁或更細粒度,例如 0xfffffffffffff000。
param3
當“flags”中設定了 0x1 位時使用,用於指定 APIC ID
param4 當“flags”中設定了 0x4 位時使用,用於指定目標 PCIe 裝置
notrigger
錯誤注入機制是一個兩步過程。首先注入錯誤,然後執行一些操作來觸發它。將“notrigger”設定為 1 會跳過觸發階段,這*可能*允許使用者透過簡單地訪問作為錯誤注入目標的 CPU、記憶體位置或裝置,在其他上下文中導致錯誤。這是否實際有效取決於 BIOS 在觸發階段實際包含了哪些操作。
CXL 錯誤型別從 ACPI 6.5 開始支援(如果存在 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),您可以將 flags 設定為 0x3(位 0 用 param3,位 1 用 param1 和 param2),以便為正在注入的錯誤簽名新增更多資訊。實際傳遞的資料是這樣的:
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)
一個 CXL 錯誤注入示例,其中 $dport_dev=0000:e0:01.1
# 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 模式)。
- 可以使用以下序列:
確定 enclave 頁面的物理地址
使用“notrigger=1”模式進行注入(這將設定注入地址,但不會實際注入)
進入 enclave
將資料儲存到與步驟 1 中的物理地址匹配的虛擬地址
對該虛擬地址執行 CLFLUSH
自旋延遲 250ms
從該虛擬地址讀取。這將觸發錯誤
有關 EINJ 的更多資訊,請參閱 ACPI 規範 4.0 版本第 17.5 節和 ACPI 5.0 版本第 18.6 節。