aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-10-15 09:36:40 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-10-15 09:36:40 +0100
commit61f2a440299cdf5ff809cd330a6400f6a5cdf910 (patch)
tree227d1b51b47a9bddf6dec437371eabad8d346221 /tools/debugger
parentf8e61527db79a580d9d9d5602059ed4db6c06b2d (diff)
downloadxen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.tar.gz
xen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.tar.bz2
xen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.zip
gdbsx: a gdbserver stub for xen.
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@oracle.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
Diffstat (limited to 'tools/debugger')
-rw-r--r--tools/debugger/gdbsx/Makefile28
-rw-r--r--tools/debugger/gdbsx/README143
-rw-r--r--tools/debugger/gdbsx/Rules.mk9
-rw-r--r--tools/debugger/gdbsx/gx/Makefile20
-rw-r--r--tools/debugger/gdbsx/gx/gx.h40
-rw-r--r--tools/debugger/gdbsx/gx/gx_comm.c328
-rw-r--r--tools/debugger/gdbsx/gx/gx_local.c146
-rw-r--r--tools/debugger/gdbsx/gx/gx_main.c696
-rw-r--r--tools/debugger/gdbsx/gx/gx_utils.c145
-rw-r--r--tools/debugger/gdbsx/gx/xg_dummy.c122
-rw-r--r--tools/debugger/gdbsx/xg/Makefile36
-rw-r--r--tools/debugger/gdbsx/xg/xg_main.c806
-rw-r--r--tools/debugger/gdbsx/xg/xg_public.h110
13 files changed, 2629 insertions, 0 deletions
diff --git a/tools/debugger/gdbsx/Makefile b/tools/debugger/gdbsx/Makefile
new file mode 100644
index 0000000000..4b25454e03
--- /dev/null
+++ b/tools/debugger/gdbsx/Makefile
@@ -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 --git a/tools/debugger/gdbsx/README b/tools/debugger/gdbsx/README
new file mode 100644
index 0000000000..ac01add93c
--- /dev/null
+++ b/tools/debugger/gdbsx/README
@@ -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 --git a/tools/debugger/gdbsx/Rules.mk b/tools/debugger/gdbsx/Rules.mk
new file mode 100644
index 0000000000..d729323cb5
--- /dev/null
+++ b/tools/debugger/gdbsx/Rules.mk
@@ -0,0 +1,9 @@
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS += -Werror -Wmissing-prototypes
+# (gcc 4.3x and later) -Wconversion -Wno-sign-conversion
+
+# just in case have to debug gdbsx, keep life simple.
+TMPFLAGS := $(CFLAGS)
+CFLAGS := $(filter-out -O% -DNDEBUG -fomit-frame-pointer, $(TMPFLAGS))
+CFLAGS += -O0
diff --git a/tools/debugger/gdbsx/gx/Makefile b/tools/debugger/gdbsx/gx/Makefile
new file mode 100644
index 0000000000..c9a48c0ccf
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/Makefile
@@ -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 --git a/tools/debugger/gdbsx/gx/gx.h b/tools/debugger/gdbsx/gx/gx.h
new file mode 100644
index 0000000000..47594c3696
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/gx.h
@@ -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 --git a/tools/debugger/gdbsx/gx/gx_comm.c b/tools/debugger/gdbsx/gx/gx_comm.c
new file mode 100644
index 0000000000..f991952239
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/gx_comm.c
@@ -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 --git a/tools/debugger/gdbsx/gx/gx_local.c b/tools/debugger/gdbsx/gx/gx_local.c
new file mode 100644
index 0000000000..c8f0e72a1b
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/gx_local.c
@@ -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 --git a/tools/debugger/gdbsx/gx/gx_main.c b/tools/debugger/gdbsx/gx/gx_main.c
new file mode 100644
index 0000000000..f20f6ef0d7
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/gx_main.c
@@ -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, &regs, guest_bitness)) {
+ gxprt("WARN: Unable to get read regs. errno:%d\n", errno);
+ return;
+ }
+ if (guest_bitness == 32)
+ rbuf = copy_mini_context32(rbuf, &regs.u.gregs_32);
+ else
+ rbuf = copy_mini_context64(rbuf, &regs.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 --git a/tools/debugger/gdbsx/gx/gx_utils.c b/tools/debugger/gdbsx/gx/gx_utils.c
new file mode 100644
index 0000000000..e87ffcb730
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/gx_utils.c
@@ -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 --git a/tools/debugger/gdbsx/gx/xg_dummy.c b/tools/debugger/gdbsx/gx/xg_dummy.c
new file mode 100644
index 0000000000..b82899f5e8
--- /dev/null
+++ b/tools/debugger/gdbsx/gx/xg_dummy.c
@@ -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 --git a/tools/debugger/gdbsx/xg/Makefile b/tools/debugger/gdbsx/xg/Makefile
new file mode 100644
index 0000000000..4ea695987e
--- /dev/null
+++ b/tools/debugger/gdbsx/xg/Makefile
@@ -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 --git a/tools/debugger/gdbsx/xg/xg_main.c b/tools/debugger/gdbsx/xg/xg_main.c
new file mode 100644
index 0000000000..9cd9fc3a6d
--- /dev/null
+++ b/tools/debugger/gdbsx/xg/xg_main.c
@@ -0,0 +1,806 @@
+/*
+ * 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;
+
+ /*
+ * Try to unpause an invalid vcpu. If hypervisor supports gdbsx then
+ * this should fail with an error other than ENOSYS.
+ */
+ domctl.u.gdbsx_pauseunp_vcpu.vcpu = ~0u;
+ (void)_domctl_hcall(XEN_DOMCTL_gdbsx_unpausevcpu, NULL, 0);
+ if (errno == ENOSYS) {
+ XGERR("Hyp is NOT enabled for gdbsx\n");
+ return -1;
+ }
+
+ 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;
+ }
+
+ _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.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_gdbsx_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_gdbsx_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.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 = &regsp->gregs_32;
+ struct xg_gdb_regs64 *r64p = &regsp->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 = &regsp->gregs_32;
+ struct xg_gdb_regs64 *r64p = &regsp->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_gdbsx_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->pgd3val = pgd3val;
+ iop->gva = guestva;
+ iop->uva = (uint64_aligned_t)((unsigned long)tobuf);
+ iop->len = tobuf_len;
+ iop->gwr = 0; /* not writing to guest */
+
+ _domctl_hcall(XEN_DOMCTL_gdbsx_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->remain, u.llbuf8);
+
+ return iop->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_gdbsx_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->pgd3val = pgd3val;
+ iop->gva = guestva;
+ iop->uva = (uint64_aligned_t)((unsigned long)frombuf);
+ iop->len = buflen;
+ iop->gwr = 1; /* writing to guest */
+
+ if ((rc=_domctl_hcall(XEN_DOMCTL_gdbsx_guestmemio, frombuf, buflen)))
+ XGERR("ERROR: failed to write %d bytes. errno:%d rc:%d\n",
+ iop->remain, errno, rc);
+ return iop->remain;
+}
+
diff --git a/tools/debugger/gdbsx/xg/xg_public.h b/tools/debugger/gdbsx/xg/xg_public.h
new file mode 100644
index 0000000000..6236d08fef
--- /dev/null
+++ b/tools/debugger/gdbsx/xg/xg_public.h
@@ -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, ...);