hmp: Ignore Error objects where the return value suffices
[qemu.git] / target / mips / mips-semi.c
1 /*
2 * Unified Hosting Interface syscalls.
3 *
4 * Copyright (c) 2015 Imagination Technologies
5 *
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 of the License, or (at your option) any later version.
10 *
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.
15 *
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/>.
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 "hw/semihosting/semihost.h"
26 #include "hw/semihosting/console.h"
27
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;
46
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;
66
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
75 };
76
77 /* Errno values taken from asm-mips/errno.h */
78 static uint16_t host_to_mips_errno[] = {
79 [ENAMETOOLONG] = 78,
80 #ifdef EOVERFLOW
81 [EOVERFLOW] = 79,
82 #endif
83 #ifdef ELOOP
84 [ELOOP] = 90,
85 #endif
86 };
87
88 static int errno_mips(int err)
89 {
90 if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
91 return EINVAL;
92 } else if (host_to_mips_errno[err]) {
93 return host_to_mips_errno[err];
94 } else {
95 return err;
96 }
97 }
98
99 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
100 target_ulong vaddr)
101 {
102 hwaddr len = sizeof(struct UHIStat);
103 UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
104 if (!dst) {
105 errno = EFAULT;
106 return -1;
107 }
108
109 dst->uhi_st_dev = tswap16(src->st_dev);
110 dst->uhi_st_ino = tswap16(src->st_ino);
111 dst->uhi_st_mode = tswap32(src->st_mode);
112 dst->uhi_st_nlink = tswap16(src->st_nlink);
113 dst->uhi_st_uid = tswap16(src->st_uid);
114 dst->uhi_st_gid = tswap16(src->st_gid);
115 dst->uhi_st_rdev = tswap16(src->st_rdev);
116 dst->uhi_st_size = tswap64(src->st_size);
117 dst->uhi_st_atime = tswap64(src->st_atime);
118 dst->uhi_st_mtime = tswap64(src->st_mtime);
119 dst->uhi_st_ctime = tswap64(src->st_ctime);
120 #ifdef _WIN32
121 dst->uhi_st_blksize = 0;
122 dst->uhi_st_blocks = 0;
123 #else
124 dst->uhi_st_blksize = tswap64(src->st_blksize);
125 dst->uhi_st_blocks = tswap64(src->st_blocks);
126 #endif
127 unlock_user(dst, vaddr, len);
128 return 0;
129 }
130
131 static int get_open_flags(target_ulong target_flags)
132 {
133 int open_flags = 0;
134
135 if (target_flags & UHIOpen_RDWR) {
136 open_flags |= O_RDWR;
137 } else if (target_flags & UHIOpen_WRONLY) {
138 open_flags |= O_WRONLY;
139 } else {
140 open_flags |= O_RDONLY;
141 }
142
143 open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
144 open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0;
145 open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0;
146 open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0;
147
148 return open_flags;
149 }
150
151 static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
152 target_ulong len, target_ulong offset)
153 {
154 int num_of_bytes;
155 void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
156 if (!dst) {
157 errno = EFAULT;
158 return -1;
159 }
160
161 if (offset) {
162 #ifdef _WIN32
163 num_of_bytes = 0;
164 #else
165 num_of_bytes = pwrite(fd, dst, len, offset);
166 #endif
167 } else {
168 num_of_bytes = write(fd, dst, len);
169 }
170
171 unlock_user(dst, vaddr, 0);
172 return num_of_bytes;
173 }
174
175 static int read_from_file(CPUMIPSState *env, target_ulong fd,
176 target_ulong vaddr, target_ulong len,
177 target_ulong offset)
178 {
179 int num_of_bytes;
180 void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
181 if (!dst) {
182 errno = EFAULT;
183 return -1;
184 }
185
186 if (offset) {
187 #ifdef _WIN32
188 num_of_bytes = 0;
189 #else
190 num_of_bytes = pread(fd, dst, len, offset);
191 #endif
192 } else {
193 num_of_bytes = read(fd, dst, len);
194 }
195
196 unlock_user(dst, vaddr, len);
197 return num_of_bytes;
198 }
199
200 static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
201 target_ulong vaddr)
202 {
203 int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
204 char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
205 if (!dst) {
206 return -1;
207 }
208
209 strcpy(dst, semihosting_get_arg(arg_num));
210
211 unlock_user(dst, vaddr, strsize);
212 return 0;
213 }
214
215 #define GET_TARGET_STRING(p, addr) \
216 do { \
217 p = lock_user_string(addr); \
218 if (!p) { \
219 gpr[2] = -1; \
220 gpr[3] = EFAULT; \
221 return; \
222 } \
223 } while (0)
224
225 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \
226 do { \
227 p = lock_user_string(addr); \
228 if (!p) { \
229 gpr[2] = -1; \
230 gpr[3] = EFAULT; \
231 return; \
232 } \
233 p2 = lock_user_string(addr2); \
234 if (!p2) { \
235 unlock_user(p, addr, 0); \
236 gpr[2] = -1; \
237 gpr[3] = EFAULT; \
238 return; \
239 } \
240 } while (0)
241
242 #define FREE_TARGET_STRING(p, gpr) \
243 do { \
244 unlock_user(p, gpr, 0); \
245 } while (0)
246
247 void helper_do_semihosting(CPUMIPSState *env)
248 {
249 target_ulong *gpr = env->active_tc.gpr;
250 const UHIOp op = gpr[25];
251 char *p, *p2;
252
253 switch (op) {
254 case UHI_exit:
255 qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
256 exit(gpr[4]);
257 case UHI_open:
258 GET_TARGET_STRING(p, gpr[4]);
259 if (!strcmp("/dev/stdin", p)) {
260 gpr[2] = 0;
261 } else if (!strcmp("/dev/stdout", p)) {
262 gpr[2] = 1;
263 } else if (!strcmp("/dev/stderr", p)) {
264 gpr[2] = 2;
265 } else {
266 gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
267 gpr[3] = errno_mips(errno);
268 }
269 FREE_TARGET_STRING(p, gpr[4]);
270 break;
271 case UHI_close:
272 if (gpr[4] < 3) {
273 /* ignore closing stdin/stdout/stderr */
274 gpr[2] = 0;
275 return;
276 }
277 gpr[2] = close(gpr[4]);
278 gpr[3] = errno_mips(errno);
279 break;
280 case UHI_read:
281 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
282 gpr[3] = errno_mips(errno);
283 break;
284 case UHI_write:
285 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
286 gpr[3] = errno_mips(errno);
287 break;
288 case UHI_lseek:
289 gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
290 gpr[3] = errno_mips(errno);
291 break;
292 case UHI_unlink:
293 GET_TARGET_STRING(p, gpr[4]);
294 gpr[2] = remove(p);
295 gpr[3] = errno_mips(errno);
296 FREE_TARGET_STRING(p, gpr[4]);
297 break;
298 case UHI_fstat:
299 {
300 struct stat sbuf;
301 memset(&sbuf, 0, sizeof(sbuf));
302 gpr[2] = fstat(gpr[4], &sbuf);
303 gpr[3] = errno_mips(errno);
304 if (gpr[2]) {
305 return;
306 }
307 gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
308 gpr[3] = errno_mips(errno);
309 }
310 break;
311 case UHI_argc:
312 gpr[2] = semihosting_get_argc();
313 break;
314 case UHI_argnlen:
315 if (gpr[4] >= semihosting_get_argc()) {
316 gpr[2] = -1;
317 return;
318 }
319 gpr[2] = strlen(semihosting_get_arg(gpr[4]));
320 break;
321 case UHI_argn:
322 if (gpr[4] >= semihosting_get_argc()) {
323 gpr[2] = -1;
324 return;
325 }
326 gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
327 break;
328 case UHI_plog:
329 GET_TARGET_STRING(p, gpr[4]);
330 p2 = strstr(p, "%d");
331 if (p2) {
332 int char_num = p2 - p;
333 GString *s = g_string_new_len(p, char_num);
334 g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
335 gpr[2] = qemu_semihosting_log_out(s->str, s->len);
336 g_string_free(s, true);
337 } else {
338 gpr[2] = qemu_semihosting_log_out(p, strlen(p));
339 }
340 FREE_TARGET_STRING(p, gpr[4]);
341 break;
342 case UHI_assert:
343 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
344 printf("assertion '");
345 printf("\"%s\"", p);
346 printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
347 FREE_TARGET_STRING(p2, gpr[5]);
348 FREE_TARGET_STRING(p, gpr[4]);
349 abort();
350 break;
351 case UHI_pread:
352 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
353 gpr[3] = errno_mips(errno);
354 break;
355 case UHI_pwrite:
356 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
357 gpr[3] = errno_mips(errno);
358 break;
359 #ifndef _WIN32
360 case UHI_link:
361 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
362 gpr[2] = link(p, p2);
363 gpr[3] = errno_mips(errno);
364 FREE_TARGET_STRING(p2, gpr[5]);
365 FREE_TARGET_STRING(p, gpr[4]);
366 break;
367 #endif
368 default:
369 fprintf(stderr, "Unknown UHI operation %d\n", op);
370 abort();
371 }
372 return;
373 }