Revision: 6f45ef5d4e16
Branch: default
Author: Angen Zheng <
angen...@gmail.com>
Date: Wed Nov 6 19:48:40 2013 UTC
Log: Adds support for debugging Kitten processes from GDB running in a
Linux VM.
There are 2 newly added modules (identified by the red box): the GDB
Virtio Backend in Palacios and the GDB Stub module in kitten:
The GDB virtio console backend simply forwards GDB requests to the GDB
Stub, then waits for replies from the stub, and finally forwards replies to
virtio frontend.
The GDB Stub module implements GDB Remote Serial Protocol, which handles
GDB requests, such as reading/writing registers and reading/writing memory,
and sends back GDB replies.
In order to debug an app running in Kitten, you first need to attach to it
by invoking the following system call implemented in GDB stub module:
gdb_attach_process(id_t pid, char *gdb_cons)
The gdbcons param specifies the gdb virtio console backend that the GDB
stub will listen on. After attaching to the task, gdb client running in
Linux guest can now connect to it by running:
target remote /dev/hvc0
The /dev/hvc0 corresponds to the gdb virtio console frontend you wanna use.
The mapping between gdb virtio console backend and frontend is configured
in palacios guest config file.
http://code.google.com/p/kitten/source/detail?r=6f45ef5d4e16
Added:
/arch/x86_64/kernel/gdb.c
/arch/x86_64/kernel/palacios/palacios_gdb.c
/include/arch-x86_64/gdb.h
/include/lwk/gdb.h
/include/lwk/gdbio.h
/kernel/gdb.c
/kernel/gdb_target_desc.c
/kernel/gdbio.c
Modified:
/arch/x86_64/Kconfig
/arch/x86_64/kernel/Makefile
/arch/x86_64/kernel/interrupts.c
/arch/x86_64/kernel/palacios/Makefile
/include/arch-x86_64/desc.h
/include/lwk/task.h
/kernel/Makefile
/kernel/sched.c
/kernel/waitq.c
=======================================
--- /dev/null
+++ /arch/x86_64/kernel/gdb.c Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,330 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Amit S. Kale <
amit...@linsyssoft.com>
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Copyright (C) 2007-2008 Jason Wessel, Wind River Systems, Inc.
+ * Copyright (C) 2008 Patrick Bridges, University of New Mexico
+ */
+/****************************************************************************
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<
ak...@veritas.com>
+ * Updated by: Tom Rini <
tr...@kernel.crashing.org>
+ * Updated by: Jason Wessel <
jason....@windriver.com>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl gdb, compatibility with 2.1.xx kernel by
+ * David Grothe <
da...@gcom.com>
+ * Integrated into 2.2.5 kernel by Tigran Aivazian <
tig...@sco.com>
+ * X86_64 changes from Andi Kleen's patch merged by Jim Houston
+ * Ported to Kitten by Patrick Bridges
+ */
+#include <lwk/spinlock.h>
+#include <lwk/string.h>
+#include <lwk/kernel.h>
+#include <lwk/ptrace.h>
+#include <lwk/delay.h>
+#include <lwk/gdb.h>
+#include <lwk/init.h>
+#include <lwk/smp.h>
+#include <lwk/cpuinfo.h>
+
+#include <arch/apic.h>
+#include <arch/apicdef.h>
+#include <arch/idt_vectors.h>
+#include <arch/system.h>
+
+/* #include <asm/mach_apic.h> */
+
+/*
+ * Put the error code here just in case the user cares:
+ */
+static int gdb_x86errcode;
+
+/*
+ * Likewise, the vector number here (since GDB only gets the signal
+ * number through the usual means, and that's not very specific):
+ */
+static int gdb_x86vector = -1;
+
+/**
+ * pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs
+ * @gdb_regs: A pointer to hold the registers in the order GDB wants.
+ * @regs: The &struct pt_regs of the current process.
+ *
+ * Convert the pt_regs in @regs into the format for registers that
+ * GDB expects, stored in @gdb_regs.
+ */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ gdb_regs[GDB_RAX_REGNUM] = regs->rax;
+ gdb_regs[GDB_RBX_REGNUM] = regs->rbx;
+ gdb_regs[GDB_RCX_REGNUM] = regs->rcx;
+ gdb_regs[GDB_RDX_REGNUM] = regs->rdx;
+ gdb_regs[GDB_RSI_REGNUM] = regs->rsi;
+ gdb_regs[GDB_RDI_REGNUM] = regs->rdi;
+ gdb_regs[GDB_RBP_REGNUM] = regs->rbp;
+ gdb_regs[GDB_RSP_REGNUM] = regs->rsp;
+
+ gdb_regs[GDB_R8_REGNUM] = regs->r8;
+ gdb_regs[GDB_R9_REGNUM] = regs->r9;
+ gdb_regs[GDB_R10_REGNUM] = regs->r10;
+ gdb_regs[GDB_R11_REGNUM] = regs->r11;
+ gdb_regs[GDB_R12_REGNUM] = regs->r12;
+ gdb_regs[GDB_R13_REGNUM] = regs->r13;
+ gdb_regs[GDB_R14_REGNUM] = regs->r14;
+ gdb_regs[GDB_R15_REGNUM] = regs->r15;
+
+ //gdb_regs[GDB_ORIG_RAX_REGNUM] = regs->orig_rax;
+
+ gdb_regs[GDB_RIP_REGNUM] = regs->rip;
+ gdb_regs[GDB_EFLAGS_REGNUM] = regs->eflags;
+ gdb_regs[GDB_CS_REGNUM] = regs->cs;
+ gdb_regs[GDB_SS_REGNUM] = regs->ss;
+
+ /*//TODO
+ struct thread_struct tss = TASK_PTR(regs)->arch.thread;
+ gdb_regs[GDB_DS_REGNUM] = tss.ds;
+ gdb_regs[GDB_ES_REGNUM] =
tss.es;
+ gdb_regs[GDB_FS_REGNUM] = tss.fs;
+ gdb_regs[GDB_GS_REGNUM] =
tss.gs;
+ */
+ //TODO ST0~ST7, FCTRL, FSTAT, FTAG, XMM0, XMM1, MXCSR, YMM0H, YMM15H
+}
+
+/**
+ * sleeping_thread_to_gdb_regs - Convert ptrace regs to GDB regs
+ * @gdb_regs: A pointer to hold the registers in the order GDB wants.
+ * @p: The &struct task_struct of the desired process.
+ *
+ * Convert the register values of the sleeping process in @p to
+ * the format that GDB expects.
+ * This function is called when gdb does not have access to the
+ * &struct pt_regs and therefore it should fill the gdb registers
+ * @gdb_regs with what has been saved in &struct thread_struct
+ * thread field during switch_to.
+ */
+/*void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct
task_struct *p)
+{
+ gdb_regs[GDB_RAX_REGNUM] = 0;
+ gdb_regs[GDB_RBX_REGNUM] = 0;
+ gdb_regs[GDB_RCX_REGNUM] = 0;
+ gdb_regs[GDB_RDX_REGNUM] = 0;
+ gdb_regs[GDB_RSI_REGNUM] = 0;
+ gdb_regs[GDB_RDI_REGNUM] = 0;
+ gdb_regs[GDB_RBP_REGNUM] = *(unsigned long *)p->arch.thread.rsp;
+ gdb_regs[GDB_RSP_REGNUM] = p->arch.thread.rsp;
+
+ gdb_regs[GDB_R8_REGNUM] = 0;
+ gdb_regs[GDB_R9_REGNUM] = 0;
+ gdb_regs[GDB_R10_REGNUM] = 0;
+ gdb_regs[GDB_R11_REGNUM] = 0;
+ gdb_regs[GDB_R12_REGNUM] = 0;
+ gdb_regs[GDB_R13_REGNUM] = 0;
+ gdb_regs[GDB_R14_REGNUM] = 0;
+ gdb_regs[GDB_R15_REGNUM] = 0;
+
+ gdb_regs[GDB_RIP_REGNUM] = 0;
+ gdb_regs[GDB_EFLAGS_REGNUM] = *(unsigned long *)(p->arch.thread.rsp + 8);
+
+}
+*/
+/**
+ * gdb_regs_to_pt_regs - Convert GDB regs to ptrace regs.
+ * @gdb_regs: A pointer to hold the registers we've received from GDB.
+ * @regs: A pointer to a &struct pt_regs to hold these values in.
+ *
+ * Convert the GDB regs in @gdb_regs into the pt_regs, and store them
+ * in @regs.
+ */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ regs->rax = gdb_regs[GDB_RAX_REGNUM];
+ regs->rbx = gdb_regs[GDB_RBX_REGNUM];
+ regs->rcx = gdb_regs[GDB_RCX_REGNUM];
+ regs->rdx = gdb_regs[GDB_RDX_REGNUM];
+ regs->rsi = gdb_regs[GDB_RSI_REGNUM];
+ regs->rdi = gdb_regs[GDB_RDI_REGNUM];
+ regs->rbp = gdb_regs[GDB_RBP_REGNUM];
+ regs->rsp = gdb_regs[GDB_RSP_REGNUM];
+
+ regs->r8 = gdb_regs[GDB_R8_REGNUM];
+ regs->r9 = gdb_regs[GDB_R9_REGNUM];
+ regs->r10 = gdb_regs[GDB_R10_REGNUM];
+ regs->r11 = gdb_regs[GDB_R11_REGNUM];
+ regs->r12 = gdb_regs[GDB_R12_REGNUM];
+ regs->r13 = gdb_regs[GDB_R13_REGNUM];
+ regs->r14 = gdb_regs[GDB_R14_REGNUM];
+ regs->r15 = gdb_regs[GDB_R15_REGNUM];
+
+ regs->rip = gdb_regs[GDB_RIP_REGNUM];
+ regs->cs = gdb_regs[GDB_CS_REGNUM];
+ regs->ss = gdb_regs[GDB_SS_REGNUM];
+ regs->eflags = gdb_regs[GDB_EFLAGS_REGNUM];
+
+ //regs->orig_rax = gdb_regs[GDB_ORIG_RAX_REGNUM];
+}
+
+/**
+ * gdb_post_primary_code - Save error vector/code numbers.
+ * @regs: Original pt_regs.
+ * @e_vector: Original error vector.
+ * @err_code: Original error code.
+ *
+ * This is needed on architectures which support SMP and GDB.
+ * This function is called after all the slave cpus have been put
+ * to a know spin state and the primary CPU has control over GDB.
+ */
+void gdb_post_primary_code(struct pt_regs *regs, int e_vector, int
err_code)
+{
+ /* primary processor is completely in the debugger */
+ gdb_x86vector = e_vector;
+ gdb_x86errcode = err_code;
+}
+
+/**
+ * gdb_arch_handle_exception - Handle architecture specific GDB packets.
+ * @vector: The error vector of the exception that happened.
+ * @signo: The signal number of the exception that happened.
+ * @err_code: The error code of the exception that happened.
+ * @remcom_in_buffer: The buffer of the packet we have read.
+ * @remcom_out_buffer: The buffer of %BUFMAX bytes to write a packet into.
+ * @regs: The &struct pt_regs of the current process.
+ *
+ * This function MUST handle the 'c' and 's' command packets,
+ * as well packets to set / remove a hardware breakpoint, if used.
+ * If there are additional packets which the hardware needs to handle,
+ * they are handled here. The code should return -1 if it wants to
+ * process more packets, and a %0 or %1 if it wants to exit from the
+ * gdb callback.
+ */
+int gdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ struct pt_regs *linux_regs,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ atomic_t *gdb_cpu_doing_single_step)
+{
+ unsigned long addr;
+ char *ptr;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (gdb_hex2long(&ptr, &addr))
+ linux_regs->rip = addr;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+ atomic_set(gdb_cpu_doing_single_step, -1);
+
+ if (remcom_in_buffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+// gdb_single_step = 1;
+ atomic_set(gdb_cpu_doing_single_step, raw_smp_processor_id());
+ }
+ return 0;
+ }
+
+ /* this means that we do not want to exit from the handler: */
+ return -1;
+}
+
+//static inline int
+//single_step_cont(int trapnr, int signr, int err, struct pt_regs *regs)
+//{
+// /*
+// * Single step exception from kernel space to user space so
+// * eat the exception and continue the process:
+// */
+// printk(KERN_ERR "GDB: trap/step from kernel to user space, "
+// "resuming...\n");
+// gdb_arch_handle_exception(trapnr, signr, err, "c", "", regs);
+//
+// return 0;
+//}
+
+/* Return 1 to crash the processor, 0 to continue the processor */
+static int
+__gdb_exception_enter(int trapnr, int signr, int err, struct pt_regs *regs)
+{
+ /*if (atomic_read(&gdb_cpu_doing_single_step) == raw_smp_processor_id())
+ return single_step_cont(trapnr, signr, err, regs);
+ */
+ if (gdb_handle_exception(trapnr, signr, err, regs))
+ return 1;
+ return 0;
+}
+
+int
+gdb_exception_enter(int trapnr, int err, struct pt_regs *regs)
+{
+ unsigned long flags;
+ int ret;
+
+ local_irq_save(flags);
+ /* TODO: map trapnr to signr just to make things more sane XXX */
+ ret = __gdb_exception_enter(trapnr, 0x05, err, regs);
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+void
+gdb_nmi_cpus(void)
+{
+ int fromcpu = raw_smp_processor_id();
+ int i;
+ for (i = 0; i < num_cpus(); i++)
+ if (i != fromcpu)
+ lapic_send_ipi(i, NMI_VECTOR);
+ return;
+}
+
+/**
+ *
+ * gdb_skipexception - Bail of of GDB when we've been triggered.
+ * @exception: Exception vector number
+ * @regs: Current &struct pt_regs.
+ *
+ * On some architectures we need to skip a breakpoint exception when
+ * it occurs after a breakpoint has been removed.
+ *
+ * Skip an int3 exception when it occurs after a breakpoint has been
+ * removed. Backtrack eip by 1 since the int3 would have caused it to
+ * increment by 1.
+ */
+int gdb_skipexception(struct gdb_bkpt *gdb_breaks, int aspace_id, int
exception, struct pt_regs *regs)
+{
+ if (exception == 3 && gdb_isremovedbreak(gdb_breaks, aspace_id, regs->rip
- 1)) {
+ regs->rip -= 1;
+ return 1;
+ }
+ return 0;
+}
+
+unsigned long gdb_arch_pc(int exception, struct pt_regs *regs)
+{
+ if (exception == 3)
+ return instruction_pointer(regs) - 1;
+ return instruction_pointer(regs);
+}
+
+struct gdb_arch arch_gdb_ops = {
+ /* Breakpoint instruction: */
+ .gdb_bpt_instr = { 0xcc },
+};
=======================================
--- /dev/null
+++ /arch/x86_64/kernel/palacios/palacios_gdb.c Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,68 @@
+#include <interfaces/vmm_gdb.h>
+
+#include <lwk/kfs.h>
+#include <lwk/gdb.h>
+#include <lwk/gdbio.h>
+#include <lwk/driver.h>
+
+#include <arch-generic/fcntl.h>
+
+static void * palacios_gdb_connect(char *gdbcons_backend){
+ return gdbio_connect(gdbcons_backend);
+}
+
+static int palacios_gdb_disconnect(void * file_ptr){
+ struct file *fd = (struct file *) file_ptr;
+ static char *detach_pkt = "$D#44";
+ gdbclient_write(fd, detach_pkt, strlen(detach_pkt));
+
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ while(1){
+ gdbclient_read(fd, buf, 2);
+ buf[2] = '\0';
+ if(strcmp(buf, "OK") == 0){
+ gdbclient_write(fd, "+", 1);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int palacios_gdb_attach_process(int pid, char *gdbcons_backend){
+ return gdb_attach_process(pid, gdbcons_backend);
+}
+
+static int palacios_gdb_attach_thread(int pid, int tid, char
*gdbcons_backend){
+ return gdb_attach_thread(pid, tid, gdbcons_backend);
+}
+
+static unsigned long long palacios_gdb_read(void * file_ptr, void *
buffer, unsigned long long len){
+ struct file *fd = (struct file*) file_ptr;
+ return gdbclient_read(fd, buffer, len);
+}
+
+static unsigned long long palacios_gdb_write(void * file_ptr, void *
buffer, unsigned long long len) {
+ struct file *fd = (struct file*) file_ptr;
+ return gdbclient_write(fd, buffer, len);
+}
+
+static struct v3_gdb_hooks palacios_gdb_hooks = {
+ .connect = palacios_gdb_connect,
+ .disconnect = palacios_gdb_disconnect,
+
+ .attach_process = palacios_gdb_attach_process,
+ .attach_thread = palacios_gdb_attach_thread,
+
+ .write = palacios_gdb_write,
+ .read = palacios_gdb_read,
+};
+
+int
+palacios_gdb_init(void){
+ V3_Init_GDB(&palacios_gdb_hooks);
+ return 0;
+}
+
+DRIVER_INIT("module", palacios_gdb_init);
+
=======================================
--- /dev/null
+++ /include/arch-x86_64/gdb.h Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,79 @@
+#ifndef _ASM_GDB_H_
+#define _ASM_GDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ * Copyright (C) 2008 Wind River Systems, Inc.
+ */
+
+enum gdb_regnum{
+ GDB_RAX_REGNUM, /* %rax */
+ GDB_RBX_REGNUM, /* %rbx */
+ GDB_RCX_REGNUM, /* %rcx */
+ GDB_RDX_REGNUM, /* %rdx */
+ GDB_RSI_REGNUM, /* %rsi */
+ GDB_RDI_REGNUM, /* %rdi */
+ GDB_RBP_REGNUM, /* %rbp */
+ GDB_RSP_REGNUM, /* %rsp */
+
+ GDB_R8_REGNUM, /* %r8 */
+ GDB_R9_REGNUM, /* %r9 */
+ GDB_R10_REGNUM, /* %r10 */
+ GDB_R11_REGNUM, /* %r11 */
+ GDB_R12_REGNUM, /* %r12 */
+ GDB_R13_REGNUM, /* %r13 */
+ GDB_R14_REGNUM, /* %r14 */
+ GDB_R15_REGNUM, /* %r15 */
+
+ GDB_RIP_REGNUM, /* %rip */
+
+ //32bits
+ GDB_EFLAGS_REGNUM, /* %eflags */
+ GDB_CS_REGNUM, /* %cs */
+ GDB_SS_REGNUM, /* %ss */
+ GDB_DS_REGNUM, /* %ds */
+ GDB_ES_REGNUM, /* %es */
+ GDB_FS_REGNUM, /* %fs */
+ GDB_GS_REGNUM, /* %gs */
+
+ //TODO Register ST0~ST7 occupy 10bytes in total
+ GDB_ST0_REGNUM = 24,
+ /*GDB_ST1_REGNUM,
+ GDB_ST2_REGNUM,
+ GDB_ST3_REGNUM,
+ GDB_ST4_REGNUM,
+ GDB_ST5_REGNUM,
+ GDB_ST6_REGNUM,
+ GDB_ST7_REGNUM,
+ */
+
+ //32bit float point registers
+ GDB_FCTRL_REGNUM = GDB_ST0_REGNUM + 10,
+ GDB_FSTAT_REGNUM,
+ GDB_FTAG_REGNUM,
+ GDB_FISEG_ERGNUM,
+ GDB_FIOFF_ERGNUM,
+ GDB_FOSEG_ERGNUM,
+ GDB_FOOFF_ERGNUM,
+ GDB_FOP_ERGNUM
+};
+
+#define GDB_NUM_GPREGS (GDB_GS_REGNUM + 1)
+#define GDB_NUM_STREGS 8
+#define GDB_NUM_FPREGS 8
+#define GDB_NUM_GPREGBYTES (GDB_NUM_GPREGS * 8)
+#define GDB_NUM_STREGBYTES (GDB_NUM_STREGS * 10)
+#define GDB_NUM_FPREGBYTES (GDB_NUM_FPREGS * 8)
+#define GDB_NUM_REGBYTES (GDB_NUM_GPREGBYTES + GDB_NUM_STREGBYTES +
GDB_NUM_FPREGBYTES)
+
+static inline void arch_gdb_breakpoint(void)
+{
+ asm(" int $3");
+}
+int gdb_exception_enter(int trapnr, int err, struct pt_regs *regs);
+//void gdb_nmi_cpus(void);
+
+#define BREAK_INSTR_SIZE 1
+#define CACHE_FLUSH_IS_SAFE 1
+
+#endif
=======================================
--- /dev/null
+++ /include/lwk/gdb.h Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,264 @@
+/*
+ * This provides the callbacks and functions that GDB needs to share
between
+ * the core, I/O and arch-specific portions.
+ *
+ * Author: Amit Kale <
amit...@linsyssoft.com> and
+ * Tom Rini <
tr...@kernel.crashing.org>
+ * modified for Kitten by Patrick Bridges <
bri...@cs.unm.edu>
+ *
+ * 2001-2004 (c) Amit S. Kale and 2003-2005 (c) MontaVista Software, Inc.
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+#ifndef _GDB_H_
+#define _GDB_H_
+
+/* #include <lwk/serial.h> */
+#include <lwk/linkage.h>
+#include <lwk/init.h>
+#include <lwk/ptrace.h>
+#include <lwk/task.h>
+
+#include <arch/atomic.h>
+#include <arch/gdb.h>
+
+struct pt_regs;
+struct tasklet_struct;
+struct task_struct;
+struct uart_port;
+
+enum gdb_bptype {
+ GDB_BP_BREAKPOINT = 0,
+ GDB_BP_HARDWARE_BREAKPOINT,
+ GDB_BP_WRITE_WATCHPOINT,
+ GDB_BP_READ_WATCHPOINT,
+ GDB_BP_ACCESS_WATCHPOINT
+};
+
+enum gdb_bpstate {
+ GDB_BP_UNDEFINED = 0,
+ GDB_BP_REMOVED,
+ GDB_BP_SET,
+ GDB_BP_ACTIVE
+};
+
+struct gdb_bkpt {
+ unsigned long bpt_addr;
+ unsigned char saved_instr[BREAK_INSTR_SIZE];
+ enum gdb_bptype type;
+ enum gdb_bpstate state;
+};
+
+/*
+ * gdb_skipexception - Bail of of GDB when we've been triggered.
+ * @exception: Exception vector number
+ * @regs: Current &struct pt_regs.
+ *
+ * On some architectures we need to skip a breakpoint exception when
+ * it occurs after a breakpoint has been removed.
+ */
+extern int gdb_skipexception(struct gdb_bkpt *gdb_breaks, int aspace_id,
int exception, struct pt_regs *regs);
+
+/*
+ * gdb_post_primary_code - Save error vector/code numbers.
+ * @regs: Original pt_regs.
+ * @e_vector: Original error vector.
+ * @err_code: Original error code.
+ *
+ * This is needed on architectures which support SMP and GDB.
+ * This function is called after all the secondary cpus have been put
+ * to a know spin state and the primary CPU has control over GDB.
+ */
+extern void gdb_post_primary_code(struct pt_regs *regs, int e_vector,
+ int err_code);
+
+/*
+ * gdb_disable_hw_debug - Disable hardware debugging while we in gdb.
+ * @regs: Current &struct pt_regs.
+ *
+ * This function will be called if the particular architecture must
+ * disable hardware debugging while it is processing gdb packets or
+ * handling exception.
+ */
+extern void gdb_disable_hw_debug(struct pt_regs *regs);
+
+#ifndef GDB_MAX_BREAKPOINTS
+# define GDB_MAX_BREAKPOINTS 1000
+#endif
+
+#define GDB_HW_BREAKPOINT 1
+
+/*
+ * Functions each GDB-supporting architecture must provide:
+ */
+
+/*
+ * gdb_arch_init - Perform any architecture specific initalization.
+ *
+ * This function will handle the initalization of any architecture
+ * specific callbacks.
+ */
+extern int gdb_arch_init(void);
+
+/*
+ * gdb_arch_exit - Perform any architecture specific uninitalization.
+ *
+ * This function will handle the uninitalization of any architecture
+ * specific callbacks, for dynamic registration and unregistration.
+ */
+extern void gdb_arch_exit(void);
+
+/*
+ * pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs
+ * @gdb_regs: A pointer to hold the registers in the order GDB wants.
+ * @regs: The &struct pt_regs of the current process.
+ *
+ * Convert the pt_regs in @regs into the format for registers that
+ * GDB expects, stored in @gdb_regs.
+ */
+extern void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs
*regs);
+
+/*
+ * sleeping_thread_to_gdb_regs - Convert ptrace regs to GDB regs
+ * @gdb_regs: A pointer to hold the registers in the order GDB wants.
+ * @p: The &struct task_struct of the desired process.
+ *
+ * Convert the register values of the sleeping process in @p to
+ * the format that GDB expects.
+ * This function is called when gdb does not have access to the
+ * &struct pt_regs and therefore it should fill the gdb registers
+ * @gdb_regs with what has been saved in &struct thread_struct
+ * thread field during switch_to.
+ */
+/*extern void
+sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct
*p);
+*/
+/*
+ * gdb_regs_to_pt_regs - Convert GDB regs to ptrace regs.
+ * @gdb_regs: A pointer to hold the registers we've received from GDB.
+ * @regs: A pointer to a &struct pt_regs to hold these values in.
+ *
+ * Convert the GDB regs in @gdb_regs into the pt_regs, and store them
+ * in @regs.
+ */
+extern void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs
*regs);
+
+/*
+ * gdb_arch_handle_exception - Handle architecture specific GDB packets.
+ * @vector: The error vector of the exception that happened.
+ * @signo: The signal number of the exception that happened.
+ * @err_code: The error code of the exception that happened.
+ * @remcom_in_buffer: The buffer of the packet we have read.
+ * @remcom_out_buffer: The buffer of %BUFMAX bytes to write a packet into.
+ * @regs: The &struct pt_regs of the current process.
+ *
+ * This function MUST handle the 'c' and 's' command packets,
+ * as well packets to set / remove a hardware breakpoint, if used.
+ * If there are additional packets which the hardware needs to handle,
+ * they are handled here. The code should return -1 if it wants to
+ * process more packets, and a %0 or %1 if it wants to exit from the
+ * gdb callback.
+ */
+extern int
+gdb_arch_handle_exception(int vector, int signo, int err_code,
+ struct pt_regs *regs,
+ char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ atomic_t *gdb_cpu_single_step);
+
+/*
+ * gdb_roundup_cpus - Get other CPUs into a holding pattern
+ * @flags: Current IRQ state
+ *
+ * On SMP systems, we need to get the attention of the other CPUs
+ * and get them be in a known state. This should do what is needed
+ * to get the other CPUs to call gdb_wait(). Note that on some arches,
+ * the NMI approach is not used for rounding up all the CPUs. For example,
+ * in case of MIPS, smp_call_function() is used to roundup CPUs. In
+ * this case, we have to make sure that interrupts are enabled before
+ * calling smp_call_function(). The argument to this function is
+ * the flags that will be used when restoring the interrupts. There is
+ * local_irq_save() call before gdb_roundup_cpus().
+ *
+ * On non-SMP systems, this is not called.
+ */
+//extern void gdb_roundup_cpus(unsigned long flags);
+
+/* Optional functions. */
+extern int gdb_validate_break_address(unsigned long addr);
+extern int gdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
+extern int gdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
+
+/*
+ * struct gdb_arch - Describe architecture specific values.
+ * @gdb_bpt_instr: The instruction to trigger a breakpoint.
+ * @flags: Flags for the breakpoint, currently just %GDB_HW_BREAKPOINT.
+ * @set_breakpoint: Allow an architecture to specify how to set a software
+ * breakpoint.
+ * @remove_breakpoint: Allow an architecture to specify how to remove a
+ * software breakpoint.
+ * @set_hw_breakpoint: Allow an architecture to specify how to set a
hardware
+ * breakpoint.
+ * @remove_hw_breakpoint: Allow an architecture to specify how to remove a
+ * hardware breakpoint.
+ * @remove_all_hw_break: Allow an architecture to specify how to remove all
+ * hardware breakpoints.
+ * @correct_hw_break: Allow an architecture to specify how to correct the
+ * hardware debug registers.
+ */
+struct gdb_arch {
+ unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE];
+ unsigned long flags;
+
+ int (*set_breakpoint)(unsigned long, char *);
+ int (*remove_breakpoint)(unsigned long, char *);
+ int (*set_hw_breakpoint)(unsigned long, int, enum gdb_bptype);
+ int (*remove_hw_breakpoint)(unsigned long, int, enum gdb_bptype);
+ void (*remove_all_hw_break)(void);
+ void (*correct_hw_break)(void);
+};
+
+/*
+ * struct gdb_io - Describe the interface for an I/O driver to talk with
GDB.
+ * @name: Name of the I/O driver.
+ * @read_char: Pointer to a function that will return one char.
+ * @write_char: Pointer to a function that will write one char.
+ * @flush: Pointer to a function that will flush any pending writes.
+ * @init: Pointer to a function that will initialize the device.
+ * @pre_exception: Pointer to a function that will do any prep work for
+ * the I/O driver.
+ * @post_exception: Pointer to a function that will do any cleanup work
+ * for the I/O driver.
+ */
+/*struct gdb_io {
+ const char *name;
+ int (*read_char) (void);
+ void (*write_char) (u8);
+ void (*flush) (void);
+ int (*init) (void);
+ void (*deinit) (void);
+ void (*pre_exception) (void);
+ void (*post_exception) (void);
+};
+*/
+int gdb_attach_process(id_t pid, char *gdb_cons);
+int gdb_attach_thread(id_t pid, id_t tid, char *gdb_cons);
+
+int sys_gdb_attach_process(id_t pid, char __user *gdb_cons);
+int sys_gdb_attach_thread(id_t pid, id_t tid, char __user *gdb_cons);
+
+extern struct gdb_arch arch_gdb_ops;
+
+extern int gdb_hex2long(char **ptr, long *long_val);
+extern int gdb_mem2hex(char *mem, char *buf, int count);
+extern int gdb_hex2mem(char *buf, char *mem, int count);
+
+extern int gdb_isremovedbreak(struct gdb_bkpt *gdb_breaks, id_t aspace_id,
unsigned long addr);
+
+extern int
+gdb_handle_exception(int ex_vector, int signo, int err_code,
+ struct pt_regs *regs);
+/*extern int gdb_nmicallback(int cpu, void *regs);
+*/
+#endif /* _GDB_H_ */
=======================================
--- /dev/null
+++ /include/lwk/gdbio.h Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,49 @@
+#ifndef _GDBIO_H_
+#define _GDBIO_H_
+
+#include <lwk/kernel.h>
+#include <lwk/poll.h>
+#include <lwk/kfs.h>
+#include <lwk/sched.h>
+#include <lwk/htable.h>
+
+#include <arch/unistd.h>
+#include <arch/uaccess.h>
+#include <arch/atomic.h>
+
+#define GDBIO_BUFFER_SIZE 4096
+#define GDB_HTABLE_ORDER 6
+
+struct gdbio_buffer {
+ unsigned char *buf;
+ waitq_t poll_wait;
+
+ atomic_t avail_space;
+ atomic_t avail_data;
+
+ int capacity;
+ int rindex;
+ int windex;
+};
+
+struct gdb_fifo_file {
+ struct gdbio_buffer* in_buffer; //From the perspective of GDB server, it
caches GDB requests
+ struct gdbio_buffer* out_buffer;//From the perspective of GDB server, it
caches GDB replies
+};
+
+struct gdb_fifo_inode_priv {
+ struct gdb_fifo_file* file_ptr;
+};
+
+struct file *gdbio_connect(char *gdbcons); //called by gdb client
+void gdbio_detach(struct file *filep); //called by gdb server stub
+ssize_t gdbclient_read(struct file *filep, char *buffer, ssize_t len);
+ssize_t gdbclient_write(struct file *filep, char *buffer, ssize_t size);
+ssize_t gdbserver_read(struct file *filep, char *buffer, ssize_t len);
+ssize_t gdbserver_write(struct file *filep, const char *buffer, ssize_t
size);
+
+struct file* open_gdb_fifo(const char *gdb_cons);
+int mk_gdb_fifo(const char* name);
+void rm_gdb_fifo(struct file *filep);
+
+#endif
=======================================
--- /dev/null
+++ /kernel/gdb.c Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,1519 @@
+/*
+ * GDB stub.
+ *
+ * Maintainer: Jason Wessel <
jason....@windriver.com>
+ *
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002-2004 Timesys Corporation
+ * Copyright (C) 2003-2004 Amit S. Kale <
amit...@linsyssoft.com>
+ * Copyright (C) 2004 Pavel Machek <
pa...@suse.cz>
+ * Copyright (C) 2004-2006 Tom Rini <
tr...@kernel.crashing.org>
+ * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2005-2008 Wind River Systems, Inc.
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <
mi...@redhat.com>
+ * Copyright (C) 2008 Patrick Bridges, University of New Mexico
+ *
+ * Contributors at various stages not listed above:
+ * Jason Wessel (
jason....@windriver.com )
+ * George Anzinger <
geo...@mvista.com>
+ * Anurekh Saxena (
anurekh...@timesys.com)
+ * Lake Stevens Instrument Division (Glenn Engel)
+ * Jim Kingdon, Cygnus Support.
+ *
+ * Original GDB stub: David Grothe <
da...@gcom.com>,
+ * Tigran Aivazian <
tig...@sco.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+#include <lwk/driver.h>
+#include <lwk/errno.h>
+#include <lwk/compiler.h>
+#include <lwk/interrupt.h>
+#include <lwk/spinlock.h>
+#include <lwk/console.h>
+#include <lwk/kernel.h>
+#include <lwk/ptrace.h>
+#include <lwk/string.h>
+#include <lwk/delay.h>
+#include <lwk/init.h>
+#include <lwk/aspace.h>
+#include <lwk/gdb.h>
+#include <lwk/smp.h>
+#include <lwk/params.h>
+#include <lwk/linux_compat.h>
+#include <lwk/kfs.h>
+#include <lwk/gdbio.h>
+#include <lwk/htable.h>
+#include <lwk/list.h>
+#include <lwk/driver.h>
+#include <lwk/xcall.h>
+#include <lwk/kthread.h>
+
+#include <arch-generic/fcntl.h>
+#include <arch/byteorder.h>
+#include <arch/atomic.h>
+#include <arch/cacheflush.h>
+#include <arch/system.h>
+#include <arch/uaccess.h>
+#include <arch/gdb.h>
+
+struct gdb_per_thread_state {//gdb per thread state
+ int ex_vector;
+ int signo;
+ int err_code;
+ int cpu;
+ int pass_exception;
+ long threadid;
+ struct pt_regs *lwk_regs;
+};
+
+struct gdb_per_process_state {//gdb per process state, the following
states are shared by all threads belonging to the process
+ id_t pid; //process id
+ struct hlist_node ht_link;
+
+ struct task_struct *gdb_usethread;
+ struct task_struct *gdb_contthread;
+
+ struct gdb_bkpt gdb_break[GDB_MAX_BREAKPOINTS];
+ struct file *gdb_filep;
+
+ /*
+ * The CPU# of the active CPU, or -1 if none:
+ */
+ atomic_t gdb_active; //serializing breakpoint hits from multiple
threads at the same time
+ int gdb_connected;
+ //int gdb_single_step = 0;
+ /* to keep track of the CPU which is doing the single stepping*/
+ atomic_t gdb_cpu_doing_single_step;
+
+ /* Our I/O buffers. */
+ char remcom_in_buffer[GDBIO_BUFFER_SIZE];
+ char remcom_out_buffer[GDBIO_BUFFER_SIZE];
+
+ /* Storage for the registers, in GDB format. */
+ unsigned long gdb_regs[(GDB_NUM_REGBYTES + sizeof(unsigned long) - 1) /
sizeof(unsigned long)];
+
+ //static unsigned int gdb_stop_cpus;
+ /*
+ * We use NR_CPUs not PERCPU, in case gdb is used to debug early
+ * bootup code (which might not have percpu set up yet):
+ */
+ //static atomic_t passive_cpu_wait[NR_CPUS];
+ //static atomic_t cpu_in_gdb[NR_CPUS];
+};
+//mappings between gdb global state and task being debuged
+static struct htable *gdb_htable = NULL;
+static spinlock_t htable_lock;
+static atomic_t htable_inited = ATOMIC_INIT(0);
+
+static atomic_t pt_regs_task_offset = ATOMIC_INIT(-1);
+//TODO always right???
+#define TASK_PTR(pt_regs) ((struct task_struct *)((void *) pt_regs -
atomic_read(&pt_regs_task_offset)))
+
+/*
+ * Finally, some GDB code :-)
+ */
+/*
+ * gdb_htable contains info about the GDB global state.
+ */
+static uint64_t gdb_hash_pid(const void *pid, size_t order)
+{
+ return (* ((id_t *) pid)) % (1<<order);
+}
+
+static int gdb_key_compare(const void *key_in_search, const void
*key_in_table){
+ id_t * id_in_search = (id_t *) key_in_search;
+ id_t * id_in_table = (id_t *) key_in_table;
+ if(*id_in_search == *id_in_table){
+ return 0;
+ }else if(*id_in_search < *id_in_table){
+ return -1;
+ }
+ return 1;
+}
+
+static void gdb_htable_add(struct gdb_per_process_state *ggs){
+ spin_lock(&htable_lock);
+ htable_add(gdb_htable, ggs);
+ spin_unlock(&htable_lock);
+}
+
+static void gdb_htable_del(struct gdb_per_process_state *ggs){
+ spin_lock(&htable_lock);
+ htable_del(gdb_htable, ggs);
+ spin_unlock(&htable_lock);
+}
+
+static struct gdb_per_process_state *gdb_htable_lookup(id_t pid){
+ spin_lock(&htable_lock);
+ struct gdb_per_process_state *ggs = htable_lookup(gdb_htable, &pid);
+ spin_unlock(&htable_lock);
+ return ggs;
+}
+
+//GDB IO related code
+static struct file *gdbio_init(char *gdb_cons){
+ if(mk_gdb_fifo(gdb_cons) < 0){
+ return NULL;
+ }
+ return open_gdb_fifo(gdb_cons);
+}
+
+//block reading
+static char gdbio_get_char(struct file *gdb_filep){
+ char ch;
+ gdbserver_read(gdb_filep, &ch, 1);
+ return ch;
+}
+
+//block writing
+static void gdbio_write_char(struct file *gdb_filep, char ch){
+ gdbserver_write(gdb_filep, &ch, 1);
+}
+
+
+/*
+ * Weak aliases for breakpoint management,
+ * can be overriden by architectures when needed:
+ */
+/*int __weak gdb_validate_break_address(unsigned long addr)
+{
+ char tmp_variable[BREAK_INSTR_SIZE];
+
+ return probe_kernel_read(tmp_variable, (char *)addr, BREAK_INSTR_SIZE);
+}*/
+/*
+ * Replace the first byte of the instruction with INT3(cc)
+ */
+int __weak gdb_arch_set_breakpoint(unsigned long addr, char *saved_instr){
+ int err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE);
+ if (err) return err;
+
+ return probe_kernel_write((char *)addr, arch_gdb_ops.gdb_bpt_instr,
BREAK_INSTR_SIZE);
+}
+/*
+ * Restore the instruction repalced by gdb_arch_set_breakpoint func
+ * */
+int __weak gdb_arch_remove_breakpoint(unsigned long addr, char *bundle){
+ return probe_kernel_write((char *)addr, (char *)bundle, BREAK_INSTR_SIZE);
+}
+
+unsigned long __weak gdb_arch_pc(int exception, struct pt_regs *regs){
+ return instruction_pointer(regs);
+}
+
+int __weak gdb_arch_init(void){
+ return 0;
+}
+
+/**
+ * gdb_disable_hw_debug - Disable hardware debugging while we in gdb.
+ * @regs: Current &struct pt_regs.
+ *
+ * This function will be called if the particular architecture must
+ * disable hardware debugging while it is processing gdb packets or
+ * handling exception.
+ */
+void __weak gdb_disable_hw_debug(struct pt_regs *regs){
+ //set_debugreg(0UL, 7);
+}
+
+/*
+ * GDB remote protocol parser:
+ */
+
+static const char hexchars[] = "0123456789abcdef";
+
+static int hex(char ch){
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ if ((ch >= 'A') && (ch <= 'F'))
+ return ch - 'A' + 10;
+ return -1;
+}
+
+
+/* scan for the sequence $<data>#<checgsum> */
+static void gdb_get_packet(struct gdb_per_process_state *ggs)
+{
+ unsigned char checgsum;
+ unsigned char xmitcsum;
+ int count;
+ char ch;
+ do {
+
+ while(gdbio_get_char(ggs->gdb_filep) != '$')
+ ggs->gdb_connected = 1;
+ checgsum = 0;
+ xmitcsum = -1;
+ count = 0;
+ memset(ggs->remcom_in_buffer, 0, sizeof(ggs->remcom_in_buffer));
+
+ /*
+ * now, read until a # or end of buffer is found:
+ */
+ while (count < (GDBIO_BUFFER_SIZE - 1)) {
+ ch = gdbio_get_char(ggs->gdb_filep);
+ if (ch == '#')
+ break;
+ ggs->remcom_in_buffer[count++] = ch;
+ checgsum = checgsum + ch;
+ }
+ ggs->remcom_in_buffer[count] = 0;
+
+ if (ch == '#') {
+ xmitcsum = hex(gdbio_get_char(ggs->gdb_filep)) << 4;
+ xmitcsum += hex(gdbio_get_char(ggs->gdb_filep));
+
+ if (checgsum != xmitcsum) {
+ //printk("GDB: packet(%s) is invalid\n", buffer);
+ gdbio_write_char(ggs->gdb_filep, '-');
+ } else {
+ //printk("GDB: packet(%s) is valid\n", buffer);
+ gdbio_write_char(ggs->gdb_filep, '+');
+ }
+ }
+ } while (checgsum != xmitcsum);
+ //printk("GDB Request<2>: %s\n", buffer);
+}
+
+/*
+ * Send the packet in buffer.
+ * Check for gdb connection if asked for.
+ */
+static void gdb_put_packet(struct gdb_per_process_state *ggs)
+{
+ //printk("GDB Reply<2>: %s\n", buffer);
+ unsigned char checgsum;
+ int count;
+ char ch;
+ /*
+ * $<packet info>#<checgsum>.
+ */
+ while (1) {
+ checgsum = 0;
+ count = 0;
+
+ gdbio_write_char(ggs->gdb_filep, '$');
+ while ((ch = ggs->remcom_out_buffer[count])) {
+ gdbio_write_char(ggs->gdb_filep, ch);
+ checgsum += ch;
+ count++;
+ }
+ gdbio_write_char(ggs->gdb_filep, '#');
+ gdbio_write_char(ggs->gdb_filep, hexchars[checgsum >> 4]);
+ gdbio_write_char(ggs->gdb_filep, hexchars[checgsum & 0xf]);
+
+ /* Now see what we get in reply. */
+ ch = gdbio_get_char(ggs->gdb_filep);
+
+ if (ch == 3){
+ ch = gdbio_get_char(ggs->gdb_filep);
+ }
+
+ /* If we get an ACK, we are done. */
+ if (ch == '+'){
+ //printk("GDB: send reply(%s) successfully\n", buffer);
+ return;
+ }
+
+ /*
+ * If we get the start of another packet, this means
+ * that GDB is attempting to reconnect. We will NAK
+ * the packet being sent, and stop trying to send this
+ * packet.
+ */
+ if (ch == '$') {
+ //printk("GDB: send reply(%s) failed\n", buffer);
+ gdbio_write_char(ggs->gdb_filep, '-');
+ return;
+ }
+
+ //printk("GDB: resending reply(%s)\n", buffer);
+ }
+}
+
+static char *pack_hex_byte(char *pkt, u8 byte){
+ *pkt++ = hexchars[byte >> 4];
+ *pkt++ = hexchars[byte & 0xf];
+
+ return pkt;
+}
+
+static unsigned long userspace_vaddr2kenel_vaddr(id_t aspace_id, unsigned
long userspace_vaddr){
+ addr_t paddr;
+
+ aspace_virt_to_phys(aspace_id, userspace_vaddr, &paddr);
+ return (unsigned long) __va(paddr);
+}
+
+/*
+ * @param:
+ * mem: kernel virtual address
+ * buf: kernel virtual address
+ * Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null). May return an
error.
+ */
+int gdb_mem2hex(char *mem, char *buf, int count)
+{
+ char *tmp;
+ int err;
+
+ /*
+ * We use the upper half of buf as an intermediate buffer for the
+ * raw memory copy. Hex conversion will work against this one.
+ */
+ tmp = buf + count;
+
+ err = probe_kernel_read(tmp, mem, count);
+ if (!err) {
+ while (count > 0) {
+ buf = pack_hex_byte(buf, *tmp);
+ tmp++;
+ count--;
+ }
+
+ *buf = 0;
+ }
+
+ return err;
+}
+
+/*
+ * @param:
+ * buf: kernel virtual address
+ * mem: kernel virtual address
+ * Copy the binary array pointed to by buf into mem. Fix $, #, and
+ * 0x7d escaped with 0x7d. Return a pointer to the character after
+ * the last byte written.
+ */
+static int gdb_ebin2mem(char *buf, char *mem, int count)
+{
+ int err = 0;
+ char c;
+
+ while (count-- > 0) {
+ c = *buf++;
+ if (c == 0x7d)
+ c = *buf++ ^ 0x20;
+
+ err = probe_kernel_write(mem, &c, 1);
+ if (err)
+ break;
+
+ mem++;
+ }
+
+ return err;
+}
+
+/*
+ * Convert the hex array pointed to by buf into binary to be placed in mem.
+ * Return a pointer to the character AFTER the last byte written.
+ * May return an error.
+ */
+int gdb_hex2mem(char *buf, char *mem, int count)
+{
+ char *tmp_raw;
+ char *tmp_hex;
+
+ /*
+ * We use the upper half of buf as an intermediate buffer for the
+ * raw memory that is converted from hex.
+ */
+ tmp_raw = buf + count * 2;
+
+ tmp_hex = tmp_raw - 1;
+ while (tmp_hex >= buf) {
+ tmp_raw--;
+ *tmp_raw = hex(*tmp_hex--);
+ *tmp_raw |= hex(*tmp_hex--) << 4;
+ }
+
+ return probe_kernel_write(mem, tmp_raw, count);
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int gdb_hex2long(char **ptr, long *long_val)
+{
+ int hex_val;
+ int num = 0;
+
+ *long_val = 0;
+
+ while (**ptr) {
+ hex_val = hex(**ptr);
+ if (hex_val < 0)
+ break;
+
+ *long_val = (*long_val << 4) | hex_val;
+ num++;
+ (*ptr)++;
+ }
+
+ return num;
+}
+
+/* Write memory due to an 'M' or 'X' packet. */
+static int write_mem_msg(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs, int binary)
+{
+ char *ptr = &ggs->remcom_in_buffer[1];
+ unsigned long addr;
+ unsigned long length;
+ int err;
+
+ if (gdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&
+ gdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {
+ /*Address translationg: because gdbserver needs to write debugie
process's memory*/
+ addr = userspace_vaddr2kenel_vaddr(ggs->pid, addr);
+ if(!addr){
+ return -EINVAL;
+ }
+ if (binary)
+ err = gdb_ebin2mem(ptr, (char *)addr, length);
+ else
+ err = gdb_hex2mem(ptr, (char *)addr, length);
+ if (err) return err;
+ if (CACHE_FLUSH_IS_SAFE)
+ flush_icache_range(addr, addr + length + 1);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void error_packet(char *pkt, int error){
+ error = -error;
+ pkt[0] = 'E';
+ pkt[1] = hexchars[(error / 10)];
+ pkt[2] = hexchars[(error % 10)];
+ pkt[3] = '\0';
+}
+
+
+#define BUF_THREAD_ID_SIZE 16
+
+static char *pack_threadid(char *pkt, unsigned char *id){
+ char *limit;
+
+ limit = pkt + BUF_THREAD_ID_SIZE;
+ while (pkt < limit)
+ pkt = pack_hex_byte(pkt, *id++);
+
+ return pkt;
+}
+
+static void int_to_threadref(unsigned char *id, int value){
+ unsigned char *scan;
+ int i = 4;
+
+ scan = (unsigned char *)id;
+ while (i--)
+ *scan++ = 0;
+ *scan++ = (value >> 24) & 0xff;
+ *scan++ = (value >> 16) & 0xff;
+ *scan++ = (value >> 8) & 0xff;
+ *scan++ = (value & 0xff);
+}
+
+struct task_struct *get_task(id_t pid, id_t tid){
+ struct task_struct *tsk, *tskret = NULL;
+ struct aspace *aspace = aspace_acquire(pid);
+ if(aspace){
+ list_for_each_entry(tsk, &aspace->task_list, aspace_link) {
+ if (tsk->id == tid)
+ tskret = tsk;
+ }
+ }
+ aspace_release(aspace);
+ return tskret;
+}
+
+#define GDB_STOP_TASK 0
+#define GDB_RESUME_TASK 1
+/*
+ * Even if the task is in TASK_(UN)INTERRUPTIBLE state, it still works!
+ * Becuase these tasks are wakeup via sched_wakeup_task() api, which only
wakeup tasks in TASK_(UN)INTERRUPTIBLE state.
+ **/
+static void gdb_stop_or_resume_single_task(struct task_struct *task, int
req_type){
+ unsigned long irqstate;
+ spin_lock_irqsave(&task->aspace->lock, irqstate);
+ if(req_type == GDB_STOP_TASK){//stop a running task
+ if(task->state != TASK_STOPPED){
+ task->ptrace = (task->state << 1) | 1; //bit 0 of ptrace flag
indicates the task is being traced,
+ //bit 1~3 of ptrace flag indicates task state before it is stopped
+ set_task_state(task, TASK_STOPPED);
+ //printk("GDB: Task(pid=%d, tid=%d) stopped with ptrace=%d\n)",
task->aspace->id, task->id, task->ptrace);
+ }
+ }else{//resume a stopped task
+ if(task->state == TASK_STOPPED){//TODO for tasks in
TASK_(UN)INTERRUPTIBLE, shouldn't simply set it's state to TASK_RUNNING
+ //struct pt_regs *regs = task_pt_regs(&task->arch);
+ //printk("GDB: Resume from %p TF=%lu\n", (void *) regs->rip,
regs->eflags & TF_MASK);
+ set_task_state(task, task->ptrace >> 1);
+ task->ptrace = 0;
+ //printk("GDB: Task(pid=%d, tid=%d) resume with task->state=%d",
task->aspace->id, task->id, task->state);
+ }
+ }
+ xcall_reschedule(task->cpu_id);
+ spin_unlock_irqrestore(&task->aspace->lock, irqstate);
+}
+
+static void gdb_stop_or_resume_task(struct task_struct *task, int
req_type){
+ if(task == NULL){
+ return;
+ }
+
+ if(task->id == task->aspace->id){//GDB ALL stop mode, stop or resume all
threads within the process
+ struct task_struct *tsk;
+ struct aspace *aspace = aspace_acquire(task->aspace->id);
+ list_for_each_entry(tsk, &aspace->task_list, aspace_link) {
+ gdb_stop_or_resume_single_task(tsk, req_type);
+ }
+ aspace_release(aspace);
+ }else{
+ gdb_stop_or_resume_single_task(task, req_type);
+ }
+}
+
+/*
+ * Some architectures need cache flushes when we set/clear a
+ * breakpoint:
+ */
+static void gdb_flush_swbreak_addr(unsigned long kernel_vaddr){
+ if (!CACHE_FLUSH_IS_SAFE)
+ return;
+
+ if (current->aspace) {
+ flush_cache_range(current->aspace, kernel_vaddr, kernel_vaddr +
BREAK_INSTR_SIZE);
+ } else {
+ flush_icache_range(kernel_vaddr, kernel_vaddr + BREAK_INSTR_SIZE);
+ }
+}
+
+/*
+ * SW breakpoint management:
+ */
+static int gdb_activate_sw_breakpoints(struct gdb_bkpt *gdb_break){
+ unsigned long addr;
+ int error = 0;
+ int i;
+
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if (gdb_break[i].state != GDB_BP_SET)
+ continue;
+
+ addr = gdb_break[i].bpt_addr;
+ error = gdb_arch_set_breakpoint(addr,
+ gdb_break[i].saved_instr);
+ if (error) return error;
+
+ gdb_flush_swbreak_addr(addr);
+ gdb_break[i].state = GDB_BP_ACTIVE;
+ //printk("GDB: activate breakpoint at addr: %p\n", (void *) addr);
+ }
+ return 0;
+}
+
+static int gdb_set_sw_break(struct gdb_bkpt *gdb_break, id_t aspace_id,
unsigned long u_vaddr)
+{
+ //Address translation: because gdbserver needs to read/write debugie
process's address space
+ int breakno = -1;
+ int i;
+
+ unsigned long kernel_vaddr = userspace_vaddr2kenel_vaddr(aspace_id,
u_vaddr);
+ if(!kernel_vaddr){
+ return -EINVAL;
+ }
+ //printk("GDB: set breakpoint at addr(%p %p)\n", (void *) u_vaddr, (void
*) kernel_vaddr);
+
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if ((gdb_break[i].state == GDB_BP_SET) &&
+ (gdb_break[i].bpt_addr == kernel_vaddr))
+ return -EEXIST;
+ }
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if (gdb_break[i].state == GDB_BP_REMOVED &&
+ gdb_break[i].bpt_addr == kernel_vaddr) {
+ breakno = i;
+ break;
+ }
+ }
+
+ if (breakno == -1) {
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if (gdb_break[i].state == GDB_BP_UNDEFINED) {
+ breakno = i;
+ break;
+ }
+ }
+ }
+
+ if (breakno == -1)
+ return -E2BIG;
+
+ gdb_break[breakno].state = GDB_BP_SET;
+ gdb_break[breakno].type = GDB_BP_BREAKPOINT;
+ gdb_break[breakno].bpt_addr = kernel_vaddr;
+
+ return 0;
+}
+
+static int gdb_deactivate_sw_breakpoints(struct gdb_bkpt *gdb_break){
+ unsigned long addr;
+ int error = 0;
+ int i;
+
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if (gdb_break[i].state != GDB_BP_ACTIVE)
+ continue;
+ addr = gdb_break[i].bpt_addr;
+ error = gdb_arch_remove_breakpoint(addr,
+ gdb_break[i].saved_instr);
+ if (error)
+ return error;
+
+ gdb_flush_swbreak_addr(addr);
+ gdb_break[i].state = GDB_BP_SET;
+ //printk("GDB: deactivate breakpoint at addr: %p\n", (void *) addr);
+ }
+ return 0;
+}
+
+static int gdb_remove_sw_break(struct gdb_bkpt *gdb_break, id_t aspace_id,
unsigned long u_vaddr){
+ unsigned long kernel_vaddr = userspace_vaddr2kenel_vaddr(aspace_id,
u_vaddr);
+ int i;
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if ((gdb_break[i].state == GDB_BP_SET) &&
+ (gdb_break[i].bpt_addr == kernel_vaddr)) {
+ gdb_break[i].state = GDB_BP_REMOVED;
+ //printk("GDB: remove breakpoint at addr: %p %p\n", (void *) u_vaddr,
(void *) kernel_vaddr);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int gdb_isremovedbreak(struct gdb_bkpt *gdb_break, id_t aspace_id,
unsigned long u_vaddr){
+ unsigned long kernel_vaddr = userspace_vaddr2kenel_vaddr(aspace_id,
u_vaddr);
+ int i;
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if ((gdb_break[i].state == GDB_BP_REMOVED) &&
+ (gdb_break[i].bpt_addr == kernel_vaddr))
+ return 1;
+ }
+ return 0;
+}
+
+int remove_all_break(struct gdb_bkpt *gdb_break){
+ unsigned long addr;
+ int error;
+ int i;
+
+ /* Clear memory breakpoints. */
+ for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
+ if (gdb_break[i].state != GDB_BP_SET)
+ continue;
+ addr = gdb_break[i].bpt_addr;
+ error = gdb_arch_remove_breakpoint(addr, gdb_break[i].saved_instr);
+ if (error)
+ return error;
+ gdb_break[i].state = GDB_BP_REMOVED;
+ }
+
+ /* Clear hardware breakpoints. */
+ if (arch_gdb_ops.remove_all_hw_break)
+ arch_gdb_ops.remove_all_hw_break();
+
+ return 0;
+}
+
+/*
+ * All the functions that start with gdb_cmd are the various
+ * operations to implement the handlers for the gdbserial protocol
+ * where GDB is communicating with an external debugger
+ */
+
+/* Handle the 'g' get registers request */
+static void gdb_cmd_getregs(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ if(ggs->gdb_usethread){//Hg0 or Hgpid
+ //show_registers(task_pt_regs(&(ggs->gdb_usethread->arch)));
+ pt_regs_to_gdb_regs(ggs->gdb_regs,
task_pt_regs(&(ggs->gdb_usethread->arch)));
+ gdb_mem2hex((char *)ggs->gdb_regs, ggs->remcom_out_buffer,
GDB_NUM_GPREGBYTES);
+ }else{//Hg-1 apply g command to all threads
+ //error_packet(ggs->remcom_out_buffer, -EINVAL);
+ int i = 0;
+ struct task_struct *tsk;
+ struct aspace *aspace = aspace_acquire(ggs->pid);
+ list_for_each_entry(tsk, &aspace->task_list, aspace_link) {
+ pt_regs_to_gdb_regs(ggs->gdb_regs, task_pt_regs(&(tsk->arch)));
+ gdb_mem2hex((char *)ggs->gdb_regs, ggs->remcom_out_buffer +
GDB_NUM_REGBYTES * i, GDB_NUM_REGBYTES);
+ i++;
+ }
+ aspace_release(aspace);
+ }
+}
+/* Handle the 'G' set registers request */
+static void gdb_cmd_setregs(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ gdb_hex2mem(&ggs->remcom_in_buffer[1], (char *)ggs->gdb_regs,
GDB_NUM_GPREGBYTES);
+ if (!ggs->gdb_usethread
+ //|| ggs->gdb_usethread != TASK_PTR(gs->lwk_regs)
+ ) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ } else {
+ gdb_regs_to_pt_regs(ggs->gdb_regs,
task_pt_regs(&(ggs->gdb_usethread->arch)));
+ strcpy(ggs->remcom_out_buffer, "OK");
+ }
+}
+/* Handle the '?' status packets */
+static void gdb_cmd_status(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ /*
+ * We know that this packet is only sent
+ * during initial connect. So to be safe,
+ * we clear out our breakpoints now in case
+ * GDB is reconnecting.
+ */
+ remove_all_break(ggs->gdb_break);
+
+ ggs->remcom_out_buffer[0] = 'S';
+ pack_hex_byte(&ggs->remcom_out_buffer[1], gs->signo);
+}
+
+/* Handle the 'm' memory read bytes */
+static void gdb_cmd_memread(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ char *ptr = &ggs->remcom_in_buffer[1];
+ unsigned long length;
+ unsigned long addr;
+ int err = 0;
+
+ if (gdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
+ gdb_hex2long(&ptr, &length) > 0) {
+ unsigned long kernel_vaddr = userspace_vaddr2kenel_vaddr(ggs->pid, addr);
+ if(!kernel_vaddr){
+ err = -EINVAL;
+ }else{
+ err = gdb_mem2hex((char *) kernel_vaddr, ggs->remcom_out_buffer,
length);
+ }
+ if (err) error_packet(ggs->remcom_out_buffer, err);
+ } else {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ }
+}
+
+/* Handle the 'M' memory write bytes */
+static void gdb_cmd_memwrite(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ int err = write_mem_msg(ggs, gs, 0);
+
+ if (err)
+ error_packet(ggs->remcom_out_buffer, err);
+ else
+ strcpy(ggs->remcom_out_buffer, "OK");
+}
+
+/* Handle the 'X' memory binary write bytes */
+static void gdb_cmd_binwrite(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ int err = write_mem_msg(ggs, gs, 1);
+
+ if (err)
+ error_packet(ggs->remcom_out_buffer, err);
+ else
+ strcpy(ggs->remcom_out_buffer, "OK");
+}
+
+/* Handle the 'D' or 'k', detach or kill packets */
+static void gdb_cmd_detachkill(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ int error;
+
+ /* The detach case */
+ if (ggs->remcom_in_buffer[0] == 'D') {
+ error = remove_all_break(ggs->gdb_break);
+ if (error < 0) {
+ error_packet(ggs->remcom_out_buffer, error);
+ } else {
+ strcpy(ggs->remcom_out_buffer, "OK");
+ }
+ gdb_put_packet(ggs);
+ } else {
+ /*
+ * Assume the kill case, with no exit code checking,
+ * trying to force detach the debugger:
+ */
+ remove_all_break(ggs->gdb_break);
+ }
+ //clear TF flag
+ gs->lwk_regs->eflags &= ~TF_MASK;
+ gdbio_detach(ggs->gdb_filep);
+ gdb_htable_del(ggs);
+ kmem_free(ggs);
+}
+
+/* Handle the 'R' reboot packets */
+static int gdb_cmd_reboot(struct gdb_per_thread_state *gs){
+ return 0;
+}
+
+/* Handle qXfer:features:read. */
+static int handle_qxfer_features (struct gdb_per_process_state *ggs,
struct gdb_per_thread_state *gs){
+ extern const char *const xml_builtin[][2]; //target description
+
+ size_t total_len = strlen (xml_builtin[0][1]);
+ size_t offset = 0, len = 0xFFF;
+ if (offset > total_len)
+ return -1;
+ if (offset + len > total_len)
+ len = total_len - offset;
+
+ ggs->remcom_out_buffer[0] = 'l';
+ memcpy (ggs->remcom_out_buffer + 1, xml_builtin[0][1] + offset, len);
+
+ return len;
+}
+
+
+/* Handle the 'q' query packets */
+static void gdb_cmd_query(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ struct task_struct *thread;
+ struct aspace *aspace;
+ unsigned char thref[8];
+ char *ptr;
+
+ switch (ggs->remcom_in_buffer[1]) {
+ case 's':
+ case 'f':
+ if (memcmp(ggs->remcom_in_buffer + 2, "ThreadInfo", 10)) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ break;
+ }
+
+ ggs->remcom_out_buffer[0] = 'm';
+ ptr = ggs->remcom_out_buffer + 1;
+
+ aspace = aspace_acquire(ggs->pid);
+ list_for_each_entry(thread, &aspace->task_list, aspace_link) {
+ int_to_threadref(thref, thread->id);
+ pack_threadid(ptr, thref);
+ ptr += BUF_THREAD_ID_SIZE;
+ *(ptr++) = ',';
+ }
+ aspace_release(aspace);
+ *(--ptr) = '\0';
+ break;
+ case 'C':
+ /* Current thread id */
+ strcpy(ggs->remcom_out_buffer, "QC");
+ int_to_threadref(thref, TASK_PTR(gs->lwk_regs)->id);
+ pack_threadid(ggs->remcom_out_buffer + 2, thref);
+ break;
+ case 'T':
+ if (memcmp(ggs->remcom_in_buffer + 1, "TStatus", 7) == 0){
+ break;
+ }
+
+ if (memcmp(ggs->remcom_in_buffer + 1, "ThreadExtraInfo,", 16)) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ break;
+ }
+ ptr = ggs->remcom_in_buffer + 17;
+ gdb_hex2long(&ptr, &gs->threadid);
+ thread = get_task(ggs->pid, gs->threadid);
+ if (!thread) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ break;
+ }
+
+ gdb_mem2hex(thread->name, ggs->remcom_out_buffer, 16);
+ break;
+ case 'S':
+ if(memcmp(ggs->remcom_in_buffer + 1, "Supported:", 10) == 0){
+ static char str[33 + sizeof(int)];
+ sprintf(str, "PacketSize=%04x;qXfer:features:read+", GDBIO_BUFFER_SIZE);
+ memcpy(ggs->remcom_out_buffer, str, strlen(str));
+ }
+ break;
+ case 'X':
+ if(memcmp(ggs->remcom_in_buffer + 1, "Xfer:features:read:", 19) == 0){
+ handle_qxfer_features(ggs, gs);
+ }
+ break;
+ case 'A':
+ if(memcmp(ggs->remcom_in_buffer + 1, "Attached", 8) == 0){
+ ggs->remcom_out_buffer[0] = 1; //TODO 1 attach to an existing process;
0 start a new process
+ }
+ break;
+ }
+}
+
+/* Handle the 'H' task query packets */
+static void gdb_cmd_task(struct gdb_per_process_state *ggs, struct
gdb_per_thread_state *gs){
+ struct task_struct *thread;
+ char *ptr;
+
+ switch (ggs->remcom_in_buffer[1]) {
+ case 'g': //Hg0 Hg-1 Hgpid
+ ptr = &ggs->remcom_in_buffer[2];
+ gdb_hex2long(&ptr, &gs->threadid);
+
+ if(gs->threadid == -1){//Hg-1
+ thread = NULL;
+ }else if(gs->threadid == 0){
+ thread = TASK_PTR(gs->lwk_regs);
+ }else{
+ thread = get_task(ggs->pid, gs->threadid);
+ if (!thread) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ break;
+ }
+ }
+
+ ggs->gdb_usethread = thread;
+ strcpy(ggs->remcom_out_buffer, "OK");
+ break;
+ case 'c'://Hc-1 Hc0 Hcpid
+ ptr = &ggs->remcom_in_buffer[2];
+ gdb_hex2long(&ptr, &gs->threadid);
+ if (!gs->threadid) {//Hc0 continue command apply to any threads
+ thread = TASK_PTR(gs->lwk_regs);
+ } else {
+ if(gs->threadid == -1){//Hc-1 continue command apply to all threads
+ gs->threadid = ggs->pid;
+ }
+ thread = get_task(ggs->pid, gs->threadid);
+ if (!thread) {
+ error_packet(ggs->remcom_out_buffer, -EINVAL);
+ break;
+ }
+ }
+ ggs->gdb_contthread = thread;
+ strcpy(ggs->remcom_out_buffer, "OK");
+ break;
+ }
+}
+
***The diff for this file has been truncated for email.***
=======================================
--- /dev/null
+++ /kernel/gdb_target_desc.c Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,386 @@
+static const char xml_feature_x86_64_linux_xml[] = {
+ '<', '?', 'x', 'm', 'l', ' ', 'v', 'e', 'r', 's',
+ 'i', 'o', 'n', '=', '"', '1', '.', '0', '"', '?',
+ '>', '\n',
+ '<', '!', '-', '-', ' ', 'C', 'o', 'p', 'y', 'r',
+ 'i', 'g', 'h', 't', ' ', '(', 'C', ')', ' ', '2',
+ '0', '1', '0', '-', '2', '0', '1', '3', ' ', 'F',
+ 'r', 'e', 'e', ' ', 'S', 'o', 'f', 't', 'w', 'a',
+ 'r', 'e', ' ', 'F', 'o', 'u', 'n', 'd', 'a', 't',
+ 'i', 'o', 'n', ',', ' ', 'I', 'n', 'c', '.', '\n',
+ '\n',
+ ' ', ' ', ' ', ' ', ' ', 'C', 'o', 'p', 'y', 'i',
+ 'n', 'g', ' ', 'a', 'n', 'd', ' ', 'd', 'i', 's',
+ 't', 'r', 'i', 'b', 'u', 't', 'i', 'o', 'n', ' ',
+ 'o', 'f', ' ', 't', 'h', 'i', 's', ' ', 'f', 'i',
+ 'l', 'e', ',', ' ', 'w', 'i', 't', 'h', ' ', 'o',
+ 'r', ' ', 'w', 'i', 't', 'h', 'o', 'u', 't', ' ',
+ 'm', 'o', 'd', 'i', 'f', 'i', 'c', 'a', 't', 'i',
+ 'o', 'n', ',', '\n',
+ ' ', ' ', ' ', ' ', ' ', 'a', 'r', 'e', ' ', 'p',
+ 'e', 'r', 'm', 'i', 't', 't', 'e', 'd', ' ', 'i',
+ 'n', ' ', 'a', 'n', 'y', ' ', 'm', 'e', 'd', 'i',
+ 'u', 'm', ' ', 'w', 'i', 't', 'h', 'o', 'u', 't',
+ ' ', 'r', 'o', 'y', 'a', 'l', 't', 'y', ' ', 'p',
+ 'r', 'o', 'v', 'i', 'd', 'e', 'd', ' ', 't', 'h',
+ 'e', ' ', 'c', 'o', 'p', 'y', 'r', 'i', 'g', 'h',
+ 't', '\n',
+ ' ', ' ', ' ', ' ', ' ', 'n', 'o', 't', 'i', 'c',
+ 'e', ' ', 'a', 'n', 'd', ' ', 't', 'h', 'i', 's',
+ ' ', 'n', 'o', 't', 'i', 'c', 'e', ' ', 'a', 'r',
+ 'e', ' ', 'p', 'r', 'e', 's', 'e', 'r', 'v', 'e',
+ 'd', '.', ' ', ' ', '-', '-', '>', '\n',
+ '\n',
+ '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ',
+ 'f', 'e', 'a', 't', 'u', 'r', 'e', ' ', 'S', 'Y',
+ 'S', 'T', 'E', 'M', ' ', '"', 'g', 'd', 'b', '-',
+ 't', 'a', 'r', 'g', 'e', 't', '.', 'd', 't', 'd',
+ '"', '>', '\n',
+ '<', 't', 'a', 'r', 'g', 'e', 't', '>', '\n',
+ ' ', ' ', ' ', ' ', '<', 'a', 'r', 'c', 'h', 'i',
+ 't', 'e', 'c', 't', 'u', 'r', 'e', '>', 'i', '3',
+ '8', '6', ':', 'x', '8', '6', '-', '6', '4', '<',
+ '/', 'a', 'r', 'c', 'h', 'i', 't', 'e', 'c', 't',
+ 'u', 'r', 'e', '>', '\n',
+ ' ', ' ', ' ', ' ', '<', 'o', 's', 'a', 'b', 'i',
+ '>', 'G', 'N', 'U', '/', 'L', 'i', 'n', 'u', 'x',
+ '<', '/', 'o', 's', 'a', 'b', 'i', '>', '\n',
+ ' ', ' ', ' ', ' ', '<', 'f', 'e', 'a', 't', 'u',
+ 'r', 'e', ' ', 'n', 'a', 'm', 'e', '=', '"', 'o',
+ 'r', 'g', '.', 'g', 'n', 'u', '.', 'g', 'd', 'b',
+ '.', 'i', '3', '8', '6', '.', 'c', 'o', 'r', 'e',
+ '"', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f', 'l', 'a',
+ 'g', 's', ' ', 'i', 'd', '=', '"', 'i', '3', '8',
+ '6', '_', 'e', 'f', 'l', 'a', 'g', 's', '"', ' ',
+ 's', 'i', 'z', 'e', '=', '"', '4', '"', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'C', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '0', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '0', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', '"', ' ', 's', 't', 'a', 'r', 't', '=', '"',
+ '1', '"', ' ', 'e', 'n', 'd', '=', '"', '1', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'P', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '2', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'A', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '4', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'Z', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '6', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '6', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'S', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '7', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '7', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'T', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '8', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '8', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'I', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '9', '"', ' ', 'e', 'n', 'd', '=', '"',
+ '9', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'D', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '0', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '0', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'O', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '1', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '1', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'N', 'T', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '4', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'R', 'F', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '6', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '6', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'V', 'M', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '7', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '7', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'A', 'C', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '1', '8', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '1', '8', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'V', 'I', 'F', '"', ' ', 's', 't', 'a', 'r',
+ 't', '=', '"', '1', '9', '"', ' ', 'e', 'n', 'd',
+ '=', '"', '1', '9', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'V', 'I', 'P', '"', ' ', 's', 't', 'a', 'r',
+ 't', '=', '"', '2', '0', '"', ' ', 'e', 'n', 'd',
+ '=', '"', '2', '0', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', 'f',
+ 'i', 'e', 'l', 'd', ' ', 'n', 'a', 'm', 'e', '=',
+ '"', 'I', 'D', '"', ' ', 's', 't', 'a', 'r', 't',
+ '=', '"', '2', '1', '"', ' ', 'e', 'n', 'd', '=',
+ '"', '2', '1', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', '/', 'f', 'l',
+ 'a', 'g', 's', '>', '\n',
+ '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'a', 'x',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'b', 'x',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'c', 'x',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'd', 'x',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 's', 'i',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'd', 'i',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'b', 'p',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'd', 'a', 't', 'a', '_', 'p', 't', 'r', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 's', 'p',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'd', 'a', 't', 'a', '_', 'p', 't', 'r', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '8', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '9', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '0',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '1',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '2',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '3',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '4',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', '1', '5',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '6', '4', '"', '/', '>', '\n',
+ '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'r', 'i', 'p',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '6', '4', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'c', 'o', 'd', 'e', '_', 'p', 't', 'r', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'e', 'f', 'l',
+ 'a', 'g', 's', '"', ' ', 'b', 'i', 't', 's', 'i',
+ 'z', 'e', '=', '"', '3', '2', '"', ' ', 't', 'y',
+ 'p', 'e', '=', '"', 'i', '3', '8', '6', '_', 'e',
+ 'f', 'l', 'a', 'g', 's', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'c', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'd', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'e', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'g', 's', '"',
+ ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=', '"',
+ '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=', '"',
+ 'i', 'n', 't', '3', '2', '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '0',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '1',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '2',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '3',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '4',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '5',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '6',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 's', 't', '7',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '8', '0', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', '3', '8', '7', '_', 'e', 'x', 't', '"',
+ '/', '>', '\n',
+ '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'c', 't',
+ 'r', 'l', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 's', 't',
+ 'a', 't', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 't', 'a',
+ 'g', '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e',
+ '=', '"', '3', '2', '"', ' ', 't', 'y', 'p', 'e',
+ '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r', 'o',
+ 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't', '"',
+ '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'i', 's',
+ 'e', 'g', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'i', 'o',
+ 'f', 'f', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'o', 's',
+ 'e', 'g', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'o', 'o',
+ 'f', 'f', '"', ' ', 'b', 'i', 't', 's', 'i', 'z',
+ 'e', '=', '"', '3', '2', '"', ' ', 't', 'y', 'p',
+ 'e', '=', '"', 'i', 'n', 't', '"', ' ', 'g', 'r',
+ 'o', 'u', 'p', '=', '"', 'f', 'l', 'o', 'a', 't',
+ '"', '/', '>', '\n',
+ ' ', ' ', ' ', ' ', ' ', ' ', '<', 'r', 'e', 'g',
+ ' ', 'n', 'a', 'm', 'e', '=', '"', 'f', 'o', 'p',
+ '"', ' ', 'b', 'i', 't', 's', 'i', 'z', 'e', '=',
+ '"', '3', '2', '"', ' ', 't', 'y', 'p', 'e', '=',
+ '"', 'i', 'n', 't', '"', ' ', 'g', 'r', 'o', 'u',
+ 'p', '=', '"', 'f', 'l', 'o', 'a', 't', '"', '/',
+ '>', '\n',
+ ' ', ' ', ' ', ' ', '\n',
+ ' ', ' ', ' ', ' ', '<', '/', 'f', 'e', 'a', 't',
+ 'u', 'r', 'e', '>', '\n',
+ '<', '/', 't', 'a', 'r', 'g', 'e', 't', '>', '\n',
+ 0 };
+
+const char *const xml_builtin[][2] = {
+ { "x86_64-linux.xml", xml_feature_x86_64_linux_xml },
+ { 0, 0 }
+};
=======================================
--- /dev/null
+++ /kernel/gdbio.c Wed Nov 6 19:48:40 2013 UTC
@@ -0,0 +1,399 @@
+#include <lwk/gdbio.h>
+#include <lwk/waitq.h>
+#include <lwk/htable.h>
+#include <lwk/list.h>
+#include <lwk/spinlock.h>
+#include <lwk/driver.h>
+
+#include <arch/atomic.h>
+#include <arch-generic/fcntl.h>
+
+#define dbg(fmt,args...)
+struct gdbio_state{
+ char gdbcons[32]; //gdb console id
+ struct hlist_node ht_link;
+ int detached;
+};
+
+static struct htable *gdbio_htable = NULL;
+static spinlock_t htable_lock;
+
+static uint64_t gdbio_hash_key(const void *gdbcons, size_t order){
+ char *buf = (char *) gdbcons;
+ int sum = 0, i;
+ for(i=0; i<strlen(buf); i++){
+ sum += buf[i];
+ }
+ //printk("GDB: hash(%s)=%d\n", (char *) gdbcons, sum % (1<<order));
+ return (sum % (1<<order));
+}
+
+static int gdbio_key_compare(const void *key_in_search,
+ const void *key_in_table){
+ char *search = (char *) key_in_search;
+ char *table = (char *) key_in_table;
+ return strcmp(search, table);
+}
+
+static void gdbio_htable_add(struct gdbio_state *gs){
+ spin_lock(&htable_lock);
+ htable_add(gdbio_htable, gs);
+ spin_unlock(&htable_lock);
+}
+
+static void gdbio_htable_del(struct gdbio_state *gs){
+ spin_lock(&htable_lock);
+ htable_del(gdbio_htable, gs);
+ spin_unlock(&htable_lock);
+}
+
+static struct gdbio_state *gdbio_htable_lookup(char *gdbcons){
+ spin_lock(&htable_lock);
+ struct gdbio_state *gs = htable_lookup(gdbio_htable, gdbcons);
+ spin_unlock(&htable_lock);
+ return gs;
+}
+/*
+void gdbio_htable_test(void){
+ char * cons = "gdbcons0";
+ struct gdbio_state *gs = kmem_alloc(sizeof(struct gdbio_state));
+ strcpy(gs->gdbcons, cons);
+
+ gdbio_htable_add(gs);
+
+ if(gdbio_htable_lookup(gs->gdbcons)){
+ printk("GDB: gdbio_htable test succeed\n");
+ }else{
+ printk("GDB: gdbio_htable test failed\n");
+ }
+ kmem_free(gs);
+}
+*/
+static int __init gdbio_module_init(void){
+ gdbio_htable = htable_create(
+ GDB_HTABLE_ORDER,
+ offsetof(struct gdbio_state, gdbcons),
+ offsetof(struct gdbio_state, ht_link),
+ gdbio_hash_key,
+ gdbio_key_compare
+ );
+ if(!gdbio_htable){
+ printk("GDB Error: init gdbio module failed\n");
+ return -1;
+ }
+ spin_lock_init(&htable_lock);
+ //printk("GDB: init gdbio module successfully\n");
+
+ return 0;
+}
+
+static void __exit gdbio_module_deinit(void){
+ //printk("GDB: deinit gdbio module\n");
+ spin_lock(&htable_lock);
+ if(gdbio_htable){
+ struct gdbio_state *gs;
+ struct htable_iter iter = htable_iter( gdbio_htable );
+ while( (gs = htable_next( &iter )) ){
+ kmem_free(gs);
+ }
+
+ htable_destroy(gdbio_htable);
+ gdbio_htable = NULL;
+ }
+ spin_unlock(&htable_lock);
+}
+
+
+
+struct file *gdbio_connect(char *gdbcons){//invoked by gdbclient
+ struct gdbio_state *gs = gdbio_htable_lookup(gdbcons);
+ if(gs){
+ printk("GDB Error: %s is busy\n", gdbcons);
+ return NULL;
+ }
+
+ struct file *fd = open_gdb_fifo(gdbcons);
+ if(fd){
+ gs = kmem_alloc(sizeof(struct gdbio_state));
+ strcpy(gs->gdbcons, fd->inode->name);
+ gs->detached = 0;
+
+ gdbio_htable_add(gs);
+
+ //printk("GDB: gdb client connect to gdb server stub via %s\n", (char *)
gs->gdbcons);
+ //gdbclient_write(fd, "+", 1);
+ }
+ return fd;
+}
+
+void gdbio_detach(struct file *filep){//invoked by gdbserver
+ struct gdbio_state *gs = gdbio_htable_lookup(filep->inode->name);
+ /*if(!gs){
+ return;
+ }*/
+ gs->detached = 1;
+
+ //wakeup gdb client (gdb client may block in gdbclient_read)
+ struct gdb_fifo_file *file = (struct gdb_fifo_file *) filep->private_data;
+ struct gdbio_buffer* out_buf = file->out_buffer;
+ strcpy(out_buf->buf, "$#00");
+ atomic_set(&out_buf->avail_data, 4);
+ waitq_wake_nr(&out_buf->poll_wait, 1);
+
+ kfs_close(filep);
+}
+
+static int gdbio_is_detached(struct file *filep){
+ if(!filep){
+ return 1;
+ }
+ struct gdbio_state *gs = gdbio_htable_lookup(filep->inode->name);
+ if(gs && gs->detached){
+ //printk("GDB Error: gdb server stub is not running\n");
+ gdbio_htable_del(gs);
+ kmem_free(gs);
+ rm_gdb_fifo(filep);
+ return 1;
+ }
+ return 0;
+}
+
+static struct gdbio_buffer* alloc_gdbio_buffer( int length ) {
+ struct gdbio_buffer* pbuf = kmem_alloc(sizeof(struct gdbio_buffer));
+
+ if ( ! pbuf ) return NULL;
+
+ if ( ( pbuf->buf = kmem_alloc( length ) ) == NULL ) {
+ kmem_free(pbuf);
+ return NULL;
+ }
+ memset(pbuf->buf, 0, sizeof(pbuf->buf));
+
+ waitq_init(&pbuf->poll_wait);
+
+ atomic_set(&pbuf->avail_data, 0);
+ atomic_set(&pbuf->avail_space, length);
+
+ pbuf->rindex = 0;
+ pbuf->windex = 0;
+ pbuf->capacity = length;
+ return pbuf;
+}
+
+static void free_gdbio_buffer( struct gdbio_buffer* buf ) {
+ kmem_free( buf->buf );
+ kmem_free( buf );
+}
+
+/*static void reset_gdbio_buffer(struct gdbio_buffer* pbuf){
+ memset(pbuf->buf, 0, sizeof(pbuf->buf));
+ pbuf->rindex = 0;
+ pbuf->windex = 0;
+ atomic_set(&pbuf->avail_data, 0);
+ atomic_set(&pbuf->avail_space, pbuf->capacity);
+ waitq_init(&pbuf->poll_wait); //TODO ??
+}*/
+
+#if 0
+static void buf_print(char *buffer, ssize_t len){
+ int i = 0;
+ while(i < len){
+ printk("%c", buffer[i++]);
+ }
+ printk("\n");
+}
+#endif
+
+static ssize_t buf_read(struct gdbio_buffer *pbuf, char *buffer, ssize_t
len){
+ wait_event_interruptible(pbuf->poll_wait, atomic_read(&pbuf->avail_data));
+
+ ssize_t avail_data = atomic_read(&pbuf->avail_data);
+ len = (len > avail_data ? avail_data : len);
+ int i = 0;
+ while(i < len){
+ buffer[i++] = pbuf->buf[pbuf->rindex++];
+ pbuf->rindex %= pbuf->capacity;
+ atomic_dec(&pbuf->avail_data);
+ atomic_inc(&pbuf->avail_space);
+ }
+
+ waitq_wake_nr(&pbuf->poll_wait, 1);
+ return len;
+}
+
+static ssize_t buf_write(struct gdbio_buffer *pbuf, const char *buffer,
ssize_t size){
+ ssize_t avail, len, written_bytes = 0;
+ int i;
+ do{
+ wait_event_interruptible(pbuf->poll_wait,
atomic_read(&pbuf->avail_space));
+
+ i=0;
+ avail = atomic_read(&pbuf->avail_space);
+ len = (size > avail ? avail : size);
+ while(i<len){
+ pbuf->buf[pbuf->windex++] = buffer[i++];
+ pbuf->windex %= pbuf->capacity;
+ atomic_dec(&pbuf->avail_space);
+ atomic_inc(&pbuf->avail_data);
+ }
+ size -= len;
+ written_bytes += len;
+ waitq_wake_nr(&pbuf->poll_wait, 1);
+ }while(size != 0);
+
+ return written_bytes;
+}
+
+/*
+* gdbclient sending RSP packet to gdbserver
+*/
+ssize_t gdbclient_write(struct file *filep, char *buffer, ssize_t size){
+ if(gdbio_is_detached(filep)){
+ return 0;
+ }
+
+ struct gdb_fifo_file *file = filep->private_data;
+ struct gdbio_buffer* in_buf = file->in_buffer;
+ return buf_write(in_buf, buffer, size);
+}
+/*
+* gdbserver receiving RSP packet from gdbclient
+*/
+ssize_t gdbserver_read(struct file *filep, char *buffer, ssize_t len){
+ struct gdb_fifo_file *file = filep->private_data;
+ struct gdbio_buffer* in_buf = file->in_buffer;
+
+ return buf_read(in_buf, buffer, len);
+}
+
+/*
+* gdbserver sending RSP packet to gdbclient
+*/
+ssize_t gdbserver_write(struct file *filep, const char *buffer, ssize_t
size){
+ struct gdb_fifo_file *file = filep->private_data;
+ struct gdbio_buffer* out_buf = file->out_buffer;
+ return buf_write(out_buf, buffer, size);
+}
+
+/*
+* gdbclient receiving RSP packet from gdbserver
+*/
+ssize_t gdbclient_read(struct file *filep, char *buffer, ssize_t len){
+ if(gdbio_is_detached(filep)){
+ return 0;
+ }
+ struct gdb_fifo_file *file = filep->private_data;
+ struct gdbio_buffer* out_buf = file->out_buffer;
+ return buf_read(out_buf, buffer, len);
+}
+
+static int open(struct inode * inodep, struct file * filep){
+ struct gdb_fifo_inode_priv* priv = inodep->i_private;
+ filep->private_data = priv->file_ptr;
+
+ return 0;
+}
+
+static struct kfs_fops gdb_fifo_fops = {
+ .open = open,
+ .write = NULL,
+ .read = NULL,
+ .poll = NULL,
+ .close = NULL,
+};
+
+
+static int create(struct inode *inode, int mode ){
+ struct gdb_fifo_inode_priv* priv = kmem_alloc( sizeof( *priv ) );
+ if ( ! priv ) {
+ return -1;
+ }
+
+ dbg("\n");
+ priv->file_ptr = kmem_alloc(sizeof(struct gdb_fifo_file));
+ if ( ! priv->file_ptr ) return -1;
+
+ priv->file_ptr->in_buffer = alloc_gdbio_buffer( GDBIO_BUFFER_SIZE / 4);
+ if(!priv->file_ptr->in_buffer){
+ kmem_free(priv->file_ptr);
+ kmem_free(priv);
+ return -1;
+ }
+ priv->file_ptr->out_buffer = alloc_gdbio_buffer( GDBIO_BUFFER_SIZE / 4);
+ if(!priv->file_ptr->out_buffer){
+ free_gdbio_buffer(priv->file_ptr->in_buffer);
+ kmem_free(priv->file_ptr);
+ kmem_free(priv);
+ return -1;
+ }
+ inode->i_private = priv;
+ return 0;
+}
+
+static int unlink(struct inode *inode ){
+ struct gdb_fifo_inode_priv* priv = inode->i_private;
+ free_gdbio_buffer( priv->file_ptr->in_buffer);
+ free_gdbio_buffer( priv->file_ptr->out_buffer);
+
+ kmem_free(priv->file_ptr);
+ kmem_free(priv);
+ return 0;
+}
+
+
+static struct inode_operations gdb_fifo_iops = {
+ .create = create,
+ .unlink = unlink,
+};
+
+static void get_gdb_fifo_path(const char *gdb_cons, char *path){
+ sprintf(path, "/dev/%s", gdb_cons);
+}
+
+
+struct file *open_gdb_fifo(const char *gdb_cons){
+ char path[32];
+ get_gdb_fifo_path(gdb_cons, path);
+ struct file *fd = NULL;
+ kfs_open_path(path, 0, O_RDWR, &fd);
+ return fd;
+}
+
+int mk_gdb_fifo(const char *gdb_cons){
+ char path[32];
+ get_gdb_fifo_path(gdb_cons, path);
+
+ struct file *console = NULL;
+ kfs_open_path(path, 0, 0, &console);
+ if(console){
+ kfs_close(console);
+ printk("GDB Error: %s is busy\n", path);
+ return -1;
+ }
+
+ if (!kfs_create(path, &gdb_fifo_iops, &gdb_fifo_fops, 0777, NULL, 0)){
+ printk("GDB Error: creating %s failed\n", path);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void rm_gdb_fifo(struct file *filep){
+ struct inode *inode = filep->inode;
+ inode->i_op->unlink(inode);
+ htable_del(inode->parent->files, inode);
+ atomic_set(&inode->i_count, 0);
+ kfs_destroy(inode);
+ kmem_free(filep);
+}
+
+/*void gdbio_reset(struct file *filep){
+ struct gdb_fifo_file *file = (struct gdb_fifo_file *) filep->private_data;
+ reset_gdbio_buffer(file->in_buffer);
+ reset_gdbio_buffer(file->out_buffer);
+}*/
+
+#ifdef CONFIG_PALACIOS_GDB
+DRIVER_INIT("module", gdbio_module_init);
+DRIVER_EXIT(gdbio_module_deinit);
+#endif
=======================================
--- /arch/x86_64/Kconfig Mon Mar 11 21:41:49 2013 UTC
+++ /arch/x86_64/Kconfig Wed Nov 6 19:48:40 2013 UTC
@@ -118,6 +118,11 @@
depends on NETWORK && LWIP_TCP && LWIP_UDP && LWIP_SOCKET
default "y"
+config PALACIOS_GDB
+ bool
+ depends on PALACIOS
+ default "y"
+
config PALACIOS_PATH
string "Path to pre-built Palacios tree"
depends on PALACIOS
@@ -130,7 +135,7 @@
config GUEST_OS_ISOIMAGE_PATH
string "Path to guest OS image"
depends on PALACIOS
- default "/opt/vmm-tools/isos/puppy.img"
+ default "../guest_os/linux_guest.img"
help
Path to a guest image to link with the example user/hello_world
init task. The hello_world init task will use this image
=======================================
--- /arch/x86_64/kernel/Makefile Mon Jun 3 14:51:05 2013 UTC
+++ /arch/x86_64/kernel/Makefile Wed Nov 6 19:48:40 2013 UTC
@@ -12,6 +12,8 @@
obj-$(CONFIG_PALACIOS) += palacios/
+obj-$(CONFIG_PALACIOS_GDB) += gdb.o
+
obj-$(CONFIG_X86_MCE) += mcheck/
obj-y += pci/
=======================================
--- /arch/x86_64/kernel/interrupts.c Mon Oct 21 23:04:24 2013 UTC
+++ /arch/x86_64/kernel/interrupts.c Wed Nov 6 19:48:40 2013 UTC
@@ -8,6 +8,7 @@
#include <lwk/init.h>
#include <lwk/kallsyms.h>
#include <lwk/kgdb.h>
+#include <lwk/gdb.h>
#include <lwk/task.h>
#include <lwk/sched.h>
#include <lwk/timer.h>
@@ -50,6 +51,18 @@
while (1) {}
}
+/* DEBUG exception is used for debug single stepping */
+void
+do_debug(struct pt_regs *regs, unsigned int vector) {
+#ifdef CONFIG_PALACIOS_GDB
+ /* If exception is from user-space, pass control to the GDB stub */
+ if (user_mode(regs))
+ gdb_exception_enter(vector, 0, regs);
+#else
+ printk("Debug Exception\n");
+#endif
+}
+
void
do_nmi(struct pt_regs *regs, unsigned int vector)
{
@@ -58,9 +71,18 @@
while (1) {}
}
+/* INT3 exception is used for debug breakpoints */
void
do_int3(struct pt_regs *regs, unsigned int vector)
{
+#ifdef CONFIG_PALACIOS_GDB
+ /* If exception is from user-space, pass control to the GDB stub */
+ if (user_mode(regs)) {
+ gdb_exception_enter(vector, 0, regs);
+ return;
+ }
+#endif
+
#ifdef CONFIG_KGDB
kgdb_exception_enter(vector, 0, regs);
#else
@@ -337,6 +359,7 @@
* Register handlers for the standard x86_64 interrupts & exceptions.
*/
set_idtvec_handler( DIVIDE_ERROR_VECTOR,
&do_divide_error );
+ set_idtvec_handler( DEBUG_VECTOR,
&do_debug );
set_idtvec_handler( NMI_VECTOR,
&do_nmi );
set_idtvec_handler( INT3_VECTOR,
&do_int3 );
set_idtvec_handler( OVERFLOW_VECTOR,
&do_overflow );
=======================================
--- /arch/x86_64/kernel/palacios/Makefile Fri Aug 14 22:26:43 2009 UTC
+++ /arch/x86_64/kernel/palacios/Makefile Wed Nov 6 19:48:40 2013 UTC
@@ -1,6 +1,7 @@
# Interface glue between the LWK and Palacios
obj-$(CONFIG_PALACIOS) += palacios.o
obj-$(CONFIG_PALACIOS_SOCKET) += palacios_socket.o
+obj-$(CONFIG_PALACIOS_GDB) += palacios_gdb.o
$(obj)/palacios.o :: EXTRA_CFLAGS = \
-I$(CONFIG_PALACIOS_PATH)/palacios/include \
@@ -8,3 +9,5 @@
$(obj)/palacios_socket.o :: EXTRA_CFLAGS = \
-I$(CONFIG_PALACIOS_PATH)/palacios/include \
+$(obj)/palacios_gdb.o :: EXTRA_CFLAGS = \
+ -I$(CONFIG_PALACIOS_PATH)/palacios/include \
=======================================
--- /include/arch-x86_64/desc.h Mon Nov 10 21:49:18 2008 UTC
+++ /include/arch-x86_64/desc.h Wed Nov 6 19:48:40 2013 UTC
@@ -11,6 +11,7 @@
#include <lwk/string.h>
#include <lwk/smp.h>
+#include <arch/idt_vectors.h>
#include <arch/segment.h>
#include <arch/mmu.h>
@@ -155,11 +156,16 @@
{
BUG_ON((unsigned)nr > 0xFF);
+ /* Debug interrupts must be triggerable from user-space */
+ int dpl = 0;
+ if ((nr == INT3_VECTOR) || (nr == DEBUG_VECTOR))
+ dpl = 3;
+
_set_gate(
&idt_table[nr],
GATE_INTERRUPT,
(unsigned long) func,
- 0,
+ dpl,
ist
);
}
=======================================
--- /include/lwk/task.h Wed Oct 26 22:25:16 2011 UTC
+++ /include/lwk/task.h Wed Nov 6 19:48:40 2013 UTC
@@ -75,7 +75,8 @@
#define TASK_RUNNING (1 << 0)
#define TASK_INTERRUPTIBLE (1 << 1)
#define TASK_UNINTERRUPTIBLE (1 << 2)
-#define TASK_EXITED (1 << 3)
+#define TASK_STOPPED (1 << 3)
+#define TASK_EXITED (1 << 4)
typedef unsigned int taskstate_t;
=======================================
--- /kernel/Makefile Mon Jun 10 00:53:02 2013 UTC
+++ /kernel/Makefile Wed Nov 6 19:48:40 2013 UTC
@@ -38,6 +38,11 @@
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_DEBUG_HW_NOISE) += noise.o
obj-$(CONFIG_NETWORK) += netdev.o
+obj-$(CONFIG_PALACIOS_GDB) += \
+ gdb_target_desc.o \
+ gdb.o \
+ gdbio.o \
+
obj-y += mm/
obj-y += lwk_syscalls/
obj-y += linux_syscalls/
=======================================
--- /kernel/sched.c Wed May 2 19:20:04 2012 UTC
+++ /kernel/sched.c Wed Nov 6 19:48:40 2013 UTC
@@ -145,6 +145,8 @@
set_task_state(task, TASK_RUNNING);
status = 0;
} else {
+ if (task->state == TASK_STOPPED)
+ task->ptrace = (TASK_RUNNING << 1) | 1;
status = -EINVAL;
}
spin_unlock_irqrestore(&runq->lock, irqstate);
=======================================
--- /kernel/waitq.c Wed Jan 20 08:58:47 2010 UTC
+++ /kernel/waitq.c Wed Nov 6 19:48:40 2013 UTC
@@ -133,8 +133,15 @@
waitq_entry_t *entry;
list_for_each_entry(entry, &waitq->waitq, link) {
+ struct task_struct *task = (struct task_struct *) entry->private;
+
+ // Nothing to do if the task has already been woken up
+ if ((task->state == TASK_STOPPED) && ((task->ptrace >> 1) ==
TASK_RUNNING))
+ continue;
+
if( ++count > nr )
break;
+
entry->func(entry, 0, 0, NULL);
}