RAS--APEI 报错解析流程(1)
除了APEI 中除了GHES会记录错误,在Post过程中的错误通常是通过BERT Table汇报
1.BERT
Boot Error Record Table is used to report unhandled errors that occurred in a previous boot,it is reported as a ‘one-time polled’type error source.
Bert 用于记录post 过程中产生的 error 以及UCE hang 重启 BIOS 错误状态未进行清除在下次重启扫描出的error
具体结构如图所示:
使用BERT 的header 通过section type 区分错误类型 对应到不同的错误结构 都是通过block error status addrss 链接
内存 pcie cpu的错误汇报信息结构体
使用IASL 解析BERT table:
BIOS 在Post 过程中去扫描pcie error 内存 error CPU error
NBIOErrorDetection 检测到错误 addbert ->GENERIC_PCIE_AER_ERR_ENTRY
MCAErrorDetection 检测到MCA Bank UMC 错误 addbert ->GENERIC_MEM_ERR_ENTRY
MCAErrorDetection 检测到MCA Bank PIE 错误 addbert ->GENERIC_PRO_ERR_ENTRY
CPU BERT OS 解析
内存OS 解析
PCIE OS 解析
Post过程中检测到多个BERT :有内存和CPU bank 的错误状态
BIOS 日志:两个MCA Bank 读取到错误MCA_Status
BERT Table: BIOS 汇报和OS 解析通过Boot Error Region Address 联系
Boot Error Region Address 对应到结构体
///
/// Generic Error Status Definition
///
typedef struct {
EFI_ACPI_6_2_ERROR_BLOCK_STATUS BlockStatus;
UINT32 RawDataOffset;
UINT32 RawDataLength;
UINT32 DataLength;
UINT32 ErrorSeverity;
} EFI_ACPI_6_2_GENERIC_ERROR_STATUS_STRUCTURE;
对于OS去打印HardWare error 只需要参考GHES 中的Error Block status 就会去打印 错误的GHES Table中的错误信息
typedef struct {
UINT32 UncorrectableErrorValid : 1;
UINT32 CorrectableErrorValid : 1;
UINT32 MultipleUncorrectableErrors : 1;
UINT32 MultipleCorrectableErrors : 1;
UINT32 ErrorDataEntryCount : 10;
UINT32 Reserved : 18;
} EFI_ACPI_6_2_ERROR_BLOCK_STATUS;
也就是当扫描到BlockStatus 存在错误状态 OS就会上报Hardware error 然后清除错误状态
后面接着结构体,后面的结构体会根据SectionType 接着内存/CPU/PCIE 的结构体
typedef struct {
UINT8 SectionType[16];
UINT32 ErrorSeverity;
UINT16 Revision;
UINT8 ValidationBits;
UINT8 Flags;
UINT32 ErrorDataLength;
UINT8 FruId[16];
UINT8 FruText[20];
UINT8 Timestamp[8];
} EFI_ACPI_6_2_GENERIC_ERROR_DATA_ENTRY_STRUCTURE;
/*
* Section type definitions, used in section_type field in struct
* cper_section_descriptor
*
* Processor Generic
*/
#define CPER_SEC_PROC_GENERIC \
GUID_INIT(0x9876CCAD, 0x47B4, 0x4bdb, 0xB6, 0x5E, 0x16, 0xF1, \
0x93, 0xC4, 0xF3, 0xDB)
/* Processor Specific: X86/X86_64 */
#define CPER_SEC_PROC_IA \
GUID_INIT(0xDC3EA0B0, 0xA144, 0x4797, 0xB9, 0x5B, 0x53, 0xFA, \
0x24, 0x2B, 0x6E, 0x1D)
/* Processor Specific: IA64 */
#define CPER_SEC_PROC_IPF \
GUID_INIT(0xE429FAF1, 0x3CB7, 0x11D4, 0x0B, 0xCA, 0x07, 0x00, \
0x80, 0xC7, 0x3C, 0x88, 0x81)
/* Processor Specific: ARM */
#define CPER_SEC_PROC_ARM \
GUID_INIT(0xE19E3D16, 0xBC11, 0x11E4, 0x9C, 0xAA, 0xC2, 0x05, \
0x1D, 0x5D, 0x46, 0xB0)
/* Platform Memory */
#define CPER_SEC_PLATFORM_MEM \
GUID_INIT(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \
0xED, 0x7C, 0x83, 0xB1)
#define CPER_SEC_PCIE \
GUID_INIT(0xD995E954, 0xBBC1, 0x430F, 0xAD, 0x91, 0xB4, 0x4D, \
0xCB, 0x3C, 0x6F, 0x35)
/* Firmware Error Record Reference */
Section Type=CPER_SEC_PROC_GENERIC 对应到 CPU 的错误结构体
对于GHES 的错误OS需要使用定时器,BERT 只需要在Kernel 加载时跑一边即可。
OS 下错误解析 ghes.c bert.c cper.c
GHES 驱动:
static struct platform_driver ghes_platform_driver = {
.driver = {
.name = "GHES",
},
.probe = ghes_probe,
.remove = ghes_remove,
};
ghes_init 加载GHES的驱动 ,系统下的解析策略和Notify 的结构体相关联,BIOS中会设置Notify Type ,Pollinterval
系统下扫描GHES 的驱动 是通过定时器周期性去扫描错误状态,Pollinterval 是定时器的参考时间
switch (generic->notify.type) {
case ACPI_HEST_NOTIFY_POLLED:
case ACPI_HEST_NOTIFY_EXTERNAL:
case ACPI_HEST_NOTIFY_SCI:
case ACPI_HEST_NOTIFY_GSIV:
case ACPI_HEST_NOTIFY_GPIO:
break;
case ACPI_HEST_NOTIFY_SEA:
if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEA is not supported\n",
generic->header.source_id);
rc = -ENOTSUPP;
goto err;
}
break;
case ACPI_HEST_NOTIFY_NMI:
if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
generic->header.source_id);
goto err;
}
break;
case ACPI_HEST_NOTIFY_LOCAL:
pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
generic->header.source_id);
goto err;
timer_setup(&ghes->timer, ghes_poll_func, TIMER_DEFERRABLE);
ghes_proc(ghes);
ghes_read_estatus(ghes, 0);//-->apei_read(&buf_paddr, &g->error_status_address);
ghes_print_estatus
cper_estatus_print(pfx_seq, estatus);
这就对应到Dmesg 中的HardWare error 错误,就可以识别到错误的source id ,既可以大致定位错误信息来源 Source id = 512 对应到PCIE 错误
后续通过Section error type 定位到更加详细的信息
前面的信息都是来自于固定结构体
///
/// Generic Error Data Entry Definition
///
typedef struct {
UINT8 SectionType[16];
UINT32 ErrorSeverity;
UINT16 Revision;
UINT8 ValidationBits;
UINT8 Flags;
UINT32 ErrorDataLength;
UINT8 FruId[16];
UINT8 FruText[20];
UINT8 Timestamp[8];
} EFI_ACPI_6_2_GENERIC_ERROR_DATA_ENTRY_STRUCTURE;
static void
cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
int sec_no)
{
guid_t *sec_type = (guid_t *)gdata->section_type;
__u16 severity;
char newpfx[64];
if (acpi_hest_get_version(gdata) >= 3)
cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
severity = gdata->error_severity;
printk("%s""Error %d, type: %s\n", pfx, sec_no,
cper_severity_str(severity));
if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
}
对于后续的结构体包括内存PCIE CPU 有着不同的结构体主要包括三个函数打印错误信息,通过匹配Section type Guid 判断
cper_print_proc_generic(); cper_print_mem(); cper_print_pcie()
if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
printk("%s""section_type: general processor error\n", newpfx);
if (gdata->error_data_length >= sizeof(*proc_err))
cper_print_proc_generic(newpfx, proc_err);
else
goto err_section_too_small;
} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
printk("%s""section_type: memory error\n", newpfx);
if (gdata->error_data_length >=
sizeof(struct cper_sec_mem_err_old))
cper_print_mem(newpfx, mem_err,
gdata->error_data_length);
else
goto err_section_too_small;
} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
printk("%s""section_type: PCIe error\n", newpfx);
if (gdata->error_data_length >= sizeof(*pcie))
cper_print_pcie(newpfx, pcie, gdata);
else
goto err_section_too_small;
#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
printk("%ssection_type: ARM processor error\n", newpfx);
if (gdata->error_data_length >= sizeof(*arm_err))
cper_print_proc_arm(newpfx, arm_err);
else
goto err_section_too_small;
CPU错误信息结构体:
typedef struct _PLATFORM_PROC_ERR_SEC {
PROC_ERR_VALID_BIT ValidBits; ///< Validation Bits
UINT64 LocalApicID; ///< Processor APIC ID
UINT64 CpuIdInfo_EAX; ///< CPUID Information output value from EAX
UINT64 CpuIdInfo_EBX; ///< CPUID Information output value from EBX
UINT64 CpuIdInfo_ECX; ///< CPUID Information output value from ECX
UINT64 CpuIdInfo_EDX; ///< CPUID Information output value from EDX
UINT64 CpuIdInfo_PD1; ///< CPUID Information Padding 1
UINT64 CpuIdInfo_PD2; ///< CPUID Information Padding 2
} PLATFORM_PROC_ERR_SEC;
内存错误信息结构体:
typedef struct _PLATFORM_MEM_ERR_SEC {
MEM_ERR_VALID_BIT ValidBits; ///< Valid bits Bitmp
UINT64 ErrStatus; ///< Error Status
UINT64 PhyAddr; ///< Physical memory address of detected error
UINT64 PhyAddrMask; ///< Physical Error Address mask
UINT16 Node; ///< Node Number
UINT16 Card; ///< Card Number
UINT16 Module; ///< Module Number
UINT16 Bank; ///< Bank Number
UINT16 Device; ///< Device Number
UINT16 Row; ///< Row Number
UINT16 Column; ///< Column Number
UINT16 BitPosition; ///< Bit Position
UINT64 RequestorID; ///< Requestor ID
UINT64 ResponderID; ///< Responder ID
UINT64 TargetID; ///< Target ID
UINT8 MemErrType; ///< Memory Error Type
UINT8 Extend; ///< Extened
UINT16 RankNumber; ///< Rank Number
UINT16 CardHandle; ///< Card Number
UINT16 ModuleHandle;///< Module Number
} PLATFORM_MEM_ERR_SEC;
PCIE 错误信息结构体:
///
/// PCIE Error Section
///
typedef struct {
PCIE_ERR_VALID_BIT Validation; ///< Validation Bits
UINT32 PortType; ///< Port Type
UINT32 Revision; ///< Revision
UINT32 CommandStatus; ///< Command Status
UINT32 Reserved; ///< Reserved
DEVICE_ID DeviceId; ///< Device Id
UINT8 SerialNum[8]; ///< Serial Num
UINT32 BridgeCtrlStatus; ///< Bridge Control Status
CAP_STRUCTURE CapabilityStructure; ///< Capability Structure
AER_INFO AerInfo; ///< AER Info
} PCIE_ERROR_SECTION;
对于BERT 错误不需要使用定时器在Kernel 加载的时候会去初始化一遍BERT
BERT \drivers\acpi\apei\bert.c
bert_init -->
pr_info_once("Error records from previous boot:\n");
bert_print_all(boot_error_region, region_len);
cper_estatus_print(KERN_INFO HW_ERR, estatus);
错误解析和HEST GHES 类似