Commit 8e71621f784b27ac06c3b6301df161e445132b88
1 parent
57be54bb
Add ARM Angel semihosting to system emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2340 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
159 additions
and
16 deletions
Makefile.target
... | ... | @@ -397,6 +397,7 @@ VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o |
397 | 397 | VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o |
398 | 398 | VL_OBJS+= versatile_pci.o |
399 | 399 | VL_OBJS+= arm_gic.o realview.o arm_sysctl.o |
400 | +VL_OBJS+= arm-semi.o | |
400 | 401 | endif |
401 | 402 | ifeq ($(TARGET_BASE_ARCH), sh4) |
402 | 403 | VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o | ... | ... |
linux-user/arm-semi.c renamed to arm-semi.c
1 | 1 | /* |
2 | 2 | * Arm "Angel" semihosting syscalls |
3 | 3 | * |
4 | - * Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook. | |
4 | + * Copyright (c) 2005, 2007 CodeSourcery. | |
5 | + * Written by Paul Brook. | |
5 | 6 | * |
6 | 7 | * This program is free software; you can redistribute it and/or modify |
7 | 8 | * it under the terms of the GNU General Public License as published by |
... | ... | @@ -26,9 +27,14 @@ |
26 | 27 | #include <stdio.h> |
27 | 28 | #include <time.h> |
28 | 29 | |
30 | +#include "cpu.h" | |
31 | +#ifdef CONFIG_USER_ONLY | |
29 | 32 | #include "qemu.h" |
30 | 33 | |
31 | 34 | #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) |
35 | +#else | |
36 | +#include "vl.h" | |
37 | +#endif | |
32 | 38 | |
33 | 39 | #define SYS_OPEN 0x01 |
34 | 40 | #define SYS_CLOSE 0x02 |
... | ... | @@ -70,12 +76,71 @@ int open_modeflags[12] = { |
70 | 76 | O_RDWR | O_CREAT | O_APPEND | O_BINARY |
71 | 77 | }; |
72 | 78 | |
79 | +#ifdef CONFIG_USER_ONLY | |
73 | 80 | static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) |
74 | 81 | { |
75 | - if (code == (uint32_t)-1) | |
76 | - ts->swi_errno = errno; | |
77 | - return code; | |
82 | + if (code == (uint32_t)-1) | |
83 | + ts->swi_errno = errno; | |
84 | + return code; | |
85 | +} | |
86 | +#else | |
87 | +static inline uint32_t set_swi_errno(CPUState *env, uint32_t code) | |
88 | +{ | |
89 | + return code; | |
90 | +} | |
91 | + | |
92 | +static uint32_t softmmu_tget32(CPUState *env, uint32_t addr) | |
93 | +{ | |
94 | + uint32_t val; | |
95 | + | |
96 | + cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0); | |
97 | + return tswap32(val); | |
78 | 98 | } |
99 | +static uint32_t softmmu_tget8(CPUState *env, uint32_t addr) | |
100 | +{ | |
101 | + uint8_t val; | |
102 | + | |
103 | + cpu_memory_rw_debug(env, addr, &val, 1, 0); | |
104 | + return val; | |
105 | +} | |
106 | +#define tget32(p) softmmu_tget32(env, p) | |
107 | +#define tget8(p) softmmu_tget8(env, p) | |
108 | + | |
109 | +static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len, | |
110 | + int copy) | |
111 | +{ | |
112 | + char *p; | |
113 | + /* TODO: Make this something that isn't fixed size. */ | |
114 | + p = malloc(len); | |
115 | + if (copy) | |
116 | + cpu_memory_rw_debug(env, addr, p, len, 0); | |
117 | + return p; | |
118 | +} | |
119 | +#define lock_user(p, len, copy) softmmu_lock_user(env, p, len, copy) | |
120 | +static char *softmmu_lock_user_string(CPUState *env, uint32_t addr) | |
121 | +{ | |
122 | + char *p; | |
123 | + char *s; | |
124 | + uint8_t c; | |
125 | + /* TODO: Make this something that isn't fixed size. */ | |
126 | + s = p = malloc(1024); | |
127 | + do { | |
128 | + cpu_memory_rw_debug(env, addr, &c, 1, 0); | |
129 | + addr++; | |
130 | + *(p++) = c; | |
131 | + } while (c); | |
132 | + return s; | |
133 | +} | |
134 | +#define lock_user_string(p) softmmu_lock_user_string(env, p) | |
135 | +static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr, | |
136 | + target_ulong len) | |
137 | +{ | |
138 | + if (len) | |
139 | + cpu_memory_rw_debug(env, addr, p, len, 1); | |
140 | + free(p); | |
141 | +} | |
142 | +#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) | |
143 | +#endif | |
79 | 144 | |
80 | 145 | #define ARG(n) tget32(args + (n) * 4) |
81 | 146 | #define SET_ARG(n, val) tput32(args + (n) * 4,val) |
... | ... | @@ -85,13 +150,18 @@ uint32_t do_arm_semihosting(CPUState *env) |
85 | 150 | char * s; |
86 | 151 | int nr; |
87 | 152 | uint32_t ret; |
153 | + uint32_t len; | |
154 | +#ifdef CONFIG_USER_ONLY | |
88 | 155 | TaskState *ts = env->opaque; |
156 | +#else | |
157 | + CPUState *ts = env; | |
158 | +#endif | |
89 | 159 | |
90 | 160 | nr = env->regs[0]; |
91 | 161 | args = env->regs[1]; |
92 | 162 | switch (nr) { |
93 | 163 | case SYS_OPEN: |
94 | - s = (char *)g2h(ARG(0)); | |
164 | + s = lock_user_string(ARG(0)); | |
95 | 165 | if (ARG(1) >= 12) |
96 | 166 | return (uint32_t)-1; |
97 | 167 | if (strcmp(s, ":tt") == 0) { |
... | ... | @@ -100,7 +170,9 @@ uint32_t do_arm_semihosting(CPUState *env) |
100 | 170 | else |
101 | 171 | return STDOUT_FILENO; |
102 | 172 | } |
103 | - return set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); | |
173 | + ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); | |
174 | + unlock_user(s, ARG(0), 0); | |
175 | + return ret; | |
104 | 176 | case SYS_CLOSE: |
105 | 177 | return set_swi_errno(ts, close(ARG(0))); |
106 | 178 | case SYS_WRITEC: |
... | ... | @@ -115,12 +187,20 @@ uint32_t do_arm_semihosting(CPUState *env) |
115 | 187 | unlock_user(s, args, 0); |
116 | 188 | return ret; |
117 | 189 | case SYS_WRITE: |
118 | - ret = set_swi_errno(ts, write(ARG(0), g2h(ARG(1)), ARG(2))); | |
190 | + len = ARG(2); | |
191 | + s = lock_user(ARG(1), len, 1); | |
192 | + ret = set_swi_errno(ts, write(ARG(0), s, len)); | |
193 | + unlock_user(s, ARG(1), 0); | |
119 | 194 | if (ret == (uint32_t)-1) |
120 | 195 | return -1; |
121 | 196 | return ARG(2) - ret; |
122 | 197 | case SYS_READ: |
123 | - ret = set_swi_errno(ts, read(ARG(0), g2h(ARG(1)), ARG(2))); | |
198 | + len = ARG(2); | |
199 | + s = lock_user(ARG(1), len, 0); | |
200 | + do | |
201 | + ret = set_swi_errno(ts, read(ARG(0), s, len)); | |
202 | + while (ret == -1 && errno == EINTR); | |
203 | + unlock_user(s, ARG(1), len); | |
124 | 204 | if (ret == (uint32_t)-1) |
125 | 205 | return -1; |
126 | 206 | return ARG(2) - ret; |
... | ... | @@ -146,19 +226,36 @@ uint32_t do_arm_semihosting(CPUState *env) |
146 | 226 | /* XXX: Not implemented. */ |
147 | 227 | return -1; |
148 | 228 | case SYS_REMOVE: |
149 | - return set_swi_errno(ts, remove((char *)g2h(ARG(0)))); | |
229 | + s = lock_user_string(ARG(0)); | |
230 | + ret = set_swi_errno(ts, remove(s)); | |
231 | + unlock_user(s, ARG(0), 0); | |
232 | + return ret; | |
150 | 233 | case SYS_RENAME: |
151 | - return set_swi_errno(ts, rename((char *)g2h(ARG(0)), | |
152 | - (char *)g2h(ARG(2)))); | |
234 | + { | |
235 | + char *s2; | |
236 | + s = lock_user_string(ARG(0)); | |
237 | + s2 = lock_user_string(ARG(2)); | |
238 | + ret = set_swi_errno(ts, rename(s, s2)); | |
239 | + unlock_user(s2, ARG(2), 0); | |
240 | + unlock_user(s, ARG(0), 0); | |
241 | + return ret; | |
242 | + } | |
153 | 243 | case SYS_CLOCK: |
154 | 244 | return clock() / (CLOCKS_PER_SEC / 100); |
155 | 245 | case SYS_TIME: |
156 | 246 | return set_swi_errno(ts, time(NULL)); |
157 | 247 | case SYS_SYSTEM: |
158 | - return set_swi_errno(ts, system((char *)g2h(ARG(0)))); | |
248 | + s = lock_user_string(ARG(0)); | |
249 | + ret = set_swi_errno(ts, system(s)); | |
250 | + unlock_user(s, ARG(0), 0); | |
159 | 251 | case SYS_ERRNO: |
252 | +#ifdef CONFIG_USER_ONLY | |
160 | 253 | return ts->swi_errno; |
254 | +#else | |
255 | + return 0; | |
256 | +#endif | |
161 | 257 | case SYS_GET_CMDLINE: |
258 | +#ifdef CONFIG_USER_ONLY | |
162 | 259 | /* Build a commandline from the original argv. */ |
163 | 260 | { |
164 | 261 | char **arg = ts->info->host_argv; |
... | ... | @@ -194,12 +291,16 @@ uint32_t do_arm_semihosting(CPUState *env) |
194 | 291 | /* Return success if commandline fit into buffer. */ |
195 | 292 | return *arg ? -1 : 0; |
196 | 293 | } |
294 | +#else | |
295 | + return -1; | |
296 | +#endif | |
197 | 297 | case SYS_HEAPINFO: |
198 | 298 | { |
199 | 299 | uint32_t *ptr; |
200 | 300 | uint32_t limit; |
201 | 301 | |
202 | - /* Some C llibraries assume the heap immediately follows .bss, so | |
302 | +#ifdef CONFIG_USER_ONLY | |
303 | + /* Some C libraries assume the heap immediately follows .bss, so | |
203 | 304 | allocate it using sbrk. */ |
204 | 305 | if (!ts->heap_limit) { |
205 | 306 | long ret; |
... | ... | @@ -216,12 +317,22 @@ uint32_t do_arm_semihosting(CPUState *env) |
216 | 317 | ts->heap_limit = limit; |
217 | 318 | } |
218 | 319 | |
219 | - page_unprotect_range (ARG(0), 32); | |
220 | - ptr = (uint32_t *)g2h(ARG(0)); | |
320 | + ptr = lock_user(ARG(0), 16, 0); | |
221 | 321 | ptr[0] = tswap32(ts->heap_base); |
222 | 322 | ptr[1] = tswap32(ts->heap_limit); |
223 | 323 | ptr[2] = tswap32(ts->stack_base); |
224 | 324 | ptr[3] = tswap32(0); /* Stack limit. */ |
325 | + unlock_user(ptr, ARG(0), 16); | |
326 | +#else | |
327 | + limit = ram_size; | |
328 | + ptr = lock_user(ARG(0), 16, 0); | |
329 | + /* TODO: Make this use the limit of the loaded application. */ | |
330 | + ptr[0] = tswap32(limit / 2); | |
331 | + ptr[1] = tswap32(limit); | |
332 | + ptr[2] = tswap32(limit); /* Stack base */ | |
333 | + ptr[3] = tswap32(0); /* Stack limit. */ | |
334 | + unlock_user(ptr, ARG(0), 16); | |
335 | +#endif | |
225 | 336 | return 0; |
226 | 337 | } |
227 | 338 | case SYS_EXIT: |
... | ... | @@ -232,4 +343,3 @@ uint32_t do_arm_semihosting(CPUState *env) |
232 | 343 | abort(); |
233 | 344 | } |
234 | 345 | } |
235 | - | ... | ... |
qemu-doc.texi
... | ... | @@ -661,6 +661,11 @@ Exit instead of rebooting. |
661 | 661 | |
662 | 662 | @item -loadvm file |
663 | 663 | Start right away with a saved state (@code{loadvm} in monitor) |
664 | + | |
665 | +@item -semihosting | |
666 | +Enable "Angel" semihosting interface (ARM target machines only). | |
667 | +Note that this allows guest direct access to the host filesystem, | |
668 | +so should only be used with trusted guest OS. | |
664 | 669 | @end table |
665 | 670 | |
666 | 671 | @c man end | ... | ... |
target-arm/helper.c
... | ... | @@ -105,6 +105,8 @@ void switch_mode(CPUState *env, int mode) |
105 | 105 | |
106 | 106 | #else |
107 | 107 | |
108 | +extern int semihosting_enabled; | |
109 | + | |
108 | 110 | /* Map CPU modes onto saved register banks. */ |
109 | 111 | static inline int bank_number (int mode) |
110 | 112 | { |
... | ... | @@ -175,6 +177,22 @@ void do_interrupt(CPUARMState *env) |
175 | 177 | offset = 4; |
176 | 178 | break; |
177 | 179 | case EXCP_SWI: |
180 | + if (semihosting_enabled) { | |
181 | + /* Check for semihosting interrupt. */ | |
182 | + if (env->thumb) { | |
183 | + mask = lduw_code(env->regs[15] - 2) & 0xff; | |
184 | + } else { | |
185 | + mask = ldl_code(env->regs[15] - 4) & 0xffffff; | |
186 | + } | |
187 | + /* Only intercept calls from privileged modes, to provide some | |
188 | + semblance of security. */ | |
189 | + if (((mask == 0x123456 && !env->thumb) | |
190 | + || (mask == 0xab && env->thumb)) | |
191 | + && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { | |
192 | + env->regs[0] = do_arm_semihosting(env); | |
193 | + return; | |
194 | + } | |
195 | + } | |
178 | 196 | new_mode = ARM_CPU_MODE_SVC; |
179 | 197 | addr = 0x08; |
180 | 198 | mask = CPSR_I; | ... | ... |
vl.c
... | ... | @@ -171,6 +171,7 @@ int no_reboot = 0; |
171 | 171 | int daemonize = 0; |
172 | 172 | const char *option_rom[MAX_OPTION_ROMS]; |
173 | 173 | int nb_option_roms; |
174 | +int semihosting_enabled = 0; | |
174 | 175 | |
175 | 176 | /***********************************************************/ |
176 | 177 | /* x86 ISA bus support */ |
... | ... | @@ -6227,6 +6228,7 @@ enum { |
6227 | 6228 | QEMU_OPTION_no_reboot, |
6228 | 6229 | QEMU_OPTION_daemonize, |
6229 | 6230 | QEMU_OPTION_option_rom, |
6231 | + QEMU_OPTION_semihosting | |
6230 | 6232 | }; |
6231 | 6233 | |
6232 | 6234 | typedef struct QEMUOption { |
... | ... | @@ -6309,6 +6311,9 @@ const QEMUOption qemu_options[] = { |
6309 | 6311 | { "no-reboot", 0, QEMU_OPTION_no_reboot }, |
6310 | 6312 | { "daemonize", 0, QEMU_OPTION_daemonize }, |
6311 | 6313 | { "option-rom", HAS_ARG, QEMU_OPTION_option_rom }, |
6314 | +#if defined(TARGET_ARM) | |
6315 | + { "semihosting", 0, QEMU_OPTION_semihosting }, | |
6316 | +#endif | |
6312 | 6317 | { NULL }, |
6313 | 6318 | }; |
6314 | 6319 | |
... | ... | @@ -6970,6 +6975,9 @@ int main(int argc, char **argv) |
6970 | 6975 | option_rom[nb_option_roms] = optarg; |
6971 | 6976 | nb_option_roms++; |
6972 | 6977 | break; |
6978 | + case QEMU_OPTION_semihosting: | |
6979 | + semihosting_enabled = 1; | |
6980 | + break; | |
6973 | 6981 | } |
6974 | 6982 | } |
6975 | 6983 | } | ... | ... |
vl.h