target/arm/arm-semi: Make semihosting code hand out its own file descriptors
[qemu/ar7.git] / target / arm / arm-semi.c
bloba4741d7e11b6bf38cf20ad1e8829669145dea1a8
1 /*
2 * Arm "Angel" semihosting syscalls
4 * Copyright (c) 2005, 2007 CodeSourcery.
5 * Copyright (c) 2019 Linaro
6 * Written by Paul Brook.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 * ARM Semihosting is documented in:
22 * Semihosting for AArch32 and AArch64 Release 2.0
23 * https://static.docs.arm.com/100863/0200/semihosting.pdf
26 #include "qemu/osdep.h"
28 #include "cpu.h"
29 #include "hw/semihosting/semihost.h"
30 #include "hw/semihosting/console.h"
31 #include "qemu/log.h"
32 #ifdef CONFIG_USER_ONLY
33 #include "qemu.h"
35 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
36 #else
37 #include "exec/gdbstub.h"
38 #include "qemu/cutils.h"
39 #endif
41 #define TARGET_SYS_OPEN 0x01
42 #define TARGET_SYS_CLOSE 0x02
43 #define TARGET_SYS_WRITEC 0x03
44 #define TARGET_SYS_WRITE0 0x04
45 #define TARGET_SYS_WRITE 0x05
46 #define TARGET_SYS_READ 0x06
47 #define TARGET_SYS_READC 0x07
48 #define TARGET_SYS_ISTTY 0x09
49 #define TARGET_SYS_SEEK 0x0a
50 #define TARGET_SYS_FLEN 0x0c
51 #define TARGET_SYS_TMPNAM 0x0d
52 #define TARGET_SYS_REMOVE 0x0e
53 #define TARGET_SYS_RENAME 0x0f
54 #define TARGET_SYS_CLOCK 0x10
55 #define TARGET_SYS_TIME 0x11
56 #define TARGET_SYS_SYSTEM 0x12
57 #define TARGET_SYS_ERRNO 0x13
58 #define TARGET_SYS_GET_CMDLINE 0x15
59 #define TARGET_SYS_HEAPINFO 0x16
60 #define TARGET_SYS_EXIT 0x18
61 #define TARGET_SYS_SYNCCACHE 0x19
63 /* ADP_Stopped_ApplicationExit is used for exit(0),
64 * anything else is implemented as exit(1) */
65 #define ADP_Stopped_ApplicationExit (0x20026)
67 #ifndef O_BINARY
68 #define O_BINARY 0
69 #endif
71 #define GDB_O_RDONLY 0x000
72 #define GDB_O_WRONLY 0x001
73 #define GDB_O_RDWR 0x002
74 #define GDB_O_APPEND 0x008
75 #define GDB_O_CREAT 0x200
76 #define GDB_O_TRUNC 0x400
77 #define GDB_O_BINARY 0
79 static int gdb_open_modeflags[12] = {
80 GDB_O_RDONLY,
81 GDB_O_RDONLY | GDB_O_BINARY,
82 GDB_O_RDWR,
83 GDB_O_RDWR | GDB_O_BINARY,
84 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
85 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
86 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
87 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
88 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
89 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
90 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
91 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
94 static int open_modeflags[12] = {
95 O_RDONLY,
96 O_RDONLY | O_BINARY,
97 O_RDWR,
98 O_RDWR | O_BINARY,
99 O_WRONLY | O_CREAT | O_TRUNC,
100 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
101 O_RDWR | O_CREAT | O_TRUNC,
102 O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
103 O_WRONLY | O_CREAT | O_APPEND,
104 O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
105 O_RDWR | O_CREAT | O_APPEND,
106 O_RDWR | O_CREAT | O_APPEND | O_BINARY
109 typedef enum GuestFDType {
110 GuestFDUnused = 0,
111 GuestFDHost = 1,
112 } GuestFDType;
115 * Guest file descriptors are integer indexes into an array of
116 * these structures (we will dynamically resize as necessary).
118 typedef struct GuestFD {
119 GuestFDType type;
120 int hostfd;
121 } GuestFD;
123 static GArray *guestfd_array;
126 * Allocate a new guest file descriptor and return it; if we
127 * couldn't allocate a new fd then return -1.
128 * This is a fairly simplistic implementation because we don't
129 * expect that most semihosting guest programs will make very
130 * heavy use of opening and closing fds.
132 static int alloc_guestfd(void)
134 guint i;
136 if (!guestfd_array) {
137 /* New entries zero-initialized, i.e. type GuestFDUnused */
138 guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
141 for (i = 0; i < guestfd_array->len; i++) {
142 GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
144 if (gf->type == GuestFDUnused) {
145 return i;
149 /* All elements already in use: expand the array */
150 g_array_set_size(guestfd_array, i + 1);
151 return i;
155 * Look up the guestfd in the data structure; return NULL
156 * for out of bounds, but don't check whether the slot is unused.
157 * This is used internally by the other guestfd functions.
159 static GuestFD *do_get_guestfd(int guestfd)
161 if (!guestfd_array) {
162 return NULL;
165 if (guestfd < 0 || guestfd >= guestfd_array->len) {
166 return NULL;
169 return &g_array_index(guestfd_array, GuestFD, guestfd);
173 * Associate the specified guest fd (which must have been
174 * allocated via alloc_fd() and not previously used) with
175 * the specified host fd.
177 static void associate_guestfd(int guestfd, int hostfd)
179 GuestFD *gf = do_get_guestfd(guestfd);
181 assert(gf);
182 gf->type = GuestFDHost;
183 gf->hostfd = hostfd;
187 * Deallocate the specified guest file descriptor. This doesn't
188 * close the host fd, it merely undoes the work of alloc_fd().
190 static void dealloc_guestfd(int guestfd)
192 GuestFD *gf = do_get_guestfd(guestfd);
194 assert(gf);
195 gf->type = GuestFDUnused;
199 * Given a guest file descriptor, get the associated struct.
200 * If the fd is not valid, return NULL. This is the function
201 * used by the various semihosting calls to validate a handle
202 * from the guest.
203 * Note: calling alloc_guestfd() or dealloc_guestfd() will
204 * invalidate any GuestFD* obtained by calling this function.
206 static GuestFD *get_guestfd(int guestfd)
208 GuestFD *gf = do_get_guestfd(guestfd);
210 if (!gf || gf->type == GuestFDUnused) {
211 return NULL;
213 return gf;
216 #ifdef CONFIG_USER_ONLY
217 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
219 if (code == (uint32_t)-1)
220 ts->swi_errno = errno;
221 return code;
223 #else
224 static target_ulong syscall_err;
226 static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
228 if (code == (uint32_t)-1) {
229 syscall_err = errno;
231 return code;
234 #include "exec/softmmu-semi.h"
235 #endif
237 static target_ulong arm_semi_syscall_len;
239 static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
241 ARMCPU *cpu = ARM_CPU(cs);
242 CPUARMState *env = &cpu->env;
243 #ifdef CONFIG_USER_ONLY
244 TaskState *ts = cs->opaque;
245 #endif
246 target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0];
248 if (ret == (target_ulong)-1) {
249 #ifdef CONFIG_USER_ONLY
250 ts->swi_errno = err;
251 #else
252 syscall_err = err;
253 #endif
254 reg0 = ret;
255 } else {
256 /* Fixup syscalls that use nonstardard return conventions. */
257 switch (reg0) {
258 case TARGET_SYS_WRITE:
259 case TARGET_SYS_READ:
260 reg0 = arm_semi_syscall_len - ret;
261 break;
262 case TARGET_SYS_SEEK:
263 reg0 = 0;
264 break;
265 default:
266 reg0 = ret;
267 break;
270 if (is_a64(env)) {
271 env->xregs[0] = reg0;
272 } else {
273 env->regs[0] = reg0;
277 static target_ulong arm_flen_buf(ARMCPU *cpu)
279 /* Return an address in target memory of 64 bytes where the remote
280 * gdb should write its stat struct. (The format of this structure
281 * is defined by GDB's remote protocol and is not target-specific.)
282 * We put this on the guest's stack just below SP.
284 CPUARMState *env = &cpu->env;
285 target_ulong sp;
287 if (is_a64(env)) {
288 sp = env->xregs[31];
289 } else {
290 sp = env->regs[13];
293 return sp - 64;
296 static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
298 ARMCPU *cpu = ARM_CPU(cs);
299 CPUARMState *env = &cpu->env;
300 /* The size is always stored in big-endian order, extract
301 the value. We assume the size always fit in 32 bits. */
302 uint32_t size;
303 cpu_memory_rw_debug(cs, arm_flen_buf(cpu) + 32, (uint8_t *)&size, 4, 0);
304 size = be32_to_cpu(size);
305 if (is_a64(env)) {
306 env->xregs[0] = size;
307 } else {
308 env->regs[0] = size;
310 #ifdef CONFIG_USER_ONLY
311 ((TaskState *)cs->opaque)->swi_errno = err;
312 #else
313 syscall_err = err;
314 #endif
317 static int arm_semi_open_guestfd;
319 static void arm_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err)
321 ARMCPU *cpu = ARM_CPU(cs);
322 CPUARMState *env = &cpu->env;
323 #ifdef CONFIG_USER_ONLY
324 TaskState *ts = cs->opaque;
325 #endif
326 if (ret == (target_ulong)-1) {
327 #ifdef CONFIG_USER_ONLY
328 ts->swi_errno = err;
329 #else
330 syscall_err = err;
331 #endif
332 dealloc_guestfd(arm_semi_open_guestfd);
333 } else {
334 associate_guestfd(arm_semi_open_guestfd, ret);
335 ret = arm_semi_open_guestfd;
338 if (is_a64(env)) {
339 env->xregs[0] = ret;
340 } else {
341 env->regs[0] = ret;
345 static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb,
346 const char *fmt, ...)
348 va_list va;
349 CPUARMState *env = &cpu->env;
351 va_start(va, fmt);
352 gdb_do_syscallv(cb, fmt, va);
353 va_end(va);
356 * FIXME: in softmmu mode, the gdbstub will schedule our callback
357 * to occur, but will not actually call it to complete the syscall
358 * until after this function has returned and we are back in the
359 * CPU main loop. Therefore callers to this function must not
360 * do anything with its return value, because it is not necessarily
361 * the result of the syscall, but could just be the old value of X0.
362 * The only thing safe to do with this is that the callers of
363 * do_arm_semihosting() will write it straight back into X0.
364 * (In linux-user mode, the callback will have happened before
365 * gdb_do_syscallv() returns.)
367 * We should tidy this up so neither this function nor
368 * do_arm_semihosting() return a value, so the mistake of
369 * doing something with the return value is not possible to make.
372 return is_a64(env) ? env->xregs[0] : env->regs[0];
375 /* Read the input value from the argument block; fail the semihosting
376 * call if the memory read fails.
378 #define GET_ARG(n) do { \
379 if (is_a64(env)) { \
380 if (get_user_u64(arg ## n, args + (n) * 8)) { \
381 errno = EFAULT; \
382 return set_swi_errno(ts, -1); \
384 } else { \
385 if (get_user_u32(arg ## n, args + (n) * 4)) { \
386 errno = EFAULT; \
387 return set_swi_errno(ts, -1); \
390 } while (0)
392 #define SET_ARG(n, val) \
393 (is_a64(env) ? \
394 put_user_u64(val, args + (n) * 8) : \
395 put_user_u32(val, args + (n) * 4))
398 * Do a semihosting call.
400 * The specification always says that the "return register" either
401 * returns a specific value or is corrupted, so we don't need to
402 * report to our caller whether we are returning a value or trying to
403 * leave the register unchanged. We use 0xdeadbeef as the return value
404 * when there isn't a defined return value for the call.
406 target_ulong do_arm_semihosting(CPUARMState *env)
408 ARMCPU *cpu = env_archcpu(env);
409 CPUState *cs = env_cpu(env);
410 target_ulong args;
411 target_ulong arg0, arg1, arg2, arg3;
412 char * s;
413 int nr;
414 uint32_t ret;
415 uint32_t len;
416 #ifdef CONFIG_USER_ONLY
417 TaskState *ts = cs->opaque;
418 #else
419 CPUARMState *ts = env;
420 #endif
421 GuestFD *gf;
423 if (is_a64(env)) {
424 /* Note that the syscall number is in W0, not X0 */
425 nr = env->xregs[0] & 0xffffffffU;
426 args = env->xregs[1];
427 } else {
428 nr = env->regs[0];
429 args = env->regs[1];
432 switch (nr) {
433 case TARGET_SYS_OPEN:
435 int guestfd;
437 GET_ARG(0);
438 GET_ARG(1);
439 GET_ARG(2);
440 s = lock_user_string(arg0);
441 if (!s) {
442 errno = EFAULT;
443 return set_swi_errno(ts, -1);
445 if (arg1 >= 12) {
446 unlock_user(s, arg0, 0);
447 errno = EINVAL;
448 return set_swi_errno(ts, -1);
451 guestfd = alloc_guestfd();
452 if (guestfd < 0) {
453 unlock_user(s, arg0, 0);
454 errno = EMFILE;
455 return set_swi_errno(ts, -1);
458 if (strcmp(s, ":tt") == 0) {
459 int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
460 associate_guestfd(guestfd, result_fileno);
461 unlock_user(s, arg0, 0);
462 return guestfd;
464 if (use_gdb_syscalls()) {
465 arm_semi_open_guestfd = guestfd;
466 ret = arm_gdb_syscall(cpu, arm_semi_open_cb, "open,%s,%x,1a4", arg0,
467 (int)arg2+1, gdb_open_modeflags[arg1]);
468 } else {
469 ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
470 if (ret == (uint32_t)-1) {
471 dealloc_guestfd(guestfd);
472 } else {
473 associate_guestfd(guestfd, ret);
474 ret = guestfd;
477 unlock_user(s, arg0, 0);
478 return ret;
480 case TARGET_SYS_CLOSE:
481 GET_ARG(0);
483 gf = get_guestfd(arg0);
484 if (!gf) {
485 errno = EBADF;
486 return set_swi_errno(ts, -1);
489 if (use_gdb_syscalls()) {
490 ret = arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", gf->hostfd);
491 } else {
492 ret = set_swi_errno(ts, close(gf->hostfd));
494 dealloc_guestfd(arg0);
495 return ret;
496 case TARGET_SYS_WRITEC:
497 qemu_semihosting_console_outc(env, args);
498 return 0xdeadbeef;
499 case TARGET_SYS_WRITE0:
500 return qemu_semihosting_console_outs(env, args);
501 case TARGET_SYS_WRITE:
502 GET_ARG(0);
503 GET_ARG(1);
504 GET_ARG(2);
505 len = arg2;
507 gf = get_guestfd(arg0);
508 if (!gf) {
509 errno = EBADF;
510 return set_swi_errno(ts, -1);
513 if (use_gdb_syscalls()) {
514 arm_semi_syscall_len = len;
515 return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x",
516 gf->hostfd, arg1, len);
517 } else {
518 s = lock_user(VERIFY_READ, arg1, len, 1);
519 if (!s) {
520 /* Return bytes not written on error */
521 return len;
523 ret = set_swi_errno(ts, write(gf->hostfd, s, len));
524 unlock_user(s, arg1, 0);
525 if (ret == (uint32_t)-1) {
526 ret = 0;
528 /* Return bytes not written */
529 return len - ret;
531 case TARGET_SYS_READ:
532 GET_ARG(0);
533 GET_ARG(1);
534 GET_ARG(2);
535 len = arg2;
537 gf = get_guestfd(arg0);
538 if (!gf) {
539 errno = EBADF;
540 return set_swi_errno(ts, -1);
543 if (use_gdb_syscalls()) {
544 arm_semi_syscall_len = len;
545 return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x",
546 gf->hostfd, arg1, len);
547 } else {
548 s = lock_user(VERIFY_WRITE, arg1, len, 0);
549 if (!s) {
550 /* return bytes not read */
551 return len;
553 do {
554 ret = set_swi_errno(ts, read(gf->hostfd, s, len));
555 } while (ret == -1 && errno == EINTR);
556 unlock_user(s, arg1, len);
557 if (ret == (uint32_t)-1) {
558 ret = 0;
560 /* Return bytes not read */
561 return len - ret;
563 case TARGET_SYS_READC:
564 qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__);
565 return 0;
566 case TARGET_SYS_ISTTY:
567 GET_ARG(0);
569 gf = get_guestfd(arg0);
570 if (!gf) {
571 errno = EBADF;
572 return set_swi_errno(ts, -1);
575 if (use_gdb_syscalls()) {
576 return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", gf->hostfd);
577 } else {
578 return isatty(gf->hostfd);
580 case TARGET_SYS_SEEK:
581 GET_ARG(0);
582 GET_ARG(1);
584 gf = get_guestfd(arg0);
585 if (!gf) {
586 errno = EBADF;
587 return set_swi_errno(ts, -1);
590 if (use_gdb_syscalls()) {
591 return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0",
592 gf->hostfd, arg1);
593 } else {
594 ret = set_swi_errno(ts, lseek(gf->hostfd, arg1, SEEK_SET));
595 if (ret == (uint32_t)-1)
596 return -1;
597 return 0;
599 case TARGET_SYS_FLEN:
600 GET_ARG(0);
602 gf = get_guestfd(arg0);
603 if (!gf) {
604 errno = EBADF;
605 return set_swi_errno(ts, -1);
608 if (use_gdb_syscalls()) {
609 return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x",
610 gf->hostfd, arm_flen_buf(cpu));
611 } else {
612 struct stat buf;
613 ret = set_swi_errno(ts, fstat(gf->hostfd, &buf));
614 if (ret == (uint32_t)-1)
615 return -1;
616 return buf.st_size;
618 case TARGET_SYS_TMPNAM:
619 qemu_log_mask(LOG_UNIMP, "%s: SYS_TMPNAM not implemented", __func__);
620 return -1;
621 case TARGET_SYS_REMOVE:
622 GET_ARG(0);
623 GET_ARG(1);
624 if (use_gdb_syscalls()) {
625 ret = arm_gdb_syscall(cpu, arm_semi_cb, "unlink,%s",
626 arg0, (int)arg1+1);
627 } else {
628 s = lock_user_string(arg0);
629 if (!s) {
630 errno = EFAULT;
631 return set_swi_errno(ts, -1);
633 ret = set_swi_errno(ts, remove(s));
634 unlock_user(s, arg0, 0);
636 return ret;
637 case TARGET_SYS_RENAME:
638 GET_ARG(0);
639 GET_ARG(1);
640 GET_ARG(2);
641 GET_ARG(3);
642 if (use_gdb_syscalls()) {
643 return arm_gdb_syscall(cpu, arm_semi_cb, "rename,%s,%s",
644 arg0, (int)arg1+1, arg2, (int)arg3+1);
645 } else {
646 char *s2;
647 s = lock_user_string(arg0);
648 s2 = lock_user_string(arg2);
649 if (!s || !s2) {
650 errno = EFAULT;
651 ret = set_swi_errno(ts, -1);
652 } else {
653 ret = set_swi_errno(ts, rename(s, s2));
655 if (s2)
656 unlock_user(s2, arg2, 0);
657 if (s)
658 unlock_user(s, arg0, 0);
659 return ret;
661 case TARGET_SYS_CLOCK:
662 return clock() / (CLOCKS_PER_SEC / 100);
663 case TARGET_SYS_TIME:
664 return set_swi_errno(ts, time(NULL));
665 case TARGET_SYS_SYSTEM:
666 GET_ARG(0);
667 GET_ARG(1);
668 if (use_gdb_syscalls()) {
669 return arm_gdb_syscall(cpu, arm_semi_cb, "system,%s",
670 arg0, (int)arg1+1);
671 } else {
672 s = lock_user_string(arg0);
673 if (!s) {
674 errno = EFAULT;
675 return set_swi_errno(ts, -1);
677 ret = set_swi_errno(ts, system(s));
678 unlock_user(s, arg0, 0);
679 return ret;
681 case TARGET_SYS_ERRNO:
682 #ifdef CONFIG_USER_ONLY
683 return ts->swi_errno;
684 #else
685 return syscall_err;
686 #endif
687 case TARGET_SYS_GET_CMDLINE:
689 /* Build a command-line from the original argv.
691 * The inputs are:
692 * * arg0, pointer to a buffer of at least the size
693 * specified in arg1.
694 * * arg1, size of the buffer pointed to by arg0 in
695 * bytes.
697 * The outputs are:
698 * * arg0, pointer to null-terminated string of the
699 * command line.
700 * * arg1, length of the string pointed to by arg0.
703 char *output_buffer;
704 size_t input_size;
705 size_t output_size;
706 int status = 0;
707 #if !defined(CONFIG_USER_ONLY)
708 const char *cmdline;
709 #endif
710 GET_ARG(0);
711 GET_ARG(1);
712 input_size = arg1;
713 /* Compute the size of the output string. */
714 #if !defined(CONFIG_USER_ONLY)
715 cmdline = semihosting_get_cmdline();
716 if (cmdline == NULL) {
717 cmdline = ""; /* Default to an empty line. */
719 output_size = strlen(cmdline) + 1; /* Count terminating 0. */
720 #else
721 unsigned int i;
723 output_size = ts->info->arg_end - ts->info->arg_start;
724 if (!output_size) {
726 * We special-case the "empty command line" case (argc==0).
727 * Just provide the terminating 0.
729 output_size = 1;
731 #endif
733 if (output_size > input_size) {
734 /* Not enough space to store command-line arguments. */
735 errno = E2BIG;
736 return set_swi_errno(ts, -1);
739 /* Adjust the command-line length. */
740 if (SET_ARG(1, output_size - 1)) {
741 /* Couldn't write back to argument block */
742 errno = EFAULT;
743 return set_swi_errno(ts, -1);
746 /* Lock the buffer on the ARM side. */
747 output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
748 if (!output_buffer) {
749 errno = EFAULT;
750 return set_swi_errno(ts, -1);
753 /* Copy the command-line arguments. */
754 #if !defined(CONFIG_USER_ONLY)
755 pstrcpy(output_buffer, output_size, cmdline);
756 #else
757 if (output_size == 1) {
758 /* Empty command-line. */
759 output_buffer[0] = '\0';
760 goto out;
763 if (copy_from_user(output_buffer, ts->info->arg_start,
764 output_size)) {
765 errno = EFAULT;
766 status = set_swi_errno(ts, -1);
767 goto out;
770 /* Separate arguments by white spaces. */
771 for (i = 0; i < output_size - 1; i++) {
772 if (output_buffer[i] == 0) {
773 output_buffer[i] = ' ';
776 out:
777 #endif
778 /* Unlock the buffer on the ARM side. */
779 unlock_user(output_buffer, arg0, output_size);
781 return status;
783 case TARGET_SYS_HEAPINFO:
785 target_ulong retvals[4];
786 target_ulong limit;
787 int i;
789 GET_ARG(0);
791 #ifdef CONFIG_USER_ONLY
793 * Some C libraries assume the heap immediately follows .bss, so
794 * allocate it using sbrk.
796 if (!ts->heap_limit) {
797 abi_ulong ret;
799 ts->heap_base = do_brk(0);
800 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
801 /* Try a big heap, and reduce the size if that fails. */
802 for (;;) {
803 ret = do_brk(limit);
804 if (ret >= limit) {
805 break;
807 limit = (ts->heap_base >> 1) + (limit >> 1);
809 ts->heap_limit = limit;
812 retvals[0] = ts->heap_base;
813 retvals[1] = ts->heap_limit;
814 retvals[2] = ts->stack_base;
815 retvals[3] = 0; /* Stack limit. */
816 #else
817 limit = ram_size;
818 /* TODO: Make this use the limit of the loaded application. */
819 retvals[0] = limit / 2;
820 retvals[1] = limit;
821 retvals[2] = limit; /* Stack base */
822 retvals[3] = 0; /* Stack limit. */
823 #endif
825 for (i = 0; i < ARRAY_SIZE(retvals); i++) {
826 bool fail;
828 if (is_a64(env)) {
829 fail = put_user_u64(retvals[i], arg0 + i * 8);
830 } else {
831 fail = put_user_u32(retvals[i], arg0 + i * 4);
834 if (fail) {
835 /* Couldn't write back to argument block */
836 errno = EFAULT;
837 return set_swi_errno(ts, -1);
840 return 0;
842 case TARGET_SYS_EXIT:
843 if (is_a64(env)) {
845 * The A64 version of this call takes a parameter block,
846 * so the application-exit type can return a subcode which
847 * is the exit status code from the application.
849 GET_ARG(0);
850 GET_ARG(1);
852 if (arg0 == ADP_Stopped_ApplicationExit) {
853 ret = arg1;
854 } else {
855 ret = 1;
857 } else {
859 * ARM specifies only Stopped_ApplicationExit as normal
860 * exit, everything else is considered an error
862 ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
864 gdb_exit(env, ret);
865 exit(ret);
866 case TARGET_SYS_SYNCCACHE:
868 * Clean the D-cache and invalidate the I-cache for the specified
869 * virtual address range. This is a nop for us since we don't
870 * implement caches. This is only present on A64.
872 if (is_a64(env)) {
873 return 0;
875 /* fall through -- invalid for A32/T32 */
876 default:
877 fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
878 cpu_dump_state(cs, stderr, 0);
879 abort();