target/mips: Add UHI errno values
[qemu/rayw.git] / target / mips / tcg / sysemu / mips-semi.c
blob2a039baf4ce08d1a1da71e82e0d446d058a97b12
1 /*
2 * Unified Hosting Interface syscalls.
4 * Copyright (c) 2015 Imagination Technologies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "qemu/log.h"
23 #include "semihosting/softmmu-uaccess.h"
24 #include "semihosting/semihost.h"
25 #include "semihosting/console.h"
26 #include "internal.h"
28 typedef 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;
47 typedef 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;
67 enum 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
77 enum UHIErrno {
78 UHI_EACCESS = 13,
79 UHI_EAGAIN = 11,
80 UHI_EBADF = 9,
81 UHI_EBADMSG = 77,
82 UHI_EBUSY = 16,
83 UHI_ECONNRESET = 104,
84 UHI_EEXIST = 17,
85 UHI_EFBIG = 27,
86 UHI_EINTR = 4,
87 UHI_EINVAL = 22,
88 UHI_EIO = 5,
89 UHI_EISDIR = 21,
90 UHI_ELOOP = 92,
91 UHI_EMFILE = 24,
92 UHI_EMLINK = 31,
93 UHI_ENAMETOOLONG = 91,
94 UHI_ENETDOWN = 115,
95 UHI_ENETUNREACH = 114,
96 UHI_ENFILE = 23,
97 UHI_ENOBUFS = 105,
98 UHI_ENOENT = 2,
99 UHI_ENOMEM = 12,
100 UHI_ENOSPC = 28,
101 UHI_ENOSR = 63,
102 UHI_ENOTCONN = 128,
103 UHI_ENOTDIR = 20,
104 UHI_ENXIO = 6,
105 UHI_EOVERFLOW = 139,
106 UHI_EPERM = 1,
107 UHI_EPIPE = 32,
108 UHI_ERANGE = 34,
109 UHI_EROFS = 30,
110 UHI_ESPIPE = 29,
111 UHI_ETIMEDOUT = 116,
112 UHI_ETXTBSY = 26,
113 UHI_EWOULDBLOCK = 11,
114 UHI_EXDEV = 18,
117 static int errno_mips(int host_errno)
119 /* Errno values taken from asm-mips/errno.h */
120 switch (host_errno) {
121 case 0: return 0;
122 case ENAMETOOLONG: return 78;
123 #ifdef EOVERFLOW
124 case EOVERFLOW: return 79;
125 #endif
126 #ifdef ELOOP
127 case ELOOP: return 90;
128 #endif
129 default: return EINVAL;
133 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
134 target_ulong vaddr)
136 hwaddr len = sizeof(struct UHIStat);
137 UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
138 if (!dst) {
139 errno = EFAULT;
140 return -1;
143 dst->uhi_st_dev = tswap16(src->st_dev);
144 dst->uhi_st_ino = tswap16(src->st_ino);
145 dst->uhi_st_mode = tswap32(src->st_mode);
146 dst->uhi_st_nlink = tswap16(src->st_nlink);
147 dst->uhi_st_uid = tswap16(src->st_uid);
148 dst->uhi_st_gid = tswap16(src->st_gid);
149 dst->uhi_st_rdev = tswap16(src->st_rdev);
150 dst->uhi_st_size = tswap64(src->st_size);
151 dst->uhi_st_atime = tswap64(src->st_atime);
152 dst->uhi_st_mtime = tswap64(src->st_mtime);
153 dst->uhi_st_ctime = tswap64(src->st_ctime);
154 #ifdef _WIN32
155 dst->uhi_st_blksize = 0;
156 dst->uhi_st_blocks = 0;
157 #else
158 dst->uhi_st_blksize = tswap64(src->st_blksize);
159 dst->uhi_st_blocks = tswap64(src->st_blocks);
160 #endif
161 unlock_user(dst, vaddr, len);
162 return 0;
165 static int get_open_flags(target_ulong target_flags)
167 int open_flags = 0;
169 if (target_flags & UHIOpen_RDWR) {
170 open_flags |= O_RDWR;
171 } else if (target_flags & UHIOpen_WRONLY) {
172 open_flags |= O_WRONLY;
173 } else {
174 open_flags |= O_RDONLY;
177 open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
178 open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0;
179 open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0;
180 open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0;
182 return open_flags;
185 static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
186 target_ulong len, target_ulong offset)
188 int num_of_bytes;
189 void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
190 if (!dst) {
191 errno = EFAULT;
192 return -1;
195 if (offset) {
196 #ifdef _WIN32
197 num_of_bytes = 0;
198 #else
199 num_of_bytes = pwrite(fd, dst, len, offset);
200 #endif
201 } else {
202 num_of_bytes = write(fd, dst, len);
205 unlock_user(dst, vaddr, 0);
206 return num_of_bytes;
209 static int read_from_file(CPUMIPSState *env, target_ulong fd,
210 target_ulong vaddr, target_ulong len,
211 target_ulong offset)
213 int num_of_bytes;
214 void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
215 if (!dst) {
216 errno = EFAULT;
217 return -1;
220 if (offset) {
221 #ifdef _WIN32
222 num_of_bytes = 0;
223 #else
224 num_of_bytes = pread(fd, dst, len, offset);
225 #endif
226 } else {
227 num_of_bytes = read(fd, dst, len);
230 unlock_user(dst, vaddr, len);
231 return num_of_bytes;
234 static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
235 target_ulong vaddr)
237 int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
238 char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
239 if (!dst) {
240 return -1;
243 strcpy(dst, semihosting_get_arg(arg_num));
245 unlock_user(dst, vaddr, strsize);
246 return 0;
249 #define GET_TARGET_STRING(p, addr) \
250 do { \
251 p = lock_user_string(addr); \
252 if (!p) { \
253 gpr[2] = -1; \
254 gpr[3] = EFAULT; \
255 return; \
257 } while (0)
259 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \
260 do { \
261 p = lock_user_string(addr); \
262 if (!p) { \
263 gpr[2] = -1; \
264 gpr[3] = EFAULT; \
265 return; \
267 p2 = lock_user_string(addr2); \
268 if (!p2) { \
269 unlock_user(p, addr, 0); \
270 gpr[2] = -1; \
271 gpr[3] = EFAULT; \
272 return; \
274 } while (0)
276 #define FREE_TARGET_STRING(p, gpr) \
277 do { \
278 unlock_user(p, gpr, 0); \
279 } while (0)
281 void mips_semihosting(CPUMIPSState *env)
283 target_ulong *gpr = env->active_tc.gpr;
284 const UHIOp op = gpr[25];
285 char *p, *p2;
287 switch (op) {
288 case UHI_exit:
289 qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
290 exit(gpr[4]);
291 case UHI_open:
292 GET_TARGET_STRING(p, gpr[4]);
293 if (!strcmp("/dev/stdin", p)) {
294 gpr[2] = 0;
295 } else if (!strcmp("/dev/stdout", p)) {
296 gpr[2] = 1;
297 } else if (!strcmp("/dev/stderr", p)) {
298 gpr[2] = 2;
299 } else {
300 gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
301 gpr[3] = errno_mips(errno);
303 FREE_TARGET_STRING(p, gpr[4]);
304 break;
305 case UHI_close:
306 if (gpr[4] < 3) {
307 /* ignore closing stdin/stdout/stderr */
308 gpr[2] = 0;
309 return;
311 gpr[2] = close(gpr[4]);
312 gpr[3] = errno_mips(errno);
313 break;
314 case UHI_read:
315 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
316 gpr[3] = errno_mips(errno);
317 break;
318 case UHI_write:
319 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
320 gpr[3] = errno_mips(errno);
321 break;
322 case UHI_lseek:
323 gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
324 gpr[3] = errno_mips(errno);
325 break;
326 case UHI_unlink:
327 GET_TARGET_STRING(p, gpr[4]);
328 gpr[2] = remove(p);
329 gpr[3] = errno_mips(errno);
330 FREE_TARGET_STRING(p, gpr[4]);
331 break;
332 case UHI_fstat:
334 struct stat sbuf;
335 memset(&sbuf, 0, sizeof(sbuf));
336 gpr[2] = fstat(gpr[4], &sbuf);
337 gpr[3] = errno_mips(errno);
338 if (gpr[2]) {
339 return;
341 gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
342 gpr[3] = errno_mips(errno);
344 break;
345 case UHI_argc:
346 gpr[2] = semihosting_get_argc();
347 break;
348 case UHI_argnlen:
349 if (gpr[4] >= semihosting_get_argc()) {
350 gpr[2] = -1;
351 return;
353 gpr[2] = strlen(semihosting_get_arg(gpr[4]));
354 break;
355 case UHI_argn:
356 if (gpr[4] >= semihosting_get_argc()) {
357 gpr[2] = -1;
358 return;
360 gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
361 break;
362 case UHI_plog:
363 GET_TARGET_STRING(p, gpr[4]);
364 p2 = strstr(p, "%d");
365 if (p2) {
366 int char_num = p2 - p;
367 GString *s = g_string_new_len(p, char_num);
368 g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
369 gpr[2] = qemu_semihosting_log_out(s->str, s->len);
370 g_string_free(s, true);
371 } else {
372 gpr[2] = qemu_semihosting_log_out(p, strlen(p));
374 FREE_TARGET_STRING(p, gpr[4]);
375 break;
376 case UHI_assert:
377 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
378 printf("assertion '");
379 printf("\"%s\"", p);
380 printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
381 FREE_TARGET_STRING(p2, gpr[5]);
382 FREE_TARGET_STRING(p, gpr[4]);
383 abort();
384 break;
385 case UHI_pread:
386 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
387 gpr[3] = errno_mips(errno);
388 break;
389 case UHI_pwrite:
390 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
391 gpr[3] = errno_mips(errno);
392 break;
393 #ifndef _WIN32
394 case UHI_link:
395 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
396 gpr[2] = link(p, p2);
397 gpr[3] = errno_mips(errno);
398 FREE_TARGET_STRING(p2, gpr[5]);
399 FREE_TARGET_STRING(p, gpr[4]);
400 break;
401 #endif
402 default:
403 fprintf(stderr, "Unknown UHI operation %d\n", op);
404 abort();
406 return;