[PATCH v2 03/11] ACPI: APEI: GHES: move CPER read helpers
Himanshu Chauhan
himanshu.chauhan at oss.qualcomm.com
Wed Feb 25 21:58:06 PST 2026
On Fri, Feb 20, 2026 at 7:13 PM Ahmed Tiba <ahmed.tiba at arm.com> wrote:
>
> Relocate the CPER buffer mapping, peek, and clear helpers from ghes.c into
> ghes_cper.c so they can be shared with other firmware-first providers.
> This commit only shuffles code; behavior stays the same.
>
> Signed-off-by: Ahmed Tiba <ahmed.tiba at arm.com>
> ---
> drivers/acpi/apei/ghes.c | 170 +-----------------------------------------
> drivers/acpi/apei/ghes_cper.c | 170 +++++++++++++++++++++++++++++++++++++++++-
> include/acpi/ghes_cper.h | 14 ++--
> 3 files changed, 177 insertions(+), 177 deletions(-)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 07b70bcb8342..b159dbee90ac 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_CACHES_SIZE];
> static atomic_t ghes_estatus_cache_alloced;
>
> -static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx)
> -{
> - phys_addr_t paddr;
> - pgprot_t prot;
> -
> - paddr = PFN_PHYS(pfn);
> - prot = 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_idx)
> -{
> - int _idx = virt_to_fix((unsigned long)vaddr);
> -
> - WARN_ON_ONCE(fixmap_idx != _idx);
> - clear_fixmap(fixmap_idx);
> -}
> -
> int ghes_estatus_pool_init(unsigned int num_ghes)
> {
> unsigned long addr, len;
> @@ -193,22 +173,7 @@ static void unmap_gen_v2(struct ghes *ghes)
> apei_unmap_generic_address(&ghes->generic_v2->read_ack_register);
> }
>
> -static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2)
> -{
> - int rc;
> - u64 val = 0;
> -
> - rc = apei_read(&val, &gv2->read_ack_register);
> - if (rc)
> - return;
> -
> - val &= gv2->read_ack_preserve << gv2->read_ack_register.bit_offset;
> - val |= gv2->read_ack_write << gv2->read_ack_register.bit_offset;
> -
> - apei_write(val, &gv2->read_ack_register);
> -}
> -
> -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;
> @@ -255,7 +220,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
> return ERR_PTR(rc);
> }
>
> -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);
> @@ -280,137 +245,6 @@ static inline int ghes_severity(int severity)
> }
> }
Can it be "ghes_finish"? We already have "creat" without 'e'.
>
> -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 = paddr - (paddr & PAGE_MASK);
> - vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx);
> - trunk = PAGE_SIZE - offset;
> - trunk = min(trunk, len);
> - if (from_phys)
> - memcpy_fromio(buffer, vaddr + offset, trunk);
> - else
> - memcpy_toio(vaddr + offset, buffer, trunk);
> - len -= trunk;
> - paddr += trunk;
> - buffer += trunk;
> - ghes_unmap(vaddr, fixmap_idx);
> - }
> -}
> -
> -/* Check the top-level record header has an appropriate size. */
> -static int __ghes_check_estatus(struct ghes *ghes,
> - struct acpi_hest_generic_status *estatus)
> -{
> - u32 len = cper_estatus_len(estatus);
> - u32 max_len = 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. */
> -static 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 = ghes->generic;
> - int rc;
> -
> - rc = apei_read(buf_paddr, &g->error_status_address);
> - if (rc) {
> - *buf_paddr = 0;
> - pr_warn_ratelimited(FW_WARN GHES_PFX
> -"Failed to read error status block address for hardware error source: %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 = 0;
> - return -ENOENT;
> - }
> -
> - return 0;
> -}
> -
> -static 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;
> -}
> -
> -static int ghes_read_estatus(struct ghes *ghes,
> - struct acpi_hest_generic_status *estatus,
> - u64 *buf_paddr, enum fixed_addresses fixmap_idx)
> -{
> - int rc;
> -
> - rc = __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx);
> - if (rc)
> - return rc;
> -
> - rc = __ghes_check_estatus(ghes, estatus);
> - if (rc)
> - return rc;
> -
> - return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx,
> - cper_estatus_len(estatus));
> -}
> -
> -static void ghes_clear_estatus(struct ghes *ghes,
> - struct acpi_hest_generic_status *estatus,
> - u64 buf_paddr, enum fixed_addresses fixmap_idx)
> -{
> - estatus->block_status = 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);
> -}
>
> /**
> * struct ghes_task_work - for synchronous RAS event
> diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c
> index 63047322a3d9..7e0015e960c1 100644
> --- a/drivers/acpi/apei/ghes_cper.c
> +++ b/drivers/acpi/apei/ghes_cper.c
IMO, just "cper.c" would be fine.
> @@ -1,7 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0
> /*
> *
> - * APEI GHES CPER helper translation unit - staging file for helper moves
> + * APEI GHES CPER helper translation unit - code mechanically moved from ghes.c
> *
> * Copyright (C) 2026 ARM Ltd.
> * Author: Ahmed Tiba <ahmed.tiba at arm.com>
> @@ -17,10 +17,176 @@
> #include <linux/slab.h>
>
> #include <acpi/apei.h>
> +#include <acpi/ghes_cper.h>
>
> #include <asm/fixmap.h>
> #include <asm/tlbflush.h>
>
> #include "apei-internal.h"
>
> -/* Helper bodies will be moved here in follow-up commits. */
> +static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx)
> +{
> + phys_addr_t paddr;
> + pgprot_t prot;
> +
> + paddr = PFN_PHYS(pfn);
> + prot = 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_idx)
> +{
> + int _idx = virt_to_fix((unsigned long)vaddr);
> +
> + WARN_ON_ONCE(fixmap_idx != _idx);
> + clear_fixmap(fixmap_idx);
> +}
> +
> +static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2)
> +{
> + int rc;
> + u64 val = 0;
> +
> + rc = apei_read(&val, &gv2->read_ack_register);
> + if (rc)
> + return;
> +
> + val &= gv2->read_ack_preserve << gv2->read_ack_register.bit_offset;
> + val |= 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 = paddr - (paddr & PAGE_MASK);
> + vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx);
> + trunk = PAGE_SIZE - offset;
> + trunk = min(trunk, len);
> + if (from_phys)
> + memcpy_fromio(buffer, vaddr + offset, trunk);
> + else
> + memcpy_toio(vaddr + offset, buffer, trunk);
> + len -= trunk;
> + paddr += trunk;
> + buffer += 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 = cper_estatus_len(estatus);
> + u32 max_len = 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 = ghes->generic;
> + int rc;
> +
> + rc = apei_read(buf_paddr, &g->error_status_address);
> + if (rc) {
> + *buf_paddr = 0;
> + pr_warn_ratelimited(FW_WARN GHES_PFX
> +"Failed to read error status block address for hardware error source: %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 = 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 = __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx);
> + if (rc)
> + return rc;
> +
> + rc = __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 = 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);
> +}
> diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h
> index 2597fbadc4f3..2e3919f0c3e7 100644
> --- a/include/acpi/ghes_cper.h
> +++ b/include/acpi/ghes_cper.h
> @@ -74,21 +74,21 @@ struct ghes_vendor_record_entry {
> char vendor_record[];
> };
>
ditto. "include/acpi/cper.h"
> -static struct ghes *ghes_new(struct acpi_hest_generic *generic);
> -static void ghes_fini(struct ghes *ghes);
> +struct ghes *ghes_new(struct acpi_hest_generic *generic);
> +void ghes_fini(struct ghes *ghes);
>
> -static int ghes_read_estatus(struct ghes *ghes,
> +int ghes_read_estatus(struct ghes *ghes,
> struct acpi_hest_generic_status *estatus,
> u64 *buf_paddr, enum fixed_addresses fixmap_idx);
> -static void ghes_clear_estatus(struct ghes *ghes,
> +void ghes_clear_estatus(struct ghes *ghes,
> struct acpi_hest_generic_status *estatus,
> u64 buf_paddr, enum fixed_addresses fixmap_idx);
> -static int __ghes_peek_estatus(struct ghes *ghes,
> +int __ghes_peek_estatus(struct ghes *ghes,
> struct acpi_hest_generic_status *estatus,
> u64 *buf_paddr, enum fixed_addresses fixmap_idx);
> -static int __ghes_check_estatus(struct ghes *ghes,
> +int __ghes_check_estatus(struct ghes *ghes,
> struct acpi_hest_generic_status *estatus);
> -static int __ghes_read_estatus(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);
>
>
> --
> 2.43.0
>
>
More information about the linux-arm-kernel
mailing list