"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@citrix.com>
This commit is contained in:
Tim Deegan committed 2010-10-26 11:56:31 +01:00
1 parent 7f18fb47e5
commit 5717f51369
7 files changed
+2229

No files matched your search

+1
View File
@@ -129,6 +129,7 @@
^tools/debugger/gdb/gdb-6\.2\.1/.*$
^tools/debugger/gdb/gdb-6\.2\.1\.tar\.bz2$
^tools/debugger/gdbsx/gdbsx$
^tools/debugger/kdd/kdd$
^tools/debugger/xenitp/xenitp$
^tools/firmware/.*/biossums$
^tools/firmware/.*\.bin$
+5
View File
@@ -107,6 +107,11 @@ S: Supported
F: xen/arch/x86/debug.c
F: tools/debugger/gdbsx/
KDD DEBUGGER
M: Tim Deegan <Tim.Deegan@citrix.com>
S: Supported
F: tools/debugger/kdd/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
M: Joseph Cihula <joseph.cihula@intel.com>
M: Shane Wang <shane.wang@intel.com>
+11
View File
@@ -36,6 +36,7 @@ SUBDIRS-y += libxl
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-install-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
+22
View File
@@ -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
+625
View File
@@ -0,0 +1,625 @@
/*
* kdd-xen.c -- xen-specific functions for the kdd debugging stub
*
* Tim Deegan <Tim.Deegan@citrix.com>
*
* 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);
}
File diff suppressed because it is too large. Load diff
+520
View File
@@ -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@citrix.com>
*
* 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_ */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.