From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BD3913F926D; Mon, 18 May 2026 11:58:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105511; cv=none; b=mmxJg0PNdAzpO3NMDTPVOYHAzcjDvvedZVlhVuHVyZ1a0tvlf2cPQI3yMoRZaIbz3+N1PQsKGxHD7/nBFfMA2JmsJcgnv5TIhr2/NCbNqr0aYh8/Q3SKm8VcSlts3xwwyScKzV1BBeNpjUEtnRP/OvGmeYsC1a4oH48kqnx6GuA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105511; c=relaxed/simple; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=C8OjDHVg0Sl3E3+tByEgpFgfgsv34RpdtyQNv/qdgppAqfpW/vaZYkiUkQvo1KQDexNVeD9lytRu0rAxvjYLSH8idaGnDcPjAZItiWCrhBY+jEO0tA9E02uEfqHRRQpgxYun4/8bRb28W7gPenauUxhfZZdY+SufmmNrWZZxyMM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=T4QPeP6c; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="T4QPeP6c" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2E3384617; Mon, 18 May 2026 04:58:16 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 35FE23F85F; Mon, 18 May 2026 04:58:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105501; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=T4QPeP6cz2CqmB9aA1DA9XM3E+rBmp4WPny50Y1vNCUW6ViaLuAplY8jFknoF3UCd Oxa7ntq9KKunWZweDHJMprokMYtnVUV7xCCoB5VR99+I9o6VapbMyMhFzlgvn18EJO 4Ec0xe01wt6ntqa84YLEpDOl7Q3iXHywJxNJXR+8= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:44 +0100 Subject: [PATCH v4 01/10] ACPI: APEI: GHES: share macros via a private header Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-1-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=10841; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; b=wJzJt+zo81m+ERBSLhKS4qTQw6I6mbGeFNqrtWfj6AP5rVAYgBe542ZpiRvpSyAQiw3I/S6au 9F+f7TdBXupB65/8MTlsdM1i+V5XLs1guao5XUgSl1794JBIXXhm4Qd X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Carve the CPER helper macros out of ghes.c and place them in a private header so they can be shared with upcoming helper files. This is a mechanical include change with no functional differences. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 94 +++++++++---------------------------------- include/acpi/ghes_cper.h | 102 +++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 120 insertions(+), 76 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3236a3ce79d6..4f67f46410c4 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -49,6 +49,7 @@ =20 #include #include +#include #include #include #include @@ -57,40 +58,6 @@ =20 #include "apei-internal.h" =20 -#define GHES_PFX "GHES: " - -#define GHES_ESTATUS_MAX_SIZE 65536 -#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 - -#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 - -/* This is just an estimation for memory pool allocation */ -#define GHES_ESTATUS_CACHE_AVG_SIZE 512 - -#define GHES_ESTATUS_CACHES_SIZE 4 - -#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL -/* Prevent too many caches are allocated because of RCU */ -#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) - -#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ - (sizeof(struct ghes_estatus_cache) + (estatus_len)) -#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ - ((struct acpi_hest_generic_status *) \ - ((struct ghes_estatus_cache *)(estatus_cache) + 1)) - -#define GHES_ESTATUS_NODE_LEN(estatus_len) \ - (sizeof(struct ghes_estatus_node) + (estatus_len)) -#define GHES_ESTATUS_FROM_NODE(estatus_node) \ - ((struct acpi_hest_generic_status *) \ - ((struct ghes_estatus_node *)(estatus_node) + 1)) - -#define GHES_VENDOR_ENTRY_LEN(gdata_len) \ - (sizeof(struct ghes_vendor_record_entry) + (gdata_len)) -#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \ - ((struct acpi_hest_generic_data *) \ - ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) - /* * NMI-like notifications vary by architecture, before the compiler can p= rune * unused static functions it needs a value for these enums. @@ -102,25 +69,6 @@ =20 static ATOMIC_NOTIFIER_HEAD(ghes_report_chain); =20 -static inline bool is_hest_type_generic_v2(struct ghes *ghes) -{ - return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; -} - -/* - * A platform may describe one error source for the handling of synchronous - * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI - * or External Interrupt). On x86, the HEST notifications are always - * asynchronous, so only SEA on ARM is delivered as a synchronous - * notification. - */ -static inline bool is_hest_sync_notify(struct ghes *ghes) -{ - u8 notify_type =3D ghes->generic->notify.type; - - return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; -} - /* * This driver isn't really modular, however for the time being, * continuing to use module_param is the easiest way to remain @@ -165,12 +113,6 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -struct ghes_vendor_record_entry { - struct work_struct work; - int error_severity; - char vendor_record[]; -}; - static struct gen_pool *ghes_estatus_pool; =20 static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; @@ -266,7 +208,7 @@ static void ghes_ack_error(struct acpi_hest_generic_v2 = *gv2) apei_write(val, &gv2->read_ack_register); } =20 -static struct ghes *ghes_new(struct acpi_hest_generic *generic) +struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; unsigned int error_block_length; @@ -313,7 +255,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *= generic) return ERR_PTR(rc); } =20 -static void ghes_fini(struct ghes *ghes) +void ghes_fini(struct ghes *ghes) { kfree(ghes->estatus); apei_unmap_generic_address(&ghes->generic->error_status_address); @@ -363,8 +305,8 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 pad= dr, u32 len, } =20 /* Check the top-level record header has an appropriate size. */ -static int __ghes_check_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus) +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus) { u32 len =3D cper_estatus_len(estatus); u32 max_len =3D min(ghes->generic->error_block_length, @@ -389,9 +331,9 @@ static int __ghes_check_estatus(struct ghes *ghes, } =20 /* Read the CPER block, returning its address, and header in estatus. */ -static int __ghes_peek_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) { struct acpi_hest_generic *g =3D ghes->generic; int rc; @@ -400,7 +342,7 @@ static int __ghes_peek_estatus(struct ghes *ghes, if (rc) { *buf_paddr =3D 0; pr_warn_ratelimited(FW_WARN GHES_PFX -"Failed to read error status block address for hardware error source: %d.\= n", + "Failed to read error status block address for hardware error sour= ce: %d.\n", g->header.source_id); return -EIO; } @@ -417,9 +359,9 @@ static int __ghes_peek_estatus(struct ghes *ghes, return 0; } =20 -static int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx, - size_t buf_len) +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len) { ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); if (cper_estatus_check(estatus)) { @@ -431,9 +373,9 @@ static int __ghes_read_estatus(struct acpi_hest_generic= _status *estatus, return 0; } =20 -static int ghes_read_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) { int rc; =20 @@ -449,9 +391,9 @@ static int ghes_read_estatus(struct ghes *ghes, cper_estatus_len(estatus)); } =20 -static void ghes_clear_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx) +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx) { estatus->block_status =3D 0; =20 diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h new file mode 100644 index 000000000000..6b7632cfaf66 --- /dev/null +++ b/include/acpi/ghes_cper.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * GHES declarations used by both the ACPI APEI GHES driver + * and the firmware-first CPER provider. + * + * These declarations lets GHES and other firmware-first error sources use + * the same helper so the non-ACPI path follows the same + * behavior as GHES instead of carrying a separate copy. + * + * Derived from the ACPI APEI GHES driver. + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + */ + +#ifndef ACPI_APEI_GHES_CPER_H +#define ACPI_APEI_GHES_CPER_H + +#include + +#include + +#define GHES_PFX "GHES: " + +#define GHES_ESTATUS_MAX_SIZE 65536 +#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 + +#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 + +/* This is just an estimation for memory pool allocation */ +#define GHES_ESTATUS_CACHE_AVG_SIZE 512 + +#define GHES_ESTATUS_CACHES_SIZE 4 + +#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL +/* Prevent too many caches are allocated because of RCU */ +#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) + +#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_cache) + (estatus_len)) +#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_cache *)(estatus_cache) + 1)) + +#define GHES_ESTATUS_NODE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_node) + (estatus_len)) +#define GHES_ESTATUS_FROM_NODE(estatus_node) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_node *)(estatus_node) + 1)) + +#define GHES_VENDOR_ENTRY_LEN(gdata_len) \ + (sizeof(struct ghes_vendor_record_entry) + (gdata_len)) +#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \ + ((struct acpi_hest_generic_data *) \ + ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) + +static inline bool is_hest_type_generic_v2(struct ghes *ghes) +{ + return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; +} + +/* + * A platform may describe one error source for the handling of synchronous + * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI + * or External Interrupt). On x86, the HEST notifications are always + * asynchronous, so only SEA on ARM is delivered as a synchronous + * notification. + */ +static inline bool is_hest_sync_notify(struct ghes *ghes) +{ + u8 notify_type =3D ghes->generic->notify.type; + + return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; +} + +struct ghes_vendor_record_entry { + struct work_struct work; + int error_severity; + char vendor_record[]; +}; + +#ifdef CONFIG_ACPI_APEI +struct ghes *ghes_new(struct acpi_hest_generic *generic); +void ghes_fini(struct ghes *ghes); + +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx); +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx); +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx); +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus); +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len); +#endif + +#endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A271732D441; Mon, 18 May 2026 11:58:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105519; cv=none; b=k67OWdFpWRLlwyzHW3bK7UkZcFRrpxUM1Qqa9v/miRX/8utnn7c7Z98uIXJ76DgraaFTTi/xTwIETlf0rMW2iYBh0eggLMy7aRFN2+QZDeATQIuNihiynEov5s0Vl3rWB3U+Nqk17ixh1vrQcmHr4NbtfMs8xPpPaROCpPwjUKk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105519; c=relaxed/simple; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=msTBqOJUD1NOANyrN6au7YXOUrSUPsHm4q8Op3GyjOSAWbl6euC9FaRjB0SJ01vj5n1z8N7eP6Ceg98zmoFO2NJRc+yxa6smRizN7kjibJ3shkLluXl9cweszwlLYaw0x2/zvig3LfQhr5zkuZlu330MA8ddXQCnAYR8A9BJHhk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=sUhk6MY9; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="sUhk6MY9" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BC4F24660; Mon, 18 May 2026 04:58:21 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B34D13F85F; Mon, 18 May 2026 04:58:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105506; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=sUhk6MY9+0kEI8+FjC3Kx0lRo1Mq+969qciZXu5IqVTZhWR+C3hScBfndozwi/OnF zxvbK/iyOrcA4AH4VfgdNOfc/4AYD9zlt5pcETpmBPrAFLFokfNfOLJAP/7iLifnEf z0Do0VRIn2uLRXoD7ybrlitoSqxKjVThQWGF0aaI= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:45 +0100 Subject: [PATCH v4 02/10] ACPI: APEI: GHES: move CPER read helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-2-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=11668; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; b=OxO040hm0b+3wPjfRGp8dkn+d1oPv0nuoX2ovDWTk7ceySITvcFZmlmnr9YObuPTeGaoUoUPK Epd/m55KYZ+A0CWuQAOMXj0SLWBIvG/lWR5CksZ2fPM9uWMCVbXx/YD X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Relocate the CPER buffer mapping, peek, and clear helpers from ghes.c into ghes_cper.c so they can be shared with other firmware-first providers. This commit only shuffles code; behavior stays the same. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/Makefile | 2 +- drivers/acpi/apei/ghes.c | 166 ----------------------------------- drivers/acpi/apei/ghes_cper.c | 195 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 196 insertions(+), 167 deletions(-) diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index 66588d6be56f..f57f3b009d8e 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ACPI_APEI) +=3D apei.o -obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o +obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o ghes_cper.o # clang versions prior to 18 may blow out the stack with KASAN ifeq ($(CONFIG_COMPILE_TEST)_$(CONFIG_CC_IS_CLANG)_$(call clang-min-versio= n, 180000),y_y_) KASAN_SANITIZE_ghes.o :=3D n diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 4f67f46410c4..3f35580e8efd 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -118,26 +118,6 @@ static struct gen_pool *ghes_estatus_pool; static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; =20 -static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) -{ - phys_addr_t paddr; - pgprot_t prot; - - paddr =3D PFN_PHYS(pfn); - prot =3D arch_apei_get_mem_attribute(paddr); - __set_fixmap(fixmap_idx, paddr, prot); - - return (void __iomem *) __fix_to_virt(fixmap_idx); -} - -static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_id= x) -{ - int _idx =3D virt_to_fix((unsigned long)vaddr); - - WARN_ON_ONCE(fixmap_idx !=3D _idx); - clear_fixmap(fixmap_idx); -} - int ghes_estatus_pool_init(unsigned int num_ghes) { unsigned long addr, len; @@ -193,21 +173,6 @@ static void unmap_gen_v2(struct ghes *ghes) apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); } =20 -static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2) -{ - int rc; - u64 val =3D 0; - - rc =3D apei_read(&val, &gv2->read_ack_register); - if (rc) - return; - - val &=3D gv2->read_ack_preserve << gv2->read_ack_register.bit_offset; - val |=3D gv2->read_ack_write << gv2->read_ack_register.bit_offset; - - apei_write(val, &gv2->read_ack_register); -} - struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; @@ -280,137 +245,6 @@ static inline int ghes_severity(int severity) } } =20 -static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, - int from_phys, - enum fixed_addresses fixmap_idx) -{ - void __iomem *vaddr; - u64 offset; - u32 trunk; - - while (len > 0) { - offset =3D paddr - (paddr & PAGE_MASK); - vaddr =3D ghes_map(PHYS_PFN(paddr), fixmap_idx); - trunk =3D PAGE_SIZE - offset; - trunk =3D min(trunk, len); - if (from_phys) - memcpy_fromio(buffer, vaddr + offset, trunk); - else - memcpy_toio(vaddr + offset, buffer, trunk); - len -=3D trunk; - paddr +=3D trunk; - buffer +=3D trunk; - ghes_unmap(vaddr, fixmap_idx); - } -} - -/* Check the top-level record header has an appropriate size. */ -int __ghes_check_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus) -{ - u32 len =3D cper_estatus_len(estatus); - u32 max_len =3D min(ghes->generic->error_block_length, - ghes->estatus_length); - - if (len < sizeof(*estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); - return -EIO; - } - - if (!len || len > max_len) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!= \n"); - return -EIO; - } - - if (cper_estatus_check_header(estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n"); - return -EIO; - } - - return 0; -} - -/* Read the CPER block, returning its address, and header in estatus. */ -int __ghes_peek_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) -{ - struct acpi_hest_generic *g =3D ghes->generic; - int rc; - - rc =3D apei_read(buf_paddr, &g->error_status_address); - if (rc) { - *buf_paddr =3D 0; - pr_warn_ratelimited(FW_WARN GHES_PFX - "Failed to read error status block address for hardware error sour= ce: %d.\n", - g->header.source_id); - return -EIO; - } - if (!*buf_paddr) - return -ENOENT; - - ghes_copy_tofrom_phys(estatus, *buf_paddr, sizeof(*estatus), 1, - fixmap_idx); - if (!estatus->block_status) { - *buf_paddr =3D 0; - return -ENOENT; - } - - return 0; -} - -int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx, - size_t buf_len) -{ - ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); - if (cper_estatus_check(estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX - "Failed to read error status block!\n"); - return -EIO; - } - - return 0; -} - -int ghes_read_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) -{ - int rc; - - rc =3D __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx); - if (rc) - return rc; - - rc =3D __ghes_check_estatus(ghes, estatus); - if (rc) - return rc; - - return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx, - cper_estatus_len(estatus)); -} - -void ghes_clear_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx) -{ - estatus->block_status =3D 0; - - if (!buf_paddr) - return; - - ghes_copy_tofrom_phys(estatus, buf_paddr, - sizeof(estatus->block_status), 0, - fixmap_idx); - - /* - * GHESv2 type HEST entries introduce support for error acknowledgment, - * so only acknowledge the error if this support is present. - */ - if (is_hest_type_generic_v2(ghes)) - ghes_ack_error(ghes->generic_v2); -} =20 /** * struct ghes_task_work - for synchronous RAS event diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c new file mode 100644 index 000000000000..7bb72fe57838 --- /dev/null +++ b/drivers/acpi/apei/ghes_cper.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Shared GHES helpers for firmware-first CPER error handling. + * + * This file holds the GHES helper code that is shared by the in-tree GHES + * driver and by other firmware-first error sources that reuse the same CP= ER + * handling flow. + * + * Derived from the ACPI APEI GHES driver. + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "apei-internal.h" + +static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) +{ + phys_addr_t paddr; + pgprot_t prot; + + paddr =3D PFN_PHYS(pfn); + prot =3D arch_apei_get_mem_attribute(paddr); + __set_fixmap(fixmap_idx, paddr, prot); + + return (void __iomem *) __fix_to_virt(fixmap_idx); +} + +static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_id= x) +{ + int _idx =3D virt_to_fix((unsigned long)vaddr); + + WARN_ON_ONCE(fixmap_idx !=3D _idx); + clear_fixmap(fixmap_idx); +} + +static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2) +{ + int rc; + u64 val =3D 0; + + rc =3D apei_read(&val, &gv2->read_ack_register); + if (rc) + return; + + val &=3D gv2->read_ack_preserve << gv2->read_ack_register.bit_offset; + val |=3D gv2->read_ack_write << gv2->read_ack_register.bit_offset; + + apei_write(val, &gv2->read_ack_register); +} + +static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, + int from_phys, + enum fixed_addresses fixmap_idx) +{ + void __iomem *vaddr; + u64 offset; + u32 trunk; + + while (len > 0) { + offset =3D paddr - (paddr & PAGE_MASK); + vaddr =3D ghes_map(PHYS_PFN(paddr), fixmap_idx); + trunk =3D PAGE_SIZE - offset; + trunk =3D min(trunk, len); + if (from_phys) + memcpy_fromio(buffer, vaddr + offset, trunk); + else + memcpy_toio(vaddr + offset, buffer, trunk); + len -=3D trunk; + paddr +=3D trunk; + buffer +=3D trunk; + ghes_unmap(vaddr, fixmap_idx); + } +} + +/* Check the top-level record header has an appropriate size. */ +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus) +{ + u32 len =3D cper_estatus_len(estatus); + u32 max_len =3D min(ghes->generic->error_block_length, + ghes->estatus_length); + + if (len < sizeof(*estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); + return -EIO; + } + + if (!len || len > max_len) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!= \n"); + return -EIO; + } + + if (cper_estatus_check_header(estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n"); + return -EIO; + } + + return 0; +} + +/* Read the CPER block, returning its address, and header in estatus. */ +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) +{ + struct acpi_hest_generic *g =3D ghes->generic; + int rc; + + rc =3D apei_read(buf_paddr, &g->error_status_address); + if (rc) { + *buf_paddr =3D 0; + pr_warn_ratelimited(FW_WARN GHES_PFX + "Failed to read error status block address for hardware error sour= ce: %d.\n", + g->header.source_id); + return -EIO; + } + if (!*buf_paddr) + return -ENOENT; + + ghes_copy_tofrom_phys(estatus, *buf_paddr, sizeof(*estatus), 1, + fixmap_idx); + if (!estatus->block_status) { + *buf_paddr =3D 0; + return -ENOENT; + } + + return 0; +} + +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len) +{ + ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); + if (cper_estatus_check(estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX + "Failed to read error status block!\n"); + return -EIO; + } + + return 0; +} + +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) +{ + int rc; + + rc =3D __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx); + if (rc) + return rc; + + rc =3D __ghes_check_estatus(ghes, estatus); + if (rc) + return rc; + + return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx, + cper_estatus_len(estatus)); +} + +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx) +{ + estatus->block_status =3D 0; + + if (!buf_paddr) + return; + + ghes_copy_tofrom_phys(estatus, buf_paddr, + sizeof(estatus->block_status), 0, + fixmap_idx); + + /* + * GHESv2 type HEST entries introduce support for error acknowledgment, + * so only acknowledge the error if this support is present. + */ + if (is_hest_type_generic_v2(ghes)) + ghes_ack_error(ghes->generic_v2); +} --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 03C473F8EDE; Mon, 18 May 2026 11:58:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105523; cv=none; b=N/XVRbLey3zq7mItikByW6xc4t364otQHMw1gm/yqh0VSDWrm9WvuzEDWzzjGkuLtPWMzymiyrd4LW7nUALOJJyfePTxn7eHii2+usI4FuKyoJt73iz0WjduaX83kF4om1XX7sSEu2o2FxlNcQyZvieLUDiM0nX1tZrZa8Eqjqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105523; c=relaxed/simple; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LUfuOP5FQCIwprCDKZc9UtNSjtMvhEYs1ijZQYSZGrgLB/AhGVVuP3373CJCHEh9/cQlIPCZYz1918cc/ky4W/bHr8CVcIiZs4VwAsVuXL5Mc0IM0GODRp0Vj0Zwn0DOijdjSjsL5B3aKh8QgEVX4WoAmaVs5ubRI4aLsocgnmY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=iKI3kJ1P; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="iKI3kJ1P" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4A7511CE0; Mon, 18 May 2026 04:58:27 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4FB4A3F85F; Mon, 18 May 2026 04:58:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105512; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=iKI3kJ1P2tpugrijq9UeEOqiaR0keIg6UiKca9y1KPs//sWHVKfSzwvx7mug9QL8e IUKR8qj5VTHtKEe9RXeGOXA6o6JBxYe1v+k0YJvoioUjSFrTvOyzDN6uum7vrky2Ls dmFZhp2KyjLWI7JadYq1I0WL2WkExYlsNAYCgcEY= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:46 +0100 Subject: [PATCH v4 03/10] ACPI: APEI: GHES: move GHESv2 ack and alloc helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-3-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=4727; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; b=RG6MQi029/Wuzz7XeYkDq/Om18yJLIEx7woMY5OKLHb/PTBReZRKAJvoBlDX6NnEw1ydohh4v fpetPBS2w0xDLTunNw30MMk3SHQqStqzCwo85eA3/Y7ZshTASiznpM0 X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Move the GHESv2 acknowledgment and error-source allocation helpers from ghes.c into ghes_cper.c. This is a mechanical refactor that keeps the logic unchanged while making the helpers reusable. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 65 ---------------------------------------= ---- drivers/acpi/apei/ghes_cper.c | 65 +++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3f35580e8efd..91638ae7e05e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -163,71 +163,6 @@ void ghes_estatus_pool_region_free(unsigned long addr,= u32 size) } EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); =20 -static int map_gen_v2(struct ghes *ghes) -{ - return apei_map_generic_address(&ghes->generic_v2->read_ack_register); -} - -static void unmap_gen_v2(struct ghes *ghes) -{ - apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); -} - -struct ghes *ghes_new(struct acpi_hest_generic *generic) -{ - struct ghes *ghes; - unsigned int error_block_length; - int rc; - - ghes =3D kzalloc_obj(*ghes); - if (!ghes) - return ERR_PTR(-ENOMEM); - - ghes->generic =3D generic; - if (is_hest_type_generic_v2(ghes)) { - rc =3D map_gen_v2(ghes); - if (rc) - goto err_free; - } - - rc =3D apei_map_generic_address(&generic->error_status_address); - if (rc) - goto err_unmap_read_ack_addr; - error_block_length =3D generic->error_block_length; - if (error_block_length > GHES_ESTATUS_MAX_SIZE) { - pr_warn(FW_WARN GHES_PFX - "Error status block length is too long: %u for " - "generic hardware error source: %d.\n", - error_block_length, generic->header.source_id); - error_block_length =3D GHES_ESTATUS_MAX_SIZE; - } - ghes->estatus =3D kmalloc(error_block_length, GFP_KERNEL); - ghes->estatus_length =3D error_block_length; - if (!ghes->estatus) { - rc =3D -ENOMEM; - goto err_unmap_status_addr; - } - - return ghes; - -err_unmap_status_addr: - apei_unmap_generic_address(&generic->error_status_address); -err_unmap_read_ack_addr: - if (is_hest_type_generic_v2(ghes)) - unmap_gen_v2(ghes); -err_free: - kfree(ghes); - return ERR_PTR(rc); -} - -void ghes_fini(struct ghes *ghes) -{ - kfree(ghes->estatus); - apei_unmap_generic_address(&ghes->generic->error_status_address); - if (is_hest_type_generic_v2(ghes)) - unmap_gen_v2(ghes); -} - static inline int ghes_severity(int severity) { switch (severity) { diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 7bb72fe57838..8080e0f76dac 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -62,6 +62,71 @@ static void ghes_ack_error(struct acpi_hest_generic_v2 *= gv2) apei_write(val, &gv2->read_ack_register); } =20 +static int map_gen_v2(struct ghes *ghes) +{ + return apei_map_generic_address(&ghes->generic_v2->read_ack_register); +} + +static void unmap_gen_v2(struct ghes *ghes) +{ + apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); +} + +struct ghes *ghes_new(struct acpi_hest_generic *generic) +{ + struct ghes *ghes; + unsigned int error_block_length; + int rc; + + ghes =3D kzalloc_obj(*ghes); + if (!ghes) + return ERR_PTR(-ENOMEM); + + ghes->generic =3D generic; + if (is_hest_type_generic_v2(ghes)) { + rc =3D map_gen_v2(ghes); + if (rc) + goto err_free; + } + + rc =3D apei_map_generic_address(&generic->error_status_address); + if (rc) + goto err_unmap_read_ack_addr; + error_block_length =3D generic->error_block_length; + if (error_block_length > GHES_ESTATUS_MAX_SIZE) { + pr_warn(FW_WARN GHES_PFX + "Error status block length is too long: %u for " + "generic hardware error source: %d.\n", + error_block_length, generic->header.source_id); + error_block_length =3D GHES_ESTATUS_MAX_SIZE; + } + ghes->estatus =3D kmalloc(error_block_length, GFP_KERNEL); + ghes->estatus_length =3D error_block_length; + if (!ghes->estatus) { + rc =3D -ENOMEM; + goto err_unmap_status_addr; + } + + return ghes; + +err_unmap_status_addr: + apei_unmap_generic_address(&generic->error_status_address); +err_unmap_read_ack_addr: + if (is_hest_type_generic_v2(ghes)) + unmap_gen_v2(ghes); +err_free: + kfree(ghes); + return ERR_PTR(rc); +} + +void ghes_fini(struct ghes *ghes) +{ + kfree(ghes->estatus); + apei_unmap_generic_address(&ghes->generic->error_status_address); + if (is_hest_type_generic_v2(ghes)) + unmap_gen_v2(ghes); +} + static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, int from_phys, enum fixed_addresses fixmap_idx) --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CCCA73F9A1C; Mon, 18 May 2026 11:58:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105532; cv=none; b=bIWUdNqefDAzqAxq3sMQaLy057Je+mWqoaCpj0VxGJ5SKCyHe35azknmivsDkt/BQyY0KzD2r0yQ4kwGFJXiIRIgK7rgdRn0yg5Y8eHiX2MX1LgrA0DGOpLyOu0E74e40mOFmlCn6uKE8UL/+27E00gJl8yHXn8ULDv6Aaww7Aw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105532; c=relaxed/simple; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SoTK4IM++PCLH76kZ+mkACDhISXJbsiW75LZaYbmm8osSpZYwL326prMEO6Oo6BkkjRCqUfOQvrQArTma4bLvsUeqx82UBB25DbF+439eiBiEzE5MOq/7ffwbbXzXouQV+2d/cIRk0Mo/vHeRr3uuATt24HLJ1n+6BI11JE35sg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=XK4nRpA8; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="XK4nRpA8" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E1A5B4617; Mon, 18 May 2026 04:58:32 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D3F703F85F; Mon, 18 May 2026 04:58:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105518; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XK4nRpA8S3v+L9YccG5DJAAOi8Zx0AvlNIF4npTvFjqg0+WFnuopmK0sJ52zJoPap yvK8P1xEySgfnrJ3v7z6dyGJX2G8CE80PTn/cm2BiLNdQMY7Z9oefyMZelyf8irkVc afyirj9Zw8QmbN8joH6K/Y2+pA5urG+Pax7y6KnE= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:47 +0100 Subject: [PATCH v4 04/10] ACPI: APEI: GHES: move estatus cache helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-4-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=11290; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; b=p+L5vphRyYdouYSzoH/XVFh+X2bOsDHEwLzZA4wo49gzIKKN33G7w0EO1iMy5zwxcOJYCuiFU TLnJep9kBQFDPVWYiEcgRHQtM1+FIDxICRXqCcVe83HlZAmP4IEXmtR X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Relocate the estatus cache allocation and lookup helpers from ghes.c into ghes_cper.c. This code move keeps the logic intact while making the cache implementation available to forthcoming users. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 138 +-------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 140 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 6 ++ 3 files changed, 147 insertions(+), 137 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 91638ae7e05e..adab7404310e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -113,10 +113,7 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -static struct gen_pool *ghes_estatus_pool; - -static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; -static atomic_t ghes_estatus_cache_alloced; +struct gen_pool *ghes_estatus_pool; =20 int ghes_estatus_pool_init(unsigned int num_ghes) { @@ -733,139 +730,6 @@ static int ghes_print_estatus(const char *pfx, return 0; } =20 -/* - * GHES error status reporting throttle, to report more kinds of - * errors, instead of just most frequently occurred errors. - */ -static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) -{ - u32 len; - int i, cached =3D 0; - unsigned long long now; - struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; - - len =3D cper_estatus_len(estatus); - rcu_read_lock(); - for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { - cache =3D rcu_dereference(ghes_estatus_caches[i]); - if (cache =3D=3D NULL) - continue; - if (len !=3D cache->estatus_len) - continue; - cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); - if (memcmp(estatus, cache_estatus, len)) - continue; - atomic_inc(&cache->count); - now =3D sched_clock(); - if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) - cached =3D 1; - break; - } - rcu_read_unlock(); - return cached; -} - -static struct ghes_estatus_cache *ghes_estatus_cache_alloc( - struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) -{ - int alloced; - u32 len, cache_len; - struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; - - alloced =3D atomic_add_return(1, &ghes_estatus_cache_alloced); - if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { - atomic_dec(&ghes_estatus_cache_alloced); - return NULL; - } - len =3D cper_estatus_len(estatus); - cache_len =3D GHES_ESTATUS_CACHE_LEN(len); - cache =3D (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); - if (!cache) { - atomic_dec(&ghes_estatus_cache_alloced); - return NULL; - } - cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); - memcpy(cache_estatus, estatus, len); - cache->estatus_len =3D len; - atomic_set(&cache->count, 0); - cache->generic =3D generic; - cache->time_in =3D sched_clock(); - return cache; -} - -static void ghes_estatus_cache_rcu_free(struct rcu_head *head) -{ - struct ghes_estatus_cache *cache; - u32 len; - - cache =3D container_of(head, struct ghes_estatus_cache, rcu); - len =3D cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); - len =3D GHES_ESTATUS_CACHE_LEN(len); - gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); - atomic_dec(&ghes_estatus_cache_alloced); -} - -static void -ghes_estatus_cache_add(struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) -{ - unsigned long long now, duration, period, max_period =3D 0; - struct ghes_estatus_cache *cache, *new_cache; - struct ghes_estatus_cache __rcu *victim; - int i, slot =3D -1, count; - - new_cache =3D ghes_estatus_cache_alloc(generic, estatus); - if (!new_cache) - return; - - rcu_read_lock(); - now =3D sched_clock(); - for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { - cache =3D rcu_dereference(ghes_estatus_caches[i]); - if (cache =3D=3D NULL) { - slot =3D i; - break; - } - duration =3D now - cache->time_in; - if (duration >=3D GHES_ESTATUS_IN_CACHE_MAX_NSEC) { - slot =3D i; - break; - } - count =3D atomic_read(&cache->count); - period =3D duration; - do_div(period, (count + 1)); - if (period > max_period) { - max_period =3D period; - slot =3D i; - } - } - rcu_read_unlock(); - - if (slot !=3D -1) { - /* - * Use release semantics to ensure that ghes_estatus_cached() - * running on another CPU will see the updated cache fields if - * it can see the new value of the pointer. - */ - victim =3D xchg_release(&ghes_estatus_caches[slot], - RCU_INITIALIZER(new_cache)); - - /* - * At this point, victim may point to a cached item different - * from the one based on which we selected the slot. Instead of - * going to the loop again to pick another slot, let's just - * drop the other item anyway: this may cause a false cache - * miss later on, but that won't cause any problems. - */ - if (victim) - call_rcu(&unrcu_pointer(victim)->rcu, - ghes_estatus_cache_rcu_free); - } -} - static void __ghes_panic(struct ghes *ghes, struct acpi_hest_generic_status *estatus, u64 buf_paddr, enum fixed_addresses fixmap_idx) diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 8080e0f76dac..0a117f478afb 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -13,10 +13,14 @@ */ =20 #include +#include #include #include +#include #include #include +#include +#include #include =20 #include @@ -27,6 +31,9 @@ =20 #include "apei-internal.h" =20 +static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; +static atomic_t ghes_estatus_cache_alloced; + static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) { phys_addr_t paddr; @@ -258,3 +265,136 @@ void ghes_clear_estatus(struct ghes *ghes, if (is_hest_type_generic_v2(ghes)) ghes_ack_error(ghes->generic_v2); } + +/* + * GHES error status reporting throttle, to report more kinds of + * errors, instead of just most frequently occurred errors. + */ +int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) +{ + u32 len; + int i, cached =3D 0; + unsigned long long now; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + len =3D cper_estatus_len(estatus); + rcu_read_lock(); + for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache =3D rcu_dereference(ghes_estatus_caches[i]); + if (cache =3D=3D NULL) + continue; + if (len !=3D cache->estatus_len) + continue; + cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); + if (memcmp(estatus, cache_estatus, len)) + continue; + atomic_inc(&cache->count); + now =3D sched_clock(); + if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) + cached =3D 1; + break; + } + rcu_read_unlock(); + return cached; +} + +static struct ghes_estatus_cache *ghes_estatus_cache_alloc( + struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + int alloced; + u32 len, cache_len; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + alloced =3D atomic_add_return(1, &ghes_estatus_cache_alloced); + if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + len =3D cper_estatus_len(estatus); + cache_len =3D GHES_ESTATUS_CACHE_LEN(len); + cache =3D (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); + if (cache =3D=3D NULL) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); + memcpy(cache_estatus, estatus, len); + cache->estatus_len =3D len; + atomic_set(&cache->count, 0); + cache->generic =3D generic; + cache->time_in =3D sched_clock(); + return cache; +} + +static void ghes_estatus_cache_rcu_free(struct rcu_head *head) +{ + struct ghes_estatus_cache *cache; + u32 len; + + cache =3D container_of(head, struct ghes_estatus_cache, rcu); + len =3D cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); + len =3D GHES_ESTATUS_CACHE_LEN(len); + gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); + atomic_dec(&ghes_estatus_cache_alloced); +} + +void +ghes_estatus_cache_add(struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + unsigned long long now, duration, period, max_period =3D 0; + struct ghes_estatus_cache *cache, *new_cache; + struct ghes_estatus_cache __rcu *victim; + int i, slot =3D -1, count; + + new_cache =3D ghes_estatus_cache_alloc(generic, estatus); + if (!new_cache) + return; + + rcu_read_lock(); + now =3D sched_clock(); + for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache =3D rcu_dereference(ghes_estatus_caches[i]); + if (cache =3D=3D NULL) { + slot =3D i; + break; + } + duration =3D now - cache->time_in; + if (duration >=3D GHES_ESTATUS_IN_CACHE_MAX_NSEC) { + slot =3D i; + break; + } + count =3D atomic_read(&cache->count); + period =3D duration; + do_div(period, (count + 1)); + if (period > max_period) { + max_period =3D period; + slot =3D i; + } + } + rcu_read_unlock(); + + if (slot !=3D -1) { + /* + * Use release semantics to ensure that ghes_estatus_cached() + * running on another CPU will see the updated cache fields if + * it can see the new value of the pointer. + */ + victim =3D xchg_release(&ghes_estatus_caches[slot], + RCU_INITIALIZER(new_cache)); + + /* + * At this point, victim may point to a cached item different + * from the one based on which we selected the slot. Instead of + * going to the loop again to pick another slot, let's just + * drop the other item anyway: this may cause a false cache + * miss later on, but that won't cause any problems. + */ + if (victim) + call_rcu(&unrcu_pointer(victim)->rcu, + ghes_estatus_cache_rcu_free); + } +} diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 6b7632cfaf66..1b5dbeca9bb6 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -16,6 +16,7 @@ #ifndef ACPI_APEI_GHES_CPER_H #define ACPI_APEI_GHES_CPER_H =20 +#include #include =20 #include @@ -54,6 +55,8 @@ ((struct acpi_hest_generic_data *) \ ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) =20 +extern struct gen_pool *ghes_estatus_pool; + static inline bool is_hest_type_generic_v2(struct ghes *ghes) { return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; @@ -98,5 +101,8 @@ int __ghes_read_estatus(struct acpi_hest_generic_status = *estatus, u64 buf_paddr, enum fixed_addresses fixmap_idx, size_t buf_len); #endif +int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); +void ghes_estatus_cache_add(struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 400653F99EE; Mon, 18 May 2026 11:58:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105536; cv=none; b=cMZr+PGTc9FweoQ8LI9bFdAQkvyLgR7pwnM3FlO6/WragxMINKlyp0tpxy/CVId36XwFh9p2k07kVyyNPbSFPRLDpIvrS0wwLkpl3lp2L1+hrX5xv2jhNu+hzGa0dfsUTV3gF+3Q4sDfb270vg56wqDgrpw69OAI4uDPLXBB+vU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105536; c=relaxed/simple; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jN3wKv3YDOyLTbKslTmY5+wHyvJD7NB8JmSn91vINRmdRxxBzfh6MHlbW4egx/NKVQCevCF6kOltHfUq+D6XpyvZF5vlVgOZsj7ZnwJyeJrqe4LbV1X3A2BeV8clhgL0pQ/iAy7X+Piq30Ah3NCBMsz3davKi7FD3C/6DIC/b8o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=dxvdZxqv; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="dxvdZxqv" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 718251CE0; Mon, 18 May 2026 04:58:38 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 778933F85F; Mon, 18 May 2026 04:58:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105523; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dxvdZxqv67sgqMJvGRNBj+DnAZaQp07oiXxKamk+MpxEem3rzI4JzDnnRkGLkj0HD U4WU8k/aUw6sh//OoxiQHmWu73l32/dWJK841Bql9uXflLHx2u75xtb1yPN7e9gV2y EJsXxH016NGNKIlZzrn67xijKl2UsfODEX7KLiBs= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:48 +0100 Subject: [PATCH v4 05/10] ACPI: APEI: GHES: move vendor record helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-5-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=6986; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; b=OmFlDa8sOT+gLHBfPojnN+1QMtx8q6ZkdMzOVaRReLWVZl1NP4zdDSUHD23s1lz+t9WmwyZbV dbgh6P7hRJtBJalpTi/Sx8IDYXGEMv7CYQkgAiAiovvOycQ9OlssLGD X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Shift the vendor record workqueue helpers into ghes_cper.c so both GHES and future DT-based providers can use the same implementation. The change is mechanical and keeps the notifier behavior identical. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 86 +++++++++------------------------------= ---- drivers/acpi/apei/ghes_cper.c | 55 +++++++++++++++++++++++++++ include/acpi/ghes_cper.h | 2 + 3 files changed, 75 insertions(+), 68 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index adab7404310e..81ac51632f21 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -383,74 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_d= ata *gdata) #endif } =20 -static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); - -int ghes_register_vendor_record_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&vendor_record_notify_list, nb); -} -EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier); - -void ghes_unregister_vendor_record_notifier(struct notifier_block *nb) -{ - blocking_notifier_chain_unregister(&vendor_record_notify_list, nb); -} -EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier); - -static void ghes_vendor_record_notifier_destroy(void *nb) -{ - ghes_unregister_vendor_record_notifier(nb); -} - -int devm_ghes_register_vendor_record_notifier(struct device *dev, - struct notifier_block *nb) -{ - int ret; - - ret =3D ghes_register_vendor_record_notifier(nb); - if (ret) - return ret; - - return devm_add_action_or_reset(dev, ghes_vendor_record_notifier_destroy,= nb); -} -EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); - -static void ghes_vendor_record_work_func(struct work_struct *work) -{ - struct ghes_vendor_record_entry *entry; - struct acpi_hest_generic_data *gdata; - u32 len; - - entry =3D container_of(work, struct ghes_vendor_record_entry, work); - gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); - - blocking_notifier_call_chain(&vendor_record_notify_list, - entry->error_severity, gdata); - - len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); - gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len); -} - -static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *g= data, - int sev) -{ - struct acpi_hest_generic_data *copied_gdata; - struct ghes_vendor_record_entry *entry; - u32 len; - - len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); - entry =3D (void *)gen_pool_alloc(ghes_estatus_pool, len); - if (!entry) - return; - - copied_gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); - memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata)); - entry->error_severity =3D sev; - - INIT_WORK(&entry->work, ghes_vendor_record_work_func); - schedule_work(&entry->work); -} - /* Room for 8 entries */ #define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, @@ -514,6 +446,24 @@ int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_e= rr_work_data *wd) } EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); =20 +static void ghes_vendor_record_notifier_destroy(void *nb) +{ + ghes_unregister_vendor_record_notifier(nb); +} + +int devm_ghes_register_vendor_record_notifier(struct device *dev, + struct notifier_block *nb) +{ + int ret; + + ret =3D ghes_register_vendor_record_notifier(nb); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ghes_vendor_record_notifier_destroy,= nb); +} +EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); + /* Room for 8 entries for each of the 4 event log queues */ #define CXL_CPER_FIFO_DEPTH 32 DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH= ); diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 0a117f478afb..131980d36064 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -14,12 +14,17 @@ =20 #include #include +#include #include #include +#include #include #include +#include +#include #include #include +#include #include #include =20 @@ -266,6 +271,56 @@ void ghes_clear_estatus(struct ghes *ghes, ghes_ack_error(ghes->generic_v2); } =20 +static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); + +int ghes_register_vendor_record_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier); + +void ghes_unregister_vendor_record_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier); + +static void ghes_vendor_record_work_func(struct work_struct *work) +{ + struct ghes_vendor_record_entry *entry; + struct acpi_hest_generic_data *gdata; + u32 len; + + entry =3D container_of(work, struct ghes_vendor_record_entry, work); + gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); + + blocking_notifier_call_chain(&vendor_record_notify_list, + entry->error_severity, gdata); + + len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); + gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len); +} + +void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, + int sev) +{ + struct acpi_hest_generic_data *copied_gdata; + struct ghes_vendor_record_entry *entry; + u32 len; + + len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); + entry =3D (void *)gen_pool_alloc(ghes_estatus_pool, len); + if (!entry) + return; + + copied_gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); + memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata)); + entry->error_severity =3D sev; + + INIT_WORK(&entry->work, ghes_vendor_record_work_func); + schedule_work(&entry->work); +} + /* * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 1b5dbeca9bb6..51725f25c516 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -104,5 +104,7 @@ int __ghes_read_estatus(struct acpi_hest_generic_status= *estatus, int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); void ghes_estatus_cache_add(struct acpi_hest_generic *generic, struct acpi_hest_generic_status *estatus); +void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, + int sev); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 94FCC3F99E0; Mon, 18 May 2026 11:58:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105541; cv=none; b=YnQtUx6Q0ATKSbHHH/TfXH++fVbR0iX+MWhKqiaujovzNBbgh792nZLJef1FBSWofgCheT3N1Aqhp1Zi1oz3gm9o/GyZ3uneh7Cha3Odhmd9A4k4rKKmAO7admy8gOrRxjdTsja9EZ/ugSmWo7wS5IFb6pFbWzhp7qRSUiAUPS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105541; c=relaxed/simple; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=i5sLg6CCZD1ll0WNmTfULDgGEXRis3t1cqSeTMvNciAVoDwShXMU1qVVISvWRkiDBVt/3uJR6A5fTI6lju3bjT37dX/wcMumtELb5/TdPvkB7LXvpae0kCbxFCWPL2IXc1mwrRWgqxG+58Jpzocs13PJiEdSJBOF7Duf+IAkHC0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=Ho6+ioAM; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="Ho6+ioAM" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 02A204660; Mon, 18 May 2026 04:58:44 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 01F1A3F85F; Mon, 18 May 2026 04:58:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105529; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Ho6+ioAMQXMaH16/hDUnt33VbYA0fYe1Hu3Lh72trd1no6alYDoUoq3SdYCL/Lu6v n0YE4FQ+bMeA1z4uiycZggUxl9MIUTKvG0CphwMQjd3BbQKoTcfQx7mFdi1Fn4FVEs m6FYpNDiLPe4O66p+Dq4BpkqugmcFp1ckN2/N+p8= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:49 +0100 Subject: [PATCH v4 06/10] ACPI: APEI: GHES: move CXL CPER helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-6-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=10220; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; b=e93y9dNLb0+3aW8sPzJELkIQ7nitmVnuEMMNblZAkFLNDDOyeZPVK6s2fgAO7bCQgaZlr2ncq B8IVtNYhil5AQLatVBiQnn0e4Voup6XvvatYQxZbLbu73F6xFk7UPX6 X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Move the CXL CPER handling paths out of ghes.c and into ghes_cper.c so the helpers can be reused. The code is moved as-is, with the public prototypes updated so GHES keeps calling into the new translation unit. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 132 --------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 134 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 11 ++++ 3 files changed, 145 insertions(+), 132 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 81ac51632f21..85be2ebf4d3e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -383,69 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_d= ata *gdata) #endif } =20 -/* Room for 8 entries */ -#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 -static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, - CXL_CPER_PROT_ERR_FIFO_DEPTH); - -/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */ -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock); -struct work_struct *cxl_cper_prot_err_work; - -static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, - int severity) -{ -#ifdef CONFIG_ACPI_APEI_PCIEAER - struct cxl_cper_prot_err_work_data wd; - - if (cxl_cper_sec_prot_err_valid(prot_err)) - return; - - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); - - if (!cxl_cper_prot_err_work) - return; - - if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) - return; - - if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) { - pr_err_ratelimited("CXL CPER kfifo overflow\n"); - return; - } - - schedule_work(cxl_cper_prot_err_work); -#endif -} - -int cxl_cper_register_prot_err_work(struct work_struct *work) -{ - if (cxl_cper_prot_err_work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_prot_err_work_lock); - cxl_cper_prot_err_work =3D work; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL"); - -int cxl_cper_unregister_prot_err_work(struct work_struct *work) -{ - if (cxl_cper_prot_err_work !=3D work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_prot_err_work_lock); - cxl_cper_prot_err_work =3D NULL; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL"); - -int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd) -{ - return kfifo_get(&cxl_cper_prot_err_fifo, wd); -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); - static void ghes_vendor_record_notifier_destroy(void *nb) { ghes_unregister_vendor_record_notifier(nb); @@ -464,75 +401,6 @@ int devm_ghes_register_vendor_record_notifier(struct d= evice *dev, } EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); =20 -/* Room for 8 entries for each of the 4 event log queues */ -#define CXL_CPER_FIFO_DEPTH 32 -DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH= ); - -/* Synchronize schedule_work() with cxl_cper_work changes */ -static DEFINE_SPINLOCK(cxl_cper_work_lock); -struct work_struct *cxl_cper_work; - -static void cxl_cper_post_event(enum cxl_event_type event_type, - struct cxl_cper_event_rec *rec) -{ - struct cxl_cper_work_data wd; - - if (rec->hdr.length <=3D sizeof(rec->hdr) || - rec->hdr.length > sizeof(*rec)) { - pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", - rec->hdr.length); - return; - } - - if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { - pr_err(FW_WARN "CXL CPER invalid event\n"); - return; - } - - guard(spinlock_irqsave)(&cxl_cper_work_lock); - - if (!cxl_cper_work) - return; - - wd.event_type =3D event_type; - memcpy(&wd.rec, rec, sizeof(wd.rec)); - - if (!kfifo_put(&cxl_cper_fifo, wd)) { - pr_err_ratelimited("CXL CPER kfifo overflow\n"); - return; - } - - schedule_work(cxl_cper_work); -} - -int cxl_cper_register_work(struct work_struct *work) -{ - if (cxl_cper_work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_work_lock); - cxl_cper_work =3D work; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL"); - -int cxl_cper_unregister_work(struct work_struct *work) -{ - if (cxl_cper_work !=3D work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_work_lock); - cxl_cper_work =3D NULL; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL"); - -int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) -{ - return kfifo_get(&cxl_cper_fifo, wd); -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); - static void ghes_log_hwerr(int sev, guid_t *sec_type) { if (sev !=3D CPER_SEV_RECOVERABLE) diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 131980d36064..d7a666a163c3 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -12,10 +12,12 @@ * Author: Huang Ying */ =20 +#include #include #include #include #include +#include #include #include #include @@ -321,6 +323,138 @@ void ghes_defer_non_standard_event(struct acpi_hest_g= eneric_data *gdata, schedule_work(&entry->work); } =20 +/* Room for 8 entries */ +#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 +static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, + CXL_CPER_PROT_ERR_FIFO_DEPTH); + +/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */ +static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock); +struct work_struct *cxl_cper_prot_err_work; + +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, + int severity) +{ +#ifdef CONFIG_ACPI_APEI_PCIEAER + struct cxl_cper_prot_err_work_data wd; + + if (cxl_cper_sec_prot_err_valid(prot_err)) + return; + + guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); + + if (!cxl_cper_prot_err_work) + return; + + if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) + return; + + if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) { + pr_err_ratelimited("CXL CPER kfifo overflow\n"); + return; + } + + schedule_work(cxl_cper_prot_err_work); +#endif +} + +int cxl_cper_register_prot_err_work(struct work_struct *work) +{ + if (cxl_cper_prot_err_work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_prot_err_work_lock); + cxl_cper_prot_err_work =3D work; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL"); + +int cxl_cper_unregister_prot_err_work(struct work_struct *work) +{ + if (cxl_cper_prot_err_work !=3D work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_prot_err_work_lock); + cxl_cper_prot_err_work =3D NULL; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL"); + +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd) +{ + return kfifo_get(&cxl_cper_prot_err_fifo, wd); +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); + +/* Room for 8 entries for each of the 4 event log queues */ +#define CXL_CPER_FIFO_DEPTH 32 +static DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIF= O_DEPTH); + +/* Synchronize schedule_work() with cxl_cper_work changes */ +static DEFINE_SPINLOCK(cxl_cper_work_lock); +struct work_struct *cxl_cper_work; + +void cxl_cper_post_event(enum cxl_event_type event_type, + struct cxl_cper_event_rec *rec) +{ + struct cxl_cper_work_data wd; + + if (rec->hdr.length <=3D sizeof(rec->hdr) || + rec->hdr.length > sizeof(*rec)) { + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", + rec->hdr.length); + return; + } + + if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { + pr_err(FW_WARN "CXL CPER invalid event\n"); + return; + } + + guard(spinlock_irqsave)(&cxl_cper_work_lock); + + if (!cxl_cper_work) + return; + + wd.event_type =3D event_type; + memcpy(&wd.rec, rec, sizeof(wd.rec)); + + if (!kfifo_put(&cxl_cper_fifo, wd)) { + pr_err_ratelimited("CXL CPER kfifo overflow\n"); + return; + } + + schedule_work(cxl_cper_work); +} + +int cxl_cper_register_work(struct work_struct *work) +{ + if (cxl_cper_work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_work_lock); + cxl_cper_work =3D work; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL"); + +int cxl_cper_unregister_work(struct work_struct *work) +{ + if (cxl_cper_work !=3D work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_work_lock); + cxl_cper_work =3D NULL; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL"); + +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) +{ + return kfifo_get(&cxl_cper_fifo, wd); +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); + /* * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 51725f25c516..dd49e9179b63 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -20,6 +20,7 @@ #include =20 #include +#include =20 #define GHES_PFX "GHES: " =20 @@ -106,5 +107,15 @@ void ghes_estatus_cache_add(struct acpi_hest_generic *= generic, struct acpi_hest_generic_status *estatus); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, + int severity); +int cxl_cper_register_prot_err_work(struct work_struct *work); +int cxl_cper_unregister_prot_err_work(struct work_struct *work); +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd); +void cxl_cper_post_event(enum cxl_event_type event_type, + struct cxl_cper_event_rec *rec); +int cxl_cper_register_work(struct work_struct *work); +int cxl_cper_unregister_work(struct work_struct *work); +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 423EE3FB051; Mon, 18 May 2026 11:58:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105545; cv=none; b=Lo9pJLZEtX1fLYT7wDIACm7CeLuute5Zyh5ZQOa7VcdsYZejDmWXxDRaqPUYtP5XkkGllsxYAWJYMr7TPEpvPcSUligxndPVr/WO6yPrHy3KkWXlYncGGls/wYbYRRBcviqO0Z3sDJrOfIyWpGu8clEDezfH0towJuxsgAwjLbc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105545; c=relaxed/simple; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EzV6ePmA31Am8ZieR9y6v3zqvRrLeyFFMOLOpLlAAKobN9HQvMq5MP/mQZM42SJ0zGGRFrtbh5OfDqu60OuwPORxPOawSx4N3hqmXeB6IxKH4vi6QH0sgAnIC7aINqAp2rY7D2nMwMKrC2q7330J73+4OfZj1a5fQ8kcE3p/ohg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=slmFk/D0; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="slmFk/D0" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 885734617; Mon, 18 May 2026 04:58:49 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 862FD3F85F; Mon, 18 May 2026 04:58:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105534; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=slmFk/D04MLP/owaJd/W2OWEY9rtoGtQXWrCWiDHr1qJf8EcnDJQq2H/itVYuN7Ed x8hH17IQLOOLR4CoBWI5BS+KLDxwOiZi10YRsxjuRzlPzXyqx3Z6m55nYaN51LjoOl LKJpkDPRo0/2MNFVzS1qkSTYM6X0pOqHIPAlA7CM= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:50 +0100 Subject: [PATCH v4 07/10] ACPI: APEI: introduce GHES helper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-7-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=3795; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; b=9qAJ+IYU7SDR5KvkJbTa4iw9kjVtH/2bAoSeyBXnUY3Udfq6EBAx0CDVFOjyhHSsUUwzGToYT DBOmT367y5jDpHFqn4gA+vZ2yMfVg9xPbYXADwv4fngfhb9HgT6dYCl X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Add a dedicated GHES_CPER_HELPERS Kconfig entry so the shared helper code can be built even when ACPI_APEI_GHES is disabled. Update the build glue and headers to depend on the new symbol. Signed-off-by: Ahmed Tiba --- drivers/Makefile | 1 + drivers/acpi/Kconfig | 4 ++++ drivers/acpi/apei/Kconfig | 1 + drivers/acpi/apei/Makefile | 2 +- include/acpi/ghes.h | 10 ++++++---- include/cxl/event.h | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index 0841ea851847..27a664cb45ea 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,6 +31,7 @@ obj-y +=3D idle/ obj-y +=3D char/ipmi/ =20 obj-$(CONFIG_ACPI) +=3D acpi/ +obj-$(CONFIG_GHES_CPER_HELPERS) +=3D acpi/apei/ghes_cper.o =20 # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f165d14cf61a..13ef0e99f840 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -6,6 +6,10 @@ config ARCH_SUPPORTS_ACPI bool =20 +config GHES_CPER_HELPERS + bool + select UEFI_CPER + menuconfig ACPI bool "ACPI (Advanced Configuration and Power Interface) Support" depends on ARCH_SUPPORTS_ACPI diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index 428458c623f0..ddb62638eb02 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -21,6 +21,7 @@ config ACPI_APEI_GHES bool "APEI Generic Hardware Error Source" depends on ACPI_APEI select ACPI_HED + select GHES_CPER_HELPERS select IRQ_WORK select GENERIC_ALLOCATOR select ARM_SDE_INTERFACE if ARM64 diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index f57f3b009d8e..66588d6be56f 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ACPI_APEI) +=3D apei.o -obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o ghes_cper.o +obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o # clang versions prior to 18 may blow out the stack with KASAN ifeq ($(CONFIG_COMPILE_TEST)_$(CONFIG_CC_IS_CLANG)_$(call clang-min-versio= n, 180000),y_y_) KASAN_SANITIZE_ghes.o :=3D n diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 8d7e5caef3f1..2ffab36b6154 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -83,15 +83,17 @@ int devm_ghes_register_vendor_record_notifier(struct de= vice *dev, struct notifier_block *nb); =20 struct list_head *ghes_get_devices(void); - -void ghes_estatus_pool_region_free(unsigned long addr, u32 size); #else static inline struct list_head *ghes_get_devices(void) { return NULL; } - -static inline void ghes_estatus_pool_region_free(unsigned long addr, u32 s= ize) { return; } #endif =20 +#ifdef CONFIG_GHES_CPER_HELPERS int ghes_estatus_pool_init(unsigned int num_ghes); +void ghes_estatus_pool_region_free(unsigned long addr, u32 size); +#else +static inline int ghes_estatus_pool_init(unsigned int num_ghes) { return -= ENODEV; } +static inline void ghes_estatus_pool_region_free(unsigned long addr, u32 s= ize) { } +#endif =20 static inline int acpi_hest_get_version(struct acpi_hest_generic_data *gda= ta) { diff --git a/include/cxl/event.h b/include/cxl/event.h index ff97fea718d2..2ebd65b0d9d6 100644 --- a/include/cxl/event.h +++ b/include/cxl/event.h @@ -285,7 +285,7 @@ struct cxl_cper_prot_err_work_data { int severity; }; =20 -#ifdef CONFIG_ACPI_APEI_GHES +#ifdef CONFIG_GHES_CPER_HELPERS int cxl_cper_register_work(struct work_struct *work); int cxl_cper_unregister_work(struct work_struct *work); int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd); --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8A74D3F9F42; Mon, 18 May 2026 11:59:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105553; cv=none; b=pWUn9vBfQCz8BRUrzOLLeVz3BZwS16H4D8D9da/15zLJZD79S/HwrVzOj1vfseX5M0itBeSPoLVcuXVy2E8ERhak6uEhOqiQdtXKzEieiBhjkaS30X2pThSnU4SQXEvZhYz1E99z3OHITS0uSePDHdqbcI1f3TfyY+IWYJMW1lc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105553; c=relaxed/simple; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JYL/4EQVUgNjSnBRz8y7wapi/1QlxvssxZ2KCAHVcG7lk1MMURkBdZMxU7IRz9GC2TxR6fJ5kMIP6FgD/SecvdRwInArR4nAoLLfs/pXyK+m6I2md7CUu89DqMEzhkfW257Z5ZvgG7FaAtdLadPhcIeJ8PjMa3jj2BhXdXQB8r8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=IxvdPbCB; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="IxvdPbCB" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4289C1CE0; Mon, 18 May 2026 04:58:55 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 1DA023F85F; Mon, 18 May 2026 04:58:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105540; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=IxvdPbCBuDzU+hs9Ya0dj1SLxucBNxU8CWj3mJYtHfgDxEg2pql0U2IkWid+fDfqf oMHGIuOb8zH5b/e97Bj0ps9MZyPG0Q61ISQusG93C2AGlFFrBma//+UEDF8WcI65yM IVcayWT7XRnpo6Y7D3jy+tmzPhIAW4lRSpUfpr8Y= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:51 +0100 Subject: [PATCH v4 08/10] ACPI: APEI: share GHES CPER helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-8-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=31155; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; b=UusrT7kByp9iCwIU0IhO+76QJzOsBB1SNHX2yUIN/rwJBVTlri61GmQkMIAUeFptlrw9qPMzm Yvp0itBfp/FD8hwK3lfgTydcH/Xc85i6VGzFp1bOHtMc93hwdVX7bKO X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Wire GHES up to the helper routines in ghes_cper.c and remove the local copies from ghes.c. This keeps the control flow identical while letting the helpers be shared with other firmware-first providers. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 416 +-------------------------------------- drivers/acpi/apei/ghes_cper.c | 438 ++++++++++++++++++++++++++++++++++++++= +++- include/acpi/ghes_cper.h | 20 ++ 3 files changed, 459 insertions(+), 415 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 85be2ebf4d3e..f85b97c4db4c 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -67,8 +67,6 @@ #define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses #endif =20 -static ATOMIC_NOTIFIER_HEAD(ghes_report_chain); - /* * This driver isn't really modular, however for the time being, * continuing to use module_param is the easiest way to remain @@ -113,276 +111,6 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -struct gen_pool *ghes_estatus_pool; - -int ghes_estatus_pool_init(unsigned int num_ghes) -{ - unsigned long addr, len; - int rc; - - ghes_estatus_pool =3D gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, = -1); - if (!ghes_estatus_pool) - return -ENOMEM; - - len =3D GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX; - len +=3D (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE); - - addr =3D (unsigned long)vmalloc(PAGE_ALIGN(len)); - if (!addr) - goto err_pool_alloc; - - rc =3D gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1); - if (rc) - goto err_pool_add; - - return 0; - -err_pool_add: - vfree((void *)addr); - -err_pool_alloc: - gen_pool_destroy(ghes_estatus_pool); - - return -ENOMEM; -} - -/** - * ghes_estatus_pool_region_free - free previously allocated memory - * from the ghes_estatus_pool. - * @addr: address of memory to free. - * @size: size of memory to free. - * - * Returns none. - */ -void ghes_estatus_pool_region_free(unsigned long addr, u32 size) -{ - gen_pool_free(ghes_estatus_pool, addr, size); -} -EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); - -static inline int ghes_severity(int severity) -{ - switch (severity) { - case CPER_SEV_INFORMATIONAL: - return GHES_SEV_NO; - case CPER_SEV_CORRECTED: - return GHES_SEV_CORRECTED; - case CPER_SEV_RECOVERABLE: - return GHES_SEV_RECOVERABLE; - case CPER_SEV_FATAL: - return GHES_SEV_PANIC; - default: - /* Unknown, go panic */ - return GHES_SEV_PANIC; - } -} - - -/** - * struct ghes_task_work - for synchronous RAS event - * - * @twork: callback_head for task work - * @pfn: page frame number of corrupted page - * @flags: work control flags - * - * Structure to pass task work to be handled before - * returning to user-space via task_work_add(). - */ -struct ghes_task_work { - struct callback_head twork; - u64 pfn; - int flags; -}; - -static void memory_failure_cb(struct callback_head *twork) -{ - struct ghes_task_work *twcb =3D container_of(twork, struct ghes_task_work= , twork); - int ret; - - ret =3D memory_failure(twcb->pfn, twcb->flags); - gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); - - if (!ret || ret =3D=3D -EHWPOISON || ret =3D=3D -EOPNOTSUPP) - return; - - pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\= n", - twcb->pfn, current->comm, task_pid_nr(current)); - force_sig(SIGBUS); -} - -static bool ghes_do_memory_failure(u64 physical_addr, int flags) -{ - struct ghes_task_work *twcb; - unsigned long pfn; - - if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) - return false; - - pfn =3D PHYS_PFN(physical_addr); - - if (flags =3D=3D MF_ACTION_REQUIRED && current->mm) { - twcb =3D (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); - if (!twcb) - return false; - - twcb->pfn =3D pfn; - twcb->flags =3D flags; - init_task_work(&twcb->twork, memory_failure_cb); - task_work_add(current, &twcb->twork, TWA_RESUME); - return true; - } - - memory_failure_queue(pfn, flags); - return true; -} - -static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdat= a, - int sev, bool sync) -{ - int flags =3D -1; - int sec_sev =3D ghes_severity(gdata->error_severity); - struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); - - if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) - return false; - - /* iff following two events can be handled properly by now */ - if (sec_sev =3D=3D GHES_SEV_CORRECTED && - (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) - flags =3D MF_SOFT_OFFLINE; - if (sev =3D=3D GHES_SEV_RECOVERABLE && sec_sev =3D=3D GHES_SEV_RECOVERABL= E) - flags =3D sync ? MF_ACTION_REQUIRED : 0; - - if (flags !=3D -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) -{ - struct cper_sec_proc_arm *err =3D acpi_hest_get_payload(gdata); - int flags =3D sync ? MF_ACTION_REQUIRED : 0; - int length =3D gdata->error_data_length; - char error_type[120]; - bool queued =3D false; - int sec_sev, i; - char *p; - - sec_sev =3D ghes_severity(gdata->error_severity); - if (length >=3D sizeof(*err)) { - log_arm_hw_error(err, sec_sev); - } else { - pr_warn(FW_BUG "arm error length: %d\n", length); - pr_warn(FW_BUG "length is too small\n"); - pr_warn(FW_BUG "firmware-generated error record is incorrect\n"); - return false; - } - - if (sev !=3D GHES_SEV_RECOVERABLE || sec_sev !=3D GHES_SEV_RECOVERABLE) - return false; - - p =3D (char *)(err + 1); - length -=3D sizeof(err); - - for (i =3D 0; i < err->err_info_num; i++) { - struct cper_arm_err_info *err_info; - bool is_cache, has_pa; - - /* Ensure we have enough data for the error info header */ - if (length < sizeof(*err_info)) - break; - - err_info =3D (struct cper_arm_err_info *)p; - - /* Validate the claimed length before using it */ - length -=3D err_info->length; - if (length < 0) - break; - - is_cache =3D err_info->type & CPER_ARM_CACHE_ERROR; - has_pa =3D (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADD= R); - - /* - * The field (err_info->error_info & BIT(26)) is fixed to set to - * 1 in some old firmware of HiSilicon Kunpeng920. We assume that - * firmware won't mix corrected errors in an uncorrected section, - * and don't filter out 'corrected' error here. - */ - if (is_cache && has_pa) { - queued =3D ghes_do_memory_failure(err_info->physical_fault_addr, flags); - p +=3D err_info->length; - continue; - } - - cper_bits_to_str(error_type, sizeof(error_type), - FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type), - cper_proc_error_type_strs, - ARRAY_SIZE(cper_proc_error_type_strs)); - - pr_warn_ratelimited(FW_WARN GHES_PFX - "Unhandled processor error type 0x%02x: %s%s\n", - err_info->type, error_type, - (err_info->type & ~CPER_ARM_ERR_TYPE_MASK) ? " with reserved bit(s= )" : ""); - p +=3D err_info->length; - } - - return queued; -} - -/* - * PCIe AER errors need to be sent to the AER driver for reporting and - * recovery. The GHES severities map to the following AER severities and - * require the following handling: - * - * GHES_SEV_CORRECTABLE -> AER_CORRECTABLE - * These need to be reported by the AER driver but no recovery is - * necessary. - * GHES_SEV_RECOVERABLE -> AER_NONFATAL - * GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL - * These both need to be reported and recovered from by the AER driver. - * GHES_SEV_PANIC does not make it to this handling since the kernel must - * panic. - */ -static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) -{ -#ifdef CONFIG_ACPI_APEI_PCIEAER - struct cper_sec_pcie *pcie_err =3D acpi_hest_get_payload(gdata); - - if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && - pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { - unsigned int devfn; - int aer_severity; - u8 *aer_info; - - devfn =3D PCI_DEVFN(pcie_err->device_id.device, - pcie_err->device_id.function); - aer_severity =3D cper_severity_to_aer(gdata->error_severity); - - /* - * If firmware reset the component to contain - * the error, we must reinitialize it before - * use, so treat it as a fatal AER error. - */ - if (gdata->flags & CPER_SEC_RESET) - aer_severity =3D AER_FATAL; - - aer_info =3D (void *)gen_pool_alloc(ghes_estatus_pool, - sizeof(struct aer_capability_regs)); - if (!aer_info) - return; - memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs)); - - aer_recover_queue(pcie_err->device_id.segment, - pcie_err->device_id.bus, - devfn, aer_severity, - (struct aer_capability_regs *) - aer_info); - } -#endif -} - static void ghes_vendor_record_notifier_destroy(void *nb) { ghes_unregister_vendor_record_notifier(nb); @@ -401,151 +129,11 @@ int devm_ghes_register_vendor_record_notifier(struct= device *dev, } EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); =20 -static void ghes_log_hwerr(int sev, guid_t *sec_type) -{ - if (sev !=3D CPER_SEV_RECOVERABLE) - return; - - if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) || - guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) || - guid_equal(sec_type, &CPER_SEC_PROC_IA)) { - hwerr_log_error_type(HWERR_RECOV_CPU); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) || - guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) || - guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) || - guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { - hwerr_log_error_type(HWERR_RECOV_CXL); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_PCIE) || - guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) { - hwerr_log_error_type(HWERR_RECOV_PCI); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { - hwerr_log_error_type(HWERR_RECOV_MEMORY); - return; - } - - hwerr_log_error_type(HWERR_RECOV_OTHERS); -} - static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { - int sev, sec_sev; - struct acpi_hest_generic_data *gdata; - guid_t *sec_type; - const guid_t *fru_id =3D &guid_null; - char *fru_text =3D ""; - bool queued =3D false; - bool sync =3D is_hest_sync_notify(ghes); - - sev =3D ghes_severity(estatus->error_severity); - apei_estatus_for_each_section(estatus, gdata) { - sec_type =3D (guid_t *)gdata->section_type; - sec_sev =3D ghes_severity(gdata->error_severity); - if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) - fru_id =3D (guid_t *)gdata->fru_id; - - if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) - fru_text =3D gdata->fru_text; - - ghes_log_hwerr(sev, sec_type); - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { - struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); - - atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err); - - arch_apei_report_mem_error(sev, mem_err); - queued =3D ghes_handle_memory_failure(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)) { - queued =3D ghes_handle_arm_hw_error(gdata, sev, sync); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { - struct cxl_cper_sec_prot_err *prot_err =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_prot_err(prot_err, gdata->error_severity); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); - } else { - void *err =3D acpi_hest_get_payload(gdata); - - ghes_defer_non_standard_event(gdata, sev); - log_non_standard_event(sec_type, fru_id, fru_text, - sec_sev, err, - gdata->error_data_length); - } - } - - /* - * If no memory failure work is queued for abnormal synchronous - * errors, do a force kill. - */ - if (sync && !queued) { - dev_err(ghes->dev, - HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", - current->comm, task_pid_nr(current)); - force_sig(SIGBUS); - } -} - -static void __ghes_print_estatus(const char *pfx, - const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) -{ - static atomic_t seqno; - unsigned int curr_seqno; - char pfx_seq[64]; - - if (pfx =3D=3D NULL) { - if (ghes_severity(estatus->error_severity) <=3D - GHES_SEV_CORRECTED) - pfx =3D KERN_WARNING; - else - pfx =3D KERN_ERR; - } - curr_seqno =3D atomic_inc_return(&seqno); - snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); - printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", - pfx_seq, generic->header.source_id); - cper_estatus_print(pfx_seq, estatus); -} - -static int ghes_print_estatus(const char *pfx, - const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) -{ - /* Not more than 2 messages every 5 seconds */ - static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); - static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); - struct ratelimit_state *ratelimit; - - if (ghes_severity(estatus->error_severity) <=3D GHES_SEV_CORRECTED) - ratelimit =3D &ratelimit_corrected; - else - ratelimit =3D &ratelimit_uncorrected; - if (__ratelimit(ratelimit)) { - __ghes_print_estatus(pfx, generic, estatus); - return 1; - } - return 0; + ghes_cper_handle_status(ghes->dev, ghes->generic, + estatus, is_hest_sync_notify(ghes)); } =20 static void __ghes_panic(struct ghes *ghes, diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index d7a666a163c3..0ff9d06eb78f 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -13,22 +13,32 @@ */ =20 #include +#include +#include #include #include -#include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include =20 #include #include @@ -38,9 +48,363 @@ =20 #include "apei-internal.h" =20 +ATOMIC_NOTIFIER_HEAD(ghes_report_chain); + +#ifndef CONFIG_ACPI_APEI +void __weak arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *m= em_err) { } +#endif + static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; =20 +struct gen_pool *ghes_estatus_pool; + +int ghes_estatus_pool_init(unsigned int num_ghes) +{ + unsigned long addr, len; + int rc; + + ghes_estatus_pool =3D gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, = -1); + if (!ghes_estatus_pool) + return -ENOMEM; + + len =3D GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX; + len +=3D (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE); + + addr =3D (unsigned long)vmalloc(PAGE_ALIGN(len)); + if (!addr) + goto err_pool_alloc; + + rc =3D gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1); + if (rc) + goto err_pool_add; + + return 0; + +err_pool_add: + vfree((void *)addr); + +err_pool_alloc: + gen_pool_destroy(ghes_estatus_pool); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(ghes_estatus_pool_init); + +/** + * ghes_estatus_pool_region_free - free previously allocated memory + * from the ghes_estatus_pool. + * @addr: address of memory to free. + * @size: size of memory to free. + * + * Returns none. + */ +void ghes_estatus_pool_region_free(unsigned long addr, u32 size) +{ + gen_pool_free(ghes_estatus_pool, addr, size); +} +EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); + +int ghes_severity(int severity) +{ + switch (severity) { + case CPER_SEV_INFORMATIONAL: + return GHES_SEV_NO; + case CPER_SEV_CORRECTED: + return GHES_SEV_CORRECTED; + case CPER_SEV_RECOVERABLE: + return GHES_SEV_RECOVERABLE; + case CPER_SEV_FATAL: + return GHES_SEV_PANIC; + default: + /* Unknown, go panic */ + return GHES_SEV_PANIC; + } +} + +/** + * struct ghes_task_work - for synchronous RAS event + * + * @twork: callback_head for task work + * @pfn: page frame number of corrupted page + * @flags: work control flags + * + * Structure to pass task work to be handled before + * returning to user-space via task_work_add(). + */ +struct ghes_task_work { + struct callback_head twork; + u64 pfn; + int flags; +}; + +static void memory_failure_cb(struct callback_head *twork) +{ + struct ghes_task_work *twcb =3D container_of(twork, struct ghes_task_work= , twork); + int ret; + + ret =3D memory_failure(twcb->pfn, twcb->flags); + gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); + + if (!ret || ret =3D=3D -EHWPOISON || ret =3D=3D -EOPNOTSUPP) + return; + + pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\= n", + twcb->pfn, current->comm, task_pid_nr(current)); + force_sig(SIGBUS); +} + +static bool ghes_do_memory_failure(u64 physical_addr, int flags) +{ + struct ghes_task_work *twcb; + unsigned long pfn; + + if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) + return false; + + pfn =3D PHYS_PFN(physical_addr); + + if (flags =3D=3D MF_ACTION_REQUIRED && current->mm) { + twcb =3D (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); + if (!twcb) + return false; + + twcb->pfn =3D pfn; + twcb->flags =3D flags; + init_task_work(&twcb->twork, memory_failure_cb); + task_work_add(current, &twcb->twork, TWA_RESUME); + return true; + } + + memory_failure_queue(pfn, flags); + return true; +} + +bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, + int sev, bool sync) +{ + int flags =3D -1; + int sec_sev =3D ghes_severity(gdata->error_severity); + struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); + + if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) + return false; + + /* iff following two events can be handled properly by now */ + if (sec_sev =3D=3D GHES_SEV_CORRECTED && + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) + flags =3D MF_SOFT_OFFLINE; + if (sev =3D=3D GHES_SEV_RECOVERABLE && sec_sev =3D=3D GHES_SEV_RECOVERABL= E) + flags =3D sync ? MF_ACTION_REQUIRED : 0; + + if (flags !=3D -1) + return ghes_do_memory_failure(mem_err->physical_addr, flags); + + return false; +} + +bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, + int sev, bool sync) +{ + struct cper_sec_proc_arm *err =3D acpi_hest_get_payload(gdata); + int flags =3D sync ? MF_ACTION_REQUIRED : 0; + int length =3D gdata->error_data_length; + char error_type[120]; + bool queued =3D false; + int sec_sev, i; + char *p; + + sec_sev =3D ghes_severity(gdata->error_severity); + if (length >=3D sizeof(*err)) { + log_arm_hw_error(err, sec_sev); + } else { + pr_warn(FW_BUG "arm error length: %d\n", length); + pr_warn(FW_BUG "length is too small\n"); + pr_warn(FW_BUG "firmware-generated error record is incorrect\n"); + return false; + } + + if (sev !=3D GHES_SEV_RECOVERABLE || sec_sev !=3D GHES_SEV_RECOVERABLE) + return false; + + p =3D (char *)(err + 1); + length -=3D sizeof(err); + + for (i =3D 0; i < err->err_info_num; i++) { + struct cper_arm_err_info *err_info; + bool is_cache, has_pa; + + /* Ensure we have enough data for the error info header */ + if (length < sizeof(*err_info)) + break; + + err_info =3D (struct cper_arm_err_info *)p; + + /* Validate the claimed length before using it */ + length -=3D err_info->length; + if (length < 0) + break; + + is_cache =3D err_info->type & CPER_ARM_CACHE_ERROR; + has_pa =3D (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADD= R); + + /* + * The field (err_info->error_info & BIT(26)) is fixed to set to + * 1 in some old firmware of HiSilicon Kunpeng920. We assume that + * firmware won't mix corrected errors in an uncorrected section, + * and don't filter out 'corrected' error here. + */ + if (is_cache && has_pa) { + queued =3D ghes_do_memory_failure(err_info->physical_fault_addr, flags); + p +=3D err_info->length; + continue; + } + + cper_bits_to_str(error_type, sizeof(error_type), + FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type), + cper_proc_error_type_strs, + ARRAY_SIZE(cper_proc_error_type_strs)); + + pr_warn_ratelimited(FW_WARN GHES_PFX + "Unhandled processor error type 0x%02x: %s%s\n", + err_info->type, error_type, + err_info->type & ~CPER_ARM_ERR_TYPE_MASK ? + " with reserved bit(s)" : ""); + p +=3D err_info->length; + } + + return queued; +} + +/* + * PCIe AER errors need to be sent to the AER driver for reporting and + * recovery. The GHES severities map to the following AER severities and + * require the following handling: + * + * GHES_SEV_CORRECTABLE -> AER_CORRECTABLE + * These need to be reported by the AER driver but no recovery is + * necessary. + * GHES_SEV_RECOVERABLE -> AER_NONFATAL + * GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL + * These both need to be reported and recovered from by the AER driver. + * GHES_SEV_PANIC does not make it to this handling since the kernel must + * panic. + */ +void ghes_handle_aer(struct acpi_hest_generic_data *gdata) +{ +#ifdef CONFIG_ACPI_APEI_PCIEAER + struct cper_sec_pcie *pcie_err =3D acpi_hest_get_payload(gdata); + + if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && + pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { + unsigned int devfn; + int aer_severity; + u8 *aer_info; + + devfn =3D PCI_DEVFN(pcie_err->device_id.device, + pcie_err->device_id.function); + aer_severity =3D cper_severity_to_aer(gdata->error_severity); + + /* + * If firmware reset the component to contain + * the error, we must reinitialize it before + * use, so treat it as a fatal AER error. + */ + if (gdata->flags & CPER_SEC_RESET) + aer_severity =3D AER_FATAL; + + aer_info =3D (void *)gen_pool_alloc(ghes_estatus_pool, + sizeof(struct aer_capability_regs)); + if (!aer_info) + return; + memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs)); + + aer_recover_queue(pcie_err->device_id.segment, + pcie_err->device_id.bus, + devfn, aer_severity, + (struct aer_capability_regs *) + aer_info); + } +#endif +} + +void ghes_log_hwerr(int sev, guid_t *sec_type) +{ + if (sev !=3D CPER_SEV_RECOVERABLE) + return; + + if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) || + guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) || + guid_equal(sec_type, &CPER_SEC_PROC_IA)) { + hwerr_log_error_type(HWERR_RECOV_CPU); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) || + guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { + hwerr_log_error_type(HWERR_RECOV_CXL); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PCIE) || + guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) { + hwerr_log_error_type(HWERR_RECOV_PCI); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { + hwerr_log_error_type(HWERR_RECOV_MEMORY); + return; + } + + hwerr_log_error_type(HWERR_RECOV_OTHERS); +} + +void __ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + + if (!pfx) { + if (ghes_severity(estatus->error_severity) <=3D + GHES_SEV_CORRECTED) + pfx =3D KERN_WARNING; + else + pfx =3D KERN_ERR; + } + curr_seqno =3D atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); + printk("%sHardware error from APEI Generic Hardware Error Source: %d\n", + pfx_seq, generic->header.source_id); + cper_estatus_print(pfx_seq, estatus); +} + +int ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5 * HZ, 2); + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5 * HZ, 2); + struct ratelimit_state *ratelimit; + + if (ghes_severity(estatus->error_severity) <=3D GHES_SEV_CORRECTED) + ratelimit =3D &ratelimit_corrected; + else + ratelimit =3D &ratelimit_uncorrected; + if (__ratelimit(ratelimit)) { + __ghes_print_estatus(pfx, generic, estatus); + return 1; + } + return 0; +} + +#ifdef CONFIG_ACPI_APEI static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) { phys_addr_t paddr; @@ -272,6 +636,7 @@ void ghes_clear_estatus(struct ghes *ghes, if (is_hest_type_generic_v2(ghes)) ghes_ack_error(ghes->generic_v2); } +#endif /* CONFIG_ACPI_APEI */ =20 static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); =20 @@ -323,6 +688,77 @@ void ghes_defer_non_standard_event(struct acpi_hest_ge= neric_data *gdata, schedule_work(&entry->work); } =20 +void ghes_cper_handle_status(struct device *dev, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus, + bool sync) +{ + int sev, sec_sev; + struct acpi_hest_generic_data *gdata; + guid_t *sec_type; + const guid_t *fru_id =3D &guid_null; + char *fru_text =3D ""; + bool queued =3D false; + + sev =3D ghes_severity(estatus->error_severity); + apei_estatus_for_each_section(estatus, gdata) { + sec_type =3D (guid_t *)gdata->section_type; + sec_sev =3D ghes_severity(gdata->error_severity); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + fru_id =3D (guid_t *)gdata->fru_id; + + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + fru_text =3D gdata->fru_text; + + ghes_log_hwerr(sev, sec_type); + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); + + atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err); + + arch_apei_report_mem_error(sev, mem_err); + queued =3D ghes_handle_memory_failure(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)) { + queued =3D ghes_handle_arm_hw_error(gdata, sev, sync); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { + struct cxl_cper_sec_prot_err *prot_err =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_prot_err(prot_err, gdata->error_severity); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); + } else { + void *err =3D acpi_hest_get_payload(gdata); + + ghes_defer_non_standard_event(gdata, sev); + log_non_standard_event(sec_type, fru_id, fru_text, + sec_sev, err, + gdata->error_data_length); + } + } + + /* + * If no memory failure work is queued for abnormal synchronous + * errors, do a force kill. + */ + if (sync && !queued) { + dev_err(dev, + HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", + current->comm, task_pid_nr(current)); + force_sig(SIGBUS); + } +} /* Room for 8 entries */ #define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index dd49e9179b63..511b95b50911 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -17,6 +17,8 @@ #define ACPI_APEI_GHES_CPER_H =20 #include +#include +#include #include =20 #include @@ -57,6 +59,7 @@ ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) =20 extern struct gen_pool *ghes_estatus_pool; +extern struct atomic_notifier_head ghes_report_chain; =20 static inline bool is_hest_type_generic_v2(struct ghes *ghes) { @@ -107,6 +110,23 @@ void ghes_estatus_cache_add(struct acpi_hest_generic *= generic, struct acpi_hest_generic_status *estatus); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); +int ghes_severity(int severity); +bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, + int sev, bool sync); +bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, + int sev, bool sync); +void ghes_handle_aer(struct acpi_hest_generic_data *gdata); +void ghes_log_hwerr(int sev, guid_t *sec_type); +void __ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus); +int ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus); +void ghes_cper_handle_status(struct device *dev, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus, + bool sync); void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, int severity); int cxl_cper_register_prot_err_work(struct work_struct *work); --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 55E723F9282; Mon, 18 May 2026 11:59:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105553; cv=none; b=FYViVnOn7EQTxrC9B6uLz8RhUrljafUNFns/NeGyuLU+XUOak3mc2LZFE6b/H3iOfL0U/IXHII7VPDpWgXk8Z7NUXnVhDBGL7IuGhNcJXGOQ7VojdJgcBBmLu1fp9NTRnaXYmQvR7RNjaEcsyY+0NaNOIvpmBOMfNmqFOnNCA6o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105553; c=relaxed/simple; bh=t7eff0nNKGqGSzC5Dpn2m5cRP7fPVTRKNbLymISWJn0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HcQ9EnsAbb67Up7NTeJT4lK0QsJtmGtrcWg/xWl3H+i+duhgsd5Qc73cxu8q2J8eWif3jCRwx8MAvoYDWgAhwRrHhilCgErSFk54OTfPUn1EYelOjFJnqQhTPBZRpHnLktzfKOMXmjaGcxF2jEaz3skwh+rk4+HN/2d0iX+dWL4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=chAQVYzq; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="chAQVYzq" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C9CA44660; Mon, 18 May 2026 04:59:00 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C60BA3F85F; Mon, 18 May 2026 04:59:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105546; bh=t7eff0nNKGqGSzC5Dpn2m5cRP7fPVTRKNbLymISWJn0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=chAQVYzqoAtBk1xmJ0LQA/Yk5bciRaNX2fBV1L04I7oVDXeL5rmVGlnNq0qBS8asx AFFXunfWa8soHrVYBApe1lN00BMcr3Anc0/8KsfTfwVSrnNpzAH+nXhwgMXtlSCCLw B7asKW5o9FNMY5ZE+QWdNjyzy1ZW01TPNCMn0wwU= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:52 +0100 Subject: [PATCH v4 09/10] dt-bindings: firmware: add arm,ras-cper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-9-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=3188; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=t7eff0nNKGqGSzC5Dpn2m5cRP7fPVTRKNbLymISWJn0=; b=SgWeIgcCxMLYKi3Aq/Hv3NVmCarm1O6TQOTIVwu8Q4qifa5LF03fUyU363y8JGnSRuuIld8Wc XwAFkNsII5TDIDl37jyzslr6DElygh2yn7o3VZt2kYLoealFofD+Itr X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Describe the DeviceTree node that exposes the Arm firmware-first CPER provider and hook the file into MAINTAINERS so the binding has an owner. Signed-off-by: Ahmed Tiba --- .../devicetree/bindings/firmware/arm,ras-cper.yaml | 71 ++++++++++++++++++= ++++ MAINTAINERS | 5 ++ 2 files changed, 76 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml b= /Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml new file mode 100644 index 000000000000..81dc37390af5 --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/arm,ras-cper.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm RAS CPER provider + +maintainers: + - Ahmed Tiba + +description: + Arm Reliability, Availability and Serviceability (RAS) firmware can expo= se + a firmware-first CPER error source directly via DeviceTree. Firmware + provides the CPER Generic Error Status block and notifies the OS through + an interrupt. + +properties: + compatible: + const: arm,ras-cper + + memory-region: + oneOf: + - items: + - description: + CPER Generic Error Status block exposed by firmware + - items: + - description: + CPER Generic Error Status block exposed by firmware. + - description: + Optional firmware-owned ack buffer used on platforms + where firmware needs an explicit "ack" handshake before over= writing + the CPER buffer. Firmware watches bit 0 and expects the OS t= o set it + once the current status block has been consumed. + + interrupts: + maxItems: 1 + description: + Interrupt used to signal that a new status record is ready. + +required: + - compatible + - memory-region + - interrupts + +additionalProperties: false + +examples: + - | + #include + + reserved-memory { + #address-cells =3D <2>; + #size-cells =3D <2>; + ras_cper_buffer: memory@fe800000 { + reg =3D <0x0 0xfe800000 0x0 0x1000>; + no-map; + }; + + ras_cper_ack: memory@fe801000 { + reg =3D <0x0 0xfe801000 0x0 0x1000>; + no-map; + }; + }; + + error-handler { + compatible =3D "arm,ras-cper"; + memory-region =3D <&ras_cper_buffer>, <&ras_cper_ack>; + interrupts =3D ; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 7492fefa447c..3bbc19589f1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22251,6 +22251,11 @@ M: Alexandre Bounine S: Maintained F: drivers/rapidio/ =20 +RAS ERROR STATUS +M: Ahmed Tiba +S: Maintained +F: Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml + RAS INFRASTRUCTURE M: Tony Luck M: Borislav Petkov --=20 2.43.0 From nobody Mon May 25 05:15:54 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9A3F132C957; Mon, 18 May 2026 11:59:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105560; cv=none; b=nDaud4vsOD22sdbr+GxSexNHf+yFigl+J2A1HQyoUu2KKEnqNHbwpf17WW8sUXZAIf+dfKbPWezbswOT4KZulFYJ4OMDgbVc7n8ScG7R7Rj65l3nPGZL0IEsSzyppmtEc5FELOxBOkdEDhgcsVayzLUM6k8emRgPqaev7jHe02w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779105560; c=relaxed/simple; bh=r6+RDImTuCKLMNv+Yx19iSfyf7Yu2SLHUNVPzgPpbt4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lwMWNrPjT1k+Gm+Afc85UvqBbZOdxM/iGSc+MqRDCHFYuwy10A1j0ZWmFC5Yxrx6ejQIcNmdLOlUt8vfimFpUz6rIUZ/PI9xPSr8hK/ZwNaGr6VID8rKoW1T0GvOvTwC3FcKpArHeGnv4DTSKhyOv8MNrnU/utRrbvS+p3kJwIM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=lFcsdUWu; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="lFcsdUWu" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 509FC4617; Mon, 18 May 2026 04:59:06 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5A0C53F85F; Mon, 18 May 2026 04:59:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779105551; bh=r6+RDImTuCKLMNv+Yx19iSfyf7Yu2SLHUNVPzgPpbt4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=lFcsdUWuJTHXOBnDTFvCPSLLjDhMH/ctZ7t4YGt9TkjjgkryqV+Cku8CHVb0eBvdc yD5xtNHR6EEry5W7g8zPwUE0O+qELtcGtaq7H4VDdWF+FAHdY1V4GU2ylIGkLSlCXw SD8KjCcQrfrYSGxjK/sHFs8VxxRpZQ7FP2VZsEIA= From: Ahmed Tiba Date: Mon, 18 May 2026 12:57:53 +0100 Subject: [PATCH v4 10/10] RAS: add firmware-first CPER provider Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-10-42698675ba61@arm.com> References: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> In-Reply-To: <20260518-topics-ahmtib01-ras_ffh_arm_internal_review-v4-0-42698675ba61@arm.com> To: rafael@kernel.org, bp@alien8.de, saket.dumbre@intel.com, will@kernel.org, xueshuai@linux.alibaba.com, mchehab@kernel.org, krzk+dt@kernel.org, dave@stgolabs.net, conor+dt@kernel.org, vishal.l.verma@intel.com, jic23@kernel.org, corbet@lwn.net, guohanjun@huawei.com, dave.jiang@intel.com, catalin.marinas@arm.com, lenb@kernel.org, tony.luck@intel.com, skhan@linuxfoundation.org, djbw@kernel.org, alison.schofield@intel.com, ira.weiny@intel.com, robh@kernel.org Cc: Ahmed Tiba , devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com, linux-cxl@vger.kernel.org, Michael.Zhao2@arm.com, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779105490; l=12767; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=r6+RDImTuCKLMNv+Yx19iSfyf7Yu2SLHUNVPzgPpbt4=; b=Dm1ll5gN7H7QlF+vqSoU49cHhTmuC+3TCZAcO7Ij8jXl6nzLkCv+tqxm8Ay8UQsVevm9PfJx3 TZrV+7nVEP3A5gIuqoU/2XcT4eBkhT2/zkfA6m7w8FYBwx5TNIBoKow X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Add a firmware-first CPER provider that reuses the shared GHES helpers, wire it into the RAS Kconfig/Makefile and document it in the admin guide. Update MAINTAINERS now that the driver exists. Signed-off-by: Ahmed Tiba --- Documentation/admin-guide/RAS/main.rst | 18 +++ MAINTAINERS | 1 + drivers/acpi/apei/apei-internal.h | 10 +- drivers/acpi/apei/ghes_cper.c | 2 + drivers/ras/Kconfig | 11 ++ drivers/ras/Makefile | 1 + drivers/ras/cper-esource.c | 257 +++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 10 ++ 8 files changed, 301 insertions(+), 9 deletions(-) diff --git a/Documentation/admin-guide/RAS/main.rst b/Documentation/admin-g= uide/RAS/main.rst index 5a45db32c49b..84219d25a072 100644 --- a/Documentation/admin-guide/RAS/main.rst +++ b/Documentation/admin-guide/RAS/main.rst @@ -205,6 +205,24 @@ Architecture (MCA)\ [#f3]_. .. [#f3] For more details about the Machine Check Architecture (MCA), please read Documentation/arch/x86/x86_64/machinecheck.rst at the Kernel= tree. =20 +Firmware-first CPER providers +----------------------------- + +Some systems expose Common Platform Error Record (CPER) data +through platform firmware instead of ACPI HEST tables. +Enable ``CONFIG_RAS_CPER_ESOURCE`` to build the ``drivers/ras/cper-esource= .c`` +driver. The current in-tree firmware description uses the +``Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml`` binding. +The driver reuses the GHES CPER helper object in +``drivers/acpi/apei/ghes_cper.c`` so the logging, notifier chains, and +memory failure handling match the ACPI GHES behaviour even when +ACPI is disabled. + +Once a platform describes a firmware-first provider, both ACPI GHES and the +firmware-described driver reuse the same code paths. This keeps the +behaviour consistent regardless of whether the error source is described +by ACPI tables or another firmware description. + EDAC - Error Detection And Correction ************************************* =20 diff --git a/MAINTAINERS b/MAINTAINERS index 3bbc19589f1a..8a5151a49820 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22255,6 +22255,7 @@ RAS ERROR STATUS M: Ahmed Tiba S: Maintained F: Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml +F: drivers/ras/cper-esource.c =20 RAS INFRASTRUCTURE M: Tony Luck diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-int= ernal.h index 77c10a7a7a9f..c16ac541f15b 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -8,6 +8,7 @@ #define APEI_INTERNAL_H =20 #include +#include =20 struct apei_exec_context; =20 @@ -120,15 +121,6 @@ int apei_exec_collect_resources(struct apei_exec_conte= xt *ctx, struct dentry; struct dentry *apei_get_debugfs_dir(void); =20 -static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatu= s) -{ - if (estatus->raw_data_length) - return estatus->raw_data_offset + \ - estatus->raw_data_length; - else - return sizeof(*estatus) + estatus->data_length; -} - int apei_osc_setup(void); =20 int einj_get_available_error_type(u32 *type, int einj_action); diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 0ff9d06eb78f..a7691aa5011c 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -46,7 +46,9 @@ #include #include =20 +#ifdef CONFIG_ACPI_APEI #include "apei-internal.h" +#endif =20 ATOMIC_NOTIFIER_HEAD(ghes_report_chain); =20 diff --git a/drivers/ras/Kconfig b/drivers/ras/Kconfig index fc4f4bb94a4c..3c1c63b2fefc 100644 --- a/drivers/ras/Kconfig +++ b/drivers/ras/Kconfig @@ -34,6 +34,17 @@ if RAS source "arch/x86/ras/Kconfig" source "drivers/ras/amd/atl/Kconfig" =20 +config RAS_CPER_ESOURCE + bool "Firmware-first CPER error source block provider" + select GHES_CPER_HELPERS + help + Enable support for firmware-first Common Platform Error Record + (CPER) error source block providers. The current in-tree user is + described by the arm,ras-cper DeviceTree binding. The driver + reuses the existing GHES CPER helpers so the error processing + matches the ACPI code paths, but it can be built even when ACPI is + disabled. + config RAS_FMPM tristate "FRU Memory Poison Manager" default m diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile index 11f95d59d397..0de069557f31 100644 --- a/drivers/ras/Makefile +++ b/drivers/ras/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_RAS) +=3D ras.o obj-$(CONFIG_DEBUG_FS) +=3D debugfs.o obj-$(CONFIG_RAS_CEC) +=3D cec.o +obj-$(CONFIG_RAS_CPER_ESOURCE) +=3D cper-esource.o =20 obj-$(CONFIG_RAS_FMPM) +=3D amd/fmpm.o obj-y +=3D amd/atl/ diff --git a/drivers/ras/cper-esource.c b/drivers/ras/cper-esource.c new file mode 100644 index 000000000000..83f7a910e50a --- /dev/null +++ b/drivers/ras/cper-esource.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Firmware-first CPER error source provider. + * + * This driver shares the GHES CPER helpers so we keep the reporting and + * notifier behaviour identical to ACPI GHES. + * + * Copyright (C) 2026 ARM Ltd. + * Author: Ahmed Tiba + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static DEFINE_IDA(cper_esource_source_ids); + +struct cper_esource_ack { + void __iomem *addr; + u64 preserve; + u64 set; + u8 width; + bool present; +}; + +struct cper_esource { + struct device *dev; + void __iomem *status; + size_t status_len; + + struct cper_esource_ack ack; + + struct acpi_hest_generic *generic; + struct acpi_hest_generic_status *estatus; + + bool sync; + int irq; + + /* Serializes access while firmware and the OS share the status buffer. */ + spinlock_t lock; +}; + +static void cper_esource_release_source_id(void *data) +{ + struct acpi_hest_generic *generic =3D data; + + ida_free(&cper_esource_source_ids, generic->header.source_id); +} + +static int cper_esource_init_pool(void) +{ + if (ghes_estatus_pool) + return 0; + + return ghes_estatus_pool_init(1); +} + +static int cper_esource_copy_status(struct cper_esource *ctx) +{ + memcpy_fromio(ctx->estatus, ctx->status, ctx->status_len); + return 0; +} + +static void cper_esource_ack(struct cper_esource *ctx) +{ + u64 val; + + if (!ctx->ack.present) + return; + + if (ctx->ack.width =3D=3D 64) { + val =3D readq(ctx->ack.addr); + val &=3D ctx->ack.preserve; + val |=3D ctx->ack.set; + writeq(val, ctx->ack.addr); + } else { + val =3D readl(ctx->ack.addr); + val &=3D (u32)ctx->ack.preserve; + val |=3D (u32)ctx->ack.set; + writel(val, ctx->ack.addr); + } +} + +static void cper_esource_fatal(struct cper_esource *ctx) +{ + __ghes_print_estatus(KERN_EMERG, ctx->generic, ctx->estatus); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); + panic("GHES: fatal firmware-first CPER record from %s\n", + dev_name(ctx->dev)); +} + +static void cper_esource_process(struct cper_esource *ctx) +{ + int sev; + + guard(spinlock_irqsave)(&ctx->lock); + + if (cper_esource_copy_status(ctx)) + return; + + sev =3D ghes_severity(ctx->estatus->error_severity); + if (sev >=3D GHES_SEV_PANIC) + cper_esource_fatal(ctx); + + if (!ghes_estatus_cached(ctx->estatus) && + ghes_print_estatus(NULL, ctx->generic, ctx->estatus)) + ghes_estatus_cache_add(ctx->generic, ctx->estatus); + + ghes_cper_handle_status(ctx->dev, ctx->generic, ctx->estatus, ctx->sync); + cper_esource_ack(ctx); +} + +static irqreturn_t cper_esource_irq(int irq, void *data) +{ + struct cper_esource *ctx =3D data; + + cper_esource_process(ctx); + + return IRQ_HANDLED; +} + +static int cper_esource_init_ack(struct platform_device *pdev, + struct cper_esource *ctx) +{ + struct device *dev =3D &pdev->dev; + struct resource *res; + size_t size; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return 0; + + ctx->ack.addr =3D devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (IS_ERR(ctx->ack.addr)) + return PTR_ERR(ctx->ack.addr); + + size =3D resource_size(res); + switch (size) { + case 4: + ctx->ack.width =3D 32; + ctx->ack.preserve =3D ~0U; + break; + case 8: + ctx->ack.width =3D 64; + ctx->ack.preserve =3D ~0ULL; + break; + default: + return dev_err_probe(dev, -EINVAL, + "unsupported ack resource size %zu\n", size); + } + + ctx->ack.set =3D BIT_ULL(0); + ctx->ack.present =3D true; + return 0; +} + +static int cper_esource_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct cper_esource *ctx; + struct resource *res; + int source_id; + int rc; + + ctx =3D devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + spin_lock_init(&ctx->lock); + ctx->dev =3D dev; + ctx->sync =3D device_property_read_bool(dev, "arm,sea-notify"); + + ctx->status =3D devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(ctx->status)) + return dev_err_probe(dev, PTR_ERR(ctx->status), + "failed to map status region\n"); + + ctx->status_len =3D resource_size(res); + if (!ctx->status_len) + return dev_err_probe(dev, -EINVAL, "status region has zero length\n"); + + rc =3D cper_esource_init_ack(pdev, ctx); + if (rc) + return rc; + + rc =3D cper_esource_init_pool(); + if (rc) + return rc; + + ctx->estatus =3D devm_kzalloc(dev, ctx->status_len, GFP_KERNEL); + if (!ctx->estatus) + return -ENOMEM; + + ctx->generic =3D devm_kzalloc(dev, sizeof(*ctx->generic), GFP_KERNEL); + if (!ctx->generic) + return -ENOMEM; + + source_id =3D ida_alloc_min(&cper_esource_source_ids, 1, GFP_KERNEL); + if (source_id < 0) + return source_id; + + ctx->generic->header.type =3D ACPI_HEST_TYPE_GENERIC_ERROR; + ctx->generic->header.source_id =3D source_id; + + rc =3D devm_add_action_or_reset(dev, cper_esource_release_source_id, + ctx->generic); + if (rc) + return rc; + + ctx->generic->notify.type =3D ctx->sync ? + ACPI_HEST_NOTIFY_SEA : ACPI_HEST_NOTIFY_EXTERNAL; + ctx->generic->error_block_length =3D ctx->status_len; + + ctx->irq =3D platform_get_irq(pdev, 0); + if (ctx->irq < 0) + return ctx->irq; + + rc =3D devm_request_threaded_irq(dev, ctx->irq, NULL, cper_esource_irq, + IRQF_ONESHOT, + dev_name(dev), ctx); + if (rc) + return dev_err_probe(dev, rc, "failed to request interrupt\n"); + + return 0; +} + +static const struct of_device_id cper_esource_of_match[] =3D { + { .compatible =3D "arm,ras-cper" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cper_esource_of_match); + +static struct platform_driver cper_esource_driver =3D { + .driver =3D { + .name =3D "cper-esource", + .of_match_table =3D cper_esource_of_match, + }, + .probe =3D cper_esource_probe, +}; + +module_platform_driver(cper_esource_driver); + +MODULE_AUTHOR("Ahmed Tiba "); +MODULE_DESCRIPTION("Firmware-first CPER provider"); +MODULE_LICENSE("GPL"); diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 511b95b50911..a78d4a773129 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -80,6 +80,14 @@ static inline bool is_hest_sync_notify(struct ghes *ghes) return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; } =20 +static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatu= s) +{ + if (estatus->raw_data_length) + return estatus->raw_data_offset + estatus->raw_data_length; + else + return sizeof(*estatus) + estatus->data_length; +} + struct ghes_vendor_record_entry { struct work_struct work; int error_severity; @@ -108,6 +116,8 @@ int __ghes_read_estatus(struct acpi_hest_generic_status= *estatus, int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); void ghes_estatus_cache_add(struct acpi_hest_generic *generic, struct acpi_hest_generic_status *estatus); +int ghes_register_vendor_record_notifier(struct notifier_block *nb); +void ghes_unregister_vendor_record_notifier(struct notifier_block *nb); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); int ghes_severity(int severity); --=20 2.43.0