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 | 233 | ifeq ($(TARGET_ARCH), arm) |
234 | 234 | OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \ |
235 | 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 | 237 | endif |
238 | 238 | SRCS:= $(OBJS:.o=.c) |
239 | 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 | 29 | |
30 | 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 | 35 | #if defined(TARGET_WORDS_BIGENDIAN) |
33 | 36 | #define UNAME_MACHINE "armv5teb" |
34 | 37 | #else |
35 | 38 | #define UNAME_MACHINE "armv5tel" |
36 | 39 | #endif |
40 | + | |
41 | +uint32_t do_arm_semihosting(CPUState *); | ... | ... |
linux-user/main.c
... | ... | @@ -363,6 +363,9 @@ void cpu_loop(CPUARMState *env) |
363 | 363 | n = insn & 0xffffff; |
364 | 364 | if (n == ARM_NR_cacheflush) { |
365 | 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 | 369 | } else if (n >= ARM_SYSCALL_BASE) { |
367 | 370 | /* linux syscall */ |
368 | 371 | n -= ARM_SYSCALL_BASE; |
... | ... | @@ -1207,6 +1210,10 @@ int main(int argc, char **argv) |
1207 | 1210 | env->regs[i] = regs->uregs[i]; |
1208 | 1211 | } |
1209 | 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 | 1218 | #elif defined(TARGET_SPARC) |
1212 | 1219 | { | ... | ... |
linux-user/qemu.h
... | ... | @@ -62,6 +62,11 @@ typedef struct TaskState { |
62 | 62 | #ifdef TARGET_ARM |
63 | 63 | /* FPA state */ |
64 | 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 | 70 | #endif |
66 | 71 | #ifdef TARGET_I386 |
67 | 72 | struct target_vm86plus_struct *target_v86; |
... | ... | @@ -80,6 +85,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, |
80 | 85 | struct target_pt_regs * regs, struct image_info *infop); |
81 | 86 | |
82 | 87 | void target_set_brk(char *new_brk); |
88 | +long do_brk(char *new_brk); | |
83 | 89 | void syscall_init(void); |
84 | 90 | long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
85 | 91 | long arg4, long arg5, long arg6); | ... | ... |