From patchwork Tue Jan 24 12:40:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 137543 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 2F438B6EFE for ; Tue, 24 Jan 2012 23:41:00 +1100 (EST) Received: from localhost ([::1]:38197 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RpfgH-0001fA-SH for incoming@patchwork.ozlabs.org; Tue, 24 Jan 2012 07:40:57 -0500 Received: from eggs.gnu.org ([140.186.70.92]:58134) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RpfgB-0001f4-E3 for qemu-devel@nongnu.org; Tue, 24 Jan 2012 07:40:52 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Rpfg5-0005xb-0P for qemu-devel@nongnu.org; Tue, 24 Jan 2012 07:40:51 -0500 Received: from smtp1-g21.free.fr ([212.27.42.1]:53290) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rpfg4-0005xS-5R for qemu-devel@nongnu.org; Tue, 24 Jan 2012 07:40:44 -0500 Received: from Quad (unknown [IPv6:2a01:e34:eeee:5240:219:66ff:fe77:f219]) by smtp1-g21.free.fr (Postfix) with ESMTP id B6739940422; Tue, 24 Jan 2012 13:40:36 +0100 (CET) Received: from laurent by Quad with local (Exim 4.76) (envelope-from ) id 1Rpffv-0001sN-9D; Tue, 24 Jan 2012 13:40:35 +0100 From: Laurent Vivier To: qemu-devel@nongnu.org Date: Tue, 24 Jan 2012 13:40:23 +0100 Message-Id: <1327408823-7181-1-git-send-email-laurent@vivier.eu> X-Mailer: git-send-email 1.7.5.4 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 212.27.42.1 Cc: Laurent Vivier , Francis Moreau Subject: [Qemu-devel] [PATCH] Workaround to the real-mode/protected-mode conflict in 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 When qemu is started with '-S' to freeze at startup and gdb is connected to inner gdb server, gdb is using by default 16-bit real-mode. Then if we put a breakpoint inside kernel linux, the CPU is switched to 32/64-bit protected mode but GDB is always in 16 bit real-mode. The size of the registers dump differs and GDB is not able to manage it. See the following thread for more details: "Failed to use gdb with qemu 15.1 (with and without kvm support)" http://permalink.gmane.org/gmane.comp.emulators.qemu/133120 By adding a breakpoint at startup, we can connect GDB to qemu gdb server when CPU is already in protected mode. Example: - find the address of the symbol to stop on. $ nm ../linux/vmlinux | grep start_kernel ffffffff81ad391f T start_kernel ffffffff81ad334a T x86_64_start_kernel ffffffff81ad6a9c T xen_start_kernel - Then start qemu with the breakpoint option (-hb): ./x86_64-softmmu/qemu-system-x86_64 -kernel ../linux/arch/x86/boot/bzImage \ -append console=ttyS0 -nographic \ -hb 0xffffffff81ad391f - and now start GDB: $ gdb (gdb) set arch i386:x86-64 The target architecture is assumed to be i386:x86-64 (gdb) sym vmlinux Reading symbols from /home/laurent/linux/vmlinux...done. (gdb) target remote :1234 Remote debugging using :1234 start_kernel () at init/main.c:468 468 { (gdb) x/i $pc => 0xffffffff81ad391f : push %rbp (gdb) c Signed-off-by: Laurent Vivier --- gdbstub.c | 5 ++++- gdbstub.h | 2 +- monitor.c | 2 +- qemu-options.hx | 10 ++++++++++ vl.c | 11 ++++++++++- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 640cf4e..e27c262 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2860,7 +2860,7 @@ static void gdb_sigterm_handler(int signal) } #endif -int gdbserver_start(const char *device) +int gdbserver_start(const char *device, unsigned long long hbreak) { GDBState *s; char gdbstub_device_name[128]; @@ -2916,6 +2916,9 @@ int gdbserver_start(const char *device) s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; + if (hbreak != (unsigned long long)-1) { + gdb_breakpoint_insert(hbreak, 1, GDB_BREAKPOINT_HW); + } return 0; } #endif diff --git a/gdbstub.h b/gdbstub.h index d82334f..25d34ed 100644 --- a/gdbstub.h +++ b/gdbstub.h @@ -35,7 +35,7 @@ void gdb_register_coprocessor(CPUState *env, #ifdef CONFIG_USER_ONLY int gdbserver_start(int); #else -int gdbserver_start(const char *port); +int gdbserver_start(const char *port, unsigned long long hbreak); #endif /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ diff --git a/monitor.c b/monitor.c index 1be222e..208624b 100644 --- a/monitor.c +++ b/monitor.c @@ -1137,7 +1137,7 @@ static void do_gdbserver(Monitor *mon, const QDict *qdict) const char *device = qdict_get_try_str(qdict, "device"); if (!device) device = "tcp::" DEFAULT_GDBSTUB_PORT; - if (gdbserver_start(device) < 0) { + if (gdbserver_start(device, -1) < 0) { monitor_printf(mon, "Could not open gdbserver on device '%s'\n", device); } else if (strcmp(device, "none") == 0) { diff --git a/qemu-options.hx b/qemu-options.hx index b3db10c..c0d2435 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2194,6 +2194,16 @@ Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 (@pxref{gdb_usage}). ETEXI +DEF("hb", HAS_ARG, QEMU_OPTION_breakpoint, \ + "-hb addr set an hardware breakpoint at address addr\n", + QEMU_ARCH_ALL) +STEXI +@item -hb @var{addr} +@findex -hb +Set an hardware breakpoint at address @var{addr}, by default implies "-s". +ETEXI + + DEF("d", HAS_ARG, QEMU_OPTION_d, \ "-d item1,... output log to /tmp/qemu.log (use -d ? for a list of log items)\n", QEMU_ARCH_ALL) diff --git a/vl.c b/vl.c index 5372a96..9e5dc64 100644 --- a/vl.c +++ b/vl.c @@ -188,6 +188,7 @@ int mem_prealloc = 0; /* force preallocation of physical target memory */ int nb_nics; NICInfo nd_table[MAX_NICS]; int autostart; +unsigned long long hbreak; static int rtc_utc = 1; static int rtc_date_offset = -1; /* -1 means no change */ QEMUClock *rtc_clock; @@ -2222,6 +2223,7 @@ int main(int argc, char **argv, char **envp) nb_nics = 0; autostart= 1; + hbreak = -1; /* first pass of option parsing */ optind = 1; @@ -2582,6 +2584,13 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_S: autostart = 0; break; + case QEMU_OPTION_breakpoint: + hbreak = strtoull(optarg, NULL, 0); + /* hbreak needs a gdb server */ + if (gdbstub_dev == NULL) { + gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT; + } + break; case QEMU_OPTION_k: keyboard_layout = optarg; break; @@ -3442,7 +3451,7 @@ int main(int argc, char **argv, char **envp) } text_consoles_set_display(ds); - if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) { + if (gdbstub_dev && gdbserver_start(gdbstub_dev, hbreak) < 0) { fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n", gdbstub_dev); exit(1);