aboutsummaryrefslogtreecommitdiff
path: root/plat/ti/k3/common/drivers/sec_proxy/sec_proxy.c
blob: 92414b9050f0ae1d82be1e5e08a28a3ffc7ce4be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
 * Texas Instruments K3 Secure Proxy Driver
 *   Based on Linux and U-Boot implementation
 *
 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <debug.h>
#include <errno.h>
#include <mmio.h>
#include <platform_def.h>
#include <stdlib.h>
#include <utils.h>
#include <utils_def.h>

#include "sec_proxy.h"

/* SEC PROXY RT THREAD STATUS */
#define RT_THREAD_STATUS			(0x0)
#define RT_THREAD_STATUS_ERROR_SHIFT		(31)
#define RT_THREAD_STATUS_ERROR_MASK		BIT(31)
#define RT_THREAD_STATUS_CUR_CNT_SHIFT		(0)
#define RT_THREAD_STATUS_CUR_CNT_MASK		GENMASK(7, 0)

/* SEC PROXY SCFG THREAD CTRL */
#define SCFG_THREAD_CTRL			(0x1000)
#define SCFG_THREAD_CTRL_DIR_SHIFT		(31)
#define SCFG_THREAD_CTRL_DIR_MASK		BIT(31)

#define SEC_PROXY_THREAD(base, x)		((base) + (0x1000 * (x)))
#define THREAD_IS_RX				(1)
#define THREAD_IS_TX				(0)

/**
 * struct k3_sec_proxy_desc - Description of secure proxy integration
 * @timeout_us:		Timeout for communication (in Microseconds)
 * @max_msg_size:	Message size in bytes
 * @data_start_offset:	Offset of the First data register of the thread
 * @data_end_offset:	Offset of the Last data register of the thread
 */
struct k3_sec_proxy_desc {
	uint32_t timeout_us;
	uint16_t max_msg_size;
	uint16_t data_start_offset;
	uint16_t data_end_offset;
};

/**
 * struct k3_sec_proxy_thread - Description of a secure proxy Thread
 * @id:		Thread ID
 * @data:	Thread Data path region for target
 * @scfg:	Secure Config Region for Thread
 * @rt:		RealTime Region for Thread
 */
struct k3_sec_proxy_thread {
	uint32_t id;
	uintptr_t data;
	uintptr_t scfg;
	uintptr_t rt;
};

/**
 * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance
 * @desc:	Description of the SoC integration
 * @chans:	Array for valid thread instances
 */
struct k3_sec_proxy_mbox {
	const struct k3_sec_proxy_desc desc;
	struct k3_sec_proxy_thread threads[];
};

/*
 * Thread ID #0: DMSC notify
 * Thread ID #1: DMSC request response
 * Thread ID #2: DMSC request high priority
 * Thread ID #3: DMSC request low priority
 * Thread ID #4: DMSC notify response
 */
#define SP_THREAD(_x) \
	[_x] = { \
		.id = _x, \
		.data = SEC_PROXY_THREAD(SEC_PROXY_DATA_BASE, _x), \
		.scfg = SEC_PROXY_THREAD(SEC_PROXY_SCFG_BASE, _x), \
		.rt = SEC_PROXY_THREAD(SEC_PROXY_RT_BASE, _x), \
	}

static struct k3_sec_proxy_mbox spm = {
	.desc = {
		.timeout_us = SEC_PROXY_TIMEOUT_US,
		.max_msg_size = SEC_PROXY_MAX_MESSAGE_SIZE,
		.data_start_offset = 0x4,
		.data_end_offset = 0x3C,
	},
	.threads = {
		SP_THREAD(SP_NOTIFY),
		SP_THREAD(SP_RESPONSE),
		SP_THREAD(SP_HIGH_PRIORITY),
		SP_THREAD(SP_LOW_PRIORITY),
		SP_THREAD(SP_NOTIFY_RESP),
	},
};

/**
 * struct sec_msg_hdr - Message header for secure messages and responses
 * @checksum:	CRC of message for integrity checking
 */
union sec_msg_hdr {
	struct {
		uint16_t checksum;
		uint16_t reserved;
	} __packed;
	uint32_t data;
};

/**
 * k3_sec_proxy_verify_thread() - Verify thread status before
 *				  sending/receiving data
 * @spt: Pointer to Secure Proxy thread description
 * @dir: Direction of the thread
 *
 * Return: 0 if all goes well, else appropriate error message
 */
static inline int k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread *spt,
					     uint32_t dir)
{
	/* Check for any errors already available */
	if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
	    RT_THREAD_STATUS_ERROR_MASK) {
		ERROR("Thread %d is corrupted, cannot send data\n", spt->id);
		return -EINVAL;
	}

	/* Make sure thread is configured for right direction */
	if ((mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)
	    != (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) {
		if (dir)
			ERROR("Trying to receive data on tx Thread %d\n",
			      spt->id);
		else
			ERROR("Trying to send data on rx Thread %d\n",
			      spt->id);
		return -EINVAL;
	}

	/* Check the message queue before sending/receiving data */
	uint32_t tick_start = (uint32_t)read_cntpct_el0();
	uint32_t ticks_per_us = SYS_COUNTER_FREQ_IN_TICKS / 1000000;
	while (!(mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) {
		VERBOSE("Waiting for thread %d to clear\n", spt->id);
		if (((uint32_t)read_cntpct_el0() - tick_start) >
		    (spm.desc.timeout_us * ticks_per_us)) {
			ERROR("Timeout waiting for thread %d to clear\n", spt->id);
			return -ETIMEDOUT;
		}
	}

	return 0;
}

/**
 * k3_sec_proxy_send() - Send data over a Secure Proxy thread
 * @id: Channel Identifier
 * @msg: Pointer to k3_sec_proxy_msg
 *
 * Return: 0 if all goes well, else appropriate error message
 */
int k3_sec_proxy_send(enum k3_sec_proxy_chan_id id, const struct k3_sec_proxy_msg *msg)
{
	struct k3_sec_proxy_thread *spt = &spm.threads[id];
	union sec_msg_hdr secure_header;
	int num_words, trail_bytes, i, ret;
	uintptr_t data_reg;

	ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_TX);
	if (ret) {
		ERROR("Thread %d verification failed (%d)\n", spt->id, ret);
		return ret;
	}

	/* Check the message size */
	if (msg->len + sizeof(secure_header) > spm.desc.max_msg_size) {
		ERROR("Thread %d message length %lu > max msg size\n",
		      spt->id, msg->len);
		return -EINVAL;
	}

	/* TODO: Calculate checksum */
	secure_header.checksum = 0;

	/* Send the secure header */
	data_reg = spm.desc.data_start_offset;
	mmio_write_32(spt->data + data_reg, secure_header.data);
	data_reg += sizeof(uint32_t);

	/* Send whole words */
	num_words = msg->len / sizeof(uint32_t);
	for (i = 0; i < num_words; i++) {
		mmio_write_32(spt->data + data_reg, ((uint32_t *)msg->buf)[i]);
		data_reg += sizeof(uint32_t);
	}

	/* Send remaining bytes */
	trail_bytes = msg->len % sizeof(uint32_t);
	if (trail_bytes) {
		uint32_t data_trail = 0;

		i = msg->len - trail_bytes;
		while (trail_bytes--) {
			data_trail <<= 8;
			data_trail |= msg->buf[i++];
		}

		mmio_write_32(spt->data + data_reg, data_trail);
		data_reg += sizeof(uint32_t);
	}
	/*
	 * 'data_reg' indicates next register to write. If we did not already
	 * write on tx complete reg(last reg), we must do so for transmit
	 */
	if (data_reg <= spm.desc.data_end_offset)
		mmio_write_32(spt->data + spm.desc.data_end_offset, 0);

	VERBOSE("Message successfully sent on thread %ud\n", id);

	return 0;
}

/**
 * k3_sec_proxy_recv() - Receive data from a Secure Proxy thread
 * @id: Channel Identifier
 * @msg: Pointer to k3_sec_proxy_msg
 *
 * Return: 0 if all goes well, else appropriate error message
 */
int k3_sec_proxy_recv(uint32_t id, struct k3_sec_proxy_msg *msg)
{
	struct k3_sec_proxy_thread *spt = &spm.threads[id];
	union sec_msg_hdr secure_header;
	uintptr_t data_reg;
	int num_words, trail_bytes, i, ret;

	ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_RX);
	if (ret) {
		ERROR("Thread %d verification failed (%d)\n", spt->id, ret);
		return ret;
	}

	/* Read secure header */
	data_reg = spm.desc.data_start_offset;
	secure_header.data = mmio_read_32(spt->data + data_reg);
	data_reg += sizeof(uint32_t);

	/* Read whole words */
	num_words = msg->len / sizeof(uint32_t);
	for (i = 0; i < num_words; i++) {
		((uint32_t *)msg->buf)[i] = mmio_read_32(spt->data + data_reg);
		data_reg += sizeof(uint32_t);
	}

	/* Read remaining bytes */
	trail_bytes = msg->len % sizeof(uint32_t);
	if (trail_bytes) {
		uint32_t data_trail = mmio_read_32(spt->data + data_reg);
		data_reg += sizeof(uint32_t);

		i = msg->len - trail_bytes;
		while (trail_bytes--) {
			msg->buf[i] = data_trail & 0xff;
			data_trail >>= 8;
		}
	}

	/*
	 * 'data_reg' indicates next register to read. If we did not already
	 * read on rx complete reg(last reg), we must do so for receive
	 */
	if (data_reg <= spm.desc.data_end_offset)
		mmio_read_32(spt->data + spm.desc.data_end_offset);

	/* TODO: Verify checksum */
	(void)secure_header.checksum;

	VERBOSE("Message successfully received from thread %ud\n", id);

	return 0;
}