[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] tools: "kdd" Windows debugger stub.



"kdd" Windows debugger stub.

kdd runs in dom0, attaches to a domain and speaks the Windows kd serial
protocol over a TCP connection (which should go to kd or windbg, e.g. 
by having another VM with its virtual COM1 set up as a TCP listener).

It doesn't do breakpoints &c yet, and windbg can get quite confused
since the kernel debugger's not actually running, but it's good enough
to extract backtraces from wedged VMs.

Signed-off-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx>

diff -r ac7f64d5577b tools/Makefile
--- a/tools/Makefile    Wed Oct 20 09:56:36 2010 +0100
+++ b/tools/Makefile    Mon Oct 25 15:12:21 2010 +0100
@@ -36,6 +36,7 @@ SUBDIRS-y += remus
 SUBDIRS-y += remus
 SUBDIRS-$(CONFIG_X86) += xenpaging
 SUBDIRS-$(CONFIG_X86) += debugger/gdbsx
+SUBDIRS-$(CONFIG_X86) += debugger/kdd
 
 # These don't cross-compile
 ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH))
@@ -128,5 +129,15 @@ subdir-all-debugger/gdbsx: .phony
 subdir-all-debugger/gdbsx: .phony
        $(MAKE) -C debugger/gdbsx all
 
+
+subdir-clean-debugger/kdd subdir-distclean-debugger/kdd: .phony
+       $(MAKE) -C debugger/kdd clean
+
+subdir-install-debugger/kdd: .phony
+       $(MAKE) -C debugger/kdd install
+
+subdir-all-debugger/kdd: .phony
+       $(MAKE) -C debugger/kdd all
+
 subdir-distclean-firmware: .phony
        $(MAKE) -C firmware distclean
diff -r ac7f64d5577b tools/debugger/kdd/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/kdd/Makefile       Mon Oct 25 15:12:21 2010 +0100
@@ -0,0 +1,22 @@
+XEN_ROOT = ../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS  += $(CFLAGS_libxenctrl)
+LDLIBS  += $(LDLIBS_libxenctrl)
+
+CFILES  := kdd.c kdd-xen.c
+OBJS    := $(CFILES:.c=.o)
+
+all: kdd
+
+kdd: $(OBJS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+.PHONY: clean
+clean:
+       rm -f $(OBJS) kdd
+
+.PHONY: install
+install: all
+       [ -d $(DESTDIR)$(SBINDIR) ] || $(INSTALL_DIR) $(DESTDIR)$(SBINDIR)
+       $(INSTALL_PROG) kdd $(DESTDIR)$(SBINDIR)/kdd
diff -r ac7f64d5577b tools/debugger/kdd/kdd-xen.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/kdd/kdd-xen.c      Mon Oct 25 15:12:21 2010 +0100
@@ -0,0 +1,625 @@
+/*
+ * kdd-xen.c -- xen-specific functions for the kdd debugging stub
+ *
+ * Tim Deegan <Tim.Deegan@xxxxxxxxxx>
+ * 
+ * Copyright (c) 2007-2010, Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <xenctrl.h>
+#include <xen/xen.h>
+#include <xen/hvm/save.h>
+
+#include "kdd.h"
+
+#define MAPSIZE 4093 /* Prime */
+
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1U << PAGE_SHIFT)
+
+struct kdd_guest {
+    struct xentoollog_logger xc_log; /* Must be first for xc log callbacks */
+    xc_interface *xc_handle;
+    uint32_t domid;
+    char id[80];
+    FILE *log;
+    int verbosity;
+    void *hvm_buf;
+    uint32_t hvm_sz;
+    uint32_t pfns[MAPSIZE];
+    void * maps[MAPSIZE];
+};
+
+
+/* Flush any mappings we have of the guest memory: it's not polite
+ * top hold on to them while the guest is running */
+static void flush_maps(kdd_guest *g)
+{
+    int i;
+    for (i = 0; i < MAPSIZE; i++) {
+        if (g->maps[i] != NULL)
+            munmap(g->maps[i], PAGE_SIZE);
+        g->maps[i] = NULL;
+    }
+}
+
+
+/* Halt the guest so we can debug it */
+void kdd_halt(kdd_guest *g)
+{
+    uint32_t sz;
+    void *buf;
+
+    xc_domain_pause(g->xc_handle, g->domid);
+
+    /* How much space do we need for the HVM state? */
+    sz = xc_domain_hvm_getcontext(g->xc_handle, g->domid, 0, 0);
+    if (sz == (uint32_t) -1) {
+        KDD_LOG(g, "Can't get HVM state size for domid %"PRIu32": %s\n",
+                g->domid, strerror(errno));
+        return;
+    }
+    buf = realloc(g->hvm_buf, sz);
+    if (!buf) {
+        KDD_LOG(g, "Couldn't allocate %"PRIu32" for HVM buffer\n", sz);
+        return;
+    }
+    g->hvm_buf = buf;
+    g->hvm_sz = sz;
+    memset(buf, 0, sz);
+
+    /* Get the HVM state */
+    sz = xc_domain_hvm_getcontext(g->xc_handle, g->domid, buf, sz);
+    if (sz == (uint32_t) -1) {
+        KDD_LOG(g, "Can't get HVM state for domid %"PRIu32": %s\n",
+                g->domid, strerror(errno));
+    }
+}
+
+/* Check whether the guest has stopped.  Returns 'interesting' vcpu or -1 */
+/* TODO: open DEBUG_VIRQ if it's free and wait for events rather than
+ * always polling the guest state */
+int kdd_poll_guest(kdd_guest *g)
+{
+    /* TODO: finish plumbing through polling for breakpoints */
+    return 0;
+}
+
+/* Update the HVM state */
+static void hvm_writeback(kdd_guest *g)
+{
+    if (g->hvm_buf && xc_domain_hvm_setcontext(g->xc_handle, g->domid, 
+                                               g->hvm_buf, g->hvm_sz))
+        KDD_LOG(g, "Can't set HVM state for domid %"PRIu32": %s\n",
+                g->domid, strerror(errno));
+}
+
+/* Start the guest running again */
+void kdd_run(kdd_guest *g)
+{
+    flush_maps(g);
+    hvm_writeback(g);
+    xc_domain_unpause(g->xc_handle, g->domid);
+}
+
+/* How many CPUs are there in this guest? */
+int kdd_count_cpus(kdd_guest *g)
+{
+    struct hvm_save_descriptor *desc;
+    int maxcpu = 0;
+
+    if (!g->hvm_buf)
+        return 0;
+
+    /* Scan the CPU save records. */
+    for (desc = g->hvm_buf;
+         (void *) desc >= g->hvm_buf && (void *) desc < g->hvm_buf + g->hvm_sz;
+         desc = ((void *)desc) + (sizeof *desc) + desc->length) {
+        if (desc->typecode == HVM_SAVE_CODE(CPU)) {
+            if (maxcpu < desc->instance) 
+                maxcpu = desc->instance;
+        }
+    }
+    return maxcpu + 1;
+}
+
+/* Helper fn: get CPU state from cached HVM state */
+static struct hvm_hw_cpu *get_cpu(kdd_guest *g, int cpuid)
+{
+    struct hvm_save_descriptor *desc;
+    struct hvm_hw_cpu *cpu;
+
+    if (!g->hvm_buf)
+        return NULL;
+
+    /* Find the right CPU */
+    for (desc = g->hvm_buf;
+         (void *) desc >= g->hvm_buf && (void *) desc < g->hvm_buf + g->hvm_sz;
+         desc = ((void *)desc) + (sizeof *desc) + desc->length) {
+        if (desc->typecode == HVM_SAVE_CODE(CPU) && desc->instance == cpuid) {
+            cpu = ((void *)desc) + (sizeof *desc);
+            if ((void *) cpu + sizeof (*cpu) <= g->hvm_buf + g->hvm_sz)
+                return cpu;
+        }
+    }
+    
+    KDD_LOG(g, "Dom %"PRIu32" has no CPU %i\n", g->domid, cpuid);
+    return NULL;
+}
+
+/* Helper fn: get APIC state from cached HVM state */
+static struct hvm_hw_lapic_regs *get_lapic(kdd_guest *g, int cpuid)
+{
+    struct hvm_save_descriptor *desc;
+    struct hvm_hw_lapic_regs *regs;
+
+    if (!g->hvm_buf)
+        return NULL;
+
+    /* Find the right CPU's LAPIC */
+    for (desc = g->hvm_buf;
+         (void *) desc >= g->hvm_buf && (void *) desc < g->hvm_buf + g->hvm_sz;
+         desc = ((void *)desc) + (sizeof *desc) + desc->length) {
+        if (desc->typecode == HVM_SAVE_CODE(LAPIC_REGS) && desc->instance == 
cpuid) {
+            regs = ((void *)desc) + (sizeof *desc);
+            if ((void *) regs + sizeof (*regs) <= g->hvm_buf + g->hvm_sz)
+                return regs;
+        }
+    }
+    
+    KDD_LOG(g, "Dom %"PRIu32" has no LAPIC %i\n", g->domid, cpuid);
+    return NULL;
+}
+
+
+/* Accessors for guest user registers */
+static void kdd_get_regs_x86_32(struct hvm_hw_cpu *cpu, kdd_regs_x86_32 *r)
+{
+    r->gs     = cpu->gs_sel;
+    r->fs     = cpu->fs_sel;
+    r->es     = cpu->es_sel;
+    r->ds     = cpu->ds_sel;
+    r->edi    = cpu->rdi;
+    r->esi    = cpu->rsi;
+    r->ebx    = cpu->rbx;
+    r->edx    = cpu->rdx;
+    r->ecx    = cpu->rcx;
+    r->eax    = cpu->rax;
+    r->ebp    = cpu->rbp;
+    r->eip    = cpu->rip;
+    r->cs     = cpu->cs_sel;
+    r->eflags = cpu->rflags;
+    r->esp    = cpu->rsp;
+    r->ss     = cpu->ss_sel;
+    memcpy(r->fp, cpu->fpu_regs, 112); // 108 save area + 4 of ???
+}
+
+static void kdd_set_regs_x86_32(struct hvm_hw_cpu *cpu, kdd_regs_x86_32 *r)
+{
+    cpu->gs_sel = r->gs;
+    cpu->fs_sel = r->fs;
+    cpu->es_sel = r->es;
+    cpu->ds_sel = r->ds;
+    cpu->rdi    = r->edi;
+    cpu->rsi    = r->esi;
+    cpu->rbx    = r->ebx;
+    cpu->rdx    = r->edx;
+    cpu->rcx    = r->ecx;
+    cpu->rax    = r->eax;
+    cpu->rbp    = r->ebp;
+    cpu->rip    = r->eip;
+    cpu->cs_sel = r->cs;
+    cpu->rflags = r->eflags;
+    cpu->rsp    = r->esp;
+    cpu->ss_sel = r->ss;
+    memcpy(cpu->fpu_regs, r->fp, 112); // 108 save area + 4 of ???
+}
+
+static void kdd_get_regs_x86_64(struct hvm_hw_cpu *cpu, kdd_regs_x86_64 *r)
+{
+    // XXX debug pattern
+    uint16_t i;
+    for (i = 0 ; i < (sizeof *r / 2) ; i++)
+        ((uint16_t *)r)[i] = i;
+
+    r->cs     = cpu->cs_sel;
+    r->ds     = cpu->ds_sel;
+    r->es     = cpu->es_sel;
+    r->fs     = cpu->fs_sel;
+    r->gs     = cpu->gs_sel;
+    r->ss     = cpu->ss_sel;
+    r->rflags = cpu->rflags;
+    r->dr0    = cpu->dr0;
+    r->dr1    = cpu->dr1;
+    r->dr2    = cpu->dr2;
+    r->dr3    = cpu->dr3;
+    r->dr6    = cpu->dr6;
+    r->dr7    = cpu->dr7;
+    r->rax    = cpu->rax;
+    r->rcx    = cpu->rcx;
+    r->rdx    = cpu->rdx;
+    r->rbx    = cpu->rbx;
+    r->rsp    = cpu->rsp;
+    r->rbp    = cpu->rbp;
+    r->rsi    = cpu->rsi;
+    r->rdi    = cpu->rdi;
+    r->r8     = cpu->r8;
+    r->r9     = cpu->r9;
+    r->r10    = cpu->r10;
+    r->r11    = cpu->r11;
+    r->r12    = cpu->r12;
+    r->r13    = cpu->r13;
+    r->r14    = cpu->r14;
+    r->r15    = cpu->r15;
+    r->rip    = cpu->rip;
+    memcpy(r->fp, cpu->fpu_regs, 112); // Definitely not right
+}
+
+static void kdd_set_regs_x86_64(struct hvm_hw_cpu *cpu, kdd_regs_x86_64 *r)
+{
+    cpu->cs_sel = r->cs;
+    cpu->ds_sel = r->ds;
+    cpu->es_sel = r->es;
+    cpu->fs_sel = r->fs;
+    cpu->gs_sel = r->gs;
+    cpu->ss_sel = r->ss;
+    cpu->rflags = r->rflags;
+    cpu->dr0    = r->dr0;
+    cpu->dr1    = r->dr1;
+    cpu->dr2    = r->dr2;
+    cpu->dr3    = r->dr3;
+    cpu->dr6    = r->dr6;
+    cpu->dr7    = r->dr7;
+    cpu->rax    = r->rax;
+    cpu->rcx    = r->rcx;
+    cpu->rdx    = r->rdx;
+    cpu->rbx    = r->rbx;
+    cpu->rsp    = r->rsp;
+    cpu->rbp    = r->rbp;
+    cpu->rsi    = r->rsi;
+    cpu->rdi    = r->rdi;
+    cpu->r8     = r->r8;
+    cpu->r9     = r->r9;
+    cpu->r10    = r->r10;
+    cpu->r11    = r->r11;
+    cpu->r12    = r->r12;
+    cpu->r13    = r->r13;
+    cpu->r14    = r->r14;
+    cpu->r15    = r->r15;
+    cpu->rip    = r->rip;
+    memcpy(r->fp, cpu->fpu_regs, 112); // Definitely not right
+}
+
+
+int kdd_get_regs(kdd_guest *g, int cpuid, kdd_regs *r, int w64)
+{
+    struct hvm_hw_cpu *cpu; 
+    
+    cpu = get_cpu(g, cpuid);
+    if (!cpu) 
+        return -1;
+
+    memset(r, 0, sizeof(r));
+    
+    if (w64)
+        kdd_get_regs_x86_64(cpu, &r->r64);
+    else
+        kdd_get_regs_x86_32(cpu, &r->r32);
+
+    return 0;
+}
+
+int kdd_set_regs(kdd_guest *g, int cpuid, kdd_regs *r, int w64)
+{
+    struct hvm_hw_cpu *cpu; 
+    
+    cpu = get_cpu(g, cpuid);
+    if (!cpu) 
+        return -1;
+    
+    if (w64)
+        kdd_set_regs_x86_64(cpu, &r->r64);
+    else
+        kdd_set_regs_x86_32(cpu, &r->r32);
+
+    hvm_writeback(g);
+    return 0;
+}
+
+
+/* Accessors for guest control registers */
+static void kdd_get_ctrl_x86_32(struct hvm_hw_cpu *cpu, kdd_ctrl_x86_32 *c)
+{    
+    c->cr0 = cpu->cr0;
+    c->cr2 = cpu->cr2;
+    c->cr3 = cpu->cr3;
+    c->cr4 = cpu->cr4;
+    c->dr0 = cpu->dr0;
+    c->dr1 = cpu->dr1;
+    c->dr2 = cpu->dr2;
+    c->dr3 = cpu->dr3;
+    c->dr6 = cpu->dr6;
+    c->dr7 = cpu->dr7;
+    c->gdt_base = cpu->gdtr_base;
+    c->gdt_limit = cpu->gdtr_limit;
+    c->idt_base = cpu->idtr_base;
+    c->idt_limit = cpu->idtr_limit;
+    c->tss_sel = cpu->tr_sel;
+    c->ldt_sel = cpu->ldtr_sel;
+}
+
+static void kdd_get_ctrl_x86_64(struct hvm_hw_cpu *cpu, 
+                                struct hvm_hw_lapic_regs *lapic,
+                                kdd_ctrl_x86_64 *c)
+{    
+    c->cr0 = cpu->cr0;
+    c->cr2 = cpu->cr2;
+    c->cr3 = cpu->cr3;
+    c->cr4 = cpu->cr4;
+    c->dr0 = cpu->dr0;
+    c->dr1 = cpu->dr1;
+    c->dr2 = cpu->dr2;
+    c->dr3 = cpu->dr3;
+    c->dr6 = cpu->dr6;
+    c->dr7 = cpu->dr7;
+    c->gdt_base = cpu->gdtr_base;
+    c->gdt_limit = cpu->gdtr_limit;
+    c->idt_base = cpu->idtr_base;
+    c->idt_limit = cpu->idtr_limit;
+    c->tss_sel = cpu->tr_sel;
+    c->ldt_sel = cpu->ldtr_sel;
+    c->cr8 = lapic->data[0x80] >> 4; /* Top half of the low byte of the TPR */
+}
+
+
+int kdd_get_ctrl(kdd_guest *g, int cpuid, kdd_ctrl *ctrl, int w64)
+{
+    struct hvm_hw_cpu *cpu; 
+    struct hvm_hw_lapic_regs *lapic;
+
+    cpu = get_cpu(g, cpuid);
+    if (!cpu)
+        return -1;
+
+    if (w64) {
+        lapic = get_lapic(g, cpuid);
+        if (!lapic)
+            return -1;
+        kdd_get_ctrl_x86_64(cpu, lapic, &ctrl->c64);
+    } else {
+        kdd_get_ctrl_x86_32(cpu, &ctrl->c32);
+    }
+
+    return 0;
+}
+
+int kdd_wrmsr(kdd_guest *g, int cpuid, uint32_t msr, uint64_t value)
+{
+    struct hvm_hw_cpu *cpu;
+
+    cpu = get_cpu(g, cpuid);
+    if (!cpu)
+        return -1;
+    
+    switch (msr) {
+    case 0x00000174: cpu->sysenter_cs = value; break;
+    case 0x00000175: cpu->sysenter_esp = value; break;
+    case 0x00000176: cpu->sysenter_eip = value; break;
+    case 0xc0000080: cpu->msr_efer = value; break;
+    case 0xc0000081: cpu->msr_star = value; break;
+    case 0xc0000082: cpu->msr_lstar = value; break;
+    case 0xc0000083: cpu->msr_cstar = value; break;
+    case 0xc0000084: cpu->msr_syscall_mask = value; break;
+    case 0xc0000100: cpu->fs_base = value; break;
+    case 0xc0000101: cpu->gs_base = value; break;
+    case 0xc0000102: cpu->shadow_gs = value; break;
+    default:
+        return -1;
+    }
+
+    hvm_writeback(g);
+    return 0;   
+}
+
+int kdd_rdmsr(kdd_guest *g, int cpuid, uint32_t msr, uint64_t *value)
+{
+    struct hvm_hw_cpu *cpu;
+
+    cpu = get_cpu(g, cpuid);
+    if (!cpu)
+        return -1;
+    
+    switch (msr) {
+    case 0x00000174: *value = cpu->sysenter_cs; break;
+    case 0x00000175: *value = cpu->sysenter_esp; break;
+    case 0x00000176: *value = cpu->sysenter_eip; break;
+    case 0xc0000080: *value = cpu->msr_efer; break;
+    case 0xc0000081: *value = cpu->msr_star; break;
+    case 0xc0000082: *value = cpu->msr_lstar; break;
+    case 0xc0000083: *value = cpu->msr_cstar; break;
+    case 0xc0000084: *value = cpu->msr_syscall_mask; break;
+    case 0xc0000100: *value = cpu->fs_base; break;
+    case 0xc0000101: *value = cpu->gs_base; break;
+    case 0xc0000102: *value = cpu->shadow_gs; break;
+    default:
+        return -1;
+    }
+
+    return 0;   
+}
+
+
+/* Accessor for guest physical memory */
+static uint32_t kdd_access_physical_page(kdd_guest *g, uint64_t addr, 
+                                         uint32_t len, uint8_t *buf, int write)
+{
+    uint32_t map_pfn, map_offset;
+    uint8_t *map;
+
+    map_pfn = (addr >> PAGE_SHIFT);
+    map_offset = addr & (PAGE_SIZE - 1);
+
+    /* Evict any mapping of the wrong frame from our slot */ 
+    if (g->pfns[map_pfn % MAPSIZE] != map_pfn
+        && g->maps[map_pfn % MAPSIZE] != NULL) {
+        munmap(g->maps[map_pfn % MAPSIZE], PAGE_SIZE);
+        g->maps[map_pfn % MAPSIZE] = NULL;
+    }
+    g->pfns[map_pfn % MAPSIZE] = map_pfn;
+
+    /* Now map the frame if it's not already there */
+    if (g->maps[map_pfn % MAPSIZE] != NULL)
+        map = g->maps[map_pfn % MAPSIZE];
+    else {
+        map = xc_map_foreign_range(g->xc_handle,
+                                   g->domid,
+                                   PAGE_SIZE,
+                                   PROT_READ|PROT_WRITE,
+                                   map_pfn);
+
+        KDD_DEBUG(g, "map: %u, 0x%16.16"PRIx32": %p +0x%"PRIx32"\n",
+                  write ? PROT_READ|PROT_WRITE : PROT_READ,
+                  map_pfn, map, map_offset);
+
+        if (!map) 
+            return 0;
+        g->maps[map_pfn % MAPSIZE] = map;
+    }
+
+    if (write) 
+        memcpy(map + map_offset, buf, len);
+    else
+        memcpy(buf, map + map_offset, len);
+
+    return len;
+}
+
+uint32_t kdd_access_physical(kdd_guest *g, uint64_t addr, 
+                             uint32_t len, uint8_t *buf, int write)
+{
+    uint32_t chunk, rv, done = 0;
+    while (len > 0) {
+        chunk = PAGE_SIZE - (addr & (PAGE_SIZE - 1));
+        if (chunk > len) 
+            chunk = len;
+        rv = kdd_access_physical_page(g, addr, chunk, buf, write);
+        done += rv;
+        if (rv != chunk)
+            return done;
+        addr += chunk;
+        buf += chunk;
+        len -= chunk;
+    }
+    return done;
+}
+
+
+/* Plumb libxc log messages into our own logging */
+static void kdd_xc_log(struct xentoollog_logger *logger,
+                       xentoollog_level level,
+                       int errnoval /* or -1 */,
+                       const char *context /* eg "xc", "xl", may be 0 */,
+                       const char *format /* without level, context, \n */,
+                       va_list al)
+{
+    kdd_guest *g = (kdd_guest *) logger;
+    /* Suppress most libxc levels unless we're logging at debug level */
+    if (g->verbosity < 1 || (level < XTL_WARN && g->verbosity < 3))
+        return;
+    fprintf(g->log, "libxc[%s:%i:%i]: ", context ? : "?", level, errnoval);
+    vfprintf(g->log, format, al);
+    fprintf(g->log, "\n");
+    (void) fflush(g->log);
+}
+
+
+/* Set up guest-specific state */
+kdd_guest *kdd_guest_init(char *arg, FILE *log, int verbosity)
+{
+    kdd_guest *g = NULL;
+    xc_interface *xch = NULL;
+    uint32_t domid;
+    xc_dominfo_t info;
+
+    g = calloc(1, sizeof (kdd_guest));
+    if (!g) 
+        goto err;
+    g->log = log;
+    g->verbosity = verbosity;
+    g->xc_log.vmessage = kdd_xc_log;
+
+    xch = xc_interface_open(&g->xc_log, NULL, 0);
+    if (!xch)
+        goto err;
+    g->xc_handle = xch;
+
+    domid = strtoul(arg, NULL, 0);
+    if (domid == 0)
+        goto err;
+    g->domid = domid;
+
+    /* Check that the domain exists and is HVM */
+    if (xc_domain_getinfo(xch, domid, 1, &info) != 1 || !info.hvm)
+        goto err;
+
+    snprintf(g->id, (sizeof g->id) - 1, 
+             "a xen guest with domain id %i", g->domid);
+
+    return g;
+
+ err:
+    free(g);
+    if (xch)
+        xc_interface_close(xch);
+    return NULL;
+}
+
+/* Say what kind of guest this is */
+char *kdd_guest_identify(kdd_guest *g)
+{
+    return g->id;
+}
+
+/* Tear down guest-specific state */
+void kdd_guest_teardown(kdd_guest *g)
+{
+    flush_maps(g);
+    xc_interface_close(g->xc_handle);
+    free(g->id);
+    free(g->hvm_buf);
+    free(g);
+}
diff -r ac7f64d5577b tools/debugger/kdd/kdd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/kdd/kdd.c  Mon Oct 25 15:12:21 2010 +0100
@@ -0,0 +1,1045 @@
+/*
+ * kdd.c -- stub for debugging guest OSes with the windows kernel debugger.
+ *
+ * Tim Deegan <Tim.Deegan@xxxxxxxxxx>
+ * 
+ * Copyright (c) 2007-2010, Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <netdb.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/select.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include "kdd.h"
+
+/* Windows version details */
+typedef struct {
+    uint32_t build;             
+    int w64;
+    int mp;
+    char *name;
+    uint64_t base;              /* KernBase: start looking here */
+    uint32_t range;             /* |         and search an area this size */
+    uint32_t version;           /* +-> NtBuildNumber */
+    uint32_t modules;           /* +-> PsLoadedModuleList */
+    uint32_t prcbs;             /* +-> KiProcessorBlock */
+} kdd_os;
+
+/* State of the debugger stub */
+typedef struct {
+    union {
+        uint8_t txb[sizeof (kdd_hdr) + 65536];   /* Marshalling area for tx */
+        kdd_pkt txp;                 /* Also readable as a packet structure */
+    };
+    union {
+        uint8_t rxb[sizeof (kdd_hdr) + 65536];   /* Marshalling area for rx */
+        kdd_pkt rxp;                 /* Also readable as a packet structure */
+    };
+    unsigned int cur;       /* Offset into rx where we'll put the next byte */
+    uint32_t next_id;                     /* ID of next packet we will send */
+    int running;                      /* Are the guest's processors active? */
+    int cpuid;                                      /* Current selected CPU */
+    int fd;                                  /* TCP socket for client comms */
+    FILE *log;                                        /* For tracing output */
+    int verbosity;                              /* How much detail to trace */
+    kdd_guest *guest;              /* Arch-specific state for guest control */
+    kdd_os os;                                 /* OS-specific magic numbers */
+} kdd_state;
+
+/*****************************************************************************
+ *  Utility functions
+ */
+
+/* Get the instruction pointer */
+static uint64_t kdd_get_ip(kdd_state *s)
+{
+    kdd_regs r;
+    if (!s->os.w64 && kdd_get_regs(s->guest, s->cpuid, &r, 0) == 0)
+        return r.r32.eip;
+    else if (s->os.w64 && kdd_get_regs(s->guest, s->cpuid, &r, 1) == 0)
+        return r.r64.rip;
+    else
+        return -1ULL;
+}
+
+/* Turn write(2) into a proper blocking write. */
+static size_t blocking_write(int fd, const void *buf, size_t count)
+{
+    size_t left = count;
+    ssize_t r;
+    while (left > 0 && ((r = write(fd, buf, left)) >= 0 || errno == EINTR)) {
+        buf += r;
+        left -= r;
+    }
+    return count - left;
+}
+
+/* Dump the contents of a complete serial packet into a log file. */
+static void kdd_log_pkt(kdd_state *s, char *name, kdd_pkt *p)
+{
+    uint32_t sum = 0;
+    unsigned int i, j;
+    uint8_t ascii[17] = {0};
+    FILE *f = s->log;
+
+    if (s->verbosity < 2) 
+        return;
+
+    /* Re-check the checksum */
+    for (i = 0; i < p->h.len; i++)
+        sum += p->payload[i];
+
+    fprintf(f, "\n"
+            "%s: %s type 0x%4.4"PRIx16" len 0x%4.4"PRIx16
+            " id 0x%8.8"PRIx32" sum 0x%"PRIx32" (%s: 0x%"PRIx32")\n",
+            name,
+            p->h.dir == KDD_DIR_PKT ? "pkt" :
+            p->h.dir == KDD_DIR_ACK ? "ack" : "???",
+            (unsigned) p->h.type, p->h.len, p->h.id, p->h.sum,
+            sum == p->h.sum ? "OK" : "BAD", sum);
+
+    /* Hexdump the payload in "canonical" format*/
+    for (i = 0; i < p->h.len; i ++) {
+        if (i % 16 == 0) {
+            memset(ascii, 0, 17);
+            fprintf(f, "%8.8x ", i);
+        } else if (i % 8 == 0)
+            fprintf(f, " ");
+        fprintf(f, " %2.2x", p->payload[i]);
+        ascii[i % 16] = (isprint(((int)p->payload[i])) ? p->payload[i] : 0x2e);
+        if (i % 16 == 15)
+            fprintf(f, "  |%s|\n", ascii);
+    }
+    if (i % 16 != 0) {
+        for (j = i % 16 ; j < 16; j++) {
+            fprintf(f, "%s", (j == 8) ? "    " : "   ");
+        }
+        fprintf(f, "  |%s|\n%8.8x\n", ascii, i);
+    }
+
+    fprintf(f, "\n");
+    (void) fflush(f);
+}
+
+
+/*****************************************************************************
+ *  Memory access: virtual addresses and syntactic sugar.
+ */
+
+#define PAGE_SHIFT (12)
+#define PAGE_SIZE (1ULL << PAGE_SHIFT) 
+
+static uint32_t kdd_read_physical(kdd_state *s, uint64_t addr, 
+                                  uint32_t len, void *buf)
+{
+    return kdd_access_physical(s->guest, addr, len, buf, 0);
+}
+
+static uint32_t kdd_write_physical(kdd_state *s, uint64_t addr, 
+                                   uint32_t len, void *buf)
+{
+    return kdd_access_physical(s->guest, addr, len, buf, 1);
+}
+
+
+/* VA->PA conversion.  Returns -1ULL for failure. */
+static uint64_t v2p(kdd_state *s, int cpuid, uint64_t va)
+{
+    int pg, pae, pse, lma;
+    int levels, width, bits, shift, offset, i;
+    uint64_t efer, entry = 0, mask, pa;
+    kdd_ctrl ctrl;
+
+    if (kdd_get_ctrl(s->guest, cpuid, &ctrl, s->os.w64) != 0
+        || kdd_rdmsr(s->guest, cpuid, 0xc0000080, &efer) != 0)
+        return -1ULL;
+
+    if (s->os.w64) {
+        pg = !!(ctrl.c64.cr0 & 0x80000000);
+        lma = !!(efer & 0x00000400);
+        pae = !!(ctrl.c64.cr4 & 0x00000020);
+        pse = !!(ctrl.c64.cr4 & 0x00000010) || pae || lma;
+        pa = ctrl.c64.cr3 & ~0x0fULL;
+    } else {
+        pg = !!(ctrl.c32.cr0 & 0x80000000);
+        pae = !!(ctrl.c32.cr4 & 0x00000020);
+        lma = 0;
+        pse = !!(ctrl.c32.cr4 & 0x00000010) || pae;
+        pa = ctrl.c32.cr3 & ~0x0fULL;
+    }
+    KDD_DEBUG(s, "w64 = %u, pg = %u, pae = %u, pse = %u, lma = %u\n",
+              s->os.w64, pg, pae, pse, lma);
+
+    /* Paging disabled? */
+    if (!pg)
+        return va;
+    
+    /* 32/PAE64? */
+    if (lma) {
+        va &= (1ULL<<48) - 1;
+        width = 8; levels = 4; bits = 9;
+    } else {
+        va &= (1ULL<<32) - 1;
+        if (pae) {
+            width = 8; levels = 3; bits = 9;
+        } else {
+            width = 4; levels = 2; bits = 10;
+        }
+    }
+    KDD_DEBUG(s, "%i levels, va 0x%16.16"PRIx64"\n", levels, va);
+
+    /* Walk the appropriate number of levels */
+    for (i = levels; i > 0; i--) {
+        shift = PAGE_SHIFT + bits * (i-1);
+        mask = ((1ULL << bits) - 1) << shift;
+        offset = ((va & mask) >> shift) * width;
+        KDD_DEBUG(s, "level %i: mask 0x%16.16"PRIx64" pa 0x%16.16"PRIx64
+                  " offset %i\n",i, mask, pa, offset);
+        if (kdd_read_physical(s, pa + offset, width, &entry) != width)
+            return -1ULL; // Bad entry PA
+        KDD_DEBUG(s, "level %i: entry 0x%16.16"PRIx64"\n", i, entry);
+        if (!(entry & 0x1))
+            return -1ULL; // Not present
+        pa = entry & 0x000ffffffffff000ULL;
+        if (pse && (i == 2) && (entry & 0x80)) { // Superpage
+            mask = ((1ULL << (PAGE_SHIFT + bits)) - 1);
+            return (pa & ~mask) + (va & mask);
+        }
+    }
+
+    return pa + (va & (PAGE_SIZE - 1));
+}
+
+static uint32_t kdd_access_virtual(kdd_state *s, int cpuid, uint64_t addr,
+                                   uint32_t len, void *buf, int write)
+{
+    uint64_t pa;
+    uint32_t chunk, rv, done = 0;
+    
+    /* Process one page at a time */
+    while (len > 0) {
+        chunk = PAGE_SIZE - (addr & (PAGE_SIZE - 1));
+        if (chunk > len) 
+            chunk = len;
+        pa = v2p(s, cpuid, addr);
+        KDD_DEBUG(s, "va 0x%"PRIx64" -> pa 0x%"PRIx64"\n", addr, pa);
+        if (pa == (uint64_t) -1ULL) 
+            return done;
+        rv = kdd_access_physical(s->guest, pa, chunk, buf, write);
+        done += rv;
+        if (rv != chunk)
+            return done;
+        addr += chunk;
+        buf += chunk;
+        len -= chunk;
+    }
+    return done;
+}
+
+static uint32_t kdd_read_virtual(kdd_state *s, int cpuid, uint64_t addr,
+                                 uint32_t len, void *buf)
+{
+    return kdd_access_virtual(s, cpuid, addr, len, buf, 0);
+}
+
+static uint32_t kdd_write_virtual(kdd_state *s, int cpuid, uint64_t addr,
+                                  uint32_t len, void *buf)
+{
+    return kdd_access_virtual(s, cpuid, addr, len, buf, 1);
+}
+
+
+/*****************************************************************************
+ * Version information and related runes for different Windows flavours
+ */
+
+static kdd_os os[] = {
+ /* Build  64 MP Name                 &Kernel search base    Range       
+Version    +Modules    +PRCBs (64b) */
+    {2195, 0, 0, "w2k sp4 x32 UP",    0xffffffff80400000ULL, 0x00000000, 
0x0006d57c, 0x0006e1b8, 0x0},
+    {2195, 0, 1, "w2k sp4 x32 SMP",   0xffffffff80400000ULL, 0x00000000, 
0x0006fa1c, 0x00084520, 0x0},
+    // PAE/UP, PAE/SMP
+
+    {2600, 0, 0, "xp sp2 x32 UP",     0xffffffff804d7000ULL, 0x00000000, 
0x00075568, 0x00083b20, 0x0},
+    {2600, 0, 1, "xp sp2 x32 SMP",    0xffffffff804d7000ULL, 0x00000000, 
0x0007d0e8, 0x0008d4a0, 0x0},
+    // PAE/UP, PAE/SMP
+
+    {2600, 0, 0, "xp sp3 x32 UP",     0xffffffff804d7000ULL, 0x00000000, 
0x00075be8, 0x000841c0, 0x0},
+    {2600, 0, 1, "xp sp3 x32 SMP",    0xffffffff804d7000ULL, 0x00000000, 
0x0007c0e8, 0x0008c4c0, 0x0},
+    {2600, 0, 0, "xp sp3 x32p UP",    0xffffffff804d7000ULL, 0x00000000, 
0x0006e8e8, 0x0007cfc0, 0x0},
+    {2600, 0, 1, "xp sp3 x32p SMP",   0xffffffff804d7000ULL, 0x00000000, 
0x000760e8, 0x00086720, 0x0},
+
+    {3790, 0, 0, "w2k3 sp2 x32 UP",   0xffffffff80800000ULL, 0x00000000, 
0x00097128, 0x000a8e48, 0x0},
+    {3790, 0, 1, "w2k3 sp2 x32 SMP",  0xffffffff80800000ULL, 0x00000000, 
0x0009d128, 0x000af9c8, 0x0},
+    {3790, 0, 0, "w2k3 sp2 x32p UP",  0xffffffff80800000ULL, 0x00000000, 
0x0008e128, 0x0009ffa8, 0x0},
+    {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, 
0x00094128, 0x000a6ea8, 0x0},
+    {3790, 1, 0, "w2k3 sp2 x64 UP",   0xfffff80001000000ULL, 0x00000000, 
0x001765d0, 0x0019aae0, 0x0017b100},
+    {3790, 1, 1, "w2k3 sp2 x64 SMP",  0xfffff80001000000ULL, 0x00000000, 
0x001b05e0, 0x001d5100, 0x001b5300},
+
+    {6000, 0, 1, "vista sp0 x32p",    0xffffffff81800000ULL, 0x00000000, 
0x000a4de4, 0x00111db0, 0x0},
+    {6001, 0, 1, "vista sp1 x32p",    0xffffffff81000000ULL, 0x0f000000, 
0x000af0c4, 0x00117c70, 0x0},
+
+    {6001, 1, 1, "w2k8 sp0 x64",      0xfffff80001000000ULL, 0x0f000000, 
0x00140bf0, 0x001c5db0, 0x00229640},
+
+    {7600, 1, 1, "win7 sp0 x64",      0xfffff80001000000ULL, 0x0f000000, 
0x001af770, 0x0023de50, 0x002a8900},
+
+    {7601, 0, 1, "win7 sp1 x32p",     0xffffffff81800000ULL, 0x0f000000, 
0x000524c4, 0x00149850, 0x0},
+    {7601, 1, 1, "win7 sp1 x64",      0xfffff80001000000ULL, 0x0f000000, 
0x001b2770, 0x00240e90, 0x002ab900},
+};
+
+// 1381, 0, 0, "NT4 sp?", 0xffffffff80100000, ?, ?
+
+static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0};
+
+static int check_os(kdd_state *s)
+{
+    kdd_os *v = &s->os;
+    uint64_t addr, val;
+    uint32_t width;
+    int i;
+
+    /* Kernel address must be a DOS executable */
+    val = 0;
+    if (kdd_read_virtual(s, 0, v->base, 2, &val) != 2 || val != 0x5a4d) {
+        KDD_DEBUG(s, "not %s: krnl 0x%"PRIx64"\n", v->name, val);
+        return 0;
+    }
+
+    /* OS version must match. */
+    val = 0;
+    if (kdd_read_virtual(s, 0, v->base + v->version, 4, &val) != 4
+        || val != (v->build | 0xf0000000) ) {
+        KDD_DEBUG(s, "not %s: version 0x%"PRIx64"\n", v->name, val);
+        return 0;
+    }
+    
+    /* Module list address must be a circular linked list */
+    addr = v->base + v->modules;
+    val = 0;
+    width = v->w64 ? 8 : 4;
+    for (i = 0; val != v->base + v->modules && i < 250; i++) {
+        val = 0;
+        if (kdd_read_virtual(s, 0, addr, width, &val) != width) {
+            KDD_DEBUG(s, "not %s: bad module list\n", v->name);
+            return 0;
+        }
+        addr = val;
+    }
+
+    return 1;
+}
+
+/* Figure out what OS we're dealing with */
+static void find_os(kdd_state *s)
+{
+    int i;
+    uint64_t limit; 
+
+    /* We may already have the right one */
+    if (check_os(s))
+        return;
+
+    /* Try each OS we know about */
+    for (i = 0; i < (sizeof os / sizeof os[0]); i++) {
+        s->os = os[i];
+        /* Try each page in the potential range of kernel load addresses */
+        for (limit = s->os.base + s->os.range;
+             s->os.base <= limit;
+             s->os.base += PAGE_SIZE)
+            if (check_os(s))
+                return;
+    }
+    s->os = unknown_os;
+}
+
+
+/*****************************************************************************
+ *  How to send packets and acks.
+ */
+
+
+/* Send a serial packet */
+static void kdd_tx(kdd_state *s)
+{
+    uint32_t sum = 0;
+    size_t len;
+    int i;
+
+    /* Fix up the checksum before we send */
+    for (i = 0; i < s->txp.h.len; i++)
+        sum += s->txp.payload[i];
+    s->txp.h.sum = sum;
+
+    kdd_log_pkt(s, "TX", &s->txp);
+
+    len = s->txp.h.len + sizeof (kdd_hdr);
+    if (s->txp.h.dir == KDD_DIR_PKT)
+        /* Append the mysterious 0xaa byte to each packet */
+        s->txb[len++] = 0xaa;
+
+    (void) blocking_write(s->fd, s->txb, len);
+}
+
+
+/* Send an acknowledgement to the client */
+static void kdd_send_ack(kdd_state *s, uint32_t id, uint16_t type)
+{
+    s->txp.h.dir = KDD_DIR_ACK;
+    s->txp.h.type = type;
+    s->txp.h.len = 0;
+    s->txp.h.id = id;
+    s->txp.h.sum = 0;
+    kdd_tx(s);
+}
+
+/* Send a command_packet to the client */
+static void kdd_send_cmd(kdd_state *s, uint32_t subtype, size_t extra)
+{
+    s->txp.h.dir = KDD_DIR_PKT;
+    s->txp.h.type = KDD_PKT_CMD;
+    s->txp.h.len = sizeof (kdd_cmd) + extra;
+    s->txp.h.id = (s->next_id ^= 1);
+    s->txp.h.sum = 0;
+    s->txp.cmd.subtype = subtype;
+    kdd_tx(s);
+}
+
+/* Cause the client to print a string */
+static void kdd_send_string(kdd_state *s, char *fmt, ...)
+{
+    uint32_t len = 0xffff - sizeof (kdd_msg);
+    char *buf = (char *) s->txb + sizeof (kdd_hdr) + sizeof (kdd_msg);
+    va_list ap;
+    
+    va_start(ap, fmt);
+    len = vsnprintf(buf, len, fmt, ap);
+    va_end(ap);
+
+    s->txp.h.dir = KDD_DIR_PKT;
+    s->txp.h.type = KDD_PKT_MSG;
+    s->txp.h.len = sizeof (kdd_msg) + len;
+    s->txp.h.id = (s->next_id ^= 1);
+    s->txp.h.sum = 0;
+    s->txp.msg.subtype = KDD_MSG_PRINT;
+    s->txp.msg.length = len;
+    kdd_tx(s);
+}
+
+
+/* Stop the guest and prepare for debugging */
+static void kdd_break(kdd_state *s)
+{
+    uint16_t ilen;
+    KDD_LOG(s, "Break\n");
+
+    if (s->running)
+        kdd_halt(s->guest);
+    s->running = 0;
+
+    {
+        unsigned int i;
+        /* XXX debug pattern */
+        for (i = 0; i < 0x100 ; i++) 
+            s->txb[sizeof (kdd_hdr) + i] = i;
+    }
+
+    /* Send a state-change message to the client so it knows we've stopped */
+    s->txp.h.dir = KDD_DIR_PKT;
+    s->txp.h.type = KDD_PKT_STC;
+    s->txp.h.len = sizeof (kdd_stc);
+    s->txp.h.id = (s->next_id ^= 1);
+    s->txp.stc.subtype = KDD_STC_STOP;
+    s->txp.stc.stop.cpu = s->cpuid;
+    s->txp.stc.stop.ncpus = kdd_count_cpus(s->guest); 
+    s->txp.stc.stop.kthread = 0; /* Let the debugger figure it out */
+    s->txp.stc.stop.status = KDD_STC_STATUS_BREAKPOINT;
+    s->txp.stc.stop.rip1 = s->txp.stc.stop.rip2 = kdd_get_ip(s);
+    s->txp.stc.stop.nparams = 0;
+    s->txp.stc.stop.first_chance = 1;
+    ilen = kdd_read_virtual(s, s->cpuid, s->txp.stc.stop.rip1,
+                            sizeof s->txp.stc.stop.inst, s->txp.stc.stop.inst);
+    s->txp.stc.stop.ilen = ilen;
+    /* XXX other fields */
+
+    kdd_tx(s);
+}
+
+/* Handle an acknowledgement received from the client */
+static void kdd_handle_ack(kdd_state *s, uint32_t id, uint16_t type)
+{
+    switch (type) {
+    case KDD_ACK_OK:
+    case KDD_ACK_BAD:
+        break;
+    case KDD_ACK_RST:
+        if (id == 0) {
+            KDD_LOG(s, "Client requests a reset\n");
+            kdd_send_ack(s, 0xdeadbeef, KDD_ACK_RST);
+            kdd_send_string(s, "[kdd: connected to %s]\r\n", 
+                            kdd_guest_identify(s->guest));
+            kdd_break(s);
+        }
+        break;
+    default:
+        KDD_LOG(s, "Unhandled ACK type 0x%4.4x\n", type);
+        break;
+    }
+}
+
+/*****************************************************************************
+ *  Handlers for each kind of client packet
+ */
+
+
+/* Handle the initial handshake */
+static void kdd_handle_handshake(kdd_state *s)
+{
+    /* Figure out what we're looking at */
+    find_os(s);
+    kdd_send_string(s, "[kdd: %s @0x%"PRIx64"]\r\n", s->os.name, s->os.base);
+
+    /* Respond with some details about the debugger stub we simulate */
+    s->txp.cmd.shake.u1        = 0x01010101;
+    s->txp.cmd.shake.status    = KDD_STATUS_SUCCESS;
+    s->txp.cmd.shake.u2        = 0x02020202;
+    s->txp.cmd.shake.v_major   = 0xf;
+    s->txp.cmd.shake.v_minor   = s->os.build;
+    s->txp.cmd.shake.proto     = 6;
+    s->txp.cmd.shake.flags     = (0x02 /* ??? */
+                                  | (s->os.mp ? KDD_FLAGS_MP : 0)
+                                  | (s->os.w64 ? KDD_FLAGS_64 : 0));
+    s->txp.cmd.shake.machine   = s->os.w64 ? KDD_MACH_x64 : KDD_MACH_x32;
+    s->txp.cmd.shake.pkts      = KDD_PKT_MAX;
+    s->txp.cmd.shake.states    = 0xc; /* ??? */
+    s->txp.cmd.shake.manips    = 0x2e; /* ??? */
+    s->txp.cmd.shake.u3[0]     = 0x33;
+    s->txp.cmd.shake.u3[1]     = 0x44;
+    s->txp.cmd.shake.u3[2]     = 0x55;
+    s->txp.cmd.shake.kern_addr = s->os.base;
+    s->txp.cmd.shake.mods_addr = s->os.base + s->os.modules;
+    s->txp.cmd.shake.data_addr = 0; /* Debugger data probably doesn't exist */
+
+    KDD_LOG(s, "Client initial handshake: %s\n", s->os.name);
+    kdd_send_cmd(s, KDD_CMD_SHAKE, 0);
+}
+
+/* Handle set-cpu command */
+static void kdd_handle_setcpu(kdd_state *s)
+{
+    KDD_LOG(s, "Switch to CPU %u\n", s->rxp.cmd.setcpu.cpu);
+
+    /* This command doesn't get a direct response; instead we send a STOP. */
+    s->cpuid = s->rxp.cmd.setcpu.cpu;
+    kdd_break(s);
+
+    /* XXX find out whether kd will  be happier if we respond to this command 
after the break. */
+}
+
+/* Handle breakpoint commands */
+static void kdd_handle_soft_breakpoint(kdd_state *s)
+{
+    KDD_LOG(s, "Soft breakpoint %#"PRIx32" op %#"PRIx32"/%#"PRIx32"\n",
+            s->rxp.cmd.sbp.bp, s->rxp.cmd.sbp.u1, s->rxp.cmd.sbp.u2);
+    
+    /* Pretend we did something */
+    s->txp.cmd.sbp.u1     = s->rxp.cmd.sbp.u1;
+    s->txp.cmd.sbp.status = KDD_STATUS_SUCCESS;    
+    s->txp.cmd.sbp.u2     = s->rxp.cmd.sbp.u2;
+    s->txp.cmd.sbp.bp     = s->rxp.cmd.sbp.bp;
+    kdd_send_cmd(s, KDD_CMD_SOFT_BP, 0);
+}
+
+static void kdd_handle_hard_breakpoint(kdd_state *s)
+{
+    KDD_LOG(s, "Hard breakpoint @%#"PRIx64"\n", s->rxp.cmd.hbp.address);
+
+    kdd_send_string(s, "[kdd: breakpoints aren't implemented yet]\r\n");
+
+    s->txp.cmd.hbp.status = KDD_STATUS_FAILURE;
+    s->txp.cmd.hbp.address = s->rxp.cmd.hbp.address;    
+    kdd_send_cmd(s, KDD_CMD_HARD_BP, 0);
+}
+
+/* Register access */
+static void kdd_handle_read_regs(kdd_state *s)
+{
+    kdd_regs regs;
+    uint32_t len = s->os.w64 ? sizeof regs.r64 : sizeof regs.r32;
+    int cpuid = s->rxp.cmd.regs.cpu;
+
+    KDD_LOG(s, "Read CPU %i register state\n", cpuid);
+    if (kdd_get_regs(s->guest, cpuid, &regs, s->os.w64) == 0) {
+        memcpy(s->txb + sizeof (kdd_hdr) + sizeof (kdd_cmd), &regs, len);
+        s->txp.cmd.regs.status = KDD_STATUS_SUCCESS;
+    } else {
+        len = 0;
+        s->txp.cmd.regs.status = KDD_STATUS_FAILURE;
+    }
+    s->txp.cmd.regs.cpu = cpuid;
+    kdd_send_cmd(s, KDD_CMD_READ_REGS, len);
+}
+
+static void kdd_handle_write_regs(kdd_state *s)
+{
+    kdd_regs regs;
+    uint32_t len = s->rxp.h.len - sizeof (kdd_cmd);
+    uint32_t regsz = s->os.w64 ? sizeof regs.r64 : sizeof regs.r32;
+    int cpuid = s->rxp.cmd.regs.cpu;
+
+    KDD_LOG(s, "Write CPU %i register state\n", cpuid);
+    s->txp.cmd.regs.status = KDD_STATUS_FAILURE;
+    if (len >= regsz) {
+        memcpy(&regs, s->rxb + sizeof (kdd_hdr) + sizeof (kdd_cmd), regsz);
+        if (kdd_set_regs(s->guest, cpuid, &regs, s->os.w64) == 0)
+            s->txp.cmd.regs.status = KDD_STATUS_SUCCESS;
+    }
+    s->txp.cmd.regs.cpu = cpuid;
+    kdd_send_cmd(s, KDD_CMD_WRITE_REGS, 0);
+}
+
+/* Report control state to the guest */
+static void kdd_handle_read_ctrl(kdd_state *s)
+{
+    int i;
+    kdd_ctrl ctrl;
+    uint8_t *buf = s->txb + sizeof (kdd_hdr) + sizeof (kdd_cmd);
+    uint32_t len = s->rxp.cmd.mem.length_req;
+    uint64_t val, addr = s->rxp.cmd.mem.addr;
+    KDD_LOG(s, "Read control state: %"PRIu32" bytes @ 0x%"PRIx64"\n",
+            len, addr);
+
+    if (len > (65536 - sizeof(kdd_cmd)))
+        len = 65536 - sizeof(kdd_cmd);
+
+    /* Default contents: a debug-friendly pattern */
+    for (i = 0; i < len; i++)
+        ((uint8_t*)buf)[i] = (uint8_t) (addr + i);
+
+    if (kdd_get_ctrl(s->guest, s->cpuid, &ctrl, s->os.w64)) {
+        len = 0;
+    } else if (s->os.w64) {
+        /* Annoyingly, 64-bit kd relies on the kernel to point it at
+         * datastructures it could easily find itself with VA reads. */
+        switch (addr) {
+        case 0x0: /* KPCR */
+        case 0x1: /* KPRCB */
+        case 0x3: /* KTHREAD */
+            /* First find the PCRB's address */
+            len = kdd_read_virtual(s, s->cpuid, 
+                                   s->os.base + s->os.prcbs + 8 * s->cpuid, 
+                                   8, &val);
+            if (len != 8)
+                break;
+            /* The PCR lives 0x180 bytes before the PRCB */
+            if (addr == 0) 
+                val -= 0x180; 
+            /* The current thread's address is at offset 0x8 into the PRCB. */
+            else if (addr == 3)
+                len = kdd_read_virtual(s, s->cpuid, val + 8, 8, &val);
+            *(uint64_t *)buf = val; 
+            break;
+        case 0x2: /* Control registers */
+            if (len > sizeof ctrl.c64) 
+                len = sizeof ctrl.c64;
+            memcpy(buf, (uint8_t *)&ctrl, len);
+            break;
+        default:
+            KDD_LOG(s, "Unknown control space 0x%"PRIx64"\n", addr);
+            len = 0;
+        }
+    } else {
+        /* 32-bit control-register space starts at 0x[2]cc, for 84 bytes */
+        uint64_t offset = addr;
+        if (offset > 0x200)
+            offset -= 0x200;
+        offset -= 0xcc;
+        if (offset > sizeof ctrl.c32 || offset + len > sizeof ctrl.c32) {
+            KDD_LOG(s, "Request outside of known control space\n");
+            len = 0;
+        } else {
+            memcpy(buf, ((uint8_t *)&ctrl.c32) + offset, len);
+        }
+    }
+
+    s->txp.cmd.mem.addr = addr;
+    s->txp.cmd.mem.length_req = s->rxp.cmd.mem.length_req;
+    s->txp.cmd.mem.length_rsp = len;
+    s->txp.cmd.mem.status = ((len) ? KDD_STATUS_SUCCESS : KDD_STATUS_FAILURE);
+    kdd_send_cmd(s, KDD_CMD_READ_CTRL, len);
+}
+
+/* MSR access */
+static void kdd_handle_read_msr(kdd_state *s)
+{
+    uint32_t msr = s->rxp.cmd.msr.msr;
+    int ok;
+    KDD_LOG(s, "Read MSR 0x%"PRIx32"\n", msr);
+
+    ok = (kdd_rdmsr(s->guest, s->cpuid, msr, &s->txp.cmd.msr.val) == 0);
+    s->txp.cmd.msr.msr = msr;
+    s->txp.cmd.msr.status = (ok ? KDD_STATUS_SUCCESS : KDD_STATUS_FAILURE);
+    kdd_send_cmd(s, KDD_CMD_READ_MSR, 0);
+}
+
+static void kdd_handle_write_msr(kdd_state *s)
+{
+    uint32_t msr = s->rxp.cmd.msr.msr;
+    uint64_t val = s->rxp.cmd.msr.val;
+    int ok;
+    KDD_LOG(s, "Write MSR 0x%"PRIx32" = 0x%"PRIx64"\n", msr, val);
+
+    ok = (kdd_wrmsr(s->guest, s->cpuid, msr, val) == 0);
+    s->txp.cmd.msr.msr = msr;
+    s->txp.cmd.msr.status = (ok ? KDD_STATUS_SUCCESS : KDD_STATUS_FAILURE);
+    kdd_send_cmd(s, KDD_CMD_WRITE_MSR, 0);
+}
+
+/* Read and write guest memory */
+static void kdd_handle_memory_access(kdd_state *s)
+{
+    uint32_t len = s->rxp.cmd.mem.length_req;
+    uint64_t addr = s->rxp.cmd.mem.addr;
+    uint8_t *buf;
+
+    KDD_LOG(s, "Memory access \"%c%c\" (%s): %"PRIu32" bytes"
+            " @ 0x%"PRIx64"\n", 
+            s->rxp.cmd.subtype & 0xff, (s->rxp.cmd.subtype >>8) & 0xff, 
+            s->rxp.cmd.subtype == KDD_CMD_READ_VA ? "read virt" :
+            s->rxp.cmd.subtype == KDD_CMD_WRITE_VA ? "write virt" :
+            s->rxp.cmd.subtype == KDD_CMD_READ_PA ? "read phys" :
+            s->rxp.cmd.subtype == KDD_CMD_WRITE_PA ? "write phys" : "unknown",
+            len, addr);
+
+    if (len > (65536 - sizeof(kdd_cmd)))
+        len = 65536 - sizeof(kdd_cmd);
+
+    switch(s->rxp.cmd.subtype) {
+    case KDD_CMD_READ_VA:
+        buf = s->txb + sizeof (kdd_hdr) + sizeof (kdd_cmd);
+        len = kdd_read_virtual(s, s->cpuid, addr, len, buf);
+        break;
+    case KDD_CMD_WRITE_VA:
+        buf = s->rxb + sizeof (kdd_hdr) + sizeof (kdd_cmd);
+        len = kdd_write_virtual(s, s->cpuid, addr, len, buf);
+        break;
+    case KDD_CMD_READ_PA:
+        buf = s->txb + sizeof (kdd_hdr) + sizeof (kdd_cmd);
+        len = kdd_read_physical(s, addr, len, buf);
+        break;
+    case KDD_CMD_WRITE_PA:
+        buf = s->rxb + sizeof (kdd_hdr) + sizeof (kdd_cmd);
+        len = kdd_write_physical(s, addr, len, buf);
+        break;
+    }
+    KDD_DEBUG(s, "access returned %"PRIu32"\n", len);
+
+    s->txp.cmd.mem.addr = addr;
+    s->txp.cmd.mem.length_req = s->rxp.cmd.mem.length_req;
+    s->txp.cmd.mem.length_rsp = len;
+    s->txp.cmd.mem.status = (len) ? KDD_STATUS_SUCCESS : KDD_STATUS_FAILURE;
+    kdd_send_cmd(s, s->rxp.cmd.subtype, len);
+}
+
+
+/* Handle a packet received from the client */
+static void kdd_handle_pkt(kdd_state *s, kdd_pkt *p)
+{
+    uint32_t sum = 0;
+    int i;
+
+    /* Simple checksum: add all the bytes */
+    for (i = 0; i < p->h.len; i++)
+        sum += p->payload[i];
+    if (p->h.sum != sum) {
+        kdd_send_ack(s, p->h.id, KDD_ACK_BAD);
+        return;
+    }
+
+    /* We only understand one kind of packet from the client */
+    if (p->h.type != KDD_PKT_CMD) {
+        KDD_LOG(s, "Unhandled PKT type 0x%4.4x\n", p->h.type);
+        kdd_send_ack(s, p->h.id, KDD_ACK_BAD);
+        return;
+    }
+
+    /* Ack the packet */
+    kdd_send_ack(s, p->h.id, KDD_ACK_OK);
+
+    /* Clear the TX buffer just for sanity */
+    memset(s->txb, 0, sizeof(s->txb));
+
+    switch (p->cmd.subtype) {
+    case KDD_CMD_CONT1:
+    case KDD_CMD_CONT2:
+        KDD_LOG(s, "Continue: 0x%8.8"PRIx32"\n", p->cmd.cont.reason1);
+        if (!s->running)
+            kdd_run(s->guest);
+        s->running = 1;
+        /* No reply, just carry on running */
+        break;
+    case KDD_CMD_SHAKE:
+        kdd_handle_handshake(s);
+        break;
+    case KDD_CMD_SOFT_BP:
+        kdd_handle_soft_breakpoint(s);
+        break;
+    case KDD_CMD_HARD_BP:
+        kdd_handle_hard_breakpoint(s);
+        break;
+    case KDD_CMD_READ_REGS:
+        kdd_handle_read_regs(s);
+        break;
+    case KDD_CMD_WRITE_REGS:
+        kdd_handle_write_regs(s);
+        break;
+    case KDD_CMD_READ_CTRL:
+        kdd_handle_read_ctrl(s);
+        break;
+    case KDD_CMD_READ_MSR:
+        kdd_handle_read_msr(s);
+        break;
+    case KDD_CMD_WRITE_MSR:
+        kdd_handle_write_msr(s);
+        break;
+    case KDD_CMD_READ_VA:
+    case KDD_CMD_WRITE_VA:
+    case KDD_CMD_READ_PA:
+    case KDD_CMD_WRITE_PA:
+        kdd_handle_memory_access(s);
+        break;
+    case KDD_CMD_WRITE_Z:
+        /* No response */
+        break;
+    case KDD_CMD_SETCPU:
+        kdd_handle_setcpu(s);
+        break;
+    case KDD_CMD_WRITE_CTRL:
+    default:
+        KDD_LOG(s, "Unhandled CMD subtype 0x%8.8x\n", p->cmd.subtype);
+        /* Send back a mirror of the request saying we failed to do
+         * whatever it was. */
+        memcpy(s->txb, p, sizeof (kdd_hdr) + sizeof (kdd_cmd));
+        s->txp.h.len = sizeof (kdd_cmd);
+        s->txp.cmd.mem.status = KDD_STATUS_FAILURE;
+        s->txp.h.id = (s->next_id ^= 1);
+        kdd_tx(s);
+        break;
+    }
+}
+
+
+/*****************************************************************************
+ *  Scaffolding to get packets from the client.
+ */
+
+
+/* Set up the debugger state ready for use.  Returns a file descriptor and
+ * a state pointer for use in select() loops. */
+static int kdd_init(kdd_state **sp, struct addrinfo *addr, 
+                    kdd_guest *guest, FILE *log, int verbosity)
+{
+    kdd_state *s = NULL;
+    int opt, fd = -1;
+
+    s = malloc(sizeof *s);
+    if (s == NULL) {
+        fprintf(stderr, "Could not allocate state for kdd: %s\n", 
+                strerror(errno));
+        goto fail;
+    }
+    memset(s, 0, sizeof *s);
+    s->log = log;
+    s->verbosity = verbosity;
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        KDD_LOG(s, "Could not open a socket for kdd: %s\n", 
+                strerror(errno));
+        goto fail;
+    }
+
+    /* Try to connect to the tcp/serial gateway. */
+ again:
+    if (connect(fd, addr->ai_addr, sizeof *addr) != 0) {
+        if (errno == EINTR)
+            goto again;
+        if (addr->ai_next) {
+            addr = addr->ai_next;
+            goto again;
+        }
+        KDD_LOG(s, "Could not connect TCP stream for kdd: %s\n",
+                strerror(errno));
+        goto fail;
+    }
+
+    opt = 1;
+    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+
+    s->next_id = 0x80800001;
+    s->fd = fd;
+    s->running = 1;
+    s->cpuid = 0;
+    s->guest = guest;
+    s->os = unknown_os;
+
+    *sp = s;
+    KDD_LOG(s, "KDD starts\n");
+
+    kdd_break(s);
+
+    return fd;
+
+ fail:
+    if (fd >= 0)
+        close(fd);
+    free(s);
+    return -1;
+}
+
+/* Callback when the fd is readable, to parse packet data from the byte
+ * stream.  When a complete packet is seen, handle it.  The packet can
+ * then be read in the marshalling buffer, but only until the next call
+ * to kdd_parse_byte(). */
+void kdd_select_callback(kdd_state *s)
+{
+    kdd_pkt *p = &s->rxp;
+    unsigned int pkt_len = (unsigned) -1;
+    ssize_t rc, to_read;
+
+    /* For easy parsing, read single bytes until we can check the packet
+     * length, then read in one go to the end. */
+    if (s->cur < 8
+        || (p->h.dir != KDD_DIR_PKT && p->h.dir != KDD_DIR_ACK))
+        to_read = 1;
+    else {
+        /* Extract payload length from the header */
+        pkt_len = p->h.len + sizeof (kdd_hdr);
+
+        /* For some reason, packets always have a trailing 0xAA byte */
+        if (p->h.dir == KDD_DIR_PKT)
+            pkt_len++;
+
+        to_read = pkt_len - s->cur;
+    }
+
+    rc = read(s->fd, s->rxb + s->cur, to_read);
+
+    KDD_DEBUG(s, "read(%i) returns %i\n", (int) to_read, (int) rc);
+
+    if (rc <= 0)
+        /* XXX ignoring failures for now */
+        return;
+
+    /* Break command comes as a single byte */
+    if (s->cur == 0 && s->rxb[0] == 'b') {
+        kdd_break(s);
+        return;
+    }
+
+    /* Remember the bytes we just read */
+    s->cur += rc;
+
+    /* Sync to packet start, which will be "0000" or "iiii" */
+    if (s->cur < 4)
+        return;
+    if (p->h.dir != KDD_DIR_PKT && p->h.dir != KDD_DIR_ACK) {
+        KDD_LOG(s, "Bad hdr 0x%8.8x: resyncing\n", p->h.dir);
+        memmove(s->rxb, s->rxb + 1, --s->cur);
+        return;
+    }
+
+    /* Process complete packets/acks */
+    if (s->cur >= pkt_len) {
+        kdd_log_pkt(s, "RX", p);
+        if (p->h.dir == KDD_DIR_PKT)
+            kdd_handle_pkt(s, p);
+        else
+            kdd_handle_ack(s, p->h.id, p->h.type);
+        s->cur = 0;
+    }
+}
+
+
+static void usage(void)
+{
+    fprintf(stderr, 
+" usage: kdd [-v] <domid> <address> <port>\n"
+" \n"
+" Makes a TCP connection to <address>:<port> and speaks the kd serial\n"
+" protocol over it, to debug Xen domain <domid>.\n"
+" To connect a debugger, set up a Windows VM with it serial port confgured\n"
+" as \"serial='tcp:<address>:<port>,server,nodelay,nowait'\".  Run\n"
+" windbg or kd in that VM, connecting to COM1; then run kdd.\n\n");
+    exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+    int fd;
+    int verbosity = 0;
+    kdd_state *s;
+    kdd_guest *g;
+    struct addrinfo *addr;
+    fd_set fds;
+
+    while (argc > 4)
+        if (!strcmp(argv[1], "-v")) {
+            verbosity++;
+            argc--;
+            argv++;
+        }
+
+    if (argc != 4
+        || !(g = kdd_guest_init(argv[1], stdout, verbosity))
+        || getaddrinfo(argv[2], argv[3], NULL, &addr) != 0
+        || (fd = kdd_init(&s, addr, g, stdout, verbosity)) < 0)
+        usage();
+
+    while (1) {
+        FD_ZERO(&fds);
+        FD_SET(fd, &fds);
+        if (select(fd + 1, &fds, NULL, NULL, NULL) > 0) 
+            kdd_select_callback(s);
+    }
+
+    return 0;
+}
diff -r ac7f64d5577b tools/debugger/kdd/kdd.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/debugger/kdd/kdd.h  Mon Oct 25 15:12:21 2010 +0100
@@ -0,0 +1,520 @@
+/*
+ * kdd.h -- Structures, constants and descriptions of the Windows 
+ *          kd serial debugger protocol, for the kdd debugging stub.
+ *
+ * Tim Deegan <Tim.Deegan@xxxxxxxxxx>
+ * 
+ * Copyright (c) 2007-2010, Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KDD_H_
+#define _KDD_H_
+
+#include <stdint.h>
+
+#define PACKED __attribute__((packed))
+
+/*****************************************************************************
+ * Serial line protocol: Sender sends a 16-byte header with an optional
+ * payload following it.  Receiver responds to each packet with an
+ * acknowledgment (16-byte header only).
+ *
+ * Packet headers start with ASCII "0000" and there is a trailing byte
+ * 0xAA after the (optional) payload.  Ack headers start with ASCII
+ * "iiii"; no trailing byte).  Each packet and ack has a major type in
+ * the packet header; for packets with payload, a minor type is encoded
+ * in ASCII in the first four bytes of the payload.
+ *
+ * Packet IDs seem to start at 0x80800000 and alternate between that and
+ * 0x80800001; not clear whether the client's ID is always the ID of the
+ * last packet from the kernel or whether they're just oscillating in
+ * phase.  Either way there's clearly some state machine in the kernel
+ * that requires this exact behaviour from the client.
+ *
+ * All acks have length 0, id = id of the packet they ack.
+ */
+
+#define KDD_DIR_PKT 0x30303030   /* "0000" */
+#define KDD_DIR_ACK 0x69696969   /* "iiii" */
+
+typedef struct {
+    uint32_t dir;     /* KDD_DIR_PKT or KDD_DIR_ACK */
+    uint16_t type;    /* Major type. */
+    uint16_t len;     /* Payload length, excl. header and trailing byte */
+    uint32_t id;      /* Echoed in responses */
+    uint32_t sum;     /* Unsigned sum of all payload bytes */
+    uint8_t payload[0];
+} PACKED kdd_hdr;
+
+#define KDD_PKT_CMD 0x0002      /* Debugger commands (and replies to them) */
+#define KDD_PKT_MSG 0x0003      /* Kernel messages for the user */
+#define KDD_PKT_STC 0x0007      /* State change notification */
+#define KDD_PKT_REG 0x000b      /* Registry change notification (?) */
+#define KDD_PKT_MAX 0x000b
+
+#define KDD_ACK_OK  0x0004      /* Checksum, ID and type all fine */
+#define KDD_ACK_BAD 0x0005      /* Something is bogus */
+#define KDD_ACK_RST 0x0006      /* Not really an ack; one each way to resync */
+
+
+/*****************************************************************************
+ * Debugger commands, carried over the serial line.  In this protocol,
+ * we ignore the serial-level acking; when we talk about a response,
+ * it's another packet, sent after the request was acked, and which will
+ * itself be acked.
+ *
+ * The debugger client sends commands to the kernel, all of which have
+ * major type 2 and are 56 bytes long (not including the serial header).
+ * Not all the 56 bytes are used in every command, but the client
+ * doesn't bother to zero unused fields.  Most commands are responded to
+ * by a packet with the same subtype, containing at least a status code
+ * to indicate success or failure.
+ */
+
+#define KDD_STATUS_SUCCESS  0x00000000
+#define KDD_STATUS_FAILURE  0xc0000001
+#define KDD_STATUS_PENDING  0x00000103
+
+/* Memory access.  Read commands are echoed in the response with the
+ * status and length_rsp fields updated, and the read data appended to the
+ * packet.  Writes are the same, but with the data appended to the
+ * write command, not the response. */
+
+#define KDD_CMD_READ_VA     0x00003130  /* "01" */
+#define KDD_CMD_WRITE_VA    0x00003131  /* "11" */
+#define KDD_CMD_READ_CTRL   0x00003137  /* "71" */
+#define KDD_CMD_WRITE_CTRL  0x00003138  /* "81" */
+#define KDD_CMD_READ_PA     0x0000313D  /* "=1" */
+#define KDD_CMD_WRITE_PA    0x0000313E  /* ">1" */
+
+/* Not sure what this is, but it doesn't require a response */
+#define KDD_CMD_WRITE_Z     0x0000315A  /* "Z1" */
+
+typedef struct {
+    uint32_t u1;
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+    uint32_t u2;
+    uint64_t addr;              /* IN: address of start of read/write */
+    uint32_t length_req;        /* IN: bytes to read/write */
+    uint32_t length_rsp;        /* OUT: bytes successfully read/written */
+} PACKED kdd_cmd_mem;
+
+/* CPU register access.  As for memory accesses, but the data is a
+ * fixed-length block of register info. */
+
+#define KDD_CMD_READ_REGS   0x00003132  /* "21" */
+#define KDD_CMD_WRITE_REGS  0x00003133  /* "31" */
+
+typedef struct {
+    uint16_t u1;
+    uint16_t cpu;               /* IN: Zero-based processor ID */
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+} PACKED kdd_cmd_regs;
+
+#define KDD_CMD_READ_MSR    0x00003152  /* "R1" */
+#define KDD_CMD_WRITE_MSR   0x00003153  /* "S1" */
+
+typedef struct {
+    uint32_t u1;
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+    uint32_t u2;
+    uint32_t msr;               /* IN/OUT: MSR number */
+    uint64_t val;               /* IN/OUT: MSR contents */
+} PACKED kdd_cmd_msr;
+
+/* Breakpoint commands. */
+
+#define KDD_CMD_SOFT_BP     0x00003135  /* "51" */
+
+typedef struct {
+    uint32_t u1;
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+    uint32_t u2;
+    uint32_t bp;                /* IN: ID of breakpoint to operate on */
+} PACKED kdd_cmd_soft_bp;
+
+#define KDD_CMD_HARD_BP     0x0000315C  /* "\1" */
+
+typedef struct {
+    uint32_t u1;
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+    uint32_t u2;
+    uint64_t address;           /* IN: Address to trap on */
+    uint64_t u3;
+    uint64_t u4;
+    uint64_t u5;
+    uint64_t u6;
+} PACKED kdd_cmd_hard_bp;
+
+/* Flow control commands.  These commands are _not_ responded to.  */
+
+#define KDD_CMD_CONT1       0x00003136  /* "61" */
+#define KDD_CMD_CONT2       0x0000313c  /* "<1" */
+
+#define KDD_DBG_EXCEPTION_HANDLED    0x00010001
+#define KDD_DBG_CONTINUE             0x00010002
+
+typedef struct {
+    uint32_t u1;
+    uint32_t reason1;           /* IN: KDD_DBG_* */
+    uint32_t u2;
+    uint64_t reason2;           /* IN: always same as reason1 */
+} PACKED kdd_cmd_cont;
+
+/* Handshake command. */
+
+#define KDD_CMD_SHAKE       0x00003146 /* "F1" */
+
+#define KDD_MACH_x32        0x014c
+#define KDD_MACH_x64        0x8664
+
+#define KDD_FLAGS_MP        0x0001
+#define KDD_FLAGS_64        0x0008
+
+typedef struct {
+    uint32_t u1;
+    uint32_t status;            /* IN: STATUS_PENDING; OUT: result status. */
+    uint32_t u2;
+    uint16_t v_major;           /* OUT: OS major version (0xf for NT) */
+    uint16_t v_minor;           /* OUT: OS minor version (NT build number) */
+    uint16_t proto;             /* OUT: Protocol version (6) */
+    uint16_t flags;             /* OUT: Some flags (at least 0x3) */
+    uint16_t machine;           /* OUT: Machine type */
+    uint8_t pkts;               /* OUT: Number of packet types understood */
+    uint8_t states;             /* OUT: Number of state-change types used */
+    uint8_t manips;             /* OUT: number of "manipulation" types used */
+    uint8_t u3[3];
+    int64_t kern_addr;          /* OUT: KernBase */
+    int64_t mods_addr;          /* OUT: PsLoadedModuleList */
+    int64_t data_addr;          /* OUT: DebuggerDataList */
+} PACKED kdd_cmd_shake;
+
+/* Change active CPU.  This command is _not_ responded to */
+
+#define KDD_CMD_SETCPU      0x00003150 /* "P1" */
+
+typedef struct {
+    uint16_t u1;
+    uint16_t cpu;               /* IN: Zero-based processor ID */
+    uint32_t status;            /* IN: STATUS_PENDING */
+} PACKED kdd_cmd_setcpu;
+
+typedef struct {
+    uint32_t subtype;           /* IN: KDD_CMD_x */
+    union {
+        kdd_cmd_mem mem;
+        kdd_cmd_regs regs;
+        kdd_cmd_msr msr;
+        kdd_cmd_soft_bp sbp;
+        kdd_cmd_hard_bp hbp;
+        kdd_cmd_cont cont;
+        kdd_cmd_shake shake;
+        kdd_cmd_setcpu setcpu;
+        uint8_t pad[52];
+    };
+    uint8_t data[0];
+} PACKED kdd_cmd;
+
+
+/*****************************************************************************
+ * Kernel messages to the debugger.  The debugger does not respond to these
+ * beyond ACKing them and printing approprate things on the debugger
+ * console.
+ */
+
+/* Messages for the console */
+
+#define KDD_MSG_PRINT       0x00003230  /* "02" */
+
+typedef struct {
+    uint32_t subtype;           /* KDD_MSG_PRINT */
+    uint32_t u1;
+    uint32_t length;            /* Length in bytes of trailing string */
+    uint32_t u2;
+    uint8_t string[0];          /* Non-terminated character string */
+} PACKED kdd_msg;
+
+/* Registry updates (Hive loads?) */
+
+#define KDD_REG_CHANGE      0x00003430  /* "04" */
+
+typedef struct {
+    uint32_t subtype;           /* KDD_REG_CHANGE */
+    uint32_t u1[15];
+    uint16_t string[0];         /* Null-terminated wchar string */
+} PACKED kdd_reg;
+
+/* State changes.  After sending a state-change message the kernel halts
+ * until it receives a continue command from the debugger. */
+
+#define KDD_STC_STOP        0x00003030  /* "00" : Bug-check */
+#define KDD_STC_LOAD        0x00003031  /* "01" : Loaded a module */
+
+#define KDD_STC_STATUS_BREAKPOINT 0x80000003
+
+typedef struct {
+    uint16_t u1;
+    uint16_t cpu;               /* Zero-based processor ID */
+    uint32_t ncpus;             /* Number of processors */
+    uint32_t u2;
+    int64_t kthread;            /* Kernel thread structure */
+    int64_t rip1;               /* Instruction pointer, sign-extended */
+    uint64_t status;            /* KDD_STC_STATUS_x */
+    uint64_t u3;
+    int64_t rip2;               /* Same as rip1 */
+    uint64_t nparams;           /* Number of stopcode parameters */
+    uint64_t params[15];        /* Stopcode parameters */
+    uint64_t first_chance;      /* OS exn handlers not yet been run? */
+    uint32_t u4[2];
+    uint32_t ilen;              /* Number of bytes of instruction following */
+    uint8_t inst[36];           /* VA contents from %eip onwards */
+} PACKED kdd_stc_stop;
+
+typedef struct {
+    uint32_t u1[3];
+    uint64_t u2;
+    uint64_t rip;               /* Instruction pointer, sign-extended */
+    uint64_t u3[26];
+    uint8_t path[0];            /* Null-terminated ASCII path to loaded mod. */
+} PACKED kdd_stc_load;
+
+typedef struct {
+    uint32_t subtype;           /* KDD_STC_x */
+    union {
+        kdd_stc_stop stop;
+        kdd_stc_load load;
+    };
+} PACKED kdd_stc;
+
+
+/*****************************************************************************
+ * Overall packet type
+ */
+
+typedef struct {
+    kdd_hdr h;                  /* Major type disambiguates union below */
+    union {
+        kdd_cmd cmd;
+        kdd_msg msg;
+        kdd_reg reg;
+        kdd_stc stc;
+        uint8_t payload[0];
+    };
+} PACKED kdd_pkt;
+
+
+/*****************************************************************************
+ * Processor state layouts
+ */
+
+/* User-visible register files */
+typedef union {
+    uint32_t pad[179];
+    struct {
+        uint32_t u1[7];         /* Flags, DRx?? */
+        uint8_t fp[112];        /* FP save state (why 112 not 108?) */
+        int32_t gs;
+        int32_t fs;
+        int32_t es;
+        int32_t ds;
+        int32_t edi;
+        int32_t esi;
+        int32_t ebx;
+        int32_t edx;
+        int32_t ecx;
+        int32_t eax;
+        int32_t ebp;
+        int32_t eip;
+        int32_t cs;
+        int32_t eflags;
+        int32_t esp;
+        int32_t ss;
+        uint32_t sp2[37];       /* More 0x20202020. fp? */
+        uint32_t sp3;           /* 0x00202020 */
+    };
+} PACKED kdd_regs_x86_32;
+
+typedef union {
+    uint64_t pad[154];
+    struct {
+
+        uint64_t u1[7];
+
+        uint16_t cs; //2*1c
+        uint16_t ds;
+        uint16_t es;
+        uint16_t fs;
+        uint16_t gs;
+        uint16_t ss;
+        uint32_t rflags;
+        uint64_t dr0;
+        uint64_t dr1;
+        uint64_t dr2;
+        uint64_t dr3;
+        uint64_t dr6;
+        uint64_t dr7;
+        int64_t rax;
+        int64_t rcx;
+        int64_t rdx;
+        int64_t rbx;
+        int64_t rsp;
+        int64_t rbp;
+        int64_t rsi;
+        int64_t rdi;
+        int64_t r8;
+        int64_t r9;
+        int64_t r10;
+        int64_t r11;
+        int64_t r12;
+        int64_t r13;
+        int64_t r14;
+        int64_t r15;
+        int64_t rip; //2*7c
+
+        uint64_t u2[32];
+        
+        uint8_t fp[512]; // fp @2*100 .. 150 (+ more??)
+
+        uint64_t u3[26];
+    };
+} PACKED kdd_regs_x86_64;
+
+typedef union {
+    kdd_regs_x86_32 r32;
+    kdd_regs_x86_64 r64;
+} PACKED kdd_regs;
+
+/* System registers */
+typedef struct {
+    uint32_t cr0;
+    uint32_t cr2;
+    uint32_t cr3;
+    uint32_t cr4;
+    uint32_t dr0;
+    uint32_t dr1;
+    uint32_t dr2;
+    uint32_t dr3;
+    uint32_t dr6;
+    uint32_t dr7;
+    uint16_t gdt_pad;
+    uint16_t gdt_limit;
+    uint32_t gdt_base;
+    uint16_t idt_pad;
+    uint16_t idt_limit;
+    uint32_t idt_base;
+    uint16_t tss_sel;
+    uint16_t ldt_sel;
+    uint8_t u1[24];
+} PACKED kdd_ctrl_x86_32;
+
+typedef struct {
+    uint64_t cr0;
+    uint64_t cr2;
+    uint64_t cr3; 
+    uint64_t cr4;
+    uint64_t dr0;
+    uint64_t dr1;
+    uint64_t dr2;
+    uint64_t dr3;
+    uint64_t dr6;
+    uint64_t dr7;   
+    uint8_t  gdt_pad[6];
+    uint16_t gdt_limit;
+    uint64_t gdt_base;
+    uint8_t  idt_pad[6];
+    uint16_t idt_limit;
+    uint64_t idt_base;
+    uint16_t tss_sel;
+    uint16_t ldt_sel;
+    uint8_t u1[44];
+    uint64_t cr8;
+    uint8_t u2[40];
+    uint64_t efer; // XXX find out where EFER actually goes
+} PACKED kdd_ctrl_x86_64;
+
+typedef union {
+    kdd_ctrl_x86_32 c32;
+    kdd_ctrl_x86_64 c64;
+} kdd_ctrl;
+
+/*****************************************************************************
+ * Functions required from the emulator/hypervisor for the stub to work.
+ */
+
+typedef struct kdd_guest kdd_guest;
+
+/* Init and teardown guest-specific state */
+extern kdd_guest *kdd_guest_init(char *arg, FILE *log, int verbosity);
+extern void kdd_guest_teardown(kdd_guest *g);
+extern char *kdd_guest_identify(kdd_guest *g);
+
+/* Halt and restart the running guest */
+extern void kdd_halt(kdd_guest *g);
+extern void kdd_run(kdd_guest *g);
+
+/* How many CPUs are there? */
+extern int kdd_count_cpus(kdd_guest *g);
+
+/* Accessor for guest physical memory, returning bytes read/written */
+extern uint32_t kdd_access_physical(kdd_guest *g, uint64_t addr, 
+                                    uint32_t len, uint8_t *buf, int write);
+
+/* Accessors for guest registers, returning 0 for success */
+extern int kdd_get_regs(kdd_guest *g, int cpuid, kdd_regs *r, int w64);
+extern int kdd_set_regs(kdd_guest *g, int cpuid, kdd_regs *r, int w64);
+
+/* Accessors for guest control registers, returning 0 for success */
+extern int kdd_get_ctrl(kdd_guest *g, int cpuid, kdd_ctrl *ctrl, int w64);
+extern int kdd_set_ctrl(kdd_guest *g, int cpuid, kdd_ctrl *ctrl, int w64);
+
+/* Accessors for guest MSRs, returning 0 for success */
+extern int kdd_wrmsr(kdd_guest *g, int cpuid, uint32_t msr, uint64_t value);
+extern int kdd_rdmsr(kdd_guest *g, int cpuid, uint32_t msr, uint64_t *value);
+
+
+/*****************************************************************************
+ * Logfile usefulness
+ */
+
+/* Verbosity:
+ * 0: errors (default)
+ * 1: operations
+ * 2: packets
+ * 3: _everything_ */
+
+#define KDD_LOG_IF(_v, _s, _fmt, _a...) do {    \
+        if ((_s)->verbosity >= (_v)) {          \
+        fprintf((_s)->log, (_fmt), ##_a);       \
+        (void) fflush((_s)->log);               \
+    }                                           \
+} while (0)
+
+#define KDD_LOG(_s, _fmt, _a...) KDD_LOG_IF(1, (_s), (_fmt), ##_a)
+#define KDD_DEBUG(_s, _fmt, _a...) KDD_LOG_IF(3, (_s), (_fmt), ##_a)
+
+#endif /* _KDD_H_ */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.