* [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries
@ 2026-07-21 3:38 Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
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, prepares the
firmware work buffer, performs the requested injection, 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 now
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.
The series also handles the unusual return format of
ibm,open-errinjct:
rets[0] = error-injection session token
rets[1] = RTAS status
rtas_call() now returns rets[1] as the status and places the session
token in outputs[0], preserving the normal kernel rtas_call()
convention.
sys_rtas() is intentionally unchanged because it exposes the raw RTAS
return cells to userspace. Userspace therefore continues to receive
the session token and status in their PAPR-defined positions.
The RTAS work buffer is allocated during RTAS initialization below:
min(ppc64_rma_size, RTAS_INSTANTIATE_MAX)
using the same accessible-memory limit used for rtas_rmo_buf. The
kernel populates the buffer through its virtual mapping but passes its
physical address 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. A session token value of zero is accepted, and session
state is tracked independently from the token value.
The patches are organised as follows:
Handle the special ibm,open-errinjct return format in rtas_call().
Allocate an RTAS-accessible error-injection work buffer.
Add pSeries RTAS parameter validation and buffer encoding helpers.
Implement RTAS-based pSeries EEH error injection.
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:
https://lore.kernel.org/qemu-devel/20260520095446.64206-1-nnmlinux@linux.ibm.com/
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
Narayana Murty N (5):
powerpc/rtas: Handle ibm,open-errinjct return format
powerpc/rtas: Allocate ibm,errinjct buffer below RTAS limit
powerpc/pseries/eeh: Add RTAS error validation helpers
powerpc/pseries/eeh: Implement RTAS-based EEH error injection
powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL
arch/powerpc/include/asm/rtas.h | 26 ++
arch/powerpc/include/uapi/asm/eeh.h | 24 +-
arch/powerpc/kernel/rtas.c | 68 +++-
arch/powerpc/platforms/powernv/eeh-powernv.c | 36 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 367 +++++++++++++++++--
5 files changed, 483 insertions(+), 38 deletions(-)
Change Log:
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
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/5] powerpc/rtas: Handle ibm,open-errinjct return format
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
@ 2026-07-21 3:38 ` Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 2/5] powerpc/rtas: Allocate ibm,errinjct buffer below RTAS limit Narayana Murty N
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
PAPR specifies that ibm,open-errinjct has a unique return-cell layout:
rets[0] = injection session token (output parameter)
rets[1] = status code
This differs from every other RTAS call, where:
rets[0] = status code
rets[1..] = output parameters
As a result, the existing rtas_call() convention — return value is the
RTAS status, outputs[] receives the non-status output values — must be
preserved while correctly extracting status from rets[1] for this one
call.
Add rtas_token_is_open_errinjct() and rtas_status_from_args() helpers.
rtas_status_from_args() selects the correct status cell based on the
token, and the output-copy loop in rtas_call() is updated so that for
ibm,open-errinjct:
rtas_call() return = rets[1] (RTAS status)
outputs[0] = rets[0] (session token)
For all other calls the behaviour is unchanged: return value is rets[0]
and outputs[] receives rets[1..nret-1].
The sys_rtas userspace path is not modified: copy_to_user() still
copies raw RTAS return cells (rets[0..nret-1]) to userspace.
Callers passing a single output int (nret == 2) are safe because we
write at most nret-1 values into outputs[], never all nret cells.
Also move the '/* A -1 return code...*/' comment to immediately precede
the if (ret == -1) check it describes, and remove the redundant stale
else branch that re-assigned ret.
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 | 51 ++++++++++++++++++++++++++++++++------
1 file changed, 44 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 8d81c1e7a8db..27d53f34494d 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -1117,6 +1117,29 @@ static bool token_is_restricted_errinjct(s32 token)
token == rtas_function_token(RTAS_FN_IBM_ERRINJCT);
}
+/**
+ * rtas_token_is_open_errinjct() - Test whether @token identifies ibm,open-errinjct.
+ */
+static bool rtas_token_is_open_errinjct(int token)
+{
+ return token == rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT);
+}
+
+/**
+ * rtas_status_from_args() - Extract the RTAS status code from a completed
+ * call's return-cell array.
+ *
+ * For ibm,open-errinjct the status lives in rets[1]; for every other
+ * RTAS function it lives in rets[0].
+ */
+static int rtas_status_from_args(int token, struct rtas_args *args, int nret)
+{
+ if (rtas_token_is_open_errinjct(token) && nret > 1)
+ 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.
@@ -1213,15 +1236,29 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
va_rtas_call_unlocked(args, token, nargs, nret, list);
va_end(list);
- /* 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)
+ 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 (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;
+ if (nret > 1 && outputs != NULL) {
+ if (rtas_token_is_open_errinjct(token)) {
+ /*
+ * ibm,open-errinjct: rets[0]=session token, rets[1]=status.
+ * Expose session token in outputs[0]; skip rets[1] (status).
+ */
+ outputs[0] = be32_to_cpu(args->rets[0]);
+ for (i = 1; i < nret - 1; ++i)
+ outputs[i] = be32_to_cpu(args->rets[i + 1]);
+ } else {
+ 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);
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 2/5] powerpc/rtas: Allocate ibm,errinjct buffer below RTAS limit
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
@ 2026-07-21 3:38 ` Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers Narayana Murty N
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
ibm,errinjct requires a caller-provided work buffer whose physical
address is passed to RTAS firmware.
A static C array such as:
char rtas_errinjct_buf[1024] __aligned(SZ_1K);
only guarantees alignment, not physical placement. If the array lands
above the RTAS-safe range (RTAS_INSTANTIATE_MAX, 1 GB) or above 4 GB,
RTAS receives a truncated or invalid address and the injection call
will fail silently or corrupt memory.
Instead, introduce rtas_errinjct_buf as a global unsigned long storing
the physical address allocated during rtas_initialize() using
memblock_phys_alloc_range() with the same rtas_region upper bound used
for rtas_rmo_buf. This matches the existing placement model for
RTAS-accessible buffers and guarantees the physical address fits in 32
bits.
Usage:
void *buf = __va(rtas_errinjct_buf); /* kernel VA to fill */
u32 buf_phys = lower_32_bits(rtas_errinjct_buf); /* PA for RTAS */
Always check upper_32_bits(rtas_errinjct_buf) == 0 before passing the
lower 32 bits to RTAS.
Add rtas_errinjct_mutex to serialise the complete open-session /
inject / close-session firmware call sequence. A mutex is required
because the sequence involves multiple rtas_call() invocations with
possible busy/extended-delay retries that may sleep.
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/include/asm/rtas.h | 26 ++++++++++++++++++++++++++
arch/powerpc/kernel/rtas.c | 17 +++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index d046bbd5017d..59e3c1296018 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -4,6 +4,7 @@
#ifdef __KERNEL__
#include <linux/mutex.h>
+#include <linux/sizes.h>
#include <linux/spinlock.h>
#include <asm/page.h>
#include <asm/rtas-types.h>
@@ -519,6 +520,31 @@ int rtas_get_error_log_max(void);
extern spinlock_t rtas_data_buf_lock;
extern char rtas_data_buf[RTAS_DATA_BUF_SIZE];
+/*
+ * RTAS error-injection work buffer.
+ *
+ * ibm,errinjct requires a caller-provided work buffer whose physical
+ * address is passed to firmware. A static C array only guarantees
+ * alignment, not physical placement; if it lands above the RTAS-safe
+ * range or above 4 GB, RTAS receives a truncated or bogus address.
+ *
+ * rtas_errinjct_buf stores the physical address allocated during
+ * rtas_initialize() using memblock_phys_alloc_range() with the same
+ * rtas_region upper bound used for rtas_rmo_buf, matching the existing
+ * placement model for RTAS-accessible buffers.
+ *
+ * Use __va(rtas_errinjct_buf) to obtain the kernel virtual address for
+ * filling the buffer, and lower_32_bits(rtas_errinjct_buf) to pass the
+ * physical address to RTAS (after checking upper_32_bits() == 0).
+ *
+ * rtas_errinjct_mutex must be held across the complete
+ * ibm,open-errinjct / ibm,errinjct / ibm,close-errinjct sequence.
+ */
+#define RTAS_ERRINJCT_BUF_SIZE SZ_1K
+
+extern unsigned long rtas_errinjct_buf;
+extern struct mutex rtas_errinjct_mutex;
+
/* RMO buffer reserved for user-space RTAS use */
extern unsigned long rtas_rmo_buf;
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 27d53f34494d..7883f973e8c9 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -769,6 +769,17 @@ EXPORT_SYMBOL_GPL(rtas_data_buf);
unsigned long rtas_rmo_buf;
+/*
+ * Physical address of the ibm,errinjct work buffer. Allocated during
+ * rtas_initialize() using memblock_phys_alloc_range() below rtas_region
+ * so the address fits in 32 bits and is safe to pass to RTAS firmware.
+ */
+unsigned long rtas_errinjct_buf;
+EXPORT_SYMBOL_GPL(rtas_errinjct_buf);
+
+DEFINE_MUTEX(rtas_errinjct_mutex);
+EXPORT_SYMBOL_GPL(rtas_errinjct_mutex);
+
/*
* If non-NULL, this gets called when the kernel terminates.
* This is done like this so rtas_flash can be a module.
@@ -2109,6 +2120,12 @@ void __init rtas_initialize(void)
panic("ERROR: RTAS: Failed to allocate %lx bytes below %pa\n",
PAGE_SIZE, &rtas_region);
+ rtas_errinjct_buf = memblock_phys_alloc_range(RTAS_ERRINJCT_BUF_SIZE,
+ SZ_1K, 0, rtas_region);
+ if (!rtas_errinjct_buf)
+ panic("ERROR: RTAS: Failed to allocate %lu bytes below %pa\n",
+ (unsigned long)RTAS_ERRINJCT_BUF_SIZE, &rtas_region);
+
rtas_work_area_reserve_arena(rtas_region);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 2/5] powerpc/rtas: Allocate ibm,errinjct buffer below RTAS limit Narayana Murty N
@ 2026-07-21 3:38 ` Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL Narayana Murty N
4 siblings, 0 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
Add validation helpers and buffer-preparation infrastructure used by
the RTAS-based EEH error injection implementation.
Changes relative to the original submission:
- pr_fmt unconditionally defined at the top of the file, not wrapped
in an #ifndef guard.
- RTAS error-type constants (RTAS_ERR_TYPE_*) are defined as file-local
macros in eeh_pseries.c, not in the UAPI header. They are internal
PAPR firmware type codes, not generic EEH ABI values.
- validate_corrupted_page() drops the unused 'pe' parameter entirely
(no __maybe_unused). The mask value is silently accepted since it
is not meaningful for this error type.
- validate_ioa_bus_error() is removed; callers invoke
validate_addr_mask_in_pe() directly.
- validate_err_type() and validate_special_event() and
validate_corrupted_page() are marked static inline.
- prepare_errinjct_buffer() takes an explicit 'void *buf' pointer
(kernel virtual address) instead of accessing rtas_errinjct_buf
directly, making it easier to test and keeping the global out of
the helper.
- kernel-doc added for prepare_errinjct_buffer() including a Locking:
line stating the caller must hold rtas_errinjct_mutex.
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/include/uapi/asm/eeh.h | 24 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 250 +++++++++++++++++++
2 files changed, 271 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/include/uapi/asm/eeh.h b/arch/powerpc/include/uapi/asm/eeh.h
index 3b5c47ff3fc4..2680b22f8917 100644
--- a/arch/powerpc/include/uapi/asm/eeh.h
+++ b/arch/powerpc/include/uapi/asm/eeh.h
@@ -15,9 +15,27 @@
#define EEH_PE_STATE_STOPPED_DMA 4 /* Stopped DMA only */
#define EEH_PE_STATE_UNAVAIL 5 /* Unavailable */
-/* EEH error types and functions */
-#define EEH_ERR_TYPE_32 0 /* 32-bits error */
-#define EEH_ERR_TYPE_64 1 /* 64-bits error */
+/*
+ * EEH error types.
+ *
+ * EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 are the original ABI values and must
+ * not be renumbered. The additional types below are generic identifiers for
+ * error-injection types supported by pSeries RTAS. Platform backends may
+ * return -EOPNOTSUPP for valid generic types that are not supported by their
+ * firmware.
+ */
+#define EEH_ERR_TYPE_32 0 /* 32-bits error */
+#define EEH_ERR_TYPE_64 1 /* 64-bits error */
+#define EEH_ERR_TYPE_RECOVERED_SPECIAL_EVENT 0x03
+#define EEH_ERR_TYPE_CORRUPTED_PAGE 0x04
+#define EEH_ERR_TYPE_CORRUPTED_DCACHE_START 0x09
+#define EEH_ERR_TYPE_CORRUPTED_DCACHE_END 0x0a
+#define EEH_ERR_TYPE_CORRUPTED_ICACHE_START 0x0b
+#define EEH_ERR_TYPE_CORRUPTED_ICACHE_END 0x0c
+#define EEH_ERR_TYPE_CORRUPTED_TLB_START 0x0d
+#define EEH_ERR_TYPE_CORRUPTED_TLB_END 0x0e
+
+/* EEH error functions */
#define EEH_ERR_FUNC_MIN 0
#define EEH_ERR_FUNC_LD_MEM_ADDR 0 /* Memory load */
#define EEH_ERR_FUNC_LD_MEM_DATA 1
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b12ef382fec7..25aad86c696d 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
+#define pr_fmt(fmt) "EEH: " fmt
/*
* The file intends to implement the platform dependent EEH operations on pseries.
* Actually, the pseries platform is built based on RTAS heavily. That means the
@@ -31,8 +32,27 @@
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
+#include <linux/mutex.h>
#include <asm/rtas.h>
+/*
+ * PAPR RTAS firmware error-injection type codes.
+ *
+ * These are internal firmware encodings used by ibm,errinjct. They are
+ * NOT generic EEH ABI values. Only the types actually handled by
+ * prepare_errinjct_buffer() are defined here.
+ */
+#define RTAS_ERR_TYPE_RECOVERED_SPECIAL_EVENT 0x03
+#define RTAS_ERR_TYPE_CORRUPTED_PAGE 0x04
+#define RTAS_ERR_TYPE_IOA_BUS_ERROR 0x07
+#define RTAS_ERR_TYPE_CORRUPTED_DCACHE_START 0x09
+#define RTAS_ERR_TYPE_CORRUPTED_DCACHE_END 0x0a
+#define RTAS_ERR_TYPE_CORRUPTED_ICACHE_START 0x0b
+#define RTAS_ERR_TYPE_CORRUPTED_ICACHE_END 0x0c
+#define RTAS_ERR_TYPE_CORRUPTED_TLB_START 0x0d
+#define RTAS_ERR_TYPE_CORRUPTED_TLB_END 0x0e
+#define RTAS_ERR_TYPE_IOA_BUS_ERROR_64 0x0f
+
/* RTAS tokens */
static int ibm_set_eeh_option;
static int ibm_set_slot_reset;
@@ -786,6 +806,236 @@ static int pseries_notify_resume(struct eeh_dev *edev)
}
#endif
+/**
+ * validate_addr_mask_in_pe() - Validate that addr+mask fall within PE BARs
+ * @pe: EEH PE containing one or more PCI devices
+ * @addr: Address to validate
+ * @mask: Address mask to validate
+ *
+ * Checks that @addr is mapped into a BAR/MMIO region of any device
+ * belonging to the PE. If @mask is non-zero, ensures it is consistent
+ * with @addr.
+ *
+ * Return: 0 if valid, RTAS_INVALID_PARAMETER on failure.
+ */
+static int validate_addr_mask_in_pe(struct eeh_pe *pe, unsigned long addr,
+ unsigned long mask)
+{
+ struct eeh_dev *edev, *tmp;
+ struct pci_dev *pdev;
+ int bar;
+ resource_size_t bar_start, bar_len;
+ bool valid = false;
+
+ /* nothing to validate */
+ if (addr == 0 && mask == 0)
+ return 0;
+
+ eeh_pe_for_each_dev(pe, edev, tmp) {
+ pdev = eeh_dev_to_pci_dev(edev);
+ if (!pdev)
+ continue;
+
+ for (bar = 0; bar < PCI_NUM_RESOURCES; bar++) {
+ bar_start = pci_resource_start(pdev, bar);
+ bar_len = pci_resource_len(pdev, bar);
+
+ if (!bar_len)
+ continue;
+
+ if (addr >= bar_start && addr < (bar_start + bar_len)) {
+ if ((addr & mask) != addr) {
+ pr_err("Mask 0x%lx invalid for addr 0x%lx in BAR[%d] range 0x%llx-0x%llx\n",
+ mask, addr, bar,
+ (unsigned long long)bar_start,
+ (unsigned long long)(bar_start + bar_len));
+ return RTAS_INVALID_PARAMETER;
+ }
+ pr_debug("addr=0x%lx mask=0x%lx validated in BAR[%d] of %s\n",
+ addr, mask, bar, pci_name(pdev));
+ valid = true;
+ }
+ }
+ }
+
+ if (!valid) {
+ pr_err("addr=0x%lx not within any BAR of any device in PE\n",
+ addr);
+ return RTAS_INVALID_PARAMETER;
+ }
+
+ return 0;
+}
+
+/**
+ * validate_special_event() - Validate parameters for special-event injection
+ * @addr: Address parameter (must be zero for this type)
+ * @mask: Mask parameter (must be zero for this type)
+ *
+ * Return: 0 if valid, RTAS_INVALID_PARAMETER otherwise.
+ */
+static inline int validate_special_event(unsigned long addr, unsigned long mask)
+{
+ if (addr || mask) {
+ pr_err("special-event injection must not specify addr/mask\n");
+ return RTAS_INVALID_PARAMETER;
+ }
+ return 0;
+}
+
+/**
+ * validate_corrupted_page() - Validate parameters for corrupted-page injection
+ * @addr: Physical page address (required, must be non-zero)
+ *
+ * The mask value is not meaningful for this error type and is ignored.
+ *
+ * Return: 0 if valid, RTAS_INVALID_PARAMETER otherwise.
+ */
+static inline int validate_corrupted_page(unsigned long addr)
+{
+ if (!addr) {
+ pr_err("corrupted-page injection requires non-zero addr\n");
+ return RTAS_INVALID_PARAMETER;
+ }
+ return 0;
+}
+
+/**
+ * pseries_eeh_type_to_rtas() - Map a generic EEH error type to its RTAS encoding.
+ * @type: Generic EEH error type (EEH_ERR_TYPE_*)
+ *
+ * Translates a userspace-visible generic EEH error type to the corresponding
+ * PAPR RTAS firmware type code. Every supported value appears as an explicit
+ * case; coincidental equality between generic EEH and RTAS values is not relied
+ * upon.
+ *
+ * Return: RTAS_ERR_TYPE_* value on success, -EINVAL for unsupported 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;
+ case EEH_ERR_TYPE_RECOVERED_SPECIAL_EVENT:
+ return RTAS_ERR_TYPE_RECOVERED_SPECIAL_EVENT;
+ case EEH_ERR_TYPE_CORRUPTED_PAGE:
+ return RTAS_ERR_TYPE_CORRUPTED_PAGE;
+ case EEH_ERR_TYPE_CORRUPTED_DCACHE_START:
+ return RTAS_ERR_TYPE_CORRUPTED_DCACHE_START;
+ case EEH_ERR_TYPE_CORRUPTED_DCACHE_END:
+ return RTAS_ERR_TYPE_CORRUPTED_DCACHE_END;
+ case EEH_ERR_TYPE_CORRUPTED_ICACHE_START:
+ return RTAS_ERR_TYPE_CORRUPTED_ICACHE_START;
+ case EEH_ERR_TYPE_CORRUPTED_ICACHE_END:
+ return RTAS_ERR_TYPE_CORRUPTED_ICACHE_END;
+ case EEH_ERR_TYPE_CORRUPTED_TLB_START:
+ return RTAS_ERR_TYPE_CORRUPTED_TLB_START;
+ case EEH_ERR_TYPE_CORRUPTED_TLB_END:
+ return RTAS_ERR_TYPE_CORRUPTED_TLB_END;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * prepare_errinjct_buffer() - Build ibm,errinjct work buffer
+ * @buf: RTAS error-injection work buffer (kernel virtual address)
+ * @pe: EEH PE associated with the injection target
+ * @rtas_type: PAPR firmware error-injection type after generic EEH-to-RTAS
+ * translation (RTAS_ERR_TYPE_*)
+ * @func: Error function selector
+ * @addr: Target address, if applicable
+ * @mask: Address mask, if applicable
+ *
+ * Zeroes @buf and populates it according to the PAPR layout for @rtas_type.
+ * Performs inline parameter validation for each error type.
+ *
+ * Locking: Caller must hold rtas_errinjct_mutex.
+ *
+ * Return: 0 on success, RTAS_INVALID_PARAMETER on invalid input.
+ */
+static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
+ int rtas_type, int func,
+ unsigned long addr, unsigned long mask)
+{
+ __be64 *buf64 = (__be64 *)buf;
+ __be32 *buf32 = (__be32 *)buf;
+
+ memset(buf, 0, RTAS_ERRINJCT_BUF_SIZE);
+
+ switch (rtas_type) {
+ case RTAS_ERR_TYPE_RECOVERED_SPECIAL_EVENT:
+ /* func: 1 = non-persistent, 2 = persistent */
+ if (func < 1 || func > 2)
+ return RTAS_INVALID_PARAMETER;
+ if (validate_special_event(addr, mask))
+ return RTAS_INVALID_PARAMETER;
+ buf32[0] = cpu_to_be32(func);
+ break;
+
+ case RTAS_ERR_TYPE_CORRUPTED_PAGE:
+ if (validate_corrupted_page(addr))
+ return RTAS_INVALID_PARAMETER;
+ buf32[0] = cpu_to_be32(upper_32_bits(addr));
+ buf32[1] = cpu_to_be32(lower_32_bits(addr));
+ break;
+
+ case RTAS_ERR_TYPE_IOA_BUS_ERROR:
+ if (func < EEH_ERR_FUNC_LD_MEM_ADDR || func > EEH_ERR_FUNC_MAX)
+ return RTAS_INVALID_PARAMETER;
+ if (upper_32_bits(addr) || upper_32_bits(mask)) {
+ pr_err("32-bit IOA injection cannot encode addr=%#lx mask=%#lx\n",
+ addr, mask);
+ return RTAS_INVALID_PARAMETER;
+ }
+ if (validate_addr_mask_in_pe(pe, addr, mask))
+ return RTAS_INVALID_PARAMETER;
+ 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:
+ if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX)
+ return RTAS_INVALID_PARAMETER;
+ if (validate_addr_mask_in_pe(pe, addr, mask))
+ return RTAS_INVALID_PARAMETER;
+ 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;
+
+ case RTAS_ERR_TYPE_CORRUPTED_DCACHE_START:
+ case RTAS_ERR_TYPE_CORRUPTED_DCACHE_END:
+ case RTAS_ERR_TYPE_CORRUPTED_ICACHE_START:
+ case RTAS_ERR_TYPE_CORRUPTED_ICACHE_END:
+ buf32[0] = cpu_to_be32(lower_32_bits(addr));
+ buf32[1] = cpu_to_be32(lower_32_bits(mask));
+ break;
+
+ case RTAS_ERR_TYPE_CORRUPTED_TLB_START:
+ case RTAS_ERR_TYPE_CORRUPTED_TLB_END:
+ buf32[0] = cpu_to_be32(lower_32_bits(addr));
+ break;
+
+ default:
+ pr_err("unsupported RTAS error injection type 0x%x\n", rtas_type);
+ return RTAS_INVALID_PARAMETER;
+ }
+
+ pr_debug("errinjct buffer ready: rtas_type=0x%x func=%d addr=0x%lx mask=0x%lx\n",
+ rtas_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] 6+ messages in thread
* [PATCH v3 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
` (2 preceding siblings ...)
2026-07-21 3:38 ` [PATCH v3 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers Narayana Murty N
@ 2026-07-21 3:38 ` Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL Narayana Murty N
4 siblings, 0 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
Replace legacy MMIO error injection with full PAPR-compliant RTAS error
injection supporting 14+ error types via
- ibm,open-errinjct
- ibm,errinjct
- ibm,close-errinjct.
Key features:
- Complete open-session-inject-close cycle management
- Special handling for ibm,open-errinjct output format (token,status)
- Comprehensive buffer preparation per PAPR layouts
- All pr_* logging uses pr_fmt("EEH: ") prefix
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/platforms/pseries/eeh_pseries.c | 121 +++++++++++++++----
1 file changed, 95 insertions(+), 26 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 25aad86c696d..d32a84009fdc 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -1037,40 +1037,109 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
}
/**
- * 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
+ * pseries_eeh_err_inject - Inject specified error into the indicated PE
+ * @pe: the indicated PE
+ * @type: generic EEH error type (EEH_ERR_TYPE_*)
+ * @func: error function selector (EEH_ERR_FUNC_*)
+ * @addr: target address, if applicable
+ * @mask: address mask, if applicable
+ *
+ * Implements the EEH error-injection callback for pseries using the RTAS
+ * ibm,open-errinjct / ibm,errinjct / ibm,close-errinjct firmware services.
+ *
+ * Generic EEH error types are translated to RTAS firmware type codes via
+ * pseries_eeh_type_to_rtas() before the injection session is opened.
+ * Unsupported generic types are rejected with -EINVAL before any RTAS call
+ * is made. Existing userspace is unaffected.
+ *
+ * Return: 0 on success, negative errno or RTAS error code 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;
-
- /* Check on PCI error type */
- if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64)
- return -EINVAL;
+ int open_token, errinjct_token, close_token;
+ int session_token = 0;
+ bool session_open = false;
+ void *buf;
+ u32 buf_phys;
+ int rc;
+ int rtas_type;
- 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:
+ /* Guard: buffer must fit in 32 bits for RTAS */
+ if (WARN_ON_ONCE(upper_32_bits(rtas_errinjct_buf)))
return -ERANGE;
+
+ /* Map generic EEH ABI to RTAS-internal error type */
+ rtas_type = pseries_eeh_type_to_rtas(type);
+ if (rtas_type < 0) {
+ pr_err("unsupported EEH error type %#x\n", type);
+ return rtas_type;
}
- return 0;
+ /* Verify all three RTAS tokens are available before touching firmware */
+ 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) {
+ pr_err("ibm,open/errinjct/close-errinjct not available\n");
+ return -ENODEV;
+ }
+
+ buf = __va(rtas_errinjct_buf);
+ buf_phys = lower_32_bits(rtas_errinjct_buf);
+
+ mutex_lock(&rtas_errinjct_mutex);
+
+ /* Step 1: open injection session */
+ 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);
+ goto out_unlock;
+ }
+ session_open = true;
+
+ /* Step 2: prepare the work buffer */
+ rc = prepare_errinjct_buffer(buf, pe, rtas_type, func, addr, mask);
+ if (rc) {
+ pr_err("failed to prepare errinjct buffer: rc=%d\n", rc);
+ goto out_close;
+ }
+
+ /* Step 3: inject the error */
+ do {
+ rc = rtas_call(errinjct_token, 3, 1, NULL,
+ rtas_type, session_token, buf_phys);
+ } while (rtas_busy_delay(rc));
+
+ if (rc)
+ pr_err("ibm,errinjct failed: status=%d\n", rc);
+
+out_close:
+ /* Step 4: always close the session */
+ {
+ int close_rc;
+
+ if (session_open) {
+ 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);
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&rtas_errinjct_mutex);
+ return rc;
}
static struct eeh_ops pseries_eeh_ops = {
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
` (3 preceding siblings ...)
2026-07-21 3:38 ` [PATCH v3 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
@ 2026-07-21 3:38 ` Narayana Murty N
4 siblings, 0 replies; 6+ messages in thread
From: Narayana Murty N @ 2026-07-21 3:38 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, gregkh, oohall, npiggin
Cc: linuxppc-dev, linux-kernel, tyreld, vaibhav, sbhat, ganeshgr,
sourabhjain, haren, nnmlinux, thuth
The EEH error-injection interface passes the generic userspace ABI
values EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 to the platform backend.
The PowerNV backend currently compares those generic values directly
with OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR and
OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64. Although the corresponding values
are currently numerically identical, this implicitly couples the
generic EEH ABI to the OPAL firmware encoding.
Explicitly translate the generic EEH error types to their OPAL
equivalents in pnv_eeh_err_inject(). Keep the platform-specific
encoding within the PowerNV backend and reject unsupported generic
types with -EINVAL.
No userspace ABI values are changed.
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-powernv.c | 36 +++++++++++++++++---
1 file changed, 32 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index db3370d1673c..b0bcd014a133 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -1169,11 +1169,39 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
struct pnv_phb *phb = hose->private_data;
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",
+ /*
+ * Map generic EEH error-type ABI values to OPAL-specific type codes.
+ * EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 are the only types supported by
+ * OPAL. Additional generic types defined in the UAPI header are valid
+ * for pSeries RTAS but unsupported here; return -EOPNOTSUPP for those.
+ * Unknown or invalid values return -EINVAL.
+ *
+ * Note: currently OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR == 0 ==
+ * EEH_ERR_TYPE_32 and OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64 == 1 ==
+ * EEH_ERR_TYPE_64, but the explicit switch makes the coupling
+ * visible and allows the values to diverge independently.
+ */
+ switch (type) {
+ case EEH_ERR_TYPE_32:
+ type = OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR;
+ break;
+ case EEH_ERR_TYPE_64:
+ type = OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64;
+ break;
+ case EEH_ERR_TYPE_RECOVERED_SPECIAL_EVENT:
+ case EEH_ERR_TYPE_CORRUPTED_PAGE:
+ case EEH_ERR_TYPE_CORRUPTED_DCACHE_START:
+ case EEH_ERR_TYPE_CORRUPTED_DCACHE_END:
+ case EEH_ERR_TYPE_CORRUPTED_ICACHE_START:
+ case EEH_ERR_TYPE_CORRUPTED_ICACHE_END:
+ case EEH_ERR_TYPE_CORRUPTED_TLB_START:
+ case EEH_ERR_TYPE_CORRUPTED_TLB_END:
+ pr_warn("%s: EEH error type %d not supported by OPAL\n",
__func__, type);
- return -ERANGE;
+ return -EOPNOTSUPP;
+ default:
+ pr_warn("%s: unsupported EEH error type %d\n", __func__, type);
+ return -EINVAL;
}
if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR ||
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-21 3:39 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 3:38 [PATCH v3 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 2/5] powerpc/rtas: Allocate ibm,errinjct buffer below RTAS limit Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
2026-07-21 3:38 ` [PATCH v3 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL Narayana Murty N
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox