[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