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