• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Texas Instruments K3 Secure Proxy Driver
3  *   Based on Linux and U-Boot implementation
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #include <errno.h>
11 #include <stdlib.h>
12 
13 #include <platform_def.h>
14 
15 #include <arch_helpers.h>
16 #include <common/debug.h>
17 #include <lib/mmio.h>
18 #include <lib/utils.h>
19 #include <lib/utils_def.h>
20 
21 #include "sec_proxy.h"
22 
23 /* SEC PROXY RT THREAD STATUS */
24 #define RT_THREAD_STATUS			(0x0)
25 #define RT_THREAD_STATUS_ERROR_SHIFT		(31)
26 #define RT_THREAD_STATUS_ERROR_MASK		BIT(31)
27 #define RT_THREAD_STATUS_CUR_CNT_SHIFT		(0)
28 #define RT_THREAD_STATUS_CUR_CNT_MASK		GENMASK(7, 0)
29 
30 /* SEC PROXY SCFG THREAD CTRL */
31 #define SCFG_THREAD_CTRL			(0x1000)
32 #define SCFG_THREAD_CTRL_DIR_SHIFT		(31)
33 #define SCFG_THREAD_CTRL_DIR_MASK		BIT(31)
34 
35 #define SEC_PROXY_THREAD(base, x)		((base) + (0x1000 * (x)))
36 #define THREAD_IS_RX				(1)
37 #define THREAD_IS_TX				(0)
38 
39 /**
40  * struct k3_sec_proxy_desc - Description of secure proxy integration
41  * @timeout_us:		Timeout for communication (in Microseconds)
42  * @max_msg_size:	Message size in bytes
43  * @data_start_offset:	Offset of the First data register of the thread
44  * @data_end_offset:	Offset of the Last data register of the thread
45  */
46 struct k3_sec_proxy_desc {
47 	uint32_t timeout_us;
48 	uint16_t max_msg_size;
49 	uint16_t data_start_offset;
50 	uint16_t data_end_offset;
51 };
52 
53 /**
54  * struct k3_sec_proxy_thread - Description of a Secure Proxy Thread
55  * @name:	Thread Name
56  * @data:	Thread Data path region for target
57  * @scfg:	Secure Config Region for Thread
58  * @rt:		RealTime Region for Thread
59  */
60 struct k3_sec_proxy_thread {
61 	const char *name;
62 	uintptr_t data;
63 	uintptr_t scfg;
64 	uintptr_t rt;
65 };
66 
67 /**
68  * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance
69  * @desc:	Description of the SoC integration
70  * @chans:	Array for valid thread instances
71  */
72 struct k3_sec_proxy_mbox {
73 	const struct k3_sec_proxy_desc desc;
74 	struct k3_sec_proxy_thread threads[];
75 };
76 
77 /*
78  * Thread ID #0: DMSC notify
79  * Thread ID #1: DMSC request response
80  * Thread ID #2: DMSC request high priority
81  * Thread ID #3: DMSC request low priority
82  * Thread ID #4: DMSC notify response
83  */
84 #define SP_THREAD(_x) \
85 	[_x] = { \
86 		.name = #_x, \
87 		.data = SEC_PROXY_THREAD(SEC_PROXY_DATA_BASE, _x), \
88 		.scfg = SEC_PROXY_THREAD(SEC_PROXY_SCFG_BASE, _x), \
89 		.rt = SEC_PROXY_THREAD(SEC_PROXY_RT_BASE, _x), \
90 	}
91 
92 static struct k3_sec_proxy_mbox spm = {
93 	.desc = {
94 		.timeout_us = SEC_PROXY_TIMEOUT_US,
95 		.max_msg_size = SEC_PROXY_MAX_MESSAGE_SIZE,
96 		.data_start_offset = 0x4,
97 		.data_end_offset = 0x3C,
98 	},
99 	.threads = {
100 #if !K3_SEC_PROXY_LITE
101 		SP_THREAD(SP_NOTIFY),
102 		SP_THREAD(SP_RESPONSE),
103 		SP_THREAD(SP_HIGH_PRIORITY),
104 		SP_THREAD(SP_LOW_PRIORITY),
105 		SP_THREAD(SP_NOTIFY_RESP),
106 #else
107 		SP_THREAD(SP_RESPONSE),
108 		SP_THREAD(SP_HIGH_PRIORITY),
109 #endif /* K3_SEC_PROXY_LITE */
110 	},
111 };
112 
113 /**
114  * struct sec_msg_hdr - Message header for secure messages and responses
115  * @checksum:	CRC of message for integrity checking
116  */
117 union sec_msg_hdr {
118 	struct {
119 		uint16_t checksum;
120 		uint16_t reserved;
121 	} __packed;
122 	uint32_t data;
123 };
124 
125 /**
126  * k3_sec_proxy_verify_thread() - Verify thread status before
127  *				  sending/receiving data
128  * @spt: Pointer to Secure Proxy thread description
129  * @dir: Direction of the thread
130  *
131  * Return: 0 if all goes well, else appropriate error message
132  */
k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread * spt,uint32_t dir)133 static inline int k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread *spt,
134 					     uint32_t dir)
135 {
136 	/* Check for any errors already available */
137 	if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
138 	    RT_THREAD_STATUS_ERROR_MASK) {
139 		ERROR("Thread %s is corrupted, cannot send data\n", spt->name);
140 		return -EINVAL;
141 	}
142 
143 	/* Make sure thread is configured for right direction */
144 	if ((mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)
145 	    != (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) {
146 		if (dir == THREAD_IS_TX)
147 			ERROR("Trying to send data on RX Thread %s\n",
148 			      spt->name);
149 		else
150 			ERROR("Trying to receive data on TX Thread %s\n",
151 			      spt->name);
152 		return -EINVAL;
153 	}
154 
155 	/* Check the message queue before sending/receiving data */
156 	uint32_t tick_start = (uint32_t)read_cntpct_el0();
157 	uint32_t ticks_per_us = SYS_COUNTER_FREQ_IN_TICKS / 1000000;
158 	while (!(mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) {
159 		VERBOSE("Waiting for thread %s to %s\n",
160 			spt->name, (dir == THREAD_IS_TX) ? "empty" : "fill");
161 		if (((uint32_t)read_cntpct_el0() - tick_start) >
162 		    (spm.desc.timeout_us * ticks_per_us)) {
163 			ERROR("Timeout waiting for thread %s to %s\n",
164 				spt->name, (dir == THREAD_IS_TX) ? "empty" : "fill");
165 			return -ETIMEDOUT;
166 		}
167 	}
168 
169 	return 0;
170 }
171 
172 /**
173  * k3_sec_proxy_clear_rx_thread() - Clear Secure Proxy thread
174  *
175  * @id: Channel Identifier
176  *
177  * Return: 0 if all goes well, else appropriate error message
178  */
k3_sec_proxy_clear_rx_thread(enum k3_sec_proxy_chan_id id)179 int k3_sec_proxy_clear_rx_thread(enum k3_sec_proxy_chan_id id)
180 {
181 	struct k3_sec_proxy_thread *spt = &spm.threads[id];
182 
183 	/* Check for any errors already available */
184 	if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
185 	    RT_THREAD_STATUS_ERROR_MASK) {
186 		ERROR("Thread %s is corrupted, cannot send data\n", spt->name);
187 		return -EINVAL;
188 	}
189 
190 	/* Make sure thread is configured for right direction */
191 	if (!(mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)) {
192 		ERROR("Cannot clear a transmit thread %s\n", spt->name);
193 		return -EINVAL;
194 	}
195 
196 	/* Read off messages from thread until empty */
197 	uint32_t try_count = 10;
198 	while (mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK) {
199 		if (!(try_count--)) {
200 			ERROR("Could not clear all messages from thread %s\n", spt->name);
201 			return -ETIMEDOUT;
202 		}
203 		WARN("Clearing message from thread %s\n", spt->name);
204 		mmio_read_32(spt->data + spm.desc.data_end_offset);
205 	}
206 
207 	return 0;
208 }
209 
210 /**
211  * k3_sec_proxy_send() - Send data over a Secure Proxy thread
212  * @id: Channel Identifier
213  * @msg: Pointer to k3_sec_proxy_msg
214  *
215  * Return: 0 if all goes well, else appropriate error message
216  */
k3_sec_proxy_send(enum k3_sec_proxy_chan_id id,const struct k3_sec_proxy_msg * msg)217 int k3_sec_proxy_send(enum k3_sec_proxy_chan_id id, const struct k3_sec_proxy_msg *msg)
218 {
219 	struct k3_sec_proxy_thread *spt = &spm.threads[id];
220 	union sec_msg_hdr secure_header;
221 	int num_words, trail_bytes, i, ret;
222 	uintptr_t data_reg;
223 
224 	ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_TX);
225 	if (ret) {
226 		ERROR("Thread %s verification failed (%d)\n", spt->name, ret);
227 		return ret;
228 	}
229 
230 	/* Check the message size */
231 	if (msg->len + sizeof(secure_header) > spm.desc.max_msg_size) {
232 		ERROR("Thread %s message length %lu > max msg size\n",
233 		      spt->name, msg->len);
234 		return -EINVAL;
235 	}
236 
237 	/* TODO: Calculate checksum */
238 	secure_header.checksum = 0;
239 
240 	/* Send the secure header */
241 	data_reg = spm.desc.data_start_offset;
242 	mmio_write_32(spt->data + data_reg, secure_header.data);
243 	data_reg += sizeof(uint32_t);
244 
245 	/* Send whole words */
246 	num_words = msg->len / sizeof(uint32_t);
247 	for (i = 0; i < num_words; i++) {
248 		mmio_write_32(spt->data + data_reg, ((uint32_t *)msg->buf)[i]);
249 		data_reg += sizeof(uint32_t);
250 	}
251 
252 	/* Send remaining bytes */
253 	trail_bytes = msg->len % sizeof(uint32_t);
254 	if (trail_bytes) {
255 		uint32_t data_trail = 0;
256 
257 		i = msg->len - trail_bytes;
258 		while (trail_bytes--) {
259 			data_trail <<= 8;
260 			data_trail |= msg->buf[i++];
261 		}
262 
263 		mmio_write_32(spt->data + data_reg, data_trail);
264 		data_reg += sizeof(uint32_t);
265 	}
266 	/*
267 	 * 'data_reg' indicates next register to write. If we did not already
268 	 * write on tx complete reg(last reg), we must do so for transmit
269 	 * In addition, we also need to make sure all intermediate data
270 	 * registers(if any required), are reset to 0 for TISCI backward
271 	 * compatibility to be maintained.
272 	 */
273 	while (data_reg <= spm.desc.data_end_offset) {
274 		mmio_write_32(spt->data + data_reg, 0);
275 		data_reg += sizeof(uint32_t);
276 	}
277 
278 	VERBOSE("Message successfully sent on thread %s\n", spt->name);
279 
280 	return 0;
281 }
282 
283 /**
284  * k3_sec_proxy_recv() - Receive data from a Secure Proxy thread
285  * @id: Channel Identifier
286  * @msg: Pointer to k3_sec_proxy_msg
287  *
288  * Return: 0 if all goes well, else appropriate error message
289  */
k3_sec_proxy_recv(enum k3_sec_proxy_chan_id id,struct k3_sec_proxy_msg * msg)290 int k3_sec_proxy_recv(enum k3_sec_proxy_chan_id id, struct k3_sec_proxy_msg *msg)
291 {
292 	struct k3_sec_proxy_thread *spt = &spm.threads[id];
293 	union sec_msg_hdr secure_header;
294 	uintptr_t data_reg;
295 	int num_words, trail_bytes, i, ret;
296 
297 	ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_RX);
298 	if (ret) {
299 		ERROR("Thread %s verification failed (%d)\n", spt->name, ret);
300 		return ret;
301 	}
302 
303 	/* Read secure header */
304 	data_reg = spm.desc.data_start_offset;
305 	secure_header.data = mmio_read_32(spt->data + data_reg);
306 	data_reg += sizeof(uint32_t);
307 
308 	/* Read whole words */
309 	num_words = msg->len / sizeof(uint32_t);
310 	for (i = 0; i < num_words; i++) {
311 		((uint32_t *)msg->buf)[i] = mmio_read_32(spt->data + data_reg);
312 		data_reg += sizeof(uint32_t);
313 	}
314 
315 	/* Read remaining bytes */
316 	trail_bytes = msg->len % sizeof(uint32_t);
317 	if (trail_bytes) {
318 		uint32_t data_trail = mmio_read_32(spt->data + data_reg);
319 		data_reg += sizeof(uint32_t);
320 
321 		i = msg->len - trail_bytes;
322 		while (trail_bytes--) {
323 			msg->buf[i] = data_trail & 0xff;
324 			data_trail >>= 8;
325 		}
326 	}
327 
328 	/*
329 	 * 'data_reg' indicates next register to read. If we did not already
330 	 * read on rx complete reg(last reg), we must do so for receive
331 	 */
332 	if (data_reg <= spm.desc.data_end_offset)
333 		mmio_read_32(spt->data + spm.desc.data_end_offset);
334 
335 	/* TODO: Verify checksum */
336 	(void)secure_header.checksum;
337 
338 	VERBOSE("Message successfully received from thread %s\n", spt->name);
339 
340 	return 0;
341 }
342