1*7e2a1038SAlvin Chang // SPDX-License-Identifier: BSD-2-Clause
2*7e2a1038SAlvin Chang /*
3*7e2a1038SAlvin Chang * Copyright (c) 2024 Andes Technology Corporation
4*7e2a1038SAlvin Chang */
5*7e2a1038SAlvin Chang
6*7e2a1038SAlvin Chang #include <kernel/semihosting.h>
7*7e2a1038SAlvin Chang #include <string.h>
8*7e2a1038SAlvin Chang
9*7e2a1038SAlvin Chang /*
10*7e2a1038SAlvin Chang * ARM and RISC-V have defined the standard way to perform
11*7e2a1038SAlvin Chang * the semihosting operations.
12*7e2a1038SAlvin Chang * - Operation codes and open modes are identical.
13*7e2a1038SAlvin Chang * - The implementation of the low-level __do_semihosting() call is
14*7e2a1038SAlvin Chang * architecture-specific.
15*7e2a1038SAlvin Chang * - Arm semihosting interface:
16*7e2a1038SAlvin Chang * https://developer.arm.com/documentation/dui0471/g/Semihosting/The-semihosting-interface
17*7e2a1038SAlvin Chang * - RISC-V semihosting interface:
18*7e2a1038SAlvin Chang * https://github.com/riscv-non-isa/riscv-semihosting/blob/main/binary-interface.adoc
19*7e2a1038SAlvin Chang */
20*7e2a1038SAlvin Chang
21*7e2a1038SAlvin Chang /* An integer that specifies the file open mode */
22*7e2a1038SAlvin Chang enum semihosting_open_mode {
23*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_R = 0,
24*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_RB = 1,
25*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_RX = 2,
26*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_RXB = 3,
27*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_W = 4,
28*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_WB = 5,
29*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_WX = 6,
30*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_WXB = 7,
31*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_A = 8,
32*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_AB = 9,
33*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_AX = 10,
34*7e2a1038SAlvin Chang SEMIHOSTING_OPEN_AXB = 11,
35*7e2a1038SAlvin Chang };
36*7e2a1038SAlvin Chang
37*7e2a1038SAlvin Chang enum semihosting_sys_ops {
38*7e2a1038SAlvin Chang /* Regular operations */
39*7e2a1038SAlvin Chang SEMIHOSTING_SYS_OPEN = 0x01,
40*7e2a1038SAlvin Chang SEMIHOSTING_SYS_CLOSE = 0x02,
41*7e2a1038SAlvin Chang SEMIHOSTING_SYS_WRITEC = 0x03,
42*7e2a1038SAlvin Chang SEMIHOSTING_SYS_WRITE = 0x05,
43*7e2a1038SAlvin Chang SEMIHOSTING_SYS_READ = 0x06,
44*7e2a1038SAlvin Chang SEMIHOSTING_SYS_READC = 0x07,
45*7e2a1038SAlvin Chang };
46*7e2a1038SAlvin Chang
47*7e2a1038SAlvin Chang struct semihosting_param_t {
48*7e2a1038SAlvin Chang uintptr_t param0;
49*7e2a1038SAlvin Chang uintptr_t param1;
50*7e2a1038SAlvin Chang uintptr_t param2;
51*7e2a1038SAlvin Chang };
52*7e2a1038SAlvin Chang
53*7e2a1038SAlvin Chang /**
54*7e2a1038SAlvin Chang * @brief Read one character byte from the semihosting host debug terminal
55*7e2a1038SAlvin Chang *
56*7e2a1038SAlvin Chang * @retval the character read from the semihosting host
57*7e2a1038SAlvin Chang */
semihosting_sys_readc(void)58*7e2a1038SAlvin Chang char semihosting_sys_readc(void)
59*7e2a1038SAlvin Chang {
60*7e2a1038SAlvin Chang return __do_semihosting(SEMIHOSTING_SYS_READC, 0);
61*7e2a1038SAlvin Chang }
62*7e2a1038SAlvin Chang
63*7e2a1038SAlvin Chang /**
64*7e2a1038SAlvin Chang * @brief Write one character byte to the semihosting host debug terminal
65*7e2a1038SAlvin Chang * @param c: the character to be written
66*7e2a1038SAlvin Chang */
semihosting_sys_writec(char c)67*7e2a1038SAlvin Chang void semihosting_sys_writec(char c)
68*7e2a1038SAlvin Chang {
69*7e2a1038SAlvin Chang __do_semihosting(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&c);
70*7e2a1038SAlvin Chang }
71*7e2a1038SAlvin Chang
72*7e2a1038SAlvin Chang /**
73*7e2a1038SAlvin Chang * @brief Request the semihosting host to open a file on the host system
74*7e2a1038SAlvin Chang * @param fname: the path or name of the file
75*7e2a1038SAlvin Chang * @param flags: sys/fcntl.h standard flags to open the file with
76*7e2a1038SAlvin Chang *
77*7e2a1038SAlvin Chang * @retval nonzero if OK, or -1 if fails
78*7e2a1038SAlvin Chang */
semihosting_open(const char * fname,int flags)79*7e2a1038SAlvin Chang int semihosting_open(const char *fname, int flags)
80*7e2a1038SAlvin Chang {
81*7e2a1038SAlvin Chang int semi_open_flags = 0;
82*7e2a1038SAlvin Chang const int flags_mask = O_RDONLY | O_WRONLY | O_RDWR |
83*7e2a1038SAlvin Chang O_CREAT | O_TRUNC | O_APPEND;
84*7e2a1038SAlvin Chang struct semihosting_param_t arg = { };
85*7e2a1038SAlvin Chang
86*7e2a1038SAlvin Chang /* Convert the flags to semihosting open. */
87*7e2a1038SAlvin Chang switch (flags & flags_mask) {
88*7e2a1038SAlvin Chang case O_RDONLY: /* 'r' */
89*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_R;
90*7e2a1038SAlvin Chang break;
91*7e2a1038SAlvin Chang case O_WRONLY | O_CREAT | O_TRUNC: /* 'w' */
92*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_W;
93*7e2a1038SAlvin Chang break;
94*7e2a1038SAlvin Chang case O_WRONLY | O_CREAT | O_APPEND: /* 'a' */
95*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_A;
96*7e2a1038SAlvin Chang break;
97*7e2a1038SAlvin Chang case O_RDWR: /* 'r+' */
98*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_RX;
99*7e2a1038SAlvin Chang break;
100*7e2a1038SAlvin Chang case O_RDWR | O_CREAT | O_TRUNC: /* 'w+' */
101*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_WX;
102*7e2a1038SAlvin Chang break;
103*7e2a1038SAlvin Chang case O_RDWR | O_CREAT | O_APPEND: /* 'a+' */
104*7e2a1038SAlvin Chang semi_open_flags = SEMIHOSTING_OPEN_AX;
105*7e2a1038SAlvin Chang break;
106*7e2a1038SAlvin Chang default:
107*7e2a1038SAlvin Chang return -1;
108*7e2a1038SAlvin Chang }
109*7e2a1038SAlvin Chang
110*7e2a1038SAlvin Chang arg.param0 = (uintptr_t)fname;
111*7e2a1038SAlvin Chang arg.param1 = semi_open_flags;
112*7e2a1038SAlvin Chang arg.param2 = strlen(fname);
113*7e2a1038SAlvin Chang
114*7e2a1038SAlvin Chang return (int)__do_semihosting(SEMIHOSTING_SYS_OPEN, (uintptr_t)&arg);
115*7e2a1038SAlvin Chang }
116*7e2a1038SAlvin Chang
117*7e2a1038SAlvin Chang /**
118*7e2a1038SAlvin Chang * @brief Read data from a file on the semihosting host system
119*7e2a1038SAlvin Chang * @param fd: a handle for a file previously opened
120*7e2a1038SAlvin Chang * @param ptr: pointer to a buffer
121*7e2a1038SAlvin Chang * @param len: the number of bytes to read to the buffer from the file
122*7e2a1038SAlvin Chang *
123*7e2a1038SAlvin Chang * @retval zero if OK, the same value as @len if fails, smaller value than @len
124*7e2a1038SAlvin Chang * for partial success
125*7e2a1038SAlvin Chang */
semihosting_read(int fd,void * ptr,size_t len)126*7e2a1038SAlvin Chang size_t semihosting_read(int fd, void *ptr, size_t len)
127*7e2a1038SAlvin Chang {
128*7e2a1038SAlvin Chang struct semihosting_param_t arg = {
129*7e2a1038SAlvin Chang .param0 = fd,
130*7e2a1038SAlvin Chang .param1 = (uintptr_t)ptr,
131*7e2a1038SAlvin Chang .param2 = len
132*7e2a1038SAlvin Chang };
133*7e2a1038SAlvin Chang
134*7e2a1038SAlvin Chang return __do_semihosting(SEMIHOSTING_SYS_READ, (uintptr_t)&arg);
135*7e2a1038SAlvin Chang }
136*7e2a1038SAlvin Chang
137*7e2a1038SAlvin Chang /**
138*7e2a1038SAlvin Chang * @brief Write data into a file on the semihosting host system
139*7e2a1038SAlvin Chang * @param fd: a handle for a file previously opened
140*7e2a1038SAlvin Chang * @param ptr: pointer to a buffer
141*7e2a1038SAlvin Chang * @param len: the number of bytes to be written from the buffer to the file
142*7e2a1038SAlvin Chang *
143*7e2a1038SAlvin Chang * @retval zero if OK, otherwise the number of bytes that are not written
144*7e2a1038SAlvin Chang */
semihosting_write(int fd,const void * ptr,size_t len)145*7e2a1038SAlvin Chang size_t semihosting_write(int fd, const void *ptr, size_t len)
146*7e2a1038SAlvin Chang {
147*7e2a1038SAlvin Chang struct semihosting_param_t arg = {
148*7e2a1038SAlvin Chang .param0 = fd,
149*7e2a1038SAlvin Chang .param1 = (uintptr_t)ptr,
150*7e2a1038SAlvin Chang .param2 = len
151*7e2a1038SAlvin Chang };
152*7e2a1038SAlvin Chang
153*7e2a1038SAlvin Chang return __do_semihosting(SEMIHOSTING_SYS_WRITE, (uintptr_t)&arg);
154*7e2a1038SAlvin Chang }
155*7e2a1038SAlvin Chang
156*7e2a1038SAlvin Chang /**
157*7e2a1038SAlvin Chang * @brief Close a file on the semihosting host system
158*7e2a1038SAlvin Chang * @param fd: a handle for a file previously opened
159*7e2a1038SAlvin Chang *
160*7e2a1038SAlvin Chang * @retval zero if OK, -1 if fails
161*7e2a1038SAlvin Chang */
semihosting_close(int fd)162*7e2a1038SAlvin Chang int semihosting_close(int fd)
163*7e2a1038SAlvin Chang {
164*7e2a1038SAlvin Chang struct semihosting_param_t arg = {
165*7e2a1038SAlvin Chang .param0 = fd,
166*7e2a1038SAlvin Chang };
167*7e2a1038SAlvin Chang
168*7e2a1038SAlvin Chang return (int)__do_semihosting(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&arg);
169*7e2a1038SAlvin Chang }
170