]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/commitdiff
Add ARM Angel semihosting to system emulation.
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 20 Jan 2007 17:12:09 +0000 (17:12 +0000)
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 20 Jan 2007 17:12:09 +0000 (17:12 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2340 c046a42c-6fe2-441c-8c8c-71466251a162

Makefile.target
arm-semi.c [moved from linux-user/arm-semi.c with 63% similarity]
qemu-doc.texi
target-arm/helper.c
vl.c
vl.h

index af4a69a6e20ddf12841604f495a4efbc66b2bca6..d8b2be2a2a5d9f3cc6d9fafc353d8e2081a61c12 100644 (file)
@@ -397,6 +397,7 @@ VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
 VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o
 VL_OBJS+= versatile_pci.o
 VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
+VL_OBJS+= arm-semi.o
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
 VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
similarity index 63%
rename from linux-user/arm-semi.c
rename to arm-semi.c
index b182eb4080f212ec0252f76659bd74cd7843afaa..4254cba84aefdd31efa7de2a20990a701722072c 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *  Arm "Angel" semihosting syscalls
  * 
- *  Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook.
+ *  Copyright (c) 2005, 2007 CodeSourcery.
+ *  Written by Paul Brook.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <stdio.h>
 #include <time.h>
 
+#include "cpu.h"
+#ifdef CONFIG_USER_ONLY
 #include "qemu.h"
 
 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
+#else
+#include "vl.h"
+#endif
 
 #define SYS_OPEN        0x01
 #define SYS_CLOSE       0x02
@@ -70,12 +76,71 @@ int open_modeflags[12] = {
     O_RDWR | O_CREAT | O_APPEND | O_BINARY
 };
 
+#ifdef CONFIG_USER_ONLY
 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
 {
-  if (code == (uint32_t)-1)
-      ts->swi_errno = errno;
-  return code;
+    if (code == (uint32_t)-1)
+        ts->swi_errno = errno;
+    return code;
+}
+#else
+static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
+{
+    return code;
+}
+
+static uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
+{
+    uint32_t val;
+
+    cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0);
+    return tswap32(val);
 }
+static uint32_t softmmu_tget8(CPUState *env, uint32_t addr)
+{
+    uint8_t val;
+
+    cpu_memory_rw_debug(env, addr, &val, 1, 0);
+    return val;
+}
+#define tget32(p) softmmu_tget32(env, p)
+#define tget8(p) softmmu_tget8(env, p)
+
+static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len,
+                               int copy)
+{
+    char *p;
+    /* TODO: Make this something that isn't fixed size.  */
+    p = malloc(len);
+    if (copy)
+        cpu_memory_rw_debug(env, addr, p, len, 0);
+    return p;
+}
+#define lock_user(p, len, copy) softmmu_lock_user(env, p, len, copy)
+static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
+{
+    char *p;
+    char *s;
+    uint8_t c;
+    /* TODO: Make this something that isn't fixed size.  */
+    s = p = malloc(1024);
+    do {
+        cpu_memory_rw_debug(env, addr, &c, 1, 0);
+        addr++;
+        *(p++) = c;
+    } while (c);
+    return s;
+}
+#define lock_user_string(p) softmmu_lock_user_string(env, p)
+static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
+                                target_ulong len)
+{
+    if (len)
+        cpu_memory_rw_debug(env, addr, p, len, 1);
+    free(p);
+}
+#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
+#endif
 
 #define ARG(n) tget32(args + (n) * 4)
 #define SET_ARG(n, val) tput32(args + (n) * 4,val)
@@ -85,13 +150,18 @@ uint32_t do_arm_semihosting(CPUState *env)
     char * s;
     int nr;
     uint32_t ret;
+    uint32_t len;
+#ifdef CONFIG_USER_ONLY
     TaskState *ts = env->opaque;
+#else
+    CPUState *ts = env;
+#endif
 
     nr = env->regs[0];
     args = env->regs[1];
     switch (nr) {
     case SYS_OPEN:
-        s = (char *)g2h(ARG(0));
+        s = lock_user_string(ARG(0));
         if (ARG(1) >= 12)
           return (uint32_t)-1;
         if (strcmp(s, ":tt") == 0) {
@@ -100,7 +170,9 @@ uint32_t do_arm_semihosting(CPUState *env)
             else
                 return STDOUT_FILENO;
         }
-        return set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+        ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+        unlock_user(s, ARG(0), 0);
+        return ret;
     case SYS_CLOSE:
         return set_swi_errno(ts, close(ARG(0)));
     case SYS_WRITEC:
@@ -115,12 +187,20 @@ uint32_t do_arm_semihosting(CPUState *env)
         unlock_user(s, args, 0);
         return ret;
     case SYS_WRITE:
-        ret = set_swi_errno(ts, write(ARG(0), g2h(ARG(1)), ARG(2)));
+        len = ARG(2);
+        s = lock_user(ARG(1), len, 1);
+        ret = set_swi_errno(ts, write(ARG(0), s, len));
+        unlock_user(s, ARG(1), 0);
         if (ret == (uint32_t)-1)
             return -1;
         return ARG(2) - ret;
     case SYS_READ:
-        ret = set_swi_errno(ts, read(ARG(0), g2h(ARG(1)), ARG(2)));
+        len = ARG(2);
+        s = lock_user(ARG(1), len, 0);
+       do
+         ret = set_swi_errno(ts, read(ARG(0), s, len));
+       while (ret == -1 && errno == EINTR);
+        unlock_user(s, ARG(1), len);
         if (ret == (uint32_t)-1)
             return -1;
         return ARG(2) - ret;
@@ -146,19 +226,36 @@ uint32_t do_arm_semihosting(CPUState *env)
         /* XXX: Not implemented.  */
         return -1;
     case SYS_REMOVE:
-        return set_swi_errno(ts, remove((char *)g2h(ARG(0))));
+        s = lock_user_string(ARG(0));
+        ret =  set_swi_errno(ts, remove(s));
+        unlock_user(s, ARG(0), 0);
+        return ret;
     case SYS_RENAME:
-        return set_swi_errno(ts, rename((char *)g2h(ARG(0)),
-                             (char *)g2h(ARG(2))));
+        {
+            char *s2;
+            s = lock_user_string(ARG(0));
+            s2 = lock_user_string(ARG(2));
+            ret = set_swi_errno(ts, rename(s, s2));
+            unlock_user(s2, ARG(2), 0);
+            unlock_user(s, ARG(0), 0);
+            return ret;
+        }
     case SYS_CLOCK:
         return clock() / (CLOCKS_PER_SEC / 100);
     case SYS_TIME:
         return set_swi_errno(ts, time(NULL));
     case SYS_SYSTEM:
-        return set_swi_errno(ts, system((char *)g2h(ARG(0))));
+        s = lock_user_string(ARG(0));
+        ret = set_swi_errno(ts, system(s));
+        unlock_user(s, ARG(0), 0);
     case SYS_ERRNO:
+#ifdef CONFIG_USER_ONLY
         return ts->swi_errno;
+#else
+        return 0;
+#endif
     case SYS_GET_CMDLINE:
+#ifdef CONFIG_USER_ONLY
         /* Build a commandline from the original argv.  */
         {
             char **arg = ts->info->host_argv;
@@ -194,12 +291,16 @@ uint32_t do_arm_semihosting(CPUState *env)
             /* Return success if commandline fit into buffer.  */
             return *arg ? -1 : 0;
         }
+#else
+      return -1;
+#endif
     case SYS_HEAPINFO:
         {
             uint32_t *ptr;
             uint32_t limit;
 
-            /* Some C llibraries assume the heap immediately follows .bss, so
+#ifdef CONFIG_USER_ONLY
+            /* Some C libraries assume the heap immediately follows .bss, so
                allocate it using sbrk.  */
             if (!ts->heap_limit) {
                 long ret;
@@ -216,12 +317,22 @@ uint32_t do_arm_semihosting(CPUState *env)
                 ts->heap_limit = limit;
             }
               
-            page_unprotect_range (ARG(0), 32);
-            ptr = (uint32_t *)g2h(ARG(0));
+            ptr = lock_user(ARG(0), 16, 0);
             ptr[0] = tswap32(ts->heap_base);
             ptr[1] = tswap32(ts->heap_limit);
             ptr[2] = tswap32(ts->stack_base);
             ptr[3] = tswap32(0); /* Stack limit.  */
+            unlock_user(ptr, ARG(0), 16);
+#else
+            limit = ram_size;
+            ptr = lock_user(ARG(0), 16, 0);
+            /* TODO: Make this use the limit of the loaded application.  */
+            ptr[0] = tswap32(limit / 2);
+            ptr[1] = tswap32(limit);
+            ptr[2] = tswap32(limit); /* Stack base */
+            ptr[3] = tswap32(0); /* Stack limit.  */
+            unlock_user(ptr, ARG(0), 16);
+#endif
             return 0;
         }
     case SYS_EXIT:
@@ -232,4 +343,3 @@ uint32_t do_arm_semihosting(CPUState *env)
         abort();
     }
 }
-
index 0423ef065455bbcb534e8b9dfb9babb01625ceb7..11cd82ceb274f8e8f441eb3b2bbb71da40c4c055 100644 (file)
@@ -661,6 +661,11 @@ Exit instead of rebooting.
 
 @item -loadvm file
 Start right away with a saved state (@code{loadvm} in monitor)
+
+@item -semihosting
+Enable "Angel" semihosting interface (ARM target machines only).
+Note that this allows guest direct access to the host filesystem,
+so should only be used with trusted guest OS.
 @end table
 
 @c man end
index 04d3b5254d1871e8c6ceaa1312c55617f7d88838..5b4cd13933bebf3b9817244d49f2e40131d5e204 100644 (file)
@@ -105,6 +105,8 @@ void switch_mode(CPUState *env, int mode)
 
 #else
 
+extern int semihosting_enabled;
+
 /* Map CPU modes onto saved register banks.  */
 static inline int bank_number (int mode)
 {
@@ -175,6 +177,22 @@ void do_interrupt(CPUARMState *env)
             offset = 4;
         break;
     case EXCP_SWI:
+        if (semihosting_enabled) {
+            /* Check for semihosting interrupt.  */
+            if (env->thumb) {
+                mask = lduw_code(env->regs[15] - 2) & 0xff;
+            } else {
+                mask = ldl_code(env->regs[15] - 4) & 0xffffff;
+            }
+            /* Only intercept calls from privileged modes, to provide some
+               semblance of security.  */
+            if (((mask == 0x123456 && !env->thumb)
+                    || (mask == 0xab && env->thumb))
+                  && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
+                env->regs[0] = do_arm_semihosting(env);
+                return;
+            }
+        }
         new_mode = ARM_CPU_MODE_SVC;
         addr = 0x08;
         mask = CPSR_I;
diff --git a/vl.c b/vl.c
index eb675078aeb91533118bc299eea0be82180e76b2..9f620cb1deed1a21fa9e31f20a38421b9169d7ee 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -171,6 +171,7 @@ int no_reboot = 0;
 int daemonize = 0;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
+int semihosting_enabled = 0;
 
 /***********************************************************/
 /* x86 ISA bus support */
@@ -6227,6 +6228,7 @@ enum {
     QEMU_OPTION_no_reboot,
     QEMU_OPTION_daemonize,
     QEMU_OPTION_option_rom,
+    QEMU_OPTION_semihosting
 };
 
 typedef struct QEMUOption {
@@ -6309,6 +6311,9 @@ const QEMUOption qemu_options[] = {
     { "no-reboot", 0, QEMU_OPTION_no_reboot },
     { "daemonize", 0, QEMU_OPTION_daemonize },
     { "option-rom", HAS_ARG, QEMU_OPTION_option_rom },
+#if defined(TARGET_ARM)
+    { "semihosting", 0, QEMU_OPTION_semihosting },
+#endif
     { NULL },
 };
 
@@ -6970,6 +6975,9 @@ int main(int argc, char **argv)
                option_rom[nb_option_roms] = optarg;
                nb_option_roms++;
                break;
+            case QEMU_OPTION_semihosting:
+                semihosting_enabled = 1;
+                break;
             }
         }
     }
diff --git a/vl.h b/vl.h
index 5cbbc4fb81067b9e90e4aa0050325fbf2efc9fce..6bc2d73f6a2eb34d3e751103f3334548e63171ef 100644 (file)
--- a/vl.h
+++ b/vl.h
@@ -159,6 +159,7 @@ extern int win2k_install_hack;
 extern int usb_enabled;
 extern int smp_cpus;
 extern int no_quit;
+extern int semihosting_enabled;
 
 #define MAX_OPTION_ROMS 16
 extern const char *option_rom[MAX_OPTION_ROMS];