| ######################## |
| 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.* |