WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

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

To: <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] tools: "kdd" Windows debugger stub.
From: Tim Deegan <Tim.Deegan@xxxxxxxxxx>
Date: Mon, 25 Oct 2010 15:32:38 +0100
Delivery-date: Mon, 25 Oct 2010 07:34:02 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.18 (2008-05-17)
"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

<Prev in Thread] Current Thread [Next in Thread>