From aaf9038a6c20f4f27044e06306e08c0494ab2b4c Mon Sep 17 00:00:00 2001 From: LeoLiu-oc Date: Thu, 14 May 2026 10:23:12 +0800 Subject: [PATCH 1/6] efi/cper: Refactor Zhaoxin ZDI/ZPI error print code 1. Wrap ZDI/ZPI related functions and strings with CONFIG_X86 to restrict the code to X86 architecture only. 2. Remove __maybe_unused attribute from cper_print_proc_generic_zdi_zpi() since the function is now guarded by CONFIG_X86 and always used on X86. 3. Directly use zdi_zpi->responder_id as error type parameter instead of redundant local variable etype. 4. Clean up code formatting for better readability. 5. Replace IS_ENABLED(CONFIG_X86) with #ifdef CONFIG_X86 for consistent preprocessor usage. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 511309542d245..a1eb4ece5d24a 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -201,6 +201,7 @@ static const char * const proc_flag_strs[] = { "corrected", }; +#ifdef CONFIG_X86 static const char * const zdi_zpi_err_type_strs[] = { "No Error", "Training Error Status (PHY)", @@ -230,11 +231,9 @@ const char *cper_zdi_zpi_err_type_str(unsigned int etype) } EXPORT_SYMBOL_GPL(cper_zdi_zpi_err_type_str); -static void __maybe_unused -cper_print_proc_generic_zdi_zpi(const char *pfx, const struct cper_sec_proc_generic *zdi_zpi) +static void cper_print_proc_generic_zdi_zpi(const char *pfx, + const struct cper_sec_proc_generic *zdi_zpi) { - u8 etype = zdi_zpi->responder_id; - if ((zdi_zpi->requestor_id & 0xff) == 7) { pr_info("%s general processor error(zpi error)\n", pfx); } else if ((zdi_zpi->requestor_id & 0xff) == 6) { @@ -243,11 +242,12 @@ cper_print_proc_generic_zdi_zpi(const char *pfx, const struct cper_sec_proc_gene pr_info("%s general processor error(unknown error)\n", pfx); return; } - pr_info("%s bus number %llx device number %llx function number 0\n", pfx, - ((zdi_zpi->requestor_id)>>8) & 0xff, zdi_zpi->requestor_id & 0xff); - pr_info("%s apic id %lld error_type: %s\n", pfx, zdi_zpi->proc_id, - cper_zdi_zpi_err_type_str(etype)); + pr_info("%s bus number %llx device number %llx function number 0\n", + pfx, ((zdi_zpi->requestor_id) >> 8) & 0xff, zdi_zpi->requestor_id & 0xff); + pr_info("%s apic id %lld error_type: %s\n", + pfx, zdi_zpi->proc_id, cper_zdi_zpi_err_type_str(zdi_zpi->responder_id)); } +#endif static void cper_print_proc_generic(const char *pfx, const struct cper_sec_proc_generic *proc) @@ -292,7 +292,7 @@ static void cper_print_proc_generic(const char *pfx, pfx, proc->responder_id); if (proc->validation_bits & CPER_PROC_VALID_IP) printk("%s""IP: 0x%016llx\n", pfx, proc->ip); -#if IS_ENABLED(CONFIG_X86) +#ifdef CONFIG_X86 if (boot_cpu_data.x86_vendor == X86_VENDOR_ZHAOXIN || boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) cper_print_proc_generic_zdi_zpi(pfx, proc); From e4fd5e5fd2dff876564d577ecaec68de5fe5ccb7 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:30:07 +0800 Subject: [PATCH 2/6] efi/cper: Add Zhaoxin ZDI ZPI error decode for KH-50000 zhaoxin inclusion category: feature -------------------- The Zhaoxin KH-50000 processor's ZDI and ZPI support reporting additional error types with more detailed analysis, hence the addition of KH-50000 ZDI and ZPI error parsing. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 82 +++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index a1eb4ece5d24a..5d36d21fb2ce0 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -222,17 +222,39 @@ static const char * const zdi_zpi_err_type_strs[] = { "ZPI Gen2 Link Speed Unreliable Status", "ZDI Gen3 Link Speed Unreliable Status", "ZDI Gen4 Link Speed Unreliable Status", + "SL_NA_CYCLE Status", + "8.0GT/s Link Speed Unreliable Status", + "16.0GT/s Link Speed Unreliable Status", + "32.0GT/s Link Speed Unreliable Status", + "X2 Link Width Unreliable Status", + "X4 Link Width Unreliable Status", + "X8 Link Width Unreliable Status", + "X16X12 Link Width Unreliable Status", + "X32X24 Link Width Unreliable Status", }; const char *cper_zdi_zpi_err_type_str(unsigned int etype) { + switch (boot_cpu_data.x86_model) { + case 0x5b: + if (etype >= 0x13) + return "unknown error"; + break; + case 0x7b: + if (etype == 0x6 || (etype >= 0xb && etype <= 0x12)) + return "unknown error"; + break; + default: + return "unknown error"; + } return etype < ARRAY_SIZE(zdi_zpi_err_type_strs) ? - zdi_zpi_err_type_strs[etype] : "unknown error"; + zdi_zpi_err_type_strs[etype] : + "unknown error"; } EXPORT_SYMBOL_GPL(cper_zdi_zpi_err_type_str); -static void cper_print_proc_generic_zdi_zpi(const char *pfx, - const struct cper_sec_proc_generic *zdi_zpi) +static void cper_print_proc_generic_zdi_zpi_kh40000(const char *pfx, + const struct cper_sec_proc_generic *zdi_zpi) { if ((zdi_zpi->requestor_id & 0xff) == 7) { pr_info("%s general processor error(zpi error)\n", pfx); @@ -247,6 +269,60 @@ static void cper_print_proc_generic_zdi_zpi(const char *pfx, pr_info("%s apic id %lld error_type: %s\n", pfx, zdi_zpi->proc_id, cper_zdi_zpi_err_type_str(zdi_zpi->responder_id)); } + +static void cper_print_proc_generic_zdi_zpi_kh50000(const char *pfx, + const struct cper_sec_proc_generic *zdi_zpi) +{ + u8 etype = (zdi_zpi->requestor_id & 0xff) >> 4; + const char *zdi_etype_str; + + if (!(zdi_zpi->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) || + !(zdi_zpi->validation_bits & CPER_PROC_VALID_RESPONDER_ID)) + return; + + if (etype == 0xf) { + pr_info("%s general processor error(zpi port 0x%llx error)\n", + pfx, zdi_zpi->requestor_id & 0xf); + } else if (etype >= 0x0 && etype <= 0xb) { + switch (zdi_zpi->requestor_id & 0xf) { + case 0x0: + zdi_etype_str = "ccdzdi"; + break; + case 0x1: + zdi_etype_str = "iodzdi"; + break; + default: + zdi_etype_str = "unknown"; + } + pr_info("%s general processor error(zdi port 0x%x %s error)\n", + pfx, etype, zdi_etype_str); + } else { + pr_info("%s general processor error(unknown error)\n", pfx); + return; + } + + pr_info("%s socket id %lld error_type: %s\n", + pfx, + (zdi_zpi->requestor_id & 0xfff) >> 8, + cper_zdi_zpi_err_type_str(zdi_zpi->responder_id)); +} + +static void cper_print_proc_generic_zdi_zpi(const char *pfx, + const struct cper_sec_proc_generic *zdi_zpi) +{ + if (boot_cpu_data.x86 == 0x7) { + switch (boot_cpu_data.x86_model) { + case 0x5b: + cper_print_proc_generic_zdi_zpi_kh40000(pfx, zdi_zpi); + break; + case 0x7b: + cper_print_proc_generic_zdi_zpi_kh50000(pfx, zdi_zpi); + break; + default: + return; + } + } +} #endif static void cper_print_proc_generic(const char *pfx, From 6c344195de74f70d58c55ea5ca393ed3cc41aff4 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:31:03 +0800 Subject: [PATCH 3/6] efi/cper: Add Zhaoxin non standard cper error decode zhaoxin inclusion category: feature -------------------- The Zhaoxin processor's HIF and SVID error types are not defined in the UEFI spec and are reported via a non-standard CPER structure. This patch adds support to decode these proprietary error records. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 131 ++++++++++++++++++++++++++++++++++++ include/linux/cper.h | 45 +++++++++++++ 2 files changed, 176 insertions(+) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 5d36d21fb2ce0..36846c1f208a7 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -696,6 +696,121 @@ static void cper_print_fw_err(const char *pfx, print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); } +static const char *const svid_error_type_strs[] = { + "reserved", + "SVID resend fail error", + "VRM over current error", + "VRM over temperature error", + "VRM parity error", +}; + +static void cper_print_svid_err(const char *pfx, const struct cper_sec_svid *svid) +{ + if (svid->validation_bits & CPER_SVID_VALID_SOCKET_ID) + pr_info("%s socket id : %d\n", pfx, svid->socket_id); + if (svid->validation_bits & CPER_SVID_VALID_SVID_ID) + pr_info("%s svid id : %d\n", pfx, svid->svid_id); + if (svid->validation_bits & CPER_SVID_VALID_VRM_NUM) + pr_info("%s vrm number : %d\n", pfx, svid->vrm_number); + if (svid->validation_bits & CPER_SVID_VALID_ERROR_TYPE) + pr_info("%s error type: %d, %s\n", pfx, svid->error_type, + svid->error_type < ARRAY_SIZE(svid_error_type_strs) ? + svid_error_type_strs[svid->error_type] : "unknown"); +} + +static const char *const cxl_error_type_strs[] = { + "reserved", + "reserved", + "reserved", + "reserved", + "decode poison UC error", + "decode poison CE error", + "parity error", + "reserved", + "timeout error", +}; + +static const char *const snt_error_type_strs[] = { + "no error", + "correctable error", + "uncorrectable error", + "multi correctable error", + "multi Uncorrectable error", + "uncorrectable + correctable error", +}; + +static void dump_cxl_error_type(const char *pfx, u8 port, u64 decode_addr, u8 type) +{ + pr_info("%s CXL Port%d Error Type: %d, %s\n", + pfx, + port, + type, + type < ARRAY_SIZE(cxl_error_type_strs) ? + cxl_error_type_strs[type] : + "unknown"); + if (type == 0x4 || type == 0x5) + pr_info("%s CXL Decode Error Address: 0x%llx\n", pfx, decode_addr); +} + +static void dump_hif_error_type(const char *pfx, const struct cper_sec_hif *hif) +{ + u64 data, addr; + + data = (u64)hif->snt_error_data[0] | + (u64)hif->snt_error_data[1] << 8 | + (u64)hif->snt_error_data[2] << 16 | + (u64)hif->snt_error_data[3] << 24 | + (u64)hif->snt_error_data[4] << 32; + addr = (u64)hif->snt_error_addr[0] | + (u64)hif->snt_error_addr[1] << 8 | + (u64)hif->snt_error_addr[2] << 16 | + (u64)hif->snt_error_addr[3] << 24 | + (u64)hif->snt_error_addr[4] << 32; + pr_info("%s SNT Location, Way: %d Bank Number: %d Channel: %d\n", + pfx, + (hif->snt_location >> 4) & 0x7, + (hif->snt_location >> 2) & 0x3, + hif->snt_location & 0x3); + pr_info("%s SNT Error Type: %d, %s\n", + pfx, + hif->snt_error_type, + hif->snt_error_type < ARRAY_SIZE(snt_error_type_strs) ? + snt_error_type_strs[hif->snt_error_type] : + "unknown"); + pr_info("%s SNT Error Data: 0x%llx, Error Address: 0x%llx\n", pfx, data, addr); +} + +static void cper_print_hif_err(const char *pfx, const struct cper_sec_hif *hif) +{ + pr_info("%s Error occurred at Socket %d, Hnode %d\n", pfx, hif->socket_id, hif->hnod_id); + + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL0) + pr_info("%s Sub channel 0 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[0]); + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL1) + pr_info("%s Sub channel 1 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[1]); + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL2) + pr_info("%s Sub channel 2 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[2]); + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL3) + pr_info("%s Sub channel 3 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[3]); + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL4) + pr_info("%s Sub channel 4 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[4]); + if (hif->validation_bits & CPER_HIF_VALID_DVAD_CHANNEL5) + pr_info("%s Sub channel 5 DVAD Error Address : %llx\n", + pfx, hif->dvad_error_addr[5]); + if (hif->validation_bits & CPER_HIF_VALID_SNT) + dump_hif_error_type(pfx, hif); + + if (hif->validation_bits & CPER_HIF_VALID_CXL_PORT0) + dump_cxl_error_type(pfx, 0, hif->cxl_decode_error_addr[0], hif->cxl_error_type[0]); + if (hif->validation_bits & CPER_HIF_VALID_CXL_PORT1) + dump_cxl_error_type(pfx, 1, hif->cxl_decode_error_addr[1], hif->cxl_error_type[1]); +} + static void cper_print_tstamp(const char *pfx, struct acpi_hest_generic_data_v300 *gdata) { @@ -802,6 +917,22 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata cper_print_prot_err(newpfx, prot_err); else goto err_section_too_small; + } else if (guid_equal(sec_type, &CPER_SEC_SVID)) { + struct cper_sec_svid *svid_err = acpi_hest_get_payload(gdata); + + printk("%ssection_type: SVID Error\n", newpfx); + if (gdata->error_data_length >= sizeof(*svid_err)) + cper_print_svid_err(newpfx, svid_err); + else + goto err_section_too_small; + } else if (guid_equal(sec_type, &CPER_SEC_HIF)) { + struct cper_sec_hif *hif_err = acpi_hest_get_payload(gdata); + + printk("%ssection_type: HIF Error\n", newpfx); + if (gdata->error_data_length >= sizeof(*hif_err)) + cper_print_hif_err(newpfx, hif_err); + else + goto err_section_too_small; } else { const void *err = acpi_hest_get_payload(gdata); diff --git a/include/linux/cper.h b/include/linux/cper.h index c0af91e12e752..59744477f3f80 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -198,6 +198,14 @@ enum { GUID_INIT(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F, \ 0xDF, 0xAA, 0x84, 0xEC) +#define CPER_SEC_SVID \ + GUID_INIT(0x3B1E4A7C, 0x9F82, 0x4E3D, 0x8A, 0x5B, 0x1C, 0x7D, \ + 0x3F, 0x6E, 0x8A, 0x2C) + +#define CPER_SEC_HIF \ + GUID_INIT(0x137B5703, 0xFD9C, 0x4C3F, 0xA2, 0x75, 0x3E, 0x00, \ + 0x2A, 0xDF, 0x7B, 0x04) + #define CPER_PROC_VALID_TYPE 0x0001 #define CPER_PROC_VALID_ISA 0x0002 #define CPER_PROC_VALID_ERROR_TYPE 0x0004 @@ -254,6 +262,21 @@ enum { #define CPER_PCIE_SLOT_SHIFT 3 +#define CPER_SVID_VALID_SOCKET_ID 0x1 +#define CPER_SVID_VALID_SVID_ID 0x2 +#define CPER_SVID_VALID_VRM_NUM 0x4 +#define CPER_SVID_VALID_ERROR_TYPE 0x8 + +#define CPER_HIF_VALID_DVAD_CHANNEL0 0x1 +#define CPER_HIF_VALID_DVAD_CHANNEL1 0x2 +#define CPER_HIF_VALID_DVAD_CHANNEL2 0x4 +#define CPER_HIF_VALID_DVAD_CHANNEL3 0x8 +#define CPER_HIF_VALID_DVAD_CHANNEL4 0x10 +#define CPER_HIF_VALID_DVAD_CHANNEL5 0x20 +#define CPER_HIF_VALID_SNT 0x40 +#define CPER_HIF_VALID_CXL_PORT0 0x80 +#define CPER_HIF_VALID_CXL_PORT1 0x100 + #define CPER_ARM_VALID_MPIDR BIT(0) #define CPER_ARM_VALID_AFFINITY_LEVEL BIT(1) #define CPER_ARM_VALID_RUNNING_STATE BIT(2) @@ -541,6 +564,28 @@ struct cper_sec_pcie { u8 aer_info[96]; }; +struct cper_sec_svid { + u8 validation_bits; + u8 socket_id; + u8 svid_id; + u8 vrm_number; + u16 error_type; + u16 reserved; +} __packed; + +struct cper_sec_hif { + u16 validation_bits; + u8 socket_id; + u8 hnod_id; + u8 snt_location; + u8 snt_error_type; + u8 snt_error_data[5]; + u8 snt_error_addr[5]; + u64 dvad_error_addr[6]; + u64 cxl_decode_error_addr[2]; + u8 cxl_error_type[2]; +} __packed; + /* Firmware Error Record Reference, UEFI v2.7 sec N.2.10 */ struct cper_sec_fw_err_rec_ref { u8 record_type; From 845a8d1e26a11911182de672d9b9c3f3eb82c90d Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:31:30 +0800 Subject: [PATCH 4/6] efi/cper: Add Zhaoxin micro-architectural error decode zhaoxin inclusion category: feature -------------------- Zhaoxin processors report detailed micro-architectural error classifications by re-purposing the Responder ID and Requestor ID fields. This patch adds support to decode these proprietary error types into human-readable messages. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 71 ++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 36846c1f208a7..7a2656292418c 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -323,6 +323,75 @@ static void cper_print_proc_generic_zdi_zpi(const char *pfx, } } } + +static const char * const zx_micro_arch_cpu_err_type_strs[] = { + "unknown error", + "unknown error", + "machine hang", + "undefined ucode address", +}; + +static const char * const zx_micro_arch_shutdown_err_type_strs[] = { + "unknown error", + "#PF happened again when handle #DF caused by #PF", + "MCE happened when handle #DF", + "#GP happened again when handle #DF caused by #GP", + "exit smm mode if already shutdown before enter smm mode", + "exit smm mode if CR0.CD bit is 0 and CR0.NW bit is 1 stored in SMRAM and in smm mode", + "exit smm mode if CR0.PE bit is 0 and CR0.PG bit is 1 stored in SMRAM and in smm mode", + "exit smm mode if the reserved bits in CR4 are writed to 1 in smm mode", + "exit smm mode if CR4.VMXE bit is writed to 1 in smm mode", + "exit smm mode if CR4.PCIDE bit is writed to 1 and EFER.LMA bit is writed to 0 in smm mode", + "software inject an UC error after MCE changed to SMI happened", + "reserved", + "MCE happened when CR4 MCE is 0", + "MCE happened again when didn't clear MCIP in the first MCE handler", + "using vmentry to enter guest mode", + "configuration for VM-exit MSR-store and load address is wrong when vmexit caused by VMX guest mode", + "TXT check fail", +}; + +static void cper_print_proc_generic_zx_micro_arch(const char *pfx, + const struct cper_sec_proc_generic *arch) +{ + u8 etype = arch->responder_id; + + if (!(arch->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) || + !(arch->validation_bits & CPER_PROC_VALID_RESPONDER_ID)) + return; + + if (arch->requestor_id == 0x1) + pr_info("%s CPU Error, error type: %d, %s\n", + pfx, + etype, + etype < ARRAY_SIZE(zx_micro_arch_cpu_err_type_strs) ? + zx_micro_arch_cpu_err_type_strs[etype] : + "unknown error"); + else if (arch->requestor_id == 0x2) + pr_info("%s Shutdown Error, error type: %d, %s\n", + pfx, + etype, + etype < ARRAY_SIZE(zx_micro_arch_shutdown_err_type_strs) ? + zx_micro_arch_shutdown_err_type_strs[etype] : + "unknown error"); +} + +static void cper_print_proc_generic_zx(const char *pfx, + const struct cper_sec_proc_generic *proc) +{ + if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { + switch (proc->proc_error_type) { + case 0x4: + cper_print_proc_generic_zdi_zpi(pfx, proc); + break; + case 0x8: + cper_print_proc_generic_zx_micro_arch(pfx, proc); + break; + default: + break; + } + } +} #endif static void cper_print_proc_generic(const char *pfx, @@ -371,7 +440,7 @@ static void cper_print_proc_generic(const char *pfx, #ifdef CONFIG_X86 if (boot_cpu_data.x86_vendor == X86_VENDOR_ZHAOXIN || boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) - cper_print_proc_generic_zdi_zpi(pfx, proc); + cper_print_proc_generic_zx(pfx, proc); #endif } From b92ba93e5b0cd6f4d42fdbfe3fdd07638a101ab3 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:31:48 +0800 Subject: [PATCH 5/6] efi/cper: Add Zhaoxin cache error decode zhaoxin inclusion category: feature -------------------- Zhaoxin processors report detailed cache error types by re-purposing the Responder ID field. This patch adds a decoder to translate these numeric values into human-readable strings. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 7a2656292418c..ea8d6fbed2e3f 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -376,11 +376,39 @@ static void cper_print_proc_generic_zx_micro_arch(const char *pfx, "unknown error"); } +static const char * const zx_cache_err_type_strs[] = { + "unknown error", + "single bit ECC for data part in the same line", + "single bit ECC for different line", + "multi bit ECC for data part", +}; + +static void cper_print_proc_generic_zx_cache(const char *pfx, + const struct cper_sec_proc_generic *cache) +{ + u8 etype = cache->responder_id; + const char *etype_str = etype < ARRAY_SIZE(zx_cache_err_type_strs) ? + zx_cache_err_type_strs[etype] : + "unknown error"; + + if (!(cache->validation_bits & CPER_PROC_VALID_LEVEL) || + !(cache->validation_bits & CPER_PROC_VALID_RESPONDER_ID)) + return; + + if (cache->level == 0x2) + pr_info("%s PL2 Error, error type: %d, %s\n", pfx, etype, etype_str); + else if (cache->level == 0x3) + pr_info("%s LLC Error, error type: %d, %s\n", pfx, etype, etype_str); +} + static void cper_print_proc_generic_zx(const char *pfx, const struct cper_sec_proc_generic *proc) { if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { switch (proc->proc_error_type) { + case 0x1: + cper_print_proc_generic_zx_cache(pfx, proc); + break; case 0x4: cper_print_proc_generic_zdi_zpi(pfx, proc); break; From b780b67c3227ea09affe0a888551aa5b632bee1e Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:32:09 +0800 Subject: [PATCH 6/6] efi/cper: Add Zhaoxin mem error decode for KH-50000 zhaoxin inclusion category: feature -------------------- Some memory error types on the Zhaoxin KH-50000 do not fit the are reported by repurposing the Requestor ID field. Add parsing logic to decode this non-standard usage and present human-readable error messages. Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index ea8d6fbed2e3f..01d719f2d950a 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -420,6 +420,49 @@ static void cper_print_proc_generic_zx(const char *pfx, } } } + +static const char * const zx_mem_err_type_strs[] = { + "reserved", + "reserved", + "correctable write CRC error", + "uncorrectable write CRC error", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "single device error", + "multi devices error", + "correctable read CRC error", + "uncorrectable read CRC error", + "rank counter overflow", + "UC occurred after mirror broken", + "sPPR done", +}; + +static void cper_print_mem_zx(const char *pfx, const struct cper_sec_mem_err *mem) +{ + u8 etype; + + if (boot_cpu_data.x86 != 0x7 || boot_cpu_data.x86_model != 0x7b) + return; + + if (!(mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)) + return; + + etype = mem->requestor_id; + if (etype < 0x2 || (etype > 0x3 && etype < 0x10) || etype > 0x16) + return; + + pr_info("%s Specific error type: %d, %s\n", pfx, etype, zx_mem_err_type_strs[etype]); +} #endif static void cper_print_proc_generic(const char *pfx, @@ -669,6 +712,12 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, } if (cper_dimm_err_location(&cmem, rcd_decode_str)) printk("%s%s\n", pfx, rcd_decode_str); + +#ifdef CONFIG_X86 + if (boot_cpu_data.x86_vendor == X86_VENDOR_ZHAOXIN || + boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) + cper_print_mem_zx(pfx, mem); +#endif } static const char * const pcie_port_type_strs[] = {