blob: 5d80cacd79a5f001f1a651a6894d30c2fabfa35d [file] [log] [blame]
########################
RSE communication design
########################
The RSE communication protocol is designed to be a lightweight serialization of
the psa_call() API through a combination of in-band MHU (Message Handling Unit)
transport and parameter-passing through shared memory.
**************
Message format
**************
To call an RSE service, the client must send a message in-band over the MHU
sender link to RSE and wait for a reply message on the MHU receiver (either by
polling the MHU or waiting for interrupt). The messages are defined as packed C
structs, which are serialized in byte-order over the MHU links.
Messages encoding a psa_call() to RSE take the form::
__PACKED_STRUCT serialized_psa_msg_t {
struct serialized_rse_comms_header_t header;
__PACKED_UNION {
struct rse_embed_msg_t embed;
struct rse_pointer_access_msg_t pointer_access;
} msg;
};
Replies from RSE take the form::
__PACKED_STRUCT serialized_psa_reply_t {
struct serialized_rse_comms_header_t header;
__PACKED_UNION {
struct rse_embed_reply_t embed;
struct rse_pointer_access_reply_t pointer_access;
} reply;
};
All messages (calls and replies), in all protocols carry the following header::
__PACKED_STRUCT serialized_rse_comms_header_t {
uint8_t protocol_ver;
uint8_t seq_num;
uint16_t client_id;
};
The ``client_id`` can be used by the caller to identify different clients at the
endpoint for access control purposes. It is combined with an RSE-internal
identifier for the endpoint to create the PSA Client ID for the call.
The sequence number, ``seq_num``, is returned in the reply message to allow the
client to identify which message is being responded to, since replies may be
out-of-order. The sender is free to assign sequence numbers using a scheme of
its choice, and it is the sender's responsibility to ensure that two messages
with the same sequence number are not in simultaneous flight.
The ``protocol_ver`` identifies which message protocol is being used. There are
two protocols currently supported, "embed" (``protocol_ver=0``) and "pointer
access" (``protocol_ver=1``).
Embed protocol
==============
The embed protocol embeds the psa_call iovecs into the message sent over the
MHU. It has the following format::
__PACKED_STRUCT rse_embed_msg_t {
psa_handle_t handle;
uint32_t ctrl_param;
uint16_t io_size[PSA_MAX_IOVEC];
uint8_t payload[RSE_COMMS_PAYLOAD_MAX_SIZE];
};
The ``handle`` is the psa_call handle parameter and the ``ctrl_param`` packs the
type, in_len and out_len parameters of psa_call into one parameter.
The ``io_size`` array contains the sizes of the up to ``PSA_MAX_IOVEC`` (4)
iovecs, in order, with the invec sizes before the outvec sizes.
The ``payload`` array then contains the invec data packed contiguously in order.
The length of this parameter is variable, equal to the sum of the invec lengths
in io_size. The caller does not need to pad the payload to the maximum size. The
maximum payload size for this protocol, ``RSE_COMMS_PAYLOAD_MAX_SIZE``, is a
build-time option.
Replies in the embed protocol take the form::
__PACKED_STRUCT rse_embed_reply_t {
int32_t return_val;
uint16_t out_size[PSA_MAX_IOVEC];
uint8_t payload[RSE_COMMS_PAYLOAD_MAX_SIZE];
};
The ``return_val`` is the return value of the psa_call() invocation, the
``out_size`` is the (potentially updated) sizes of the outvecs and the
``payload`` buffer contains the outvec data serialized contiguously in outvec
order.
Pointer access protocol
=======================
The pointer access protocol passes the psa_call iovecs as pointers to shared
memory using the following MHU message format::
__PACKED_STRUCT rse_pointer_access_msg_t {
psa_handle_t handle;
uint32_t ctrl_param;
uint32_t io_sizes[PSA_MAX_IOVEC];
uint64_t host_ptrs[PSA_MAX_IOVEC];
};
The ``handle``, ``ctrl_param`` and ``io_sizes`` have the same definition as in
the embed protocol.
The ``host_ptrs`` point to each of the up to ``PSA_MAX_IOVEC`` iovec buffers in
host system memory. It is the caller's responsibility to write the invec data to
the invec pointers before sending the message, and ensure that the memory
pointed-to remains valid for the duration of the call.
The reply message has the form::
__PACKED_STRUCT rse_pointer_access_reply_t {
int32_t return_val;
uint32_t out_size[PSA_MAX_IOVEC];
};
The ``return_val`` and ``out_size`` have the same meaning as in the embed
protocol.
RSE writes the outvec data to the pointers supplied in the call message prior to
sending the MHU reply message, so no further payload is sent in the reply
message.
************************
Implementation structure
************************
The RSE side of the communication implementation is located in
``platform/ext/target/arm/rse/common/rse_comms``. The implementation is
structured as follows:
- ``rse_comms.c``: Implements the TF-M RPC layer using RSE comms implementation.
- ``rse_comms_hal.c``: Abstracts MHU message sending and receiving.
- ``rse_comms_protocol.c``: The common part of the RSE comms protocol.
- ``rse_comms_protocol_embed.c``: The embed RSE comms protocol.
- ``rse_comms_protocol_protocol_access.c``: The pointer access RSE comms protocol.
- ``rse_comms_atu.c``: Allocates and frees ATU regions for host pointer access.
- ``rse_comms_permissions_hal.c``: Checks service access permissions and pointer validity.
A reference implementation of the client side of the RSE comms is available in
the Trusted Firmware-A repository.
--------------
*Copyright (c) 2022-2023, Arm Limited. All rights reserved.*