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