* [PATCH v4 1/5] powerpc/rtas: Handle ibm,open-errinjct return format
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
@ 2026-08-31 6:54 ` Narayana Murty N
2026-09-01 9:22 ` Sourabh Jain
2026-08-31 6:54 ` [PATCH v4 2/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Narayana Murty N @ 2026-08-31 6:54 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, 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:
if (rtas_token_is_open_errinjct(token) && nret < 2) {
WARN_ON_ONCE(1);
return RTAS_INVALID_PARAMETER;
}
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: ibm,open-errinjct
places status at rets[1], so the -1 sentinel check must use rets[1]
for that function rather than always using rets[0].
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, 68 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 8d81c1e7a8db..7131870655c6 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 RTAS_INVALID_PARAMETER;
+ }
+
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 != NULL) {
+ 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);
@@ -1942,10 +1992,18 @@ 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)
- errbuf = __fetch_rtas_last_error(buff_copy);
+ /*
+ * A -1 return code indicates that the last command couldn't
+ * be completed due to a hardware error. ibm,open-errinjct
+ * places status at rets[1] rather than rets[0]; check the
+ * correct position for the -1 sentinel.
+ */
+ {
+ __be32 status_cell = (token == rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT) &&
+ nret >= 2) ? args.rets[1] : args.rets[0];
+ if (be32_to_cpu(status_cell) == -1)
+ errbuf = __fetch_rtas_last_error(buff_copy);
+ }
lockdep_unpin_lock(&rtas_lock, cookie);
raw_spin_unlock_irqrestore(&rtas_lock, flags);
--
2.51.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v4 1/5] powerpc/rtas: Handle ibm,open-errinjct return format
2026-08-31 6:54 ` [PATCH v4 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
@ 2026-09-01 9:22 ` Sourabh Jain
0 siblings, 0 replies; 9+ messages in thread
From: Sourabh Jain @ 2026-09-01 9:22 UTC (permalink / raw)
To: Narayana Murty N, mahesh, maddy, mpe, christophe.leroy, oohall,
npiggin, tpearson, alex, Ritesh Harjani (IBM)
Cc: linuxppc-dev, linux-kernel, sbhat, harshpb
On 31/08/26 12:24, Narayana Murty N wrote:
> 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:
>
> if (rtas_token_is_open_errinjct(token) && nret < 2) {
> WARN_ON_ONCE(1);
> return RTAS_INVALID_PARAMETER;
> }
>
> 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: ibm,open-errinjct
> places status at rets[1], so the -1 sentinel check must use rets[1]
> for that function rather than always using rets[0].
>
> 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, 68 insertions(+), 10 deletions(-)
>
> diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
> index 8d81c1e7a8db..7131870655c6 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 RTAS_INVALID_PARAMETER;
Nit: I would prefer -EINVAL instead. RTAS_INVALID_PARAMETER is RTAS
error code but here kernel is validating the parameter so I think
-EINVAL would be better.
> + }
> +
> 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 != NULL) {
> + 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);
> @@ -1942,10 +1992,18 @@ 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)
> - errbuf = __fetch_rtas_last_error(buff_copy);
> + /*
> + * A -1 return code indicates that the last command couldn't
> + * be completed due to a hardware error. ibm,open-errinjct
> + * places status at rets[1] rather than rets[0]; check the
> + * correct position for the -1 sentinel.
> + */
> + {
> + __be32 status_cell = (token == rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT) &&
> + nret >= 2) ? args.rets[1] : args.rets[0];
> + if (be32_to_cpu(status_cell) == -1)
> + errbuf = __fetch_rtas_last_error(buff_copy);
Do we know what happens when the ibm,open-errinjct RTAS call is made
with nret < 2?
The reason I’m asking is that, even with the above changes,
args.rets[0] is used as the return code if the ibm,open-errinjct call
is made with nret < 2.
I like the approach you took in rtas_call() of pre-validating nret for
the ibm,open-errinjct RTAS call and returning early if it is less than
2. I think we can use a similar approach here as well. If we do that,
the above code changes will be much cleaner. In that case, we don't
have to figure out how RTAS processes ibm,open-errinjct with nret < 2.
The only concern I have is that this change would alter the system
call behavior. Right now, the kernel accepts nret < 2 for
ibm,open-errinjct and makes the RTAS call, but with the above suggested
change, the kernel would return early if nret < 2.
The prominent user of this system call is librtas, which passes
nret = 2 for ibm,open-errinjct:
https://github.com/ibm-power-utilities/librtas/blob/d321a1f5ae3d528ba027fc748d1cc1123dd4ae29/librtas_src/syscall_calls.c#L488
Also, as per PAPR, users are supposed to pass nret = 2 for this RTAS
call. So I think it should be fine to validate nret in sys_rtas for
ibm,open-errinjct and return early if it is found to be less than 2.
Since this is a change in system call behavior, I want to be a
little cautious. So, I’d like to hear your thoughts and would also
like to know what others think about making the above change.
Thanks, Sourabh Jain
> + }
>
> lockdep_unpin_lock(&rtas_lock, cookie);
> raw_spin_unlock_irqrestore(&rtas_lock, flags);
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 2/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
2026-08-31 6:54 ` [PATCH v4 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
@ 2026-08-31 6:54 ` Narayana Murty N
2026-08-31 6:54 ` [PATCH v4 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers Narayana Murty N
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-08-31 6:54 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, 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.
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 | 90 +++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 1c0eec228cc6..8a6cc856d7da 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -774,6 +774,85 @@ static long tce_iommu_create_default_window(struct tce_container *container)
return ret;
}
+static bool vfio_spapr_eeh_err_needs_addr_normalize(unsigned int type)
+{
+ if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64)
+ return false;
+
+ return true;
+}
+
+static int vfio_spapr_eeh_normalize_addr(struct eeh_pe *pe,
+ unsigned long addr,
+ 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;
+
+ if (!pe || !normalized)
+ return -EINVAL;
+
+ if (!addr) {
+ *normalized = addr;
+ 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_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;
+ return 0;
+ }
+
+ /*
+ * 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;
+
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
static long vfio_spapr_ioctl_eeh_pe_op(struct iommu_group *group,
unsigned long arg)
{
@@ -818,6 +897,17 @@ static long vfio_spapr_ioctl_eeh_pe_op(struct iommu_group *group,
if (copy_from_user(&op, (void __user *)arg, minsz))
return -EFAULT;
+ if (vfio_spapr_eeh_err_needs_addr_normalize(op.err.type)) {
+ unsigned long normalized;
+ long ret;
+
+ ret = vfio_spapr_eeh_normalize_addr(pe, op.err.addr, &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.51.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v4 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
2026-08-31 6:54 ` [PATCH v4 1/5] powerpc/rtas: Handle ibm,open-errinjct return format Narayana Murty N
2026-08-31 6:54 ` [PATCH v4 2/5] vfio/spapr_tce: Normalize EEH IOA error injection addresses Narayana Murty N
@ 2026-08-31 6:54 ` Narayana Murty N
2026-08-31 6:54 ` [PATCH v4 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-08-31 6:54 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, harshpb
Add pseries-specific infrastructure for RTAS-based EEH error injection.
Define pr_fmt unconditionally at the top of eeh_pseries.c so all
pr_*() calls carry the "EEH: " prefix:
#define pr_fmt(fmt) "EEH: " fmt
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. 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. Address/mask validation against BARs is done in the buffer
preparation step.
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 | 215 +++++++++++++++++++
1 file changed, 215 insertions(+)
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b12ef382fec7..fafe0004e738 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>
@@ -786,6 +788,219 @@ 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;
+ 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.
+ */
+ 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);
+
+ 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);
+ return 0;
+ }
+ }
+ }
+
+ pr_err("addr=0x%lx mask=0x%lx not within any PCI bus BAR of any device in PE\n",
+ addr, mask);
+ return -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_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_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_type) {
+ case RTAS_ERR_TYPE_IOA_BUS_ERROR:
+ if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX)
+ return -EINVAL;
+
+ 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 -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:
+ if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX)
+ return -EINVAL;
+
+ 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_type);
+ return -EINVAL;
+ }
+
+ 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.51.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v4 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
` (2 preceding siblings ...)
2026-08-31 6:54 ` [PATCH v4 3/5] powerpc/pseries/eeh: Add RTAS error validation helpers Narayana Murty N
@ 2026-08-31 6:54 ` Narayana Murty N
2026-09-02 5:16 ` Sourabh Jain
2026-08-31 6:54 ` [PATCH v4 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL Narayana Murty N
2026-09-01 18:08 ` [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
5 siblings, 1 reply; 9+ messages in thread
From: Narayana Murty N @ 2026-08-31 6:54 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, harshpb
Replace the legacy MMIO stub in pseries_eeh_err_inject() with a full
PAPR-compliant RTAS error injection path using the existing RTAS
work-area allocator.
The mutex is not a buffer lock; it serializes the firmware session
open/inject/close sequence as required by PAPR. No global buffer
is allocated or used.
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.
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 | 123 ++++++++++++++-----
1 file changed, 95 insertions(+), 28 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index fafe0004e738..fcb8c560d813 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -25,6 +25,7 @@
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
+#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/crash_dump.h>
@@ -34,6 +35,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;
@@ -958,8 +960,6 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
return -EINVAL;
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 -EINVAL;
}
@@ -992,50 +992,117 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
break;
default:
- pr_err("unsupported RTAS error injection type 0x%x\n", rtas_type);
+ pr_err("unsupported RTAS error injection type 0x%x\n",
+ rtas_type);
return -EINVAL;
}
- 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-local mutex serializes the open/inject/close RTAS session */
+static DEFINE_MUTEX(pseries_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
+ *
+ * A short-lived RTAS work area is allocated per call; no global buffer
+ * is used. pseries_errinjct_mutex serializes the open/inject/close
+ * session sequence.
+ *
+ * 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;
+ u32 buf_phys;
+ void *buf;
+ int open_token, errinjct_token, close_token;
+ int session_token;
+ int rtas_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_type = pseries_eeh_type_to_rtas(type);
+ if (rtas_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;
+
+ area = rtas_work_area_alloc(RTAS_ERRINJCT_BUF_SIZE);
+ buf = rtas_work_area_raw_buf(area);
+ area_phys = rtas_work_area_phys(area);
+
+ if (WARN_ON_ONCE(upper_32_bits(area_phys))) {
+ rc = -ERANGE;
+ goto out_free_area;
}
- return 0;
+ buf_phys = lower_32_bits(area_phys);
+
+ rc = prepare_errinjct_buffer(buf, pe, rtas_type, func, addr, mask);
+ if (rc)
+ goto out_free_area;
+
+ mutex_lock(&pseries_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;
+ }
+
+ 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);
+ rc = rtas_error_rc(rc);
+ }
+
+ 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(&pseries_errinjct_mutex);
+
+out_free_area:
+ rtas_work_area_free(area);
+ return rc;
}
static struct eeh_ops pseries_eeh_ops = {
--
2.51.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v4 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection
2026-08-31 6:54 ` [PATCH v4 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
@ 2026-09-02 5:16 ` Sourabh Jain
0 siblings, 0 replies; 9+ messages in thread
From: Sourabh Jain @ 2026-09-02 5:16 UTC (permalink / raw)
To: Narayana Murty N, mahesh, maddy, mpe, christophe.leroy, oohall,
npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, harshpb
On 31/08/26 12:24, Narayana Murty N wrote:
> Replace the legacy MMIO stub in pseries_eeh_err_inject() with a full
> PAPR-compliant RTAS error injection path using the existing RTAS
> work-area allocator.
>
> The mutex is not a buffer lock;
Sorry but I didn't get this...
> it serializes the firmware session
> open/inject/close sequence as required by PAPR. No global buffer
> is allocated or used.
Nit:
I think the above para is influenced form old suggestion to not use global
dedicated buffer. Lets drop the global buffer thing from commit message
explain just the current approach.
>
> 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.
>
> 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 | 123 ++++++++++++++-----
> 1 file changed, 95 insertions(+), 28 deletions(-)
>
> diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
> index fafe0004e738..fcb8c560d813 100644
> --- a/arch/powerpc/platforms/pseries/eeh_pseries.c
> +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
> @@ -25,6 +25,7 @@
> #include <linux/rbtree.h>
> #include <linux/sched.h>
> #include <linux/seq_file.h>
> +#include <linux/mutex.h>
> #include <linux/spinlock.h>
> #include <linux/crash_dump.h>
>
> @@ -34,6 +35,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;
> @@ -958,8 +960,6 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
> return -EINVAL;
>
> if (upper_32_bits(addr) || upper_32_bits(mask)) {
> - pr_err("32-bit IOA injection cannot encode addr=%#lx mask=%#lx\n",
> - addr, mask);
We are returning -EINVAL remove the error message, what is the need?
> return -EINVAL;
> }
>
> @@ -992,50 +992,117 @@ static int prepare_errinjct_buffer(void *buf, struct eeh_pe *pe,
> break;
>
> default:
> - pr_err("unsupported RTAS error injection type 0x%x\n", rtas_type);
> + pr_err("unsupported RTAS error injection type 0x%x\n",
> + rtas_type);
Above change is not necessary..
> return -EINVAL;
> }
>
> - pr_debug("errinjct buffer ready: rtas_type=0x%x func=%d addr=0x%lx mask=0x%lx\n",
> - rtas_type, func, addr, mask);
What is need to remove this debug message?
> return 0;
> }
>
> +/* pseries-local mutex serializes the open/inject/close RTAS session */
> +static DEFINE_MUTEX(pseries_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
> + *
> + * A short-lived RTAS work area is allocated per call; no global buffer
> + * is used.
Mentioning "no global buffer is used" is not adding any value, I think.
> pseries_errinjct_mutex serializes the open/inject/close
> + * session sequence.
> + *
> + * 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;
> + u32 buf_phys;
> + void *buf;
> + int open_token, errinjct_token, close_token;
> + int session_token;
> + int rtas_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_type = pseries_eeh_type_to_rtas(type);
> + if (rtas_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;
> +
> + area = rtas_work_area_alloc(RTAS_ERRINJCT_BUF_SIZE);
> + buf = rtas_work_area_raw_buf(area);
> + area_phys = rtas_work_area_phys(area);
I was wondering if there is any need to hold the work area buffer until
we acquire pseries_errinjct_mutex.
Would it make sense to allocate the buffer only after acquiring the
mutex? I suggested an order for the open, errinjct, and close calls
below, which I think could also help address the above comment.
Nit: this file is under pseries platform so pseries_errinjct_mutex can
renamed to errinct_mutex.
> +
> + if (WARN_ON_ONCE(upper_32_bits(area_phys))) {
> + rc = -ERANGE;
> + goto out_free_area;
> }
>
> - return 0;
> + buf_phys = lower_32_bits(area_phys);
I don't understand the above logic. First, we check area_phys and exit
early if the address is above 4G, and then we take the lower 32 bits of
the same address.
I think the RTAS work area allocation API should be responsible for
allocating the work area buffer at the right location. The user
shouldn't have to worry about where exactly the buffer is allocated
unless they have a specific requirement.
Is the work area buffer allocated by the API not meeting your
requirement? If so, could you please explain what the issue is? Let's
see if we can fix it in the work area allocation API. Otherwise, I
would suggest removing the checking and truncation done around
area_phys.
Let me know your opinion.
> +
> + rc = prepare_errinjct_buffer(buf, pe, rtas_type, func, addr, mask);
> + if (rc)
> + goto out_free_area;
> +
> + mutex_lock(&pseries_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;
> + }
I think we should allocate the work area buffer only after the
open-errinjct call succeeds. My preferred order is:
- Call RTAS open-errinjct
- Allocate the work area buffer
- Populate the work area buffer and make the errinjct RTAS call
- Release the work area buffer
- Call RTAS close-errinjct
This way, the work area buffer is held only for as long as it is needed,
and we also avoid allocating it if open-errinjct fails.
> +
> + 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);
> + rc = rtas_error_rc(rc);
> + }
> +
> + 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(&pseries_errinjct_mutex);
> +
> +out_free_area:
> + rtas_work_area_free(area);
> + return rc;
> }
>
> static struct eeh_ops pseries_eeh_ops = {
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
` (3 preceding siblings ...)
2026-08-31 6:54 ` [PATCH v4 4/5] powerpc/pseries/eeh: Implement RTAS-based EEH error injection Narayana Murty N
@ 2026-08-31 6:54 ` Narayana Murty N
2026-09-01 18:08 ` [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
5 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-08-31 6:54 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, harshpb
VFIO EEH error injection exposes a generic userspace ABI. pSeries maps
the generic EEH error types to RTAS ibm,errinjct encodings, while
PowerNV maps the same generic ABI values to OPAL-specific encodings.
Keep EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 unchanged. The mapping is
internal to the platform backend.
Add a switch 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 -EINVAL for unsupported error types rather than comparing
directly against OPAL constants. This decouples the VFIO/EEH
interface from OPAL implementation details, paralleling the approach
used on the pSeries RTAS path.
Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-powernv.c | 27 +++++++++++++++-----
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index db3370d1673c..53c4c05aaef2 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -1167,13 +1167,28 @@ 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_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);
- return -ERANGE;
+ /*
+ * VFIO EEH error injection exposes a generic userspace ABI.
+ * pSeries maps the generic EEH error types to RTAS ibm,errinjct
+ * encodings, while PowerNV maps the same generic ABI values to
+ * OPAL-specific encodings.
+ *
+ * Keep EEH_ERR_TYPE_32 and EEH_ERR_TYPE_64 unchanged. The
+ * mapping is internal to the platform backend.
+ */
+ switch (type) {
+ case EEH_ERR_TYPE_32:
+ opal_type = OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR;
+ break;
+ case EEH_ERR_TYPE_64:
+ opal_type = OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64;
+ break;
+ default:
+ pr_warn("%s: Invalid error type %d\n", __func__, type);
+ return -EINVAL;
}
if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR ||
@@ -1192,7 +1207,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_type, func, addr, mask);
if (rc != OPAL_SUCCESS) {
pr_warn("%s: Failure %lld injecting error "
"%d-%d to PHB#%x-PE#%x\n",
--
2.51.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries
2026-08-31 6:54 [PATCH v4 0/5] powerpc/eeh: Add RTAS-based error injection support on pSeries Narayana Murty N
` (4 preceding siblings ...)
2026-08-31 6:54 ` [PATCH v4 5/5] powerpc/powernv/eeh: Map VFIO EEH error injection to OPAL Narayana Murty N
@ 2026-09-01 18:08 ` Narayana Murty N
5 siblings, 0 replies; 9+ messages in thread
From: Narayana Murty N @ 2026-09-01 18:08 UTC (permalink / raw)
To: mahesh, maddy, mpe, christophe.leroy, oohall, npiggin, tpearson, alex
Cc: linuxppc-dev, linux-kernel, sbhat, sourabhjain, harshpb
Hi,
I noticed that I missed adding the changelog in the v4 cover letter.
Adding it here for reference v4 changes from v3:
On 31/08/26 12:24 PM, Narayana Murty N wrote:
> 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>
The corresponding QEMU v5 series is posted here:
https://lore.kernel.org/qemu-devel/20260901174110.45356-1-nnmlinux@linux.ibm.com/
Change Log:
V3 -> V4:
* 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
Thanks,
Narayana
>
> Narayana Murty N (5):
> powerpc/rtas: Handle ibm,open-errinjct return format
> vfio/spapr_tce: Normalize EEH IOA error injection addresses
> 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/kernel/rtas.c | 78 ++++-
> arch/powerpc/platforms/powernv/eeh-powernv.c | 27 +-
> arch/powerpc/platforms/pseries/eeh_pseries.c | 328 +++++++++++++++++--
> drivers/vfio/vfio_iommu_spapr_tce.c | 90 +++++
> 4 files changed, 484 insertions(+), 39 deletions(-)
>
^ permalink raw reply [flat|nested] 9+ messages in thread