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