Fix the size of the property fields.
[qemu/navara.git] / arm-semi.c
blobea76aac6f7cd4ca62402fadfa6ca9168d4b0d47b
1 /*
2 * Arm "Angel" semihosting syscalls
4 * Copyright (c) 2005, 2007 CodeSourcery.
5 * Written by Paul Brook.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <time.h>
29 #include "cpu.h"
30 #ifdef CONFIG_USER_ONLY
31 #include "qemu.h"
33 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
34 #else
35 #include "qemu-common.h"
36 #include "sysemu.h"
37 #include "gdbstub.h"
38 #endif
40 #undef SYS_OPEN
41 #define SYS_OPEN 0x01
42 #define SYS_CLOSE 0x02
43 #define SYS_WRITEC 0x03
44 #define SYS_WRITE0 0x04
45 #define SYS_WRITE 0x05
46 #define SYS_READ 0x06
47 #define SYS_READC 0x07
48 #define SYS_ISTTY 0x09
49 #define SYS_SEEK 0x0a
50 #define SYS_FLEN 0x0c
51 #define SYS_TMPNAM 0x0d
52 #define SYS_REMOVE 0x0e
53 #define SYS_RENAME 0x0f
54 #define SYS_CLOCK 0x10
55 #define SYS_TIME 0x11
56 #define SYS_SYSTEM 0x12
57 #define SYS_ERRNO 0x13
58 #define SYS_GET_CMDLINE 0x15
59 #define SYS_HEAPINFO 0x16
60 #define SYS_EXIT 0x18
62 #define GDB_O_RDONLY 0x000
63 #define GDB_O_WRONLY 0x001
64 #define GDB_O_RDWR 0x002
65 #define GDB_O_APPEND 0x008
66 #define GDB_O_CREAT 0x200
67 #define GDB_O_TRUNC 0x400
68 #define GDB_O_BINARY 0
70 static int gdb_open_modeflags[12] = {
71 GDB_O_RDONLY,
72 GDB_O_RDONLY | GDB_O_BINARY,
73 GDB_O_RDWR,
74 GDB_O_RDWR | GDB_O_BINARY,
75 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
76 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
77 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
78 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
79 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
80 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
81 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
82 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
85 static int open_modeflags[12] = {
86 O_RDONLY,
87 O_RDONLY | O_BINARY,
88 O_RDWR,
89 O_RDWR | O_BINARY,
90 O_WRONLY | O_CREAT | O_TRUNC,
91 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
92 O_RDWR | O_CREAT | O_TRUNC,
93 O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
94 O_WRONLY | O_CREAT | O_APPEND,
95 O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
96 O_RDWR | O_CREAT | O_APPEND,
97 O_RDWR | O_CREAT | O_APPEND | O_BINARY
100 #ifdef CONFIG_USER_ONLY
101 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
103 if (code == (uint32_t)-1)
104 ts->swi_errno = errno;
105 return code;
107 #else
108 static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
110 return code;
113 #include "softmmu-semi.h"
114 #endif
116 static target_ulong arm_semi_syscall_len;
118 #if !defined(CONFIG_USER_ONLY)
119 static target_ulong syscall_err;
120 #endif
122 static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
124 #ifdef CONFIG_USER_ONLY
125 TaskState *ts = env->opaque;
126 #endif
128 if (ret == (target_ulong)-1) {
129 #ifdef CONFIG_USER_ONLY
130 ts->swi_errno = err;
131 #else
132 syscall_err = err;
133 #endif
134 env->regs[0] = ret;
135 } else {
136 /* Fixup syscalls that use nonstardard return conventions. */
137 switch (env->regs[0]) {
138 case SYS_WRITE:
139 case SYS_READ:
140 env->regs[0] = arm_semi_syscall_len - ret;
141 break;
142 case SYS_SEEK:
143 env->regs[0] = 0;
144 break;
145 default:
146 env->regs[0] = ret;
147 break;
152 static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
154 /* The size is always stored in big-endian order, extract
155 the value. We assume the size always fit in 32 bits. */
156 uint32_t size;
157 cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
158 env->regs[0] = be32_to_cpu(size);
159 #ifdef CONFIG_USER_ONLY
160 ((TaskState *)env->opaque)->swi_errno = err;
161 #else
162 syscall_err = err;
163 #endif
166 #define ARG(n) \
167 ({ \
168 target_ulong __arg; \
169 /* FIXME - handle get_user() failure */ \
170 get_user_ual(__arg, args + (n) * 4); \
171 __arg; \
173 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
174 uint32_t do_arm_semihosting(CPUState *env)
176 target_ulong args;
177 char * s;
178 int nr;
179 uint32_t ret;
180 uint32_t len;
181 #ifdef CONFIG_USER_ONLY
182 TaskState *ts = env->opaque;
183 #else
184 CPUState *ts = env;
185 #endif
187 nr = env->regs[0];
188 args = env->regs[1];
189 switch (nr) {
190 case SYS_OPEN:
191 if (!(s = lock_user_string(ARG(0))))
192 /* FIXME - should this error code be -TARGET_EFAULT ? */
193 return (uint32_t)-1;
194 if (ARG(1) >= 12)
195 return (uint32_t)-1;
196 if (strcmp(s, ":tt") == 0) {
197 if (ARG(1) < 4)
198 return STDIN_FILENO;
199 else
200 return STDOUT_FILENO;
202 if (use_gdb_syscalls()) {
203 gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
204 (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
205 return env->regs[0];
206 } else {
207 ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
209 unlock_user(s, ARG(0), 0);
210 return ret;
211 case SYS_CLOSE:
212 if (use_gdb_syscalls()) {
213 gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
214 return env->regs[0];
215 } else {
216 return set_swi_errno(ts, close(ARG(0)));
218 case SYS_WRITEC:
220 char c;
222 if (get_user_u8(c, args))
223 /* FIXME - should this error code be -TARGET_EFAULT ? */
224 return (uint32_t)-1;
225 /* Write to debug console. stderr is near enough. */
226 if (use_gdb_syscalls()) {
227 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
228 return env->regs[0];
229 } else {
230 return write(STDERR_FILENO, &c, 1);
233 case SYS_WRITE0:
234 if (!(s = lock_user_string(args)))
235 /* FIXME - should this error code be -TARGET_EFAULT ? */
236 return (uint32_t)-1;
237 len = strlen(s);
238 if (use_gdb_syscalls()) {
239 gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
240 ret = env->regs[0];
241 } else {
242 ret = write(STDERR_FILENO, s, len);
244 unlock_user(s, args, 0);
245 return ret;
246 case SYS_WRITE:
247 len = ARG(2);
248 if (use_gdb_syscalls()) {
249 arm_semi_syscall_len = len;
250 gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
251 return env->regs[0];
252 } else {
253 if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
254 /* FIXME - should this error code be -TARGET_EFAULT ? */
255 return (uint32_t)-1;
256 ret = set_swi_errno(ts, write(ARG(0), s, len));
257 unlock_user(s, ARG(1), 0);
258 if (ret == (uint32_t)-1)
259 return -1;
260 return len - ret;
262 case SYS_READ:
263 len = ARG(2);
264 if (use_gdb_syscalls()) {
265 arm_semi_syscall_len = len;
266 gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
267 return env->regs[0];
268 } else {
269 if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
270 /* FIXME - should this error code be -TARGET_EFAULT ? */
271 return (uint32_t)-1;
273 ret = set_swi_errno(ts, read(ARG(0), s, len));
274 while (ret == -1 && errno == EINTR);
275 unlock_user(s, ARG(1), len);
276 if (ret == (uint32_t)-1)
277 return -1;
278 return len - ret;
280 case SYS_READC:
281 /* XXX: Read from debug cosole. Not implemented. */
282 return 0;
283 case SYS_ISTTY:
284 if (use_gdb_syscalls()) {
285 gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
286 return env->regs[0];
287 } else {
288 return isatty(ARG(0));
290 case SYS_SEEK:
291 if (use_gdb_syscalls()) {
292 gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
293 return env->regs[0];
294 } else {
295 ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
296 if (ret == (uint32_t)-1)
297 return -1;
298 return 0;
300 case SYS_FLEN:
301 if (use_gdb_syscalls()) {
302 gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
303 ARG(0), env->regs[13]-64);
304 return env->regs[0];
305 } else {
306 struct stat buf;
307 ret = set_swi_errno(ts, fstat(ARG(0), &buf));
308 if (ret == (uint32_t)-1)
309 return -1;
310 return buf.st_size;
312 case SYS_TMPNAM:
313 /* XXX: Not implemented. */
314 return -1;
315 case SYS_REMOVE:
316 if (use_gdb_syscalls()) {
317 gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
318 ret = env->regs[0];
319 } else {
320 if (!(s = lock_user_string(ARG(0))))
321 /* FIXME - should this error code be -TARGET_EFAULT ? */
322 return (uint32_t)-1;
323 ret = set_swi_errno(ts, remove(s));
324 unlock_user(s, ARG(0), 0);
326 return ret;
327 case SYS_RENAME:
328 if (use_gdb_syscalls()) {
329 gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
330 ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
331 return env->regs[0];
332 } else {
333 char *s2;
334 s = lock_user_string(ARG(0));
335 s2 = lock_user_string(ARG(2));
336 if (!s || !s2)
337 /* FIXME - should this error code be -TARGET_EFAULT ? */
338 ret = (uint32_t)-1;
339 else
340 ret = set_swi_errno(ts, rename(s, s2));
341 if (s2)
342 unlock_user(s2, ARG(2), 0);
343 if (s)
344 unlock_user(s, ARG(0), 0);
345 return ret;
347 case SYS_CLOCK:
348 return clock() / (CLOCKS_PER_SEC / 100);
349 case SYS_TIME:
350 return set_swi_errno(ts, time(NULL));
351 case SYS_SYSTEM:
352 if (use_gdb_syscalls()) {
353 gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
354 return env->regs[0];
355 } else {
356 if (!(s = lock_user_string(ARG(0))))
357 /* FIXME - should this error code be -TARGET_EFAULT ? */
358 return (uint32_t)-1;
359 ret = set_swi_errno(ts, system(s));
360 unlock_user(s, ARG(0), 0);
361 return ret;
363 case SYS_ERRNO:
364 #ifdef CONFIG_USER_ONLY
365 return ts->swi_errno;
366 #else
367 return syscall_err;
368 #endif
369 case SYS_GET_CMDLINE:
370 #ifdef CONFIG_USER_ONLY
371 /* Build a commandline from the original argv. */
373 char **arg = ts->info->host_argv;
374 int len = ARG(1);
375 /* lock the buffer on the ARM side */
376 char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
378 if (!cmdline_buffer)
379 /* FIXME - should this error code be -TARGET_EFAULT ? */
380 return (uint32_t)-1;
382 s = cmdline_buffer;
383 while (*arg && len > 2) {
384 int n = strlen(*arg);
386 if (s != cmdline_buffer) {
387 *(s++) = ' ';
388 len--;
390 if (n >= len)
391 n = len - 1;
392 memcpy(s, *arg, n);
393 s += n;
394 len -= n;
395 arg++;
397 /* Null terminate the string. */
398 *s = 0;
399 len = s - cmdline_buffer;
401 /* Unlock the buffer on the ARM side. */
402 unlock_user(cmdline_buffer, ARG(0), len);
404 /* Adjust the commandline length argument. */
405 SET_ARG(1, len);
407 /* Return success if commandline fit into buffer. */
408 return *arg ? -1 : 0;
410 #else
411 return -1;
412 #endif
413 case SYS_HEAPINFO:
415 uint32_t *ptr;
416 uint32_t limit;
418 #ifdef CONFIG_USER_ONLY
419 /* Some C libraries assume the heap immediately follows .bss, so
420 allocate it using sbrk. */
421 if (!ts->heap_limit) {
422 long ret;
424 ts->heap_base = do_brk(0);
425 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
426 /* Try a big heap, and reduce the size if that fails. */
427 for (;;) {
428 ret = do_brk(limit);
429 if (ret != -1)
430 break;
431 limit = (ts->heap_base >> 1) + (limit >> 1);
433 ts->heap_limit = limit;
436 if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
437 /* FIXME - should this error code be -TARGET_EFAULT ? */
438 return (uint32_t)-1;
439 ptr[0] = tswap32(ts->heap_base);
440 ptr[1] = tswap32(ts->heap_limit);
441 ptr[2] = tswap32(ts->stack_base);
442 ptr[3] = tswap32(0); /* Stack limit. */
443 unlock_user(ptr, ARG(0), 16);
444 #else
445 limit = ram_size;
446 if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
447 /* FIXME - should this error code be -TARGET_EFAULT ? */
448 return (uint32_t)-1;
449 /* TODO: Make this use the limit of the loaded application. */
450 ptr[0] = tswap32(limit / 2);
451 ptr[1] = tswap32(limit);
452 ptr[2] = tswap32(limit); /* Stack base */
453 ptr[3] = tswap32(0); /* Stack limit. */
454 unlock_user(ptr, ARG(0), 16);
455 #endif
456 return 0;
458 case SYS_EXIT:
459 exit(0);
460 default:
461 fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
462 cpu_dump_state(env, stderr, fprintf, 0);
463 abort();