1 /*
2 * Copyright (c) 2023 Synopsys Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/uart.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/toolchain.h>
10 #include <string.h>
11
12 #define DT_DRV_COMPAT snps_hostlink_uart
13
14 /* Only supported by HW and nSIM targets */
15 BUILD_ASSERT(!IS_ENABLED(CONFIG_QEMU_TARGET));
16 /* Only supported by ARC targets */
17 BUILD_ASSERT(IS_ENABLED(CONFIG_ARC));
18
19 #define HL_SYSCALL_OPEN 0
20 #define HL_SYSCALL_CLOSE 1
21 #define HL_SYSCALL_READ 2
22 #define HL_SYSCALL_WRITE 3
23 #define HL_SYSCALL_LSEEK 4
24 #define HL_SYSCALL_UNLINK 5
25 #define HL_SYSCALL_ISATTY 6
26 #define HL_SYSCALL_TMPNAM 7
27 #define HL_SYSCALL_GETENV 8
28 #define HL_SYSCALL_CLOCK 9
29 #define HL_SYSCALL_TIME 10
30 #define HL_SYSCALL_RENAME 11
31 #define HL_SYSCALL_ARGC 12
32 #define HL_SYSCALL_ARGV 13
33 #define HL_SYSCALL_RETCODE 14
34 #define HL_SYSCALL_ACCESS 15
35 #define HL_SYSCALL_GETPID 16
36 #define HL_SYSCALL_GETCWD 17
37 #define HL_SYSCALL_USER 18
38
39 #define HL_VERSION 1
40
41 /* "No message here" mark. */
42 #define HL_NOADDRESS 0xFFFFFFFF
43
44 /* TODO: if we want to carve some additional space we can use the actual maximum processor cache
45 * line size here (i.e 128)
46 */
47 #define HL_MAX_DCACHE_LINE 256
48
49 /* Hostlink gateway structure. */
50 struct hl_hdr {
51 uint32_t version; /* Current version is 1. */
52 uint32_t target2host_addr; /* Packet address from target to host. */
53 uint32_t host2target_addr; /* Packet address from host to target. */
54 uint32_t buf_addr; /* Address for host to write answer. */
55 uint32_t payload_size; /* Buffer size without packet header. */
56 uint32_t options; /* For future use. */
57 uint32_t break_to_mon_addr; /* For future use. */
58 } __packed;
59
60 /* Hostlink packet header. */
61 struct hl_pkt_hdr {
62 uint32_t packet_id; /* Packet id. Always set to 1 here. */
63 uint32_t total_size; /* Size of packet including header. */
64 uint32_t priority; /* For future use. */
65 uint32_t type; /* For future use. */
66 uint32_t checksum; /* For future use. */
67 } __packed;
68
69 struct hl_packed_int {
70 volatile uint16_t type;
71 volatile uint16_t size;
72 volatile int32_t value;
73 } __packed;
74
75 struct hl_packed_short_buff {
76 volatile uint16_t type;
77 volatile uint16_t size;
78 volatile uint8_t payload_short[4];
79 } __packed;
80
81 BUILD_ASSERT(sizeof(struct hl_packed_int) == sizeof(struct hl_packed_short_buff));
82
83 struct hl_pkt_write_char_put {
84 struct hl_packed_int syscall_nr;
85 struct hl_packed_int fd;
86 struct hl_packed_short_buff buff;
87 struct hl_packed_int nbyte;
88 } __packed;
89
90 struct hl_pkt_write_char_get {
91 struct hl_packed_int byte_written;
92 struct hl_packed_int host_errno;
93 } __packed;
94
95 #define MAX_PKT_SZ MAX(sizeof(struct hl_pkt_write_char_put), sizeof(struct hl_pkt_write_char_get))
96 #define HL_HEADERS_SZ (sizeof(struct hl_hdr) + sizeof(struct hl_pkt_hdr))
97 BUILD_ASSERT(HL_HEADERS_SZ + MAX_PKT_SZ < HL_MAX_DCACHE_LINE);
98
99 union payload_u {
100 struct hl_pkt_write_char_put pkt_write_char_put;
101 struct hl_pkt_write_char_get pkt_write_char_get;
102 char reserved[HL_MAX_DCACHE_LINE - HL_HEADERS_SZ];
103 } __packed;
104
105 BUILD_ASSERT(sizeof(union payload_u) % 4 == 0);
106
107 /* Main hostlink structure. */
108 struct hl {
109 /* General hostlink information. */
110 volatile struct hl_hdr hdr;
111 /* Start of the hostlink buffer. */
112 volatile struct hl_pkt_hdr pkt_hdr;
113 /* Payload buffer */
114 volatile union payload_u payload;
115 } __aligned(HL_MAX_DCACHE_LINE) __packed;
116
117 /* In general we must exactly fit into one or multiple cache lines as we shouldn't share hostlink
118 * buffer (which is uncached) with any cached data
119 */
120 BUILD_ASSERT(sizeof(struct hl) % HL_MAX_DCACHE_LINE == 0);
121 /* However, with current supported functionality we fit into one MAX cache line. If we add
122 * some features which require bigger payload buffer this might become not true.
123 */
124 BUILD_ASSERT(sizeof(struct hl) == HL_MAX_DCACHE_LINE);
125
126 /* Main structure. Do not rename as nSIM simulator / MDB debugger looks for the '__HOSTLINK__'
127 * symbol. We need to keep it initialized so it won't be put into BSS (so we won't write with
128 * regular cached access in it).
129 */
130 volatile struct hl __HOSTLINK__ = {
131 .hdr = {
132 .version = HL_VERSION,
133 .target2host_addr = HL_NOADDRESS
134 }
135 };
136
137 BUILD_ASSERT(sizeof(__HOSTLINK__) % HL_MAX_DCACHE_LINE == 0);
138
139 #if defined(__CCAC__)
140 #define HL_HAS_C_ACCESSORS 0
141 #elif defined(CONFIG_ISA_ARCV3)
142 #define HL_HAS_C_ACCESSORS 0
143 #else
144 #define HL_HAS_C_ACCESSORS 1
145 #endif
146
147 #if HL_HAS_C_ACCESSORS
148
149 #ifndef __uncached
150 #define __uncached __attribute__((uncached))
151 #endif /* __uncached */
152
hl_write32(volatile void * addr,uint32_t val)153 static inline void hl_write32(volatile void *addr, uint32_t val)
154 {
155 *(volatile __uncached uint32_t *)addr = val;
156 }
157
hl_write16(volatile void * addr,uint16_t val)158 static inline void hl_write16(volatile void *addr, uint16_t val)
159 {
160 *(volatile __uncached uint16_t *)addr = val;
161 }
162
hl_write8(volatile void * addr,uint8_t val)163 static inline void hl_write8(volatile void *addr, uint8_t val)
164 {
165 *(volatile __uncached uint8_t *)addr = val;
166 }
167
hl_read32(volatile void * addr)168 static inline uint32_t hl_read32(volatile void *addr)
169 {
170 return *(volatile __uncached uint32_t *)addr;
171 }
172
hl_read16(volatile void * addr)173 static inline uint16_t hl_read16(volatile void *addr)
174 {
175 return *(volatile __uncached uint16_t *)addr;
176 }
177 #else
hl_write32(volatile void * addr,uint32_t val)178 static inline void hl_write32(volatile void *addr, uint32_t val)
179 {
180 __asm__ __volatile__("st.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
181 }
182
hl_write16(volatile void * addr,uint16_t val)183 static inline void hl_write16(volatile void *addr, uint16_t val)
184 {
185 __asm__ __volatile__("stb.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
186 }
187
hl_write8(volatile void * addr,uint8_t val)188 static inline void hl_write8(volatile void *addr, uint8_t val)
189 {
190 __asm__ __volatile__("sth.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
191 }
192
hl_read32(volatile void * addr)193 static inline uint32_t hl_read32(volatile void *addr)
194 {
195 uint32_t w;
196
197 __asm__ __volatile__("ld.di %0, [%1]" : "=r" (w) : "r" (addr) : "memory");
198
199 return w;
200 }
201
hl_read16(volatile void * addr)202 static inline uint16_t hl_read16(volatile void *addr)
203 {
204 uint16_t w;
205
206 __asm__ __volatile__("ld.di %0, [%1]" : "=r" (w) : "r" (addr) : "memory");
207
208 return w;
209 }
210 #endif /* HL_HAS_C_ACCESSORS */
211
212 /* Get hostlink payload size (iochunk + reserved space). */
hl_payload_size(void)213 static uint32_t hl_payload_size(void)
214 {
215 return sizeof(__HOSTLINK__.payload);
216 }
217
218 #define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
219
220 /* Fill hostlink packet header. */
hl_pkt_init(volatile struct hl_pkt_hdr * pkt,int size)221 static void hl_pkt_init(volatile struct hl_pkt_hdr *pkt, int size)
222 {
223 hl_write32(&pkt->packet_id, 1);
224 hl_write32(&pkt->total_size, ALIGN(size, 4) + sizeof(struct hl_pkt_hdr));
225 hl_write32(&pkt->priority, 0);
226 hl_write32(&pkt->type, 0);
227 hl_write32(&pkt->checksum, 0);
228 }
229
230 /* Send hostlink packet to the host. */
hl_static_send(size_t payload_used)231 static void hl_static_send(size_t payload_used)
232 {
233 /* We are OK to cast pointer to uint32_t even on 64bit platforms as we support to build
234 * Zephyr on ARCv3 64bit only to lower 4GiB. Still we need to cast via uintptr_t to avoid
235 * compiler warnings.
236 */
237 uint32_t buf_addr = (uint32_t)(uintptr_t)(&__HOSTLINK__.pkt_hdr);
238
239 hl_pkt_init(&__HOSTLINK__.pkt_hdr, payload_used);
240
241 hl_write32(&__HOSTLINK__.hdr.buf_addr, buf_addr);
242 hl_write32(&__HOSTLINK__.hdr.payload_size, hl_payload_size());
243 hl_write32(&__HOSTLINK__.hdr.host2target_addr, HL_NOADDRESS);
244 hl_write32(&__HOSTLINK__.hdr.version, HL_VERSION);
245 hl_write32(&__HOSTLINK__.hdr.options, 0);
246 hl_write32(&__HOSTLINK__.hdr.break_to_mon_addr, 0);
247
248 compiler_barrier();
249
250 /* This tells the debugger we have a command.
251 * It is responsibility of debugger to set this back to HL_NOADDRESS
252 * after receiving the packet.
253 * Please note that we don't wait here because some implementations
254 * use hl_blockedPeek() function as a signal that we send a message.
255 */
256 hl_write32(&__HOSTLINK__.hdr.target2host_addr, buf_addr);
257
258 compiler_barrier();
259 }
260
261 /*
262 * Wait for host response and return pointer to hostlink payload.
263 * Symbol hl_blockedPeek() is used by the simulator as message signal.
264 */
_hl_blockedPeek(void)265 static void __noinline _hl_blockedPeek(void)
266 {
267 while (hl_read32(&__HOSTLINK__.hdr.host2target_addr) == HL_NOADDRESS) {
268 /* TODO: Timeout. */
269 }
270 }
271
hl_static_recv(void)272 static void hl_static_recv(void)
273 {
274 compiler_barrier();
275 _hl_blockedPeek();
276 compiler_barrier();
277 }
278
279 /* Mark hostlink buffer as "No message here". */
hl_delete(void)280 static void hl_delete(void)
281 {
282 hl_write32(&__HOSTLINK__.hdr.target2host_addr, HL_NOADDRESS);
283 }
284
285 /* Parameter types. */
286 #define PAT_CHAR 1
287 #define PAT_SHORT 2
288 #define PAT_INT 3
289 #define PAT_STRING 4
290 /* For future use. */
291 #define PAT_INT64 5
292
hl_static_pack_int(volatile struct hl_packed_int * pack,int32_t value)293 static void hl_static_pack_int(volatile struct hl_packed_int *pack, int32_t value)
294 {
295 hl_write16(&pack->type, PAT_INT);
296 hl_write16(&pack->size, 4);
297 hl_write32(&pack->value, value);
298 }
299
hl_static_pack_char(volatile struct hl_packed_short_buff * pack,unsigned char c)300 static void hl_static_pack_char(volatile struct hl_packed_short_buff *pack, unsigned char c)
301 {
302 hl_write16(&pack->type, PAT_STRING);
303 hl_write16(&pack->size, 1);
304 hl_write8(&pack->payload_short, c);
305 }
306
hl_static_unpack_int(volatile struct hl_packed_int * pack,int32_t * value)307 static int hl_static_unpack_int(volatile struct hl_packed_int *pack, int32_t *value)
308 {
309 uint16_t type = hl_read16(&pack->type);
310 uint16_t size = hl_read16(&pack->size);
311
312 if (type != PAT_INT) {
313 return -1;
314 }
315
316 if (size != 4) {
317 return -1;
318 }
319
320 *value = hl_read32(&pack->value);
321
322 return 0;
323 }
324
hl_write_char(int fd,const char c)325 static inline int32_t hl_write_char(int fd, const char c)
326 {
327 /*
328 * Format:
329 * in, int -> syscall (HL_SYSCALL_WRITE)
330 * in, int -> file descriptor
331 * in, ptr -> buffer
332 * in, int -> bytes number
333 * out, int -> bytes written
334 * out, int, host errno
335 */
336
337 hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.syscall_nr, HL_SYSCALL_WRITE);
338
339 hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.fd, fd);
340
341 hl_static_pack_char(&__HOSTLINK__.payload.pkt_write_char_put.buff, c);
342
343 hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.nbyte, 1);
344
345 hl_static_send(sizeof(struct hl_pkt_write_char_put));
346 hl_static_recv();
347
348 int32_t bwr = 0;
349 int ret = hl_static_unpack_int(&__HOSTLINK__.payload.pkt_write_char_get.byte_written, &bwr);
350
351 /* we can get host errno here with:
352 * hl_static_unpack_int(&__HOSTLINK__.pkt_write_char_get.host_errno, &host_errno);
353 * but we don't need it for UART emulation.
354 */
355
356 if (bwr <= 0) {
357 ret = -1;
358 }
359
360 hl_delete();
361
362 return ret;
363 }
364
365 /**
366 * @brief Poll the device for input.
367 *
368 * @param dev UART device struct
369 * @param c Pointer to character
370 *
371 * @return 0 if a character arrived, -1 if the input buffer if empty.
372 */
uart_hostlink_poll_in(const struct device * dev,unsigned char * c)373 static int uart_hostlink_poll_in(const struct device *dev, unsigned char *c)
374 {
375 ARG_UNUSED(dev);
376
377 /* We plan to use hostlink for logging, so no much sense in poll_in implementation */
378 return -1;
379 }
380
381 /**
382 * @brief Output a character in polled mode.
383 *
384 * @param dev UART device struct
385 * @param c Character to send
386 */
uart_hostlink_poll_out(const struct device * dev,unsigned char c)387 static void uart_hostlink_poll_out(const struct device *dev, unsigned char c)
388 {
389 ARG_UNUSED(dev);
390
391 hl_write_char(1, c);
392 }
393
394 static DEVICE_API(uart, uart_hostlink_driver_api) = {
395 .poll_in = uart_hostlink_poll_in,
396 .poll_out = uart_hostlink_poll_out,
397 };
398
399 DEVICE_DT_DEFINE(DT_NODELABEL(hostlink), NULL, NULL, NULL, NULL, PRE_KERNEL_1,
400 CONFIG_SERIAL_INIT_PRIORITY, &uart_hostlink_driver_api);
401