| /* |
| * Copyright (c) 2022-2024, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <common/debug.h> |
| #include <drivers/arm/mhu.h> |
| #include <drivers/arm/rse_comms.h> |
| #include <psa/client.h> |
| #include <rse_comms_protocol.h> |
| |
| /* Union as message space and reply space are never used at the same time, and this saves space as |
| * we can overlap them. |
| */ |
| union __packed __attribute__((aligned(4))) rse_comms_io_buffer_t { |
| struct serialized_rse_comms_msg_t msg; |
| struct serialized_rse_comms_reply_t reply; |
| }; |
| |
| static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len, |
| const psa_outvec *out_vec, size_t out_len) |
| { |
| size_t comms_mhu_msg_size; |
| size_t comms_embed_msg_min_size; |
| size_t comms_embed_reply_min_size; |
| size_t in_size_total = 0; |
| size_t out_size_total = 0; |
| size_t i; |
| |
| for (i = 0U; i < in_len; ++i) { |
| in_size_total += in_vec[i].len; |
| } |
| for (i = 0U; i < out_len; ++i) { |
| out_size_total += out_vec[i].len; |
| } |
| |
| comms_mhu_msg_size = mhu_get_max_message_size(); |
| |
| comms_embed_msg_min_size = sizeof(struct serialized_rse_comms_header_t) + |
| sizeof(struct rse_embed_msg_t) - |
| PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE; |
| |
| comms_embed_reply_min_size = sizeof(struct serialized_rse_comms_header_t) + |
| sizeof(struct rse_embed_reply_t) - |
| PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE; |
| |
| /* Use embed if we can pack into one message and reply, else use |
| * pointer_access. The underlying MHU transport protocol uses a |
| * single uint32_t to track the length, so the amount of data that |
| * can be in a message is 4 bytes less than mhu_get_max_message_size |
| * reports. |
| * |
| * TODO tune this with real performance numbers, it's possible a |
| * pointer_access message is less performant than multiple embed |
| * messages due to ATU configuration costs to allow access to the |
| * pointers. |
| */ |
| if ((comms_embed_msg_min_size + in_size_total > |
| comms_mhu_msg_size - sizeof(uint32_t)) || |
| (comms_embed_reply_min_size + out_size_total > |
| comms_mhu_msg_size - sizeof(uint32_t))) { |
| return RSE_COMMS_PROTOCOL_POINTER_ACCESS; |
| } else { |
| return RSE_COMMS_PROTOCOL_EMBED; |
| } |
| } |
| |
| psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec, size_t in_len, |
| psa_outvec *out_vec, size_t out_len) |
| { |
| /* Declared statically to avoid using huge amounts of stack space. Maybe revisit if |
| * functions not being reentrant becomes a problem. |
| */ |
| static union rse_comms_io_buffer_t io_buf; |
| enum mhu_error_t err; |
| psa_status_t status; |
| static uint8_t seq_num = 1U; |
| size_t msg_size; |
| size_t reply_size = sizeof(io_buf.reply); |
| psa_status_t return_val; |
| size_t idx; |
| |
| if (type > PSA_CALL_TYPE_MAX || type < PSA_CALL_TYPE_MIN || |
| in_len > PSA_MAX_IOVEC || out_len > PSA_MAX_IOVEC) { |
| return PSA_ERROR_INVALID_ARGUMENT; |
| } |
| |
| io_buf.msg.header.seq_num = seq_num, |
| /* No need to distinguish callers (currently concurrent calls are not supported). */ |
| io_buf.msg.header.client_id = 1U, |
| io_buf.msg.header.protocol_ver = select_protocol_version(in_vec, in_len, out_vec, out_len); |
| |
| status = rse_protocol_serialize_msg(handle, type, in_vec, in_len, out_vec, |
| out_len, &io_buf.msg, &msg_size); |
| if (status != PSA_SUCCESS) { |
| return status; |
| } |
| |
| VERBOSE("[RSE-COMMS] Sending message\n"); |
| VERBOSE("protocol_ver=%u\n", io_buf.msg.header.protocol_ver); |
| VERBOSE("seq_num=%u\n", io_buf.msg.header.seq_num); |
| VERBOSE("client_id=%u\n", io_buf.msg.header.client_id); |
| for (idx = 0; idx < in_len; idx++) { |
| VERBOSE("in_vec[%lu].len=%lu\n", idx, in_vec[idx].len); |
| VERBOSE("in_vec[%lu].buf=%p\n", idx, (void *)in_vec[idx].base); |
| } |
| |
| err = mhu_send_data((uint8_t *)&io_buf.msg, msg_size); |
| if (err != MHU_ERR_NONE) { |
| return PSA_ERROR_COMMUNICATION_FAILURE; |
| } |
| |
| #if DEBUG |
| /* |
| * Poisoning the message buffer (with a known pattern). |
| * Helps in detecting hypothetical RSE communication bugs. |
| */ |
| memset(&io_buf.msg, 0xA5, msg_size); |
| #endif |
| |
| err = mhu_receive_data((uint8_t *)&io_buf.reply, &reply_size); |
| if (err != MHU_ERR_NONE) { |
| return PSA_ERROR_COMMUNICATION_FAILURE; |
| } |
| |
| VERBOSE("[RSE-COMMS] Received reply\n"); |
| VERBOSE("protocol_ver=%u\n", io_buf.reply.header.protocol_ver); |
| VERBOSE("seq_num=%u\n", io_buf.reply.header.seq_num); |
| VERBOSE("client_id=%u\n", io_buf.reply.header.client_id); |
| |
| status = rse_protocol_deserialize_reply(out_vec, out_len, &return_val, |
| &io_buf.reply, reply_size); |
| if (status != PSA_SUCCESS) { |
| return status; |
| } |
| |
| VERBOSE("return_val=%d\n", return_val); |
| for (idx = 0U; idx < out_len; idx++) { |
| VERBOSE("out_vec[%lu].len=%lu\n", idx, out_vec[idx].len); |
| VERBOSE("out_vec[%lu].buf=%p\n", idx, (void *)out_vec[idx].base); |
| } |
| |
| /* Clear the MHU message buffer to remove assets from memory */ |
| memset(&io_buf, 0x0, sizeof(io_buf)); |
| |
| seq_num++; |
| |
| return return_val; |
| } |
| |
| int rse_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base) |
| { |
| enum mhu_error_t err; |
| |
| err = mhu_init_sender(mhu_sender_base); |
| if (err != MHU_ERR_NONE) { |
| if (err == MHU_ERR_ALREADY_INIT) { |
| INFO("[RSE-COMMS] Host to RSE MHU driver already initialized\n"); |
| } else { |
| ERROR("[RSE-COMMS] Host to RSE MHU driver initialization failed: %d\n", err); |
| return -1; |
| } |
| } |
| |
| err = mhu_init_receiver(mhu_receiver_base); |
| if (err != MHU_ERR_NONE) { |
| if (err == MHU_ERR_ALREADY_INIT) { |
| INFO("[RSE-COMMS] RSE to Host MHU driver already initialized\n"); |
| } else { |
| ERROR("[RSE-COMMS] RSE to Host MHU driver initialization failed: %d\n", err); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |