diff mbox series

[4/4] firmware: arm_ffa: Add support for FFA_MSG_SEND2

Message ID 20240415-ffa_msg2_support-v1-4-a28c964b1b3f@arm.com (mailing list archive)
State New
Headers show
Series firmware: arm_ffa: Support for MSG_SEND2 and minor harderning checks | expand

Commit Message

Sudeep Holla April 15, 2024, 4:05 p.m. UTC
The FFA_MSG_SEND2 can be  used to transmit a partition message from
the Tx buffer of the sender(the driver in this case) endpoint to the Rx
buffer of the receiver endpoint.

An invocation of the FFA_MSG_SEND2 transfers the ownership to the
receiver endpoint(or any intermediate consumer). Completion of an
FFA_MSG_SEND2 invocation transfers the ownership back to the sender
endpoint.

The framework defines the FFA_MSG_SEND2 interface to transmit a partition
message from the Tx buffer of the sender to the Rx buffer of a receiver
and inform the scheduler that the receiver must be run.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
 include/linux/arm_ffa.h           |  9 +++++++++
 2 files changed, 49 insertions(+)

Comments

Bertrand Marquis April 16, 2024, 7:26 a.m. UTC | #1
Hi Sudeep,

> On 15 Apr 2024, at 18:05, Sudeep Holla <sudeep.holla@arm.com> wrote:
> 
> The FFA_MSG_SEND2 can be  used to transmit a partition message from
> the Tx buffer of the sender(the driver in this case) endpoint to the Rx
> buffer of the receiver endpoint.
> 
> An invocation of the FFA_MSG_SEND2 transfers the ownership to the
> receiver endpoint(or any intermediate consumer). Completion of an
> FFA_MSG_SEND2 invocation transfers the ownership back to the sender
> endpoint.
> 
> The framework defines the FFA_MSG_SEND2 interface to transmit a partition
> message from the Tx buffer of the sender to the Rx buffer of a receiver
> and inform the scheduler that the receiver must be run.
> 
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
> drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
> include/linux/arm_ffa.h           |  9 +++++++++
> 2 files changed, 49 insertions(+)
> 
> diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
> index d5087e4f6d35..6c2602f7e7cc 100644
> --- a/drivers/firmware/arm_ffa/driver.c
> +++ b/drivers/firmware/arm_ffa/driver.c
> @@ -344,6 +344,34 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
> return -EINVAL;
> }
> 
> +static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
> +{
> + u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
> + struct ffa_indirect_msg_hdr *msg;
> + ffa_value_t ret;
> +
> + mutex_lock(&drv_info->tx_lock);
> +
> + msg = drv_info->tx_buffer;
> + msg->flags = 0;
> + msg->res0 = 0;
> + msg->offset = sizeof(*msg);
> + msg->send_recv_id = src_dst_ids;
> + msg->size = sz;
> + memcpy(msg + msg->offset, buf, sz);

Here there should be a check that the user is not trying to send more data
than what can fit in the TX Buffer.

Other than that LGTM.

Cheers
Bertrand

> +
> + /* flags = 0, sender VMID = 0 works for both physical/virtual NS */
> + invoke_ffa_fn((ffa_value_t){
> +      .a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
> +      }, &ret);
> +
> + if (ret.a0 == FFA_ERROR)
> + return ffa_to_linux_errno((int)ret.a2);
> +
> + mutex_lock(&drv_info->tx_lock);
> + return 0;
> +}
> +
> static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
>      u32 frag_len, u32 len, u64 *handle)
> {
> @@ -888,6 +916,17 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
>       dev->mode_32bit, data);
> }
> 
> +#define ffa_partition_supports_indirect_msg(dev) \
> + ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
> +
> +static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
> +{
> + if (!ffa_partition_supports_direct_recv(dev))
> + return -EOPNOTSUPP;
> +
> + return ffa_msg_send2(drv_info->vm_id, dev->vm_id, buf, sz);
> +}
> +
> static int ffa_memory_share(struct ffa_mem_ops_args *args)
> {
> if (drv_info->mem_ops_native)
> @@ -1167,6 +1206,7 @@ static const struct ffa_info_ops ffa_drv_info_ops = {
> static const struct ffa_msg_ops ffa_drv_msg_ops = {
> .mode_32bit_set = ffa_mode_32bit_set,
> .sync_send_receive = ffa_sync_send_receive,
> + .indirect_send = ffa_indirect_msg_send,
> };
> 
> static const struct ffa_mem_ops ffa_drv_mem_ops = {
> diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
> index 13830be5851d..d61a6df397f6 100644
> --- a/include/linux/arm_ffa.h
> +++ b/include/linux/arm_ffa.h
> @@ -238,6 +238,14 @@ struct ffa_send_direct_data {
> unsigned long data4; /* w7/x7 */
> };
> 
> +struct ffa_indirect_msg_hdr {
> + u32 flags;
> + u32 res0;
> + u32 offset;
> + u32 send_recv_id;
> + u32 size;
> +};
> +
> struct ffa_mem_region_addr_range {
> /* The base IPA of the constituent memory region, aligned to 4 kiB */
> u64 address;
> @@ -398,6 +406,7 @@ struct ffa_msg_ops {
> void (*mode_32bit_set)(struct ffa_device *dev);
> int (*sync_send_receive)(struct ffa_device *dev,
> struct ffa_send_direct_data *data);
> + int (*indirect_send)(struct ffa_device *dev, void *buf, size_t sz);
> };
> 
> struct ffa_mem_ops {
> 
> -- 
> 2.43.2
>
Jens Wiklander April 16, 2024, 7:41 a.m. UTC | #2
On Mon, Apr 15, 2024 at 6:05 PM Sudeep Holla <sudeep.holla@arm.com> wrote:
>
> The FFA_MSG_SEND2 can be  used to transmit a partition message from
> the Tx buffer of the sender(the driver in this case) endpoint to the Rx
> buffer of the receiver endpoint.
>
> An invocation of the FFA_MSG_SEND2 transfers the ownership to the

ownership of the TX buffer to the

> receiver endpoint(or any intermediate consumer). Completion of an
> FFA_MSG_SEND2 invocation transfers the ownership back to the sender

ownership of the buffer back

> endpoint.
>
> The framework defines the FFA_MSG_SEND2 interface to transmit a partition
> message from the Tx buffer of the sender to the Rx buffer of a receiver
> and inform the scheduler that the receiver must be run.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
>  include/linux/arm_ffa.h           |  9 +++++++++
>  2 files changed, 49 insertions(+)
>
> diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
> index d5087e4f6d35..6c2602f7e7cc 100644
> --- a/drivers/firmware/arm_ffa/driver.c
> +++ b/drivers/firmware/arm_ffa/driver.c
> @@ -344,6 +344,34 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
>         return -EINVAL;
>  }
>
> +static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
> +{
> +       u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
> +       struct ffa_indirect_msg_hdr *msg;
> +       ffa_value_t ret;
> +
> +       mutex_lock(&drv_info->tx_lock);
> +
> +       msg = drv_info->tx_buffer;
> +       msg->flags = 0;
> +       msg->res0 = 0;
> +       msg->offset = sizeof(*msg);
> +       msg->send_recv_id = src_dst_ids;
> +       msg->size = sz;
> +       memcpy(msg + msg->offset, buf, sz);
> +
> +       /* flags = 0, sender VMID = 0 works for both physical/virtual NS */
> +       invoke_ffa_fn((ffa_value_t){
> +                     .a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
> +                     }, &ret);
> +
> +       if (ret.a0 == FFA_ERROR)
> +               return ffa_to_linux_errno((int)ret.a2);
> +
> +       mutex_lock(&drv_info->tx_lock);

mutex_unlock(), before the potential return above?

> +       return 0;
> +}
> +
>  static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
>                               u32 frag_len, u32 len, u64 *handle)
>  {
> @@ -888,6 +916,17 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
>                                        dev->mode_32bit, data);
>  }
>
> +#define ffa_partition_supports_indirect_msg(dev)       \
> +       ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
> +
> +static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
> +{
> +       if (!ffa_partition_supports_direct_recv(dev))

ffa_partition_supports_indirect_msg(), but I'm not sure we should do
this check at all. The client could do this in advance. Unexpected
FFA_MSG_SEND2 calls are caught in other layers.

Cheers,
Jens

> +               return -EOPNOTSUPP;
> +
> +       return ffa_msg_send2(drv_info->vm_id, dev->vm_id, buf, sz);
> +}
> +
>  static int ffa_memory_share(struct ffa_mem_ops_args *args)
>  {
>         if (drv_info->mem_ops_native)
> @@ -1167,6 +1206,7 @@ static const struct ffa_info_ops ffa_drv_info_ops = {
>  static const struct ffa_msg_ops ffa_drv_msg_ops = {
>         .mode_32bit_set = ffa_mode_32bit_set,
>         .sync_send_receive = ffa_sync_send_receive,
> +       .indirect_send = ffa_indirect_msg_send,
>  };
>
>  static const struct ffa_mem_ops ffa_drv_mem_ops = {
> diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
> index 13830be5851d..d61a6df397f6 100644
> --- a/include/linux/arm_ffa.h
> +++ b/include/linux/arm_ffa.h
> @@ -238,6 +238,14 @@ struct ffa_send_direct_data {
>         unsigned long data4; /* w7/x7 */
>  };
>
> +struct ffa_indirect_msg_hdr {
> +       u32 flags;
> +       u32 res0;
> +       u32 offset;
> +       u32 send_recv_id;
> +       u32 size;
> +};
> +
>  struct ffa_mem_region_addr_range {
>         /* The base IPA of the constituent memory region, aligned to 4 kiB */
>         u64 address;
> @@ -398,6 +406,7 @@ struct ffa_msg_ops {
>         void (*mode_32bit_set)(struct ffa_device *dev);
>         int (*sync_send_receive)(struct ffa_device *dev,
>                                  struct ffa_send_direct_data *data);
> +       int (*indirect_send)(struct ffa_device *dev, void *buf, size_t sz);
>  };
>
>  struct ffa_mem_ops {
>
> --
> 2.43.2
>
Sudeep Holla April 16, 2024, 8:48 a.m. UTC | #3
On Tue, Apr 16, 2024 at 09:41:51AM +0200, Jens Wiklander wrote:
> On Mon, Apr 15, 2024 at 6:05 PM Sudeep Holla <sudeep.holla@arm.com> wrote:
> >
> > The FFA_MSG_SEND2 can be  used to transmit a partition message from
> > the Tx buffer of the sender(the driver in this case) endpoint to the Rx
> > buffer of the receiver endpoint.
> >
> > An invocation of the FFA_MSG_SEND2 transfers the ownership to the
> 
> ownership of the TX buffer to the
> 
> > receiver endpoint(or any intermediate consumer). Completion of an
> > FFA_MSG_SEND2 invocation transfers the ownership back to the sender
> 
> ownership of the buffer back
> 
> > endpoint.
> >
> > The framework defines the FFA_MSG_SEND2 interface to transmit a partition
> > message from the Tx buffer of the sender to the Rx buffer of a receiver
> > and inform the scheduler that the receiver must be run.
> >
> > Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> > ---
> >  drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
> >  include/linux/arm_ffa.h           |  9 +++++++++
> >  2 files changed, 49 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
> > index d5087e4f6d35..6c2602f7e7cc 100644
> > --- a/drivers/firmware/arm_ffa/driver.c
> > +++ b/drivers/firmware/arm_ffa/driver.c
> > @@ -344,6 +344,34 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
> >         return -EINVAL;
> >  }
> >
> > +static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
> > +{
> > +       u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
> > +       struct ffa_indirect_msg_hdr *msg;
> > +       ffa_value_t ret;
> > +
> > +       mutex_lock(&drv_info->tx_lock);
> > +
> > +       msg = drv_info->tx_buffer;
> > +       msg->flags = 0;
> > +       msg->res0 = 0;
> > +       msg->offset = sizeof(*msg);
> > +       msg->send_recv_id = src_dst_ids;
> > +       msg->size = sz;
> > +       memcpy(msg + msg->offset, buf, sz);
> > +
> > +       /* flags = 0, sender VMID = 0 works for both physical/virtual NS */
> > +       invoke_ffa_fn((ffa_value_t){
> > +                     .a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
> > +                     }, &ret);
> > +
> > +       if (ret.a0 == FFA_ERROR)
> > +               return ffa_to_linux_errno((int)ret.a2);
> > +
> > +       mutex_lock(&drv_info->tx_lock);
> 
> mutex_unlock(), before the potential return above?
>

Ah, my bad. Thanks for the catch.

> > +       return 0;
> > +}
> > +
> >  static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
> >                               u32 frag_len, u32 len, u64 *handle)
> >  {
> > @@ -888,6 +916,17 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
> >                                        dev->mode_32bit, data);
> >  }
> >
> > +#define ffa_partition_supports_indirect_msg(dev)       \
> > +       ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
> > +
> > +static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
> > +{
> > +       if (!ffa_partition_supports_direct_recv(dev))
> 
> ffa_partition_supports_indirect_msg(), but I'm not sure we should do
> this check at all. The client could do this in advance. Unexpected
> FFA_MSG_SEND2 calls are caught in other layers.
>

Good point. I was not sure if it makes sense to add on each message but
I wasn't sure if we can defer this to the client. But based on what you
say, it should be OK do defer it to the client.

So the next question I have is: should we populate properties in the
ffa_device so that client can use the same. I started with that but then
didn't want to expose the info to the client.

I can move the properties to the struct ffa_device and keep these macro
arm_ffa.h for clients to use if they wish. Does that make sense ?

Thanks for taking look at the patches. I will skip responding on other
2 patches as I have asked all my questions as part of this patch and they
apply to those 2 as well.

--
Regards,
Sudeep
Sudeep Holla April 16, 2024, 9:47 a.m. UTC | #4
On Tue, Apr 16, 2024 at 08:26:55AM +0100, Bertrand Marquis wrote:
> Hi Sudeep,
> 
> > On 15 Apr 2024, at 18:05, Sudeep Holla <sudeep.holla@arm.com> wrote:
> >
> > The FFA_MSG_SEND2 can be  used to transmit a partition message from
> > the Tx buffer of the sender(the driver in this case) endpoint to the Rx
> > buffer of the receiver endpoint.
> >
> > An invocation of the FFA_MSG_SEND2 transfers the ownership to the
> > receiver endpoint(or any intermediate consumer). Completion of an
> > FFA_MSG_SEND2 invocation transfers the ownership back to the sender
> > endpoint.
> >
> > The framework defines the FFA_MSG_SEND2 interface to transmit a partition
> > message from the Tx buffer of the sender to the Rx buffer of a receiver
> > and inform the scheduler that the receiver must be run.
> >
> > Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> > ---
> > drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
> > include/linux/arm_ffa.h           |  9 +++++++++
> > 2 files changed, 49 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
> > index d5087e4f6d35..6c2602f7e7cc 100644
> > --- a/drivers/firmware/arm_ffa/driver.c
> > +++ b/drivers/firmware/arm_ffa/driver.c
> > @@ -344,6 +344,34 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
> > return -EINVAL;
> > }
> >
> > +static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
> > +{
> > + u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
> > + struct ffa_indirect_msg_hdr *msg;
> > + ffa_value_t ret;
> > +
> > + mutex_lock(&drv_info->tx_lock);
> > +
> > + msg = drv_info->tx_buffer;
> > + msg->flags = 0;
> > + msg->res0 = 0;
> > + msg->offset = sizeof(*msg);
> > + msg->send_recv_id = src_dst_ids;
> > + msg->size = sz;
> > + memcpy(msg + msg->offset, buf, sz);
> 
> Here there should be a check that the user is not trying to send more data
> than what can fit in the TX Buffer.
>

Good point.

> Other than that LGTM.
>

Thanks!

--
Regards,
Sudeep
Jens Wiklander April 16, 2024, 10:53 a.m. UTC | #5
On Tue, Apr 16, 2024 at 10:48 AM Sudeep Holla <sudeep.holla@arm.com> wrote:
>
> On Tue, Apr 16, 2024 at 09:41:51AM +0200, Jens Wiklander wrote:
> > On Mon, Apr 15, 2024 at 6:05 PM Sudeep Holla <sudeep.holla@arm.com> wrote:
> > >
> > > The FFA_MSG_SEND2 can be  used to transmit a partition message from
> > > the Tx buffer of the sender(the driver in this case) endpoint to the Rx
> > > buffer of the receiver endpoint.
> > >
> > > An invocation of the FFA_MSG_SEND2 transfers the ownership to the
> >
> > ownership of the TX buffer to the
> >
> > > receiver endpoint(or any intermediate consumer). Completion of an
> > > FFA_MSG_SEND2 invocation transfers the ownership back to the sender
> >
> > ownership of the buffer back
> >
> > > endpoint.
> > >
> > > The framework defines the FFA_MSG_SEND2 interface to transmit a partition
> > > message from the Tx buffer of the sender to the Rx buffer of a receiver
> > > and inform the scheduler that the receiver must be run.
> > >
> > > Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> > > ---
> > >  drivers/firmware/arm_ffa/driver.c | 40 +++++++++++++++++++++++++++++++++++++++
> > >  include/linux/arm_ffa.h           |  9 +++++++++
> > >  2 files changed, 49 insertions(+)
> > >
> > > diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
> > > index d5087e4f6d35..6c2602f7e7cc 100644
> > > --- a/drivers/firmware/arm_ffa/driver.c
> > > +++ b/drivers/firmware/arm_ffa/driver.c
> > > @@ -344,6 +344,34 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
> > >         return -EINVAL;
> > >  }
> > >
> > > +static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
> > > +{
> > > +       u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
> > > +       struct ffa_indirect_msg_hdr *msg;
> > > +       ffa_value_t ret;
> > > +
> > > +       mutex_lock(&drv_info->tx_lock);
> > > +
> > > +       msg = drv_info->tx_buffer;
> > > +       msg->flags = 0;
> > > +       msg->res0 = 0;
> > > +       msg->offset = sizeof(*msg);
> > > +       msg->send_recv_id = src_dst_ids;
> > > +       msg->size = sz;
> > > +       memcpy(msg + msg->offset, buf, sz);
> > > +
> > > +       /* flags = 0, sender VMID = 0 works for both physical/virtual NS */
> > > +       invoke_ffa_fn((ffa_value_t){
> > > +                     .a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
> > > +                     }, &ret);
> > > +
> > > +       if (ret.a0 == FFA_ERROR)
> > > +               return ffa_to_linux_errno((int)ret.a2);
> > > +
> > > +       mutex_lock(&drv_info->tx_lock);
> >
> > mutex_unlock(), before the potential return above?
> >
>
> Ah, my bad. Thanks for the catch.
>
> > > +       return 0;
> > > +}
> > > +
> > >  static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
> > >                               u32 frag_len, u32 len, u64 *handle)
> > >  {
> > > @@ -888,6 +916,17 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
> > >                                        dev->mode_32bit, data);
> > >  }
> > >
> > > +#define ffa_partition_supports_indirect_msg(dev)       \
> > > +       ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
> > > +
> > > +static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
> > > +{
> > > +       if (!ffa_partition_supports_direct_recv(dev))
> >
> > ffa_partition_supports_indirect_msg(), but I'm not sure we should do
> > this check at all. The client could do this in advance. Unexpected
> > FFA_MSG_SEND2 calls are caught in other layers.
> >
>
> Good point. I was not sure if it makes sense to add on each message but
> I wasn't sure if we can defer this to the client. But based on what you
> say, it should be OK do defer it to the client.
>
> So the next question I have is: should we populate properties in the
> ffa_device so that client can use the same. I started with that but then
> didn't want to expose the info to the client.
>
> I can move the properties to the struct ffa_device and keep these macro
> arm_ffa.h for clients to use if they wish. Does that make sense ?

Yes, that sounds good.

>
> Thanks for taking look at the patches. I will skip responding on other
> 2 patches as I have asked all my questions as part of this patch and they
> apply to those 2 as well.

OK

Cheers,
Jens

>
> --
> Regards,
> Sudeep
diff mbox series

Patch

diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index d5087e4f6d35..6c2602f7e7cc 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -344,6 +344,34 @@  static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
 	return -EINVAL;
 }
 
+static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
+{
+	u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
+	struct ffa_indirect_msg_hdr *msg;
+	ffa_value_t ret;
+
+	mutex_lock(&drv_info->tx_lock);
+
+	msg = drv_info->tx_buffer;
+	msg->flags = 0;
+	msg->res0 = 0;
+	msg->offset = sizeof(*msg);
+	msg->send_recv_id = src_dst_ids;
+	msg->size = sz;
+	memcpy(msg + msg->offset, buf, sz);
+
+	/* flags = 0, sender VMID = 0 works for both physical/virtual NS */
+	invoke_ffa_fn((ffa_value_t){
+		      .a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
+		      }, &ret);
+
+	if (ret.a0 == FFA_ERROR)
+		return ffa_to_linux_errno((int)ret.a2);
+
+	mutex_lock(&drv_info->tx_lock);
+	return 0;
+}
+
 static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
 			      u32 frag_len, u32 len, u64 *handle)
 {
@@ -888,6 +916,17 @@  static int ffa_sync_send_receive(struct ffa_device *dev,
 				       dev->mode_32bit, data);
 }
 
+#define ffa_partition_supports_indirect_msg(dev)	\
+	ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
+
+static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
+{
+	if (!ffa_partition_supports_direct_recv(dev))
+		return -EOPNOTSUPP;
+
+	return ffa_msg_send2(drv_info->vm_id, dev->vm_id, buf, sz);
+}
+
 static int ffa_memory_share(struct ffa_mem_ops_args *args)
 {
 	if (drv_info->mem_ops_native)
@@ -1167,6 +1206,7 @@  static const struct ffa_info_ops ffa_drv_info_ops = {
 static const struct ffa_msg_ops ffa_drv_msg_ops = {
 	.mode_32bit_set = ffa_mode_32bit_set,
 	.sync_send_receive = ffa_sync_send_receive,
+	.indirect_send = ffa_indirect_msg_send,
 };
 
 static const struct ffa_mem_ops ffa_drv_mem_ops = {
diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index 13830be5851d..d61a6df397f6 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -238,6 +238,14 @@  struct ffa_send_direct_data {
 	unsigned long data4; /* w7/x7 */
 };
 
+struct ffa_indirect_msg_hdr {
+	u32 flags;
+	u32 res0;
+	u32 offset;
+	u32 send_recv_id;
+	u32 size;
+};
+
 struct ffa_mem_region_addr_range {
 	/* The base IPA of the constituent memory region, aligned to 4 kiB */
 	u64 address;
@@ -398,6 +406,7 @@  struct ffa_msg_ops {
 	void (*mode_32bit_set)(struct ffa_device *dev);
 	int (*sync_send_receive)(struct ffa_device *dev,
 				 struct ffa_send_direct_data *data);
+	int (*indirect_send)(struct ffa_device *dev, void *buf, size_t sz);
 };
 
 struct ffa_mem_ops {