/* * 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(n) tget32(args + (n) * 4) #define SET_ARG(n, val) tput32(args + (n) * 4,val) uint32_t do_arm_semihosting(CPUState *env) { target_ulong args; char * s; int nr; uint32_t ret; TaskState *ts = env->opaque; nr = env->regs[0]; args = env->regs[1]; switch (nr) { case SYS_OPEN: s = (char *)g2h(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)], 0644)); case SYS_CLOSE: return set_swi_errno(ts, close(ARG(0))); case SYS_WRITEC: { char c = tget8(args); /* Write to debug console. stderr is near enough. */ return write(STDERR_FILENO, &c, 1); } case SYS_WRITE0: s = lock_user_string(args); ret = write(STDERR_FILENO, s, strlen(s)); unlock_user(s, args, 0); return ret; case SYS_WRITE: ret = set_swi_errno(ts, write(ARG(0), g2h(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), g2h(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: ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); if (ret == (uint32_t)-1) return -1; return 0; 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 *)g2h(ARG(0)))); case SYS_RENAME: return set_swi_errno(ts, rename((char *)g2h(ARG(0)), (char *)g2h(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 *)g2h(ARG(0)))); case SYS_ERRNO: return ts->swi_errno; case SYS_GET_CMDLINE: /* Build a commandline from the original argv. */ { char **arg = ts->info->host_argv; int len = ARG(1); /* lock the buffer on the ARM side */ char *cmdline_buffer = (char*)lock_user(ARG(0), len, 0); s = cmdline_buffer; while (*arg && len > 2) { int n = strlen(*arg); if (s != cmdline_buffer) { *(s++) = ' '; len--; } if (n >= len) n = len - 1; memcpy(s, *arg, n); s += n; len -= n; arg++; } /* Null terminate the string. */ *s = 0; len = s - cmdline_buffer; /* Unlock the buffer on the ARM side. */ unlock_user(cmdline_buffer, ARG(0), len); /* Adjust the commandline length argument. */ SET_ARG(1, len); /* Return success if commandline fit into buffer. */ return *arg ? -1 : 0; } 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(0); limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE; /* Try a big heap, and reduce the size if that fails. */ for (;;) { ret = do_brk(limit); if (ret != -1) break; limit = (ts->heap_base >> 1) + (limit >> 1); } ts->heap_limit = limit; } page_unprotect_range (ARG(0), 32); ptr = (uint32_t *)g2h(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(); } }