* [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries
@ 2026-10-19 10:18 Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
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 current 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. 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 organised 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 the corresponding QEMU support.
The corresponding QEMU v5 series is posted here:
https://lore.kernel.org/qemu-devel/20260901174110.45356-1-nnmlinux@linux.ibm.com/
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
Change Log:
V4 -> V5:
* Addressed the 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 the 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 the 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 the 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 the caller-side RTAS work-area address check and truncation and
rely on the RTAS work-area allocation guarantee for the 32-bit RTAS
argument.
* Remove the unnecessary error message for an 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 the IOA error types
directly at the call site.
* Treat only addr == 0 && mask == 0 as a no-address IOA injection.
* Limit address matching to standard PCI BARs and add a debug message
when the supplied address does not match a BAR.
* Expand the PowerNV commit message to explain the 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/20260831065441.48654-1-nnmlinux@linux.ibm.com/
* 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/20260527072433.94510-1-nnmlinux@linux.ibm.com/
* 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/20251205094510.4671-1-nnmlinux@linux.ibm.com/
* 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/20251107091009.43034-1-nnmlinux@linux.ibm.com/
* 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 | 5 -
arch/powerpc/kernel/rtas.c | 78 ++++++-
arch/powerpc/platforms/powernv/eeh-powernv.c | 22 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 335 +++++++++++++++++++++++++--
drivers/vfio/vfio_iommu_spapr_tce.c | 105 +++++++++
6 files changed, 503 insertions(+), 44 deletions(-)
base-commit: 2f2f7eeef8b0a1a9b9ca998231419873ccae8d4e
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5 1/5] powerpc/rtas: Handle ibm,open-errinjct return format
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
@ 2026-10-19 10:18 ` Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers Narayana Murty N
` (3 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
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
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/kernel/rtas.c | 78 +++++++++++++++++++++++++++++++++-----
1 file changed, 69 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 8d81c1e7a8db..38f3d08d4228 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -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);
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
@ 2026-10-19 10:18 ` Narayana Murty N
2026-10-19 10:34 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
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 <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202512101130.EYUo0oZx-lkp@intel.com/
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
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 <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,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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers Narayana Murty N
@ 2026-10-19 10:18 ` Narayana Murty N
2026-10-19 10:26 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 5/5] powerpc/powernv/eeh: Map generic EEH error types to OPAL Narayana Murty N
4 siblings, 1 reply; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
Replace 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 the now-unused eeh_pe_inject_mmio_error() helper and its
declaration; this patch removes its only caller.
Tested with corresponding QEMU patches:
https://lore.kernel.org/all/20251029150618.186803-1-nnmlinux@linux.ibm.com/
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 2 -
arch/powerpc/kernel/eeh.c | 5 -
arch/powerpc/platforms/pseries/eeh_pseries.c | 114 +++++++++++++++----
3 files changed, 91 insertions(+), 30 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index b7ebb4ac2c71..083ac58e7eb7 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -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.
*
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index bb836f02101c..369aad298f75 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1664,11 +1664,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
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 999b3065e09b..52d001042663 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -24,6 +24,7 @@
#include <linux/proc_fs.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
+#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
@@ -35,6 +36,7 @@
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include <asm/rtas.h>
+#include <asm/rtas-work-area.h>
/* RTAS tokens */
static int ibm_set_eeh_option;
@@ -1006,41 +1008,107 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
return 0;
}
+/* Serializes the open/inject/close RTAS error injection session */
+static DEFINE_MUTEX(errinjct_mutex);
+
/**
* pseries_eeh_err_inject - Inject specified error to the indicated PE
* @pe: the indicated PE
- * @type: error type
- * @func: specific error type
- * @addr: address
- * @mask: address mask
- * The routine is called to inject specified error, which is
- * determined by @type and @func, to the indicated PE
+ * @type: generic EEH error type (EEH_ERR_TYPE_32 or EEH_ERR_TYPE_64)
+ * @func: specific error function
+ * @addr: address argument (type-dependent, may be zero)
+ * @mask: address mask (type-dependent, may be zero)
+ *
+ * Implements PAPR-compliant error injection using:
+ * ibm,open-errinjct -> ibm,errinjct -> ibm,close-errinjct
+ *
+ * errinjct_mutex serializes the open/inject/close session sequence
+ * because RTAS maintains state between these calls. A short-lived
+ * RTAS work area is allocated after a successful open and freed before
+ * close.
+ *
+ * Return: 0 on success, negative errno on failure.
*/
static int pseries_eeh_err_inject(struct eeh_pe *pe, int type, int func,
unsigned long addr, unsigned long mask)
{
- struct eeh_dev *pdev;
+ struct rtas_work_area *area;
+ phys_addr_t area_phys;
+ void *buf;
+ int open_token, errinjct_token, close_token;
+ int session_token;
+ int rtas_err_type;
+ int close_rc;
+ int rc;
+
+ rc = validate_errinjct_args(pe, type, func, addr, mask);
+ if (rc)
+ return rc;
- /* Check on PCI error type */
- if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64)
+ rtas_err_type = pseries_eeh_type_to_rtas(type);
+ if (rtas_err_type < 0)
return -EINVAL;
- switch (func) {
- case EEH_ERR_FUNC_LD_MEM_ADDR:
- case EEH_ERR_FUNC_LD_MEM_DATA:
- case EEH_ERR_FUNC_ST_MEM_ADDR:
- case EEH_ERR_FUNC_ST_MEM_DATA:
- /* injects a MMIO error for all pdev's belonging to PE */
- pci_lock_rescan_remove();
- list_for_each_entry(pdev, &pe->edevs, entry)
- eeh_pe_inject_mmio_error(pdev->pdev);
- pci_unlock_rescan_remove();
- break;
- default:
- return -ERANGE;
+ open_token = rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT);
+ errinjct_token = rtas_function_token(RTAS_FN_IBM_ERRINJCT);
+ close_token = rtas_function_token(RTAS_FN_IBM_CLOSE_ERRINJCT);
+
+ if (open_token == RTAS_UNKNOWN_SERVICE ||
+ errinjct_token == RTAS_UNKNOWN_SERVICE ||
+ close_token == RTAS_UNKNOWN_SERVICE)
+ return -ENODEV;
+
+ mutex_lock(&errinjct_mutex);
+
+ do {
+ rc = rtas_call(open_token, 0, 2, &session_token);
+ } while (rtas_busy_delay(rc));
+
+ if (rc) {
+ pr_err("ibm,open-errinjct failed: status=%d\n", rc);
+ rc = rtas_error_rc(rc);
+ goto out_unlock;
}
- return 0;
+ area = rtas_work_area_alloc(RTAS_ERRINJCT_BUF_SIZE);
+ buf = rtas_work_area_raw_buf(area);
+ area_phys = rtas_work_area_phys(area);
+
+ rc = prepare_errinjct_buffer(buf, pe, rtas_err_type, func, addr, mask);
+ if (rc)
+ goto out_free_area;
+
+ /*
+ * RTAS work areas are allocated below RTAS_INSTANTIATE_MAX, so
+ * area_phys fits in the 32-bit RTAS argument.
+ */
+ do {
+ rc = rtas_call(errinjct_token, 3, 1, NULL,
+ rtas_err_type, session_token,
+ (u32)area_phys);
+ } while (rtas_busy_delay(rc));
+
+ if (rc) {
+ pr_err("ibm,errinjct failed: status=%d\n", rc);
+ rc = rtas_error_rc(rc);
+ }
+
+out_free_area:
+ rtas_work_area_free(area);
+
+ do {
+ close_rc = rtas_call(close_token, 1, 1, NULL, session_token);
+ } while (rtas_busy_delay(close_rc));
+
+ if (close_rc) {
+ pr_warn("ibm,close-errinjct failed: status=%d\n", close_rc);
+ if (!rc)
+ rc = rtas_error_rc(close_rc);
+ }
+
+out_unlock:
+ mutex_unlock(&errinjct_mutex);
+ return rc;
}
static struct eeh_ops pseries_eeh_ops = {
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
` (2 preceding siblings ...)
2026-10-19 10:18 ` [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
@ 2026-10-19 10:18 ` Narayana Murty N
2026-10-19 10:35 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 5/5] powerpc/powernv/eeh: Map generic EEH error types to OPAL Narayana Murty N
4 siblings, 1 reply; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
VFIO_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 to EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64; other
error types do not carry an IOA bus address.
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. 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 are unchanged. This provides common handling for
both pseries and PowerNV backends.
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
drivers/vfio/vfio_iommu_spapr_tce.c | 105 ++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 1c0eec228cc6..dad9ba54bae6 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -774,6 +774,97 @@ static long tce_iommu_create_default_window(struct tce_container *container)
return ret;
}
+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];
+
+ if (!resource_size(res))
+ continue;
+
+ if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
+ continue;
+
+ pcibios_resource_to_bus(pdev->bus, ®ion, res);
+
+ pci_start = region.start;
+ pci_len = resource_size(res);
+
+ /*
+ * 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 = resource_size(res);
+
+ 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, injection may fail\n",
+ addr);
+ return rc;
+}
+
static long vfio_spapr_ioctl_eeh_pe_op(struct iommu_group *group,
unsigned long arg)
{
@@ -818,6 +909,20 @@ 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) {
+ 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:
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 5/5] powerpc/powernv/eeh: Map generic EEH error types to OPAL
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
` (3 preceding siblings ...)
2026-10-19 10:18 ` [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
@ 2026-10-19 10:18 ` Narayana Murty N
4 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-10-19 10:18 UTC (permalink / raw)
To: Mahesh J Salgaonkar, Madhavan Srinivasan, Oliver O'Halloran,
Timothy Pearson, Alex Williamson
Cc: Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, linuxppc-dev, linux-kernel, kvm,
sbhat, harshpb
eeh_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.
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-powernv.c | 22 +++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index db3370d1673c..01dae760cecb 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -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",
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection
2026-10-19 10:18 ` [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
@ 2026-10-19 10:26 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-19 10:26 UTC (permalink / raw)
To: Narayana Murty N; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] powerpc/eeh: unused function eeh_break_device() when CONFIG_DEBUG_FS=n
--
commit f6dd99f31147847bdca67c87e513a0d4e111ae24
Author: Narayana Murty N <nnmlinux@linux.ibm.com>
powerpc/pseries/eeh: Implement RTAS-based EEH error injection
This commit replaces the legacy MMIO stub in pseries_eeh_err_inject() with
a PAPR-compliant RTAS error injection sequence using ibm,open-errinjct,
ibm,errinjct, and ibm,close-errinjct. It also removes the now-unused
eeh_pe_inject_mmio_error() helper.
> diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
> index bb836f02101cf..369aad298f752 100644
> --- a/arch/powerpc/kernel/eeh.c
> +++ b/arch/powerpc/kernel/eeh.c
> @@ -1664,11 +1664,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
>
>
[Severity: Low]
Previously, commit 3af2e2f68cc6 ("powerpc/pseries/eeh: move
pseries_eeh_err_inject() outside CONFIG_DEBUG_FS block") moved
eeh_break_device() outside the CONFIG_DEBUG_FS block in
arch/powerpc/kernel/eeh.c so that eeh_pe_inject_mmio_error() could call it
when CONFIG_DEBUG_FS=n.
With eeh_pe_inject_mmio_error() removed, the only remaining caller of
eeh_break_device() is eeh_dev_break_write(), which is inside the
CONFIG_DEBUG_FS block:
arch/powerpc/kernel/eeh.c:eeh_dev_break_write() {
...
ret = eeh_break_device(pdev);
...
}
When building with CONFIG_EEH=y and CONFIG_DEBUG_FS=n, won't leaving
eeh_break_device() outside CONFIG_DEBUG_FS trigger an unused-function
compiler warning? Should eeh_break_device() be moved back inside the
CONFIG_DEBUG_FS block?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019101813.45085-1-nnmlinux@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers
2026-10-19 10:18 ` [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers Narayana Murty N
@ 2026-10-19 10:34 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-19 10:34 UTC (permalink / raw)
To: Narayana Murty N; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] powerpc/pseries/eeh: validate_addr_mask_in_pe() matches unassigned BARs
- [Low] powerpc/pseries/eeh: unused static helpers in eeh_pseries.c fail -Werror build
--
commit 0a875c4e62df7429dee888710961a7609ab3e1b1
Author: Narayana Murty N <nnmlinux@linux.ibm.com>
powerpc/pseries/eeh: Add RTAS error injection helpers
This patch adds helper functions to arch/powerpc/platforms/pseries/eeh_pseries.c
to translate generic EEH error types to RTAS encodings, validate error
injection arguments and PCI bus BAR addresses against the target PE, and
populate the PAPR ibm,errinjct work buffer.
> diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
> index b12ef382fec70..999b3065e09b6 100644
> --- a/arch/powerpc/platforms/pseries/eeh_pseries.c
> +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
[ ... ]
> @@ -786,6 +789,223 @@ static int pseries_notify_resume(struct eeh_dev *edev)
> }
> #endif
[ ... ]
> +static int validate_addr_mask_in_pe(struct eeh_pe *pe, unsigned long addr,
> + unsigned long mask)
> +{
[ ... ]
> + 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);
[Severity: Medium]
Can validate_addr_mask_in_pe() falsely match unassigned, disabled, or
zero-initialized BARs when called via:
eeh_pe_inject_err()
-> pseries_eeh_err_inject()
-> prepare_errinjct_buffer()
-> validate_addr_mask_in_pe()
First, resource_size(res) computes res->end - res->start + 1, which
evaluates to 1 rather than 0 when res->start == 0 and res->end == 0
(unlike pci_resource_len(pdev, bar)).
Second, when a standard BAR is sized but unassigned on powerpc, such as in
pcibios_fixup_resources():
res->end -= res->start;
res->start = 0;
res->flags |= IORESOURCE_UNSET;
res->flags retains IORESOURCE_MEM or IORESOURCE_IO alongside
IORESOURCE_UNSET, with a zero-based range [0, size - 1].
When pcibios_resource_to_bus() is called on an IORESOURCE_UNSET resource:
resource_list_for_each_entry(window, &bridge->windows) {
if (resource_contains(window->res, res)) {
offset = window->offset;
break;
}
}
region->start = res->start - offset;
region->end = res->end - offset;
resource_contains() returns false because it rejects IORESOURCE_UNSET, so
window->offset is not subtracted and region.start is set to 0 with
region.end set to size - 1 (or left as the untranslated CPU resource
address if of_pci_parse_addrs() shifted res by window->offset without
zero-basing it). Any caller-supplied addr in [0, size) (including addr = 0
with a non-zero mask) will then match the unassigned BAR.
Should this check pci_resource_len(pdev, bar) and reject BARs with
IORESOURCE_UNSET | IORESOURCE_DISABLED set in res->flags, similar to
eeh_break_device()?
> +
> + bus_start = region.start;
> + bus_len = resource_size(res);
[ ... ]
> +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;
> +}
> +
[ ... ]
> +static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
> + int rtas_err_type, int func,
> + unsigned long addr, unsigned long mask)
> +{
[Severity: Low]
Does compiling arch/powerpc/platforms/pseries/eeh_pseries.o at this commit
fail with -Werror=unused-function when CONFIG_PPC_WERROR=y or
CONFIG_WERROR=y is enabled?
Both validate_errinjct_args() and prepare_errinjct_buffer() are defined as
static without __maybe_unused, and pseries_eeh_err_inject() does not call
them until the subsequent patch in the series ("powerpc/pseries/eeh:
Implement RTAS-based EEH error injection").
The commit message notes:
"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."
Should validate_errinjct_args() and prepare_errinjct_buffer() be marked
__maybe_unused in this patch (or folded into the next patch) so bisection
does not hit a build failure under -Werror?
> + __be64 *buf64 = (__be64 *)buf;
> + __be32 *buf32 = (__be32 *)buf;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019101813.45085-1-nnmlinux@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses
2026-10-19 10:18 ` [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
@ 2026-10-19 10:35 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-19 10:35 UTC (permalink / raw)
To: Narayana Murty N; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] vfio/spapr_tce: vfio_spapr_ioctl_eeh_pe_op() breaks non-BAR error injection
- [Medium] vfio/spapr_tce: vfio_spapr_eeh_normalize_addr() matches unset PCI BARs
--
commit 02ecc4e02395ee3674eee2a40d451772c566a3ad
Author: Narayana Murty N <nnmlinux@linux.ibm.com>
vfio/spapr_tce: Normalize EEH IOA error injection addresses
This patch normalizes IOA/MMIO error-injection addresses in the sPAPR
VFIO EEH ioctl path before dispatching to eeh_pe_inject_err(), translating
host Linux BAR resource addresses to PCI bus addresses.
> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
> index 1c0eec228cc6f..dad9ba54bae60 100644
> --- a/drivers/vfio/vfio_iommu_spapr_tce.c
> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
> @@ -774,6 +774,97 @@ static long tce_iommu_create_default_window(struct tce_container *container)
> return ret;
> }
>
> +static int vfio_spapr_eeh_normalize_addr(struct eeh_pe *pe,
> + unsigned long addr,
> + unsigned long mask,
> + unsigned long *normalized)
> +{
[ ... ]
> + 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];
> +
> + if (!resource_size(res))
> + continue;
> +
> + if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
> + continue;
> +
> + pcibios_resource_to_bus(pdev->bus, ®ion, res);
[Severity: Medium]
Can vfio_spapr_eeh_normalize_addr() match unassigned or disabled BARs here?
On PowerPC, unassigned BARs in pcibios_fixup_resources() and
alloc_resource() in arch/powerpc/kernel/pci-common.c have res->start = 0,
res->end = size - 1, and IORESOURCE_UNSET set alongside IORESOURCE_MEM or
IORESOURCE_IO:
arch/powerpc/kernel/pci-common.c:pcibios_fixup_resources() {
...
res->end -= res->start;
res->start = 0;
res->flags |= IORESOURCE_UNSET;
...
}
Because resource_size(res) is non-zero and IORESOURCE_UNSET is not checked,
vfio_spapr_eeh_normalize_addr() passes res to pcibios_resource_to_bus().
In drivers/pci/host-bridge.c, pcibios_resource_to_bus() calls
resource_contains(window->res, res), which returns false when
IORESOURCE_UNSET is set, leaving offset = 0 and setting region.start =
res->start.
When a device in the PE has an unassigned standard BAR, wouldn't any
supplied addr in [0, size) match Case 1 on the unassigned BAR, or if an
unassigned BAR retained res->start = window->offset from
of_pci_parse_addrs(), cause a valid CPU resource address of a later BAR at
the start of the PHB window to match Case 1 on the unset BAR and skip
translation?
For comparison, eeh_break_device() in arch/powerpc/kernel/eeh.c skips
such resources before matching BARs. Should this loop also skip BARs
where !res->start or res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)?
> +
> + pci_start = region.start;
> + pci_len = resource_size(res);
[ ... ]
> +done:
> + pci_unlock_rescan_remove();
> +
> + if (rc)
> + pr_debug("vfio EEH: addr=0x%lx not found in any PCI bus BAR of PE, injection may fail\n",
> + addr);
> + return rc;
> +}
> +
> static long vfio_spapr_ioctl_eeh_pe_op(struct iommu_group *group,
> unsigned long arg)
> {
> @@ -818,6 +909,20 @@ 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) {
> + 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:
[Severity: Medium]
Does calling vfio_spapr_eeh_normalize_addr() for all EEH_ERR_TYPE_32 and
EEH_ERR_TYPE_64 requests break non-BAR error injections such as DMA and
PCI config-space errors?
In arch/powerpc/include/uapi/asm/eeh.h, EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64
are the only two error types and are used across all op.err.func values
(EEH_ERR_FUNC_MIN through EEH_ERR_FUNC_MAX), including PCI config-space
error functions (EEH_ERR_FUNC_LD_CFG_ADDR, EEH_ERR_FUNC_LD_CFG_DATA,
EEH_ERR_FUNC_ST_CFG_ADDR, EEH_ERR_FUNC_ST_CFG_DATA) and DMA error functions
(EEH_ERR_FUNC_DMA_RD_* and EEH_ERR_FUNC_DMA_WR_*).
When userspace invokes VFIO_EEH_PE_INJECT_ERR for a DMA or config-space
error function with a non-zero op.err.addr (such as an IOVA in the TCE
window or a config offset) or a non-zero op.err.mask,
vfio_spapr_eeh_normalize_addr() searches standard MEM/IO BARs, fails to
find a match, and returns -EINVAL, causing vfio_spapr_ioctl_eeh_pe_op() to
abort before calling eeh_pe_inject_err() (or corrupts op.err.addr if the
DMA address happens to fall within a host BAR range).
Also, notice that the pr_debug() in vfio_spapr_eeh_normalize_addr() says
"injection may fail", whereas vfio_spapr_ioctl_eeh_pe_op() immediately
returns ret (-EINVAL) without attempting injection. Should BAR address
normalization be restricted to MMIO/PIO op.err.func values, or pass the
address through when it is not a BAR-targeted function?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019101813.45085-1-nnmlinux@linux.ibm.com?part=4
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-19 10:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 10:18 [PATCH v5 0/5] powerpc: Add RTAS based error injection support for pSeries Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
2026-10-19 10:18 ` [PATCH v5 2/5] powerpc/pseries/eeh: Add RTAS error injection helpers Narayana Murty N
2026-10-19 10:34 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 3/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
2026-10-19 10:26 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 4/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
2026-10-19 10:35 ` sashiko-bot
2026-10-19 10:18 ` [PATCH v5 5/5] powerpc/powernv/eeh: Map generic EEH error types to OPAL Narayana Murty N
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.