1 /*
2  * Copyright (c) 2022, Arm Limited. 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/mhu.h>
12 #include <drivers/arm/rss_comms.h>
13 #include <psa/client.h>
14 #include <rss_comms_protocol.h>
15 
16 /* Union as message space and reply space are never used at the same time, and this saves space as
17  * we can overlap them.
18  */
19 union __packed __attribute__((aligned(4))) rss_comms_io_buffer_t {
20 	struct serialized_rss_comms_msg_t msg;
21 	struct serialized_rss_comms_reply_t reply;
22 };
23 
select_protocol_version(const psa_invec * in_vec,size_t in_len,const psa_outvec * out_vec,size_t out_len)24 static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len,
25 				       const psa_outvec *out_vec, size_t out_len)
26 {
27 	size_t comms_mhu_msg_size;
28 	size_t comms_embed_msg_min_size;
29 	size_t comms_embed_reply_min_size;
30 	size_t in_size_total = 0;
31 	size_t out_size_total = 0;
32 	size_t i;
33 
34 	for (i = 0U; i < in_len; ++i) {
35 		in_size_total += in_vec[i].len;
36 	}
37 	for (i = 0U; i < out_len; ++i) {
38 		out_size_total += out_vec[i].len;
39 	}
40 
41 	comms_mhu_msg_size = mhu_get_max_message_size();
42 
43 	comms_embed_msg_min_size = sizeof(struct serialized_rss_comms_header_t) +
44 				   sizeof(struct rss_embed_msg_t) -
45 				   PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE;
46 
47 	comms_embed_reply_min_size = sizeof(struct serialized_rss_comms_header_t) +
48 				     sizeof(struct rss_embed_reply_t) -
49 				     PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE;
50 
51 	/* Use embed if we can pack into one message and reply, else use
52 	 * pointer_access. The underlying MHU transport protocol uses a
53 	 * single uint32_t to track the length, so the amount of data that
54 	 * can be in a message is 4 bytes less than mhu_get_max_message_size
55 	 * reports.
56 	 *
57 	 * TODO tune this with real performance numbers, it's possible a
58 	 * pointer_access message is less performant than multiple embed
59 	 * messages due to ATU configuration costs to allow access to the
60 	 * pointers.
61 	 */
62 	if ((comms_embed_msg_min_size + in_size_total > comms_mhu_msg_size - sizeof(uint32_t))
63 	 || (comms_embed_reply_min_size + out_size_total > comms_mhu_msg_size) - sizeof(uint32_t)) {
64 		return RSS_COMMS_PROTOCOL_POINTER_ACCESS;
65 	} else {
66 		return RSS_COMMS_PROTOCOL_EMBED;
67 	}
68 }
69 
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)70 psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec, size_t in_len,
71 		      psa_outvec *out_vec, size_t out_len)
72 {
73 	/* Declared statically to avoid using huge amounts of stack space. Maybe revisit if
74 	 * functions not being reentrant becomes a problem.
75 	 */
76 	static union rss_comms_io_buffer_t io_buf;
77 	enum mhu_error_t err;
78 	psa_status_t status;
79 	static uint8_t seq_num = 1U;
80 	size_t msg_size;
81 	size_t reply_size = sizeof(io_buf.reply);
82 	psa_status_t return_val;
83 	size_t idx;
84 
85 	if (type > INT16_MAX || type < INT16_MIN || in_len > PSA_MAX_IOVEC
86 	    || out_len > PSA_MAX_IOVEC) {
87 		return PSA_ERROR_INVALID_ARGUMENT;
88 	}
89 
90 	io_buf.msg.header.seq_num = seq_num,
91 	/* No need to distinguish callers (currently concurrent calls are not supported). */
92 	io_buf.msg.header.client_id = 1U,
93 	io_buf.msg.header.protocol_ver = select_protocol_version(in_vec, in_len, out_vec, out_len);
94 
95 	status = rss_protocol_serialize_msg(handle, type, in_vec, in_len, out_vec,
96 					    out_len, &io_buf.msg, &msg_size);
97 	if (status != PSA_SUCCESS) {
98 		return status;
99 	}
100 
101 	VERBOSE("[RSS-COMMS] Sending message\n");
102 	VERBOSE("protocol_ver=%u\n", io_buf.msg.header.protocol_ver);
103 	VERBOSE("seq_num=%u\n", io_buf.msg.header.seq_num);
104 	VERBOSE("client_id=%u\n", io_buf.msg.header.client_id);
105 	for (idx = 0; idx < in_len; idx++) {
106 		VERBOSE("in_vec[%lu].len=%lu\n", idx, in_vec[idx].len);
107 		VERBOSE("in_vec[%lu].buf=%p\n", idx, (void *)in_vec[idx].base);
108 	}
109 
110 	err = mhu_send_data((uint8_t *)&io_buf.msg, msg_size);
111 	if (err != MHU_ERR_NONE) {
112 		return PSA_ERROR_COMMUNICATION_FAILURE;
113 	}
114 
115 #if DEBUG
116 	/*
117 	 * Poisoning the message buffer (with a known pattern).
118 	 * Helps in detecting hypothetical RSS communication bugs.
119 	 */
120 	memset(&io_buf.msg, 0xA5, msg_size);
121 #endif
122 
123 	err = mhu_receive_data((uint8_t *)&io_buf.reply, &reply_size);
124 	if (err != MHU_ERR_NONE) {
125 		return PSA_ERROR_COMMUNICATION_FAILURE;
126 	}
127 
128 	VERBOSE("[RSS-COMMS] Received reply\n");
129 	VERBOSE("protocol_ver=%u\n", io_buf.reply.header.protocol_ver);
130 	VERBOSE("seq_num=%u\n", io_buf.reply.header.seq_num);
131 	VERBOSE("client_id=%u\n", io_buf.reply.header.client_id);
132 
133 	status = rss_protocol_deserialize_reply(out_vec, out_len, &return_val,
134 						&io_buf.reply, reply_size);
135 	if (status != PSA_SUCCESS) {
136 		return status;
137 	}
138 
139 	VERBOSE("return_val=%d\n", return_val);
140 	for (idx = 0U; idx < out_len; idx++) {
141 		VERBOSE("out_vec[%lu].len=%lu\n", idx, out_vec[idx].len);
142 		VERBOSE("out_vec[%lu].buf=%p\n", idx, (void *)out_vec[idx].base);
143 	}
144 
145 	/* Clear the MHU message buffer to remove assets from memory */
146 	memset(&io_buf, 0x0, sizeof(io_buf));
147 
148 	seq_num++;
149 
150 	return return_val;
151 }
152 
rss_comms_init(uintptr_t mhu_sender_base,uintptr_t mhu_receiver_base)153 int rss_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base)
154 {
155 	enum mhu_error_t err;
156 
157 	err = mhu_init_sender(mhu_sender_base);
158 	if (err != MHU_ERR_NONE) {
159 		ERROR("[RSS-COMMS] Host to RSS MHU driver initialization failed: %d\n", err);
160 		return -1;
161 	}
162 
163 	err = mhu_init_receiver(mhu_receiver_base);
164 	if (err != MHU_ERR_NONE) {
165 		ERROR("[RSS-COMMS] RSS to Host MHU driver initialization failed: %d\n", err);
166 		return -1;
167 	}
168 
169 	return 0;
170 }
171