aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger/libxendebug/xendebug.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/debugger/libxendebug/xendebug.c')
-rw-r--r--tools/debugger/libxendebug/xendebug.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/tools/debugger/libxendebug/xendebug.c b/tools/debugger/libxendebug/xendebug.c
new file mode 100644
index 0000000000..844cdf0e03
--- /dev/null
+++ b/tools/debugger/libxendebug/xendebug.c
@@ -0,0 +1,599 @@
+/*
+ * xendebug.c
+ *
+ * alex ho
+ * http://www.cl.cam.ac.uk/netos/pdb
+ *
+ * xendebug_memory_page adapted from xc_ptrace.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <xc.h>
+#include "list.h"
+
+#if defined(__i386__)
+#define L1_PAGETABLE_SHIFT 12
+#define L2_PAGETABLE_SHIFT 22
+#elif defined(__x86_64__)
+#define L1_PAGETABLE_SHIFT 12
+#define L2_PAGETABLE_SHIFT 21
+#define L3_PAGETABLE_SHIFT 30
+#define L4_PAGETABLE_SHIFT 39
+#endif
+
+#define PAGE_SHIFT L1_PAGETABLE_SHIFT
+#define PAGE_SIZE (1UL<<PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+/* from xen/include/asm-x86/processor.h */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+
+typedef int boolean;
+#define true 1
+#define false 0
+
+
+typedef struct bwcpoint /* break/watch/catch point */
+{
+ struct list_head list;
+ memory_t address;
+ u32 domain;
+ u16 vcpu;
+ u8 old_value; /* old value for software bkpt */
+} bwcpoint_t, *bwcpoint_p;
+
+static bwcpoint_t bwcpoint_list;
+
+
+
+typedef struct domain_context /* local cache of domain state */
+{
+ struct list_head list;
+ u32 domid;
+ boolean valid[MAX_VIRT_CPUS];
+ vcpu_guest_context_t context[MAX_VIRT_CPUS];
+
+ long total_pages;
+ unsigned long *page_array;
+
+ unsigned long cr3_phys[MAX_VIRT_CPUS];
+ unsigned long *cr3_virt[MAX_VIRT_CPUS];
+ unsigned long pde_phys[MAX_VIRT_CPUS];
+ unsigned long *pde_virt[MAX_VIRT_CPUS];
+ unsigned long page_phys[MAX_VIRT_CPUS];
+ unsigned long *page_virt[MAX_VIRT_CPUS];
+ int page_perm[MAX_VIRT_CPUS];
+} domain_context_t, *domain_context_p;
+
+static domain_context_t domain_context_list;
+
+/* initialization */
+
+static boolean xendebug_initialized = false;
+
+static __inline__ void
+xendebug_initialize()
+{
+ if ( !xendebug_initialized )
+ {
+ memset((void *) &domain_context_list, 0, sizeof(domain_context_t));
+ INIT_LIST_HEAD(&domain_context_list.list);
+
+ memset((void *) &bwcpoint_list, 0, sizeof(bwcpoint_t));
+ INIT_LIST_HEAD(&bwcpoint_list.list);
+
+ xendebug_initialized = true;
+ }
+}
+
+/**************/
+
+static domain_context_p
+xendebug_domain_context_search (u32 domid)
+{
+ struct list_head *entry;
+ domain_context_p ctxt;
+
+ list_for_each(entry, &domain_context_list.list)
+ {
+ ctxt = list_entry(entry, domain_context_t, list);
+ if ( domid == ctxt->domid )
+ return ctxt;
+ }
+ return (domain_context_p)NULL;
+}
+
+static __inline__ domain_context_p
+xendebug_get_context (int xc_handle, u32 domid, u32 vcpu)
+{
+ int rc;
+ domain_context_p ctxt;
+
+ xendebug_initialize();
+
+ if ( (ctxt = xendebug_domain_context_search(domid)) == NULL)
+ return NULL;
+
+ if ( !ctxt->valid[vcpu] )
+ {
+ if ( (rc = xc_domain_get_vcpu_context(xc_handle, domid, vcpu,
+ &ctxt->context[vcpu])) )
+ return NULL;
+
+ ctxt->valid[vcpu] = true;
+ }
+
+ return ctxt;
+}
+
+static __inline__ int
+xendebug_set_context (int xc_handle, domain_context_p ctxt, u32 vcpu)
+{
+ dom0_op_t op;
+ int rc;
+
+ if ( !ctxt->valid[vcpu] )
+ return -EINVAL;
+
+ op.interface_version = DOM0_INTERFACE_VERSION;
+ op.cmd = DOM0_SETDOMAININFO;
+ op.u.setdomaininfo.domain = ctxt->domid;
+ op.u.setdomaininfo.vcpu = vcpu;
+ op.u.setdomaininfo.ctxt = &ctxt->context[vcpu];
+
+ if ( (rc = mlock(&ctxt->context[vcpu], sizeof(vcpu_guest_context_t))) )
+ return rc;
+
+ rc = xc_dom0_op(xc_handle, &op);
+ (void) munlock(&ctxt->context[vcpu], sizeof(vcpu_guest_context_t));
+
+ return rc;
+}
+
+/**************/
+
+int
+xendebug_attach(int xc_handle,
+ u32 domid,
+ u32 vcpu)
+{
+ domain_context_p ctxt;
+
+ xendebug_initialize();
+
+ if ( (ctxt = malloc(sizeof(domain_context_t))) == NULL )
+ return -1;
+ memset(ctxt, 0, sizeof(domain_context_t));
+
+ ctxt->domid = domid;
+ list_add(&ctxt->list, &domain_context_list.list);
+
+ return xc_domain_pause(xc_handle, domid);
+}
+
+int
+xendebug_detach(int xc_handle,
+ u32 domid,
+ u32 vcpu)
+{
+ domain_context_p ctxt;
+
+ xendebug_initialize();
+
+ if ( (ctxt = xendebug_domain_context_search (domid)) == NULL)
+ return -EINVAL;
+
+ list_del(&ctxt->list);
+
+ if ( ctxt->page_array ) free(ctxt->page_array);
+
+ free(ctxt);
+
+ return xc_domain_unpause(xc_handle, domid);
+}
+
+int
+xendebug_read_registers(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ cpu_user_regs_t **regs)
+{
+ domain_context_p ctxt;
+ int rc = -1;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ if (ctxt)
+ {
+ *regs = &ctxt->context[vcpu].user_regs;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+xendebug_read_fpregisters (int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ char **regs)
+{
+ domain_context_p ctxt;
+ int rc = -1;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ if (ctxt)
+ {
+ *regs = ctxt->context[vcpu].fpu_ctxt.x;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+xendebug_write_registers(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ cpu_user_regs_t *regs)
+{
+ domain_context_p ctxt;
+ int rc = -1;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ if (ctxt)
+ {
+ memcpy(&ctxt->context[vcpu].user_regs, regs, sizeof(cpu_user_regs_t));
+ rc = xendebug_set_context(xc_handle, ctxt, vcpu);
+ }
+
+ return rc;
+}
+
+int
+xendebug_step(int xc_handle,
+ u32 domid,
+ u32 vcpu)
+{
+ domain_context_p ctxt;
+ int rc;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ if (!ctxt) return -EINVAL;
+
+ ctxt->context[vcpu].user_regs.eflags |= X86_EFLAGS_TF;
+
+ if ( (rc = xendebug_set_context(xc_handle, ctxt, vcpu)) )
+ return rc;
+
+ ctxt->valid[vcpu] = false;
+ return xc_domain_unpause(xc_handle, domid);
+}
+
+int
+xendebug_continue(int xc_handle,
+ u32 domid,
+ u32 vcpu)
+{
+ domain_context_p ctxt;
+ int rc;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ if (!ctxt) return -EINVAL;
+
+ if ( ctxt->context[vcpu].user_regs.eflags & X86_EFLAGS_TF )
+ {
+ ctxt->context[vcpu].user_regs.eflags &= ~X86_EFLAGS_TF;
+ if ( (rc = xendebug_set_context(xc_handle, ctxt, vcpu)) )
+ return rc;
+ }
+ ctxt->valid[vcpu] = false;
+ return xc_domain_unpause(xc_handle, domid);
+}
+
+/*************************************************/
+
+#define vtopdi(va) ((va) >> L2_PAGETABLE_SHIFT)
+#define vtopti(va) (((va) >> PAGE_SHIFT) & 0x3ff)
+
+/* access to one page */
+static int
+xendebug_memory_page (domain_context_p ctxt, int xc_handle, u32 vcpu,
+ int protection, memory_t address, int length, u8 *buffer)
+{
+ vcpu_guest_context_t *vcpu_ctxt = &ctxt->context[vcpu];
+ unsigned long pde, page;
+ unsigned long va = (unsigned long)address;
+ void *ptr;
+ long pages;
+
+ pages = xc_get_tot_pages(xc_handle, ctxt->domid);
+
+ if ( ctxt->total_pages != pages )
+ {
+ if ( ctxt->total_pages > 0 ) free( ctxt->page_array );
+ ctxt->total_pages = pages;
+
+ ctxt->page_array = malloc(pages * sizeof(unsigned long));
+ if ( ctxt->page_array == NULL )
+ {
+ printf("Could not allocate memory\n");
+ return 0;
+ }
+
+ if ( xc_get_pfn_list(xc_handle, ctxt->domid, ctxt->page_array,pages) !=
+ pages )
+ {
+ printf("Could not get the page frame list\n");
+ return 0;
+ }
+ }
+
+ if ( vcpu_ctxt->pt_base != ctxt->cr3_phys[vcpu])
+ {
+ ctxt->cr3_phys[vcpu] = vcpu_ctxt->pt_base;
+ if ( ctxt->cr3_virt[vcpu] )
+ munmap(ctxt->cr3_virt[vcpu], PAGE_SIZE);
+ ctxt->cr3_virt[vcpu] = xc_map_foreign_range(xc_handle, ctxt->domid,
+ PAGE_SIZE, PROT_READ, ctxt->cr3_phys[vcpu] >> PAGE_SHIFT);
+ if ( ctxt->cr3_virt[vcpu] == NULL )
+ return 0;
+ }
+
+
+ if ( (pde = ctxt->cr3_virt[vcpu][vtopdi(va)]) == 0) /* logical address */
+ return 0;
+ if (ctxt->context[vcpu].flags & VGCF_VMX_GUEST)
+ pde = ctxt->page_array[pde >> PAGE_SHIFT] << PAGE_SHIFT;
+ if (pde != ctxt->pde_phys[vcpu])
+ {
+ ctxt->pde_phys[vcpu] = pde;
+ if ( ctxt->pde_virt[vcpu])
+ munmap(ctxt->pde_virt[vcpu], PAGE_SIZE);
+ ctxt->pde_virt[vcpu] = xc_map_foreign_range(xc_handle, ctxt->domid,
+ PAGE_SIZE, PROT_READ, ctxt->pde_phys[vcpu] >> PAGE_SHIFT);
+ if ( ctxt->pde_virt[vcpu] == NULL )
+ return 0;
+ }
+
+ if ((page = ctxt->pde_virt[vcpu][vtopti(va)]) == 0) /* logical address */
+ return 0;
+ if (ctxt->context[vcpu].flags & VGCF_VMX_GUEST)
+ page = ctxt->page_array[page >> PAGE_SHIFT] << PAGE_SHIFT;
+ if (page != ctxt->page_phys[vcpu] || protection != ctxt->page_perm[vcpu])
+ {
+ ctxt->page_phys[vcpu] = page;
+ if (ctxt->page_virt[vcpu])
+ munmap(ctxt->page_virt[vcpu], PAGE_SIZE);
+ ctxt->page_virt[vcpu] = xc_map_foreign_range(xc_handle, ctxt->domid,
+ PAGE_SIZE, protection, ctxt->page_phys[vcpu] >> PAGE_SHIFT);
+ if ( ctxt->page_virt[vcpu] == NULL )
+ {
+ printf("cr3 %lx pde %lx page %lx pti %lx\n",
+ vcpu_ctxt->pt_base, pde, page, vtopti(va));
+ ctxt->page_phys[vcpu] = 0;
+ return 0;
+ }
+ ctxt->page_perm[vcpu] = protection;
+ }
+
+ ptr = (void *)( (unsigned long)ctxt->page_virt[vcpu] |
+ (va & ~PAGE_MASK) );
+
+ if ( protection & PROT_WRITE )
+ {
+ memcpy(ptr, buffer, length);
+ }
+ else
+ {
+ memcpy(buffer, ptr, length);
+ }
+
+ return length;
+}
+
+/* divide a memory operation into accesses to individual pages */
+static int
+xendebug_memory_op (domain_context_p ctxt, int xc_handle, u32 vcpu,
+ int protection, memory_t address, int length, u8 *buffer)
+{
+ int remain; /* number of bytes to touch past this page */
+ int bytes = 0;
+
+ while ( (remain = (address + length - 1) - (address | (PAGE_SIZE-1))) > 0)
+ {
+ bytes += xendebug_memory_page(ctxt, xc_handle, vcpu, protection,
+ address, length - remain, buffer);
+ buffer += (length - remain);
+ length = remain;
+ address = (address | (PAGE_SIZE - 1)) + 1;
+ }
+
+ bytes += xendebug_memory_page(ctxt, xc_handle, vcpu, protection,
+ address, length, buffer);
+
+ return bytes;
+}
+
+int
+xendebug_read_memory(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ memory_t address,
+ u32 length,
+ u8 *data)
+{
+ domain_context_p ctxt;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+
+ xendebug_memory_op(ctxt, xc_handle, vcpu, PROT_READ,
+ address, length, data);
+
+ return 0;
+}
+
+int
+xendebug_write_memory(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ memory_t address,
+ u32 length,
+ u8 *data)
+{
+ domain_context_p ctxt;
+
+ xendebug_initialize();
+
+ ctxt = xendebug_get_context(xc_handle, domid, vcpu);
+ xendebug_memory_op(ctxt, xc_handle, vcpu, PROT_READ | PROT_WRITE,
+
+ address, length, data);
+
+ return 0;
+}
+
+int
+xendebug_insert_memory_breakpoint(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ memory_t address,
+ u32 length)
+{
+ bwcpoint_p bkpt;
+ u8 breakpoint_opcode = 0xcc;
+
+ printf("insert breakpoint %d:%lx %d\n",
+ domid, address, length);
+
+ xendebug_initialize();
+
+ bkpt = malloc(sizeof(bwcpoint_t));
+ if ( bkpt == NULL )
+ {
+ printf("error: breakpoint length should be 1\n");
+ return -1;
+ }
+
+ if ( length != 1 )
+ {
+ printf("error: breakpoint length should be 1\n");
+ free(bkpt);
+ return -1;
+ }
+
+ bkpt->address = address;
+ bkpt->domain = domid;
+
+ xendebug_read_memory(xc_handle, domid, vcpu, address, 1,
+ &bkpt->old_value);
+
+ xendebug_write_memory(xc_handle, domid, vcpu, address, 1,
+ &breakpoint_opcode);
+
+ list_add(&bkpt->list, &bwcpoint_list.list);
+
+ printf("breakpoint_set %d:%lx 0x%x\n",
+ domid, address, bkpt->old_value);
+
+ return 0;
+}
+
+int
+xendebug_remove_memory_breakpoint(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ memory_t address,
+ u32 length)
+{
+ bwcpoint_p bkpt = NULL;
+
+ printf ("remove breakpoint %d:%lx\n",
+ domid, address);
+
+ struct list_head *entry;
+ list_for_each(entry, &bwcpoint_list.list)
+ {
+ bkpt = list_entry(entry, bwcpoint_t, list);
+ if ( domid == bkpt->domain && address == bkpt->address )
+ break;
+ }
+
+ if (bkpt == &bwcpoint_list || bkpt == NULL)
+ {
+ printf ("error: no breakpoint found\n");
+ return -1;
+ }
+
+ list_del(&bkpt->list);
+
+ xendebug_write_memory(xc_handle, domid, vcpu, address, 1,
+ &bkpt->old_value);
+
+ free(bkpt);
+ return 0;
+}
+
+int
+xendebug_query_domain_stop(int xc_handle, int *dom_list, int dom_list_size)
+{
+ xc_dominfo_t *info;
+ u32 first_dom = 0;
+ int max_doms = 1024;
+ int nr_doms, loop;
+ int count = 0;
+
+ if ( (info = malloc(max_doms * sizeof(xc_dominfo_t))) == NULL )
+ return -ENOMEM;
+
+ nr_doms = xc_domain_getinfo(xc_handle, first_dom, max_doms, info);
+
+ for (loop = 0; loop < nr_doms; loop++)
+ {
+ printf ("domid: %d", info[loop].domid);
+ printf (" %c%c%c%c%c%c",
+ info[loop].dying ? 'D' : '-',
+ info[loop].crashed ? 'C' : '-',
+ info[loop].shutdown ? 'S' : '-',
+ info[loop].paused ? 'P' : '-',
+ info[loop].blocked ? 'B' : '-',
+ info[loop].running ? 'R' : '-');
+ printf (" pages: %ld, vcpus %d",
+ info[loop].nr_pages, info[loop].vcpus);
+ printf ("\n");
+
+ if ( info[loop].paused && count < dom_list_size)
+ {
+ dom_list[count++] = info[loop].domid;
+ }
+ }
+
+ free(info);
+
+ return count;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */