powerpc: Add RTAS based error injection support for pSeries
Subsystems (5)
v6 cover powerpc: Add RTAS based error injection support for pSeries
The pSeries EEH error-injection backend currently implements a limited software-generated MMIO failure and does not use the error-injection services provided by RTAS.
This series replaces that implementation with the PAPR-defined RTAS workflow based on:
- ibm,open-errinjct
- ibm,errinjct
- ibm,close-errinjct
The implementation opens an RTAS error-injection session, allocates and prepares an RTAS-accessible work area, performs the requested injection, releases the work area, and closes the session on both success and failure paths.
The existing EEH userspace ABI is preserved. EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 continue to represent generic 32-bit and 64-bit IOA bus-error injection requests. The pSeries backend maps these values to the corresponding RTAS error types, while the PowerNV backend explicitly maps them to the corresponding OPAL types.
Additional generic EEH error types (EEH_ERR_TYPE_RECOVERED_SPECIAL_EVENT, EEH_ERR_TYPE_CORRUPTED_PAGE, and the cache/TLB corruption types) are defined in the UAPI header and mapped explicitly to RTAS firmware encodings by the pSeries backend. Platform backends that do not support a valid generic type return -EOPNOTSUPP.
No existing userspace ABI values are changed.
The injection path can be exercised for VFIO-assigned devices through VFIO_EEH_PE_INJECT_ERR. The guest or userspace VFIO application continues to use the same generic EEH type and function values, independent of whether the host platform uses RTAS or OPAL.
For IOA bus-error injection, the VFIO sPAPR path normalizes a Linux BAR resource address to the corresponding PCI bus address before dispatching the request to the platform EEH backend. Address normalization is restricted to memory and IO load/store functions whose addr argument is a BAR-backed PCI address. Config-space and DMA functions are passed through without translation because their addr arguments represent PCI config register offsets and IOVA/TCE addresses respectively. The no-address form is represented by both address and mask being zero.
The series also handles the unusual return format of ibm,open-errinjct:
rets[0] = error-injection session token
rets[1] = RTAS status
rtas_call() returns rets[1] as the status and places the session token in outputs[0], preserving the normal kernel rtas_call() convention.
sys_rtas() continues to expose the raw RTAS return cells to userspace. For ibm,open-errinjct, it validates that at least two return cells were requested before entering RTAS and uses the RTAS status cell for last-error handling. Userspace therefore continues to receive the session token and status in their PAPR-defined positions.
The pSeries backend uses the existing RTAS work-area allocator for the error-injection parameter buffer. The work area is allocated only after ibm,open-errinjct succeeds and is released before ibm,close-errinjct. RTAS work areas reside below RTAS_INSTANTIATE_MAX, so the physical address fits in the 32-bit RTAS argument passed to firmware.
The complete open, inject, and close sequence is serialized with a mutex. RTAS busy and extended-delay return values are handled for all three calls. Once a session has been opened, ibm,close-errinjct is attempted even when parameter preparation or error injection fails.
The patches are organized as follows:
1. Handle the special ibm,open-errinjct return format in rtas_call().
2. Add pSeries RTAS error-injection validation and buffer helpers.
3. Implement RTAS-based pSeries EEH error injection.
4. Normalize VFIO sPAPR IOA error-injection addresses.
5. Explicitly map generic EEH error types to OPAL types on PowerNV.
Testing was performed on PowerVM with firmware providing the RTAS error-injection services and with corresponding QEMU support.
The corresponding QEMU v6 series is posted here:
https://lore.kernel.org/qemu-devel/[email protected]/
---
Change Log:
V5 -> V6:
https://lore.kernel.org/all/[email protected]/
* Addressed v5 review comments from Sashiko.
* Patch 1:
- No changes.
* Patch 2:
- Fix false BAR matches against unassigned or disabled BARs in validate_addr_mask_in_pe(): use pci_resource_len() instead of resource_size() (which returns 1 for zero-initialized BARs), and explicitly reject BARs with IORESOURCE_UNSET or IORESOURCE_DISABLED.
- Move validate_errinjct_args() and prepare_errinjct_buffer() to Patch 3 alongside their callers so each intermediate commit builds cleanly with CONFIG_WERROR=y without unused-function warnings.
* Patch 3:
- Include <asm/rtas-work-area.h> for the RTAS work-area API used by the pSeries error-injection implementation.
- Introduce validate_errinjct_args() and prepare_errinjct_buffer() here alongside their callers (moved from Patch 2).
- Fix eeh_break_device() visibility: keep it under #ifdef CONFIG_DEBUG_FS to prevent unused-function compiler warnings when CONFIG_DEBUG_FS=n.
- Add pseries_eeh_func_needs_bar_addr() to identify error functions whose addr argument is a BAR-backed MMIO/IO address. Guard validate_addr_mask_in_pe() in prepare_errinjct_buffer() with this predicate so config offsets and DMA/IOVA addresses bypass BAR membership validation.
- Demote the validate_addr_mask_in_pe() diagnostic message from pr_err() to pr_debug() since invalid user-supplied inputs do not represent internal kernel errors.
- Update prepare_errinjct_buffer() kernel-doc comments to document function-specific addr semantics (MEM/IO = BAR, CFG = offset, DMA = IOVA).
* Patch 4:
- Restrict address normalization to error functions carrying BAR-backed PCI MMIO/IO addresses (memory and IO load/store). Config-space and DMA functions bypass normalization rather than incorrectly returning -EINVAL.
- Fix false matches against unassigned/disabled BARs in vfio_spapr_eeh_normalize_addr() using pci_resource_len() and checking IORESOURCE_UNSET / IORESOURCE_DISABLED.
- Remove the misleading ", injection may fail" suffix from the pr_debug() message since the caller returns -EINVAL immediately.
- Update commit description to accurately reflect function-specific address domains.
* Patch 5:
- No changes.
V4 -> V5:
https://lore.kernel.org/all/[email protected]/
* Addressed v4 review comments from Sourabh Jain.
* Return -EINVAL when ibm,open-errinjct is used with an invalid return-cell count.
* Validate the ibm,open-errinjct return-cell count in sys_rtas() before entering RTAS and use the common status helper for last-error handling.
* Rework the pSeries helper description to describe validation and RTAS parameter-buffer construction first.
* Rename rtas_type to rtas_err_type to distinguish the mapped RTAS type from the generic EEH type.
* Keep common error-function validation in validate_errinjct_args() and remove duplicate check from prepare_errinjct_buffer().
* Protect PE device and BAR traversal with pci_rescan_remove_lock.
* Rename pseries_errinjct_mutex to errinjct_mutex.
* Allocate RTAS work area only after ibm,open-errinjct succeeds and release it before ibm,close-errinjct.
* Ensure ibm,close-errinjct is attempted after a successful open when parameter preparation or error injection subsequently fails.
* Remove caller-side RTAS work-area address check and truncation, relying on RTAS work-area allocation guarantees for the 32-bit RTAS argument.
* Remove unnecessary error message for unencodable 32-bit IOA address or mask and retain the buffer-ready debug message.
* Remove eeh_pe_inject_mmio_error() and its declaration after removing its last caller.
* Reorder the VFIO address-normalization patch after the pSeries RTAS implementation.
* Remove the one-use VFIO error-type helper and check IOA error types directly at call site.
* Treat only addr == 0 && mask == 0 as a no-address IOA injection.
* Limit address matching to standard PCI BARs and add debug message when supplied address does not match a BAR.
* Expand PowerNV commit message to explain generic EEH-to-OPAL type mapping and rename opal_type to opal_err_type.
* Preserve -ERANGE for unsupported PowerNV error types.
V3 -> V4:
https://lore.kernel.org/all/[email protected]/
* Use the RTAS work area allocator instead of adding a dedicated global error-injection buffer.
* Keep sys_rtas() raw return-cell semantics unchanged while handling the special ibm,open-errinjct return format in rtas_call().
* Treat ibm,open-errinjct with fewer than two return cells as invalid rtas_call() usage instead of interpreting rets[0] as status.
* Use normal Linux errno values in internal validation helpers instead of RTAS status values.
* Simplify the pSeries RTAS open/inject/close flow.
* Keep close-session failure as cleanup reporting without overwriting the primary operation status.
* Add VFIO sPAPR-side address normalization before dispatching IOA error injection to the platform EEH backend.
* Normalize IOA addresses based on the IOA error type, not only selected function codes.
* Keep the generic EEH ABI values unchanged and keep pSeries RTAS and PowerNV OPAL mappings explicit.
V2 -> V3:
* Fixed ibm,open-errinjct return handling to correctly process firmware responses.
* Allocate the error-injection buffer in RTAS-accessible memory instead of general kernel memory.
* Pass the physical address of the error-injection buffer to firmware (previously incorrect address type).
* Accept session token zero as a valid token (previously rejected erroneously).
* Handle RTAS busy and extended-delay return codes for open, inject, and close calls.
* Simplified the validation helper -- reduced complexity and removed redundant checks.
* Simplified the buffer-preparation helper for cleaner, more maintainable code.
* Validate that all required RTAS tokens are present before attempting to open a session.
* Added explicit generic EEH-to-OPAL error-type mapping for the PowerNV platform.
v1 -> v2:
https://lore.kernel.org/all/[email protected]/
* Addressed all review comments from Sourabh Jain
* Removed unnecessary empty line in rtas_call()
* Enhanced comment to explain PAPR specification requirements
* Corrected misleading comment about output handling
* Improved else block comment for better code clarity
* Fixed kernel test robot warnings
* Fixed kernel-doc warning for __maybe_unused parameter
* Confirmed sparse warnings are false positives (correct endianness handling)
* Added PowerNV platform abstraction layer (new Patch 5)
* Maps EEH error types to OPAL-specific types
* Simplifies type handling by direct variable update
* Improved code comments and documentation throughout
* Added Reported-by tags for kernel test robot findings
* Split into logical 5-patch series for better review
RFC -> v1:
https://lore.kernel.org/all/[email protected]/
* Initial 4-patch series
* Fixed PAPR ibm,open-errinjct output format (token,status order)
* Added pr_fmt handling for EEH subsystem compatibility
* Implemented comprehensive validation helpers
RFC:
https://lore.kernel.org/all/[email protected]/
* Initial RFC implementation
Narayana Murty N (5):
powerpc/rtas: Handle ibm,open-errinjct return format powerpc/pseries/eeh: Add RTAS error injection helpers powerpc/pseries/eeh: Implement RTAS-based EEH error injection vfio/spapr_tce: Normalize EEH IOA error injection addresses powerpc/powernv/eeh: Map generic EEH error types to OPAL
arch/powerpc/include/asm/eeh.h | 2 - arch/powerpc/kernel/eeh.c | 10 +- arch/powerpc/kernel/rtas.c | 78 +++- arch/powerpc/platforms/powernv/eeh-powernv.c | 22 +- arch/powerpc/platforms/pseries/eeh_pseries.c | 375 ++++++++++++++++++- drivers/vfio/vfio_iommu_spapr_tce.c | 140 +++++++ 6 files changed, 585 insertions(+), 42 deletions(-)
Signature · 1 line
2.54.0
PATCH v6 1/5 powerpc/rtas: Handle ibm,open-errinjct return format
ibm,open-errinjct uses a non-standard RTAS return layout:
rets[0] = session token (output parameter)
rets[1] = status code
Unlike all other RTAS functions which use:
rets[0] = status code
rets[1..] = output parameters
Add rtas_token_is_open_errinjct() to identify this call, and rtas_status_from_args() to extract status from the correct position.
Add an early guard in rtas_call() that rejects ibm,open-errinjct invocations where nret < 2, since reading rets[1] would be out of bounds. This is a kernel-side call-convention error, so -EINVAL is returned rather than an RTAS firmware status code:
if (rtas_token_is_open_errinjct(token) && nret < 2) {
WARN_ON_ONCE(1);
return -EINVAL;
}Adjust the output-copy loop so that for ibm,open-errinjct:
return value = rets[1] (status)
outputs[0] = rets[0] (session token)
For all other functions the existing convention is preserved:
return value = rets[0] (status)
outputs[0..] = rets[1..] (non-status outputs)
Move the "/* A -1 return code... */" comment immediately before the ret == -1 check so it documents the check it guards.
Also fix sys_rtas() last-error status detection: add the same early nret < 2 validation at the syscall boundary (returning -EINVAL) to avoid entering RTAS with an invalid ibm,open-errinjct return layout. After that guard, use rtas_status_from_args() to obtain the RTAS status for last-error handling.
The prominent user librtas already passes nret = 2 for this function as required by PAPR, so well-formed callers are unaffected.
Reference: OpenPOWER PAPR documentation
https://files.openpower.foundation/s/XFgfMaqLMD5Bcm8
--- arch/powerpc/kernel/rtas.c | 78 +++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 9 deletions(-)
arch/powerpc/kernel/rtas.c +69 -9
@@ -1117,6 +1117,28 @@ static bool token_is_restricted_errinjct(s32 token) token ==rtas_function_token (RTAS_FN_IBM_ERRINJCT ); } +/* +* ibm,open-errinjct uses a non-standard return layout: +* rets[0] = session token (output parameter) +* rets[1] = status code +* +* All other RTAS functions use the standard layout: +* rets[0] = status code +* rets[1..] = output parameters +*/ +static inline bool rtas_token_is_open_errinjct (int token) +{ +return token ==rtas_function_token (RTAS_FN_IBM_OPEN_ERRINJCT ); +} + +static int rtas_status_from_args (int token,struct rtas_args *args,int nret) +{ +if (rtas_token_is_open_errinjct (token)) +return be32_to_cpu (args->rets[1 ]); + +return nret >0 ?be32_to_cpu (args->rets[0 ]) :0 ; +} +/** * rtas_call() - Invoke an RTAS firmware function. * @token: Identifies the function being invoked.
@@ -1198,6 +1220,16 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)return -1 ; } +/* +* ibm,open-errinjct returns rets[0]=session_token, rets[1]=status. +* We need nret >= 2 to read status from rets[1]. Reject early if +* the caller forgot to account for the extra return cell. +*/ +if (rtas_token_is_open_errinjct (token) && nret <2 ) { +WARN_ON_ONCE (1 ); +return -EINVAL ; + } +if ((mfmsr () & (MSR_IR |MSR_DR )) != (MSR_IR |MSR_DR )) {WARN_ON_ONCE (1 );return -1 ;
@@ -1213,15 +1245,33 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)va_rtas_call_unlocked (args, token, nargs, nret, list);va_end (list); + ret =rtas_status_from_args (token, args, nret); +/* A -1 return code indicates that the last command couldn't -be completed due to a hardware error. */ -if (be32_to_cpu (args->rets[0 ]) ==-1 ) +* be completed due to a hardware error. +*/ +if (ret ==-1 ) buff_copy =__fetch_rtas_last_error (NULL ); -if (nret >1 && outputs !=NULL ) -for (i =0 ; i < nret-1 ; ++i) - outputs[i] =be32_to_cpu (args->rets[i +1 ]); - ret = (nret >0 ) ?be32_to_cpu (args->rets[0 ]) :0 ; +/* +* Copy non-status outputs to the caller's buffer. +* +* For ibm,open-errinjct the layout is: +* rets[0] = session token -> outputs[0] +* rets[1] = status (returned, not copied) +* +* For all other RTAS functions: +* rets[0] = status (returned, not copied) +* rets[1..nret-1] -> outputs[0..nret-2] +*/ +if (outputs) { +if (rtas_token_is_open_errinjct (token)) { + outputs[0 ] =be32_to_cpu (args->rets[0 ]); + }else if (nret >1 ) { +for (i =0 ; i < nret -1 ; ++i) + outputs[i] =be32_to_cpu (args->rets[i +1 ]); + } + }lockdep_unpin_lock (&rtas_lock, cookie);raw_spin_unlock_irqrestore (&rtas_lock, flags);
@@ -1896,6 +1946,14 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)if (block_rtas_call (func, nargs, &args))return -EINVAL ; +/* +* ibm,open-errinjct places status at rets[1]; reject calls with +* fewer than 2 return cells before entering RTAS, since reading +* rets[1] for the -1 sentinel would be out of bounds. +*/ +if (rtas_token_is_open_errinjct (token) && nret <2 ) +return -EINVAL ; +if (token_is_restricted_errinjct (token)) {int err;
@@ -1942,9 +2000,11 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)do_enter_rtas (&rtas_args); args = rtas_args; -/* A -1 return code indicates that the last command couldn't -be completed due to a hardware error. */ -if (be32_to_cpu (args.rets[0 ]) ==-1 ) +/* +* A -1 return code indicates that the last command couldn't +* be completed due to a hardware error. +*/ +if (rtas_status_from_args (token, &args, nret) ==-1 ) errbuf =__fetch_rtas_last_error (buff_copy);lockdep_unpin_lock (&rtas_lock, cookie);
Signature · 1 line
2.54.0
PATCH v6 2/5 powerpc/pseries/eeh: Add RTAS error injection helpers
In reply to Narayana Murty NPrepare 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.
--- arch/powerpc/platforms/pseries/eeh_pseries.c | 134 +++++++++++++++++++ 1 file changed, 134 insertions(+)
arch/powerpc/platforms/pseries/eeh_pseries.c +134 -0
@@ -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
Signature · 1 line
2.54.0
[email protected]
AI reviewRe: [PATCH v6 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] powerpc/pseries/eeh: static RTAS helpers defined without callers --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] powerpc/pseries/eeh: static RTAS helpers defined without callers
--
commit 8b171281b038661dc89a614c1b95db68271706f4
Author: Narayana Murty N <[email protected]>
powerpc/pseries/eeh: Add RTAS error injection helpers
This patch adds static RTAS error injection helper functions, pseries_eeh_type_to_rtas() and validate_addr_mask_in_pe(), to prepare the pSeries EEH backend for RTAS-based error injection.
Quoted 4 lines
arch/powerpc/platforms/pseries/eeh_pseries.c +0
-0
[ ... ]
Quoted 44 lines
arch/powerpc/platforms/pseries/eeh_pseries.c +40
-0@@ -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
arch/powerpc/platforms/pseries/eeh_pseries.c static int pseries_notify_resume(struct eeh_dev *edev) · line 841
Quoted 10 lines
arch/powerpc/platforms/pseries/eeh_pseries.c +10
-0+* 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)
[Severity: Low]
Does defining pseries_eeh_type_to_rtas() and validate_addr_mask_in_pe() in arch/powerpc/platforms/pseries/eeh_pseries.c without callers or __maybe_unused cause a -Werror=unused-function build failure at this commit?
The commit message notes that validate_errinjct_args() and prepare_errinjct_buffer() were deferred to the next patch ("powerpc/pseries/eeh:
Implement RTAS-based EEH error injection") and that pseries_eeh_err_inject() retains its existing MMIO body here so each intermediate commit builds cleanly with CONFIG_WERROR:
arch/powerpc/platforms/pseries/eeh_pseries.c:pseries_eeh_err_inject() {
struct eeh_dev *pdev;
/* Check on PCI error type */
if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64)
return -EINVAL;
...
}However, those deferred functions and the updated pseries_eeh_err_inject() are the only callers of pseries_eeh_type_to_rtas() and validate_addr_mask_in_pe(). When building with CONFIG_PPC_PSERIES=y and CONFIG_PPC_WERROR=y (in arch/powerpc/Kbuild) or CONFIG_WERROR=y (in scripts/Makefile.warn), won't this commit fail to compile with -Werror=unused-function until the follow-up patch adds their callers?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v6 3/5 powerpc/pseries/eeh: Implement RTAS-based EEH error injection
In reply to Narayana Murty NReplace the legacy MMIO stub in pseries_eeh_err_inject() with a full PAPR-compliant RTAS error injection path.
Serialize each ibm,open-errinjct / ibm,errinjct / ibm,close-errinjct session with errinjct_mutex because RTAS maintains state between these calls.
Once ibm,open-errinjct succeeds, ibm,close-errinjct is attempted after parameter preparation or ibm,errinjct failures. The RTAS work area is allocated after a successful open to avoid holding it while waiting for the mutex or if open fails.
VFIO EEH error injection exposes a generic userspace ABI. pSeries maps the generic EEH error types to RTAS ibm,errinjct encodings via pseries_eeh_type_to_rtas(). EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 are unchanged; their values are defined in arch/powerpc/include/uapi/asm/eeh.h and are not renumbered.
The RTAS work-area allocator guarantees that returned memory is within the range accessible by RTAS (below min(ppc64_rma_size, RTAS_INSTANTIATE_MAX), which is at most RTAS_INSTANTIATE_MAX = 1 GiB), so the physical address fits in the 32-bit RTAS argument without truncation.
Remove eeh_pe_inject_mmio_error() and its declaration; this patch removes its only caller. The only remaining caller of eeh_break_device() is eeh_dev_break_write() inside CONFIG_DEBUG_FS, so move eeh_break_device() back inside the CONFIG_DEBUG_FS block to avoid an unused-function warning when CONFIG_DEBUG_FS=n.
Also introduce validate_errinjct_args() and prepare_errinjct_buffer() here alongside their callers, so each intermediate commit builds cleanly with CONFIG_WERROR.
The addr argument to ibm,errinjct is function-dependent. Memory and IO load/store functions (EEH_ERR_FUNC_LD/ST_MEM_* and EEH_ERR_FUNC_LD/ST_IO_*) target BAR-backed PCI bus addresses; these are validated against the PE via validate_addr_mask_in_pe(). Config-space functions carry PCI config register offsets and DMA functions carry IOVA/TCE addresses; addr for these functions is passed directly to RTAS without BAR membership validation.
For the 32-bit RTAS encoding (RTAS_ERR_TYPE_IOA_BUS_ERROR), addr and mask are checked to fit in 32 bits before buffer encoding. This is an RTAS format constraint independent of function type.
Tested with corresponding QEMU patches:
https://lore.kernel.org/all/[email protected]/
--- arch/powerpc/include/asm/eeh.h | 2 - arch/powerpc/kernel/eeh.c | 10 +- arch/powerpc/platforms/pseries/eeh_pseries.c | 246 +++++++++++++++++-- 3 files changed, 228 insertions(+), 30 deletions(-)
arch/powerpc/include/asm/eeh.h +0 -2
@@ -310,8 +310,6 @@ int eeh_pe_reset(struct eeh_pe *pe, int option, bool include_passed);int eeh_pe_configure (struct eeh_pe *pe);int eeh_pe_inject_err (struct eeh_pe *pe,int type,int func,unsigned long addr,unsigned long mask); -int eeh_pe_inject_mmio_error (struct pci_dev *pdev); -/** * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. *
arch/powerpc/kernel/eeh.c +2 -8
@@ -1571,6 +1571,8 @@ static int proc_eeh_show(struct seq_file *m, void *v) }#endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_DEBUG_FS +static int eeh_break_device (struct pci_dev *pdev) {struct resource *bar =NULL ;
@@ -1664,14 +1666,6 @@ static int eeh_break_device(struct pci_dev *pdev)return 0 ; } -int eeh_pe_inject_mmio_error (struct pci_dev *pdev) -{ -return eeh_break_device (pdev); -} - -#ifdef CONFIG_DEBUG_FS - -static struct pci_dev *eeh_debug_lookup_pdev (struct file *filp,const char __user *user_buf,size_t count,loff_t *ppos)
arch/powerpc/platforms/pseries/eeh_pseries.c +226 -20
280 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH v6 4/5 vfio/spapr_tce: Normalize EEH IOA error injection addresses
In reply to Narayana Murty NVFIO_EEH_PE_INJECT_ERR receives a target address from userspace. For userspace such as QEMU, this address may be derived from the host Linux BAR resource. The platform EEH backends, however, expect the PCI/IOA bus address used by firmware error-injection interfaces.
Normalize IOA/MMIO error-injection addresses in the sPAPR VFIO EEH ioctl path before dispatching to eeh_pe_inject_err(). If the supplied address is already a PCI bus BAR address it is left unchanged. If it is a Linux resource address, translate it to the corresponding PCI bus address using the BAR-relative offset.
Normalization applies only to EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 error types whose error function targets a memory or IO BAR (load/store memory and load/store IO functions, func 0-3 and 6-9). Config-space functions carry PCI config register offsets and DMA functions carry IOVA/TCE addresses; these address domains are not PCI BAR addresses and must not be translated. Applying BAR normalization to them would incorrectly return -EINVAL, blocking injection entirely for those function codes.
An address and mask pair of zero represents a no-address injection (e.g. a function-level error with no specific IOA target) and is passed through without BAR translation.
Only standard BARs (BAR0..BAR5) are searched; ROM and bridge resources are not IOA bus-error targets. Unassigned or disabled BARs are skipped:pci_resource_len() returns 0 for zero-initialized BARs, and the IORESOURCE_UNSET and IORESOURCE_DISABLED flags are checked explicitly to avoid false matches against unassigned resources. Hold pci_rescan_remove_lock while traversing PE devices and their BAR resources to prevent PCI device removal from racing the lookup.
A pr_debug() is emitted if the address cannot be matched to any BAR, to aid debugging without producing noise in the normal path.
Keep the helper local to VFIO so the address semantics of other in-kernel EEH callers remain unchanged.
--- drivers/vfio/vfio_iommu_spapr_tce.c | 140 ++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+)
drivers/vfio/vfio_iommu_spapr_tce.c +140 -0
@@ -774,6 +774,131 @@ static long tce_iommu_create_default_window(struct tce_container *container)return ret; } +/* +* eeh_func_needs_bar_addr - return true for error functions whose addr +* argument is a PCI MMIO or IO BAR address. +* +* Memory and IO load/store functions target specific BAR addresses. +* Config-space functions carry PCI config register offsets, and DMA +* functions carry IOVA/TCE addresses; neither requires BAR translation. +*/ +static bool eeh_func_needs_bar_addr (int func) +{ +switch (func) { +case EEH_ERR_FUNC_LD_MEM_ADDR : +case EEH_ERR_FUNC_LD_MEM_DATA : +case EEH_ERR_FUNC_LD_IO_ADDR : +case EEH_ERR_FUNC_LD_IO_DATA : +case EEH_ERR_FUNC_ST_MEM_ADDR : +case EEH_ERR_FUNC_ST_MEM_DATA : +case EEH_ERR_FUNC_ST_IO_ADDR : +case EEH_ERR_FUNC_ST_IO_DATA : +return true; +default : +return false; + } +} + +static int vfio_spapr_eeh_normalize_addr (struct eeh_pe *pe, +unsigned long addr, +unsigned long mask, +unsigned long *normalized) +{ +struct pci_bus_region region; +struct eeh_dev *edev, *tmp; +struct pci_dev *pdev; +struct resource *res; +resource_size_t pci_start, pci_len; +resource_size_t res_start, res_len; +resource_size_t offset; +int bar, rc = -EINVAL ; + +if (!pe || !normalized) +return -EINVAL ; + +/* +* addr == 0 and mask == 0 indicates a no-address injection (e.g. +* function-level error with no specific IOA target). No BAR +* translation needed. +*/ +if (!addr && !mask) { + *normalized = addr; +return 0 ; + } + +/* +* Walk standard BARs (BAR0..BAR5) of all devices in the PE. +* Hold pci_rescan_remove_lock to protect pdev lifetime during the +* traversal: pci_stop_and_remove_bus_device() requires this lock. +* ROM and bridge resources are not IOA bus-error targets, so the +* search is limited to PCI_STD_NUM_BARS. +*/ +pci_lock_rescan_remove (); +eeh_pe_for_each_dev (pe, edev, tmp) { + pdev =eeh_dev_to_pci_dev (edev); +if (!pdev) +continue ; + +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. +* 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); + + pci_start = region.start; + pci_len =pci_resource_len (pdev, bar); + +/* +* Case 1: userspace already supplied PCI/IOA +* bus address. +*/ +if ((resource_size_t )addr >= pci_start && + ((resource_size_t )addr - pci_start) < pci_len) { + *normalized = addr; + rc =0 ; + goto done; + } + +/* +* Case 2: userspace supplied Linux resource/CPU +* address. Convert it back to PCI/IOA bus address +* before calling the platform EEH backend. +*/ + res_start = res->start; + res_len =pci_resource_len (pdev, bar); + +if ((resource_size_t )addr >= res_start && + ((resource_size_t )addr - res_start) < res_len) { + offset = (resource_size_t )addr - res_start; + *normalized = region.start + offset; + rc =0 ; + goto done; + } + } + } +done: +pci_unlock_rescan_remove (); + +if (rc) +pr_debug ("vfio EEH: addr=0x%lx not found in any PCI bus BAR of PE\n" , + addr); +return rc; +} +static long vfio_spapr_ioctl_eeh_pe_op (struct iommu_group *group,unsigned long arg) {
@@ -818,6 +943,21 @@ static long vfio_spapr_ioctl_eeh_pe_op(struct iommu_group *group,if (copy_from_user (&op, (void __user *)arg, minsz))return -EFAULT ; +if ((op.err.type ==EEH_ERR_TYPE_32 || + op.err.type ==EEH_ERR_TYPE_64 ) && +eeh_func_needs_bar_addr (op.err.func)) { +unsigned long normalized; +int ret; + + ret =vfio_spapr_eeh_normalize_addr (pe, op.err.addr, + op.err.mask, + &normalized); +if (ret) +return ret; + + op.err.addr = normalized; + } +return eeh_pe_inject_err (pe, op.err.type, op.err.func, op.err.addr, op.err.mask);default :
Signature · 1 line
2.54.0
PATCH v6 5/5 powerpc/powernv/eeh: Map generic EEH error types to OPAL
In reply to Narayana Murty Neeh_pe_inject_err() is called with generic EEH error types defined in the VFIO/EEH userspace ABI (EEH_ERR_TYPE_32, EEH_ERR_TYPE_64). The PowerNV backend previously compared these generic values directly against OPAL firmware constants (OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR and OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64).
This is fragile: it relies on the generic EEH ABI values being numerically identical to OPAL-specific values, and would silently misbehave if they ever diverged.
Add an explicit mapping in pnv_eeh_err_inject() that translates:
EEH_ERR_TYPE_32 -> OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR EEH_ERR_TYPE_64 -> OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64
Return -ERANGE for unsupported error types, preserving the behavior of the original code. Decouples the VFIO/EEH interface from OPAL implementation details.
No change to existing userspace ABI or PowerNV behavior.
--- arch/powerpc/platforms/powernv/eeh-powernv.c | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
arch/powerpc/platforms/powernv/eeh-powernv.c +17 -5
@@ -1167,12 +1167,24 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func, {struct pci_controller *hose = pe->phb;struct pnv_phb *phb = hose->private_data; +int opal_err_type;s64 rc; -if (type !=OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR && - type !=OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64 ) { -pr_warn ("%s: Invalid error type %d\n" , - __func__, type); +/* +* eeh_pe_inject_err() receives generic EEH error types from the +* VFIO/EEH userspace ABI. The PowerNV backend must not rely on +* these generic values being numerically identical to OPAL firmware +* values. Translate to OPAL-specific types before calling firmware. +*/ +switch (type) { +case EEH_ERR_TYPE_32 : + opal_err_type =OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR ; +break ; +case EEH_ERR_TYPE_64 : + opal_err_type =OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64 ; +break ; +default : +pr_warn ("%s: Invalid error type %d\n" , __func__, type);return -ERANGE ; }
@@ -1192,7 +1204,7 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func,/* Do error injection */ rc =opal_pci_err_inject (phb->opal_id, pe->addr, - type, func, addr, mask); + opal_err_type, func, addr, mask);if (rc !=OPAL_SUCCESS ) { pr_warn("%s: Failure %lld injecting error " "%d-%d to PHB#%x-PE#%x\n",
Signature · 1 line
2.54.0
Greetings,
I have tested this patch on my KVM guest environment. I am able to inject EEH errors from host console as well as on guest console using location code for a VFIO passthrough device.
Thank you
Tasmiya