Prepare the pSeries EEH backend for RTAS error injection by adding helpers that: * map generic EEH error types to RTAS/PAPR error types; * validate the error injection parameters; * validate IOA addresses against the target PE; * build the PAPR ibm,errinjct parameter buffer. 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. Returns -EINVAL (Linux errno) rather than a raw RTAS status. Add validate_errinjct_args() as a top-level validation wrapper that checks the PE pointer, maps the generic type, and validates the function range. Function-range validation is owned here; prepare_errinjct_buffer() does not re-check it. Add prepare_errinjct_buffer() which: - guards against NULL buf/pe/pe->phb - zeroes the buffer unconditionally with memset() - checks 32-bit truncation for the IOA_BUS_ERROR case - calls validate_addr_mask_in_pe() for address validation - supports only the IOA bus-error types (unreachable non-IOA RTAS cases removed) The caller provides an exclusive per-call RTAS work-area buffer. Firmware session serialization is left to the caller. 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 Closes: https://lore.kernel.org/oe-kbuild-all/202512101130.EYUo0oZx-lkp@intel.com/ Signed-off-by: Narayana Murty N --- arch/powerpc/platforms/pseries/eeh_pseries.c | 220 +++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index b12ef382fec7..999b3065e09b 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 #include #include @@ -23,6 +25,7 @@ #include #include #include +#include #include #include @@ -786,6 +789,223 @@ 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]; + + if (!resource_size(res)) + continue; + + if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + continue; + + pcibios_resource_to_bus(pdev->bus, ®ion, res); + + bus_start = region.start; + bus_len = resource_size(res); + + 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; +} + +/** + * validate_errinjct_args - Top-level validation for RTAS error injection arguments + * @pe: EEH PE for the target device + * @type: generic EEH error type + * @func: error function selector + * @addr: address argument (type-dependent, may be zero) + * @mask: mask argument (type-dependent, may be zero) + * + * Validates all parameters before opening an RTAS injection session. + * Returns Linux errno values; does not return raw RTAS status codes. + * + * Return: 0 if all parameters are valid, negative errno otherwise. + */ +static int validate_errinjct_args(struct eeh_pe *pe, int type, int func, + unsigned long addr, unsigned long mask) +{ + if (!pe || !pe->phb) + return -EINVAL; + + if (pseries_eeh_type_to_rtas(type) < 0) + return -EINVAL; + + if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX) + return -EINVAL; + + return 0; +} + +/** + * prepare_errinjct_buffer() - Build ibm,errinjct work buffer + * @buf: RTAS error-injection work buffer + * @pe: EEH PE associated with the injection target + * @rtas_err_type: RTAS firmware error-injection type + * @func: Error function selector + * @addr: Target PCI bus address + * @mask: Address mask passed to firmware + * + * Zeroes @buf and populates it according to the PAPR layout for the + * selected IOA bus-error injection type. + * + * The caller provides an exclusive per-call RTAS work-area buffer. + * Firmware session serialization is handled by pseries_eeh_err_inject(). + * + * Return: 0 on success or a negative errno on invalid input. + */ +static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe, + int rtas_err_type, int func, + unsigned long addr, unsigned long mask) +{ + __be64 *buf64 = (__be64 *)buf; + __be32 *buf32 = (__be32 *)buf; + int rc; + + if (!buf || !pe || !pe->phb) + return -EINVAL; + + memset(buf, 0, RTAS_ERRINJCT_BUF_SIZE); + + switch (rtas_err_type) { + case RTAS_ERR_TYPE_IOA_BUS_ERROR: + if (upper_32_bits(addr) || upper_32_bits(mask)) + return -EINVAL; + + rc = validate_addr_mask_in_pe(pe, addr, mask); + if (rc) + return rc; + + buf32[0] = cpu_to_be32((u32)addr); + buf32[1] = cpu_to_be32((u32)mask); + buf32[2] = cpu_to_be32(pe->addr); + buf32[3] = cpu_to_be32(BUID_HI(pe->phb->buid)); + buf32[4] = cpu_to_be32(BUID_LO(pe->phb->buid)); + buf32[5] = cpu_to_be32(func); + break; + + case RTAS_ERR_TYPE_IOA_BUS_ERROR_64: + rc = validate_addr_mask_in_pe(pe, addr, mask); + if (rc) + return rc; + + buf64[0] = cpu_to_be64(addr); + buf64[1] = cpu_to_be64(mask); + buf32[4] = cpu_to_be32(pe->addr); + buf32[5] = cpu_to_be32(BUID_HI(pe->phb->buid)); + buf32[6] = cpu_to_be32(BUID_LO(pe->phb->buid)); + buf32[7] = cpu_to_be32(func); + break; + + default: + pr_err("unsupported RTAS error injection type 0x%x\n", rtas_err_type); + return -EINVAL; + } + + pr_debug("errinjct buffer ready: rtas_err_type=0x%x func=%d addr=0x%lx mask=0x%lx\n", + rtas_err_type, func, addr, mask); + return 0; +} + /** * pseries_eeh_err_inject - Inject specified error to the indicated PE * @pe: the indicated PE -- 2.54.0