[PATCH v6 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers
flat view
HOTtoday
From: Narayana Murty N <hidden>
Date: 2026-10-23 07:37:47
Also in:
kvm, lkml
Subsystem:
linux for powerpc (32-bit and 64-bit), pci enhanced error handling (eeh) for powerpc, the rest · Maintainers:
Madhavan Srinivasan, Mahesh J Salgaonkar, Linus Torvalds
Revision v6 of 2 in this series.
Revisions (2)
- v5 [diff vs current]
- v6 current
Prepare the pSeries EEH backend for RTAS error injection by adding helpers that: * map generic EEH error types to RTAS/PAPR error types; * validate IOA addresses against the target PE. Define pr_fmt unconditionally at the top of eeh_pseries.c so all pr_*() calls carry the "EEH: " prefix. Define RTAS firmware error-type encodings as file-local constants. Only the two IOA bus-error types are supported; other PAPR encodings are not reachable through the generic EEH_ERR_TYPE_* UAPI: RTAS_ERR_TYPE_IOA_BUS_ERROR 0x07 RTAS_ERR_TYPE_IOA_BUS_ERROR_64 0x0f Add RTAS_ERRINJCT_BUF_SIZE for the ibm,errinjct work buffer size. Add pseries_eeh_type_to_rtas() to translate the two generic EEH error types (EEH_ERR_TYPE_32, EEH_ERR_TYPE_64) into the corresponding RTAS ibm,errinjct encodings. Add validate_addr_mask_in_pe() to verify that a caller-provided address falls within a BAR of some device in the PE. RTAS IOA bus-error injection requires a PCI/IOA bus address; validate_addr_mask_in_pe() accepts only PCI bus addresses. VFIO userspace resource addresses are normalized to PCI bus addresses in vfio_iommu_spapr_tce.c before reaching this backend; no resource-address fallback is added here. Use pcibios_resource_to_bus() to convert each BAR resource to PCI bus address space before validating the address. Only standard BARs (BAR0..BAR5) are searched; ROM and bridge resources are not IOA bus-error targets. Hold pci_rescan_remove_lock while traversing PE devices and their BAR resources so PCI device removal cannot race the address validation. Skip unassigned or disabled BARs explicitly: on pseries, pcibios_fixup_resources() zero-bases unassigned BARs (start=0, end=size-1) and sets IORESOURCE_UNSET, so resource_size() is non-zero for them and pcibios_resource_to_bus() treats them as bus address zero. Use pci_resource_len() (which returns 0 when end==0) instead of resource_size(), and reject BARs with IORESOURCE_UNSET or IORESOURCE_DISABLED set. Returns -EINVAL (Linux errno) rather than a raw RTAS status. validate_errinjct_args() and prepare_errinjct_buffer() are introduced in the next patch alongside their callers so each intermediate commit builds cleanly with CONFIG_WERROR. pseries_eeh_err_inject() retains the existing MMIO injection body unchanged in this patch. It is replaced by the full RTAS session implementation in the next patch. This keeps the patch bisectable: each commit builds and functions correctly independently. Reported-by: kernel test robot <redacted> Closes: https://lore.kernel.org/oe-kbuild-all/202512101130.EYUo0oZx-lkp@intel.com/ (local) Signed-off-by: Narayana Murty N <redacted> --- arch/powerpc/platforms/pseries/eeh_pseries.c | 134 +++++++++++++++++++ 1 file changed, 134 insertions(+)
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b12ef382fec7..28b1fd19e8bc 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c@@ -12,6 +12,8 @@ * Copyright Linas Vepstas 2005, 2006 */ +#define pr_fmt(fmt) "EEH: " fmt + #include <linux/atomic.h> #include <linux/delay.h> #include <linux/export.h>
@@ -23,6 +25,7 @@ #include <linux/rbtree.h> #include <linux/sched.h> #include <linux/seq_file.h> +#include <linux/sizes.h> #include <linux/spinlock.h> #include <linux/crash_dump.h>
@@ -786,6 +789,137 @@ static int pseries_notify_resume(struct eeh_dev *edev) } #endif +/* + * RTAS firmware error-type encodings (PAPR). These are pseries-private; + * user-space sees only the generic EEH_ERR_TYPE_* values from eeh.h. + * Only IOA bus-error types are supported; other PAPR encodings are not + * reachable through the generic EEH_ERR_TYPE_* UAPI. + */ +#define RTAS_ERR_TYPE_IOA_BUS_ERROR 0x07 +#define RTAS_ERR_TYPE_IOA_BUS_ERROR_64 0x0f + +/* Size of the ibm,errinjct work buffer as defined by PAPR */ +#define RTAS_ERRINJCT_BUF_SIZE SZ_1K + +/** + * pseries_eeh_type_to_rtas - Map generic EEH error type to RTAS encoding + * @type: generic EEH error type (EEH_ERR_TYPE_32, EEH_ERR_TYPE_64) + * + * Translates the generic VFIO/EEH error type passed from userspace into + * the RTAS-specific ibm,errinjct error type encoding defined by PAPR. + * + * Return: RTAS error type on success, -EINVAL for unknown types. + */ +static int pseries_eeh_type_to_rtas(int type) +{ + switch (type) { + case EEH_ERR_TYPE_32: + return RTAS_ERR_TYPE_IOA_BUS_ERROR; + case EEH_ERR_TYPE_64: + return RTAS_ERR_TYPE_IOA_BUS_ERROR_64; + default: + return -EINVAL; + } +} + +/** + * validate_addr_mask_in_pe - Validate addr against PCI bus BAR addresses in PE + * @pe: EEH PE containing one or more PCI devices + * @addr: PCI bus address to validate + * @mask: address mask (passed to firmware unchanged) + * + * RTAS IOA bus-error injection uses PCI bus addresses. Linux PCI resources + * are CPU/resource addresses and may differ on pseries due to PHB window + * translation. Convert each BAR resource to PCI bus address space before + * validating @addr. + * + * Zero addr and mask are accepted without BAR lookup (no-address injection). + * + * Return: 0 if valid, -EINVAL on invalid input. + */ +static int validate_addr_mask_in_pe(struct eeh_pe *pe, unsigned long addr, + unsigned long mask) +{ + struct pci_bus_region region; + struct eeh_dev *edev, *tmp; + struct pci_dev *pdev; + struct resource *res; + resource_size_t bus_start; + resource_size_t bus_len; + bool found = false; + int bar; + + if (!addr && !mask) + return 0; + + if (!pe) + return -EINVAL; + + /* + * RTAS IOA bus-error injection uses PCI bus addresses. Linux PCI + * resources are CPU/resource addresses and may differ on pseries due + * to PHB window translation. Convert each BAR resource to PCI bus + * address space before validating @addr. + * + * Hold pci_rescan_remove_lock across the traversal to protect pdev + * lifetime: pci_stop_and_remove_bus_device() requires this lock. + */ + pci_lock_rescan_remove(); + eeh_pe_for_each_dev(pe, edev, tmp) { + pdev = eeh_dev_to_pci_dev(edev); + if (!pdev) + continue; + + /* + * Validate against standard BARs (BAR0..BAR5) only. + * ROM and bridge resources are not IOA bus-error targets. + */ + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + res = &pdev->resource[bar]; + + /* + * Skip unassigned or disabled BARs. On pseries, + * pcibios_fixup_resources() zero-bases unassigned BARs + * (start=0, end=size-1) and sets IORESOURCE_UNSET. + * resource_size() is non-zero for such BARs, and + * pcibios_resource_to_bus() treats them as bus address + * zero, so they must be excluded explicitly. + * Use pci_resource_len() which returns 0 when end==0. + */ + if (!pci_resource_len(pdev, bar)) + continue; + + if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + continue; + + if (res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) + continue; + + pcibios_resource_to_bus(pdev->bus, ®ion, res); + + bus_start = region.start; + bus_len = pci_resource_len(pdev, bar); + + if ((resource_size_t)addr >= bus_start && + ((resource_size_t)addr - bus_start) < bus_len) { + pr_debug("addr=0x%lx mask=0x%lx validated in PCI bus BAR[%d] of %s: bus range 0x%llx-0x%llx\n", + addr, mask, bar, pci_name(pdev), + (unsigned long long)region.start, + (unsigned long long)region.end); + found = true; + goto done; + } + } + } +done: + pci_unlock_rescan_remove(); + + if (!found) + pr_err("addr=0x%lx mask=0x%lx not within any PCI bus BAR of any device in PE\n", + addr, mask); + return found ? 0 : -EINVAL; +} + /** * pseries_eeh_err_inject - Inject specified error to the indicated PE * @pe: the indicated PE
--
2.54.0