1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21#include "cpu.h"
22#include "qemu/log.h"
23#include "exec/helper-proto.h"
24#include "exec/softmmu-semi.h"
25#include "semihosting/semihost.h"
26#include "semihosting/console.h"
27
28typedef enum UHIOp {
29 UHI_exit = 1,
30 UHI_open = 2,
31 UHI_close = 3,
32 UHI_read = 4,
33 UHI_write = 5,
34 UHI_lseek = 6,
35 UHI_unlink = 7,
36 UHI_fstat = 8,
37 UHI_argc = 9,
38 UHI_argnlen = 10,
39 UHI_argn = 11,
40 UHI_plog = 13,
41 UHI_assert = 14,
42 UHI_pread = 19,
43 UHI_pwrite = 20,
44 UHI_link = 22
45} UHIOp;
46
47typedef struct UHIStat {
48 int16_t uhi_st_dev;
49 uint16_t uhi_st_ino;
50 uint32_t uhi_st_mode;
51 uint16_t uhi_st_nlink;
52 uint16_t uhi_st_uid;
53 uint16_t uhi_st_gid;
54 int16_t uhi_st_rdev;
55 uint64_t uhi_st_size;
56 uint64_t uhi_st_atime;
57 uint64_t uhi_st_spare1;
58 uint64_t uhi_st_mtime;
59 uint64_t uhi_st_spare2;
60 uint64_t uhi_st_ctime;
61 uint64_t uhi_st_spare3;
62 uint64_t uhi_st_blksize;
63 uint64_t uhi_st_blocks;
64 uint64_t uhi_st_spare4[2];
65} UHIStat;
66
67enum UHIOpenFlags {
68 UHIOpen_RDONLY = 0x0,
69 UHIOpen_WRONLY = 0x1,
70 UHIOpen_RDWR = 0x2,
71 UHIOpen_APPEND = 0x8,
72 UHIOpen_CREAT = 0x200,
73 UHIOpen_TRUNC = 0x400,
74 UHIOpen_EXCL = 0x800
75};
76
77static int errno_mips(int host_errno)
78{
79
80 switch (host_errno) {
81 case 0: return 0;
82 case ENAMETOOLONG: return 78;
83#ifdef EOVERFLOW
84 case EOVERFLOW: return 79;
85#endif
86#ifdef ELOOP
87 case ELOOP: return 90;
88#endif
89 default: return EINVAL;
90 }
91}
92
93static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
94 target_ulong vaddr)
95{
96 hwaddr len = sizeof(struct UHIStat);
97 UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
98 if (!dst) {
99 errno = EFAULT;
100 return -1;
101 }
102
103 dst->uhi_st_dev = tswap16(src->st_dev);
104 dst->uhi_st_ino = tswap16(src->st_ino);
105 dst->uhi_st_mode = tswap32(src->st_mode);
106 dst->uhi_st_nlink = tswap16(src->st_nlink);
107 dst->uhi_st_uid = tswap16(src->st_uid);
108 dst->uhi_st_gid = tswap16(src->st_gid);
109 dst->uhi_st_rdev = tswap16(src->st_rdev);
110 dst->uhi_st_size = tswap64(src->st_size);
111 dst->uhi_st_atime = tswap64(src->st_atime);
112 dst->uhi_st_mtime = tswap64(src->st_mtime);
113 dst->uhi_st_ctime = tswap64(src->st_ctime);
114#ifdef _WIN32
115 dst->uhi_st_blksize = 0;
116 dst->uhi_st_blocks = 0;
117#else
118 dst->uhi_st_blksize = tswap64(src->st_blksize);
119 dst->uhi_st_blocks = tswap64(src->st_blocks);
120#endif
121 unlock_user(dst, vaddr, len);
122 return 0;
123}
124
125static int get_open_flags(target_ulong target_flags)
126{
127 int open_flags = 0;
128
129 if (target_flags & UHIOpen_RDWR) {
130 open_flags |= O_RDWR;
131 } else if (target_flags & UHIOpen_WRONLY) {
132 open_flags |= O_WRONLY;
133 } else {
134 open_flags |= O_RDONLY;
135 }
136
137 open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
138 open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0;
139 open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0;
140 open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0;
141
142 return open_flags;
143}
144
145static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
146 target_ulong len, target_ulong offset)
147{
148 int num_of_bytes;
149 void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
150 if (!dst) {
151 errno = EFAULT;
152 return -1;
153 }
154
155 if (offset) {
156#ifdef _WIN32
157 num_of_bytes = 0;
158#else
159 num_of_bytes = pwrite(fd, dst, len, offset);
160#endif
161 } else {
162 num_of_bytes = write(fd, dst, len);
163 }
164
165 unlock_user(dst, vaddr, 0);
166 return num_of_bytes;
167}
168
169static int read_from_file(CPUMIPSState *env, target_ulong fd,
170 target_ulong vaddr, target_ulong len,
171 target_ulong offset)
172{
173 int num_of_bytes;
174 void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
175 if (!dst) {
176 errno = EFAULT;
177 return -1;
178 }
179
180 if (offset) {
181#ifdef _WIN32
182 num_of_bytes = 0;
183#else
184 num_of_bytes = pread(fd, dst, len, offset);
185#endif
186 } else {
187 num_of_bytes = read(fd, dst, len);
188 }
189
190 unlock_user(dst, vaddr, len);
191 return num_of_bytes;
192}
193
194static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
195 target_ulong vaddr)
196{
197 int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
198 char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
199 if (!dst) {
200 return -1;
201 }
202
203 strcpy(dst, semihosting_get_arg(arg_num));
204
205 unlock_user(dst, vaddr, strsize);
206 return 0;
207}
208
209#define GET_TARGET_STRING(p, addr) \
210 do { \
211 p = lock_user_string(addr); \
212 if (!p) { \
213 gpr[2] = -1; \
214 gpr[3] = EFAULT; \
215 return; \
216 } \
217 } while (0)
218
219#define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \
220 do { \
221 p = lock_user_string(addr); \
222 if (!p) { \
223 gpr[2] = -1; \
224 gpr[3] = EFAULT; \
225 return; \
226 } \
227 p2 = lock_user_string(addr2); \
228 if (!p2) { \
229 unlock_user(p, addr, 0); \
230 gpr[2] = -1; \
231 gpr[3] = EFAULT; \
232 return; \
233 } \
234 } while (0)
235
236#define FREE_TARGET_STRING(p, gpr) \
237 do { \
238 unlock_user(p, gpr, 0); \
239 } while (0)
240
241void helper_do_semihosting(CPUMIPSState *env)
242{
243 target_ulong *gpr = env->active_tc.gpr;
244 const UHIOp op = gpr[25];
245 char *p, *p2;
246
247 switch (op) {
248 case UHI_exit:
249 qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
250 exit(gpr[4]);
251 case UHI_open:
252 GET_TARGET_STRING(p, gpr[4]);
253 if (!strcmp("/dev/stdin", p)) {
254 gpr[2] = 0;
255 } else if (!strcmp("/dev/stdout", p)) {
256 gpr[2] = 1;
257 } else if (!strcmp("/dev/stderr", p)) {
258 gpr[2] = 2;
259 } else {
260 gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
261 gpr[3] = errno_mips(errno);
262 }
263 FREE_TARGET_STRING(p, gpr[4]);
264 break;
265 case UHI_close:
266 if (gpr[4] < 3) {
267
268 gpr[2] = 0;
269 return;
270 }
271 gpr[2] = close(gpr[4]);
272 gpr[3] = errno_mips(errno);
273 break;
274 case UHI_read:
275 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
276 gpr[3] = errno_mips(errno);
277 break;
278 case UHI_write:
279 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
280 gpr[3] = errno_mips(errno);
281 break;
282 case UHI_lseek:
283 gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
284 gpr[3] = errno_mips(errno);
285 break;
286 case UHI_unlink:
287 GET_TARGET_STRING(p, gpr[4]);
288 gpr[2] = remove(p);
289 gpr[3] = errno_mips(errno);
290 FREE_TARGET_STRING(p, gpr[4]);
291 break;
292 case UHI_fstat:
293 {
294 struct stat sbuf;
295 memset(&sbuf, 0, sizeof(sbuf));
296 gpr[2] = fstat(gpr[4], &sbuf);
297 gpr[3] = errno_mips(errno);
298 if (gpr[2]) {
299 return;
300 }
301 gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
302 gpr[3] = errno_mips(errno);
303 }
304 break;
305 case UHI_argc:
306 gpr[2] = semihosting_get_argc();
307 break;
308 case UHI_argnlen:
309 if (gpr[4] >= semihosting_get_argc()) {
310 gpr[2] = -1;
311 return;
312 }
313 gpr[2] = strlen(semihosting_get_arg(gpr[4]));
314 break;
315 case UHI_argn:
316 if (gpr[4] >= semihosting_get_argc()) {
317 gpr[2] = -1;
318 return;
319 }
320 gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
321 break;
322 case UHI_plog:
323 GET_TARGET_STRING(p, gpr[4]);
324 p2 = strstr(p, "%d");
325 if (p2) {
326 int char_num = p2 - p;
327 GString *s = g_string_new_len(p, char_num);
328 g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
329 gpr[2] = qemu_semihosting_log_out(s->str, s->len);
330 g_string_free(s, true);
331 } else {
332 gpr[2] = qemu_semihosting_log_out(p, strlen(p));
333 }
334 FREE_TARGET_STRING(p, gpr[4]);
335 break;
336 case UHI_assert:
337 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
338 printf("assertion '");
339 printf("\"%s\"", p);
340 printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
341 FREE_TARGET_STRING(p2, gpr[5]);
342 FREE_TARGET_STRING(p, gpr[4]);
343 abort();
344 break;
345 case UHI_pread:
346 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
347 gpr[3] = errno_mips(errno);
348 break;
349 case UHI_pwrite:
350 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
351 gpr[3] = errno_mips(errno);
352 break;
353#ifndef _WIN32
354 case UHI_link:
355 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
356 gpr[2] = link(p, p2);
357 gpr[3] = errno_mips(errno);
358 FREE_TARGET_STRING(p2, gpr[5]);
359 FREE_TARGET_STRING(p, gpr[4]);
360 break;
361#endif
362 default:
363 fprintf(stderr, "Unknown UHI operation %d\n", op);
364 abort();
365 }
366 return;
367}
368