[PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)

Jamie Nguyen posted 3 patches 3 weeks, 3 days ago
drivers/acpi/arm64/ffh.c          | 185 ++++++++++++++++++++++++++++++
drivers/firmware/arm_ffa/driver.c | 169 +++++++++++++++++++++++++--
include/linux/acpi.h              |  41 +++++++
3 files changed, 386 insertions(+), 9 deletions(-)
[PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 3 weeks, 3 days ago
Arm DEN0048D (Functional Fixed Hardware Specification v1.3), published in
March 2026, adds a third FFH Operation Region flavour:

  https://developer.arm.com/documentation/den0048/latest/

An Operation Region declared with an Offset of 0x2 triggers an
FFA_MSG_SEND_DIRECT_REQ2 call instead of a bare SMC or HVC:

  OperationRegion (AFFH, FFixedHW, 2, 40)
  Field (AFFH, BufferAcc, NoLock, Preserve) { FFAD, 320 }

Each 64-bit field is one register, ordered from X0. X0 carries the call
status on return, X1[15:0] the receiver endpoint ID (or zero, which asks
OSPM to resolve it from the UUID), X2-X3 the service UUID written with
ToUUID(), and X4-X17 the payload. The region Length is 32 + 8 * N bytes
with 1 <= N <= 14, so X0-X4 at minimum and X0-X17 at most.

The spec recommends offset 0x2 for new platforms on the grounds that not
every OSPM implements offsets 0x0 and 0x1. Linux has had both since v6.2
but nothing for 0x2, so AML using the recommended encoding currently gets
AE_ERROR back.

These patches implement it. I could not find any prior posting of this on
linux-acpi or linux-arm-kernel, so apologies if I have missed one and
duplicated someone's work.

All three patches are co-developed with Dat Mach.

Design
------

drivers/acpi/arm64/ffh.c holds the DEN0048D side: region length
validation, the X0-X17 layout, the ToUUID() to FF-A UUID byte order
conversion, and the table 3 status codes. drivers/firmware/arm_ffa/ holds
the FF-A side: resolving a service UUID to an endpoint, and the call
itself, including the FFA_YIELD and FFA_INTERRUPT re-invocation DEN0048D
asks for.

The two talk through an ops structure the FF-A driver registers rather
than a direct call, because ffh.c is built in under a bool Kconfig symbol
while CONFIG_ARM_FFA_TRANSPORT is a tristate. Unregistration takes the
rwsem for writing, so it cannot race with an access already in flight.

None of what the handler needs was reachable through the existing
ffa_device interface. ffa_sync_send_receive2() always addresses
dev->vm_id, so a receiver endpoint ID supplied by AML cannot be honoured.
UUID to endpoint resolution had no in-kernel user at all. And
ffa_msg_send_direct_req2() throws away the response registers DEN0048D
wants copied back to AML. Patch 1 splits those out, leaving what existing
callers see unchanged.

Testing
-------

Built on arm64 with CONFIG_ACPI_FFH=y and CONFIG_ARM_FFA_TRANSPORT both =y
and =m, and with CONFIG_ACPI_FFH=n, W=1 clean. Every patch builds on its
own.

Runtime tested on an Arm server whose firmware reports FF-A 1.3 and picks
offset 2 in its TPM Physical Presence Interface method. Reading
/sys/class/tpm/tpm0/ppi/response drives it; kprobes show the call reaching
ffa_acpi_ffh_direct_req2() with the endpoint and the fourteen payload
registers the region implies. That method only returns a response when the
status field reads back zero, and it does.

That firmware only ever sends well formed requests naming the endpoint, so
the rest was driven from test SSDTs loaded through CONFIG_ACPI_CONFIGFS
against the same partition, one invocation per table:

  X1 zero, UUID set      endpoint resolved from the UUID, call succeeds
  X1 zero, UUID nil      rejected before any FF-A call
  X1 an unknown endpoint call attempted rather than refused, and the
                         callee's FFA_ERROR reported as FFH_FFA_CALL_FAILED
                         with the FF-A error code in X2 per table 3
  length 0x28, the min   one payload register, call succeeds
  length 0x24, invalid   rejected before any FF-A call

A second server, whose FF-A 1.1 firmware cannot do
FFA_MSG_SEND_DIRECT_REQ2, covers FFH_FFA_NOT_SUPPORTED and regressions: it
declares no FFH Operation Regions, and an offset 2 access there returns
AE_ERROR without this series. It has one service UUID per endpoint where
the first machine has six sharing one, covering both topologies.

With CONFIG_ARM_FFA_TRANSPORT=m, which nothing autoloads, an access before
the module is loaded reports FFH_FFA_NOT_SUPPORTED, loading makes it reach
the FF-A driver, unloading returns it to NOT_SUPPORTED, and reloading
reaches it again.

One path is unexercised: nothing provokes a response that is neither
FFA_ERROR nor FFA_MSG_SEND_DIRECT_RESP2, so the -EPROTO mapping is
untested. Neither platform has a UUID resolving to more than one endpoint,
so the endpoint ID comparison in ffa_acpi_ffh_partition_id() was exercised
with a throwaway stub reporting two differing IDs: it returns -ENOTUNIQ,
no FF-A call is attempted, and AML reads back FFH_FFA_INVALID_PARAMETERS.
ffa_device_match_uuid() already walks the descriptors a UUID query
returns, so that case looks reachable rather than theoretical.

Open question
-------------

1. ffa_msg_send_wait_for_completion() loops on FFA_YIELD and FFA_INTERRUPT
   with no bound, sleeping 1 ms per FFA_YIELD round. The loop predates this
   series and DEN0048D does require the reinvocation, but until now it was
   only reachable from in-kernel FF-A drivers. This series opens it up to
   any control method that writes the Operation Region, including at
   enumeration time, so a partition that keeps yielding would stall an AML
   thread indefinitely.

   Bounding it would affect the existing FF-A callers too, and table 3 has
   no status code for a call that was abandoned, so it may be out of scope
   for this series unless you disagree.

v1: https://lore.kernel.org/all/20260729175638.3796440-1-jamien@nvidia.com/

Changes since v1:
 - Rebased onto v7.3-rc1. The code is byte for byte identical to v1; only
   the base moved, and it applies to current linux-next as well.
 - The testing above was repeated on v7.3-rc1: the build matrix
   (CONFIG_ACPI_FFH=y and =n, CONFIG_ARM_FFA_TRANSPORT=y and =m, W=1 over
   both directories, and each patch on its own), and the runtime testing on
   both machines. On the FF-A 1.1 machine a diff against an unpatched
   v7.3-rc1 shows FF-A bringup, partitions, driver bindings and the whole
   dmesg error set unchanged, with the offset 2 access moving from AE_ERROR
   to FFH_FFA_NOT_SUPPORTED.
 - Not repeated on rc1: the throwaway stub for the endpoint ID comparison,
   and the CONFIG_ARM_FFA_TRANSPORT=m load and unload cycle. Both were run
   on the v1 base.
 - No review comments were received on v1.

Jamie Nguyen (3):
  firmware: arm_ffa: Split the response out of
    ffa_msg_send_direct_req2()
  ACPI: arm64: Add support for the FF-A FFH Operation Region (offset 2)
  firmware: arm_ffa: Back the ACPI FF-A FFH Operation Region

 drivers/acpi/arm64/ffh.c          | 185 ++++++++++++++++++++++++++++++
 drivers/firmware/arm_ffa/driver.c | 169 +++++++++++++++++++++++++--
 include/linux/acpi.h              |  41 +++++++
 3 files changed, 386 insertions(+), 9 deletions(-)


base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
-- 
2.43.0
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Sudeep Holla 4 days, 2 hours ago
On Tue, Sep 01, 2026 at 12:29:03PM -0700, Jamie Nguyen wrote:
> Arm DEN0048D (Functional Fixed Hardware Specification v1.3), published in
> March 2026, adds a third FFH Operation Region flavour:
> 
>   https://developer.arm.com/documentation/den0048/latest/
> 
> An Operation Region declared with an Offset of 0x2 triggers an
> FFA_MSG_SEND_DIRECT_REQ2 call instead of a bare SMC or HVC:
> 
>   OperationRegion (AFFH, FFixedHW, 2, 40)
>   Field (AFFH, BufferAcc, NoLock, Preserve) { FFAD, 320 }
> 
> Each 64-bit field is one register, ordered from X0. X0 carries the call
> status on return, X1[15:0] the receiver endpoint ID (or zero, which asks
> OSPM to resolve it from the UUID), X2-X3 the service UUID written with
> ToUUID(), and X4-X17 the payload. The region Length is 32 + 8 * N bytes
> with 1 <= N <= 14, so X0-X4 at minimum and X0-X17 at most.
> 
> The spec recommends offset 0x2 for new platforms on the grounds that not
> every OSPM implements offsets 0x0 and 0x1. Linux has had both since v6.2
> but nothing for 0x2, so AML using the recommended encoding currently gets
> AE_ERROR back.
> 
> These patches implement it. I could not find any prior posting of this on
> linux-acpi or linux-arm-kernel, so apologies if I have missed one and
> duplicated someone's work.
> 
> All three patches are co-developed with Dat Mach.
> 
> Design
> ------
> 
> drivers/acpi/arm64/ffh.c holds the DEN0048D side: region length
> validation, the X0-X17 layout, the ToUUID() to FF-A UUID byte order
> conversion, and the table 3 status codes. drivers/firmware/arm_ffa/ holds
> the FF-A side: resolving a service UUID to an endpoint, and the call
> itself, including the FFA_YIELD and FFA_INTERRUPT re-invocation DEN0048D
> asks for.
>

I have concerns regarding the new dependency on the FF-A driver.

Could you clarify how the build and runtime behavior are protected when the
FF-A driver is disabled? The current patchset introduces a strict dependency
without explicit configuration checks or guards, which will likely cause
compilation or execution failures in environments where FF-A is not enabled.

> The two talk through an ops structure the FF-A driver registers rather
> than a direct call, because ffh.c is built in under a bool Kconfig symbol
> while CONFIG_ARM_FFA_TRANSPORT is a tristate. Unregistration takes the
> rwsem for writing, so it cannot race with an access already in flight.
> 

We need to explicitly document the reasons for establishing a hard dependency
between the ACPI FFH and the FF-A driver. In my view, this FFH mechanism (0x2)
simply issues an SMC call with the FFA_MSG_SEND_DIRECT_REQ2 FID. While I
understand the specification assigns extra responsibility to the OSPM to
populate the partition ID, are there any other technical justifications for
this tight coupling?

> None of what the handler needs was reachable through the existing
> ffa_device interface. ffa_sync_send_receive2() always addresses
> dev->vm_id, so a receiver endpoint ID supplied by AML cannot be honoured.
> UUID to endpoint resolution had no in-kernel user at all. And
> ffa_msg_send_direct_req2() throws away the response registers DEN0048D
> wants copied back to AML. Patch 1 splits those out, leaving what existing
> callers see unchanged.
>

I see above as one reason why they need to be separate.

> Testing
> -------
> 
> Built on arm64 with CONFIG_ACPI_FFH=y and CONFIG_ARM_FFA_TRANSPORT both =y
> and =m, and with CONFIG_ACPI_FFH=n, W=1 clean. Every patch builds on its
> own.
> 

How about CONFIG_ARM_FFA_TRANSPORT=n and CONFIG_ACPI_FFH=y ? Is that
blocked in someway I am failing to see ?

-- 
Regards,
Sudeep
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 3 days, 19 hours ago
On Tue, Sep 22, 2026 at 09:46:59AM +0100, Sudeep Holla wrote:
> Could you clarify how the build and runtime behavior are protected when
> the FF-A driver is disabled?

There is no build-time dependency from ACPI FFH to the FF-A driver. The
registration direction is from FF-A into FFH: the FF-A driver registers an
optional backend after probe and unregisters it on removal.

With CONFIG_ARM_FFA_TRANSPORT=n, no backend is registered and an offset 2
access returns FFH_FFA_NOT_SUPPORTED. With CONFIG_ACPI_FFH=n, the
registration functions are static inline stubs.

I have also built CONFIG_ACPI_FFH=y with CONFIG_ARM_FFA_TRANSPORT=n.
The W=1 build and full vmlinux link are clean, with no unresolved
references to the FF-A transport driver. I'll mention this case in the v3
cover letter.

> Are there any other technical justifications for this tight coupling?

FFA_MSG_SEND_DIRECT_REQ2 requires FF-A state already owned by the driver:
the selected SMC or HVC conduit, the sender endpoint ID from FFA_ID_GET,
UUID-to-endpoint resolution through the partition discovery ABIs, REQ2
feature detection, and FFA_RUN handling after FFA_YIELD or FFA_INTERRUPT.

Open-coding those operations in ffh.c would duplicate FF-A initialization
and state management. The ops registration lets FFH use that state when the
driver is available while degrading cleanly when it is not.
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Sudeep Holla 3 days, 16 hours ago
On Tue, Sep 22, 2026 at 08:52:11AM -0700, Jamie Nguyen wrote:
> On Tue, Sep 22, 2026 at 09:46:59AM +0100, Sudeep Holla wrote:
> > Could you clarify how the build and runtime behavior are protected when
> > the FF-A driver is disabled?
> 
> There is no build-time dependency from ACPI FFH to the FF-A driver. The
> registration direction is from FF-A into FFH: the FF-A driver registers an
> optional backend after probe and unregisters it on removal.
> 

Equally ugly. FF-A spec doesn't talk about ACPI FFH, so it doesn't
belong in FF-A driver.

> With CONFIG_ARM_FFA_TRANSPORT=n, no backend is registered and an offset 2
> access returns FFH_FFA_NOT_SUPPORTED. With CONFIG_ACPI_FFH=n, the
> registration functions are static inline stubs.
> 

So if on a platform, ASL has FFH with offset 2, we fail if FF-A driver fails
to initialise ? What if this FFH offset 2 is used in some other device
methods that need to be executed before FF-A module is inserted ? We pull
that device down as well ? Is that acceptable ?

> I have also built CONFIG_ACPI_FFH=y with CONFIG_ARM_FFA_TRANSPORT=n.
> The W=1 build and full vmlinux link are clean, with no unresolved
> references to the FF-A transport driver. I'll mention this case in the v3
> cover letter.
> 

Fair enough, build dependency was one of the concern and not the only
concern.

> > Are there any other technical justifications for this tight coupling?
> 
> FFA_MSG_SEND_DIRECT_REQ2 requires FF-A state already owned by the driver:
> the selected SMC or HVC conduit, the sender endpoint ID from FFA_ID_GET,
> UUID-to-endpoint resolution through the partition discovery ABIs, REQ2
> feature detection, and FFA_RUN handling after FFA_YIELD or FFA_INTERRUPT.
> 

Yes but are they strong reasons to break some use-case like above.

> Open-coding those operations in ffh.c would duplicate FF-A initialization
> and state management. The ops registration lets FFH use that state when the
> driver is available while degrading cleanly when it is not.

There is no state management with simple FFA_MSG_SEND_DIRECT_REQ2, it is
synchronous send request if you look at it along wih offset 0/1 and without
FF-A driver knowledge.

I need to think apart from duplication, we shouldn't have much issue.

-- 
Regards,
Sudeep
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 2 days, 22 hours ago
On Tue, Sep 22, 2026 at 07:06:02PM +0100, Sudeep Holla wrote:
> Equally ugly. FF-A spec doesn't talk about ACPI FFH, so it doesn't
> belong in FF-A driver.

Okay -- I'll move the ACPI-specific handling into ffh.c.

> So if on a platform, ASL has FFH with offset 2, we fail if FF-A driver
> fails to initialise ? What if this FFH offset 2 is used in some other
> device methods that need to be executed before FF-A module is inserted ?
> We pull that device down as well ? Is that acceptable ?

You're right. With the current registration, offset 2 may be unavailable
during initial enumeration or before module load. That's not suitable for
AML.

> There is no state management with simple FFA_MSG_SEND_DIRECT_REQ2, it is
> synchronous send request if you look at it along wih offset 0/1 and
> without FF-A driver knowledge.

The request itself is synchronous and register-only. But the whole operation
still shares FF-A state: version negotiation must be coordinated with other
FF-A use, and X1 == 0 needs UUID lookup. It looks like
FFA_PARTITION_INFO_GET_REGS is optional at the Non-secure physical instance
according to DEN0077A Table 13.1. As such, I believe a portable
implementation would need the shared RX/TX buffers. For example, if X1 is
zero and that ABI is unavailable, UUID lookup would need to fall back to
FFA_PARTITION_INFO_GET, whose results are returned through the caller's RX
buffer.
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Sudeep Holla 2 days, 21 hours ago
On Wed, Sep 23, 2026 at 05:40:50AM -0700, Jamie Nguyen wrote:
> On Tue, Sep 22, 2026 at 07:06:02PM +0100, Sudeep Holla wrote:
> > Equally ugly. FF-A spec doesn't talk about ACPI FFH, so it doesn't
> > belong in FF-A driver.
> 
> Okay -- I'll move the ACPI-specific handling into ffh.c.
> 
> > So if on a platform, ASL has FFH with offset 2, we fail if FF-A driver
> > fails to initialise ? What if this FFH offset 2 is used in some other
> > device methods that need to be executed before FF-A module is inserted ?
> > We pull that device down as well ? Is that acceptable ?
> 
> You're right. With the current registration, offset 2 may be unavailable
> during initial enumeration or before module load. That's not suitable for
> AML.
> 
> > There is no state management with simple FFA_MSG_SEND_DIRECT_REQ2, it is
> > synchronous send request if you look at it along wih offset 0/1 and
> > without FF-A driver knowledge.
> 
> The request itself is synchronous and register-only. But the whole operation
> still shares FF-A state: version negotiation must be coordinated with other
> FF-A use, and X1 == 0 needs UUID lookup. It looks like
> FFA_PARTITION_INFO_GET_REGS is optional at the Non-secure physical instance
> according to DEN0077A Table 13.1. As such, I believe a portable
> implementation would need the shared RX/TX buffers. For example, if X1 is
> zero and that ABI is unavailable, UUID lookup would need to fall back to
> FFA_PARTITION_INFO_GET, whose results are returned through the caller's RX
> buffer.

I did try to come up something simpler and independent last evening and
this morning, far from completion and I am also thinking of 2 main changes
to it yet:
1. Maintain hash for receiver ID list
2. Drop the whole FFA_VERSION fuss as it must be dealt with in UEFI and
   that should be sufficient.

Of course not tested yet and even not sure this version is compile tested.
Also more checks needs to added like INFO_GET_REGS is used without check.

Regards,
Sudeep

-->8

From a78e71fe5a759687fbc205b817033f1a381b8c5e Mon Sep 17 00:00:00 2001
From: Sudeep Holla <sudeep.holla@kernel.org>
Date: Wed, 23 Sep 2026 13:18:37 +0100
Subject: [PATCH 1/2] firmware: arm_ffa: Share FF-A register field definitions

ACPI FFH and the FF-A transport driver will be interpreting the same
PARTITION_INFO_GET_REGS metadata and pack endpoint IDs using the same
bit fields. Define these fields once in arm_ffa.h instead of keeping
private copies.

Use the shared masks in the transport driver and check descriptor
lengths against the same minimum used by ACPI FFH. This keeps the ABI
encoding consistent between the two users.

Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
 drivers/firmware/arm_ffa/driver.c | 48 +++++++++++--------------------
 include/linux/arm_ffa.h           | 19 ++++++++++++
 2 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index 69d5f754dce4..05306ea484df 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -55,13 +55,6 @@
 #define FFA_MIN_VERSION		FFA_VERSION_1_0
 #define FFA_PLATFORM_NAME	"arm-ffa"
 
-#define SENDER_ID_MASK		GENMASK(31, 16)
-#define RECEIVER_ID_MASK	GENMASK(15, 0)
-#define SENDER_ID(x)		((u16)(FIELD_GET(SENDER_ID_MASK, (x))))
-#define RECEIVER_ID(x)		((u16)(FIELD_GET(RECEIVER_ID_MASK, (x))))
-#define PACK_TARGET_INFO(s, r)		\
-	(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))
-
 #define RXTX_MAP_MIN_BUFSZ_MASK	GENMASK(1, 0)
 #define RXTX_MAP_MAX_BUFSZ_MASK	GENMASK(31, 16)
 #define RXTX_MAP_MIN_BUFSZ(x)	(FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x)))
@@ -328,24 +321,16 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
 	return count;
 }
 
-#define LAST_INDEX_MASK		GENMASK(15, 0)
-#define CURRENT_INDEX_MASK	GENMASK(31, 16)
-#define UUID_INFO_TAG_MASK	GENMASK(47, 32)
-#define PARTITION_INFO_SZ_MASK	GENMASK(63, 48)
-#define PARTITION_COUNT(x)	((u16)(FIELD_GET(LAST_INDEX_MASK, (x))) + 1)
-#define CURRENT_INDEX(x)	((u16)(FIELD_GET(CURRENT_INDEX_MASK, (x))))
-#define UUID_INFO_TAG(x)	((u16)(FIELD_GET(UUID_INFO_TAG_MASK, (x))))
-#define PARTITION_INFO_SZ(x)	((u16)(FIELD_GET(PARTITION_INFO_SZ_MASK, (x))))
-#define PART_INFO_ID_MASK	GENMASK(15, 0)
-#define PART_INFO_EXEC_CXT_MASK	GENMASK(31, 16)
-#define PART_INFO_PROPS_MASK	GENMASK(63, 32)
+#define PARTITION_COUNT(x)	((u16)(FIELD_GET(FFA_PART_INFO_LAST_IDX_MASK, (x))) + 1)
+#define CURRENT_INDEX(x)	((u16)(FIELD_GET(FFA_PART_INFO_CUR_IDX_MASK, (x))))
+#define UUID_INFO_TAG(x)	((u16)(FIELD_GET(FFA_PART_INFO_UUID_TAG_MASK, (x))))
+#define PARTITION_INFO_SZ(x)	((u16)(FIELD_GET(FFA_PART_INFO_DESC_SIZE_MASK, (x))))
 #define FFA_PART_INFO_GET_REGS_FIRST_REG	3
-#define FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC	3
 #define FFA_PART_INFO_GET_REGS_NUM_REGS \
 	(sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0))
-#define PART_INFO_ID(x)		((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))
-#define PART_INFO_EXEC_CXT(x)	((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))
-#define PART_INFO_PROPERTIES(x)	((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
+#define PART_INFO_ID(x)		((u16)(FIELD_GET(FFA_PART_INFO_ID_MASK, (x))))
+#define PART_INFO_EXEC_CXT(x)	((u16)(FIELD_GET(FFA_PART_INFO_EXEC_CXT_MASK, (x))))
+#define PART_INFO_PROPERTIES(x)	((u32)(FIELD_GET(FFA_PART_INFO_PROPS_MASK, (x))))
 static int
 __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
 			      struct ffa_partition_info *buffer, int num_parts)
@@ -383,9 +368,9 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
 		if (buf_sz % sizeof(*regs))
 			return -EINVAL;
 
-		regs_per_desc = buf_sz / sizeof(*regs);
-		if (regs_per_desc < FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC)
+		if (buf_sz < FFA_PART_INFO_MIN_DESC_SIZE)
 			return -EINVAL;
+		regs_per_desc = buf_sz / sizeof(*regs);
 
 		nr_desc = cur_idx - start_idx + 1;
 		max_desc = (FFA_PART_INFO_GET_REGS_NUM_REGS -
@@ -501,7 +486,7 @@ static inline void ffa_msg_send_wait_for_completion(ffa_value_t *ret)
 static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
 				   struct ffa_send_direct_data *data)
 {
-	u32 req_id, resp_id, src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
+	u32 req_id, resp_id, src_dst_ids = ffa_pack_target_info(src_id, dst_id);
 	ffa_value_t ret;
 
 	if (mode_32bit) {
@@ -537,7 +522,7 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
 
 static int ffa_msg_send2(struct ffa_device *dev, u16 src_id, void *buf, size_t sz)
 {
-	u32 src_dst_ids = PACK_TARGET_INFO(src_id, dev->vm_id);
+	u32 src_dst_ids = ffa_pack_target_info(src_id, dev->vm_id);
 	struct ffa_indirect_msg_hdr *msg;
 	ffa_value_t ret;
 	int retval = 0;
@@ -571,7 +556,7 @@ static int ffa_msg_send2(struct ffa_device *dev, u16 src_id, void *buf, size_t s
 static int ffa_msg_send_direct_req2(u16 src_id, u16 dst_id, const uuid_t *uuid,
 				    struct ffa_send_direct_data2 *data)
 {
-	u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
+	u32 src_dst_ids = ffa_pack_target_info(src_id, dst_id);
 	union {
 		uuid_t uuid;
 		__le64 regs[2];
@@ -897,7 +882,7 @@ enum notify_type {
 #define RECEIVER_VCPU_MASK		GENMASK(31, 16)
 #define PACK_NOTIFICATION_GET_RECEIVER_INFO(vcpu_r, r) \
 	(FIELD_PREP(RECEIVER_VCPU_MASK, (vcpu_r)) | \
-	 FIELD_PREP(RECEIVER_ID_MASK, (r)))
+	 FIELD_PREP(FFA_TARGET_INFO_RECEIVER_MASK, (r)))
 
 #define NOTIFICATION_INFO_GET_MORE_PEND_MASK	BIT(0)
 #define NOTIFICATION_INFO_GET_ID_COUNT		GENMASK(11, 7)
@@ -928,7 +913,7 @@ static int ffa_notification_bind_common(u16 dst_id, u64 bitmap,
 					u32 flags, bool is_bind)
 {
 	ffa_value_t ret;
-	u32 func, src_dst_ids = PACK_TARGET_INFO(dst_id, drv_info->vm_id);
+	u32 func, src_dst_ids = ffa_pack_target_info(dst_id, drv_info->vm_id);
 
 	func = is_bind ? FFA_NOTIFICATION_BIND : FFA_NOTIFICATION_UNBIND;
 
@@ -950,7 +935,7 @@ static
 int ffa_notification_set(u16 src_id, u16 dst_id, u32 flags, u64 bitmap)
 {
 	ffa_value_t ret;
-	u32 src_dst_ids = PACK_TARGET_INFO(dst_id, src_id);
+	u32 src_dst_ids = ffa_pack_target_info(dst_id, src_id);
 
 	invoke_ffa_fn((ffa_value_t) {
 		  .a0 = FFA_NOTIFICATION_SET, .a1 = src_dst_ids, .a2 = flags,
@@ -1567,7 +1552,8 @@ static void handle_fwk_notif_callbacks(u32 bitmap)
 			return;
 		}
 
-		target = SENDER_ID(msg->send_recv_id);
+		target = FIELD_GET(FFA_TARGET_INFO_SENDER_MASK,
+				   msg->send_recv_id);
 		if (offset >= sizeof(*msg))
 			uuid_copy(&uuid, &msg->uuid);
 		else
diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index e71d83ee0aef..3831fbe244c1 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -130,6 +130,15 @@
 #define FFA_FEAT_RXTX_MIN_SZ_16K	2
 #define FFA_FEAT_RXTX_MIN_SZ_MASK	GENMASK(1, 0)
 
+#define FFA_TARGET_INFO_SENDER_MASK	GENMASK(31, 16)
+#define FFA_TARGET_INFO_RECEIVER_MASK	GENMASK(15, 0)
+
+static inline u32 ffa_pack_target_info(u16 sender, u16 receiver)
+{
+	return FIELD_PREP(FFA_TARGET_INFO_SENDER_MASK, sender) |
+	       FIELD_PREP(FFA_TARGET_INFO_RECEIVER_MASK, receiver);
+}
+
 /* FFA Bus/Device/Driver related */
 struct ffa_device {
 	u32 id;
@@ -228,6 +237,16 @@ extern const struct bus_type ffa_bus_type;
 /* The FF-A 1.0 partition structure lacks the uuid[4] */
 #define FFA_1_0_PARTITON_INFO_SZ	(8)
 
+/* FFA_PARTITION_INFO_GET_REGS response fields */
+#define FFA_PART_INFO_LAST_IDX_MASK	GENMASK_ULL(15, 0)
+#define FFA_PART_INFO_CUR_IDX_MASK	GENMASK_ULL(31, 16)
+#define FFA_PART_INFO_UUID_TAG_MASK	GENMASK_ULL(47, 32)
+#define FFA_PART_INFO_DESC_SIZE_MASK	GENMASK_ULL(63, 48)
+#define FFA_PART_INFO_MIN_DESC_SIZE	24
+#define FFA_PART_INFO_ID_MASK		GENMASK_ULL(15, 0)
+#define FFA_PART_INFO_EXEC_CXT_MASK	GENMASK_ULL(31, 16)
+#define FFA_PART_INFO_PROPS_MASK		GENMASK_ULL(63, 32)
+
 /* FFA transport related */
 struct ffa_partition_info {
 	u16 id;
-- 
2.43.0


From 47807754d0d21003a37a6d30022317d8696e0daa Mon Sep 17 00:00:00 2001
From: Sudeep Holla <sudeep.holla@kernel.org>
Date: Tue, 22 Sep 2026 14:36:24 +0100
Subject: [PATCH 2/2][WIP] ACPI: FFH: Support FF-A direct request 2 calls

Arm Functional Fixed Hardware specification v1.3 defines FFH
OperationRegion offset 2 for issuing FFA_MSG_SEND_DIRECT_REQ2 calls.
The arm64 FFH handler currently supports only generic 32-bit and 64-bit
SMCCC calls, so it cannot implement this standardized interface.

Add a standalone FF-A path using the existing SMCCC conduit. Negotiate
FF-A v1.2, discover the sender endpoint with FFA_ID_GET, and use either
the receiver supplied by firmware or resolve a unique receiver from the
service UUID with FFA_PARTITION_INFO_GET_REGS.

Convert the ACPI ToUUID representation to the FF-A register encoding,
construct the direct request, resume interrupted or yielded calls with
FFA_RUN, and return the defined FFH status codes to AML.

Register-based partition discovery avoids RX/TX buffer ownership and
keeps the FFH implementation independent of the Arm FF-A transport
driver.

Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
 drivers/acpi/arm64/ffh.c | 223 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 223 insertions(+)

diff --git a/drivers/acpi/arm64/ffh.c b/drivers/acpi/arm64/ffh.c
index 04380bab193d..6975f236b9b4 100644
--- a/drivers/acpi/arm64/ffh.c
+++ b/drivers/acpi/arm64/ffh.c
@@ -1,7 +1,22 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <linux/acpi.h>
+#include <linux/arm_ffa.h>
 #include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/unaligned.h>
+
+#define FFH_FFA_CALL_FAILED		1
+#define FFH_FFA_SUCCESS			0
+#define FFH_FFA_NOT_SUPPORTED		-1
+#define FFH_FFA_INVALID_PARAMETERS	-2
+#define FFH_FFA_OUT_OF_MEMORY		-3
+#define FFH_FFA_UNSPECIFIED_ERROR	-4
+
+#define FFH_FFA_MIN_LEN			(5 * sizeof(u64))
+#define FFH_FFA_MAX_LEN			(18 * sizeof(u64))
+#define FFH_FFA_UUID_OFFSET		(2 * sizeof(u64))
 
 /*
  * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as
@@ -19,6 +34,212 @@ struct acpi_ffh_data {
 				struct arm_smccc_1_2_regs *res);
 };
 
+static DEFINE_MUTEX(acpi_ffh_ffa_lock);
+static bool acpi_ffh_ffa_version_negotiated;
+static bool acpi_ffh_ffa_initialized;
+static u16 acpi_ffh_ffa_sender_id;
+
+static void acpi_ffh_ffa_invoke(struct acpi_ffh_data *ffh_ctxt,
+				const struct arm_smccc_1_2_regs *args,
+				struct arm_smccc_1_2_regs *res)
+{
+	ffh_ctxt->invoke_ffh64_fn(args, res);
+}
+
+static int acpi_ffh_ffa_features(struct acpi_ffh_data *ffh_ctxt, u32 fid)
+{
+	struct arm_smccc_1_2_regs args = {
+		.a0 = FFA_FEATURES,
+		.a1 = fid,
+	};
+	struct arm_smccc_1_2_regs res;
+
+	acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+	if (res.a0 == FFA_ERROR) {
+		if ((s32)res.a2 == FFA_RET_NOT_SUPPORTED)
+			return -EOPNOTSUPP;
+		return -EINVAL;
+	}
+
+	return res.a0 == FFA_SUCCESS ? 0 : -EIO;
+}
+
+static int acpi_ffh_ffa_init(struct acpi_ffh_data *ffh_ctxt)
+{
+	struct arm_smccc_1_2_regs args = { }, res;
+	u32 version;
+	int ret = 0;
+
+	mutex_lock(&acpi_ffh_ffa_lock);
+	if (acpi_ffh_ffa_initialized)
+		goto out;
+
+	if (!acpi_ffh_ffa_version_negotiated) {
+		args.a0 = FFA_VERSION;
+		args.a1 = FFA_VERSION_1_2;
+		acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+		version = res.a0;
+		if ((s32)version == SMCCC_RET_NOT_SUPPORTED ||
+		    FFA_MAJOR_VERSION(version) != 1 ||
+		    version < FFA_VERSION_1_2) {
+			ret = -EOPNOTSUPP;
+			goto out;
+		}
+		acpi_ffh_ffa_version_negotiated = true;
+	}
+
+	ret = acpi_ffh_ffa_features(ffh_ctxt, FFA_MSG_SEND_DIRECT_REQ2);
+	if (ret)
+		goto out;
+
+	ret = acpi_ffh_ffa_features(ffh_ctxt, FFA_MSG_SEND_DIRECT_RESP2);
+	if (ret)
+		goto out;
+
+	args = (struct arm_smccc_1_2_regs) { .a0 = FFA_ID_GET };
+	acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+	if (res.a0 == FFA_ERROR) {
+		ret = (s32)res.a2 == FFA_RET_NOT_SUPPORTED ?
+		      -EOPNOTSUPP : -EIO;
+		goto out;
+	}
+	if (res.a0 != FFA_SUCCESS) {
+		ret = -EIO;
+		goto out;
+	}
+
+	acpi_ffh_ffa_sender_id = (u16)res.a2;
+	acpi_ffh_ffa_initialized = true;
+out:
+	mutex_unlock(&acpi_ffh_ffa_lock);
+	return ret;
+}
+
+static void acpi_ffh_ffa_uuid_convert(const u8 *guid, u8 *uuid)
+{
+	/* ACPI ToUUID uses GUID byte order while FF-A uses UUID byte order. */
+	uuid[0] = guid[3];
+	uuid[1] = guid[2];
+	uuid[2] = guid[1];
+	uuid[3] = guid[0];
+	uuid[4] = guid[5];
+	uuid[5] = guid[4];
+	uuid[6] = guid[7];
+	uuid[7] = guid[6];
+	memcpy(uuid + 8, guid + 8, UUID_SIZE - 8);
+}
+
+static int acpi_ffh_ffa_receiver_id(struct acpi_ffh_data *ffh_ctxt,
+				    u64 uuid_lo, u64 uuid_hi, u16 *receiver_id)
+{
+	struct arm_smccc_1_2_regs args = {
+		.a0 = FFA_PARTITION_INFO_GET_REGS,
+		.a1 = uuid_lo,
+		.a2 = uuid_hi,
+	};
+	struct arm_smccc_1_2_regs res;
+	u32 properties;
+
+	acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+	if (res.a0 != FFA_FN64_SUCCESS)
+		return -EINVAL;
+
+	if (FIELD_GET(FFA_PART_INFO_LAST_IDX_MASK, res.a2) ||
+	    FIELD_GET(FFA_PART_INFO_CUR_IDX_MASK, res.a2) ||
+	    FIELD_GET(FFA_PART_INFO_DESC_SIZE_MASK, res.a2) <
+		    FFA_PART_INFO_MIN_DESC_SIZE)
+		return -EINVAL;
+
+	properties = FIELD_GET(FFA_PART_INFO_PROPS_MASK, res.a3);
+	if (!(properties & FFA_PARTITION_DIRECT_REQ2_RECV))
+		return -EINVAL;
+
+	*receiver_id = FIELD_GET(FFA_PART_INFO_ID_MASK, res.a3);
+	return 0;
+}
+
+static int acpi_ffh_ffa_status(int error)
+{
+	switch (error) {
+	case -EOPNOTSUPP:
+		return FFH_FFA_NOT_SUPPORTED;
+	case -EINVAL:
+		return FFH_FFA_INVALID_PARAMETERS;
+	case -ENOMEM:
+		return FFH_FFA_OUT_OF_MEMORY;
+	default:
+		return FFH_FFA_UNSPECIFIED_ERROR;
+	}
+}
+
+static int acpi_ffh_ffa_direct_req2(acpi_integer *value,
+				    struct acpi_ffh_data *ffh_ctxt)
+{
+	struct arm_smccc_1_2_regs args = { }, res;
+	u64 *buf = (u64 *)value;
+	u64 uuid_lo, uuid_hi;
+	u16 receiver_id = (u16)buf[1];
+	uuid_t uuid;
+	size_t len = ffh_ctxt->info.length;
+	int ret;
+
+	if (len < FFH_FFA_MIN_LEN || len > FFH_FFA_MAX_LEN ||
+	    !IS_ALIGNED(len - 4 * sizeof(u64), sizeof(u64))) {
+		buf[0] = FFH_FFA_INVALID_PARAMETERS;
+		return AE_OK;
+	}
+
+	acpi_ffh_ffa_uuid_convert((u8 *)value + FFH_FFA_UUID_OFFSET, uuid.b);
+	if (uuid_is_null(&uuid)) {
+		buf[0] = FFH_FFA_INVALID_PARAMETERS;
+		return AE_OK;
+	}
+
+	ret = acpi_ffh_ffa_init(ffh_ctxt);
+	if (ret) {
+		buf[0] = acpi_ffh_ffa_status(ret);
+		return AE_OK;
+	}
+
+	uuid_lo = get_unaligned_le64(uuid.b);
+	uuid_hi = get_unaligned_le64(uuid.b + sizeof(uuid_lo));
+	if (!receiver_id) {
+		ret = acpi_ffh_ffa_receiver_id(ffh_ctxt, uuid_lo, uuid_hi,
+					       &receiver_id);
+		if (ret) {
+			buf[0] = FFH_FFA_INVALID_PARAMETERS;
+			return AE_OK;
+		}
+	}
+
+	args.a0 = FFA_MSG_SEND_DIRECT_REQ2;
+	args.a1 = ffa_pack_target_info(acpi_ffh_ffa_sender_id, receiver_id);
+	args.a2 = uuid_lo;
+	args.a3 = uuid_hi;
+	memcpy(&args.a4, &buf[4], len - 4 * sizeof(u64));
+
+	acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+	while (res.a0 == FFA_INTERRUPT || res.a0 == FFA_YIELD) {
+		if (res.a0 == FFA_YIELD)
+			fsleep(1000);
+
+		args = (struct arm_smccc_1_2_regs) {
+			.a0 = FFA_RUN,
+			.a1 = res.a1,
+		};
+		acpi_ffh_ffa_invoke(ffh_ctxt, &args, &res);
+	}
+
+	memcpy(buf, &res, len);
+	if (res.a0 == FFA_MSG_SEND_DIRECT_RESP2 || res.a0 == FFA_SUCCESS ||
+	    res.a0 == FFA_FN64_SUCCESS)
+		buf[0] = FFH_FFA_SUCCESS;
+	else
+		buf[0] = FFH_FFA_CALL_FAILED;
+
+	return AE_OK;
+}
+
 int acpi_ffh_address_space_arch_setup(void *handler_ctxt, void **region_ctxt)
 {
 	enum arm_smccc_conduit conduit;
@@ -99,6 +320,8 @@ int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_contex
 			ffh_ctxt->invoke_ffh64_fn(r, r);
 			memcpy(value, r, ffh_ctxt->info.length);
 		}
+	} else if (ffh_ctxt->info.offset == 2) {
+		ret = acpi_ffh_ffa_direct_req2(value, ffh_ctxt);
 	} else {
 		ret = AE_ERROR;
 	}
-- 
2.43.0
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 2 days, 17 hours ago
On Wed, Sep 23, 2026 at 02:38:28PM +0100, Sudeep Holla wrote:
> I did try to come up something simpler and independent last evening and
> this morning, far from completion and I am also thinking of 2 main changes
> to it yet:
> 1. Maintain hash for receiver ID list
> 2. Drop the whole FFA_VERSION fuss as it must be dealt with in UEFI and
>    that should be sufficient.
>
> Of course not tested yet and even not sure this version is compile tested.

Thanks, this looks much simpler. I can confirm that both patches apply to
v7.3-rc4 and build W=1 clean with ARM_FFA_TRANSPORT=y, =m and =n. The =n
vmlinux also links with no transport-driver symbols pulled in.

On FFA_VERSION: as written, if the transport driver has already mapped
its RX/TX buffers, I think DEN0077A R0216 has this negotiation return the
Null version, so offset 2 would report NOT_SUPPORTED for any AML run after
the driver probes. But if it's dropped, I don't think it's guaranteed that
a compatible version will have been negotiated by then, right?

[...]
> +	memcpy(&args.a4, &buf[4], len - 4 * sizeof(u64));

This would trip CONFIG_FORTIFY_SOURCE's field-spanning write check
whenever the region has more than one payload register. I think this would
cover that case:

	memcpy((void *)&args + offsetof(struct arm_smccc_1_2_regs, a4),
	       &buf[4], len - 4 * sizeof(u64));
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Sudeep Holla 2 days, 1 hour ago
On Wed, Sep 23, 2026 at 10:32:40AM -0700, Jamie Nguyen wrote:
> On Wed, Sep 23, 2026 at 02:38:28PM +0100, Sudeep Holla wrote:
> > I did try to come up something simpler and independent last evening and
> > this morning, far from completion and I am also thinking of 2 main changes
> > to it yet:
> > 1. Maintain hash for receiver ID list
> > 2. Drop the whole FFA_VERSION fuss as it must be dealt with in UEFI and
> >    that should be sufficient.
> >
> > Of course not tested yet and even not sure this version is compile tested.
> 
> Thanks, this looks much simpler. I can confirm that both patches apply to
> v7.3-rc4 and build W=1 clean with ARM_FFA_TRANSPORT=y, =m and =n. The =n
> vmlinux also links with no transport-driver symbols pulled in.
>

Thanks for giving it a spin.

> On FFA_VERSION: as written, if the transport driver has already mapped
> its RX/TX buffers, I think DEN0077A R0216 has this negotiation return the
> Null version, so offset 2 would report NOT_SUPPORTED for any AML run after
> the driver probes. But if it's dropped, I don't think it's guaranteed that
> a compatible version will have been negotiated by then, right?
> 

Agreed, I need to think it through. I definitely want to avoid any usage of
Rx/Tx buffers here.

> [...]
> > +	memcpy(&args.a4, &buf[4], len - 4 * sizeof(u64));
> 
> This would trip CONFIG_FORTIFY_SOURCE's field-spanning write check
> whenever the region has more than one payload register. I think this would
> cover that case:
> 
> 	memcpy((void *)&args + offsetof(struct arm_smccc_1_2_regs, a4),
> 	       &buf[4], len - 4 * sizeof(u64));
> 

Thanks, will update.

Which version of FF-A is being deployed on your platforms ? Just wondering
if we can set v1.3 as minimum required here and use query negotiated version
feature of FFA_VERSION. It is not a must, just checking if that might simplify
things.

-- 
Regards,
Sudeep
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 1 day, 19 hours ago
On Thu, Sep 24, 2026 at 10:39:24AM +0100, Sudeep Holla wrote:
> Which version of FF-A is being deployed on your platforms?

We have platforms with FF-A firmware versions 1.1, 1.2 and 1.3. The
platform I have been using to test offset 2 reports FF-A 1.3.
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Sudeep Holla 1 day, 18 hours ago
On Thu, Sep 24, 2026 at 08:23:21AM -0700, Jamie Nguyen wrote:
> On Thu, Sep 24, 2026 at 10:39:24AM +0100, Sudeep Holla wrote:
> > Which version of FF-A is being deployed on your platforms?
> 
> We have platforms with FF-A firmware versions 1.1, 1.2 and 1.3. The
> platform I have been using to test offset 2 reports FF-A 1.3.
> 

Thanks, will think a bit on this and see what is the best way to progress.

-- 
Regards,
Sudeep
Re: [PATCH v2 0/3] ACPI: arm64: FFH Operation Region support for FF-A (offset 2)
Posted by Jamie Nguyen 1 week ago
Gentle ping on this. The series still applies cleanly to v7.3-rc1 and
current linux-next, and I'm happy to respin if you'd prefer anything
changed.

Thanks,
Jamie