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,6 +397,7 @@ VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o | ||
397 | VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o | 397 | VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o |
398 | VL_OBJS+= versatile_pci.o | 398 | VL_OBJS+= versatile_pci.o |
399 | VL_OBJS+= arm_gic.o realview.o arm_sysctl.o | 399 | VL_OBJS+= arm_gic.o realview.o arm_sysctl.o |
400 | +VL_OBJS+= arm-semi.o | ||
400 | endif | 401 | endif |
401 | ifeq ($(TARGET_BASE_ARCH), sh4) | 402 | ifeq ($(TARGET_BASE_ARCH), sh4) |
402 | VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o | 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 | * Arm "Angel" semihosting syscalls | 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 | * This program is free software; you can redistribute it and/or modify | 7 | * 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 | * it under the terms of the GNU General Public License as published by |
@@ -26,9 +27,14 @@ | @@ -26,9 +27,14 @@ | ||
26 | #include <stdio.h> | 27 | #include <stdio.h> |
27 | #include <time.h> | 28 | #include <time.h> |
28 | 29 | ||
30 | +#include "cpu.h" | ||
31 | +#ifdef CONFIG_USER_ONLY | ||
29 | #include "qemu.h" | 32 | #include "qemu.h" |
30 | 33 | ||
31 | #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) | 34 | #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) |
35 | +#else | ||
36 | +#include "vl.h" | ||
37 | +#endif | ||
32 | 38 | ||
33 | #define SYS_OPEN 0x01 | 39 | #define SYS_OPEN 0x01 |
34 | #define SYS_CLOSE 0x02 | 40 | #define SYS_CLOSE 0x02 |
@@ -70,12 +76,71 @@ int open_modeflags[12] = { | @@ -70,12 +76,71 @@ int open_modeflags[12] = { | ||
70 | O_RDWR | O_CREAT | O_APPEND | O_BINARY | 76 | O_RDWR | O_CREAT | O_APPEND | O_BINARY |
71 | }; | 77 | }; |
72 | 78 | ||
79 | +#ifdef CONFIG_USER_ONLY | ||
73 | static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) | 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 | #define ARG(n) tget32(args + (n) * 4) | 145 | #define ARG(n) tget32(args + (n) * 4) |
81 | #define SET_ARG(n, val) tput32(args + (n) * 4,val) | 146 | #define SET_ARG(n, val) tput32(args + (n) * 4,val) |
@@ -85,13 +150,18 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -85,13 +150,18 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
85 | char * s; | 150 | char * s; |
86 | int nr; | 151 | int nr; |
87 | uint32_t ret; | 152 | uint32_t ret; |
153 | + uint32_t len; | ||
154 | +#ifdef CONFIG_USER_ONLY | ||
88 | TaskState *ts = env->opaque; | 155 | TaskState *ts = env->opaque; |
156 | +#else | ||
157 | + CPUState *ts = env; | ||
158 | +#endif | ||
89 | 159 | ||
90 | nr = env->regs[0]; | 160 | nr = env->regs[0]; |
91 | args = env->regs[1]; | 161 | args = env->regs[1]; |
92 | switch (nr) { | 162 | switch (nr) { |
93 | case SYS_OPEN: | 163 | case SYS_OPEN: |
94 | - s = (char *)g2h(ARG(0)); | 164 | + s = lock_user_string(ARG(0)); |
95 | if (ARG(1) >= 12) | 165 | if (ARG(1) >= 12) |
96 | return (uint32_t)-1; | 166 | return (uint32_t)-1; |
97 | if (strcmp(s, ":tt") == 0) { | 167 | if (strcmp(s, ":tt") == 0) { |
@@ -100,7 +170,9 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -100,7 +170,9 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
100 | else | 170 | else |
101 | return STDOUT_FILENO; | 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 | case SYS_CLOSE: | 176 | case SYS_CLOSE: |
105 | return set_swi_errno(ts, close(ARG(0))); | 177 | return set_swi_errno(ts, close(ARG(0))); |
106 | case SYS_WRITEC: | 178 | case SYS_WRITEC: |
@@ -115,12 +187,20 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -115,12 +187,20 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
115 | unlock_user(s, args, 0); | 187 | unlock_user(s, args, 0); |
116 | return ret; | 188 | return ret; |
117 | case SYS_WRITE: | 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 | if (ret == (uint32_t)-1) | 194 | if (ret == (uint32_t)-1) |
120 | return -1; | 195 | return -1; |
121 | return ARG(2) - ret; | 196 | return ARG(2) - ret; |
122 | case SYS_READ: | 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 | if (ret == (uint32_t)-1) | 204 | if (ret == (uint32_t)-1) |
125 | return -1; | 205 | return -1; |
126 | return ARG(2) - ret; | 206 | return ARG(2) - ret; |
@@ -146,19 +226,36 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -146,19 +226,36 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
146 | /* XXX: Not implemented. */ | 226 | /* XXX: Not implemented. */ |
147 | return -1; | 227 | return -1; |
148 | case SYS_REMOVE: | 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 | case SYS_RENAME: | 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 | case SYS_CLOCK: | 243 | case SYS_CLOCK: |
154 | return clock() / (CLOCKS_PER_SEC / 100); | 244 | return clock() / (CLOCKS_PER_SEC / 100); |
155 | case SYS_TIME: | 245 | case SYS_TIME: |
156 | return set_swi_errno(ts, time(NULL)); | 246 | return set_swi_errno(ts, time(NULL)); |
157 | case SYS_SYSTEM: | 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 | case SYS_ERRNO: | 251 | case SYS_ERRNO: |
252 | +#ifdef CONFIG_USER_ONLY | ||
160 | return ts->swi_errno; | 253 | return ts->swi_errno; |
254 | +#else | ||
255 | + return 0; | ||
256 | +#endif | ||
161 | case SYS_GET_CMDLINE: | 257 | case SYS_GET_CMDLINE: |
258 | +#ifdef CONFIG_USER_ONLY | ||
162 | /* Build a commandline from the original argv. */ | 259 | /* Build a commandline from the original argv. */ |
163 | { | 260 | { |
164 | char **arg = ts->info->host_argv; | 261 | char **arg = ts->info->host_argv; |
@@ -194,12 +291,16 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -194,12 +291,16 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
194 | /* Return success if commandline fit into buffer. */ | 291 | /* Return success if commandline fit into buffer. */ |
195 | return *arg ? -1 : 0; | 292 | return *arg ? -1 : 0; |
196 | } | 293 | } |
294 | +#else | ||
295 | + return -1; | ||
296 | +#endif | ||
197 | case SYS_HEAPINFO: | 297 | case SYS_HEAPINFO: |
198 | { | 298 | { |
199 | uint32_t *ptr; | 299 | uint32_t *ptr; |
200 | uint32_t limit; | 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 | allocate it using sbrk. */ | 304 | allocate it using sbrk. */ |
204 | if (!ts->heap_limit) { | 305 | if (!ts->heap_limit) { |
205 | long ret; | 306 | long ret; |
@@ -216,12 +317,22 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -216,12 +317,22 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
216 | ts->heap_limit = limit; | 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 | ptr[0] = tswap32(ts->heap_base); | 321 | ptr[0] = tswap32(ts->heap_base); |
222 | ptr[1] = tswap32(ts->heap_limit); | 322 | ptr[1] = tswap32(ts->heap_limit); |
223 | ptr[2] = tswap32(ts->stack_base); | 323 | ptr[2] = tswap32(ts->stack_base); |
224 | ptr[3] = tswap32(0); /* Stack limit. */ | 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 | return 0; | 336 | return 0; |
226 | } | 337 | } |
227 | case SYS_EXIT: | 338 | case SYS_EXIT: |
@@ -232,4 +343,3 @@ uint32_t do_arm_semihosting(CPUState *env) | @@ -232,4 +343,3 @@ uint32_t do_arm_semihosting(CPUState *env) | ||
232 | abort(); | 343 | abort(); |
233 | } | 344 | } |
234 | } | 345 | } |
235 | - |
qemu-doc.texi
@@ -661,6 +661,11 @@ Exit instead of rebooting. | @@ -661,6 +661,11 @@ Exit instead of rebooting. | ||
661 | 661 | ||
662 | @item -loadvm file | 662 | @item -loadvm file |
663 | Start right away with a saved state (@code{loadvm} in monitor) | 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 | @end table | 669 | @end table |
665 | 670 | ||
666 | @c man end | 671 | @c man end |
target-arm/helper.c
@@ -105,6 +105,8 @@ void switch_mode(CPUState *env, int mode) | @@ -105,6 +105,8 @@ void switch_mode(CPUState *env, int mode) | ||
105 | 105 | ||
106 | #else | 106 | #else |
107 | 107 | ||
108 | +extern int semihosting_enabled; | ||
109 | + | ||
108 | /* Map CPU modes onto saved register banks. */ | 110 | /* Map CPU modes onto saved register banks. */ |
109 | static inline int bank_number (int mode) | 111 | static inline int bank_number (int mode) |
110 | { | 112 | { |
@@ -175,6 +177,22 @@ void do_interrupt(CPUARMState *env) | @@ -175,6 +177,22 @@ void do_interrupt(CPUARMState *env) | ||
175 | offset = 4; | 177 | offset = 4; |
176 | break; | 178 | break; |
177 | case EXCP_SWI: | 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 | new_mode = ARM_CPU_MODE_SVC; | 196 | new_mode = ARM_CPU_MODE_SVC; |
179 | addr = 0x08; | 197 | addr = 0x08; |
180 | mask = CPSR_I; | 198 | mask = CPSR_I; |
vl.c
@@ -171,6 +171,7 @@ int no_reboot = 0; | @@ -171,6 +171,7 @@ int no_reboot = 0; | ||
171 | int daemonize = 0; | 171 | int daemonize = 0; |
172 | const char *option_rom[MAX_OPTION_ROMS]; | 172 | const char *option_rom[MAX_OPTION_ROMS]; |
173 | int nb_option_roms; | 173 | int nb_option_roms; |
174 | +int semihosting_enabled = 0; | ||
174 | 175 | ||
175 | /***********************************************************/ | 176 | /***********************************************************/ |
176 | /* x86 ISA bus support */ | 177 | /* x86 ISA bus support */ |
@@ -6227,6 +6228,7 @@ enum { | @@ -6227,6 +6228,7 @@ enum { | ||
6227 | QEMU_OPTION_no_reboot, | 6228 | QEMU_OPTION_no_reboot, |
6228 | QEMU_OPTION_daemonize, | 6229 | QEMU_OPTION_daemonize, |
6229 | QEMU_OPTION_option_rom, | 6230 | QEMU_OPTION_option_rom, |
6231 | + QEMU_OPTION_semihosting | ||
6230 | }; | 6232 | }; |
6231 | 6233 | ||
6232 | typedef struct QEMUOption { | 6234 | typedef struct QEMUOption { |
@@ -6309,6 +6311,9 @@ const QEMUOption qemu_options[] = { | @@ -6309,6 +6311,9 @@ const QEMUOption qemu_options[] = { | ||
6309 | { "no-reboot", 0, QEMU_OPTION_no_reboot }, | 6311 | { "no-reboot", 0, QEMU_OPTION_no_reboot }, |
6310 | { "daemonize", 0, QEMU_OPTION_daemonize }, | 6312 | { "daemonize", 0, QEMU_OPTION_daemonize }, |
6311 | { "option-rom", HAS_ARG, QEMU_OPTION_option_rom }, | 6313 | { "option-rom", HAS_ARG, QEMU_OPTION_option_rom }, |
6314 | +#if defined(TARGET_ARM) | ||
6315 | + { "semihosting", 0, QEMU_OPTION_semihosting }, | ||
6316 | +#endif | ||
6312 | { NULL }, | 6317 | { NULL }, |
6313 | }; | 6318 | }; |
6314 | 6319 | ||
@@ -6970,6 +6975,9 @@ int main(int argc, char **argv) | @@ -6970,6 +6975,9 @@ int main(int argc, char **argv) | ||
6970 | option_rom[nb_option_roms] = optarg; | 6975 | option_rom[nb_option_roms] = optarg; |
6971 | nb_option_roms++; | 6976 | nb_option_roms++; |
6972 | break; | 6977 | break; |
6978 | + case QEMU_OPTION_semihosting: | ||
6979 | + semihosting_enabled = 1; | ||
6980 | + break; | ||
6973 | } | 6981 | } |
6974 | } | 6982 | } |
6975 | } | 6983 | } |
vl.h
@@ -159,6 +159,7 @@ extern int win2k_install_hack; | @@ -159,6 +159,7 @@ extern int win2k_install_hack; | ||
159 | extern int usb_enabled; | 159 | extern int usb_enabled; |
160 | extern int smp_cpus; | 160 | extern int smp_cpus; |
161 | extern int no_quit; | 161 | extern int no_quit; |
162 | +extern int semihosting_enabled; | ||
162 | 163 | ||
163 | #define MAX_OPTION_ROMS 16 | 164 | #define MAX_OPTION_ROMS 16 |
164 | extern const char *option_rom[MAX_OPTION_ROMS]; | 165 | extern const char *option_rom[MAX_OPTION_ROMS]; |