qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] functional ARM semihosting under GDB


From: Liviu Ionescu
Subject: [Qemu-devel] [PATCH] functional ARM semihosting under GDB
Date: Fri, 14 Nov 2014 01:55:54 +0200

Signed-off-by: Liviu Ionescu <address@hidden>
---
 gdbstub.c               | 12 +++++++++++
 hw/arm/armv7m.c         | 27 +++++++++++++++++++-----
 hw/arm/stellaris.c      | 14 +++++--------
 include/hw/arm/arm.h    |  8 ++++---
 include/hw/boards.h     |  1 +
 include/sysemu/sysemu.h |  7 +++++++
 qemu-options.hx         | 42 ++++++++++++++++++++++++++++++++++---
 target-arm/arm-semi.c   | 54 ++++++++++++++++++++++++++++++++++++++++--------
 vl.c                    | 55 ++++++++++++++++++++++++++++++++++++++++++++++---
 9 files changed, 188 insertions(+), 32 deletions(-)

diff --git a/gdbstub.c b/gdbstub.c
index 0faca56..372aa67 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -355,6 +355,18 @@ static enum {
    remote gdb syscalls.  Otherwise use native file IO.  */
 int use_gdb_syscalls(void)
 {
+    if (semihosting_target == SEMIHOSTING_TARGET_NATIVE) {
+        if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+            gdb_syscall_mode = GDB_SYS_DISABLED;
+        }
+        return FALSE;
+    } else if (semihosting_target == SEMIHOSTING_TARGET_GDB) {
+        if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+            gdb_syscall_mode = GDB_SYS_ENABLED;
+        }
+        return TRUE;
+    }
+
     if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
         gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED
                                             : GDB_SYS_DISABLED);
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index ef24ca4..f583bdc 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -7,13 +7,17 @@
  * This code is licensed under the GPL.
  */
 
+#include "hw/boards.h"
 #include "hw/sysbus.h"
 #include "hw/arm/arm.h"
 #include "hw/loader.h"
 #include "elf.h"
+#include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
 #include "qemu/error-report.h"
 
+static struct arm_boot_info armv7m_binfo;
+
 /* Bitbanded IO.  Each word corresponds to a single bit.  */
 
 /* Get the byte address of the real memory for a bitband access.  */
@@ -166,9 +170,9 @@ static void armv7m_reset(void *opaque)
    flash_size and sram_size are in kb.
    Returns the NVIC array.  */
 
-qemu_irq *armv7m_init(MemoryRegion *system_memory,
-                      int flash_size, int sram_size,
-                      const char *kernel_filename, const char *cpu_model)
+qemu_irq *armv7m_init(MachineState *machine,
+                      MemoryRegion *system_memory,
+                      int flash_size, int sram_size)
 {
     ARMCPU *cpu;
     CPUARMState *env;
@@ -180,6 +184,11 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory,
     uint64_t lowaddr;
     int i;
     int big_endian;
+
+    const char *kernel_filename = machine->kernel_filename;
+    const char *kernel_cmdline = machine->kernel_cmdline;
+    const char *cpu_model = machine->cpu_model;
+
     MemoryRegion *sram = g_new(MemoryRegion, 1);
     MemoryRegion *flash = g_new(MemoryRegion, 1);
     MemoryRegion *hack = g_new(MemoryRegion, 1);
@@ -235,11 +244,19 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory,
     big_endian = 0;
 #endif
 
-    if (!kernel_filename && !qtest_enabled()) {
-        fprintf(stderr, "Guest image must be specified (using -kernel)\n");
+    if (!kernel_filename && !qtest_enabled() && !with_gdb) {
+        fprintf(stderr,
+                "Guest image must be specified (using -image or -kernel)\n");
         exit(1);
     }
 
+    /* Fill-in a minimalistic boot info, required for semihosting */
+    armv7m_binfo.kernel_filename = kernel_filename;
+    armv7m_binfo.kernel_cmdline = kernel_cmdline;
+    armv7m_binfo.semihosting_cmdline = machine->semihosting_cmdline;
+
+    env->boot_info = &armv7m_binfo;
+
     if (kernel_filename) {
         image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
                               NULL, big_endian, ELF_MACHINE, 1);
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 64bd4b4..0f8d975 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -1198,7 +1198,7 @@ static stellaris_board_info stellaris_boards[] = {
   }
 };
 
-static void stellaris_init(const char *kernel_filename, const char *cpu_model,
+static void stellaris_init(MachineState *machine,
                            stellaris_board_info *board)
 {
     static const int uart_irq[] = {5, 6, 33, 34};
@@ -1222,8 +1222,8 @@ static void stellaris_init(const char *kernel_filename, 
const char *cpu_model,
 
     flash_size = ((board->dc0 & 0xffff) + 1) << 1;
     sram_size = (board->dc0 >> 18) + 1;
-    pic = armv7m_init(get_system_memory(),
-                      flash_size, sram_size, kernel_filename, cpu_model);
+    pic = armv7m_init(machine, get_system_memory(),
+                      flash_size, sram_size);
 
     if (board->dc1 & (1 << 16)) {
         dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000,
@@ -1335,16 +1335,12 @@ static void stellaris_init(const char *kernel_filename, 
const char *cpu_model,
 /* FIXME: Figure out how to generate these from stellaris_boards.  */
 static void lm3s811evb_init(MachineState *machine)
 {
-    const char *cpu_model = machine->cpu_model;
-    const char *kernel_filename = machine->kernel_filename;
-    stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]);
+    stellaris_init(machine, &stellaris_boards[0]);
 }
 
 static void lm3s6965evb_init(MachineState *machine)
 {
-    const char *cpu_model = machine->cpu_model;
-    const char *kernel_filename = machine->kernel_filename;
-    stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
+    stellaris_init(machine, &stellaris_boards[1]);
 }
 
 static QEMUMachine lm3s811evb_machine = {
diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h
index cefc9e6..47b916f 100644
--- a/include/hw/arm/arm.h
+++ b/include/hw/arm/arm.h
@@ -15,9 +15,10 @@
 #include "hw/irq.h"
 
 /* armv7m.c */
-qemu_irq *armv7m_init(MemoryRegion *system_memory,
-                      int flash_size, int sram_size,
-                      const char *kernel_filename, const char *cpu_model);
+qemu_irq *armv7m_init(MachineState *machine,
+                      MemoryRegion *system_memory,
+                      int flash_size, int sram_size);
+
 
 /* arm_boot.c */
 struct arm_boot_info {
@@ -26,6 +27,7 @@ struct arm_boot_info {
     const char *kernel_cmdline;
     const char *initrd_filename;
     const char *dtb_filename;
+    const char *semihosting_cmdline;
     hwaddr loader_start;
     /* multicore boards that use the default secondary core boot functions
      * need to put the address of the secondary boot code, the boot reg,
diff --git a/include/hw/boards.h b/include/hw/boards.h
index e0a6790..a739d74 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -142,6 +142,7 @@ struct MachineState {
     char *kernel_cmdline;
     char *initrd_filename;
     const char *cpu_model;
+    char *semihosting_cmdline;
     AccelState *accelerator;
 };
 
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 9fea3bc..08bbe71 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -104,6 +104,7 @@ typedef enum DisplayType
 } DisplayType;
 
 extern int autostart;
+extern int with_gdb;
 
 typedef enum {
     VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
@@ -127,7 +128,13 @@ extern int cursor_hide;
 extern int graphic_rotate;
 extern int no_quit;
 extern int no_shutdown;
+
 extern int semihosting_enabled;
+extern int semihosting_target;
+#define SEMIHOSTING_TARGET_AUTO     0
+#define SEMIHOSTING_TARGET_NATIVE   1
+#define SEMIHOSTING_TARGET_GDB      2
+
 extern int old_param;
 extern int boot_menu;
 extern bool boot_strict;
diff --git a/qemu-options.hx b/qemu-options.hx
index da9851d..e9a7d94 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -78,6 +78,14 @@ STEXI
 Select CPU model (@code{-cpu help} for list and additional feature selection)
 ETEXI
 
+DEF("image", HAS_ARG, QEMU_OPTION_image, \
+"-image elf use 'elf' as application image to emulate\n", QEMU_ARCH_ALL)
+STEXI
address@hidden -image @var{elf}
address@hidden -image
+Load @var{elf} and use it as application image to emulate.
+ETEXI
+
 DEF("smp", HAS_ARG, QEMU_OPTION_smp,
     "-smp 
[cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]\n"
     "                set the number of CPUs to 'n' [default=1]\n"
@@ -2580,7 +2588,8 @@ DEF("append", HAS_ARG, QEMU_OPTION_append, \
 STEXI
 @item -append @var{cmdline}
 @findex -append
-Use @var{cmdline} as kernel command line
+Append the space separated strings @var{cmdline} to the kernel
+full path to generate the complete kernel command line.
 ETEXI
 
 DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \
@@ -3210,14 +3219,41 @@ STEXI
 @findex -prom-env
 Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only).
 ETEXI
+
 DEF("semihosting", 0, QEMU_OPTION_semihosting,
-    "-semihosting    semihosting mode\n",
+    "-semihosting    enable semihosting\n",
     QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
 STEXI
 @item -semihosting
 @findex -semihosting
-Semihosting mode (ARM, M68K, Xtensa only).
+Enable semihosting system calls emulation (ARM, M68K, Xtensa only). By default,
+the calls are addressed to QEMU, but, if a debug session is active, the
+calls are forwarded to GDB. This behaviour can be changed by
address@hidden
+ETEXI
+DEF("semihosting-target", HAS_ARG, QEMU_OPTION_semihosting_target,
+"-semihosting-target native|gdb|auto   define semihosting target\n",
+QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
+STEXI
address@hidden -semihosting-target [native|gdb|auto]
address@hidden -semihosting-target
+Enable semihosting and define where the semihosting calls will be addressed,
+to QEMU (@code{native}) or to GDB (@code{gdb}). Default is @code{auto}.
+(ARM, M68K, Xtensa only)
+ETEXI
+DEF("semihosting-cmdline", HAS_ARG, QEMU_OPTION_semihosting_cmdline, \
+"-semihosting-cmdline args use 'args' as semihosting command line\n", 
QEMU_ARCH_ALL)
+STEXI
address@hidden -semihosting-cmdline @var{args}
address@hidden -cmdline
+Use the space separated strings @var{args} as the complete command line
+passed (via SYS_GET_CMDLINE) to the emulated program
+when semihosting is enabled. The first word is passed as argv[0], the
+program name. If missing, an empty string is passed to the program. Use
+quotes, apostrophes or backslashes (on POSIX) to make a multi-word string
+look like a single option.
 ETEXI
+
 DEF("old-param", 0, QEMU_OPTION_old_param,
     "-old-param      old param mode\n", QEMU_ARCH_ARM)
 STEXI
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index ebb5235..b032b15 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -58,6 +58,11 @@
 #define TARGET_SYS_HEAPINFO    0x16
 #define TARGET_SYS_EXIT        0x18
 
+/* ADP_Stopped_ApplicationExit is used for exit(0),
+ * anything else is implemented as exit(1) */
+#define ADP_Stopped_ApplicationExit     ((2 << 16) + 38)
+#define ADP_Stopped_RunTimeError        ((2 << 16) + 35)
+
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
@@ -434,12 +439,37 @@ uint32_t do_arm_semihosting(CPUARMState *env)
             GET_ARG(0);
             GET_ARG(1);
             input_size = arg1;
+
+            const char *argv0 = NULL;
+            const char *cmdline = NULL;
+
+            if (ts->boot_info) {
+                if (ts->boot_info->semihosting_cmdline != NULL) {
+                    argv0 = ""; /* argv[0] is also passed by the user */
+                    cmdline = ts->boot_info->semihosting_cmdline;
+                } else if (ts->boot_info->kernel_filename != NULL) {
+                    /* Use the kernel filename as argv 0 */
+                    argv0 = ts->boot_info->kernel_filename;
+                    cmdline = ts->boot_info->kernel_cmdline;
+                }
+            }
+
+            if (argv0 == NULL) {
+                argv0 = "";
+            }
+
+            if (cmdline == NULL) {
+                cmdline = "";
+            }
+
             /* Compute the size of the output string.  */
 #if !defined(CONFIG_USER_ONLY)
-            output_size = strlen(ts->boot_info->kernel_filename)
-                        + 1  /* Separating space.  */
-                        + strlen(ts->boot_info->kernel_cmdline)
-                        + 1; /* Terminating null byte.  */
+            output_size = strlen(argv0);
+            if (output_size > 0) {
+                output_size += 1;  /* Separating space.  */
+            }
+            output_size += strlen(cmdline);
+            output_size += 1; /* Terminating null byte.  */
 #else
             unsigned int i;
 
@@ -470,9 +500,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
 
             /* Copy the command-line arguments.  */
 #if !defined(CONFIG_USER_ONLY)
-            pstrcpy(output_buffer, output_size, 
ts->boot_info->kernel_filename);
-            pstrcat(output_buffer, output_size, " ");
-            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
+            pstrcpy(output_buffer, output_size, argv0);
+            if (strlen(argv0) > 0) {
+                pstrcat(output_buffer, output_size, " ");
+            }
+            pstrcat(output_buffer, output_size, cmdline);
 #else
             if (output_size == 1) {
                 /* Empty command-line.  */
@@ -551,8 +583,12 @@ uint32_t do_arm_semihosting(CPUARMState *env)
             return 0;
         }
     case TARGET_SYS_EXIT:
-        gdb_exit(env, 0);
-        exit(0);
+        /* ARM specifies only Stopped_ApplicationExit as normal
+         * exit, everything else is considered an error */
+        ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
+        gdb_exit(env, ret);
+        exit(ret);
+
     default:
         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
         cpu_dump_state(cs, stderr, fprintf, 0);
diff --git a/vl.c b/vl.c
index f4a6e5e..8c43fd0 100644
--- a/vl.c
+++ b/vl.c
@@ -138,6 +138,7 @@ bool enable_mlock = false;
 int nb_nics;
 NICInfo nd_table[MAX_NICS];
 int autostart;
+int with_gdb = FALSE;
 static int rtc_utc = 1;
 static int rtc_date_offset = -1; /* -1 means no change */
 QEMUClockType rtc_clock;
@@ -172,6 +173,7 @@ const char *watchdog;
 QEMUOptionRom option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
+int semihosting_target = SEMIHOSTING_TARGET_AUTO;
 int old_param = 0;
 const char *qemu_name;
 int alt_grab = 0;
@@ -2739,8 +2741,11 @@ int main(int argc, char **argv, char **envp)
 {
     int i;
     int snapshot, linux_boot;
+    const char *image_filename = NULL;
     const char *initrd_filename;
-    const char *kernel_filename, *kernel_cmdline;
+    const char *kernel_filename;
+    const char *kernel_cmdline;
+    const char *semihosting_cmdline = NULL;
     const char *boot_order;
     DisplayState *ds;
     int cyls, heads, secs, translation;
@@ -3036,7 +3041,10 @@ int main(int argc, char **argv, char **envp)
                     exit(1);
                 }
                 break;
-            case QEMU_OPTION_kernel:
+           case QEMU_OPTION_image:
+                image_filename = optarg;
+                break;
+           case QEMU_OPTION_kernel:
                 qemu_opts_set(qemu_find_opts("machine"), 0, "kernel", optarg);
                 break;
             case QEMU_OPTION_initrd:
@@ -3219,9 +3227,11 @@ int main(int argc, char **argv, char **envp)
                 break;
             case QEMU_OPTION_s:
                 add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT);
+                with_gdb = TRUE;
                 break;
             case QEMU_OPTION_gdb:
                 add_device_config(DEV_GDB, optarg);
+                with_gdb = TRUE;
                 break;
             case QEMU_OPTION_L:
                 if (data_dir_idx < ARRAY_SIZE(data_dir)) {
@@ -3618,6 +3628,25 @@ int main(int argc, char **argv, char **envp)
                break;
             case QEMU_OPTION_semihosting:
                 semihosting_enabled = 1;
+                semihosting_target = SEMIHOSTING_TARGET_AUTO;
+                break;
+            case QEMU_OPTION_semihosting_target:
+                semihosting_enabled = 1;
+
+                if (strcmp(optarg, "auto") == 0) {
+                    semihosting_target = SEMIHOSTING_TARGET_AUTO;
+                } else if (strcmp(optarg, "native") == 0) {
+                    semihosting_target = SEMIHOSTING_TARGET_NATIVE;
+                } else if (strcmp(optarg, "gdb") == 0) {
+                    semihosting_target = SEMIHOSTING_TARGET_GDB;
+                } else {
+                    fprintf(stderr, "Unsupported semihosting-target %s\n",
+                            optarg);
+                    exit(1);
+                }
+                break;
+            case QEMU_OPTION_semihosting_cmdline:
+                semihosting_cmdline = optarg;
                 break;
             case QEMU_OPTION_tdf:
                 fprintf(stderr, "Warning: user space PIT time drift fix "
@@ -4119,6 +4148,7 @@ int main(int argc, char **argv, char **envp)
         kernel_cmdline = "";
         current_machine->kernel_cmdline = (char *)kernel_cmdline;
     }
+    current_machine->semihosting_cmdline = (char *)semihosting_cmdline;
 
     linux_boot = (kernel_filename != NULL);
 
@@ -4137,6 +4167,24 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+    if (semihosting_cmdline != NULL && semihosting_enabled == 0) {
+        fprintf(stderr, "-semihosting-cmdline only allowed with "
+                "-semihosting or -semihosting-target\n");
+        exit(1);
+    }
+
+    if (kernel_filename != NULL && image_filename != NULL) {
+        fprintf(stderr, "-image and -kernel are mutually exclusive\n");
+        exit(1);
+    }
+
+    if (kernel_filename == NULL && image_filename != NULL) {
+        /* The rest of the code uses kernel_filename, so copy image there */
+        kernel_filename = image_filename;
+        current_machine->kernel_filename = image_filename;
+        qemu_opts_set(qemu_find_opts("machine"), 0, "kernel", image_filename);
+    }
+
     os_set_line_buffering();
 
     qemu_init_cpu_loop();
@@ -4385,7 +4433,8 @@ int main(int argc, char **argv, char **envp)
             error_free(local_err);
             exit(1);
         }
-    } else if (autostart) {
+    } else if (autostart && kernel_filename) {
+        /* If an image is defined and no -S is requested, start it. */
         vm_start();
     }
 
-- 
1.9.3 (Apple Git-50)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]