Extend the direct-request path so FF-A v1.2 guests can issue
FFA_MSG_SEND_DIRECT_REQ2 and receive the matching RESP2.
The handler now marshals registers x8–x17, and
ffa_finish_direct_req_run() copies back the 17-register response used by
FFA_MSG_SEND_DIRECT_RESP2. The new opcode is exposed via FFA_FEATURES
and gated on guests that negotiated v1.2.
Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
---
Changes in v1:
- use ACCESS_ONCE to read guest_vers
---
xen/arch/arm/tee/ffa.c | 20 +++++++++++++++++++
xen/arch/arm/tee/ffa_msg.c | 39 ++++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
index 0f07efe5a7b3..2c6443a7f6a4 100644
--- a/xen/arch/arm/tee/ffa.c
+++ b/xen/arch/arm/tee/ffa.c
@@ -237,6 +237,8 @@ out_continue:
static void handle_features(struct cpu_user_regs *regs)
{
uint32_t a1 = get_user_reg(regs, 1);
+ struct domain *d = current->domain;
+ struct ffa_ctx *ctx = d->arch.tee;
unsigned int n;
for ( n = 2; n <= 7; n++ )
@@ -268,6 +270,16 @@ static void handle_features(struct cpu_user_regs *regs)
case FFA_MSG_YIELD:
ffa_set_regs_success(regs, 0, 0);
break;
+ case FFA_MSG_SEND_DIRECT_REQ2:
+ if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 )
+ {
+ ffa_set_regs_success(regs, 0, 0);
+ }
+ else
+ {
+ ffa_set_regs_error(regs, FFA_RET_NOT_SUPPORTED);
+ }
+ break;
case FFA_MEM_SHARE_64:
case FFA_MEM_SHARE_32:
/*
@@ -353,6 +365,14 @@ static bool ffa_handle_call(struct cpu_user_regs *regs)
case FFA_MSG_SEND_DIRECT_REQ_64:
ffa_handle_msg_send_direct_req(regs, fid);
return true;
+ case FFA_MSG_SEND_DIRECT_REQ2:
+ if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 )
+ {
+ ffa_handle_msg_send_direct_req(regs, fid);
+ return true;
+ }
+ e = FFA_RET_NOT_SUPPORTED;
+ break;
case FFA_RUN:
ffa_handle_run(regs, fid);
return true;
diff --git a/xen/arch/arm/tee/ffa_msg.c b/xen/arch/arm/tee/ffa_msg.c
index c3552a3ae36d..4e26596461a9 100644
--- a/xen/arch/arm/tee/ffa_msg.c
+++ b/xen/arch/arm/tee/ffa_msg.c
@@ -49,6 +49,30 @@ static void ffa_finish_direct_req_run(struct cpu_user_regs *regs,
case FFA_MSG_YIELD:
case FFA_INTERRUPT:
break;
+ case FFA_MSG_SEND_DIRECT_RESP2:
+ /*
+ * REQ2 / RESP2 use a 17-register payload (x1–x17). Copy all of them
+ * back to the guest context.
+ */
+ set_user_reg(regs, 0, resp.a0);
+ set_user_reg(regs, 1, resp.a1);
+ set_user_reg(regs, 2, resp.a2);
+ set_user_reg(regs, 3, resp.a3);
+ set_user_reg(regs, 4, resp.a4);
+ set_user_reg(regs, 5, resp.a5);
+ set_user_reg(regs, 6, resp.a6);
+ set_user_reg(regs, 7, resp.a7);
+ set_user_reg(regs, 8, resp.a8);
+ set_user_reg(regs, 9, resp.a9);
+ set_user_reg(regs, 10, resp.a10);
+ set_user_reg(regs, 11, resp.a11);
+ set_user_reg(regs, 12, resp.a12);
+ set_user_reg(regs, 13, resp.a13);
+ set_user_reg(regs, 14, resp.a14);
+ set_user_reg(regs, 15, resp.a15);
+ set_user_reg(regs, 16, resp.a16);
+ set_user_reg(regs, 17, resp.a17);
+ return;
default:
/* Bad fid, report back to the caller. */
ffa_set_regs_error(regs, FFA_RET_ABORTED);
@@ -107,6 +131,21 @@ void ffa_handle_msg_send_direct_req(struct cpu_user_regs *regs, uint32_t fid)
arg.a6 = get_user_reg(regs, 6) & mask;
arg.a7 = get_user_reg(regs, 7) & mask;
+ if ( fid == FFA_MSG_SEND_DIRECT_REQ2 )
+ {
+ /* 17 registers are used for REQ2 */
+ arg.a8 = get_user_reg(regs, 8);
+ arg.a9 = get_user_reg(regs, 9);
+ arg.a10 = get_user_reg(regs, 10);
+ arg.a11 = get_user_reg(regs, 11);
+ arg.a12 = get_user_reg(regs, 12);
+ arg.a13 = get_user_reg(regs, 13);
+ arg.a14 = get_user_reg(regs, 14);
+ arg.a15 = get_user_reg(regs, 15);
+ arg.a16 = get_user_reg(regs, 16);
+ arg.a17 = get_user_reg(regs, 17);
+ }
+
ffa_finish_direct_req_run(regs, &arg);
return;
--
2.51.2
Hi Bertrand,
On Fri, Dec 5, 2025 at 11:37 AM Bertrand Marquis
<bertrand.marquis@arm.com> wrote:
>
> Extend the direct-request path so FF-A v1.2 guests can issue
> FFA_MSG_SEND_DIRECT_REQ2 and receive the matching RESP2.
>
> The handler now marshals registers x8–x17, and
> ffa_finish_direct_req_run() copies back the 17-register response used by
> FFA_MSG_SEND_DIRECT_RESP2. The new opcode is exposed via FFA_FEATURES
> and gated on guests that negotiated v1.2.
>
> Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
> ---
> Changes in v1:
> - use ACCESS_ONCE to read guest_vers
> ---
> xen/arch/arm/tee/ffa.c | 20 +++++++++++++++++++
> xen/arch/arm/tee/ffa_msg.c | 39 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 59 insertions(+)
Looks good.
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Cheers,
Jens
>
> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
> index 0f07efe5a7b3..2c6443a7f6a4 100644
> --- a/xen/arch/arm/tee/ffa.c
> +++ b/xen/arch/arm/tee/ffa.c
> @@ -237,6 +237,8 @@ out_continue:
> static void handle_features(struct cpu_user_regs *regs)
> {
> uint32_t a1 = get_user_reg(regs, 1);
> + struct domain *d = current->domain;
> + struct ffa_ctx *ctx = d->arch.tee;
> unsigned int n;
>
> for ( n = 2; n <= 7; n++ )
> @@ -268,6 +270,16 @@ static void handle_features(struct cpu_user_regs *regs)
> case FFA_MSG_YIELD:
> ffa_set_regs_success(regs, 0, 0);
> break;
> + case FFA_MSG_SEND_DIRECT_REQ2:
> + if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 )
> + {
> + ffa_set_regs_success(regs, 0, 0);
> + }
> + else
> + {
> + ffa_set_regs_error(regs, FFA_RET_NOT_SUPPORTED);
> + }
> + break;
> case FFA_MEM_SHARE_64:
> case FFA_MEM_SHARE_32:
> /*
> @@ -353,6 +365,14 @@ static bool ffa_handle_call(struct cpu_user_regs *regs)
> case FFA_MSG_SEND_DIRECT_REQ_64:
> ffa_handle_msg_send_direct_req(regs, fid);
> return true;
> + case FFA_MSG_SEND_DIRECT_REQ2:
> + if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 )
> + {
> + ffa_handle_msg_send_direct_req(regs, fid);
> + return true;
> + }
> + e = FFA_RET_NOT_SUPPORTED;
> + break;
> case FFA_RUN:
> ffa_handle_run(regs, fid);
> return true;
> diff --git a/xen/arch/arm/tee/ffa_msg.c b/xen/arch/arm/tee/ffa_msg.c
> index c3552a3ae36d..4e26596461a9 100644
> --- a/xen/arch/arm/tee/ffa_msg.c
> +++ b/xen/arch/arm/tee/ffa_msg.c
> @@ -49,6 +49,30 @@ static void ffa_finish_direct_req_run(struct cpu_user_regs *regs,
> case FFA_MSG_YIELD:
> case FFA_INTERRUPT:
> break;
> + case FFA_MSG_SEND_DIRECT_RESP2:
> + /*
> + * REQ2 / RESP2 use a 17-register payload (x1–x17). Copy all of them
> + * back to the guest context.
> + */
> + set_user_reg(regs, 0, resp.a0);
> + set_user_reg(regs, 1, resp.a1);
> + set_user_reg(regs, 2, resp.a2);
> + set_user_reg(regs, 3, resp.a3);
> + set_user_reg(regs, 4, resp.a4);
> + set_user_reg(regs, 5, resp.a5);
> + set_user_reg(regs, 6, resp.a6);
> + set_user_reg(regs, 7, resp.a7);
> + set_user_reg(regs, 8, resp.a8);
> + set_user_reg(regs, 9, resp.a9);
> + set_user_reg(regs, 10, resp.a10);
> + set_user_reg(regs, 11, resp.a11);
> + set_user_reg(regs, 12, resp.a12);
> + set_user_reg(regs, 13, resp.a13);
> + set_user_reg(regs, 14, resp.a14);
> + set_user_reg(regs, 15, resp.a15);
> + set_user_reg(regs, 16, resp.a16);
> + set_user_reg(regs, 17, resp.a17);
> + return;
> default:
> /* Bad fid, report back to the caller. */
> ffa_set_regs_error(regs, FFA_RET_ABORTED);
> @@ -107,6 +131,21 @@ void ffa_handle_msg_send_direct_req(struct cpu_user_regs *regs, uint32_t fid)
> arg.a6 = get_user_reg(regs, 6) & mask;
> arg.a7 = get_user_reg(regs, 7) & mask;
>
> + if ( fid == FFA_MSG_SEND_DIRECT_REQ2 )
> + {
> + /* 17 registers are used for REQ2 */
> + arg.a8 = get_user_reg(regs, 8);
> + arg.a9 = get_user_reg(regs, 9);
> + arg.a10 = get_user_reg(regs, 10);
> + arg.a11 = get_user_reg(regs, 11);
> + arg.a12 = get_user_reg(regs, 12);
> + arg.a13 = get_user_reg(regs, 13);
> + arg.a14 = get_user_reg(regs, 14);
> + arg.a15 = get_user_reg(regs, 15);
> + arg.a16 = get_user_reg(regs, 16);
> + arg.a17 = get_user_reg(regs, 17);
> + }
> +
> ffa_finish_direct_req_run(regs, &arg);
> return;
>
> --
> 2.51.2
>
© 2016 - 2025 Red Hat, Inc.