From patchwork Thu Nov 13 23:55:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liviu Ionescu X-Patchwork-Id: 412155 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 54DB714012A for ; Wed, 19 Nov 2014 07:22:08 +1100 (AEDT) Received: from localhost ([::1]:55187 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XqpHq-0006w4-Hg for incoming@patchwork.ozlabs.org; Tue, 18 Nov 2014 15:22:06 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52097) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XqpFZ-0004Yv-4n for qemu-devel@nongnu.org; Tue, 18 Nov 2014 15:19:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XqpFO-0008AG-01 for qemu-devel@nongnu.org; Tue, 18 Nov 2014 15:19:45 -0500 Received: from [109.99.239.84] (port=46598 helo=ilg-mbp.local) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XqpFN-00089v-Gd for qemu-devel@nongnu.org; Tue, 18 Nov 2014 15:19:33 -0500 Received: by ilg-mbp.local (Postfix, from userid 501) id 0CC7F2D0239D; Fri, 14 Nov 2014 01:57:13 +0200 (EET) From: Liviu Ionescu To: qemu-devel@nongnu.org Date: Fri, 14 Nov 2014 01:55:54 +0200 Message-Id: <1415922954-6116-2-git-send-email-ilg@livius.net> X-Mailer: git-send-email 1.9.3 (Apple Git-50) In-Reply-To: <1415922954-6116-1-git-send-email-ilg@livius.net> References: <1415922954-6116-1-git-send-email-ilg@livius.net> X-detected-operating-system: by eggs.gnu.org: iOS iPhone or iPad X-Received-From: 109.99.239.84 Cc: peter.maydell@linaro.org, Liviu Ionescu Subject: [Qemu-devel] [PATCH] functional ARM semihosting under GDB X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Liviu Ionescu --- 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 +@item -image @var{elf} +@findex -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 +@code{-semihosting-target}. +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 +@item -semihosting-target [native|gdb|auto] +@findex -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 +@item -semihosting-cmdline @var{args} +@findex -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(); }