Commit a4f81979e8c82ea8fb9f3c96692b9c58c8e88e5a
1 parent
89344d5a
ARM "Angel" semihosting syscalls (Paul Brook)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1383 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
213 additions
and
2 deletions
Makefile.target
| @@ -233,7 +233,7 @@ endif | @@ -233,7 +233,7 @@ endif | ||
| 233 | ifeq ($(TARGET_ARCH), arm) | 233 | ifeq ($(TARGET_ARCH), arm) |
| 234 | OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \ | 234 | OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \ |
| 235 | nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \ | 235 | nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \ |
| 236 | - nwfpe/double_cpdo.o nwfpe/extended_cpdo.o | 236 | + nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o |
| 237 | endif | 237 | endif |
| 238 | SRCS:= $(OBJS:.o=.c) | 238 | SRCS:= $(OBJS:.o=.c) |
| 239 | OBJS+= libqemu.a | 239 | OBJS+= libqemu.a |
linux-user/arm-semi.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Arm "Angel" semihosting syscalls | ||
| 3 | + * | ||
| 4 | + * Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook. | ||
| 5 | + * | ||
| 6 | + * This program is free software; you can redistribute it and/or modify | ||
| 7 | + * it under the terms of the GNU General Public License as published by | ||
| 8 | + * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | + * (at your option) any later version. | ||
| 10 | + * | ||
| 11 | + * This program 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 | ||
| 14 | + * GNU General Public License for more details. | ||
| 15 | + * | ||
| 16 | + * You should have received a copy of the GNU General Public License | ||
| 17 | + * along with this program; if not, write to the Free Software | ||
| 18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | + */ | ||
| 20 | + | ||
| 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> | ||
| 28 | + | ||
| 29 | +#include "qemu.h" | ||
| 30 | + | ||
| 31 | +#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) | ||
| 32 | + | ||
| 33 | +#define SYS_OPEN 0x01 | ||
| 34 | +#define SYS_CLOSE 0x02 | ||
| 35 | +#define SYS_WRITEC 0x03 | ||
| 36 | +#define SYS_WRITE0 0x04 | ||
| 37 | +#define SYS_WRITE 0x05 | ||
| 38 | +#define SYS_READ 0x06 | ||
| 39 | +#define SYS_READC 0x07 | ||
| 40 | +#define SYS_ISTTY 0x09 | ||
| 41 | +#define SYS_SEEK 0x0a | ||
| 42 | +#define SYS_FLEN 0x0c | ||
| 43 | +#define SYS_TMPNAM 0x0d | ||
| 44 | +#define SYS_REMOVE 0x0e | ||
| 45 | +#define SYS_RENAME 0x0f | ||
| 46 | +#define SYS_CLOCK 0x10 | ||
| 47 | +#define SYS_TIME 0x11 | ||
| 48 | +#define SYS_SYSTEM 0x12 | ||
| 49 | +#define SYS_ERRNO 0x13 | ||
| 50 | +#define SYS_GET_CMDLINE 0x15 | ||
| 51 | +#define SYS_HEAPINFO 0x16 | ||
| 52 | +#define SYS_EXIT 0x18 | ||
| 53 | + | ||
| 54 | +#ifndef O_BINARY | ||
| 55 | +#define O_BINARY 0 | ||
| 56 | +#endif | ||
| 57 | + | ||
| 58 | +int open_modeflags[12] = { | ||
| 59 | + O_RDONLY, | ||
| 60 | + O_RDONLY | O_BINARY, | ||
| 61 | + O_RDWR, | ||
| 62 | + O_RDWR | O_BINARY, | ||
| 63 | + O_WRONLY | O_CREAT | O_TRUNC, | ||
| 64 | + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | ||
| 65 | + O_RDWR | O_CREAT | O_TRUNC, | ||
| 66 | + O_RDWR | O_CREAT | O_TRUNC | O_BINARY, | ||
| 67 | + O_WRONLY | O_CREAT | O_APPEND, | ||
| 68 | + O_WRONLY | O_CREAT | O_APPEND | O_BINARY, | ||
| 69 | + O_RDWR | O_CREAT | O_APPEND, | ||
| 70 | + O_RDWR | O_CREAT | O_APPEND | O_BINARY | ||
| 71 | +}; | ||
| 72 | + | ||
| 73 | +static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) | ||
| 74 | +{ | ||
| 75 | + if (code == (uint32_t)-1) | ||
| 76 | + ts->swi_errno = errno; | ||
| 77 | + return code; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +#define ARG(x) tswap32(args[x]) | ||
| 81 | +uint32_t do_arm_semihosting(CPUState *env) | ||
| 82 | +{ | ||
| 83 | + uint32_t *args; | ||
| 84 | + char * s; | ||
| 85 | + int nr; | ||
| 86 | + uint32_t ret; | ||
| 87 | + TaskState *ts = env->opaque; | ||
| 88 | + | ||
| 89 | + nr = env->regs[0]; | ||
| 90 | + args = (uint32_t *)env->regs[1]; | ||
| 91 | + switch (nr) { | ||
| 92 | + case SYS_OPEN: | ||
| 93 | + s = (char *)ARG(0); | ||
| 94 | + if (ARG(1) >= 12) | ||
| 95 | + return (uint32_t)-1; | ||
| 96 | + if (strcmp(s, ":tt") == 0) { | ||
| 97 | + if (ARG(1) < 4) | ||
| 98 | + return STDIN_FILENO; | ||
| 99 | + else | ||
| 100 | + return STDOUT_FILENO; | ||
| 101 | + } | ||
| 102 | + return set_swi_errno(ts, open(s, open_modeflags[ARG(1)])); | ||
| 103 | + case SYS_CLOSE: | ||
| 104 | + return set_swi_errno(ts, close(ARG(0))); | ||
| 105 | + case SYS_WRITEC: | ||
| 106 | + /* Write to debug console. stderr is near enough. */ | ||
| 107 | + return write(STDERR_FILENO, args, 1); | ||
| 108 | + case SYS_WRITE0: | ||
| 109 | + s = (char *)args; | ||
| 110 | + return write(STDERR_FILENO, s, strlen(s)); | ||
| 111 | + case SYS_WRITE: | ||
| 112 | + ret = set_swi_errno(ts, write(ARG(0), (void *)ARG(1), ARG(2))); | ||
| 113 | + if (ret == (uint32_t)-1) | ||
| 114 | + return -1; | ||
| 115 | + return ARG(2) - ret; | ||
| 116 | + case SYS_READ: | ||
| 117 | + ret = set_swi_errno(ts, read(ARG(0), (void *)ARG(1), ARG(2))); | ||
| 118 | + if (ret == (uint32_t)-1) | ||
| 119 | + return -1; | ||
| 120 | + return ARG(2) - ret; | ||
| 121 | + case SYS_READC: | ||
| 122 | + /* XXX: Read from debug cosole. Not implemented. */ | ||
| 123 | + return 0; | ||
| 124 | + case SYS_ISTTY: | ||
| 125 | + return isatty(ARG(0)); | ||
| 126 | + case SYS_SEEK: | ||
| 127 | + return set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); | ||
| 128 | + case SYS_FLEN: | ||
| 129 | + { | ||
| 130 | + struct stat buf; | ||
| 131 | + ret = set_swi_errno(ts, fstat(ARG(0), &buf)); | ||
| 132 | + if (ret == (uint32_t)-1) | ||
| 133 | + return -1; | ||
| 134 | + return buf.st_size; | ||
| 135 | + } | ||
| 136 | + case SYS_TMPNAM: | ||
| 137 | + /* XXX: Not implemented. */ | ||
| 138 | + return -1; | ||
| 139 | + case SYS_REMOVE: | ||
| 140 | + return set_swi_errno(ts, remove((char *)ARG(0))); | ||
| 141 | + case SYS_RENAME: | ||
| 142 | + return set_swi_errno(ts, rename((char *)ARG(0), (char *)ARG(2))); | ||
| 143 | + case SYS_CLOCK: | ||
| 144 | + return clock() / (CLOCKS_PER_SEC / 100); | ||
| 145 | + case SYS_TIME: | ||
| 146 | + return set_swi_errno(ts, time(NULL)); | ||
| 147 | + case SYS_SYSTEM: | ||
| 148 | + return set_swi_errno(ts, system((char *)ARG(0))); | ||
| 149 | + case SYS_ERRNO: | ||
| 150 | + return ts->swi_errno; | ||
| 151 | + case SYS_GET_CMDLINE: | ||
| 152 | + /* XXX: Not implemented. */ | ||
| 153 | + s = (char *)ARG(0); | ||
| 154 | + *s = 0; | ||
| 155 | + return -1; | ||
| 156 | + case SYS_HEAPINFO: | ||
| 157 | + { | ||
| 158 | + uint32_t *ptr; | ||
| 159 | + uint32_t limit; | ||
| 160 | + | ||
| 161 | + /* Some C llibraries assume the heap immediately follows .bss, so | ||
| 162 | + allocate it using sbrk. */ | ||
| 163 | + if (!ts->heap_limit) { | ||
| 164 | + long ret; | ||
| 165 | + | ||
| 166 | + ts->heap_base = do_brk(NULL); | ||
| 167 | + limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE; | ||
| 168 | + /* Try a big heap, and reduce the size if that fails. */ | ||
| 169 | + for (;;) { | ||
| 170 | + ret = do_brk((char *)limit); | ||
| 171 | + if (ret != -1) | ||
| 172 | + break; | ||
| 173 | + limit = (ts->heap_base >> 1) + (limit >> 1); | ||
| 174 | + } | ||
| 175 | + ts->heap_limit = limit; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + ptr = (uint32_t *)tswap32(ARG(0)); | ||
| 179 | + ptr[0] = tswap32(ts->heap_base); | ||
| 180 | + ptr[1] = tswap32(ts->heap_limit); | ||
| 181 | + ptr[2] = tswap32(ts->stack_base); | ||
| 182 | + ptr[3] = tswap32(0); /* Stack limit. */ | ||
| 183 | + return 0; | ||
| 184 | + } | ||
| 185 | + case SYS_EXIT: | ||
| 186 | + exit(0); | ||
| 187 | + default: | ||
| 188 | + fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); | ||
| 189 | + cpu_dump_state(env, stderr, fprintf, 0); | ||
| 190 | + abort(); | ||
| 191 | + } | ||
| 192 | +} | ||
| 193 | + |
linux-user/arm/syscall.h
| @@ -29,8 +29,13 @@ struct target_pt_regs { | @@ -29,8 +29,13 @@ struct target_pt_regs { | ||
| 29 | 29 | ||
| 30 | #define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2) | 30 | #define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2) |
| 31 | 31 | ||
| 32 | +#define ARM_NR_semihosting 0x123456 | ||
| 33 | +#define ARM_NR_thumb_semihosting 0xAB | ||
| 34 | + | ||
| 32 | #if defined(TARGET_WORDS_BIGENDIAN) | 35 | #if defined(TARGET_WORDS_BIGENDIAN) |
| 33 | #define UNAME_MACHINE "armv5teb" | 36 | #define UNAME_MACHINE "armv5teb" |
| 34 | #else | 37 | #else |
| 35 | #define UNAME_MACHINE "armv5tel" | 38 | #define UNAME_MACHINE "armv5tel" |
| 36 | #endif | 39 | #endif |
| 40 | + | ||
| 41 | +uint32_t do_arm_semihosting(CPUState *); |
linux-user/main.c
| @@ -363,6 +363,9 @@ void cpu_loop(CPUARMState *env) | @@ -363,6 +363,9 @@ void cpu_loop(CPUARMState *env) | ||
| 363 | n = insn & 0xffffff; | 363 | n = insn & 0xffffff; |
| 364 | if (n == ARM_NR_cacheflush) { | 364 | if (n == ARM_NR_cacheflush) { |
| 365 | arm_cache_flush(env->regs[0], env->regs[1]); | 365 | arm_cache_flush(env->regs[0], env->regs[1]); |
| 366 | + } else if (n == ARM_NR_semihosting | ||
| 367 | + || n == ARM_NR_thumb_semihosting) { | ||
| 368 | + env->regs[0] = do_arm_semihosting (env); | ||
| 366 | } else if (n >= ARM_SYSCALL_BASE) { | 369 | } else if (n >= ARM_SYSCALL_BASE) { |
| 367 | /* linux syscall */ | 370 | /* linux syscall */ |
| 368 | n -= ARM_SYSCALL_BASE; | 371 | n -= ARM_SYSCALL_BASE; |
| @@ -1207,6 +1210,10 @@ int main(int argc, char **argv) | @@ -1207,6 +1210,10 @@ int main(int argc, char **argv) | ||
| 1207 | env->regs[i] = regs->uregs[i]; | 1210 | env->regs[i] = regs->uregs[i]; |
| 1208 | } | 1211 | } |
| 1209 | env->cpsr = regs->uregs[16]; | 1212 | env->cpsr = regs->uregs[16]; |
| 1213 | + ts->stack_base = info->start_stack; | ||
| 1214 | + ts->heap_base = info->brk; | ||
| 1215 | + /* This will be filled in on the first SYS_HEAPINFO call. */ | ||
| 1216 | + ts->heap_limit = 0; | ||
| 1210 | } | 1217 | } |
| 1211 | #elif defined(TARGET_SPARC) | 1218 | #elif defined(TARGET_SPARC) |
| 1212 | { | 1219 | { |
linux-user/qemu.h
| @@ -62,6 +62,11 @@ typedef struct TaskState { | @@ -62,6 +62,11 @@ typedef struct TaskState { | ||
| 62 | #ifdef TARGET_ARM | 62 | #ifdef TARGET_ARM |
| 63 | /* FPA state */ | 63 | /* FPA state */ |
| 64 | FPA11 fpa; | 64 | FPA11 fpa; |
| 65 | + /* Extra fields for semihosted binaries. */ | ||
| 66 | + uint32_t stack_base; | ||
| 67 | + uint32_t heap_base; | ||
| 68 | + uint32_t heap_limit; | ||
| 69 | + int swi_errno; | ||
| 65 | #endif | 70 | #endif |
| 66 | #ifdef TARGET_I386 | 71 | #ifdef TARGET_I386 |
| 67 | struct target_vm86plus_struct *target_v86; | 72 | struct target_vm86plus_struct *target_v86; |
| @@ -80,6 +85,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, | @@ -80,6 +85,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, | ||
| 80 | struct target_pt_regs * regs, struct image_info *infop); | 85 | struct target_pt_regs * regs, struct image_info *infop); |
| 81 | 86 | ||
| 82 | void target_set_brk(char *new_brk); | 87 | void target_set_brk(char *new_brk); |
| 88 | +long do_brk(char *new_brk); | ||
| 83 | void syscall_init(void); | 89 | void syscall_init(void); |
| 84 | long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, | 90 | long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
| 85 | long arg4, long arg5, long arg6); | 91 | long arg4, long arg5, long arg6); |
linux-user/syscall.c
| @@ -264,7 +264,7 @@ void target_set_brk(char *new_brk) | @@ -264,7 +264,7 @@ void target_set_brk(char *new_brk) | ||
| 264 | target_original_brk = new_brk; | 264 | target_original_brk = new_brk; |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | -static long do_brk(char *new_brk) | 267 | +long do_brk(char *new_brk) |
| 268 | { | 268 | { |
| 269 | char *brk_page; | 269 | char *brk_page; |
| 270 | long mapped_addr; | 270 | long mapped_addr; |