From nobody Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 938DA3CC9F6; Wed, 17 Jun 2026 13:55:32 +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=1781704534; cv=none; b=swMWUo4TN2/CXlb1l/3OgIN7nClWR757hxo/ZgfTt6P9fZEPP20bvDo8dqscBgpvbS7/9uqXZEQSNdY6jhkcSLVdQpvkntay9yLZw1Q+Jaz7aj0uzIOpIq9CQFp6ev9xrNXiUyICGFTkab7efhGUAFjIbCUGo7w3govD3cS16/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704534; c=relaxed/simple; bh=xjxZJJ2J6dkizOyQg7BHPYtAZYKV1o6KXvIAs7Sx9PI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tzPvJSQ8g0R3el2QxPIPNkXj0+WU42EHUrlBsulH3h18OejghgdVvjF22zIxq90m4mR7mU5yhRGz6ZNLzi0wLg/yNRN8wW6zkPgPkWtPYVSTP1O05E1KDrGXZcAmaxxrkGZYHnN3b7KEZIVh8YizQ74VhOSHVemHjDErFHhhaMg= 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=rmUKYzHV; 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="rmUKYzHV" 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 5E0034800; Wed, 17 Jun 2026 06:55: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 889C83F915; Wed, 17 Jun 2026 06:55:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704532; bh=xjxZJJ2J6dkizOyQg7BHPYtAZYKV1o6KXvIAs7Sx9PI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=rmUKYzHVX7rFRancfW/snh7IefJmDrD+ADcYV2EpCYhcSr8n1V7UNG2Gaaae3W+j2 ZuAF4waJEDAs4PvmxnIFGPtXDYq8Lg2QA+MZ/kc2QNf/0C5G21zKDjfcanI4bq1lYp q8de2Pwmxl0k0dUhly8AOBiyzeQNw5z48SH8xERg= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:39 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-1-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=11073; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=xjxZJJ2J6dkizOyQg7BHPYtAZYKV1o6KXvIAs7Sx9PI=; b=We9LRVIfYu0pNSbRVGvmraBiRNdZlffi17tp7oJ4511V7+rrU862dWxugs6Ss4vNiRGPunpza Fsa/4pSledTBhPD9JSj35FfOZdRqmf3C1d237l5K6b/yhSneFeHMHR/ 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. Move the vendor record entry declaration and the prototypes for the CPER read and clear helpers along with ghes_new() and ghes_fini() into the same header. This requires dropping their local static visibility in ghes.c. Reviewed-by: Jonathan Cameron Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 94 +++++++++--------------------------------- include/acpi/ghes_cper.h | 103 +++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 121 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..4649e3140888 --- /dev/null +++ b/include/acpi/ghes_cper.h @@ -0,0 +1,103 @@ +/* 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 +#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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 879633ED109; Wed, 17 Jun 2026 13:55:37 +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=1781704539; cv=none; b=dJ5H84J/W/m7Kgq4GhIMjJf+dW2dFGcn/2bhyBiW4r9GCFHyEyYsQzgxFSbjS21P55UhwQrHQR5lc7tCqz8Ym65oeP7yb4nOioA2JzFCDu/RhihLKMsjAr2GZ4aSN0q37Ff5fIbBGKMYLnXOoFA/LzOdbozZECRh+fWEpp+7mEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704539; c=relaxed/simple; bh=lVj2TW3WLFDhf9ti4r1d9WV3N3XQt0/Q0vBRDj3cRWA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=naRB3uMxMvfhnXq2U/+fRyTTOsrqp0xwbGKWk0qVcSIdR0PhyC2J6/2NYT1uWRnWX1nUZtuOrkXbGPTWZmmWZzXSvAIucdE1/LyQcKYki/UFyrcgVHYiIH8j2/fxWjzUa38T9CerHnZEUk5mUAeDpsnQcMqQ1cocEmgiIxP0Ubc= 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=hDUO47k0; 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="hDUO47k0" 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 42C5F483F; Wed, 17 Jun 2026 06:55: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 618A63F915; Wed, 17 Jun 2026 06:55:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704536; bh=lVj2TW3WLFDhf9ti4r1d9WV3N3XQt0/Q0vBRDj3cRWA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hDUO47k096iMvSfDqjTZeg9XrkURqGGIrrSQWQcjbHbx/FRGT8Rqtj2ttwAcC7dnJ DGjvNy/cuqq6DHUd7pJG/lCYAJQrYA9j8stF+1tnDZPyDQSymeGBgMJQ9yA7MoIj+E bEr8ko26tzNV2H+E5OabJmeh5P4BkXhRmZX7NdBU= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:40 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-2-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=11739; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=lVj2TW3WLFDhf9ti4r1d9WV3N3XQt0/Q0vBRDj3cRWA=; b=bNXXuCaoIPPN9EzB7gbV0rtTf2icoiI/SpDIbf/hEwQSvGDDo5OoWNtiHQsOkySvn3xnlN4qy KwpxjzayLp6BwcFi1QEOps5oarTLUIjY83RvAfwSn9qAnPLYqqZAtHl 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. Reviewed-by: Jonathan Cameron Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/Makefile | 2 +- drivers/acpi/apei/ghes.c | 166 ----------------------------------- drivers/acpi/apei/ghes_cper.c | 196 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 197 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..b365c42efce4 --- /dev/null +++ b/drivers/acpi/apei/ghes_cper.c @@ -0,0 +1,196 @@ +// 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 + +#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 > 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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5AEC63F0AB1; Wed, 17 Jun 2026 13:55:42 +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=1781704544; cv=none; b=JTMte7qOjLw+R9nKM5RLqGoA7MLbvKhs862iBo0vlOMlj62Q8Tzjt5WA9sgfcsNsZhdVpTvJoEWYtDAtyGJ3XrYzzLxYXLU3+THxO1lRavqa6dnM5ru/BIl893ck5Ze1Foi0cysMKEvxyCCljTiOCoJHGa8d17nbrqMnLPQ9ISA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704544; c=relaxed/simple; bh=NoPx2iFg4QJUGLJFFQ4Uzy7THNiSYlecHmwOVB59fJ4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oCbs4OXO1Cgw/6YKLeqIo5YNMDJQtzK5t1rS8Y9UeABBO8/cxxH9qO43XyrvPxdcNHCO/h5Ag3eE/2ei716le8SrXsI8BPoz96D/7upLbdLsMd1rE3YwstuigEopVay5ulL9p11JyP6qI4c1HT7SGwWXSkWFtoXi4ItnFe7r/DA= 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=J1fuOJY2; 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="J1fuOJY2" 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 4495832AB; Wed, 17 Jun 2026 06:55:37 -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 4B2693F915; Wed, 17 Jun 2026 06:55:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704541; bh=NoPx2iFg4QJUGLJFFQ4Uzy7THNiSYlecHmwOVB59fJ4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=J1fuOJY2z/dI/AjLO0MV9y/c5WRNbJebPazYftzKSHwztKpFFGZ13NZYzLoWFP1xq ObBx/wnFu2h/5Zcd2y4ITaxgYcfANIB2u+cO4GiPDjAFc7m1zZ37PFq1KMApIMNalX ZYQ1v6Ojqksq9KdA8aNOfMf7uaHtD0UiPEGYc+j0= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:41 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-3-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=4777; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=NoPx2iFg4QJUGLJFFQ4Uzy7THNiSYlecHmwOVB59fJ4=; b=HzZ2fPYGVH7nfDh75mxTwxY5PfGAWmrN3bigTwkFmYPjsCb2b57nyNPkJwIY6IfFsQLHGvctg 5w2UOZQyeWFBGqrKGZC5KH0b2aA4bj+DlJz1n0KmnCFEAGleYSr6Ra7 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. Reviewed-by: Jonathan Cameron 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 b365c42efce4..c6e20fdc6964 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -63,6 +63,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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 979793EEAEB; Wed, 17 Jun 2026 13:55:47 +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=1781704550; cv=none; b=Cg0ga50I5tmYwWxcIdJfWQ6hWhAVsinS6i+1glJOQoVtGMTk1Z2tsvuJvle+syxNNlrERyj9j2kpkPV/mlIw2BKESZUlvvmY68OPwi5Qdz7pzFpvBVYJpOpdoykK05gK94agMzfG6HO3f13JM7hx6rn1exMCz2psBy4a75L5p4s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704550; c=relaxed/simple; bh=G6coLRg+HrSSMx5FLZXc+S7HLc/2kDZpQIaMt6WvUbc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bROgmWouatfZ9mUwSzDll4q9RtU3D5hw+5VbpPqeIEH33+rLzj25y6Bx4/X1rRtARX7SzL7X3+WGDSscv30H2xZLjuFOt5OJRSNE0B+8ea3iV19gHvJtsikw36JiYhYmoE99k1KWAJqFk6mIz7DtZWNZNHPAypQ7Ph3uWJoPazo= 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=fSBp+SVg; 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="fSBp+SVg" 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 3DB6D4800; Wed, 17 Jun 2026 06:55:42 -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 4ADEF3F915; Wed, 17 Jun 2026 06:55:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704546; bh=G6coLRg+HrSSMx5FLZXc+S7HLc/2kDZpQIaMt6WvUbc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fSBp+SVgVlthvJ8XOlt9QaWNJAlBOhz56/JxLGtxEpjbt3lxzYaKrZsRDPDP0JOuM EoeUsjlY0GESsBbJ0TDV+7Clxwk5rzxEqTRpFkO1CbzaPwztC5HNk5KnzbqRVBSAsR vKLjbRYTCmOOySZgOTo3W/oQILHHU/+cqAOins6w= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:42 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-4-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=11163; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=G6coLRg+HrSSMx5FLZXc+S7HLc/2kDZpQIaMt6WvUbc=; b=UV6GRYnrYPatszyjZzuguelRlxkvFpsj+0v/JqRGEeefqRNOkDQL9yNyeFbneRDxLC6TCDbhp G8Z1dsQZuc6DCdBYH5/MmROy4UZuoUVO+Zf0GgG2fSvXUL18itS7eX9 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. Reviewed-by: Jonathan Cameron Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 138 +-------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 139 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 5 ++ 3 files changed, 145 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 c6e20fdc6964..5218fc57e562 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 @@ -28,6 +32,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; @@ -259,3 +266,135 @@ 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 4649e3140888..15305c8be9a7 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -55,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; @@ -99,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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5BBE13EDE6C; Wed, 17 Jun 2026 13:55: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=1781704553; cv=none; b=Xxy8Ch+F7lb61YbbNPylTMKsvdm+Czs4C0CWIHgvyfu3Y0KZatGhMOrGByPol5YNtaYTG4OZBaW8YQUZ0ORoLGVTyXOqxtd0r1trE8qn/AkvvSO+XrA3L4ZhK+x1XPtIan2cZ6jTFe5Jnndho5xrjqMWU9Wdha0N7nqpnfUageg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704553; c=relaxed/simple; bh=gJtKRBMInrqR7T9rhsSURuylQA76dBUb/rtibQUuoLo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HItHDG1SWtt8BKvCys6sn9Yhu2gIQ8vn9l/VANWkcJH5aL1AeUW0mxKAul3o0bVSbY7LdGmiir5AQaVnfwouioNyF6lbPhT/154H9eZLwCXeflT4MfIkEm5jEyq3Q2RubheeZV5m3S92HWIQ5QBIdAeXUIfi707HKCTnkXlJrcU= 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=bMocOjhA; 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="bMocOjhA" 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 1832132AB; Wed, 17 Jun 2026 06:55:47 -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 45ED13F915; Wed, 17 Jun 2026 06:55:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704551; bh=gJtKRBMInrqR7T9rhsSURuylQA76dBUb/rtibQUuoLo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bMocOjhAfW5Hpu2+DGPcTvSOmNuTE5bRRjOe347IfcfHhUVc4P28NwxwleVQPsLAv pT+MuwP1mYKkD1XMMQkb/d5WXmJr2fCTcc6PYFlyyQO0fYp+fWoAX9SMs+9PPtJc/a 83sO0EGSUBsTKzb1yYQAFDkC+glaPidyK/tgA7H8= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:43 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-5-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=6442; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=gJtKRBMInrqR7T9rhsSURuylQA76dBUb/rtibQUuoLo=; b=OeCW+PNEPl03mgWI8jUYl8I9Rkt6ajV3Kiu6Bb1/91JPcq8gL7sdT3WW6jeHlBBVjZqKbcDEa /p7lczTriM+DrhKcoOGv5zMJnPQGDG6x/WM2Yl6B0M0A5wOdSaSUnW0 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 | 68 ---------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 69 +++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 2 ++ 3 files changed, 71 insertions(+), 68 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index adab7404310e..9703c602a8c2 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, diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 5218fc57e562..b26943eafd79 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -267,6 +268,74 @@ 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_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); +} + +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 15305c8be9a7..d9f9253d8de9 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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1C5DF3F1673; Wed, 17 Jun 2026 13:55:57 +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=1781704558; cv=none; b=ozhshETyLdkipv0eHm+YQGm3HWyHJVkDfz2mH7YPLaP8v2XaifDjd2LR89iQ09bFffHBLQyledlegnjUTPMGW8Rxh94IybYhgviAd1qVRrmz0I6I6KabQmF1jaZyiirVfuDtjFA9ThoFBOUzOhT2KqZftKCOFgRj5R4oseaY98o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704558; c=relaxed/simple; bh=v7RUr735rL9PPiaEWfLRbg6MZS8am/SFraXJE7Q/MoM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=o9T26+RdQy1guGM2ftZ7k8JOt8VrgMLw5/Gy71cuvcItsW52XVCeFIAYUUFPKylNzcwsuB01H9eLjmNj4egQQ4VNnROuEqFhqSBnuiVtGi4rR2XeBVzHiLJFCijF3tW/2MmtIUVqru5Jqm5mnG3pwOZQWQ+Ae27t3JGzH/fMknw= 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=HfqMJSg5; 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="HfqMJSg5" 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 E5D1A4800; Wed, 17 Jun 2026 06:55:51 -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 1EA0F3F915; Wed, 17 Jun 2026 06:55:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704556; bh=v7RUr735rL9PPiaEWfLRbg6MZS8am/SFraXJE7Q/MoM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HfqMJSg51UlNTh9xPK2LWg2zTRMYMD9O1aKZI68+9tsP/BWnFiJLd9vhCfRftteOm VoXEUbIiclZ9P+84q4DR3PL5YWlmvuHqxE5yoteHdq0VBtAd0+xXvEpV9fGNIL3L9y MqVfurRs7DZxlFjzNZ4vRauuwQqtZsBF4KWtOet8= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:44 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-6-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=9992; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=v7RUr735rL9PPiaEWfLRbg6MZS8am/SFraXJE7Q/MoM=; b=W4vTzMAcyYqig84RTHJ6DZv/TXzUXOL8EYKDJGgf1qbn5f/iL+J3rQevLlnIEJhl+s6RU+5aE fhswFgPNmttDZ6BSlyOB1xE0somxhC7auEUCaEnUV1bFmGbRS7eLtyF 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. Reviewed-by: Jonathan Cameron Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 132 --------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 135 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 11 ++++ 3 files changed, 146 insertions(+), 132 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 9703c602a8c2..136993704d52 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -383,138 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_= data *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"); - -/* 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 b26943eafd79..66bf1af4db00 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -12,9 +12,12 @@ * Author: Huang Ying */ =20 +#include +#include #include #include #include +#include #include #include #include @@ -336,6 +339,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 d9f9253d8de9..a853a5996cdf 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -20,6 +20,7 @@ =20 #include #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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0EB403F4DF8; Wed, 17 Jun 2026 13:56:01 +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=1781704564; cv=none; b=OeRm9KEw7POwrInM8qlwVnbRdt1po+/zAeGrhO1ZW1a5jsk6v8Sjd7IyVqpOzO//IUTdTyQ0okNM4rScgbazF6iHnOls1oQb2Yl7vuIk+Zq1VXdZbkV/lXTp7zvm758zd/R6i6ChwOZ2zIWQPgX8whNvWsiZJXiz/AtkqeOca+M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704564; c=relaxed/simple; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hEWB6YGsa5wYtbLJ0UpwIlzZFFRPL1px9hU4lViXccfMHVRKPEMRBubSr5Ryc0o66hubCPY5OQk4TNzjzzqqjoQbHjO6t20q4fReZim0fiitQq1bAmXhOFyXBAddWtyA8J36TTyCCNiG/mHbRXH/W47bfuHaWJI1F+7KPjFRf7w= 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=RBZNjHxF; 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="RBZNjHxF" 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 C060D32AB; Wed, 17 Jun 2026 06:55:56 -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 EAE303F915; Wed, 17 Jun 2026 06:55:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704561; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=RBZNjHxFg0gGMxhvHbI9t9rcmNWQIptCMz0l9Nm8V2NTMXaID0p1V8bPh6yYOv8G3 hwhEhgzVq4bWpTFlENfxFb9L/7qWsthFXfwq/R3H5/i0xPd4ysvJlnbldwmHSgR+Hs YDEc3IqvVUS9vofbuOBpnWgxcfmwmymOKxBC+dPM= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:45 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-7-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=3795; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; b=v0MrLMFTZ1QRfbmap/PQmQ4tTT5xUnV8VurEgHroSkfLx7yYjVudwEAw8jIvl8lsZP9DiWXta Qr7Aiy02OwwAWuTzqa9AviHiUxN6jPJxzpP4R7ras0juX+RsrFxdG8s 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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1852F3FAE0D; Wed, 17 Jun 2026 13:56: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=1781704570; cv=none; b=qIxk2zuVCsXXxBIWabWYJJEjbW5ZwShtB2Du7MfywKZMvLljsnmREP5aWEj3lQf2UNH/Ae0DiFm5JTuNoIV5DnvnfvcUZNDn12H6Rr3Uftl7655fgrvsrKad/bW5B2qmHh33D0OX+XpTrtBkUSBYOk+IA8qyNatUkPuJLXR7gd4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704570; c=relaxed/simple; bh=qRvCaat7tTk7uptyv9xwWRWSnd0++v2v7aKMVzDAIgs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HKrYaSDiZ3FDisGUPXRZ2FzJlH5zai6/tNgdAG29R/lRf+fqnrCvfrgU/wSkBtP0i59QB0ILidFJ7rOImpeO/3yv6gMAjh6oCJvg3wHX8lGFBODIrJfLCJU8r9cMutjxBlss4F71VFdADUAlJzFBv9swXxoKVRDH9700PKdjm0I= 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=KV0NUBSS; 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="KV0NUBSS" 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 BD975483F; Wed, 17 Jun 2026 06:56:01 -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 C452C3F915; Wed, 17 Jun 2026 06:56:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704566; bh=qRvCaat7tTk7uptyv9xwWRWSnd0++v2v7aKMVzDAIgs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=KV0NUBSSCl03MK2v0RDA/HiN6xTVCdHWyzFVzf/KqowEPetX7ZNjZnn2nJpJG0GWQ reuAqEuOA9Riwz+sUUGGZ/KfOYQWRjWvf2jvdDPRq695mx9rtC1XYX+6MDUNylIQy1 Lw5ctA7uHs5x6xjXNPbmWDH5dm63N9aEs48IOnhU= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:46 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-8-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=31302; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=qRvCaat7tTk7uptyv9xwWRWSnd0++v2v7aKMVzDAIgs=; b=sg9ZOwCDclq/eVtisR0iOOaBRbxPr89OhIwXxlXJyKHt80v4to3mpyGrA2UxiSz9lKNJo5sb8 SVpvVz0e/nBBJYzeD+S/nxbf4pmcLhYlQgMh2pFFvqzpBnjyKhGaM98 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. The weak arch_apei_report_mem_error() fallback is only there to keep the shared helper buildable when GHES_CPER_HELPERS is enabled without ACPI_APEI, while preserving the current GHES behaviour when ACPI_APEI is enabled. Serialize ghes_estatus_pool_init() and allow later users to extend the shared pool instead of racing a second initialization or silently reusing a pool sized only for the first user. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 416 +------------------------------------- drivers/acpi/apei/ghes_cper.c | 452 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 20 ++ 3 files changed, 474 insertions(+), 414 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 136993704d52..2ec3c9100544 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,421 +111,11 @@ 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_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 66bf1af4db00..460fe12b6513 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -13,7 +13,9 @@ */ =20 #include +#include #include +#include #include #include #include @@ -21,11 +23,20 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include #include +#include +#include =20 #include #include @@ -36,9 +47,449 @@ =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; +static DEFINE_MUTEX(ghes_estatus_pool_lock); + +int ghes_estatus_pool_init(unsigned int num_ghes) +{ + unsigned long addr, len; + int rc; + + len =3D GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX; + len +=3D (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE); + len =3D PAGE_ALIGN(len); + + mutex_lock(&ghes_estatus_pool_lock); + + if (!ghes_estatus_pool) { + ghes_estatus_pool =3D gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER,= -1); + if (!ghes_estatus_pool) { + rc =3D -ENOMEM; + goto out_unlock; + } + } + + addr =3D (unsigned long)vmalloc(len); + if (!addr) { + rc =3D -ENOMEM; + goto err_pool_alloc; + } + + rc =3D gen_pool_add(ghes_estatus_pool, addr, len, -1); + if (rc) + goto err_pool_add; + + mutex_unlock(&ghes_estatus_pool_lock); + return 0; + +err_pool_add: + vfree((void *)addr); + +err_pool_alloc: + if (!gen_pool_avail(ghes_estatus_pool) && !gen_pool_size(ghes_estatus_poo= l)) { + gen_pool_destroy(ghes_estatus_pool); + ghes_estatus_pool =3D NULL; + } +out_unlock: + mutex_unlock(&ghes_estatus_pool_lock); + return rc; +} +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_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); + } +} + +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); +} + +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; @@ -270,6 +721,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 diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index a853a5996cdf..e09a33d343d7 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -16,6 +16,8 @@ #ifndef ACPI_APEI_GHES_CPER_H #define ACPI_APEI_GHES_CPER_H =20 +#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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E31D53F0746; Wed, 17 Jun 2026 13:56:11 +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=1781704573; cv=none; b=T2MHIPwHrzwPBWyHrME2XzDctr7gbxZGTm1gyLf/lUTtGbUkAb0E0caeDTBbDFv/OHz4LiOyWzECqW4CstTigrCGGTVL6w1JdKCHCLJwRVO0HXTbiTo3Qhyi/AHvCQ0ePcRzq4FWMKXPWUGPD0vi2NYrbtlF4wAttCwz3neWRog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704573; c=relaxed/simple; bh=Zn8XxlqbuTUbu03kyT0mZA2J926gwuoL8gKMplV7ANQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qEcAUYQFZYei18b9sY7I4nxgXbMunLudGN6k/9KnbmnSa+1CSybdIV0fQuNp8s1vUbQv949mqxZ6MYAK+31XXHyi+4ECLh4xvpPoEU1pVBYAXgb/Cni6ajKGhg4P3NadAH2r6qvqT3/9fqRyiM4OW6IR8gvyt+uWDfLcper31gc= 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=reBkMx1Y; 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="reBkMx1Y" 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 9731C32AB; Wed, 17 Jun 2026 06:56: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 C23E93F915; Wed, 17 Jun 2026 06:56:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704571; bh=Zn8XxlqbuTUbu03kyT0mZA2J926gwuoL8gKMplV7ANQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=reBkMx1Y4fl32y+wywqx8zGMC1K+dIJUCKh+WdfwbSvB+yX0TcOLDsdoZMvUGrD6C rB6DkXQ4Run9vQ9TUEHVPAF+gz4Ljcpo9jePvbHr/O94T3nge3ISoX7zKFe2Q8edZe uTgM1uJZgElahEJNieAyqChz4I96+jbxcLLkjmBQ= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:47 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-9-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=2701; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=Zn8XxlqbuTUbu03kyT0mZA2J926gwuoL8gKMplV7ANQ=; b=p905tSND28Dww4qnVPMC0hfec+iBv5oncjsJZ59HBev09gegmI6hYBWrKhFwJDKixig30+Py7 PJNUnMM/CTPC2TyyfLuVHA35Fh7C9+sSDC/VZ4NZO3A8C0FU5c9vP6w 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. The initial user is the upstream zena-css platform, validated so far on FVP. Signed-off-by: Ahmed Tiba --- .../devicetree/bindings/firmware/arm,ras-cper.yaml | 52 ++++++++++++++++++= ++++ MAINTAINERS | 5 +++ 2 files changed, 57 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..23d54008230d --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml @@ -0,0 +1,52 @@ +# 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: + items: + - description: + CPER Generic Error Status block exposed by firmware. + - description: + Firmware-owned ack buffer. Firmware watches bit 0 and expects the + OS to set it once the current status block has been consumed + before the CPER buffer is overwritten. + + interrupts: + maxItems: 1 + description: + Interrupt used to signal that a new status record is ready. + +required: + - compatible + - memory-region + - interrupts + +additionalProperties: false + +examples: + - | + #include + + 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 3d6db8cb608f..5aa495fdff72 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22322,6 +22322,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 Thu Jun 18 20:20:06 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id AA61140BCD9; Wed, 17 Jun 2026 13:56:16 +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=1781704578; cv=none; b=FYXUyN+4XgbGaZASI4HMV4m5qwyjdGDXqCMQWscnYEw+pBKYmahBPayFWGP9iBEfeNAr2Gu3+7MKYCCdV1E8AM2AqAE+zFxySyl0tf9ck/ObTZQ69FxZu0D1mjSpbODP0bfz7QrSfloYjFKDyZ1dvZcPZ4OKGws6KihKy7dXiXo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781704578; c=relaxed/simple; bh=OmCgA5BEOWiMGDGMNzGm6MDdEi0TrhHZpmSYaO2cLO8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=S8ELGTLWLyOIZSCwVhYLVl5rMeUJs+L+2m/H1u4Rj8fHREIjPqJVQ/FuOQ3ee8Mho4L5AEgPNtD8Aue3p4gsWAi/HexXEkCTTV59n9JNQlRY1l9gnvRtv8IJj/8GStR3om/HKmGHgoQvj1AFKdbYikv8yeW1/V4Qfqv81o4ifE4= 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=rIN3JAXZ; 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="rIN3JAXZ" 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 8230032AB; Wed, 17 Jun 2026 06:56:11 -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 9D41C3F915; Wed, 17 Jun 2026 06:56:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781704576; bh=OmCgA5BEOWiMGDGMNzGm6MDdEi0TrhHZpmSYaO2cLO8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=rIN3JAXZ6jK2YOc1mhJGOHG+RGWgKXh551lNgQtGiAyrUnYBO7AaAUPQRaIthuKah vCyIjo550GEGkCCIhaJA2U2fKA1CnbcFNe1htgZDwz+oQFbKkf0vig1Anneuy58lRP 277zPtvG8wGxxOAMlk+g+HWTXiwyyzbPAnXw9rso= From: Ahmed Tiba Date: Wed, 17 Jun 2026 14:54:48 +0100 Subject: [PATCH v6 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: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-10-91f725174aa0@arm.com> References: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com> To: "Rafael J. Wysocki" , Tony Luck , Borislav Petkov , Hanjun Guo , Mauro Carvalho Chehab , Shuai Xue , Len Brown , Saket Dumbre , Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams , Ahmed Tiba , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-cxl@vger.kernel.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-doc@vger.kernel.org, Dmitry.Lamerov@arm.com X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781704522; l=12455; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=OmCgA5BEOWiMGDGMNzGm6MDdEi0TrhHZpmSYaO2cLO8=; b=5OVw4jYsrP6zC9Ll05NrDCLE70iJwlsIqQyziu+EsyKZi7h9S2wqYgF+GIokplcXuEs5CIb6Y JaK6GylX+gTBcl3Ve8Ti2ynWFCvQLFtfrDIJ6u8aLH2ss1LnzqR4WQp 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 | 15 ++ MAINTAINERS | 1 + drivers/acpi/apei/apei-internal.h | 3 +- drivers/ras/Kconfig | 11 ++ drivers/ras/Makefile | 1 + drivers/ras/cper-esource.c | 322 +++++++++++++++++++++++++++++= ++++ 6 files changed, 351 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/RAS/main.rst b/Documentation/admin-g= uide/RAS/main.rst index 5a45db32c49b..d4e3c8c1b92f 100644 --- a/Documentation/admin-guide/RAS/main.rst +++ b/Documentation/admin-guide/RAS/main.rst @@ -205,6 +205,21 @@ 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, with the error source described in DeviceTree. +Enable ``CONFIG_RAS_CPER_ESOURCE`` to support those providers. The +current in-tree binding is +``Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml``. + +The DeviceTree node describes the firmware-owned status buffer and ack +buffer used to exchange CPER data with the OS. The driver reuses the +shared GHES CPER handling helpers, so parsing, logging, notifier +delivery, and memory failure handling follow the same paths as ACPI +GHES whether the error source is described by ACPI or DeviceTree. + EDAC - Error Detection And Correction ************************************* =20 diff --git a/MAINTAINERS b/MAINTAINERS index 5aa495fdff72..00b9a1abab67 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22326,6 +22326,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..15d11f10d067 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -123,8 +123,7 @@ struct dentry *apei_get_debugfs_dir(void); 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; + return estatus->raw_data_offset + estatus->raw_data_length; else return sizeof(*estatus) + estatus->data_length; } 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..cc9f5f522400 --- /dev/null +++ b/drivers/ras/cper-esource.c @@ -0,0 +1,322 @@ +// 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 +#include + +static DEFINE_IDA(cper_esource_source_ids); + +struct cper_esource_ack { + void *addr; + u64 preserve; + u64 set; + u8 width; + bool present; +}; + +struct cper_esource { + struct device *dev; + void *status; + size_t status_len; + + struct cper_esource_ack ack; + + struct acpi_hest_generic generic; + struct acpi_hest_generic_status *estatus; + + int irq; + + /* Serializes access while firmware and the OS share the status buffer. */ + spinlock_t lock; +}; + +static void *cper_esource_map_region(struct device *dev, unsigned int inde= x, + size_t *size) +{ + struct resource res; + void *addr; + + if (of_reserved_mem_region_to_resource(dev->of_node, index, &res)) + return ERR_PTR(dev_err_probe(dev, -EINVAL, + "unable to resolve memory-region %u\n", + index)); + + *size =3D resource_size(&res); + if (!*size) + return ERR_PTR(dev_err_probe(dev, -EINVAL, + "memory-region %u has zero length\n", + index)); + + addr =3D devm_memremap(dev, res.start, *size, MEMREMAP_WB); + if (!addr) + return ERR_PTR(dev_err_probe(dev, -ENOMEM, + "failed to map memory-region %u\n", + index)); + + return addr; +} + +static void cper_esource_release_source_id(void *data) +{ + struct cper_esource *ctx =3D data; + + ida_free(&cper_esource_source_ids, ctx->generic.header.source_id); +} + +static int cper_esource_init_pool(void) +{ + return ghes_estatus_pool_init(1); +} + +static u32 cper_esource_estatus_len(struct acpi_hest_generic_status *estat= us) +{ + if (estatus->raw_data_length) + return estatus->raw_data_offset + estatus->raw_data_length; + else + return sizeof(*estatus) + estatus->data_length; +} + +static int cper_esource_validate_status(struct cper_esource *ctx) +{ + size_t estatus_len; + + if (!ctx->estatus->block_status) + return -ENOENT; + + if (cper_estatus_check_header(ctx->estatus)) + return -EINVAL; + + if (ctx->estatus->raw_data_length && + (ctx->estatus->raw_data_offset > ctx->status_len || + ctx->estatus->raw_data_length > + ctx->status_len - ctx->estatus->raw_data_offset)) + return -EINVAL; + + estatus_len =3D cper_esource_estatus_len(ctx->estatus); + if (estatus_len < sizeof(*ctx->estatus) || estatus_len > ctx->status_len) + return -EINVAL; + + if (cper_estatus_check(ctx->estatus)) + return -EINVAL; + + return 0; +} + +static void cper_esource_ack(struct cper_esource *ctx) +{ + if (!ctx->ack.present) + return; + + if (ctx->ack.width =3D=3D 64) { + u64 *addr =3D ctx->ack.addr; + u64 val =3D READ_ONCE(*addr); + + /* Publish status-buffer updates before raising the ack bit. */ + wmb(); + val &=3D ctx->ack.preserve; + val |=3D ctx->ack.set; + WRITE_ONCE(*addr, val); + } else { + u32 *addr =3D ctx->ack.addr; + u32 val =3D READ_ONCE(*addr); + + /* Publish status-buffer updates before raising the ack bit. */ + wmb(); + val &=3D (u32)ctx->ack.preserve; + val |=3D (u32)ctx->ack.set; + WRITE_ONCE(*addr, val); + } +} + +static void cper_esource_clear_status(struct cper_esource *ctx) +{ + ctx->estatus->block_status =3D 0; + WRITE_ONCE(((struct acpi_hest_generic_status *)ctx->status)->block_status= , 0); +} + +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 rc; + int sev; + + guard(spinlock_irqsave)(&ctx->lock); + + memcpy(ctx->estatus, ctx->status, ctx->status_len); + + rc =3D cper_esource_validate_status(ctx); + if (rc =3D=3D -ENOENT) + return; + if (rc) { + dev_warn_ratelimited(ctx->dev, FW_WARN GHES_PFX + "Invalid error status block\n"); + cper_esource_clear_status(ctx); + cper_esource_ack(ctx); + return; + } + + sev =3D ghes_severity(ctx->estatus->error_severity); + if (sev >=3D GHES_SEV_PANIC) + cper_esource_fatal(ctx); + + ghes_print_estatus(NULL, &ctx->generic, ctx->estatus); + + ghes_cper_handle_status(ctx->dev, &ctx->generic, ctx->estatus, false); + cper_esource_clear_status(ctx); + 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 cper_esource *ctx) +{ + struct device *dev =3D ctx->dev; + size_t size; + + ctx->ack.addr =3D cper_esource_map_region(dev, 1, &size); + if (IS_ERR(ctx->ack.addr)) + return PTR_ERR(ctx->ack.addr); + + 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; + size_t size; + 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->status =3D cper_esource_map_region(dev, 0, &size); + if (IS_ERR(ctx->status)) + return PTR_ERR(ctx->status); + + ctx->status_len =3D size; + if (ctx->status_len < sizeof(*ctx->estatus)) + return dev_err_probe(dev, -EINVAL, + "status region is smaller than a CPER header\n"); + + rc =3D cper_esource_init_ack(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; + + /* Keep source_id 0 unused so a zeroed header is never treated as valid. = */ + source_id =3D ida_alloc_min(&cper_esource_source_ids, 1, GFP_KERNEL); + if (source_id < 0) + return source_id; + if (source_id > U16_MAX) { + ida_free(&cper_esource_source_ids, source_id); + return -ENOSPC; + } + + 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); + if (rc) + return rc; + + ctx->generic.notify.type =3D 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"); --=20 2.43.0