* [PATCH v2 1/6] cper: use BIT_ULL() for processor and memory validation bits
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 2/6] cper: add Memory Error Section 2 structures Pierre Gondois
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Jonathan Cameron,
Rafael J. Wysocki, Len Brown, Tony Luck, Borislav Petkov,
Hanjun Guo, Mauro Carvalho Chehab, Shuai Xue,
Maciej Wieczor-Retman, Pawel Chmielewski, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Dan Williams, Terry Bowman,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Use the BIT_ULL() macro to describe flag bits instead of
hexadecimal values which are harder to read.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
include/linux/cper.h | 72 ++++++++++++++++++++++----------------------
1 file changed, 36 insertions(+), 36 deletions(-)
diff --git a/include/linux/cper.h b/include/linux/cper.h
index 440b35e459e5..81cda9874b25 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -225,42 +225,42 @@ enum {
GUID_INIT(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F, \
0xDF, 0xAA, 0x84, 0xEC)
-#define CPER_PROC_VALID_TYPE 0x0001
-#define CPER_PROC_VALID_ISA 0x0002
-#define CPER_PROC_VALID_ERROR_TYPE 0x0004
-#define CPER_PROC_VALID_OPERATION 0x0008
-#define CPER_PROC_VALID_FLAGS 0x0010
-#define CPER_PROC_VALID_LEVEL 0x0020
-#define CPER_PROC_VALID_VERSION 0x0040
-#define CPER_PROC_VALID_BRAND_INFO 0x0080
-#define CPER_PROC_VALID_ID 0x0100
-#define CPER_PROC_VALID_TARGET_ADDRESS 0x0200
-#define CPER_PROC_VALID_REQUESTOR_ID 0x0400
-#define CPER_PROC_VALID_RESPONDER_ID 0x0800
-#define CPER_PROC_VALID_IP 0x1000
-
-#define CPER_MEM_VALID_ERROR_STATUS 0x0001
-#define CPER_MEM_VALID_PA 0x0002
-#define CPER_MEM_VALID_PA_MASK 0x0004
-#define CPER_MEM_VALID_NODE 0x0008
-#define CPER_MEM_VALID_CARD 0x0010
-#define CPER_MEM_VALID_MODULE 0x0020
-#define CPER_MEM_VALID_BANK 0x0040
-#define CPER_MEM_VALID_DEVICE 0x0080
-#define CPER_MEM_VALID_ROW 0x0100
-#define CPER_MEM_VALID_COLUMN 0x0200
-#define CPER_MEM_VALID_BIT_POSITION 0x0400
-#define CPER_MEM_VALID_REQUESTOR_ID 0x0800
-#define CPER_MEM_VALID_RESPONDER_ID 0x1000
-#define CPER_MEM_VALID_TARGET_ID 0x2000
-#define CPER_MEM_VALID_ERROR_TYPE 0x4000
-#define CPER_MEM_VALID_RANK_NUMBER 0x8000
-#define CPER_MEM_VALID_CARD_HANDLE 0x10000
-#define CPER_MEM_VALID_MODULE_HANDLE 0x20000
-#define CPER_MEM_VALID_ROW_EXT 0x40000
-#define CPER_MEM_VALID_BANK_GROUP 0x80000
-#define CPER_MEM_VALID_BANK_ADDRESS 0x100000
-#define CPER_MEM_VALID_CHIP_ID 0x200000
+#define CPER_PROC_VALID_TYPE BIT_ULL(0)
+#define CPER_PROC_VALID_ISA BIT_ULL(1)
+#define CPER_PROC_VALID_ERROR_TYPE BIT_ULL(2)
+#define CPER_PROC_VALID_OPERATION BIT_ULL(3)
+#define CPER_PROC_VALID_FLAGS BIT_ULL(4)
+#define CPER_PROC_VALID_LEVEL BIT_ULL(5)
+#define CPER_PROC_VALID_VERSION BIT_ULL(6)
+#define CPER_PROC_VALID_BRAND_INFO BIT_ULL(7)
+#define CPER_PROC_VALID_ID BIT_ULL(8)
+#define CPER_PROC_VALID_TARGET_ADDRESS BIT_ULL(9)
+#define CPER_PROC_VALID_REQUESTOR_ID BIT_ULL(10)
+#define CPER_PROC_VALID_RESPONDER_ID BIT_ULL(11)
+#define CPER_PROC_VALID_IP BIT_ULL(12)
+
+#define CPER_MEM_VALID_ERROR_STATUS BIT_ULL(0)
+#define CPER_MEM_VALID_PA BIT_ULL(1)
+#define CPER_MEM_VALID_PA_MASK BIT_ULL(2)
+#define CPER_MEM_VALID_NODE BIT_ULL(3)
+#define CPER_MEM_VALID_CARD BIT_ULL(4)
+#define CPER_MEM_VALID_MODULE BIT_ULL(5)
+#define CPER_MEM_VALID_BANK BIT_ULL(6)
+#define CPER_MEM_VALID_DEVICE BIT_ULL(7)
+#define CPER_MEM_VALID_ROW BIT_ULL(8)
+#define CPER_MEM_VALID_COLUMN BIT_ULL(9)
+#define CPER_MEM_VALID_BIT_POSITION BIT_ULL(10)
+#define CPER_MEM_VALID_REQUESTOR_ID BIT_ULL(11)
+#define CPER_MEM_VALID_RESPONDER_ID BIT_ULL(12)
+#define CPER_MEM_VALID_TARGET_ID BIT_ULL(13)
+#define CPER_MEM_VALID_ERROR_TYPE BIT_ULL(14)
+#define CPER_MEM_VALID_RANK_NUMBER BIT_ULL(15)
+#define CPER_MEM_VALID_CARD_HANDLE BIT_ULL(16)
+#define CPER_MEM_VALID_MODULE_HANDLE BIT_ULL(17)
+#define CPER_MEM_VALID_ROW_EXT BIT_ULL(18)
+#define CPER_MEM_VALID_BANK_GROUP BIT_ULL(19)
+#define CPER_MEM_VALID_BANK_ADDRESS BIT_ULL(20)
+#define CPER_MEM_VALID_CHIP_ID BIT_ULL(21)
#define CPER_MEM_EXT_ROW_MASK 0x3
#define CPER_MEM_EXT_ROW_SHIFT 16
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 2/6] cper: add Memory Error Section 2 structures
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 1/6] cper: use BIT_ULL() for processor and memory validation bits Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 3/6] cper: add helpers to handle Memory Error Section 2 Pierre Gondois
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Jonathan Cameron,
Rafael J. Wysocki, Len Brown, Tony Luck, Borislav Petkov,
Hanjun Guo, Mauro Carvalho Chehab, Shuai Xue,
Maciej Wieczor-Retman, Pawel Chmielewski, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Dan Williams, Terry Bowman,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Add CPER Memory Error Section 2 definitions to the CPER headers.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
include/linux/cper.h | 54 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/include/linux/cper.h b/include/linux/cper.h
index 81cda9874b25..ed355b240685 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -198,6 +198,10 @@ enum {
#define CPER_SEC_PLATFORM_MEM \
GUID_INIT(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \
0xED, 0x7C, 0x83, 0xB1)
+/* Platform Memory 2 */
+#define CPER_SEC_PLATFORM_MEM2 \
+ GUID_INIT(0x61EC04FC, 0x48E6, 0xD813, 0x25, 0xC9, 0x8D, 0xAA, \
+ 0x44, 0x75, 0x0B, 0x12)
#define CPER_SEC_PCIE \
GUID_INIT(0xD995E954, 0xBBC1, 0x430F, 0xAD, 0x91, 0xB4, 0x4D, \
0xCB, 0x3C, 0x6F, 0x35)
@@ -262,6 +266,30 @@ enum {
#define CPER_MEM_VALID_BANK_ADDRESS BIT_ULL(20)
#define CPER_MEM_VALID_CHIP_ID BIT_ULL(21)
+/* Memory Error Section 2 validation bits, UEFI v2.11 sec N.2.6. */
+#define CPER_MEM2_VALID_ERROR_STATUS BIT_ULL(0)
+#define CPER_MEM2_VALID_PA BIT_ULL(1)
+#define CPER_MEM2_VALID_PA_MASK BIT_ULL(2)
+#define CPER_MEM2_VALID_NODE BIT_ULL(3)
+#define CPER_MEM2_VALID_CARD BIT_ULL(4)
+#define CPER_MEM2_VALID_MODULE BIT_ULL(5)
+#define CPER_MEM2_VALID_BANK BIT_ULL(6)
+#define CPER_MEM2_VALID_DEVICE BIT_ULL(7)
+#define CPER_MEM2_VALID_ROW BIT_ULL(8)
+#define CPER_MEM2_VALID_COLUMN BIT_ULL(9)
+#define CPER_MEM2_VALID_RANK_NUMBER BIT_ULL(10)
+#define CPER_MEM2_VALID_BIT_POSITION BIT_ULL(11)
+#define CPER_MEM2_VALID_CHIP_ID BIT_ULL(12)
+#define CPER_MEM2_VALID_ERROR_TYPE BIT_ULL(13)
+#define CPER_MEM2_VALID_STATUS BIT_ULL(14)
+#define CPER_MEM2_VALID_REQUESTOR_ID BIT_ULL(15)
+#define CPER_MEM2_VALID_RESPONDER_ID BIT_ULL(16)
+#define CPER_MEM2_VALID_TARGET_ID BIT_ULL(17)
+#define CPER_MEM2_VALID_CARD_HANDLE BIT_ULL(18)
+#define CPER_MEM2_VALID_MODULE_HANDLE BIT_ULL(19)
+#define CPER_MEM2_VALID_BANK_GROUP BIT_ULL(20)
+#define CPER_MEM2_VALID_BANK_ADDRESS BIT_ULL(21)
+
#define CPER_MEM_EXT_ROW_MASK 0x3
#define CPER_MEM_EXT_ROW_SHIFT 16
@@ -506,6 +534,32 @@ struct cper_sec_mem_err {
u16 mem_dev_handle; /* "module handle" in UEFI 2.4 */
};
+/* Memory Error Section 2, UEFI v2.11 sec N.2.6 */
+struct cper_sec_mem_err2 {
+ u64 validation_bits;
+ u64 error_status;
+ u64 physical_addr;
+ u64 physical_addr_mask;
+ u16 node;
+ u16 card;
+ u16 module;
+ u16 bank;
+ u32 device;
+ u32 row;
+ u32 column;
+ u32 rank;
+ u32 bit_pos;
+ u8 chip_id;
+ u8 error_type;
+ u8 status;
+ u8 reserved;
+ u64 requestor_id;
+ u64 responder_id;
+ u64 target_id;
+ u32 mem_array_handle; /* "card handle" */
+ u32 mem_dev_handle; /* "module handle" */
+};
+
struct cper_mem_err_compact {
u64 validation_bits;
u16 node;
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 3/6] cper: add helpers to handle Memory Error Section 2
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 1/6] cper: use BIT_ULL() for processor and memory validation bits Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 2/6] cper: add Memory Error Section 2 structures Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-19 20:38 ` [PATCH v2 4/6] EDAC/ghes: Extract error report initialization Pierre Gondois
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Jonathan Cameron,
Rafael J. Wysocki, Len Brown, Tony Luck, Borislav Petkov,
Hanjun Guo, Mauro Carvalho Chehab, Shuai Xue,
Maciej Wieczor-Retman, Pawel Chmielewski, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Dan Williams, Terry Bowman,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Add separate location and printing helpers for Memory Error
Section 2:
- cper_mem_err2_location()
- cper_dimm_err2_location()
- cper_print_mem2()
Similar functions/logic exists for Memory Error Section (1),
but require a compact version of the record. To avoid relying
and propagating a non-standard record format, the new functions
directly rely on struct cper_sec_mem_err2.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/firmware/efi/cper.c | 109 ++++++++++++++++++++++++++++++++++++
include/linux/cper.h | 3 +
2 files changed, 112 insertions(+)
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 06b4fdb59917..42e324d5707c 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -445,6 +445,107 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
printk("%s%s\n", pfx, rcd_decode_str);
}
+int cper_mem_err2_location(const struct cper_sec_mem_err2 *mem, char *msg)
+{
+ u32 len, n;
+
+ if (!msg)
+ return 0;
+
+ n = 0;
+ len = CPER_REC_LEN;
+ if (mem->validation_bits & CPER_MEM2_VALID_NODE)
+ n += scnprintf(msg + n, len - n, "node:%d ", mem->node);
+ if (mem->validation_bits & CPER_MEM2_VALID_CARD)
+ n += scnprintf(msg + n, len - n, "card:%d ", mem->card);
+ if (mem->validation_bits & CPER_MEM2_VALID_MODULE)
+ n += scnprintf(msg + n, len - n, "module:%d ", mem->module);
+ if (mem->validation_bits & CPER_MEM2_VALID_RANK_NUMBER)
+ n += scnprintf(msg + n, len - n, "rank:%u ", mem->rank);
+ if (mem->validation_bits & CPER_MEM2_VALID_BANK)
+ n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank);
+ if (mem->validation_bits & CPER_MEM2_VALID_BANK_GROUP)
+ n += scnprintf(msg + n, len - n, "bank_group:%d ",
+ mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
+ if (mem->validation_bits & CPER_MEM2_VALID_BANK_ADDRESS)
+ n += scnprintf(msg + n, len - n, "bank_address:%d ",
+ mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
+ if (mem->validation_bits & CPER_MEM2_VALID_DEVICE)
+ n += scnprintf(msg + n, len - n, "device:%u ", mem->device);
+ if (mem->validation_bits & CPER_MEM2_VALID_ROW)
+ n += scnprintf(msg + n, len - n, "row:%u ", mem->row);
+ if (mem->validation_bits & CPER_MEM2_VALID_COLUMN)
+ n += scnprintf(msg + n, len - n, "column:%u ", mem->column);
+ if (mem->validation_bits & CPER_MEM2_VALID_BIT_POSITION)
+ n += scnprintf(msg + n, len - n, "bit_position:%u ",
+ mem->bit_pos);
+ if (mem->validation_bits & CPER_MEM2_VALID_REQUESTOR_ID)
+ n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ",
+ mem->requestor_id);
+ if (mem->validation_bits & CPER_MEM2_VALID_RESPONDER_ID)
+ n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ",
+ mem->responder_id);
+ if (mem->validation_bits & CPER_MEM2_VALID_TARGET_ID)
+ n += scnprintf(msg + n, len - n, "target_id:0x%016llx ",
+ mem->target_id);
+ if (mem->validation_bits & CPER_MEM2_VALID_CHIP_ID)
+ n += scnprintf(msg + n, len - n, "chip_id:%d ",
+ mem->chip_id);
+
+ return n;
+}
+EXPORT_SYMBOL_GPL(cper_mem_err2_location);
+
+int cper_dimm_err2_location(const struct cper_sec_mem_err2 *mem, char *msg)
+{
+ u32 len, n;
+ const char *bank = NULL, *device = NULL;
+
+ if (!msg || !(mem->validation_bits & CPER_MEM2_VALID_MODULE_HANDLE))
+ return 0;
+
+ /* SMBIOS handles are 16 bits; never truncate a Section 2 handle. */
+ if (mem->mem_dev_handle <= U16_MAX)
+ dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
+ else
+ pr_warn(FW_WARN "Memory Error Section 2 module handle 0x%x exceeds SMBIOS range\n",
+ mem->mem_dev_handle);
+
+ len = CPER_REC_LEN;
+ if (bank && device)
+ n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
+ else
+ n = snprintf(msg, len,
+ "DIMM location: not present. DMI handle: 0x%.8x ",
+ mem->mem_dev_handle);
+
+ return n;
+}
+EXPORT_SYMBOL_GPL(cper_dimm_err2_location);
+
+static void cper_print_mem2(const char *pfx, const struct cper_sec_mem_err2 *mem)
+{
+ char rcd_decode_str[CPER_REC_LEN];
+
+ if (mem->validation_bits & CPER_MEM2_VALID_ERROR_STATUS)
+ printk("%serror_status: %s (0x%016llx)\n", pfx,
+ cper_mem_err_status_str(mem->error_status), mem->error_status);
+ if (mem->validation_bits & CPER_MEM2_VALID_PA)
+ printk("%sphysical_address: 0x%016llx\n", pfx, mem->physical_addr);
+ if (mem->validation_bits & CPER_MEM2_VALID_PA_MASK)
+ printk("%sphysical_address_mask: 0x%016llx\n", pfx,
+ mem->physical_addr_mask);
+ if (cper_mem_err2_location(mem, rcd_decode_str))
+ printk("%s%s\n", pfx, rcd_decode_str);
+ if (mem->validation_bits & CPER_MEM2_VALID_ERROR_TYPE)
+ printk("%serror_type: %u, %s\n", pfx, mem->error_type,
+ cper_mem_err_type_str(mem->error_type));
+ if (mem->validation_bits & CPER_MEM2_VALID_STATUS)
+ printk("%sstatus: 0x%02x\n", pfx, mem->status);
+ if (cper_dimm_err2_location(mem, rcd_decode_str))
+ printk("%s%s\n", pfx, rcd_decode_str);
+}
+
static const char * const pcie_port_type_strs[] = {
"PCIe end point",
"legacy PCI end point",
@@ -650,6 +751,14 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata
gdata->error_data_length);
else
goto err_section_too_small;
+ } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
+ struct cper_sec_mem_err2 *mem_err = acpi_hest_get_payload(gdata);
+
+ printk("%ssection_type: memory error 2\n", newpfx);
+ if (gdata->error_data_length >= sizeof(*mem_err))
+ cper_print_mem2(newpfx, mem_err);
+ 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);
diff --git a/include/linux/cper.h b/include/linux/cper.h
index ed355b240685..a466cba4fd61 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -656,6 +656,9 @@ void cper_print_proc_ia(const char *pfx,
int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg);
int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg);
+int cper_mem_err2_location(const struct cper_sec_mem_err2 *mem, char *msg);
+int cper_dimm_err2_location(const struct cper_sec_mem_err2 *mem, char *msg);
+
struct acpi_hest_generic_status;
void cper_estatus_print(const char *pfx,
const struct acpi_hest_generic_status *estatus);
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 4/6] EDAC/ghes: Extract error report initialization
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
` (2 preceding siblings ...)
2026-10-19 20:38 ` [PATCH v2 3/6] cper: add helpers to handle Memory Error Section 2 Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-20 17:09 ` Jonathan Cameron
2026-10-19 20:38 ` [PATCH v2 5/6] ACPI: APEI: add separate Memory Error Section 2 handlers Pierre Gondois
` (2 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Rafael J. Wysocki,
Len Brown, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Maciej Wieczor-Retman,
Pawel Chmielewski, Jonathan Cameron, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Terry Bowman, Dan Williams,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Extract a ghes_edac_init_error() function to prepare for
Memory Error Section 2 support (and re-usage).
Also use a guard in the caller ghes_edac_report_mem_error()
function for code simplification.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/edac/ghes_edac.c | 66 +++++++++++++++++++++-------------------
1 file changed, 35 insertions(+), 31 deletions(-)
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index d80c88818691..b90150c67f4b 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -267,37 +267,14 @@ static int print_mem_error_other_detail(const struct cper_sec_mem_err *mem, char
return n;
}
-static int ghes_edac_report_mem_error(struct notifier_block *nb,
- unsigned long val, void *data)
+/* Caller must hold ghes_lock. */
+static struct edac_raw_error_desc *
+ghes_edac_init_error(struct ghes_pvt *pvt, unsigned long sev)
{
- struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
- struct cper_mem_err_compact cmem;
- struct edac_raw_error_desc *e;
- struct mem_ctl_info *mci;
- unsigned long sev = val;
- struct ghes_pvt *pvt;
- unsigned long flags;
- char *p;
-
- /*
- * We can do the locking below because GHES defers error processing
- * from NMI to IRQ context. Whenever that changes, we'd at least
- * know.
- */
- if (WARN_ON_ONCE(in_nmi()))
- return NOTIFY_OK;
-
- spin_lock_irqsave(&ghes_lock, flags);
-
- pvt = ghes_pvt;
- if (!pvt)
- goto unlock;
-
- mci = pvt->mci;
- e = &mci->error_desc;
+ struct edac_raw_error_desc *e = &pvt->mci->error_desc;
/* Cleans the error report buffer */
- memset(e, 0, sizeof (*e));
+ memset(e, 0, sizeof(*e));
e->error_count = 1;
e->grain = 1;
e->msg = pvt->msg;
@@ -323,6 +300,36 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
e->type = HW_EVENT_ERR_INFO;
}
+ return e;
+}
+
+static int ghes_edac_report_mem_error(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
+ struct cper_mem_err_compact cmem;
+ struct edac_raw_error_desc *e;
+ struct mem_ctl_info *mci;
+ struct ghes_pvt *pvt;
+ char *p;
+
+ /*
+ * We can do the locking below because GHES defers error processing
+ * from NMI to IRQ context. Whenever that changes, we'd at least
+ * know.
+ */
+ if (WARN_ON_ONCE(in_nmi()))
+ return NOTIFY_OK;
+
+ guard(spinlock_irqsave)(&ghes_lock);
+
+ pvt = ghes_pvt;
+ if (!pvt)
+ return NOTIFY_OK;
+
+ mci = pvt->mci;
+ e = ghes_edac_init_error(pvt, val);
+
edac_dbg(1, "error validation_bits: 0x%08llx\n",
(long long)mem_err->validation_bits);
@@ -375,9 +382,6 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
edac_raw_mc_handle_error(e);
-unlock:
- spin_unlock_irqrestore(&ghes_lock, flags);
-
return NOTIFY_OK;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 4/6] EDAC/ghes: Extract error report initialization
2026-10-19 20:38 ` [PATCH v2 4/6] EDAC/ghes: Extract error report initialization Pierre Gondois
@ 2026-10-20 17:09 ` Jonathan Cameron
0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2026-10-20 17:09 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, James Morse, Mark Brown, Rafael J. Wysocki,
Len Brown, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Maciej Wieczor-Retman,
Pawel Chmielewski, Dave Jiang, Fabio M. De Francesco,
Breno Leitao, Terry Bowman, Dan Williams, Ard Biesheuvel,
Morduan Zang, linux-acpi, linux-edac, acpica-devel
On Mon, 19 Oct 2026 22:38:43 +0200
Pierre Gondois <pierre.gondois@arm.com> wrote:
> Extract a ghes_edac_init_error() function to prepare for
> Memory Error Section 2 support (and re-usage).
>
> Also use a guard in the caller ghes_edac_report_mem_error()
> function for code simplification.
>
> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
> ---
> drivers/edac/ghes_edac.c | 66 +++++++++++++++++++++-------------------
> 1 file changed, 35 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
> index d80c88818691..b90150c67f4b 100644
> --- a/drivers/edac/ghes_edac.c
> +++ b/drivers/edac/ghes_edac.c
> @@ -267,37 +267,14 @@ static int print_mem_error_other_detail(const struct cper_sec_mem_err *mem, char
> return n;
> }
>
> -static int ghes_edac_report_mem_error(struct notifier_block *nb,
> - unsigned long val, void *data)
> +/* Caller must hold ghes_lock. */
> +static struct edac_raw_error_desc *
> +ghes_edac_init_error(struct ghes_pvt *pvt, unsigned long sev)
Slightly unusual to have an init function that doesn't directly take the thing
being initialized. I'd pass it in. The clearly of pvt->msg and pvt->other_detail
is also a bit of a surprise for a function that claims to be initializing something
else so I'd do those outside of the helper.
Or maybe rename helper to something like
ghes_edac_fill_error_from_pvt()
Not sure that really conveys the modification of pvt data hidden in here though.
Perhaps this refactor was a bad suggestion from me and better to just duplicate
the code. What do ACPI maintainers think?
> {
> - struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
> - struct cper_mem_err_compact cmem;
> - struct edac_raw_error_desc *e;
> - struct mem_ctl_info *mci;
> - unsigned long sev = val;
> - struct ghes_pvt *pvt;
> - unsigned long flags;
> - char *p;
> -
> - /*
> - * We can do the locking below because GHES defers error processing
> - * from NMI to IRQ context. Whenever that changes, we'd at least
> - * know.
> - */
> - if (WARN_ON_ONCE(in_nmi()))
> - return NOTIFY_OK;
> -
> - spin_lock_irqsave(&ghes_lock, flags);
> -
> - pvt = ghes_pvt;
> - if (!pvt)
> - goto unlock;
> -
> - mci = pvt->mci;
> - e = &mci->error_desc;
> + struct edac_raw_error_desc *e = &pvt->mci->error_desc;
>
> /* Cleans the error report buffer */
> - memset(e, 0, sizeof (*e));
> + memset(e, 0, sizeof(*e));
> e->error_count = 1;
> e->grain = 1;
> e->msg = pvt->msg;
> @@ -323,6 +300,36 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
> e->type = HW_EVENT_ERR_INFO;
> }
>
> + return e;
> +}
> +
> +static int ghes_edac_report_mem_error(struct notifier_block *nb,
> + unsigned long val, void *data)
> +{
> + struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
> + struct cper_mem_err_compact cmem;
> + struct edac_raw_error_desc *e;
> + struct mem_ctl_info *mci;
> + struct ghes_pvt *pvt;
> + char *p;
> +
> + /*
> + * We can do the locking below because GHES defers error processing
> + * from NMI to IRQ context. Whenever that changes, we'd at least
> + * know.
> + */
> + if (WARN_ON_ONCE(in_nmi()))
> + return NOTIFY_OK;
> +
> + guard(spinlock_irqsave)(&ghes_lock);
> +
> + pvt = ghes_pvt;
> + if (!pvt)
> + return NOTIFY_OK;
> +
> + mci = pvt->mci;
> + e = ghes_edac_init_error(pvt, val);
> +
> edac_dbg(1, "error validation_bits: 0x%08llx\n",
> (long long)mem_err->validation_bits);
>
> @@ -375,9 +382,6 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
>
> edac_raw_mc_handle_error(e);
>
> -unlock:
> - spin_unlock_irqrestore(&ghes_lock, flags);
> -
> return NOTIFY_OK;
> }
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 5/6] ACPI: APEI: add separate Memory Error Section 2 handlers
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
` (3 preceding siblings ...)
2026-10-19 20:38 ` [PATCH v2 4/6] EDAC/ghes: Extract error report initialization Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-20 17:17 ` Jonathan Cameron
2026-10-19 20:38 ` [PATCH v2 6/6] ACPI: extlog: add Memory Error Section 2 trace event Pierre Gondois
2026-10-20 17:10 ` [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Jonathan Cameron
6 siblings, 1 reply; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Rafael J. Wysocki,
Len Brown, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Maciej Wieczor-Retman,
Pawel Chmielewski, Jonathan Cameron, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Terry Bowman, Dan Williams,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Add dedicated GHES and EDAC callbacks for Memory Error Section 2
records. Use a separate GHES notifier chain so existing consumers
continue to receive legacy memory error sections.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/acpi/apei/ghes.c | 49 +++++++++++++++-
drivers/edac/ghes_edac.c | 118 +++++++++++++++++++++++++++++++++++++++
include/acpi/ghes.h | 2 +
3 files changed, 168 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index fe10ab0e02f6..c7fb15543b56 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -101,6 +101,7 @@
#endif
static ATOMIC_NOTIFIER_HEAD(ghes_report_chain);
+static ATOMIC_NOTIFIER_HEAD(ghes_report_mem2_chain);
static inline bool is_hest_type_generic_v2(struct ghes *ghes)
{
@@ -551,6 +552,29 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
return false;
}
+static bool ghes_handle_memory_failure2(struct acpi_hest_generic_data *gdata,
+ int sev, bool sync)
+{
+ int flags = -1;
+ int sec_sev = ghes_severity(gdata->error_severity);
+ struct cper_sec_mem_err2 *mem_err = acpi_hest_get_payload(gdata);
+
+ if (!(mem_err->validation_bits & CPER_MEM2_VALID_PA))
+ return false;
+
+ /* iff following two events can be handled properly by now */
+ if (sec_sev == GHES_SEV_CORRECTED &&
+ (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
+ flags = MF_SOFT_OFFLINE;
+ if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
+ flags = sync ? MF_ACTION_REQUIRED : 0;
+
+ if (flags != -1)
+ return ghes_do_memory_failure(mem_err->physical_addr, flags);
+
+ return false;
+}
+
static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
int sev, bool sync)
{
@@ -915,7 +939,8 @@ static void ghes_log_hwerr(int sev, guid_t *sec_type)
return;
}
- if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
+ if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM) ||
+ guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
hwerr_log_error_type(HWERR_RECOV_MEMORY);
return;
}
@@ -952,6 +977,16 @@ static void ghes_do_proc(struct ghes *ghes,
arch_apei_report_mem_error(sev, mem_err);
queued = ghes_handle_memory_failure(gdata, sev, sync);
+ } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
+ struct cper_sec_mem_err2 *mem_err = acpi_hest_get_payload(gdata);
+
+ if (gdata->error_data_length < sizeof(*mem_err)) {
+ pr_warn(FW_WARN "Memory Error Section 2 length is too small\n");
+ continue;
+ }
+
+ atomic_notifier_call_chain(&ghes_report_mem2_chain, sev, mem_err);
+ queued = ghes_handle_memory_failure2(gdata, sev, sync);
} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
ghes_handle_aer(gdata);
} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
@@ -1973,3 +2008,15 @@ void ghes_unregister_report_chain(struct notifier_block *nb)
atomic_notifier_chain_unregister(&ghes_report_chain, nb);
}
EXPORT_SYMBOL_GPL(ghes_unregister_report_chain);
+
+void ghes_register_report_mem2_chain(struct notifier_block *nb)
+{
+ atomic_notifier_chain_register(&ghes_report_mem2_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_register_report_mem2_chain);
+
+void ghes_unregister_report_mem2_chain(struct notifier_block *nb)
+{
+ atomic_notifier_chain_unregister(&ghes_report_mem2_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_unregister_report_mem2_chain);
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index b90150c67f4b..01defc4e3bf1 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -390,6 +390,122 @@ static struct notifier_block ghes_edac_mem_err_nb = {
.priority = 0,
};
+static int print_mem_error2_other_detail(const struct cper_sec_mem_err2 *mem, char *msg,
+ const char *location, unsigned int len)
+{
+ u32 n;
+
+ if (!msg)
+ return 0;
+
+ n = 0;
+ len -= 1;
+
+ n += scnprintf(msg + n, len - n, "APEI location: %s ", location);
+
+ if (mem->validation_bits & CPER_MEM2_VALID_ERROR_STATUS) {
+ n += scnprintf(msg + n, len - n, "status(0x%016llx): ", mem->error_status);
+ n += scnprintf(msg + n, len - n, "%s ",
+ cper_mem_err_status_str(mem->error_status));
+ }
+ if (mem->validation_bits & CPER_MEM2_VALID_STATUS)
+ n += scnprintf(msg + n, len - n, "section_status:0x%02x ",
+ mem->status);
+
+ msg[n] = '\0';
+
+ return n;
+}
+
+static int ghes_edac_report_mem_error2(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cper_sec_mem_err2 *mem_err = data;
+ struct edac_raw_error_desc *e;
+ struct mem_ctl_info *mci;
+ struct ghes_pvt *pvt;
+ char *p;
+
+ /*
+ * We can do the locking below because GHES defers error processing
+ * from NMI to IRQ context. Whenever that changes, we'd at least
+ * know.
+ */
+ if (WARN_ON_ONCE(in_nmi()))
+ return NOTIFY_OK;
+
+ guard(spinlock_irqsave)(&ghes_lock);
+
+ pvt = ghes_pvt;
+ if (!pvt)
+ return NOTIFY_OK;
+
+ mci = pvt->mci;
+ e = ghes_edac_init_error(pvt, val);
+
+ edac_dbg(1, "error validation_bits: 0x%08llx\n",
+ (long long)mem_err->validation_bits);
+
+ /* Error type, mapped on e->msg */
+ if (mem_err->validation_bits & CPER_MEM2_VALID_ERROR_TYPE) {
+ u8 etype = mem_err->error_type;
+
+ p = pvt->msg;
+ p += snprintf(p, sizeof(pvt->msg), "%s", cper_mem_err_type_str(etype));
+ } else {
+ strscpy(pvt->msg, "unknown error");
+ }
+
+ /* Error address */
+ if (mem_err->validation_bits & CPER_MEM2_VALID_PA) {
+ e->page_frame_number = PHYS_PFN(mem_err->physical_addr);
+ e->offset_in_page = offset_in_page(mem_err->physical_addr);
+ }
+
+ /* Error grain */
+ if (mem_err->validation_bits & CPER_MEM2_VALID_PA_MASK)
+ e->grain = ~mem_err->physical_addr_mask + 1;
+
+ /* Memory error location, mapped on e->location */
+ p = e->location;
+ p += cper_mem_err2_location(mem_err, p);
+
+ if (mem_err->validation_bits & CPER_MEM2_VALID_MODULE_HANDLE) {
+ struct dimm_info *dimm = NULL;
+
+ p += cper_dimm_err2_location(mem_err, p);
+ if (mem_err->mem_dev_handle <= U16_MAX)
+ dimm = find_dimm_by_handle(mci, mem_err->mem_dev_handle);
+ else
+ pr_warn(FW_WARN "Memory Error Section 2 module handle 0x%x exceeds SMBIOS range\n",
+ mem_err->mem_dev_handle);
+ if (dimm) {
+ e->top_layer = dimm->idx;
+ strscpy(e->label, dimm->label);
+ }
+ }
+ if (p > e->location)
+ *(p - 1) = '\0';
+
+ if (!*e->label)
+ strscpy(e->label, "unknown memory");
+
+ /* All other fields are mapped on e->other_detail */
+ p = pvt->other_detail;
+ p += print_mem_error2_other_detail(mem_err, p, e->location, OTHER_DETAIL_LEN);
+ if (p > pvt->other_detail)
+ *(p - 1) = '\0';
+
+ edac_raw_mc_handle_error(e);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ghes_edac_mem_err2_nb = {
+ .notifier_call = ghes_edac_report_mem_error2,
+ .priority = 0,
+};
+
static int ghes_edac_register(struct device *dev)
{
bool fake = false;
@@ -494,6 +610,7 @@ static int ghes_edac_register(struct device *dev)
spin_unlock_irqrestore(&ghes_lock, flags);
ghes_register_report_chain(&ghes_edac_mem_err_nb);
+ ghes_register_report_mem2_chain(&ghes_edac_mem_err2_nb);
/* only set on success */
refcount_set(&ghes_refcount, 1);
@@ -537,6 +654,7 @@ static void ghes_edac_unregister(struct ghes *ghes)
if (mci)
edac_mc_free(mci);
+ ghes_unregister_report_mem2_chain(&ghes_edac_mem_err2_nb);
ghes_unregister_report_chain(&ghes_edac_mem_err_nb);
unlock:
diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h
index 8d7e5caef3f1..73d099a4ea7d 100644
--- a/include/acpi/ghes.h
+++ b/include/acpi/ghes.h
@@ -143,4 +143,6 @@ static inline int ghes_notify_sea(void) { return -ENOENT; }
struct notifier_block;
extern void ghes_register_report_chain(struct notifier_block *nb);
extern void ghes_unregister_report_chain(struct notifier_block *nb);
+extern void ghes_register_report_mem2_chain(struct notifier_block *nb);
+extern void ghes_unregister_report_mem2_chain(struct notifier_block *nb);
#endif /* GHES_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 5/6] ACPI: APEI: add separate Memory Error Section 2 handlers
2026-10-19 20:38 ` [PATCH v2 5/6] ACPI: APEI: add separate Memory Error Section 2 handlers Pierre Gondois
@ 2026-10-20 17:17 ` Jonathan Cameron
0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2026-10-20 17:17 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, James Morse, Mark Brown, Rafael J. Wysocki,
Len Brown, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Maciej Wieczor-Retman,
Pawel Chmielewski, Dave Jiang, Fabio M. De Francesco,
Breno Leitao, Terry Bowman, Dan Williams, Ard Biesheuvel,
Morduan Zang, linux-acpi, linux-edac, acpica-devel
On Mon, 19 Oct 2026 22:38:44 +0200
Pierre Gondois <pierre.gondois@arm.com> wrote:
> Add dedicated GHES and EDAC callbacks for Memory Error Section 2
> records. Use a separate GHES notifier chain so existing consumers
> continue to receive legacy memory error sections.
>
> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
I think this is is ok. I don't love the dual notifier chains but
right now I also don't have a better idea.
So a somewhat tentative
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Jonathan
> ---
> drivers/acpi/apei/ghes.c | 49 +++++++++++++++-
> drivers/edac/ghes_edac.c | 118 +++++++++++++++++++++++++++++++++++++++
> include/acpi/ghes.h | 2 +
> 3 files changed, 168 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index fe10ab0e02f6..c7fb15543b56 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -101,6 +101,7 @@
> #endif
>
> static ATOMIC_NOTIFIER_HEAD(ghes_report_chain);
> +static ATOMIC_NOTIFIER_HEAD(ghes_report_mem2_chain);
>
> static inline bool is_hest_type_generic_v2(struct ghes *ghes)
> {
> @@ -551,6 +552,29 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
> return false;
> }
>
> +static bool ghes_handle_memory_failure2(struct acpi_hest_generic_data *gdata,
> + int sev, bool sync)
> +{
> + int flags = -1;
> + int sec_sev = ghes_severity(gdata->error_severity);
> + struct cper_sec_mem_err2 *mem_err = acpi_hest_get_payload(gdata);
> +
> + if (!(mem_err->validation_bits & CPER_MEM2_VALID_PA))
> + return false;
> +
> + /* iff following two events can be handled properly by now */
> + if (sec_sev == GHES_SEV_CORRECTED &&
> + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
> + flags = MF_SOFT_OFFLINE;
> + if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
> + flags = sync ? MF_ACTION_REQUIRED : 0;
> +
> + if (flags != -1)
> + return ghes_do_memory_failure(mem_err->physical_addr, flags);
> +
> + return false;
> +}
> +
> static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
> int sev, bool sync)
> {
> @@ -915,7 +939,8 @@ static void ghes_log_hwerr(int sev, guid_t *sec_type)
> return;
> }
>
> - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
> + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM) ||
> + guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
> hwerr_log_error_type(HWERR_RECOV_MEMORY);
> return;
> }
> @@ -952,6 +977,16 @@ static void ghes_do_proc(struct ghes *ghes,
>
> arch_apei_report_mem_error(sev, mem_err);
> queued = ghes_handle_memory_failure(gdata, sev, sync);
> + } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
> + struct cper_sec_mem_err2 *mem_err = acpi_hest_get_payload(gdata);
> +
> + if (gdata->error_data_length < sizeof(*mem_err)) {
> + pr_warn(FW_WARN "Memory Error Section 2 length is too small\n");
> + continue;
> + }
> +
> + atomic_notifier_call_chain(&ghes_report_mem2_chain, sev, mem_err);
> + queued = ghes_handle_memory_failure2(gdata, sev, sync);
> } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
> ghes_handle_aer(gdata);
> } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
> @@ -1973,3 +2008,15 @@ void ghes_unregister_report_chain(struct notifier_block *nb)
> atomic_notifier_chain_unregister(&ghes_report_chain, nb);
> }
> EXPORT_SYMBOL_GPL(ghes_unregister_report_chain);
> +
> +void ghes_register_report_mem2_chain(struct notifier_block *nb)
> +{
> + atomic_notifier_chain_register(&ghes_report_mem2_chain, nb);
> +}
> +EXPORT_SYMBOL_GPL(ghes_register_report_mem2_chain);
> +
> +void ghes_unregister_report_mem2_chain(struct notifier_block *nb)
> +{
> + atomic_notifier_chain_unregister(&ghes_report_mem2_chain, nb);
> +}
> +EXPORT_SYMBOL_GPL(ghes_unregister_report_mem2_chain);
> diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
> index b90150c67f4b..01defc4e3bf1 100644
> --- a/drivers/edac/ghes_edac.c
> +++ b/drivers/edac/ghes_edac.c
> @@ -390,6 +390,122 @@ static struct notifier_block ghes_edac_mem_err_nb = {
> .priority = 0,
> };
>
> +static int print_mem_error2_other_detail(const struct cper_sec_mem_err2 *mem, char *msg,
> + const char *location, unsigned int len)
> +{
> + u32 n;
> +
> + if (!msg)
> + return 0;
> +
> + n = 0;
> + len -= 1;
> +
> + n += scnprintf(msg + n, len - n, "APEI location: %s ", location);
> +
> + if (mem->validation_bits & CPER_MEM2_VALID_ERROR_STATUS) {
> + n += scnprintf(msg + n, len - n, "status(0x%016llx): ", mem->error_status);
> + n += scnprintf(msg + n, len - n, "%s ",
> + cper_mem_err_status_str(mem->error_status));
> + }
> + if (mem->validation_bits & CPER_MEM2_VALID_STATUS)
> + n += scnprintf(msg + n, len - n, "section_status:0x%02x ",
> + mem->status);
> +
> + msg[n] = '\0';
> +
> + return n;
> +}
> +
> +static int ghes_edac_report_mem_error2(struct notifier_block *nb,
> + unsigned long val, void *data)
> +{
> + struct cper_sec_mem_err2 *mem_err = data;
> + struct edac_raw_error_desc *e;
> + struct mem_ctl_info *mci;
> + struct ghes_pvt *pvt;
> + char *p;
> +
> + /*
> + * We can do the locking below because GHES defers error processing
> + * from NMI to IRQ context. Whenever that changes, we'd at least
> + * know.
> + */
> + if (WARN_ON_ONCE(in_nmi()))
> + return NOTIFY_OK;
> +
> + guard(spinlock_irqsave)(&ghes_lock);
> +
> + pvt = ghes_pvt;
> + if (!pvt)
> + return NOTIFY_OK;
> +
> + mci = pvt->mci;
> + e = ghes_edac_init_error(pvt, val);
> +
> + edac_dbg(1, "error validation_bits: 0x%08llx\n",
> + (long long)mem_err->validation_bits);
> +
> + /* Error type, mapped on e->msg */
> + if (mem_err->validation_bits & CPER_MEM2_VALID_ERROR_TYPE) {
> + u8 etype = mem_err->error_type;
> +
> + p = pvt->msg;
> + p += snprintf(p, sizeof(pvt->msg), "%s", cper_mem_err_type_str(etype));
> + } else {
> + strscpy(pvt->msg, "unknown error");
> + }
> +
> + /* Error address */
> + if (mem_err->validation_bits & CPER_MEM2_VALID_PA) {
> + e->page_frame_number = PHYS_PFN(mem_err->physical_addr);
> + e->offset_in_page = offset_in_page(mem_err->physical_addr);
> + }
> +
> + /* Error grain */
> + if (mem_err->validation_bits & CPER_MEM2_VALID_PA_MASK)
> + e->grain = ~mem_err->physical_addr_mask + 1;
> +
> + /* Memory error location, mapped on e->location */
> + p = e->location;
> + p += cper_mem_err2_location(mem_err, p);
> +
> + if (mem_err->validation_bits & CPER_MEM2_VALID_MODULE_HANDLE) {
> + struct dimm_info *dimm = NULL;
> +
> + p += cper_dimm_err2_location(mem_err, p);
> + if (mem_err->mem_dev_handle <= U16_MAX)
> + dimm = find_dimm_by_handle(mci, mem_err->mem_dev_handle);
> + else
> + pr_warn(FW_WARN "Memory Error Section 2 module handle 0x%x exceeds SMBIOS range\n",
> + mem_err->mem_dev_handle);
> + if (dimm) {
> + e->top_layer = dimm->idx;
> + strscpy(e->label, dimm->label);
> + }
> + }
> + if (p > e->location)
> + *(p - 1) = '\0';
> +
> + if (!*e->label)
> + strscpy(e->label, "unknown memory");
> +
> + /* All other fields are mapped on e->other_detail */
> + p = pvt->other_detail;
> + p += print_mem_error2_other_detail(mem_err, p, e->location, OTHER_DETAIL_LEN);
> + if (p > pvt->other_detail)
> + *(p - 1) = '\0';
> +
> + edac_raw_mc_handle_error(e);
> +
> + return NOTIFY_OK;
> +}
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 6/6] ACPI: extlog: add Memory Error Section 2 trace event
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
` (4 preceding siblings ...)
2026-10-19 20:38 ` [PATCH v2 5/6] ACPI: APEI: add separate Memory Error Section 2 handlers Pierre Gondois
@ 2026-10-19 20:38 ` Pierre Gondois
2026-10-20 17:10 ` [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Jonathan Cameron
6 siblings, 0 replies; 10+ messages in thread
From: Pierre Gondois @ 2026-10-19 20:38 UTC (permalink / raw)
To: linux-kernel
Cc: James Morse, Mark Brown, Pierre Gondois, Jonathan Cameron,
Rafael J. Wysocki, Len Brown, Tony Luck, Borislav Petkov,
Hanjun Guo, Mauro Carvalho Chehab, Shuai Xue,
Maciej Wieczor-Retman, Pawel Chmielewski, Dave Jiang,
Fabio M. De Francesco, Breno Leitao, Dan Williams, Terry Bowman,
Ard Biesheuvel, Morduan Zang, linux-acpi, linux-edac,
acpica-devel
Add a separate handling for Memory Error Section 2 trace events.
The fields of the record are individually printed so userspace
can discover the layout more dynamically if a new field is added.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/acpi/acpi_extlog.c | 6 +++
drivers/ras/ras.c | 1 +
include/ras/ras_event.h | 102 +++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+)
diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c
index 7ad3b36013cc..9e4d16e06244 100644
--- a/drivers/acpi/acpi_extlog.c
+++ b/drivers/acpi/acpi_extlog.c
@@ -231,6 +231,12 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
if (gdata->error_data_length >= sizeof(*mem))
trace_extlog_mem_event(mem, err_seq, fru_id, fru_text,
(u8)gdata->error_severity);
+ } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM2)) {
+ struct cper_sec_mem_err2 *mem = acpi_hest_get_payload(gdata);
+
+ if (gdata->error_data_length >= sizeof(*mem))
+ trace_extlog_mem2_event(mem, err_seq, fru_id, fru_text,
+ (u8)gdata->error_severity);
} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
struct cxl_cper_sec_prot_err *prot_err =
acpi_hest_get_payload(gdata);
diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c
index 03df3db62334..c6b14202a5e9 100644
--- a/drivers/ras/ras.c
+++ b/drivers/ras/ras.c
@@ -111,6 +111,7 @@ subsys_initcall(ras_init);
#if defined(CONFIG_ACPI_EXTLOG) || defined(CONFIG_ACPI_EXTLOG_MODULE)
EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem_event);
+EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem2_event);
#endif
EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event);
EXPORT_TRACEPOINT_SYMBOL_GPL(non_standard_event);
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
index fdb785fa4613..0c0dbe068dc8 100644
--- a/include/ras/ras_event.h
+++ b/include/ras/ras_event.h
@@ -74,6 +74,108 @@ TRACE_EVENT(extlog_mem_event,
&__entry->fru_id,
__get_str(fru_text))
);
+
+/*
+ * Memory Error Section 2 exposes each field separately so userspace can
+ * discover its layout. Validation bits use the UEFI Section 2 assignments.
+ */
+TRACE_EVENT(extlog_mem2_event,
+ TP_PROTO(const struct cper_sec_mem_err2 *mem,
+ u32 err_seq,
+ const guid_t *fru_id,
+ const char *fru_text,
+ u8 sev),
+
+ TP_ARGS(mem, err_seq, fru_id, fru_text, sev),
+
+ TP_STRUCT__entry(
+ __field(u32, err_seq)
+ __field(u8, sev)
+ __field_struct(guid_t, fru_id)
+ __string(fru_text, fru_text)
+ __field(u64, validation_bits)
+ __field(u64, error_status)
+ __field(u64, physical_addr)
+ __field(u64, physical_addr_mask)
+ __field(u16, node)
+ __field(u16, card)
+ __field(u16, module)
+ __field(u16, bank)
+ __field(u32, device)
+ __field(u32, row)
+ __field(u32, column)
+ __field(u32, rank)
+ __field(u32, bit_pos)
+ __field(u8, chip_id)
+ __field(u8, error_type)
+ __field(u8, status)
+ __field(u64, requestor_id)
+ __field(u64, responder_id)
+ __field(u64, target_id)
+ __field(u32, mem_array_handle)
+ __field(u32, mem_dev_handle)
+ ),
+
+ TP_fast_assign(
+ __entry->err_seq = err_seq;
+ __entry->sev = sev;
+ __entry->fru_id = *fru_id;
+ __assign_str(fru_text);
+ __entry->validation_bits = mem->validation_bits;
+ __entry->error_status = mem->error_status;
+ __entry->physical_addr = mem->physical_addr;
+ __entry->physical_addr_mask = mem->physical_addr_mask;
+ __entry->node = mem->node;
+ __entry->card = mem->card;
+ __entry->module = mem->module;
+ __entry->bank = mem->bank;
+ __entry->device = mem->device;
+ __entry->row = mem->row;
+ __entry->column = mem->column;
+ __entry->rank = mem->rank;
+ __entry->bit_pos = mem->bit_pos;
+ __entry->chip_id = mem->chip_id;
+ __entry->error_type = mem->error_type;
+ __entry->status = mem->status;
+ __entry->requestor_id = mem->requestor_id;
+ __entry->responder_id = mem->responder_id;
+ __entry->target_id = mem->target_id;
+ __entry->mem_array_handle = mem->mem_array_handle;
+ __entry->mem_dev_handle = mem->mem_dev_handle;
+ ),
+
+ TP_printk("{%u} %s error: validation_bits: 0x%llx "
+ "error_status: 0x%llx physical_addr: 0x%llx "
+ "physical_addr_mask: 0x%llx node: %u card: %u "
+ "module: %u bank: %u device: %u row: %u column: %u "
+ "rank: %u bit_pos: %u chip_id: %u error_type: %u "
+ "status: 0x%x requestor_id: 0x%llx responder_id: 0x%llx "
+ "target_id: 0x%llx mem_array_handle: 0x%x "
+ "mem_dev_handle: 0x%x FRU: %pUl %.20s",
+ __entry->err_seq, cper_severity_str(__entry->sev),
+ __entry->validation_bits,
+ __entry->error_status,
+ __entry->physical_addr,
+ __entry->physical_addr_mask,
+ __entry->node,
+ __entry->card,
+ __entry->module,
+ __entry->bank,
+ __entry->device,
+ __entry->row,
+ __entry->column,
+ __entry->rank,
+ __entry->bit_pos,
+ __entry->chip_id,
+ __entry->error_type,
+ __entry->status,
+ __entry->requestor_id,
+ __entry->responder_id,
+ __entry->target_id,
+ __entry->mem_array_handle,
+ __entry->mem_dev_handle,
+ &__entry->fru_id, __get_str(fru_text))
+);
#endif
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 0/6] CPER: Add Memory Error Section 2 support
2026-10-19 20:38 [PATCH v2 0/6] CPER: Add Memory Error Section 2 support Pierre Gondois
` (5 preceding siblings ...)
2026-10-19 20:38 ` [PATCH v2 6/6] ACPI: extlog: add Memory Error Section 2 trace event Pierre Gondois
@ 2026-10-20 17:10 ` Jonathan Cameron
6 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2026-10-20 17:10 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, James Morse, Mark Brown, Rafael J. Wysocki,
Len Brown, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Maciej Wieczor-Retman,
Pawel Chmielewski, Dave Jiang, Fabio M. De Francesco,
Breno Leitao, Dan Williams, Terry Bowman, Ard Biesheuvel,
Morduan Zang, linux-acpi, linux-edac, acpica-devel
On Mon, 19 Oct 2026 22:38:39 +0200
Pierre Gondois <pierre.gondois@arm.com> wrote:
> The support is mostly duplicated from Memory Error Section handling.
> Other than differences coming from the content of Memory Error
> Section 1 and 2, differences in the support are:
>
> - v2 do not rely on a compact "struct cper_mem_err_compact" struct.
> This structure was used to support older versions of the Memory
> Error Section (1). This is not needed for the new structure.
>
> - The compact version is also used by the tracing framework.
> To avoid relying and propagating a non-standard record format,
> through trace event (cf. extlog_mem event) the new trace event
> prints each field individually.
>
> The last patch of the serie was only build-tested due to the
> absence of relevant hardware. apei_mce_report_mem_error() and
> arch_apei_report_mem_error() implementations were also removed since
> last version.
>
> It will also require a proper handling from the rasdaemon. Applying
> this last patch without rasdaemon support will silent the reports of
> Memory Error Section 2 error, cf. ras_userspace_consumers() check.
Hi Pierre,
I'd slow down a bit. We are mid merge window and convention is normally
one version a week or so anyway. Better to gather up feedback from
a number of folk and spin only one revision to cover it all.
Jonathan
^ permalink raw reply [flat|nested] 10+ messages in thread