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 */ 133 static 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 */ 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 */ 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 */ 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