1 /*
2 * Copyright (c) 2022-2025, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <common/debug.h>
11 #include <drivers/arm/rse_comms.h>
12 #include <psa/client.h>
13 #include <rse_comms_protocol.h>
14
15 /* Union as message space and reply space are never used at the same time, and this saves space as
16 * we can overlap them.
17 */
18 union __packed __attribute__((aligned(4))) rse_comms_io_buffer_t {
19 struct serialized_rse_comms_msg_t msg;
20 struct serialized_rse_comms_reply_t reply;
21 };
22
select_protocol_version(const psa_invec * in_vec,size_t in_len,const psa_outvec * out_vec,size_t out_len)23 static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len,
24 const psa_outvec *out_vec, size_t out_len)
25 {
26 size_t comms_mbx_msg_size;
27 size_t comms_embed_msg_min_size;
28 size_t comms_embed_reply_min_size;
29 size_t in_size_total = 0;
30 size_t out_size_total = 0;
31 size_t i;
32
33 for (i = 0U; i < in_len; ++i) {
34 in_size_total += in_vec[i].len;
35 }
36 for (i = 0U; i < out_len; ++i) {
37 out_size_total += out_vec[i].len;
38 }
39
40 comms_mbx_msg_size = rse_mbx_get_max_message_size();
41
42 comms_embed_msg_min_size = sizeof(struct serialized_rse_comms_header_t) +
43 sizeof(struct rse_embed_msg_t) -
44 PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE;
45
46 comms_embed_reply_min_size = sizeof(struct serialized_rse_comms_header_t) +
47 sizeof(struct rse_embed_reply_t) -
48 PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE;
49
50 /* Use embed if we can pack into one message and reply, else use
51 * pointer_access. The underlying mailbox transport protocol uses a
52 * single uint32_t to track the length, so the amount of data that
53 * can be in a message is 4 bytes less than rse_mbx_get_max_message_size
54 * reports.
55 *
56 * TODO tune this with real performance numbers, it's possible a
57 * pointer_access message is less performant than multiple embed
58 * messages due to ATU configuration costs to allow access to the
59 * pointers.
60 */
61 if ((comms_embed_msg_min_size + in_size_total >
62 comms_mbx_msg_size - sizeof(uint32_t)) ||
63 (comms_embed_reply_min_size + out_size_total >
64 comms_mbx_msg_size - sizeof(uint32_t))) {
65 return RSE_COMMS_PROTOCOL_POINTER_ACCESS;
66 } else {
67 return RSE_COMMS_PROTOCOL_EMBED;
68 }
69 }
70
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)71 psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec, size_t in_len,
72 psa_outvec *out_vec, size_t out_len)
73 {
74 /* Declared statically to avoid using huge amounts of stack space. Maybe revisit if
75 * functions not being reentrant becomes a problem.
76 */
77 static union rse_comms_io_buffer_t io_buf;
78 int err;
79 psa_status_t status;
80 static uint8_t seq_num = 1U;
81 size_t msg_size;
82 size_t reply_size = sizeof(io_buf.reply);
83 psa_status_t return_val;
84 size_t idx;
85
86 if (type > PSA_CALL_TYPE_MAX || type < PSA_CALL_TYPE_MIN ||
87 in_len > PSA_MAX_IOVEC || out_len > PSA_MAX_IOVEC) {
88 return PSA_ERROR_INVALID_ARGUMENT;
89 }
90
91 io_buf.msg.header.seq_num = seq_num,
92 /* No need to distinguish callers (currently concurrent calls are not supported). */
93 io_buf.msg.header.client_id = 1U,
94 io_buf.msg.header.protocol_ver = select_protocol_version(in_vec, in_len, out_vec, out_len);
95
96 status = rse_protocol_serialize_msg(handle, type, in_vec, in_len, out_vec,
97 out_len, &io_buf.msg, &msg_size);
98 if (status != PSA_SUCCESS) {
99 return status;
100 }
101
102 VERBOSE("[RSE-COMMS] Sending message\n");
103 VERBOSE("protocol_ver=%u\n", io_buf.msg.header.protocol_ver);
104 VERBOSE("seq_num=%u\n", io_buf.msg.header.seq_num);
105 VERBOSE("client_id=%u\n", io_buf.msg.header.client_id);
106 for (idx = 0; idx < in_len; idx++) {
107 VERBOSE("in_vec[%lu].len=%lu\n", idx, in_vec[idx].len);
108 VERBOSE("in_vec[%lu].buf=%p\n", idx, (void *)in_vec[idx].base);
109 }
110
111 err = rse_mbx_send_data((uint8_t *)&io_buf.msg, msg_size);
112 if (err != 0) {
113 return PSA_ERROR_COMMUNICATION_FAILURE;
114 }
115
116 #if DEBUG
117 /*
118 * Poisoning the message buffer (with a known pattern).
119 * Helps in detecting hypothetical RSE communication bugs.
120 */
121 memset(&io_buf.msg, 0xA5, msg_size);
122 #endif
123
124 err = rse_mbx_receive_data((uint8_t *)&io_buf.reply, &reply_size);
125 if (err != 0) {
126 return PSA_ERROR_COMMUNICATION_FAILURE;
127 }
128
129 VERBOSE("[RSE-COMMS] Received reply\n");
130 VERBOSE("protocol_ver=%u\n", io_buf.reply.header.protocol_ver);
131 VERBOSE("seq_num=%u\n", io_buf.reply.header.seq_num);
132 VERBOSE("client_id=%u\n", io_buf.reply.header.client_id);
133
134 status = rse_protocol_deserialize_reply(out_vec, out_len, &return_val,
135 &io_buf.reply, reply_size);
136 if (status != PSA_SUCCESS) {
137 return status;
138 }
139
140 VERBOSE("return_val=%d\n", return_val);
141 for (idx = 0U; idx < out_len; idx++) {
142 VERBOSE("out_vec[%lu].len=%lu\n", idx, out_vec[idx].len);
143 VERBOSE("out_vec[%lu].buf=%p\n", idx, (void *)out_vec[idx].base);
144 }
145
146 /* Clear the mailbox message buffer to remove assets from memory */
147 memset(&io_buf, 0x0, sizeof(io_buf));
148
149 seq_num++;
150
151 return return_val;
152 }
153