Hi,
Included and attached is a patch for gdbsx against c/s 20315. It was tested
on c/s 20303 and compile tested on c/s 20315. I tested for following:
64bit hyp + 64bit dom0 : 32-pae and 64bit, both PV and HVM linux guests
64bit hyp + 32bit dom0 : 32-pae and 64bit both PV and HVM linux guests
32bit hyp: compile tested only.
Please note:
- I wasn't sure if I should add to tools/Makefile, so I didn't.
- x86 only
- I picked, 1000 - 1003 for gdbsx domctl subcalls. Please feel free to change.
Thanks,
Mukesh
# HG changeset patch
# User Mukesh Rathor
# Date 1255562790 25200
# Node ID 167e895ef770f26952d0ad7941658aecbc62cb9b
# Parent 18758847bf313a191731278c41af7519dfa85ecf
This patch implementes gdbsx, a gdbserver stub for xen. It allows for
debugging of elf guests on xen. It can also be used to dump vcpu contexts.
It should be run on dom0 on gdbsx enabled hypervisor. For details, please see
tools/debugger/gdbsx/README
Signed-off-by: Mukesh Rathor <mukesh.rathor@xxxxxxxxxx>
diff -r 18758847bf31 -r 167e895ef770 Config.mk
--- a/Config.mk Wed Oct 14 09:09:23 2009 +0100
+++ b/Config.mk Wed Oct 14 16:26:30 2009 -0700
@@ -114,6 +114,10 @@
CFLAGS += -g
endif
+ifeq ($(gdbsx),y)
+XEN_GDBSX_CONFIG ?= y
+endif
+
CFLAGS += -fno-strict-aliasing
CFLAGS += -std=gnu99
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/Makefile Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,28 @@
+XEN_ROOT = ../../..
+include ./Rules.mk
+
+sbindir=/usr/sbin
+
+.PHONY: all
+all:
+ $(MAKE) -C gx
+ $(MAKE) -C xg
+ $(MAKE) gdbsx
+
+.PHONY: clean
+clean:
+ rm -f xg_all.a gx_all.a gdbsx
+ set -e; for d in xg gx; do $(MAKE) -C $$d clean; done
+
+.PHONY: install
+install: all
+ [ -d $(DESTDIR)$(sbindir) ] || $(INSTALL_DIR) $(DESTDIR)$(sbindir)
+ $(INSTALL_PROG) gdbsx $(DESTDIR)$(sbindir)/gdbsx
+
+gdbsx: gx/gx_all.a xg/xg_all.a
+ $(CC) -o $@ $^
+
+xg/xg_all.a:
+ $(MAKE) -C xg
+gx/gx_all.a:
+ $(MAKE) -C gx
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/README Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,143 @@
+
+ gdbsx: gdbserver for xen
+
+
+Welcome to gdbsx. gdbsx is a gdbserver program to debug guest kernels and
+kernel modules. It runs on dom0 running on xen hypervisor and allows debug
+of 32 or 64bit PV or HVM elf guest binaries. It can also be run standalone,
+without remote gdb, to dump context of any/all VCPUs of any guest.
+
+It is divided in two parts, gx and xg. The former interacts with remote gdb,
+while latter interacts with xen and exports public APIs that can be used to
+create a plug in for any other debugger or binary type.
+
+
+USAGE:
+ - boot with gdbsx enabled hypervisor (eg, on OVM: xen-64bit-debug.gz)
+ - copy gdbsx binary to the dom0 (assume hostname is "dom0"), then:
+
+ USAGE 1:
+ - dom0> gdbsx -c 1 64 : displays VCPU contexts for 64bit guest with domid 1
+
+ USAGE 2:
+ - dom0> gdbsx -a 2 64 9999
+ connects to a 64bit guest with domid 2 and waits for gdb
connection
+ - now, connect to the above gdbsx from a remote system or dom0 as:
+ bash> gdb ./vmlinux (exact matching vmlinux of guest kernel)
+ (gdb) target remote dom0:9999
+
+ - Additionally, to debug loadable kernel modules, please do following:
+ (gdb) p init_mm.pgd[3]
+ $1 = {pgd = 0x1b874f027}
+ (gdb) monitor pgd3 0x1b874f027 (Make sure value is in HEX)
+ pgd3val set to: 0x1b874f027
+
+ - use gdb as normal, breakpoints, single step, etc...
+ - when need to break into gdb, instead of ctrl-c, just do "xm pause <domid>"
+ on dom0 to pause the guest. this will break into gdb right away.
+ - detach/quit from gdb (leave gdbsx alone) to gracefully exit.
+ - if ctrl-c or core-dumped, make sure to do xm unpause if guest still
paused.
+
+ - multiple vcpus:
+ o gdb>set scheduler-locking on : for single step of correct vcpu.
+
+ o since gdb is not kernel debugger, vcpus are emulated via threads
+ Thus, gdb>info threads : will show all vcpus. Then, switch thread
+ to get to another vcpu, etc... Remember, gdb has it's own [thread]
+ id, off by 1.
+
+ - See below for some useful gdb macros. Please email me if you've more.
+
+
+NOTES:
+ - For now, it is not possible to run gdbsx on a guest and gdb inside
+ the same guest at the same time.
+ - To report problems, please run gdbsx with -d and collect output.
+ - VCPU offlining is not supported. Thus [0-NUMVCPUs] are all assumed active.
+
+TIPS:
+ - make sure firewall is disabled on dom0 if running gdb on a different host.
+ - Must be at least gdb version 6.5-16.x to debug el5 kernels.
+
+BUILD: (if you don't have access to binary):
+ - first compile the hypervisor: xen> make gdbsx=y
+ To have both kdb and gdbsx, xen> make kdb=y gdbsx=y
+ (NOTE: kdb is not required for gdbsx)
+ - install the hypervisor and reboot
+ - now go to, tools/debugger/gdbsx and do make
+ On 32bit system, a 32bit binary will be built with support for both 32
+ and 64bit guests. On 64bit system, a 64bit binary will be built with
+ support for both.
+
+
+Mukesh Rathor
+Oracle Corporation,
+Redwood Shores, CA USA
+mukesh[dot]rathor[at]oracle[dot]com
+
+
+------------------------------------------------------------------------------
+
+USEFUL gdb macros:
+
+# Courtesy Zhigang W (http://10.182.120.78/tech/vt/ovm/debug/gdbinit.macros):
+
+define ps
+ dont-repeat
+ set $tasks = (struct list_head *)init_task->tasks
+ set $offset = (unsigned long)&init_task->tasks - (unsigned
long)&init_task
+ set $task = $tasks
+ set $task_entry = (struct task_struct *)((unsigned long)$task - $offset)
+ printf "Pointer PID Command\n"
+ printf "%-14p%-9d%s\n", $task_entry, $task_entry->pid, $task_entry->comm
+ set $task = $task->next
+ while $task != $tasks
+ set $task_entry = (struct task_struct *)((unsigned long)$task -
$offset)
+ if ($task_entry->pid) != 0
+ printf "%-14p%-9d%s\n", $task_entry, $task_entry->pid,
$task_entry->comm
+ end
+ set $task = $task->next
+ end
+end
+
+document ps
+Report a snapshot of the current processes.
+end
+
+
+define lsmod
+ dont-repeat
+ # 4 for 32bit kernels. 8 for 64bit kernels.
+ set $sz = sizeof(long)
+ set $mod = (struct list_head *)modules
+ printf "modptr address name\n"
+ while 1
+ set $mod_entry = (struct module *)((unsigned long)$mod - $sz)
+ if ($sz == 4)
+ printf "%08lx %08lx %s\n", $mod_entry, \
+ $mod_entry->module_core, $mod_entry->name
+ else
+ printf "%016lx %016lx %s\n", $mod_entry, \
+ $mod_entry->module_core, $mod_entry->name
+ end
+ set $mod = $mod->next
+ if ($mod == &modules)
+ loop_break
+ end
+ end
+end
+
+document lsmod
+Show the list of modules loaded in the Linux kernel.
+end
+
+define log
+ dont-repeat
+ printf "%s", log_buf
+end
+
+document log
+Dump system message buffer.
+end
+
+------------------------------------------------------------------------------
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/Rules.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/Rules.mk Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,11 @@
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS += -Werror -Wmissing-prototypes
+# (gcc 4.3x and later) -Wconversion -Wno-sign-conversion
+CFLAGS += -DXEN_GDBSX_CONFIG
+
+# just in case have to debug gdbsx, keep life simple.
+TMPFLAGS := $(CFLAGS)
+CFLAGS := $(filter-out -O% -DNDEBUG -fomit-frame-pointer, $(TMPFLAGS))
+CFLAGS += -O0
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/Makefile Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,20 @@
+XEN_ROOT = ../../../..
+include ../Rules.mk
+
+GX_OBJS := gx_comm.o gx_main.o gx_utils.o gx_local.o
+GX_HDRS := $(wildcard *.h)
+
+.PHONY: all
+all: gx_all.a
+
+.PHONY: clean
+clean:
+ rm -rf gx_all.a *.o
+
+
+#%.o: %.c $(GX_HDRS) Makefile
+# $(CC) -c $(CFLAGS) -o $@ $<
+
+gx_all.a: $(GX_OBJS) Makefile $(GX_HDRS)
+ ar cr $@ $(GX_OBJS) # problem with ld using -m32
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/gx.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/gx.h Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+typedef uint16_t domid_t;
+typedef unsigned char uchar;
+
+#include "../xg/xg_public.h"
+
+int gx_remote_open (char *commstr);
+void gx_remote_close(void);
+int gx_getpkt (char *buf);
+void gx_write_ok(char *buf);
+void gx_write_err(char *buf);
+void gx_convert_int_to_ascii (char *from, char *to, int n);
+void gx_convert_ascii_to_int (char *from, char *to, int n);
+int gx_putpkt (char *buf);
+void gx_decode_m_packet(char *, uint64_t *, int *);
+char *gx_decode_M_packet(char *, uint64_t *, int *);
+void gx_decode_zZ_packet(char *, uint64_t *);
+void gx_reply_ok(char *);
+void gx_reply_error(char *);
+int gx_fromhex(int);
+int gx_tohex(int);
+int gx_local_cmd(domid_t domid, vcpuid_t vcpuid);
+void gxprt(const char *fmt, ...);
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/gx_comm.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/gx_comm.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,328 @@
+/* Remote utility routines for the remote server for GDB.
+ Copyright (C) 2008
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+/* This module handles communication with remote gdb. courtesy
+ * of gdbserver remote-utils.c */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "gx.h"
+
+
+extern int gx_remote_dbg;
+
+static int remote_fd;
+
+
+/* Returns: 0 success. -1 failure */
+static int
+do_tcp(char *port_str)
+{
+ int port;
+ struct sockaddr_in sockaddr;
+ socklen_t tmp;
+ int sock_fd;
+
+ port = atoi(port_str);
+
+ sock_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock_fd < 0) {
+ gxprt("ERROR: failed socket open. errno:%d\n", errno);
+ return -1;
+ }
+
+ /* Allow rapid reuse of this port. */
+ tmp = 1;
+ setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char
*)&tmp,sizeof(tmp));
+
+ sockaddr.sin_family = PF_INET;
+ sockaddr.sin_port = htons (port);
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(sock_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
+ || listen (sock_fd, 1)) {
+ gxprt("ERROR: can't bind address. errno:%d\n", errno);
+ close(sock_fd);
+ return -1;
+ }
+ printf("Listening on port %d\n", port);
+
+ tmp = sizeof(sockaddr);
+ remote_fd = accept(sock_fd, (struct sockaddr *) &sockaddr, &tmp);
+ if (remote_fd == -1) {
+ gxprt("ERROR: accept failed. errno:%d\n", errno);
+ close(sock_fd);
+ return -1;
+ }
+
+ /* Enable TCP keep alive process. */
+ tmp = 1;
+ setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char
*)&tmp,sizeof(tmp));
+
+ /* Tell TCP not to delay small packets. This greatly speeds up
+ * interactive response. */
+ tmp = 1;
+ setsockopt(remote_fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&tmp, sizeof(tmp));
+
+ close(sock_fd); /* No longer need this */
+
+ signal(SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply
+ * exits when the remote side dies. */
+
+ /* Convert IP address to string */
+ printf("Remote debugging from host %s\n",
inet_ntoa(sockaddr.sin_addr));
+
+ return 0;
+}
+
+/*
+ * Open a connection for remote gdb on the given port number
+ * Returns: 0 for success. -1 for failure
+ */
+int
+gx_remote_open(char *portnum_str)
+{
+ int save_fcntl_flags;
+
+ if (do_tcp(portnum_str) == -1) {
+ close(remote_fd);
+ return -1;
+ }
+
+#if defined(F_SETFL) && defined (FASYNC)
+ save_fcntl_flags = fcntl(remote_fd, F_GETFL, 0);
+ fcntl(remote_fd, F_SETFL, save_fcntl_flags | FASYNC);
+ #if defined (F_SETOWN)
+ fcntl (remote_fd, F_SETOWN, getpid ());
+ #endif
+#endif
+ return 0;
+}
+
+void
+gx_remote_close(void)
+{
+ close(remote_fd);
+}
+
+
+/* Returns next char from remote gdb. -1 if error. */
+static int
+readchar(void)
+{
+ static char buf[BUFSIZ];
+ static int bufcnt = 0;
+ static char *bufp;
+ uint64_t ll;
+
+ if (bufcnt-- > 0)
+ return *bufp++ & 0x7f;
+
+ bufcnt = read(remote_fd, buf, sizeof (buf));
+ ll = *(uint64_t *)buf;
+ if (bufcnt <= 0) {
+ if (bufcnt == 0)
+ gxprt("readchar: Got EOF\n");
+ else
+ perror ("readchar");
+ return -1;
+ }
+ bufp = buf;
+ bufcnt--;
+ return *bufp++ & 0x7f;
+}
+
+/* Read a packet from the remote machine, with error checking,
+ * and store it in buf.
+ * Returns: length of packet, or negative int if error.
+ */
+int
+gx_getpkt (char *buf)
+{
+ char *bp;
+ unsigned char csum, c1, c2;
+ int c;
+
+ while (1) {
+ csum = 0;
+
+ while (1) {
+ c = readchar();
+ if (c == '$')
+ break;
+
+ if (gx_remote_dbg)
+ gxprt("[getpkt: discarding char '%c']\n", c);
+ if (c < 0)
+ return -1;
+ }
+
+ bp = buf;
+ while (1) {
+ c = readchar ();
+ if (c < 0)
+ return -1;
+ if (c == '#')
+ break;
+ *bp++ = c;
+ csum += c;
+ }
+ *bp = 0;
+
+ c1 = gx_fromhex(readchar());
+ c2 = gx_fromhex(readchar());
+
+ if (csum == (c1 << 4) + c2)
+ break;
+
+ gxprt("Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
+ (c1 << 4) + c2, csum, buf);
+ write(remote_fd, "-", 1);
+ }
+ if (gx_remote_dbg) {
+ gxprt("getpkt (\"%s\"); [sending ack] \n", buf);
+ }
+
+ write(remote_fd, "+", 1);
+
+ if (gx_remote_dbg) {
+ gxprt("[sent ack]\n");
+ }
+ return bp - buf;
+}
+
+void
+gx_reply_ok(char *buf)
+{
+ buf[0] = 'O';
+ buf[1] = 'K';
+ buf[2] = '\0';
+}
+
+/* ENN error */
+void
+gx_reply_error(char *buf)
+{
+ buf[0] = 'E';
+ buf[1] = '0';
+ buf[2] = '1';
+ buf[3] = '\0';
+}
+
+/*
+ * Send a packet to the remote machine, with error checking.
+ * The data of the packet is in buf.
+ * Returns: >= 0 on success, -1 otherwise.
+ */
+int
+gx_putpkt (char *buf)
+{
+ int i;
+ unsigned char csum = 0;
+ char *buf2;
+ char buf3[1];
+ int cnt = strlen (buf);
+ char *p;
+
+ buf2 = malloc(8192);
+
+ /* Copy the packet into buffer buf2, encapsulating it
+ * and giving it a checksum. */
+
+ p = buf2;
+ *p++ = '$';
+
+ for (i = 0; i < cnt; i++) {
+ csum += buf[i];
+ *p++ = buf[i];
+ }
+ *p++ = '#';
+ *p++ = gx_tohex((csum >> 4) & 0xf);
+ *p++ = gx_tohex(csum & 0xf);
+
+ *p = '\0';
+
+ /* Send it over and over until we get a positive ack. */
+
+ do {
+ int cc;
+
+ if (write(remote_fd, buf2, p - buf2) != p - buf2) {
+ perror("putpkt(write)");
+ return -1;
+ }
+ if (gx_remote_dbg)
+ gxprt("putpkt (\"%s\"); [looking for ack]\n", buf2);
+
+ cc = read(remote_fd, buf3, 1);
+ if (gx_remote_dbg)
+ gxprt("[received '%c' (0x%x)]\n", buf3[0], buf3[0]);
+
+ if (cc <= 0) {
+ if (cc == 0)
+ gxprt("putpkt(read): Got EOF\n");
+ else
+ gxprt("putpkt(read)");
+ free(buf2);
+ return -1;
+ }
+ /* Check for an input interrupt while we're here. */
+ if (buf3[0] == '\003')
+ gxprt("WARN: need to send SIGINT in putpkt\n");
+
+ } while (buf3[0] != '+');
+
+ free(buf2);
+ return 1; /* Success! */
+}
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/gx_local.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/gx_local.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+
+/* This file to impelement functions that run interactively and don't
+ * involve remote gdb. Eg, print vcpu context and exit. */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "gx.h"
+
+extern vcpuid_t max_vcpuid;
+extern int guest_bitness;
+
+static void
+prnt_32regs(struct xg_gdb_regs32 *r32p)
+{
+ printf("eip:%08x esp:%08x flags:%08x\n", r32p->eip, r32p->esp,
+ r32p->eflags);
+ printf("eax:%08x ebx:%08x ecx:%08x edx:%08x\n", r32p->eax,
+ r32p->ebx, r32p->ecx, r32p->edx);
+ printf("esi:%08x edi:%08x ebp:%08x\n", r32p->esi, r32p->edi,
+ r32p->ebp);
+ printf("cs:%x ds:%x fs:%x gs:%x\n", r32p->cs, r32p->ds, r32p->fs,
+ r32p->gs);
+ printf("\n");
+}
+
+static void
+prnt_64regs(struct xg_gdb_regs64 *r64p)
+{
+ printf("rip:"XGF64" rsp:"XGF64" flags:"XGF64"\n", r64p->rip, r64p->rsp,
+ r64p->rflags);
+ printf("rax:"XGF64" rbx:"XGF64" rcx:"XGF64"\n", r64p->rax, r64p->rbx,
+ r64p->rcx);
+ printf("rdx:"XGF64" rsi:"XGF64" rdi:"XGF64"\n", r64p->rdx, r64p->rsi,
+ r64p->rdi);
+ printf("r08:"XGF64" r09:"XGF64" r10:"XGF64"\n", r64p->r8, r64p->r9,
+ r64p->r10);
+ printf("r11:"XGF64" r12:"XGF64" r13:"XGF64"\n", r64p->r11, r64p->r12,
+ r64p->r13);
+ printf("r14:"XGF64" r15:"XGF64" rbp:"XGF64"\n", r64p->r14, r64p->r15,
+ r64p->rbp);
+ printf("cs:"XGF64" ds:"XGF64" fs:"XGF64" gs:"XGF64"\n", r64p->cs,
+ r64p->ds, r64p->fs, r64p->gs);
+ printf("\n");
+}
+
+
+static void
+prnt_call_trace32(uint32_t ip, uint32_t sp)
+{
+ int stack_max=10; /* try to print upto 10 entries if possible */
+ uint32_t loopmax=0, val;
+
+ printf("Call Trace:\n");
+ printf(" [%08x]\n", ip);
+
+ while(stack_max > 0) {
+ if (xg_read_mem((uint64_t)sp, (char *)&val, sizeof(val),0) !=
0)
+ return;
+ if (val > 0x0c000000) { /* kernel addr */
+ printf(" [%08x]\n", val);
+ --stack_max;
+ }
+ sp += sizeof(sp);
+ if (++loopmax > 10000) /* don't go forever */
+ break;
+ }
+}
+
+static void
+prnt_call_trace64(uint64_t ip, uint64_t sp)
+{
+ int stack_max=10; /* try to print upto 10 entries if possible */
+ uint64_t loopmax=0, val;
+
+ printf("Call Trace:\n");
+ printf(" ["XGF64"]\n", ip);
+
+ while(stack_max > 0) {
+ if (xg_read_mem(sp, (char *)&val, sizeof(val),0) != 0)
+ return;
+ if (val > 0xffffffff80000000UL) { /* kernel addr */
+ printf(" ["XGF64"]\n", val);
+ --stack_max;
+ }
+ sp += sizeof(sp);
+ if (++loopmax > 10000) /* don't go forever */
+ break;
+ }
+}
+
+static int
+prnt_vcpu_context(vcpuid_t vcpuid)
+{
+ union xg_gdb_regs gregs;
+ int rc;
+
+ printf("\n--> VCPU:%d\n", vcpuid);
+ rc = xg_regs_read(XG_GPRS, vcpuid, &gregs, guest_bitness);
+ if (rc) {
+ gxprt("ERROR: failed to read regs. errno:%d\n", errno);
+ return 1;
+ }
+ if (guest_bitness==32) {
+ prnt_32regs(&gregs.gregs_32);
+ prnt_call_trace32(gregs.gregs_32.eip, gregs.gregs_32.esp);
+ } else {
+ prnt_64regs(&gregs.gregs_64);
+ prnt_call_trace64(gregs.gregs_64.rip, gregs.gregs_64.rsp);
+ }
+ return 0;
+}
+
+/* vcpuid is already checked to be <= max_vcpuid */
+int
+gx_local_cmd(domid_t domid, vcpuid_t vcpuid)
+{
+ printf("===> Context for DOMID:%d\n", domid);
+ if (vcpuid == -1) {
+ int i;
+ for (i=0; i <= max_vcpuid; i++)
+ prnt_vcpu_context(i);
+ } else
+ prnt_vcpu_context(vcpuid);
+ return 0;
+}
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/gx_main.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/gx_main.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+/* This module is the main module for gdbsx implementation. gdbsx is a remote
+ * gdbserver stub for xen. It facilitates debugging of xen guests. It also
+ * prints vcpu contexts locally without remote gdb. */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+
+#include "gx.h"
+
+
+enum target_signal {
+ TARGET_SIGNAL_INT = 2,
+ TARGET_SIGNAL_TRAP = 5
+};
+
+
+/* At present, we don't support offlining VCPUs, or dynamic adding/removal
+ * of them. As such, max_vcpu means active [0 - max_vcpuid] vcpus */
+vcpuid_t max_vcpuid; /* so max_vcpuid+1 vcpus overall */
+int guest_bitness; /* 32 or 64 */
+
+const char host_name[] = "";
+
+int gx_remote_dbg; /* enable debug trace output for debugging */
+uint64_t pgd3val; /* value of init_mm.pgd[3] set by monitor gdb cmd
*/
+
+static vcpuid_t current_vcpu;
+
+/*
+ * write regs received from remote gdb to guest
+ */
+static void
+gx_write_guest_regs(char *rbuf)
+{
+ union xg_gdb_regs gregs;
+ int rc;
+ char *savrbuf = rbuf;
+ int regsz = (guest_bitness == 32) ? sizeof(gregs.gregs_32) :
+ sizeof(gregs.gregs_64);
+ rbuf++;
+ if (strlen(rbuf) != 2*regsz) {
+ gxprt("ERROR: wrong sized register pkt received...\n"
+ "Expected:%d got:%d\n", 2*regsz, strlen(rbuf));
+ }
+ gx_convert_ascii_to_int(rbuf, (char *)&gregs, regsz);
+
+ rc = xg_regs_write(XG_GPRS, current_vcpu, &gregs, guest_bitness);
+ if (rc) {
+ gxprt("ERROR: failed to write regs. errno:%d\n", errno);
+ savrbuf[0] ='\0';
+ gx_reply_error(savrbuf);
+ } else {
+ gx_reply_ok(savrbuf);
+ }
+}
+
+/*
+ * read guest regs and send to remote gdb
+ */
+static void
+gx_read_guest_regs(char *rbuf)
+{
+ union xg_gdb_regs gregs;
+ int rc;
+
+ rc = xg_regs_read(XG_GPRS, current_vcpu, &gregs, guest_bitness);
+ if (rc) {
+ gxprt("ERROR: failed to read regs. errno:%d\n", errno);
+ rbuf[0] ='\0';
+ } else {
+ int sz = (guest_bitness == 32) ? sizeof(gregs.gregs_32) :
+ sizeof(gregs.gregs_64);
+ gx_convert_int_to_ascii((char *)&gregs, rbuf, sz);
+ }
+}
+
+/* remote_buf: 'qRcmd,pgd3 c0ae9018\0' (c0ae9018 may also be 0xc0ae9018) */
+static void
+_do_qRcmd_req(char *remote_buf)
+{
+ char buf[64], buf1[64];
+ char *p = remote_buf + 6;
+ int len = strlen(p)/2; /* because "70" is one char "p" */
+
+ gx_convert_ascii_to_int(p, buf, len);
+ XGTRC("remote_buf:%s buf:%s\n", remote_buf, buf);
+
+ if (strncmp(buf, "pgd3 ", 5) == 0) {
+ char *endp;
+
+ pgd3val = strtoull(buf+5, &endp, 16);
+ XGTRC("buf+5:%s pgd3val:0x%llx\n", buf+5, pgd3val);
+
+ if (*endp == '\0' && pgd3val > 0) {
+ sprintf(buf1, "pgd3val set to: "XGF64"\n", pgd3val);
+ } else {
+ sprintf(buf1, "Invalid pgd3val "XGF64"\n", pgd3val);
+ pgd3val = 0;
+ }
+ } else {
+ sprintf(buf1, "Bad monitor command\n");
+ }
+ gx_convert_int_to_ascii(buf1, remote_buf, strlen(buf1));
+ return;
+}
+
+/* qSupported qC qfThreadInfo qsThreadInfo qThreadExtraInfo,1 etc.. */
+static void
+process_q_request(char *remote_buf)
+{
+ /* send a list of tids: "m 0,1,2,3l" */
+ if (strcmp("qfThreadInfo", remote_buf) == 0) {
+ vcpuid_t vid = 0;
+ char *p = remote_buf;
+
+ sprintf(p, "m %x", vid); /* puts null char at the end */
+ p = p + strlen(p);
+ for (vid=1; vid <= max_vcpuid; vid++) {
+ sprintf(p, ",%x", vid);
+ p = p + strlen(p);
+ }
+ sprintf(p, "l"); /* puts null char at the end */
+ return;
+ }
+ /* qSymbol works for init_mm, and not init_mm.pgd, hence we can't use
+ * it at this time. instead use "monitor" in gdb */
+ if (strncmp("qRcmd,", remote_buf, 6) == 0) {
+ _do_qRcmd_req(remote_buf);
+ return;
+ }
+
+ /* TBD : qThreadExtraInfo : send extra banner info */
+
+ /* nothing else supported right now */
+ remote_buf[0] = '\0';
+
+ return;
+}
+
+/*
+ * Set current thread/vcpu to : -1 all threads, 0 any thread, or given tid/vcpu
+ * Even tho, 0 is a valid vcpu for us, it's OK as vcpu 0 is any vcpu
+ * Eg. Hc-1\0 Hc0\0 etc...
+ */
+static void
+process_H_request(char *remote_buf)
+{
+ char ch1 = remote_buf[1];
+
+ if (ch1 == 'c' || ch1 == 'g' || ch1 == 's') {
+ vcpuid_t vcpu;
+
+ /* we keep vcpu_id (which gdb thinks is tid) and
+ * gdb_id the same for simplicity */
+
+ vcpu = strtoul(&remote_buf[2], NULL, 16);
+ if (vcpu == -1) {
+ vcpu = 0;
+ }
+ /* it doesn't matter to us: g, c, or s */
+ current_vcpu = vcpu;
+ gx_reply_ok(remote_buf);
+ } else {
+ /* Silently ignore so gdb can extend the protocol
+ * without compatibility headaches */
+ remote_buf[0] = '\0';
+ }
+}
+
+/* read guest memory to send to remote gdb user */
+static void
+process_m_request(char *remote_buf)
+{
+ uint64_t addr;
+ int len, remain;
+ char *xbuf;
+
+ gx_decode_m_packet(&remote_buf[1], &addr, &len);
+
+ if ((xbuf=malloc(len)) == NULL) {
+ gx_reply_error(remote_buf);
+ return;
+ }
+ if ((remain=xg_read_mem(addr, xbuf, len, pgd3val)) != 0) {
+ XGTRC("Failed read mem. addr:0x%llx len:%d remn:%d errno:%d\n",
+ addr, len, remain, errno);
+ gx_reply_error(remote_buf);
+ free(xbuf);
+ return;
+ }
+ gx_convert_int_to_ascii(xbuf, remote_buf, len);
+ free(xbuf);
+ return;
+}
+
+/* write guest memory */
+static void
+process_M_request(char *remote_buf)
+{
+ uint64_t addr;
+ int len, remain;
+ char *xbuf, *data_strtp; /* where guest data actually starts */
+
+ data_strtp = gx_decode_M_packet(&remote_buf[1], &addr, &len);
+
+ if ((xbuf=malloc(len)) == NULL) {
+ gx_reply_error(remote_buf);
+ return;
+ }
+ gx_convert_ascii_to_int(data_strtp, xbuf, len);
+
+ if ((remain=xg_write_mem(addr, xbuf, len, pgd3val)) != 0) {
+ gxprt("Failed write mem. addr:0x%llx len:%d rem:%d errno:%d\n",
+ addr, len, remain, errno);
+ gx_reply_error(remote_buf);
+ } else {
+ gx_reply_ok(remote_buf);
+ }
+ free(xbuf);
+ return;
+}
+
+/* Eg.: "vCont;c" "vCont;s:5" */
+static void
+process_v_cont_request(char *bufp)
+{
+ char *savbufp = bufp;
+
+ bufp = bufp + 5; /* address of semicolon */
+
+ if (*bufp == '\0' || *bufp != ';')
+ goto errout;
+ bufp++;
+ if (*bufp == 'S' || *bufp == 'C') /* we don't support signalling */
+ goto errout;
+#if 0
+ if (*bufp == 'c') {
+ if (*(bufp+1) != '\0')
+ goto errout; /* don't tolerate bad pkt */
+ xg_resume(guest_bitness); /* continue domain */
+
+ } else if (*bufp == 's') {
+
+ /* we don't support : step vcpuid. user must switch to the
+ * thread/vcpu and then do step */
+ bufp++;
+ if (*bufp != '\0')
+ goto errout;
+ xg_step(current_vcpu, guest_bitness);
+ }
+#endif
+ return;
+
+errout:
+ savbufp[0] = '\0';
+ gxprt("WARN: Bad v pkt: %s\n", savbufp);
+ return;
+}
+
+static void
+process_v_request(char *remote_buf)
+{
+ if (strncmp(remote_buf, "vCont;", 6) == 0) {
+ process_v_cont_request(remote_buf); /* valid request */
+ return;
+ }
+ if (strncmp(remote_buf, "vCont?", 6) == 0) {
+ /* tell remote gdb what we support : c and s */
+ /* strcpy(remote_buf, "vCont;c;s"); */
+ remote_buf[0] = '\0';
+ return;
+ }
+ /* failed to understand the v packet */
+ remote_buf[0] = '\0';
+ return;
+}
+
+/* TBD: add watchpoint in future */
+static int
+watchpoint_stop(void)
+{
+ return 0;
+}
+
+#if 0
+static char *
+copy_mini_context32(char *rbuf, union xg_gdb_regs32 *regsp)
+{
+ *rbuf++ = gx_tohex((EBP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(EBP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->ebp, rbuf, 4);
+
+ *rbuf++ = gx_tohex((ESP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(ESP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->esp, rbuf, 4);
+
+ *rbuf++ = gx_tohex((EIP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(EIP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->eip, rbuf, 4);
+
+ return rbuf;
+}
+
+static char *
+copy_mini_context64(char *rbuf, union xg_gdb_regs64 *regsp)
+{
+ *rbuf++ = gx_tohex((RBP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(RBP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->ebp, rbuf, 4);
+
+ *rbuf++ = gx_tohex((RSP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(RSP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->esp, rbuf, 4);
+
+ *rbuf++ = gx_tohex((RIP_IDX >> 4) & 0xf);
+ *rbuf++ = gx_tohex(RIP_IDX & 0xf);
+ *rbuf++ = ':';
+ rbuf = gx_convert_int_to_ascii(regsp->eip, rbuf, 4);
+
+ return rbuf;
+}
+
+static char *
+copy_mini_context(char *rbuf)
+{
+ union xg_gdb_regs regs;
+
+ if (xg_regs_read(XG_GPRS, 0, ®s, guest_bitness)) {
+ gxprt("WARN: Unable to get read regs. errno:%d\n", errno);
+ return;
+ }
+ if (guest_bitness == 32)
+ rbuf = copy_mini_context32(rbuf, ®s.u.gregs_32);
+ else
+ rbuf = copy_mini_context64(rbuf, ®s.u.gregs_64);
+ return rbuf;
+}
+
+#endif
+
+/*
+ * prepare reply for remote gdb as to why we stopped
+ */
+static void
+prepare_stop_reply(enum target_signal sig, char *buf, vcpuid_t vcpu)
+{
+ int nib;
+
+ *buf++ = 'T'; /* we stopped because of a trap (SIGTRAP) */
+
+ nib = ((sig & 0xf0) >> 4);
+ *buf++ = gx_tohex(nib);
+ nib = sig & 0x0f;
+ *buf++ = gx_tohex(nib);
+
+ /* TBD: check if we stopped because of watchpoint */
+ if (watchpoint_stop()) {
+ strncpy(buf, "watch:", 6);
+ buf += 6;
+ /* TBD: **/
+ }
+ sprintf(buf, "thread:%x;", vcpu);
+ buf += strlen(buf);
+ *buf++ = '\0';
+}
+/*
+ * Indicate the reason the guest halted
+ */
+static void
+process_reas_request(char *remote_buf, vcpuid_t vcpu)
+{
+ prepare_stop_reply(TARGET_SIGNAL_TRAP, remote_buf, vcpu);
+}
+
+/* continue request */
+static void
+process_c_request(char *remote_buf)
+{
+ enum target_signal sig;
+
+ if ((current_vcpu=xg_resume_n_wait(guest_bitness)) == -1) {
+ current_vcpu = 0; /* default vcpu */
+ sig = TARGET_SIGNAL_INT;
+ } else
+ sig = TARGET_SIGNAL_TRAP;
+
+ prepare_stop_reply(sig, remote_buf, current_vcpu);
+}
+
+#if 0
+/* insert a bp: Z#,addr,len : where # is 0 for software bp, 1 for hardware bp,
+ * 2 is a write watchpoint, 3 is read watchpoint, 4 access watchpt
+ * We ignore len, it should always be 1.
+ * Eg: Z0,c0267d3a,1
+ */
+static void
+process_Z_request(char *rbuf)
+{
+ char ch1 = rbuf[1];
+ uint64_t gva;
+
+ if (ch1 != '0') {
+ gx_reply_error(rbuf);
+ return;
+ }
+ gx_decode_zZ_packet(&rbuf[3], &gva);
+ if (xg_set_bp(gva, ch1))
+ gx_reply_error(rbuf);
+ else
+ gx_reply_ok(rbuf);
+}
+
+/* remove a bp */
+static void
+process_z_request(char *rbuf)
+{
+ char ch1 = rbuf[1];
+ uint64_t gva;
+
+ if (ch1 != '0') {
+ gx_reply_error(rbuf);
+ return;
+ }
+ gx_decode_zZ_packet(&rbuf[3], &gva);
+ if (xg_rm_bp(gva, ch1))
+ gx_reply_error(rbuf);
+ else
+ gx_reply_ok(rbuf);
+}
+#endif
+
+static int
+process_remote_request(char *remote_buf) /* buffer received from remote gdb
*/
+{
+ char ch;
+ int rc=0, i=0;
+
+ XGTRC("E:%s curvcpu:%d\n", remote_buf, current_vcpu);
+
+ ch = remote_buf[i++];
+ switch(ch)
+ {
+ case 'q':
+ process_q_request(remote_buf);
+ break;
+
+ case 'd': /* print debug trace output */
+ gx_remote_dbg = !gx_remote_dbg;
+ printf("WARN: received d pkt:%s\n", remote_buf);
+ remote_buf[0] = '\0';
+ break;
+
+ case 'D':
+ gx_reply_ok(remote_buf);
+ rc = 1;
+ break;
+
+ case '?':
+ process_reas_request(remote_buf, 0);
+ break;
+
+ case 'H':
+ process_H_request(remote_buf);
+ break;
+
+ /* send general registers to remote gdb */
+ case 'g':
+ assert(current_vcpu != -1);
+ gx_read_guest_regs(remote_buf);
+ break;
+
+ /* receive general regs from remote gdb */
+ case 'G':
+ assert(current_vcpu != -1);
+ gx_write_guest_regs(remote_buf);
+ break;
+
+ /* read guest memory and send to remote gdb */
+ case 'm':
+ process_m_request(remote_buf);
+ break;
+
+ case 'M':
+ process_M_request(remote_buf);
+ break;
+
+ case 'C':
+ printf("WARN: C pkt: %s\n", remote_buf);
+ remote_buf[0] = '\0';
+ break;
+
+ case 'S':
+ printf("WARN: S pkt:%s\n", remote_buf);
+ remote_buf[0] = '\0';
+ break;
+
+ case 'c':
+ process_c_request(remote_buf); /* continue request */
+ break;
+
+ case 's': /* single step */
+ if (xg_step(current_vcpu, guest_bitness) != 0) {
+ remote_buf[0] = '\0';
+ } else {
+ prepare_stop_reply(TARGET_SIGNAL_TRAP, remote_buf,
+ current_vcpu);
+ }
+ break;
+
+#if 0
+ case 'Z':
+ process_Z_request(remote_buf); /* insert a bp */
+ break;
+
+ case 'z':
+ process_z_request(remote_buf); /* remove a bp */
+ break;
+#endif
+ case 'k': /* kill inferior */
+ printf("WARN: k pkt:%s\n", remote_buf);
+ remote_buf[0] = '\0';
+ break;
+
+ case 'T': /* find out if thread is alive */
+ gx_reply_ok(remote_buf); /* no vcpu offling supported yet */
+ break;
+
+ case 'R': /* TBD: restart gdbserver program */
+ /* Restarting the inferior is only supported in the
+ * extended protocol. */
+ remote_buf[0] = '\0';
+ break;
+
+ case 'v':
+ process_v_request(remote_buf);
+ break;
+
+ default:
+ /* It is a request we don't understand. Respond with an
+ * empty packet so that gdb knows that we don't support this
+ * request. */
+ remote_buf[0] = '\0';
+ break;
+
+ } /* end of switch(ch) */
+
+ XGTRC("X:%s curvcpu:%d\n", remote_buf, current_vcpu);
+ return rc;
+}
+
+static void
+gdbsx_usage_exit(void)
+{
+ printf ("Usage 1: gdbsx -a domid <32|64> PORT [-d]\n"
+ " PORT to listen for a TCP connection.\n"
+ " Eg. gdbsx -a 3 32 9999\n\n");
+ printf("Usage 2: gdbsx -c domid <32|64> [vcpu#] [-d]\n");
+ printf(" to dump vcpu context(s) for given domid\n\n");
+ exit(1);
+}
+
+static void
+check_usage_n_stuff(int argc, char **argv, domid_t *domid_p, vcpuid_t *vp)
+{
+ char *arg_end;
+
+ if (strcmp(argv[argc-1], "-d")==0) {
+ xgtrc_on = 1; /* debug trace on */
+ argc--;
+ }
+ if (argc < 4 || (strcmp(argv[1], "-h") == 0) ||
+ (strcmp(argv[1], "-a")==0 && argc < 5)) {
+ gdbsx_usage_exit();
+ }
+ if (argc > 5 ||
+ (*domid_p=strtoul(argv[2], &arg_end, 10)) == 0 ||
+ *arg_end != '\0' ||
+ *domid_p == 0 ||
+ (guest_bitness=strtoul(argv[3], &arg_end, 10)) == 0 ||
+ *arg_end != '\0' ||
+ (guest_bitness != 32 && guest_bitness != 64)) {
+
+ gdbsx_usage_exit();
+ }
+ *vp = -1; /* assume all VCPUs */
+ if (strcmp(argv[1], "-c")==0 && argc >= 5) {
+ *vp = strtoul(argv[4], &arg_end, 10);
+ if (*arg_end != '\0') {
+ gdbsx_usage_exit();
+ }
+ }
+}
+
+static void
+initialize(char **rbufpp)
+{
+ #define BUFSIZE 4096
+
+ /* allocate buffer used to communicate back and forth with remote gdb
*/
+ /* size should be big enough to hold all registers + extra */
+ if ((*rbufpp=malloc(BUFSIZE)) == NULL) {
+ gxprt("ERROR: can't malloc %d bytes. errno:%d\n",
+ BUFSIZE, errno);
+ exit(3);
+ }
+ signal(SIGIO, SIG_IGN); /* default action is TERM */
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char *remote_buf;
+ domid_t domid = 0;
+ vcpuid_t vcpuid;
+ int exit_rc = 0;
+
+ check_usage_n_stuff(argc, argv, &domid, &vcpuid);
+
+ if (xg_init() == -1) {
+ gxprt("ERROR: failed to initialize errno:%d\n", errno);
+ exit(1);
+ }
+ if ((max_vcpuid=xg_attach(domid, guest_bitness)) == -1) {
+ gxprt("ERROR: failed to attach to domain:%d errno:%d\n",
+ domid, errno);
+ exit(1);
+ }
+ if (strcmp(argv[1], "-c")==0) {
+ if (vcpuid != -1 && vcpuid > max_vcpuid) { /* just got set
*/
+ printf("gdbsx: Invalid VCPU id:%d\n", vcpuid);
+ xg_detach_deinit();
+ gdbsx_usage_exit();
+ }
+ exit_rc = gx_local_cmd(domid, vcpuid);
+ xg_detach_deinit();
+ return exit_rc; /* EXIT */
+ }
+
+ initialize(&remote_buf);
+
+ /* we have the guest paused at this point, ready for debug. wait for
+ * connection from remote gdb */
+ if (gx_remote_open(argv[4]) == -1) {
+ xg_detach_deinit();
+ return 1;
+ }
+
+ /* we've a gdb connection at this point, process requests */
+ while(gx_getpkt(remote_buf) > 0) {
+ if ((exit_rc=process_remote_request(remote_buf)))
+ break;
+ if (gx_putpkt(remote_buf) == -1) {
+ exit_rc = 1;
+ break;
+ }
+ }
+ /* unpause and let the guest continue */
+ gxprt("Detaching from guest\n");
+ xg_detach_deinit();
+
+ if (exit_rc == 0) {
+ gxprt("Exiting.. Remote side has terminated connection\n");
+ }
+ gx_remote_close();
+ return exit_rc;
+}
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/gx_utils.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/gx_utils.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "gx.h"
+
+
+void
+gxprt(const char *fmt, ...)
+{
+ char buf[2048];
+ va_list args;
+
+ va_start(args, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ fprintf(stderr, "%s", buf);
+ fflush(stderr);
+}
+
+int
+gx_fromhex(int a)
+{
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ else if (a >= 'a' && a <= 'f')
+ return a - 'a' + 10;
+ else
+ gxprt("Reply contains invalid hex digit");
+ return 0;
+}
+
+int
+gx_tohex(int nib)
+{
+ if (nib < 10)
+ return '0' + nib;
+ else
+ return 'a' + nib - 10;
+}
+
+
+void
+gx_convert_int_to_ascii(char *from, char *to, int n)
+{
+ int nib;
+ int ch;
+ while (n--) {
+ ch = *from++;
+ nib = ((ch & 0xf0) >> 4) & 0x0f;
+ *to++ = gx_tohex(nib);
+ nib = ch & 0x0f;
+ *to++ = gx_tohex(nib);
+ }
+ *to = 0;
+}
+
+/* input: "70676433206431" output: "pgd3 d1" n == 7 */
+void
+gx_convert_ascii_to_int(char *from, char *to, int n)
+{
+ int nib1, nib2;
+ while (n--) {
+ nib1 = gx_fromhex(*from++);
+ nib2 = gx_fromhex(*from++);
+ *to++ = (((nib1 & 0x0f) << 4) & 0xf0) | (nib2 & 0x0f);
+ }
+ *to = 0;
+}
+
+void
+gx_decode_zZ_packet(char *from, uint64_t *mem_addr_ptr)
+{
+ int i = 0;
+ char ch;
+ *mem_addr_ptr = 0;
+
+ while ((ch=from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= gx_fromhex(ch) & 0x0f;
+ }
+}
+
+/* Eg: mc0267d3a,1\0 : from points to char after 'm' */
+void
+gx_decode_m_packet(char *from, uint64_t *mem_addr_ptr, int *len_ptr)
+{
+ int i = 0, j = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch=from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= gx_fromhex(ch) & 0x0f;
+ }
+ for (j = 0; j < 4; j++) {
+ if ((ch=from[i++]) == 0)
+ break;
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= gx_fromhex(ch) & 0x0f;
+ }
+}
+
+/*
+ * Decode M pkt as in: Mc0267d3a,1:cc\0 where c0267d3a is the guest addr
+ * from points to char after 'M'
+ * Returns: address of byte after ":", ie, addr of cc in buf
+ */
+char *
+gx_decode_M_packet(char *from, uint64_t *mem_addr_ptr, int *len_ptr)
+{
+ int i = 0;
+ char ch;
+
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch=from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= gx_fromhex(ch) & 0x0f;
+ }
+ while ((ch = from[i++]) != ':') {
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= gx_fromhex(ch) & 0x0f;
+ }
+ return(&from[i]);
+}
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/gx/xg_dummy.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/gx/xg_dummy.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "gx.h"
+#include "../xg/xg_public.h"
+
+int xgtrc_on = 1;
+
+/* This file is NOT part of gdbsx binary, but a dummy so gdbsx bin can be built
+ * and used to learn/test "gdb <-> gdbserver" protocol */
+
+void
+xgtrc(const char *fn, const char *fmt, ...)
+{
+ char buf[2048];
+ va_list args;
+
+ fprintf(stderr, "%s:", fn);
+ va_start(args, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ fprintf(stderr, "%s", buf);
+ fflush (stderr);
+}
+
+void
+xgprt(const char *fn, const char *fmt, ...)
+{
+ char buf[2048];
+ va_list args;
+
+ fprintf(stderr, "%s:", fn);
+ va_start(args, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ fprintf (stderr, "%s", buf);
+ fflush (stderr);
+}
+
+int
+xg_init()
+{
+ return 0;
+}
+
+void
+xg_deinit()
+{
+ /* reset debugging info in guest */
+ /* unpause the guest */
+}
+
+int
+xg_attach(domid_t domid)
+{
+ return 2;
+}
+
+int
+xg_step(vcpuid_t which_vcpu, int guest_bitness)
+{
+ return 0;
+}
+
+int
+xg_resume(int guest_bitness)
+{
+ return 0;
+}
+
+int
+xg_regs_read(regstype_t which_regs, vcpuid_t which_vcpu,
+ struct xg_gdb_regs *regsp, int guest_bitness)
+{
+ return 0;
+}
+
+int
+xg_regs_write(regstype_t which_regs, vcpuid_t which_vcpu,
+ struct xg_gdb_regs *regsp, int guest_bitness)
+{
+ return 0;
+}
+
+int
+xg_read_mem(uint64_t guestva, char *tobuf, int len)
+{
+ return 0;
+}
+
+int
+xg_write_mem(uint64_t guestva, char *frombuf, int len)
+{
+ return 0;
+}
+
+int
+xg_wait_domain(vcpuid_t *vcpu_p, int guest_bitness)
+{
+ return 0;
+}
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/xg/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/xg/Makefile Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,36 @@
+XEN_ROOT = ../../../..
+include ../Rules.mk
+
+XG_HDRS := xg_public.h
+XG_OBJS := xg_main.o
+
+CFLAGS += $(INCLUDES) -I. -I../../../include
+
+
+.PHONY: all
+all: build
+
+.PHONY: build
+build: xen-headers xg_all.a $(XG_HDRS) $(XG_OBJS) Makefile
+# build: mk-symlinks xg_all.a $(XG_HDRS) $(XG_OBJS) Makefile
+# build: mk-symlinks xg_all.a
+
+xg_all.a: $(XG_OBJS) Makefile $(XG_HDRS)
+ ar cr $@ $(XG_OBJS) # problems using -m32 in ld
+# $(LD) -b elf32-i386 $(LDFLAGS) -r -o $@ $^
+# $(CC) -m32 -c -o $@ $^
+
+xen-headers:
+ $(MAKE) -C ../../../check
+ $(MAKE) -C ../../../include
+
+# xg_main.o: xg_main.c Makefile $(XG_HDRS)
+#$(CC) -c $(CFLAGS) -o $@ $<
+
+# %.o: %.c $(XG_HDRS) Makefile -- doesn't work as it won't overwrite Rules.mk
+#%.o: %.c -- doesn't recompile when .c changed
+
+.PHONY: clean
+clean:
+ rm -rf xen xg_all.a $(XG_OBJS)
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/xg/xg_main.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/xg/xg_main.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,799 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+/* This is the main module to interface with xen. This module exports APIs that
+ * allow for creating any remote debugger plugin. The APIs are:
+ *
+ * xg_init() : initialize
+ * xg_attach(): attach to the given guest, preparing it for debug
+ * xg_detach_deinit(): exit debugging
+ *
+ * xg_step() : single step the the given vcpu
+ * xg_resume_n_wait(): resume the target guest and wait for any debug event
+ * xg_regs_read(): read context of given vcpu
+ * xg_regs_write(): write context of given vcpu
+ * xg_read_mem(): read memory of guest at given VA
+ * xg_write_mem(): write memory of guest at given VA
+ *
+ * XGERR(): generic print error utility
+ * XGTRC(): generic trace utility
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "xg_public.h"
+#include <xen/version.h>
+#include <xen/domctl.h>
+#include <xen/sys/privcmd.h>
+#include <xen/foreign/x86_32.h>
+#include <xen/foreign/x86_64.h>
+
+#define XGMIN(x,y) (((x)<(y))?(x):(y))
+
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+
+
+/*
+ * Contexts returned by xen: (gdbsx : dom 0 : hypervisor)
+ *
+ * 32 : 32 : 32 => 64bit context never returned. can't run 64bit guests
+ * 32 : 32 : 64 => 32bit ctxt for 32bit PV guest. 64bit ctxt for 64 PV
guests.
+ * HVM always 64bit ctxt.
+ * 32 : 64 : 64 => N/A
+ * 64 : 64 : 64 => Same as 32:32:64 (CONFIG_COMPAT is almost always defined)
+ * 64 : 64 : 64 => !CONFIG_COMPAT : not supported.
+ */
+typedef union vcpu_guest_context_any {
+ vcpu_guest_context_x86_64_t ctxt64;
+ vcpu_guest_context_x86_32_t ctxt32;
+ vcpu_guest_context_t ctxt;
+} vcpu_guest_context_any_t;
+
+
+int xgtrc_on = 0;
+struct xen_domctl domctl; /* just use a global domctl */
+
+static int _hvm_guest; /* hvm guest? 32bit HVMs have 64bit context
*/
+static domid_t _dom_id; /* guest domid */
+static int _max_vcpu_id; /* thus max_vcpu_id+1 VCPUs */
+static int _dom0_fd; /* fd of /dev/privcmd */
+static int _32bit_hyp; /* hyp is 32bit */
+
+
+/* print trace info with function name pre-pended */
+void
+xgtrc(const char *fn, const char *fmt, ...)
+{
+ char buf[2048];
+ va_list args;
+
+ fprintf(stderr, "%s:", fn);
+ va_start(args, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ fprintf(stderr, "%s", buf);
+ fflush (stderr);
+}
+
+/* print error msg with function name pre-pended */
+void
+xgprt(const char *fn, const char *fmt, ...)
+{
+ char buf[2048];
+ va_list args;
+
+ fprintf(stderr, "ERROR:%s:", fn);
+ va_start(args, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ fprintf (stderr, "%s", buf);
+ fflush (stderr);
+}
+
+
+/*
+ * Returns: 0 success
+ * -1 failure, errno set.
+ */
+int
+xg_init()
+{
+ int flags, saved_errno;
+
+ XGTRC("E\n");
+ if ((_dom0_fd=open("/proc/xen/privcmd", O_RDWR)) == -1) {
+ perror("Failed to open /proc/xen/privcmd\n");
+ return -1;
+ }
+ /* Although we return the file handle as the 'xc handle' the API
+ * does not specify / guarentee that this integer is in fact
+ * a file handle. Thus we must take responsiblity to ensure
+ * it doesn't propagate (ie leak) outside the process (copied
comment)*/
+ if ( (flags=fcntl(_dom0_fd, F_GETFD)) < 0 ) {
+ perror("Could not get file handle flags (F_GETFD)");
+ goto error;
+ }
+ flags |= FD_CLOEXEC;
+ if (fcntl(_dom0_fd, F_SETFD, flags) < 0) {
+ perror("Could not set file handle flags");
+ goto error;
+ }
+
+ XGTRC("X:fd:%d\n", _dom0_fd);
+ return _dom0_fd;
+
+ error:
+ XGTRC("X:Error: errno:%d\n", errno);
+ saved_errno = errno;
+ close(_dom0_fd);
+ errno = saved_errno;
+ return -1;
+}
+
+
+/*
+ * Returns : 0 Success, failure otherwise with errno set
+ */
+static int
+_domctl_hcall(uint32_t cmd, /* which domctl hypercall */
+ void *domctlarg, /* arg/buf to domctl to pin in mem */
+ int sz) /* size of *domctlarg */
+{
+ privcmd_hypercall_t hypercall;
+ int rc;
+
+ if (domctlarg && sz && mlock(domctlarg, sz)) {
+ XGERR("Unable to pin domctl arg. p:%p sz:%d errno:%d\n",
+ domctlarg, sz, errno);
+ return 1;
+ }
+ domctl.cmd = cmd;
+ hypercall.op = __HYPERVISOR_domctl;
+ hypercall.arg[0] = (unsigned long)&domctl;
+
+ rc = ioctl(_dom0_fd, IOCTL_PRIVCMD_HYPERCALL, (ulong)&hypercall);
+ if (domctlarg && sz)
+ munlock(domctlarg, sz);
+ return rc;
+}
+
+/*
+ * Make sure we are running on hyp enabled for gdbsx. Also, note whether
+ * its 32bit. Fail if user typed 64bit for guest in case of 32bit hyp.
+ *
+ * RETURNS: 0 : everything OK.
+ */
+static int
+_check_hyp(int guest_bitness)
+{
+ xen_capabilities_info_t xen_caps = "";
+ privcmd_hypercall_t hypercall;
+ int rc;
+
+ if (mlock(&xen_caps, sizeof(xen_caps))) {
+ XGERR("Unable to pin xen_caps in memory. errno:%d\n", errno);
+ return -1;
+ }
+ memset(&xen_caps, 0, sizeof(xen_caps));
+
+ hypercall.op = __HYPERVISOR_xen_version;
+ hypercall.arg[0] = (unsigned long)XENVER_capabilities;
+ hypercall.arg[1] = (unsigned long)&xen_caps;
+
+ rc = ioctl(_dom0_fd, IOCTL_PRIVCMD_HYPERCALL, (ulong)&hypercall);
+ munlock(&xen_caps, sizeof(xen_caps));
+ XGTRC("XENCAPS:%s\n", xen_caps);
+
+ if (rc != 0) {
+ XGERR("Failed xen_version hcall. errno:%d\n", errno);
+ return -1;
+ }
+ if (strstr(xen_caps, "gdbsx") == NULL) {
+ XGERR("Hyp is NOT enabled for gdbsx\n");
+ return -1;
+ }
+
+ _32bit_hyp = (strstr(xen_caps, "x86_64") == NULL);
+ if (_32bit_hyp && guest_bitness !=32) {
+ XGERR("32bit hyp can only run 32bit guests\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* check if domain is alive and well
+ * returns : 0 if domain is not alive and well
+ */
+static int
+_domain_ok(struct xen_domctl_getdomaininfo *domp)
+{
+ int rc = 0;
+ if (domp->flags & XEN_DOMINF_dying)
+ XGERR("Invalid domain (state dying)...\n");
+ else
+ rc = 1;
+ return rc;
+}
+
+/* Returns: 0 : success */
+static int
+_unpause_domain(void)
+{
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ if (_domctl_hcall(XEN_DOMCTL_unpausedomain, NULL, 0)) {
+ XGERR("Unable to unpause domain:%d errno:%d\n", _dom_id,
errno);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Attach to the given domid for debugging.
+ * Returns: max vcpu id : Success
+ * -1 : Failure
+ */
+int
+xg_attach(int domid, int guest_bitness)
+{
+ XGTRC("E:domid:%d\n", domid);
+
+ _dom_id = domctl.domain = domid;
+ domctl.interface_version = XEN_DOMCTL_INTERFACE_VERSION;
+
+ if (mlock(&domctl, sizeof(domctl))) {
+ XGERR("Unable to pin domctl in memory. errno:%d\n", errno);
+ return -1;
+ }
+ if (_check_hyp(guest_bitness))
+ return -1;
+
+ if (_domctl_hcall(XEN_DOMCTL_pausedomain, NULL, 0)) {
+ XGERR("Unable to pause domain:%d\n", _dom_id);
+ return -1;
+ }
+
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ domctl.u.setdebugging.enable = 1;
+ if (_domctl_hcall(XEN_DOMCTL_setdebugging, NULL, 0)) {
+ XGERR("Unable to set domain to debug mode: errno:%d\n", errno);
+ _unpause_domain();
+ return -1;
+ }
+
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ if (_domctl_hcall(XEN_DOMCTL_getdomaininfo, NULL, 0)) {
+ XGERR("Unable to get domain info: domid:%d errno:%d\n",
+ domid, errno);
+ _unpause_domain();
+ return -1;
+ }
+ if (!_domain_ok(&domctl.u.getdomaininfo)) {
+ _unpause_domain();
+ return -1;
+ }
+
+ _max_vcpu_id = domctl.u.getdomaininfo.max_vcpu_id;
+ _hvm_guest = (domctl.u.getdomaininfo.flags & XEN_DOMINF_hvm_guest);
+ return _max_vcpu_id;
+}
+
+
+/* Returns: 1 : domain is paused. 0 otherwise */
+static int
+_domain_is_paused(void)
+{
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ if (_domctl_hcall(XEN_DOMCTL_getdomaininfo, NULL, 0)) {
+ XGERR("ERROR: Unable to get domain paused info:%d\n", _dom_id);
+ return 0;
+ }
+ return (domctl.u.getdomaininfo.flags & XEN_DOMINF_paused);
+}
+
+/* Detach from guest for debugger exit */
+void
+xg_detach_deinit(void)
+{
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ domctl.u.setdebugging.enable = 0;
+ if (_domctl_hcall(XEN_DOMCTL_setdebugging, NULL, 0)) {
+ XGERR("Unable to reset domain debug mode: errno:%d\n", errno);
+ }
+ if (_domain_is_paused())
+ _unpause_domain();
+
+ close(_dom0_fd);
+}
+
+/*
+ * Returns : 0 success.
+ * 1 error, with errno set (hopefully :))
+ */
+static int
+_wait_domain_pause(void)
+{
+ int dom_paused;
+ struct timespec ts={0, 10*1000*1000};
+
+ XGTRC("E:\n");
+ do {
+ dom_paused = _domain_is_paused();
+ nanosleep(&ts, NULL);
+ } while(!dom_paused);
+ return 0;
+}
+
+/*
+ * Change the TF flag for single step. TF = (setit ? 1 : 0);
+ * Returns: 0 Success
+ */
+static int
+_change_TF(vcpuid_t which_vcpu, int guest_bitness, int setit)
+{
+ union vcpu_guest_context_any anyc;
+ int sz = sizeof(anyc);
+
+ memset(&anyc, 0, sz);
+ domctl.u.vcpucontext.vcpu = (uint16_t)which_vcpu;
+ set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &anyc.ctxt);
+
+ if (_domctl_hcall(XEN_DOMCTL_getvcpucontext, &anyc, sz)) {
+ XGERR("Failed hcall to get vcpu ctxt for TF.
errno:%d\n",errno);
+ return 1;
+ }
+ if (_32bit_hyp || (guest_bitness == 32 && !_hvm_guest)) {
+ if (setit)
+ anyc.ctxt32.user_regs.eflags |= X86_EFLAGS_TF;
+ else
+ anyc.ctxt32.user_regs.eflags &= ~X86_EFLAGS_TF;
+ } else {
+ if (setit)
+ anyc.ctxt64.user_regs.rflags |= X86_EFLAGS_TF;
+ else
+ anyc.ctxt64.user_regs.rflags &= ~X86_EFLAGS_TF;
+ }
+
+ if (_domctl_hcall(XEN_DOMCTL_setvcpucontext, &anyc, sz)) {
+ XGERR("Failed hcall to set vcpu ctxt for TF.
errno:%d\n",errno);
+ return 1;
+ }
+ return 0;
+}
+
+/* Do the given DOMCTL hcall action(pause or unpause) on all but the given vcpu
+ * Returns: 0 success */
+static int
+_allbutone_vcpu(uint32_t hcall, vcpuid_t which_vcpu)
+{
+ int i;
+ for (i=0; i <= _max_vcpu_id; i++) {
+ if (i == which_vcpu)
+ continue;
+
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ domctl.u.gdbsx_pauseunp_vcpu.gdbsx_vcpu = i;
+ if (_domctl_hcall(hcall, NULL, 0)) {
+ XGERR("Unable to do:%d vcpu:%d errno:%d\n",
+ hcall, i, errno);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Single step the given vcpu. This is achieved by pausing all but given vcpus,
+ * setting the TF flag, let the domain run and pause, unpause all vcpus, and
+ * clear TF flag on given vcpu.
+ * Returns: 0 success
+ */
+int
+xg_step(vcpuid_t which_vcpu, int guest_bitness)
+{
+ int rc;
+
+ XGTRC("E:vcpu:%d\n", (int)which_vcpu);
+
+ if (_allbutone_vcpu(XEN_DOMCTL_pausevcpu, which_vcpu))
+ return 1;
+
+ if ((rc=_change_TF(which_vcpu, guest_bitness, 1)))
+ return rc;
+
+ XGTRC("unpausing domain\n");
+
+ /* now unpause the domain so our vcpu can execute */
+ if (_unpause_domain())
+ return 1;
+
+ /* wait for our vcpu to finish step */
+ _wait_domain_pause();
+
+ _allbutone_vcpu(XEN_DOMCTL_unpausevcpu, which_vcpu);
+ rc = _change_TF(which_vcpu, guest_bitness, 0);
+
+ return rc;
+}
+
+/*
+ * check if any one of the vcpus is in a breakpoint
+ * Returns: vcpuid : if a vcpu found in a bp
+ * -1 : otherwise
+ */
+static vcpuid_t
+_vcpu_in_bp(void)
+{
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ if (_domctl_hcall(XEN_DOMCTL_gdbsx_domstatus, NULL, 0)) {
+ XGERR("ERROR: Unable to check vcpu bp status:%d errno:%d\n",
+ _dom_id, errno);
+ return -1;
+ }
+ return domctl.u.gdbsx_domstatus.gdbsx_vcpu_id;
+}
+
+/*
+ * Resume the domain if no pending events. If there are pending events, like
+ * another vcpu in a BP, report it. Otherwise, continue, and wait till an
+ * event, like bp or user doing xm pause, occurs.
+ *
+ * Returns: vcpuid : if a vcpu hits a breakpoint or end of step
+ * -1 : either an error (msg printed on terminal), or non-bp
+ * event, like "xm pause domid", to enter debugger
+ */
+vcpuid_t
+xg_resume_n_wait(int guest_bitness)
+{
+ vcpuid_t vcpu;
+
+ XGTRC("E:\n");
+ assert(_domain_is_paused());
+
+ if ((vcpu=_vcpu_in_bp()) != -1) {
+ /* another vcpu in breakpoint. return it's id */
+ return vcpu;
+ }
+ XGTRC("unpausing domain\n");
+ if (_unpause_domain())
+ return -1;
+
+ /* now wait for domain to pause */
+ _wait_domain_pause();
+
+ /* check again if any vcpu in BP, or user thru "xm pause" */
+ vcpu = _vcpu_in_bp();
+
+ XGTRC("X:vcpu:%d\n", vcpu);
+ return vcpu;
+}
+
+static void
+_cp_32ctxt_to_32gdb(struct cpu_user_regs_x86_32 *cp, struct xg_gdb_regs32 *rp)
+{
+ memset(rp, 0, sizeof(struct xg_gdb_regs32));
+ rp->ebx = cp->ebx;
+ rp->ecx = cp->ecx;
+ rp->edx = cp->edx;
+ rp->esi = cp->esi;
+ rp->edi = cp->edi;
+ rp->ebp = cp->ebp;
+ rp->eax = cp->eax;
+ rp->eip = cp->eip;
+ rp->cs = cp->cs;
+ rp->eflags = cp->eflags;
+ rp->esp = cp->esp;
+ rp->ss = cp->ss;
+ rp->es = cp->es;
+ rp->ds = cp->ds;
+ rp->fs = cp->fs;
+ rp->gs = cp->gs;
+}
+
+static void
+_cp_64ctxt_to_32gdb(struct cpu_user_regs_x86_64 *cp, struct xg_gdb_regs32 *rp)
+{
+ memset(rp, 0, sizeof(struct xg_gdb_regs32));
+ rp->ebx = cp->rbx;
+ rp->ecx = cp->rcx;
+ rp->edx = cp->rdx;
+ rp->esi = cp->rsi;
+ rp->edi = cp->rdi;
+ rp->ebp = cp->rbp;
+ rp->eax = cp->rax;
+ rp->eip = cp->rip;
+ rp->cs = cp->cs;
+ rp->eflags = cp->rflags;
+ rp->esp = cp->rsp;
+ rp->ss = cp->ss;
+ rp->es = cp->es;
+ rp->ds = cp->ds;
+ rp->fs = cp->fs;
+ rp->gs = cp->gs;
+}
+
+static void
+_cp_64ctxt_to_64gdb(struct cpu_user_regs_x86_64 *cp, struct xg_gdb_regs64 *rp)
+{
+ memset(rp, 0, sizeof(struct xg_gdb_regs64));
+ rp->r8 = cp->r8;
+ rp->r9 = cp->r9;
+ rp->r10 = cp->r10;
+ rp->r11 = cp->r11;
+ rp->r12 = cp->r12;
+ rp->r13 = cp->r13;
+ rp->r14 = cp->r14;
+ rp->r15 = cp->r15;
+ rp->rbx = cp->rbx;
+ rp->rcx = cp->rcx;
+ rp->rdx = cp->rdx;
+ rp->rsi = cp->rsi;
+ rp->rdi = cp->rdi;
+ rp->rbp = cp->rbp;
+ rp->rax = cp->rax;
+ rp->rip = cp->rip;
+ rp->rsp = cp->rsp;
+ rp->rflags = cp->rflags;
+
+ rp->cs = (uint64_t)cp->cs;
+ rp->ss = (uint64_t)cp->ss;
+ rp->es = (uint64_t)cp->es;
+ rp->ds = (uint64_t)cp->ds;
+ rp->fs = (uint64_t)cp->fs;
+ rp->gs = (uint64_t)cp->gs;
+#if 0
+ printf("cp:%llx bp:%llx rip:%llx\n", rp->rsp, rp->rbp, rp->rip);
+ printf("rax:%llx rbx:%llx\n", rp->rax, rp->rbx);
+ printf("cs:%04x ss:%04x ds:%04x\n", (int)rp->cs, (int)rp->ss,
+ (int)rp->ds);
+#endif
+}
+
+static void
+_cp_32gdb_to_32ctxt(struct xg_gdb_regs32 *rp, struct cpu_user_regs_x86_32 *cp)
+{
+ cp->ebx = rp->ebx;
+ cp->ecx = rp->ecx;
+ cp->edx = rp->edx;
+ cp->esi = rp->esi;
+ cp->edi = rp->edi;
+ cp->ebp = rp->ebp;
+ cp->eax = rp->eax;
+ cp->eip = rp->eip;
+ cp->esp = rp->esp;
+ cp->cs = rp->cs;
+ cp->ss = rp->ss;
+ cp->es = rp->es;
+ cp->ds = rp->ds;
+ cp->fs = rp->fs;
+ cp->gs = rp->gs;
+ cp->eflags = rp->eflags;
+}
+
+static void
+_cp_32gdb_to_64ctxt(struct xg_gdb_regs32 *rp, struct cpu_user_regs_x86_64 *cp)
+{
+ cp->rbx = rp->ebx;
+ cp->rcx = rp->ecx;
+ cp->rdx = rp->edx;
+ cp->rsi = rp->esi;
+ cp->rdi = rp->edi;
+ cp->rbp = rp->ebp;
+ cp->rax = rp->eax;
+ cp->rip = rp->eip;
+ cp->rsp = rp->esp;
+ cp->cs = rp->cs;
+ cp->ss = rp->ss;
+ cp->es = rp->es;
+ cp->ds = rp->ds;
+ cp->fs = rp->fs;
+ cp->gs = rp->gs;
+ cp->rflags = rp->eflags;
+}
+
+static void
+_cp_64gdb_to_64ctxt(struct xg_gdb_regs64 *rp, struct cpu_user_regs_x86_64 *cp)
+{
+ cp->r8 = rp->r8;
+ cp->r9 = rp->r9;
+ cp->r10 = rp->r10;
+ cp->r11 = rp->r11;
+ cp->r12 = rp->r12;
+ cp->r13 = rp->r13;
+ cp->r14 = rp->r14;
+ cp->r15 = rp->r15;
+ cp->rbx = rp->rbx;
+ cp->rcx = rp->rcx;
+ cp->rdx = rp->rdx;
+ cp->rsi = rp->rsi;
+ cp->rdi = rp->rdi;
+ cp->rbp = rp->rbp;
+ cp->rax = rp->rax;
+ cp->rip = rp->rip;
+ cp->rsp = rp->rsp;
+ cp->rflags = rp->rflags;
+
+ cp->cs = (uint16_t)rp->cs;
+ cp->ss = (uint16_t)rp->ss;
+ cp->es = (uint16_t)rp->es;
+ cp->ds = (uint16_t)rp->ds;
+ cp->fs = (uint16_t)rp->fs;
+ cp->gs = (uint16_t)rp->gs;
+}
+
+
+/* get vcpu context from xen and return it in *ctxtp
+ * RETURNS: 0 for success
+ */
+static int
+_get_vcpu_ctxt(vcpuid_t vcpu_id, union vcpu_guest_context_any *anycp)
+{
+ int sz = sizeof(union vcpu_guest_context_any);
+
+ memset(anycp, 0, sz);
+ domctl.u.vcpucontext.vcpu = (uint16_t)vcpu_id;
+ set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &anycp->ctxt);
+
+ if (_domctl_hcall(XEN_DOMCTL_getvcpucontext, anycp, sz)) {
+ XGERR("Failed hcall to get vcpu ctxt. errno:%d\n", errno);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * read regs for a particular vcpu. For now only GPRs, no FPRs.
+ * Returns: 0 success, else failure with errno set
+ */
+int
+xg_regs_read(regstype_t which_regs, vcpuid_t which_vcpu,
+ union xg_gdb_regs *regsp, int guest_bitness)
+{
+ union vcpu_guest_context_any anyc;
+ struct cpu_user_regs_x86_32 *cr32p = &anyc.ctxt32.user_regs;
+ struct cpu_user_regs_x86_64 *cr64p = &anyc.ctxt64.user_regs;
+ struct xg_gdb_regs32 *r32p = ®sp->gregs_32;
+ struct xg_gdb_regs64 *r64p = ®sp->gregs_64;
+ int rc;
+
+ if (which_regs != XG_GPRS) {
+ errno = EINVAL;
+ XGERR("regs got: %d. Expected GPRS:%d\n", which_regs, XG_GPRS);
+ return 1;
+ }
+ if ((rc=_get_vcpu_ctxt(which_vcpu, &anyc)))
+ return rc;
+
+ /* 64bit hyp: only 32bit PV returns 32bit context, all others 64bit.
+ * 32bit hyp: all contexts returned are 32bit */
+ if (guest_bitness == 32) {
+ if (_32bit_hyp || !_hvm_guest)
+ _cp_32ctxt_to_32gdb(cr32p, r32p);
+ else
+ _cp_64ctxt_to_32gdb(cr64p, r32p);
+ } else
+ _cp_64ctxt_to_64gdb(cr64p, r64p);
+
+ XGTRC("X:vcpu:%d bitness:%d rc:%d\n", which_vcpu, guest_bitness, rc);
+ return rc;
+}
+
+/*
+ * write registers for the given vcpu
+ * Returns: 0 success, 1 failure with errno
+ */
+int
+xg_regs_write(regstype_t which_regs, vcpuid_t which_vcpu,
+ union xg_gdb_regs *regsp, int guest_bitness)
+{
+ union vcpu_guest_context_any anyc;
+ struct cpu_user_regs_x86_32 *cr32p = &anyc.ctxt32.user_regs;
+ struct cpu_user_regs_x86_64 *cr64p = &anyc.ctxt64.user_regs;
+ struct xg_gdb_regs32 *r32p = ®sp->gregs_32;
+ struct xg_gdb_regs64 *r64p = ®sp->gregs_64;
+ int rc, sz = sizeof(anyc);
+
+ if (which_regs != XG_GPRS) {
+ errno = EINVAL;
+ XGERR("regs got: %d. Expected GPRS:%d\n", which_regs, XG_GPRS);
+ return 1;
+ }
+ if ((rc=_get_vcpu_ctxt(which_vcpu, &anyc)))
+ return rc;
+
+ if (guest_bitness == 32) {
+ if (_32bit_hyp || !_hvm_guest)
+ _cp_32gdb_to_32ctxt(r32p, cr32p);
+ else
+ _cp_32gdb_to_64ctxt(r32p, cr64p);
+ } else
+ _cp_64gdb_to_64ctxt(r64p, cr64p);
+
+ /* set vcpu context back */
+ if ((rc =_domctl_hcall(XEN_DOMCTL_setvcpucontext, &anyc, sz))) {
+ XGERR("Failed hcall to set vcpu ctxt. errno:%d\n", errno);
+ return rc;
+ }
+ XGTRC("X:vcpu:%d bitness:%d rc:%d\n", which_vcpu, guest_bitness, rc);
+ return rc;
+}
+
+/*
+ * Returns: bytes remaining to be read. 0 => read all bytes, ie, success.
+ */
+int
+xg_read_mem(uint64_t guestva, char *tobuf, int tobuf_len, uint64_t pgd3val)
+{
+ struct xen_domctl_memio *iop = &domctl.u.gdbsx_guest_memio;
+ union {uint64_t llbuf8; char buf8[8];} u = {0};
+ int i;
+
+ XGTRC("E:gva:%llx tobuf:%lx len:%d\n", guestva, tobuf, tobuf_len);
+
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ iop->gdbsx_pgd3val = pgd3val;
+ iop->gdbsx_gva = guestva;
+ iop->gdbsx_uva = (uint64_aligned_t)((unsigned long)tobuf);
+ iop->gdbsx_len = tobuf_len;
+ iop->gdbsx_gwr = 0; /* not writing to guest */
+
+ _domctl_hcall(XEN_DOMCTL_guestmemio, tobuf, tobuf_len);
+
+ for(i=0; i < XGMIN(8, tobuf_len); u.buf8[i]=tobuf[i], i++);
+ XGTRC("X:remain:%d buf8:0x%llx\n", iop->gdbsx_remain, u.llbuf8);
+
+ return iop->gdbsx_remain;
+}
+
+/*
+ * Returns: bytes that could not be written. 0 => wrote all bytes, ie, success.
+ */
+int
+xg_write_mem(uint64_t guestva, char *frombuf, int buflen, uint64_t pgd3val)
+{
+ struct xen_domctl_memio *iop = &domctl.u.gdbsx_guest_memio;
+ union {uint64_t llbuf8; char buf8[8];} u = {0};
+ int i, rc;
+
+ for(i=0; i < XGMIN(8, buflen); u.buf8[i]=frombuf[i], i++);
+ XGTRC("E:gva:%llx frombuf:%lx len:%d buf8:0x%llx\n", guestva, frombuf,
+ buflen, u.llbuf8);
+
+ memset(&domctl.u, 0, sizeof(domctl.u));
+ iop->gdbsx_pgd3val = pgd3val;
+ iop->gdbsx_gva = guestva;
+ iop->gdbsx_uva = (uint64_aligned_t)((unsigned long)frombuf);
+ iop->gdbsx_len = buflen;
+ iop->gdbsx_gwr = 1; /* writing to guest */
+
+ if ((rc=_domctl_hcall(XEN_DOMCTL_guestmemio, frombuf, buflen)))
+ XGERR("ERROR: failed to write %d bytes. errno:%d rc:%d\n",
+ iop->gdbsx_remain, errno, rc);
+ return iop->gdbsx_remain;
+}
+
diff -r 18758847bf31 -r 167e895ef770 tools/debugger/gdbsx/xg/xg_public.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/gdbsx/xg/xg_public.h Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#define XGERR(...) \
+ do {(xgprt(__FUNCTION__,__VA_ARGS__));} while (0)
+#define XGTRC(...) \
+ do {(xgtrc_on) ? (xgtrc(__FUNCTION__,__VA_ARGS__)):0;} while (0)
+#define XGTRC1(...) \
+ do {(xgtrc_on==2) ? (xgtrc(__FUNCTION__,__VA_ARGS__)):0;} while (0)
+
+#if defined(__x86_64__)
+ #define XGFM64 "%lx"
+ #define XGF64 "%016lx"
+#else
+ #define XGFM64 "%llx"
+ #define XGF64 "%016llx"
+#endif
+
+
+typedef enum {
+ XG_GPRS=1, /* general purpose user regs */
+ XG_FPRS=2, /* floating point user regs */
+} regstype_t;
+
+
+typedef uint32_t vcpuid_t;
+
+extern int xgtrc_on;
+
+/* what gdb wants to receive during register read, or sends during write.
+ * this from : regformats/reg-i386-linux.dat in gdbserver */
+struct xg_gdb_regs32 {
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+};
+
+/* this from: regformats/reg-x86-64.dat in gdbserver */
+struct xg_gdb_regs64 {
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t rbp;
+ uint64_t rsp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t ss;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+};
+
+union xg_gdb_regs {
+ struct xg_gdb_regs32 gregs_32;
+ struct xg_gdb_regs64 gregs_64;
+};
+
+
+int xg_init(void);
+int xg_attach(int, int);
+void xg_detach_deinit(void);
+int xg_step(vcpuid_t, int);
+vcpuid_t xg_resume_n_wait(int);
+int xg_regs_read(regstype_t, vcpuid_t, union xg_gdb_regs *, int);
+int xg_regs_write(regstype_t, vcpuid_t, union xg_gdb_regs *, int);
+int xg_read_mem(uint64_t, char *, int, uint64_t);
+int xg_write_mem(uint64_t, char *, int, uint64_t);
+void xgprt(const char *fn, const char *fmt, ...);
+void xgtrc(const char *fn, const char *fmt, ...);
diff -r 18758847bf31 -r 167e895ef770 xen/Rules.mk
--- a/xen/Rules.mk Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/Rules.mk Wed Oct 14 16:26:30 2009 -0700
@@ -56,6 +56,7 @@
CFLAGS-$(lock_profile) += -DLOCK_PROFILE
CFLAGS-$(frame_pointer) += -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER
CFLAGS-$(privileged_stubdoms) += -DPRIVILEGED_STUBDOMS
+CFLAGS-$(XEN_GDBSX_CONFIG) += -DXEN_GDBSX_CONFIG
ifneq ($(max_phys_cpus),)
CFLAGS-y += -DMAX_PHYS_CPUS=$(max_phys_cpus)
diff -r 18758847bf31 -r 167e895ef770 xen/arch/x86/Makefile
--- a/xen/arch/x86/Makefile Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/arch/x86/Makefile Wed Oct 14 16:26:30 2009 -0700
@@ -56,6 +56,7 @@
obj-y += bzimage.o
obj-$(crash_debug) += gdbstub.o
+obj-$(gdbsx) += debug.o
x86_emulate.o: x86_emulate/x86_emulate.c x86_emulate/x86_emulate.h
diff -r 18758847bf31 -r 167e895ef770 xen/arch/x86/debug.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/debug.c Wed Oct 14 16:26:30 2009 -0700
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include <xen/config.h>
+#include <xen/sched.h>
+#include <xen/compile.h>
+#include <xen/mm.h>
+#include <xen/domain_page.h>
+#include <xen/guest_access.h>
+#include <asm/p2m.h>
+
+
+/*
+ * This file for general routines common to more than one debugger, like kdb,
+ * gdbsx, etc..
+ */
+
+
+#ifdef XEN_KDB_CONFIG
+extern volatile int kdbdbg;
+extern void kdbp(const char *fmt, ...);
+#define DBGP(...) {(kdbdbg) ? kdbp(__VA_ARGS__):0;}
+#define DBGP1(...) {(kdbdbg>1) ? kdbp(__VA_ARGS__):0;}
+#define DBGP2(...) {(kdbdbg>2) ? kdbp(__VA_ARGS__):0;}
+#else
+#define DBGP1(...) {0;}
+#define DBGP2(...) {0;}
+#endif
+
+typedef unsigned long dbgva_t;
+typedef unsigned char dbgbyte_t;
+
+
+/* Returns: mfn for the given (hvm guest) vaddr */
+static noinline unsigned long
+dbg_hvm_va2mfn(dbgva_t vaddr, struct domain *dp, int toaddr)
+{
+ unsigned long mfn, gfn;
+#if XEN_SUBVERSION >= 2 /* xen >= 3.2.x */
+ uint32_t pfec = PFEC_page_present;
+ p2m_type_t gfntype;
+#endif
+
+ DBGP2("vaddr:%lx domid:%d\n", vaddr, dp->domain_id);
+#if XEN_SUBVERSION >= 2 /* xen >= 3.2.x */
+ gfn = paging_gva_to_gfn(dp->vcpu[0], vaddr, &pfec);
+#else
+ gfn = paging_gva_to_gfn(dp->vcpu[0], vaddr);
+#endif
+ if (gfn == INVALID_GFN) {
+ DBGP2("kdb:bad gfn from gva_to_gfn\n");
+ return INVALID_MFN;
+ }
+
+#if XEN_SUBVERSION >= 2 /* xen >= 3.2.x */
+ mfn = mfn_x(gfn_to_mfn(dp, gfn, &gfntype));
+ if (p2m_is_readonly(gfntype) && toaddr) {
+ DBGP2("kdb:p2m_is_readonly: gfntype:%x\n", gfntype);
+ return INVALID_MFN;
+ }
+#else
+ mfn = mfn_x(gfn_to_mfn(dp, gfn));
+#endif
+ DBGP2("X: vaddr:%lx domid:%d mfn:%lx\n", vaddr, dp->domain_id, mfn);
+ return mfn;
+}
+
+#if defined(__x86_64__)
+
+/*
+ * pgd3val: this is the value of init_mm.pgd[3] in a PV guest. It is optional.
+ * This to assist debug of modules in the guest. The kernel address
+ * space seems is always mapped, but modules are not necessarily
+ * mapped in any arbitraty guest cr3 that we pick if pgd3val is 0.
+ * Modules should always be addressible if we use cr3 from init_mm.
+ * Since pgd3val is already a pgd value, cr3->pgd[3], we just need to
+ * do 2 level lookups.
+ *
+ * NOTE: 4 level paging works for 32 PAE guests also because cpu runs in IA32-e
+ * mode.
+ * Returns: mfn for the given (pv guest) vaddr
+ */
+static noinline unsigned long
+dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val)
+{
+ l4_pgentry_t l4e, *l4t;
+ l3_pgentry_t l3e, *l3t;
+ l2_pgentry_t l2e, *l2t;
+ l1_pgentry_t l1e, *l1t;
+ unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3);
+ unsigned long mfn = cr3 >> PAGE_SHIFT;
+
+ DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id,
+ cr3, pgd3val);
+
+ if (pgd3val == 0) {
+ l4t = mfn_to_virt(mfn);
+ l4e = l4t[l4_table_offset(vaddr)];
+ mfn = l4e_get_pfn(l4e);
+ DBGP2("l4t:%p l4to:%lx l4e:%lx mfn:%lx\n", l4t,
+ l4_table_offset(vaddr), l4e, mfn);
+ if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) {
+ DBGP1("l4 PAGE not present. vaddr:%lx cr3:%lx\n",
vaddr,
+ cr3);
+ return INVALID_MFN;
+ }
+
+ l3t = mfn_to_virt(mfn);
+ l3e = l3t[l3_table_offset(vaddr)];
+ mfn = l3e_get_pfn(l3e);
+ DBGP2("l3t:%p l3to:%lx l3e:%lx mfn:%lx\n", l3t,
+ l3_table_offset(vaddr), l3e, mfn);
+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) {
+ DBGP1("l3 PAGE not present. vaddr:%lx cr3:%lx\n",
vaddr,
+ cr3);
+ return INVALID_MFN;
+ }
+ }
+ l2t = mfn_to_virt(mfn);
+ l2e = l2t[l2_table_offset(vaddr)];
+ mfn = l2e_get_pfn(l2e);
+ DBGP2("l2t:%p l2to:%lx l2e:%lx mfn:%lx\n", l2t, l2_table_offset(vaddr),
+ l2e, mfn);
+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ||
+ (l2e_get_flags(l2e) & _PAGE_PSE) ) {
+ DBGP1("l2 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3);
+ return INVALID_MFN;
+ }
+ l1t = mfn_to_virt(mfn);
+ l1e = l1t[l1_table_offset(vaddr)];
+ mfn = l1e_get_pfn(l1e);
+ DBGP2("l1t:%p l1to:%lx l1e:%lx mfn:%lx\n", l1t, l1_table_offset(vaddr),
+ l1e, mfn);
+
+ return mfn_valid(mfn) ? mfn : INVALID_MFN;
+}
+
+#else
+
+/* Returns: mfn for the given (pv guest) vaddr */
+static noinline unsigned long
+dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val)
+{
+#ifdef CONFIG_X86_PAE
+ l3_pgentry_t l3e, *l3t;
+#endif
+ l2_pgentry_t l2e, *l2t;
+ l1_pgentry_t l1e, *l1t;
+ unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3);
+ unsigned long mfn = cr3 >> PAGE_SHIFT;
+
+ DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id,
+ cr3, pgd3val);
+
+#ifdef CONFIG_X86_PAE
+ if (pgd3val == 0) {
+ l3t = map_domain_page(mfn);
+ l3t += (cr3 & 0xFE0UL) >> 3;
+ l3e = l3t[l3_table_offset(vaddr)];
+ mfn = l3e_get_pfn(l3e);
+ unmap_domain_page(l3t);
+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) )
+ return INVALID_MFN;
+ }
+#endif
+ l2t = map_domain_page(mfn);
+ l2e = l2t[l2_table_offset(vaddr)];
+ mfn = l2e_get_pfn(l2e);
+ unmap_domain_page(l2t);
+ if (!(l2e_get_flags(l2e) & _PAGE_PRESENT) ||
+ (l2e_get_flags(l2e) & _PAGE_PSE))
+ return INVALID_MFN;
+
+ l1t = map_domain_page(mfn);
+ l1e = l1t[l1_table_offset(vaddr)];
+ mfn = l1e_get_pfn(l1e);
+ unmap_domain_page(l1t);
+
+ return mfn_valid(mfn) ? mfn : INVALID_MFN;
+}
+#endif /* defined(__x86_64__) */
+
+/* Returns: number of bytes remaining to be copied */
+static noinline int
+dbg_rw_guest_mem(dbgva_t addr, dbgbyte_t *buf, int len, struct domain *dp,
+ int toaddr, uint64_t pgd3)
+{
+ while (len > 0) {
+ char *va;
+ unsigned long mfn, pagecnt;
+
+ pagecnt = min_t(long, PAGE_SIZE - (addr & ~PAGE_MASK), len);
+
+ if (dp->is_hvm)
+ mfn = dbg_hvm_va2mfn(addr, dp, toaddr);
+ else
+ mfn = dbg_pv_va2mfn(addr, dp, pgd3);
+ if (mfn == INVALID_MFN)
+ break;
+
+ va = map_domain_page(mfn);
+ va = va + (addr & (PAGE_SIZE-1));
+
+ if (toaddr) {
+ memcpy(va, buf, pagecnt); /* va = buf */
+ paging_mark_dirty(dp, mfn);
+ } else
+ memcpy(buf, va, pagecnt); /* buf = va */
+ unmap_domain_page(va);
+
+ addr += pagecnt;
+ buf += pagecnt;
+ len -= pagecnt;
+ }
+ return len;
+}
+
+/*
+ * addr is hypervisor addr if domid == IDLE_DOMAIN_ID, else it's guest addr
+ * buf is debugger buffer.
+ * if toaddr, then addr = buf (write to addr), else buf = addr (rd from guest)
+ * pgd3: value of init_mm.pgd[3] in guest. see above.
+ * Returns: number of bytes remaining to be copied.
+ */
+int
+dbg_rw_mem(dbgva_t addr, dbgbyte_t *buf, int len, domid_t domid, int toaddr,
+ uint64_t pgd3)
+{
+ struct domain *dp = get_domain_by_id(domid);
+ int hyp = (domid == IDLE_DOMAIN_ID);
+
+ DBGP2("gmem:addr:%lx buf:%p len:$%d domid:%x toaddr:%x dp:%p\n",
+ addr, buf, len, domid, toaddr, dp);
+ if (hyp) {
+ if (toaddr)
+ len = __copy_to_user((void *)addr, buf, len);
+ else
+ len = __copy_from_user(buf, (void *)addr, len);
+ } else {
+ if (dp && !dp->is_dying) /* make sure guest is still there */
+ len= dbg_rw_guest_mem(addr, buf, len, dp, toaddr,
pgd3);
+ }
+ DBGP2("gmem:exit:len:$%d\n", len);
+ return len;
+}
+
diff -r 18758847bf31 -r 167e895ef770 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c Wed Oct 14 16:26:30 2009 -0700
@@ -52,6 +52,10 @@
#include <asm/hvm/trace.h>
#include <asm/hap.h>
+#if defined(XEN_KDB_CONFIG) || defined(XEN_GDBSX_CONFIG)
+#include <asm/debugger.h>
+#endif
+
u32 svm_feature_flags;
#define set_segment_register(name, value) \
@@ -1370,6 +1374,9 @@
if ( (inst_len = __get_instruction_length(v, INSTR_INT3)) == 0 )
break;
__update_guest_eip(regs, inst_len);
+#ifdef XEN_GDBSX_CONFIG
+ current->gdbsx_vcpu_event = TRAP_int3;
+#endif
domain_pause_for_debugger();
break;
diff -r 18758847bf31 -r 167e895ef770 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Wed Oct 14 16:26:30 2009 -0700
@@ -52,6 +52,10 @@
#include <asm/hvm/trace.h>
#include <asm/xenoprof.h>
+#if defined(XEN_KDB_CONFIG) || defined(XEN_GDBSX_CONFIG)
+#include <asm/debugger.h>
+#endif
+
enum handler_return { HNDL_done, HNDL_unhandled, HNDL_exception_raised };
static void vmx_ctxt_switch_from(struct vcpu *v);
@@ -2475,6 +2479,9 @@
goto exit_and_crash;
inst_len = __get_instruction_length(); /* Safe: INT3 */
__update_guest_eip(inst_len);
+#ifdef XEN_GDBSX_CONFIG
+ current->gdbsx_vcpu_event = TRAP_int3;
+#endif
domain_pause_for_debugger();
break;
case TRAP_no_device:
diff -r 18758847bf31 -r 167e895ef770 xen/arch/x86/setup.c
--- a/xen/arch/x86/setup.c Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/arch/x86/setup.c Wed Oct 14 16:26:30 2009 -0700
@@ -1171,6 +1171,10 @@
safe_strcat(*info, s);
#endif
}
+
+#ifdef XEN_GDBSX_CONFIG
+ safe_strcat(*info, "gdbsx ");
+#endif
}
int xen_in_range(paddr_t start, paddr_t end)
diff -r 18758847bf31 -r 167e895ef770 xen/common/domctl.c
--- a/xen/common/domctl.c Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/common/domctl.c Wed Oct 14 16:26:30 2009 -0700
@@ -188,6 +188,30 @@
return cpu;
}
+#ifdef XEN_GDBSX_CONFIG
+ #ifdef XEN_KDB_CONFIG
+ #include "../kdb/include/kdbdefs.h"
+ #include "../kdb/include/kdbproto.h"
+ #else
+ typedef unsigned long kdbva_t;
+ typedef unsigned char kdbbyt_t;
+ extern int dbg_rw_mem(kdbva_t, kdbbyt_t *, int, domid_t, int, uint64_t);
+ #endif
+
+/*
+ * Read write guest memory
+ */
+static int
+gdbsx_guest_mem_io(domid_t domid, struct xen_domctl_memio *iop)
+{
+ ulong l_uva = (ulong)iop->gdbsx_uva;
+ iop->gdbsx_remain = dbg_rw_mem((kdbva_t)iop->gdbsx_gva,
+ (kdbbyt_t *)l_uva, iop->gdbsx_len,
domid,
+ iop->gdbsx_gwr, iop->gdbsx_pgd3val);
+ return (iop->gdbsx_remain ? -EFAULT : 0);
+}
+#endif /* XEN_GDBSX_CONFIG */
+
bool_t domctl_lock_acquire(void)
{
/*
@@ -946,6 +970,101 @@
}
break;
+#ifdef XEN_GDBSX_CONFIG
+ case XEN_DOMCTL_guestmemio:
+ {
+ struct domain *d;
+
+ ret = -ESRCH;
+ op->u.gdbsx_guest_memio.gdbsx_remain
=op->u.gdbsx_guest_memio.gdbsx_len;
+
+ if ((d = rcu_lock_domain_by_id(op->domain)))
+ ret = gdbsx_guest_mem_io(op->domain, &op->u.gdbsx_guest_memio);
+ if ( copy_to_guest(u_domctl, op, 1) )
+ ret = -EFAULT;
+ rcu_unlock_domain(d);
+ }
+ break;
+ case XEN_DOMCTL_pausevcpu:
+ {
+ struct domain *d;
+ struct vcpu *v;
+
+ ret = -ESRCH;
+ if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
+ break;
+
+ ret = -EBUSY;
+ if (!d->is_paused_by_controller) {
+ rcu_unlock_domain(d);
+ break;
+ }
+ ret = -EINVAL;
+ if ( op->u.gdbsx_pauseunp_vcpu.gdbsx_vcpu >= MAX_VIRT_CPUS ||
+ (v = d->vcpu[op->u.gdbsx_pauseunp_vcpu.gdbsx_vcpu]) == NULL ) {
+ rcu_unlock_domain(d);
+ break;
+ }
+ vcpu_pause(v);
+ ret = 0;
+ rcu_unlock_domain(d);
+ }
+ break;
+ case XEN_DOMCTL_unpausevcpu:
+ {
+ struct domain *d;
+ struct vcpu *v;
+
+ ret = -ESRCH;
+ if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
+ break;
+
+ ret = -EBUSY;
+ if (!d->is_paused_by_controller) {
+ rcu_unlock_domain(d);
+ break;
+ }
+ ret = -EINVAL;
+ if ( op->u.gdbsx_pauseunp_vcpu.gdbsx_vcpu >= MAX_VIRT_CPUS ||
+ (v = d->vcpu[op->u.gdbsx_pauseunp_vcpu.gdbsx_vcpu]) == NULL ) {
+ rcu_unlock_domain(d);
+ break;
+ }
+ if (!atomic_read(&v->pause_count))
+ printk("WARN: Unpausing vcpu:%d which is not paused\n",
v->vcpu_id);
+ vcpu_unpause(v);
+ ret = 0;
+ rcu_unlock_domain(d);
+ }
+ break;
+ case XEN_DOMCTL_gdbsx_domstatus:
+ {
+ struct domain *d;
+ struct vcpu *v;
+
+ ret = -ESRCH;
+ if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
+ break;
+
+ op->u.gdbsx_domstatus.gdbsx_vcpu_id = -1;
+ if ((op->u.gdbsx_domstatus.gdbsx_paused = d->is_paused_by_controller))
{
+ for_each_vcpu ( d, v ) {
+ if (v->gdbsx_vcpu_event) {
+ op->u.gdbsx_domstatus.gdbsx_vcpu_id = v->vcpu_id;
+ op->u.gdbsx_domstatus.gdbsx_vcpu_ev = v->gdbsx_vcpu_event;
+ v->gdbsx_vcpu_event = 0;
+ break;
+ }
+ }
+ }
+ ret = 0;
+ if ( copy_to_guest(u_domctl, op, 1) )
+ ret = -EFAULT;
+ rcu_unlock_domain(d);
+ }
+ break;
+#endif /* XEN_GDBSX_CONFIG */
+
default:
ret = arch_do_domctl(op, u_domctl);
break;
diff -r 18758847bf31 -r 167e895ef770 xen/include/asm-x86/debugger.h
--- a/xen/include/asm-x86/debugger.h Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/include/asm-x86/debugger.h Wed Oct 14 16:26:30 2009 -0700
@@ -68,6 +68,10 @@
if ( guest_kernel_mode(v, regs) && v->domain->debugger_attached &&
((vector == TRAP_int3) || (vector == TRAP_debug)) )
{
+#ifdef XEN_GDBSX_CONFIG
+ if (vector != TRAP_debug) /* domain pause is good enough */
+ current->gdbsx_vcpu_event = vector;
+#endif
domain_pause_for_debugger();
return 1;
}
diff -r 18758847bf31 -r 167e895ef770 xen/include/public/domctl.h
--- a/xen/include/public/domctl.h Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/include/public/domctl.h Wed Oct 14 16:26:30 2009 -0700
@@ -651,6 +651,36 @@
DEFINE_XEN_GUEST_HANDLE(xen_domctl_hvmcontext_partial_t);
+#ifdef XEN_GDBSX_CONFIG
+
+#define XEN_DOMCTL_guestmemio 1000 /* guest mem io */
+
+/* can't have any long in the struct as dom0 may be compiled in 32bit mode */
+struct xen_domctl_memio {
+ uint64_t gdbsx_pgd3val;/* optional: init_mm.pgd[3] value */
+ uint64_t gdbsx_gva; /* guest virtual address */
+ uint64_t gdbsx_uva; /* user buffer virtual address */
+ int gdbsx_len; /* number of bytes to read/write */
+ int gdbsx_gwr; /* 0 = read from guest. 1 = write to guest
*/
+ int gdbsx_remain; /* bytes remaining to be copied */
+};
+
+#define XEN_DOMCTL_pausevcpu 1001
+#define XEN_DOMCTL_unpausevcpu 1002
+struct xen_domctl_pauseunp_vcpu { /* pause/unpause a vcpu */
+ uint32_t gdbsx_vcpu; /* which vcpu */
+};
+
+#define XEN_DOMCTL_gdbsx_domstatus 1003
+struct xen_domctl_gdbsx_domstatus {
+ int gdbsx_paused; /* is the domain paused */
+ uint32_t gdbsx_vcpu_id; /* any vcpu in an event? */
+ uint32_t gdbsx_vcpu_ev; /* if yes, what event? */
+
+};
+#endif /* XEN_GDBSX_CONFIG */
+
+
struct xen_domctl {
uint32_t cmd;
uint32_t interface_version; /* XEN_DOMCTL_INTERFACE_VERSION */
@@ -696,6 +726,11 @@
#if defined(__i386__) || defined(__x86_64__)
struct xen_domctl_cpuid cpuid;
#endif
+#ifdef XEN_GDBSX_CONFIG
+ struct xen_domctl_memio gdbsx_guest_memio;
+ struct xen_domctl_pauseunp_vcpu gdbsx_pauseunp_vcpu;
+ struct xen_domctl_gdbsx_domstatus gdbsx_domstatus;
+#endif
uint8_t pad[128];
} u;
};
diff -r 18758847bf31 -r 167e895ef770 xen/include/xen/sched.h
--- a/xen/include/xen/sched.h Wed Oct 14 09:09:23 2009 +0100
+++ b/xen/include/xen/sched.h Wed Oct 14 16:26:30 2009 -0700
@@ -158,6 +158,10 @@
cpumask_t vcpu_dirty_cpumask;
struct arch_vcpu arch;
+
+#if XEN_GDBSX_CONFIG
+ uint32_t gdbsx_vcpu_event;
+#endif
};
/* Per-domain lock can be recursively acquired in fault handlers. */
gdbsx.patch
Description: Text Data
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|