/* * Arm "Angel" semihosting syscalls * * Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "qemu.h" #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) #define SYS_OPEN 0x01 #define SYS_CLOSE 0x02 #define SYS_WRITEC 0x03 #define SYS_WRITE0 0x04 #define SYS_WRITE 0x05 #define SYS_READ 0x06 #define SYS_READC 0x07 #define SYS_ISTTY 0x09 #define SYS_SEEK 0x0a #define SYS_FLEN 0x0c #define SYS_TMPNAM 0x0d #define SYS_REMOVE 0x0e #define SYS_RENAME 0x0f #define SYS_CLOCK 0x10 #define SYS_TIME 0x11 #define SYS_SYSTEM 0x12 #define SYS_ERRNO 0x13 #define SYS_GET_CMDLINE 0x15 #define SYS_HEAPINFO 0x16 #define SYS_EXIT 0x18 #ifndef O_BINARY #define O_BINARY 0 #endif int open_modeflags[12] = { O_RDONLY, O_RDONLY | O_BINARY, O_RDWR, O_RDWR | O_BINARY, O_WRONLY | O_CREAT | O_TRUNC, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, O_RDWR | O_CREAT | O_TRUNC, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, O_WRONLY | O_CREAT | O_APPEND, O_WRONLY | O_CREAT | O_APPEND | O_BINARY, O_RDWR | O_CREAT | O_APPEND, O_RDWR | O_CREAT | O_APPEND | O_BINARY }; static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) { if (code == (uint32_t)-1) ts->swi_errno = errno; return code; } #define ARG(x) tswap32(args[x]) uint32_t do_arm_semihosting(CPUState *env) { uint32_t *args; char * s; int nr; uint32_t ret; TaskState *ts = env->opaque; nr = env->regs[0]; args = (uint32_t *)env->regs[1]; switch (nr) { case SYS_OPEN: s = (char *)ARG(0); if (ARG(1) >= 12) return (uint32_t)-1; if (strcmp(s, ":tt") == 0) { if (ARG(1) < 4) return STDIN_FILENO; else return STDOUT_FILENO; } return set_swi_errno(ts, open(s, open_modeflags[ARG(1)])); case SYS_CLOSE: return set_swi_errno(ts, close(ARG(0))); case SYS_WRITEC: /* Write to debug console. stderr is near enough. */ return write(STDERR_FILENO, args, 1); case SYS_WRITE0: s = (char *)args; return write(STDERR_FILENO, s, strlen(s)); case SYS_WRITE: ret = set_swi_errno(ts, write(ARG(0), (void *)ARG(1), ARG(2))); if (ret == (uint32_t)-1) return -1; return ARG(2) - ret; case SYS_READ: ret = set_swi_errno(ts, read(ARG(0), (void *)ARG(1), ARG(2))); if (ret == (uint32_t)-1) return -1; return ARG(2) - ret; case SYS_READC: /* XXX: Read from debug cosole. Not implemented. */ return 0; case SYS_ISTTY: return isatty(ARG(0)); case SYS_SEEK: return set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); case SYS_FLEN: { struct stat buf; ret = set_swi_errno(ts, fstat(ARG(0), &buf)); if (ret == (uint32_t)-1) return -1; return buf.st_size; } case SYS_TMPNAM: /* XXX: Not implemented. */ return -1; case SYS_REMOVE: return set_swi_errno(ts, remove((char *)ARG(0))); case SYS_RENAME: return set_swi_errno(ts, rename((char *)ARG(0), (char *)ARG(2))); case SYS_CLOCK: return clock() / (CLOCKS_PER_SEC / 100); case SYS_TIME: return set_swi_errno(ts, time(NULL)); case SYS_SYSTEM: return set_swi_errno(ts, system((char *)ARG(0))); case SYS_ERRNO: return ts->swi_errno; case SYS_GET_CMDLINE: /* XXX: Not implemented. */ s = (char *)ARG(0); *s = 0; return -1; case SYS_HEAPINFO: { uint32_t *ptr; uint32_t limit; /* Some C llibraries assume the heap immediately follows .bss, so allocate it using sbrk. */ if (!ts->heap_limit) { long ret; ts->heap_base = do_brk(NULL); limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE; /* Try a big heap, and reduce the size if that fails. */ for (;;) { ret = do_brk((char *)limit); if (ret != -1) break; limit = (ts->heap_base >> 1) + (limit >> 1); } ts->heap_limit = limit; } ptr = (uint32_t *)tswap32(ARG(0)); ptr[0] = tswap32(ts->heap_base); ptr[1] = tswap32(ts->heap_limit); ptr[2] = tswap32(ts->stack_base); ptr[3] = tswap32(0); /* Stack limit. */ return 0; } case SYS_EXIT: exit(0); default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(env, stderr, fprintf, 0); abort(); } }