Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/Makefile
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/Makefile	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/Makefile	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,50 @@
+## -----------------------------------------------------------------------
+##
+##   Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+##   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, Inc., 51 Franklin St, Fifth Floor,
+##   Boston MA 02110-1301, USA; either version 2 of the License, or
+##   (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## GDB remote debugging
+##
+
+topdir = ../..
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/com32.mk
+
+CFLAGS += -fPIE
+
+LIBS	   = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC)
+LNXLIBS	   = ../libutil/libutil_lnx.a
+
+MODULES	  = gdbstub.c32
+TESTFILES =
+
+OBJS = main.o int.o serial.o gdbstub.o
+
+all: $(MODULES) $(TESTFILES)
+
+gdbstub.elf : $(OBJS) $(LIBS) $(C_LIBS)
+	$(LD) $(LDFLAGS) -o $@ $^
+
+tidy dist clean:
+	rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+
+clean: tidy
+	rm -f *.lnx
+
+spotless: clean
+	rm -f *.lss *.c32 *.com
+	rm -f *~ \#*
+
+install:
+	mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR)
+	install -m 644 $(MODULES) $(INSTALLROOT)$(AUXDIR)
+
+-include .*.d
Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/gdbstub.c
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/gdbstub.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/gdbstub.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * 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 of the
+ * License, or 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * GDB stub for remote debugging
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "serial.h"
+
+typedef uint32_t gdbreg_t;
+
+enum {
+    POSIX_EINVAL = 0x1c,	/* used to report bad arguments to GDB */
+    SIZEOF_PAYLOAD = 256,	/* buffer size of GDB payload data */
+    DR7_CLEAR = 0x00000400,	/* disable hardware breakpoints */
+    DR6_CLEAR = 0xffff0ff0,	/* clear breakpoint status */
+};
+
+/* The register snapshot, this must be in sync with interrupt handler and the
+ * GDB protocol. */
+enum {
+    GDBMACH_EAX,
+    GDBMACH_ECX,
+    GDBMACH_EDX,
+    GDBMACH_EBX,
+    GDBMACH_ESP,
+    GDBMACH_EBP,
+    GDBMACH_ESI,
+    GDBMACH_EDI,
+    GDBMACH_EIP,
+    GDBMACH_EFLAGS,
+    GDBMACH_CS,
+    GDBMACH_SS,
+    GDBMACH_DS,
+    GDBMACH_ES,
+    GDBMACH_FS,
+    GDBMACH_GS,
+    GDBMACH_NREGS,
+    GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof(gdbreg_t)
+};
+
+/* Breakpoint types */
+enum {
+    GDBMACH_BPMEM,
+    GDBMACH_BPHW,
+    GDBMACH_WATCH,
+    GDBMACH_RWATCH,
+    GDBMACH_AWATCH,
+};
+
+struct gdbstub {
+    int exit_handler;		/* leave interrupt handler */
+
+    int signo;
+    gdbreg_t *regs;
+
+    void (*parse) (struct gdbstub * stub, char ch);
+    uint8_t cksum1;
+
+    /* Buffer for payload data when parsing a packet.  Once the
+     * packet has been received, this buffer is used to hold
+     * the reply payload. */
+    char buf[SIZEOF_PAYLOAD + 4];	/* $...PAYLOAD...#XX */
+    char *payload;		/* start of payload */
+    int len;			/* length of payload */
+};
+
+/** Hardware breakpoint, fields stored in x86 bit pattern form */
+struct hwbp {
+    int type;			/* type (1=write watchpoint, 3=access watchpoint) */
+    unsigned long addr;		/* linear address */
+    size_t len;			/* length (0=1-byte, 1=2-byte, 3=4-byte) */
+    int enabled;
+};
+
+static struct hwbp hwbps[4];
+static gdbreg_t dr7 = DR7_CLEAR;
+
+static inline void gdbmach_set_pc(gdbreg_t * regs, gdbreg_t pc)
+{
+    regs[GDBMACH_EIP] = pc;
+}
+
+static inline void gdbmach_set_single_step(gdbreg_t * regs, int step)
+{
+    regs[GDBMACH_EFLAGS] &= ~(1 << 8);	/* Trace Flag (TF) */
+    regs[GDBMACH_EFLAGS] |= (step << 8);
+}
+
+static inline void gdbmach_breakpoint(void)
+{
+    __asm__ __volatile__("int $3\n");
+}
+
+static struct hwbp *gdbmach_find_hwbp(int type, unsigned long addr, size_t len)
+{
+    struct hwbp *available = NULL;
+    unsigned int i;
+    for (i = 0; i < sizeof hwbps / sizeof hwbps[0]; i++) {
+	if (hwbps[i].type == type && hwbps[i].addr == addr
+	    && hwbps[i].len == len) {
+	    return &hwbps[i];
+	}
+	if (!hwbps[i].enabled) {
+	    available = &hwbps[i];
+	}
+    }
+    return available;
+}
+
+static void gdbmach_commit_hwbp(struct hwbp *bp)
+{
+    int regnum = bp - hwbps;
+
+    /* Set breakpoint address */
+    switch (regnum) {
+    case 0:
+__asm__ __volatile__("movl %0, %%dr0\n": :"r"(bp->addr));
+	break;
+    case 1:
+__asm__ __volatile__("movl %0, %%dr1\n": :"r"(bp->addr));
+	break;
+    case 2:
+__asm__ __volatile__("movl %0, %%dr2\n": :"r"(bp->addr));
+	break;
+    case 3:
+__asm__ __volatile__("movl %0, %%dr3\n": :"r"(bp->addr));
+	break;
+    }
+
+    /* Set type */
+    dr7 &= ~(0x3 << (16 + 4 * regnum));
+    dr7 |= bp->type << (16 + 4 * regnum);
+
+    /* Set length */
+    dr7 &= ~(0x3 << (18 + 4 * regnum));
+    dr7 |= bp->len << (18 + 4 * regnum);
+
+    /* Set/clear local enable bit */
+    dr7 &= ~(0x3 << 2 * regnum);
+    dr7 |= bp->enabled << 2 * regnum;
+}
+
+int gdbmach_set_breakpoint(int type, unsigned long addr, size_t len, int enable)
+{
+    struct hwbp *bp;
+
+    /* Check and convert breakpoint type to x86 type */
+    switch (type) {
+    case GDBMACH_WATCH:
+	type = 0x1;
+	break;
+    case GDBMACH_AWATCH:
+	type = 0x3;
+	break;
+    default:
+	return 0;		/* unsupported breakpoint type */
+    }
+
+    /* Only lengths 1, 2, and 4 are supported */
+    if (len != 2 && len != 4) {
+	len = 1;
+    }
+    len--;			/* convert to x86 breakpoint length bit pattern */
+
+    /* Set up the breakpoint */
+    bp = gdbmach_find_hwbp(type, addr, len);
+    if (!bp) {
+	return 0;		/* ran out of hardware breakpoints */
+    }
+    bp->type = type;
+    bp->addr = addr;
+    bp->len = len;
+    bp->enabled = enable;
+    gdbmach_commit_hwbp(bp);
+    return 1;
+}
+
+static void gdbmach_disable_hwbps(void)
+{
+    /* Store and clear hardware breakpoints */
+    __asm__ __volatile__("movl %0, %%dr7\n"::"r"(DR7_CLEAR));
+}
+
+static void gdbmach_enable_hwbps(void)
+{
+    /* Clear breakpoint status register */
+    __asm__ __volatile__("movl %0, %%dr6\n"::"r"(DR6_CLEAR));
+
+    /* Restore hardware breakpoints */
+    __asm__ __volatile__("movl %0, %%dr7\n"::"r"(dr7));
+}
+
+/* Packet parser states */
+static void gdbstub_state_new(struct gdbstub *stub, char ch);
+static void gdbstub_state_data(struct gdbstub *stub, char ch);
+static void gdbstub_state_cksum1(struct gdbstub *stub, char ch);
+static void gdbstub_state_cksum2(struct gdbstub *stub, char ch);
+static void gdbstub_state_wait_ack(struct gdbstub *stub, char ch);
+
+static void serial_write(void *buf, size_t len)
+{
+    char *p = buf;
+    while (len-- > 0)
+	serial_putc(*p++);
+}
+
+static uint8_t gdbstub_from_hex_digit(char ch)
+{
+    if (ch >= '0' && ch <= '9')
+	return ch - '0';
+    else if (ch >= 'A' && ch <= 'F')
+	return ch - 'A' + 0xa;
+    else
+	return (ch - 'a' + 0xa) & 0xf;
+}
+
+static uint8_t gdbstub_to_hex_digit(uint8_t b)
+{
+    b &= 0xf;
+    return (b < 0xa ? '0' : 'a' - 0xa) + b;
+}
+
+/*
+ * To make reading/writing device memory atomic, we check for
+ * 2- or 4-byte aligned operations and handle them specially.
+ */
+
+static void gdbstub_from_hex_buf(char *dst, char *src, int lenbytes)
+{
+    if (lenbytes == 2 && ((unsigned long)dst & 0x1) == 0) {
+	uint16_t i = gdbstub_from_hex_digit(src[2]) << 12 |
+	    gdbstub_from_hex_digit(src[3]) << 8 |
+	    gdbstub_from_hex_digit(src[0]) << 4 |
+	    gdbstub_from_hex_digit(src[1]);
+	*(uint16_t *) dst = i;
+    } else if (lenbytes == 4 && ((unsigned long)dst & 0x3) == 0) {
+	uint32_t i = gdbstub_from_hex_digit(src[6]) << 28 |
+	    gdbstub_from_hex_digit(src[7]) << 24 |
+	    gdbstub_from_hex_digit(src[4]) << 20 |
+	    gdbstub_from_hex_digit(src[5]) << 16 |
+	    gdbstub_from_hex_digit(src[2]) << 12 |
+	    gdbstub_from_hex_digit(src[3]) << 8 |
+	    gdbstub_from_hex_digit(src[0]) << 4 |
+	    gdbstub_from_hex_digit(src[1]);
+	*(uint32_t *) dst = i;
+    } else {
+	while (lenbytes-- > 0) {
+	    *dst++ = gdbstub_from_hex_digit(src[0]) << 4 |
+		gdbstub_from_hex_digit(src[1]);
+	    src += 2;
+	}
+    }
+}
+
+static void gdbstub_to_hex_buf(char *dst, char *src, int lenbytes)
+{
+    if (lenbytes == 2 && ((unsigned long)src & 0x1) == 0) {
+	uint16_t i = *(uint16_t *) src;
+	dst[0] = gdbstub_to_hex_digit(i >> 4);
+	dst[1] = gdbstub_to_hex_digit(i);
+	dst[2] = gdbstub_to_hex_digit(i >> 12);
+	dst[3] = gdbstub_to_hex_digit(i >> 8);
+    } else if (lenbytes == 4 && ((unsigned long)src & 0x3) == 0) {
+	uint32_t i = *(uint32_t *) src;
+	dst[0] = gdbstub_to_hex_digit(i >> 4);
+	dst[1] = gdbstub_to_hex_digit(i);
+	dst[2] = gdbstub_to_hex_digit(i >> 12);
+	dst[3] = gdbstub_to_hex_digit(i >> 8);
+	dst[4] = gdbstub_to_hex_digit(i >> 20);
+	dst[5] = gdbstub_to_hex_digit(i >> 16);
+	dst[6] = gdbstub_to_hex_digit(i >> 28);
+	dst[7] = gdbstub_to_hex_digit(i >> 24);
+    } else {
+	while (lenbytes-- > 0) {
+	    *dst++ = gdbstub_to_hex_digit(*src >> 4);
+	    *dst++ = gdbstub_to_hex_digit(*src);
+	    src++;
+	}
+    }
+}
+
+static uint8_t gdbstub_cksum(char *data, int len)
+{
+    uint8_t cksum = 0;
+    while (len-- > 0) {
+	cksum += (uint8_t) * data++;
+    }
+    return cksum;
+}
+
+static void gdbstub_tx_packet(struct gdbstub *stub)
+{
+    uint8_t cksum = gdbstub_cksum(stub->payload, stub->len);
+    stub->buf[0] = '$';
+    stub->buf[stub->len + 1] = '#';
+    stub->buf[stub->len + 2] = gdbstub_to_hex_digit(cksum >> 4);
+    stub->buf[stub->len + 3] = gdbstub_to_hex_digit(cksum);
+    serial_write(stub->buf, stub->len + 4);
+    stub->parse = gdbstub_state_wait_ack;
+}
+
+/* GDB commands */
+static void gdbstub_send_ok(struct gdbstub *stub)
+{
+    stub->payload[0] = 'O';
+    stub->payload[1] = 'K';
+    stub->len = 2;
+    gdbstub_tx_packet(stub);
+}
+
+static void gdbstub_send_num_packet(struct gdbstub *stub, char reply, int num)
+{
+    stub->payload[0] = reply;
+    stub->payload[1] = gdbstub_to_hex_digit((char)num >> 4);
+    stub->payload[2] = gdbstub_to_hex_digit((char)num);
+    stub->len = 3;
+    gdbstub_tx_packet(stub);
+}
+
+/* Format is arg1,arg2,...,argn:data where argn are hex integers and data is not an argument */
+static int gdbstub_get_packet_args(struct gdbstub *stub, unsigned long *args,
+				   int nargs, int *stop_idx)
+{
+    int i;
+    char ch = 0;
+    int argc = 0;
+    unsigned long val = 0;
+    for (i = 1; i < stub->len && argc < nargs; i++) {
+	ch = stub->payload[i];
+	if (ch == ':') {
+	    break;
+	} else if (ch == ',') {
+	    args[argc++] = val;
+	    val = 0;
+	} else {
+	    val = (val << 4) | gdbstub_from_hex_digit(ch);
+	}
+    }
+    if (stop_idx) {
+	*stop_idx = i;
+    }
+    if (argc < nargs) {
+	args[argc++] = val;
+    }
+    return ((i == stub->len || ch == ':') && argc == nargs);
+}
+
+static void gdbstub_send_errno(struct gdbstub *stub, int errno)
+{
+    gdbstub_send_num_packet(stub, 'E', errno);
+}
+
+static void gdbstub_report_signal(struct gdbstub *stub)
+{
+    gdbstub_send_num_packet(stub, 'S', stub->signo);
+}
+
+static void gdbstub_read_regs(struct gdbstub *stub)
+{
+    gdbstub_to_hex_buf(stub->payload, (char *)stub->regs, GDBMACH_SIZEOF_REGS);
+    stub->len = GDBMACH_SIZEOF_REGS * 2;
+    gdbstub_tx_packet(stub);
+}
+
+static void gdbstub_write_regs(struct gdbstub *stub)
+{
+    if (stub->len != 1 + GDBMACH_SIZEOF_REGS * 2) {
+	gdbstub_send_errno(stub, POSIX_EINVAL);
+	return;
+    }
+    gdbstub_from_hex_buf((char *)stub->regs, &stub->payload[1],
+			 GDBMACH_SIZEOF_REGS);
+    gdbstub_send_ok(stub);
+}
+
+static void gdbstub_read_mem(struct gdbstub *stub)
+{
+    unsigned long args[2];
+    if (!gdbstub_get_packet_args
+	(stub, args, sizeof args / sizeof args[0], NULL)) {
+	gdbstub_send_errno(stub, POSIX_EINVAL);
+	return;
+    }
+    args[1] = (args[1] < SIZEOF_PAYLOAD / 2) ? args[1] : SIZEOF_PAYLOAD / 2;
+    gdbstub_to_hex_buf(stub->payload, (char *)args[0], args[1]);
+    stub->len = args[1] * 2;
+    gdbstub_tx_packet(stub);
+}
+
+static void gdbstub_write_mem(struct gdbstub *stub)
+{
+    unsigned long args[2];
+    int colon;
+    if (!gdbstub_get_packet_args
+	(stub, args, sizeof args / sizeof args[0], &colon) || colon >= stub->len
+	|| stub->payload[colon] != ':' || (stub->len - colon - 1) % 2 != 0) {
+	gdbstub_send_errno(stub, POSIX_EINVAL);
+	return;
+    }
+    gdbstub_from_hex_buf((char *)args[0], &stub->payload[colon + 1],
+			 (stub->len - colon - 1) / 2);
+    gdbstub_send_ok(stub);
+}
+
+static void gdbstub_continue(struct gdbstub *stub, int single_step)
+{
+    gdbreg_t pc;
+    if (stub->len > 1
+	&& gdbstub_get_packet_args(stub, (unsigned long *)&pc, 1, NULL)) {
+	gdbmach_set_pc(stub->regs, pc);
+    }
+    gdbmach_set_single_step(stub->regs, single_step);
+    stub->exit_handler = 1;
+    /* Reply will be sent when we hit the next breakpoint or interrupt */
+}
+
+static void gdbstub_breakpoint(struct gdbstub *stub)
+{
+    unsigned long args[3];
+    int enable = stub->payload[0] == 'Z' ? 1 : 0;
+    if (!gdbstub_get_packet_args
+	(stub, args, sizeof args / sizeof args[0], NULL)) {
+	gdbstub_send_errno(stub, POSIX_EINVAL);
+	return;
+    }
+    if (gdbmach_set_breakpoint(args[0], args[1], args[2], enable)) {
+	gdbstub_send_ok(stub);
+    } else {
+	/* Not supported */
+	stub->len = 0;
+	gdbstub_tx_packet(stub);
+    }
+}
+
+static void gdbstub_rx_packet(struct gdbstub *stub)
+{
+    switch (stub->payload[0]) {
+    case '?':
+	gdbstub_report_signal(stub);
+	break;
+    case 'g':
+	gdbstub_read_regs(stub);
+	break;
+    case 'G':
+	gdbstub_write_regs(stub);
+	break;
+    case 'm':
+	gdbstub_read_mem(stub);
+	break;
+    case 'M':
+	gdbstub_write_mem(stub);
+	break;
+    case 'c':			/* Continue */
+    case 'k':			/* Kill */
+    case 's':			/* Step */
+    case 'D':			/* Detach */
+	gdbstub_continue(stub, stub->payload[0] == 's');
+	if (stub->payload[0] == 'D') {
+	    gdbstub_send_ok(stub);
+	}
+	break;
+    case 'Z':			/* Insert breakpoint */
+    case 'z':			/* Remove breakpoint */
+	gdbstub_breakpoint(stub);
+	break;
+    default:
+	stub->len = 0;
+	gdbstub_tx_packet(stub);
+	break;
+    }
+}
+
+/* GDB packet parser */
+static void gdbstub_state_new(struct gdbstub *stub, char ch)
+{
+    if (ch == '$') {
+	stub->len = 0;
+	stub->parse = gdbstub_state_data;
+    }
+}
+
+static void gdbstub_state_data(struct gdbstub *stub, char ch)
+{
+    if (ch == '#') {
+	stub->parse = gdbstub_state_cksum1;
+    } else if (ch == '$') {
+	stub->len = 0;		/* retry new packet */
+    } else {
+	/* If the length exceeds our buffer, let the checksum fail */
+	if (stub->len < SIZEOF_PAYLOAD) {
+	    stub->payload[stub->len++] = ch;
+	}
+    }
+}
+
+static void gdbstub_state_cksum1(struct gdbstub *stub, char ch)
+{
+    stub->cksum1 = gdbstub_from_hex_digit(ch) << 4;
+    stub->parse = gdbstub_state_cksum2;
+}
+
+static void gdbstub_state_cksum2(struct gdbstub *stub, char ch)
+{
+    uint8_t their_cksum;
+    uint8_t our_cksum;
+
+    stub->parse = gdbstub_state_new;
+    their_cksum = stub->cksum1 + gdbstub_from_hex_digit(ch);
+    our_cksum = gdbstub_cksum(stub->payload, stub->len);
+
+    if (their_cksum == our_cksum) {
+	serial_write("+", 1);
+	if (stub->len > 0) {
+	    gdbstub_rx_packet(stub);
+	}
+    } else {
+	serial_write("-", 1);
+    }
+}
+
+static void gdbstub_state_wait_ack(struct gdbstub *stub, char ch)
+{
+    if (ch == '+') {
+	stub->parse = gdbstub_state_new;
+    } else {
+	/* This retransmit is very aggressive but necessary to keep
+	 * in sync with GDB. */
+	gdbstub_tx_packet(stub);
+    }
+}
+
+void gdbstub_handler(int signo, gdbreg_t * regs)
+{
+    struct gdbstub stub;
+
+    gdbmach_disable_hwbps();
+
+    stub.parse = gdbstub_state_new;
+    stub.payload = &stub.buf[1];
+    stub.signo = signo;
+    stub.regs = regs;
+    stub.exit_handler = 0;
+    gdbstub_report_signal(&stub);
+    while (!stub.exit_handler)
+	stub.parse(&stub, serial_getc());
+
+    gdbmach_enable_hwbps();
+}
Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/int.S
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/int.S	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/int.S	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,77 @@
+    .section ".text","ax"
+
+#define SIGTRAP 5
+
+#define SIZEOF_I386_REGS    32
+#define SIZEOF_I386_FLAGS   4
+
+/* When invoked, the stack contains: eflags, cs, eip, signo. */
+#define IH_OFFSET_GDB_REGS ( 0 )
+#define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS )
+#define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 )
+#define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS )
+#define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 )
+#define IH_OFFSET_OLD_EIP ( IH_OFFSET_GDB_END )
+#define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 )
+#define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 )
+#define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 )
+
+/* We also access the stack whilst still storing or restoring
+ * the register snapshot.  Since ESP is in flux, we need
+ * special offsets.
+ */
+#define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 )
+#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 )
+#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 )
+#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 )
+
+    .global int_handler
+int_handler:
+	/* Store CPU state in GDB register snapshot */
+	pushw	$0
+	pushw	%gs
+	pushw	$0
+	pushw	%fs
+	pushw	$0
+	pushw	%es
+	pushw	$0
+	pushw	%ds
+	pushw	$0
+	pushw	%ss
+	pushw	$0
+	pushw	IH_OFFSET_FLUX_OLD_CS + 2(%esp)
+	pushl	IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
+	pushl	IH_OFFSET_FLUX_OLD_EIP(%esp)
+	pushl	%edi
+	pushl	%esi
+	pushl	%ebp
+	leal	IH_OFFSET_FLUX_END(%esp), %edi
+	pushl	%edi /* old ESP */
+	pushl	%ebx
+	pushl	%edx
+	pushl	%ecx
+	pushl	%eax
+
+	/* Call GDB stub exception handler */
+    movl    $SIGTRAP, %eax
+	movl	%esp, %edx
+	call	gdbstub_handler
+
+	/* Restore CPU state from GDB register snapshot */
+	popl	%eax
+	popl	%ecx
+	popl	%edx
+	popl	%ebx
+	addl	$4, %esp /* Changing ESP currently not supported */
+	popl	%ebp
+	popl	%esi
+	popl	%edi
+	popl	IH_OFFSET_FLUX_OLD_EIP(%esp)
+	popl	IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
+	popl	IH_OFFSET_FLUX_OLD_CS(%esp)
+	popl	%ss
+	popl	%ds
+	popl	%es
+	popl	%fs
+	popl	%gs
+	iret
Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/main.c
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/main.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/main.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <console.h>
+#include <com32.h>
+#include <syslinux/loadfile.h>
+#include "serial.h"
+
+#define X86_INT_DB      1
+#define X86_INT_BP      3
+#define COM32_IDT       ((void*)0x100000)
+#define COM32_LOAD_ADDR ((void*)0x101000)
+#define STACK_SIZE      0x1000
+
+extern char _start[], _end[];
+
+struct reloc_info {
+    void *data;
+    size_t len;
+    uint32_t old_esp;
+    uint32_t reloc_base;
+};
+
+static inline void error(const char *msg)
+{
+    fputs(msg, stderr);
+}
+
+static inline uint32_t reloc_ptr(struct reloc_info *ri, void *ptr)
+{
+    return ri->reloc_base + (uint32_t) ((char *)ptr - _start);
+}
+
+static void hijack_interrupt(int intn, uint32_t handler)
+{
+    struct {
+	uint32_t lo;
+	uint32_t hi;
+    } *idt = COM32_IDT;
+
+    idt[intn].lo = (idt[intn].lo & 0xffff0000) | (handler & 0x0000ffff);
+    idt[intn].hi = (idt[intn].hi & 0x0000ffff) | (handler & 0xffff0000);
+}
+
+static void shift_cmdline(struct com32_sys_args *com32)
+{
+    char *p;
+
+    /* Skip leading whitespace */
+    for (p = com32->cs_cmdline; *p != '\0' && *p == ' '; p++) ;
+
+    /* Skip first word */
+    for (; *p != '\0' && *p != ' '; p++) ;
+
+    /* Skip whitespace after first word */
+    for (; *p != '\0' && *p == ' '; p++) ;
+
+    com32->cs_cmdline = p;
+}
+
+static __noreturn reloc_entry(struct reloc_info *ri)
+{
+    extern char int_handler[];
+    size_t stack_frame_size = sizeof(struct com32_sys_args) + 4;
+    struct com32_sys_args *com32;
+    uint32_t module_esp;
+
+    hijack_interrupt(X86_INT_DB, reloc_ptr(ri, int_handler));
+    hijack_interrupt(X86_INT_BP, reloc_ptr(ri, int_handler));
+
+    /* Copy module to load address */
+    memcpy(COM32_LOAD_ADDR, ri->data, ri->len);
+
+    /* Copy stack frame onto module stack */
+    module_esp = (ri->reloc_base - stack_frame_size) & ~15;
+    memcpy((void *)module_esp, (void *)ri->old_esp, stack_frame_size);
+
+    /* Fix up command line */
+    com32 = (struct com32_sys_args *)(module_esp + 4);
+    shift_cmdline(com32);
+
+    /* Set up CPU state to run module and enter GDB */
+    asm volatile ("movl %0, %%esp\n\t"
+		  "pushf\n\t"
+		  "pushl %%cs\n\t"
+		  "pushl %1\n\t"
+		  "jmp *%2\n\t"::"r" (module_esp),
+		  "c"(COM32_LOAD_ADDR), "r"(reloc_ptr(ri, int_handler))
+	);
+    for (;;) ;			/* shut the compiler up */
+}
+
+static inline __noreturn reloc(void *ptr, size_t len)
+{
+    extern uint32_t __entry_esp;
+    size_t total_size = _end - _start;
+    __noreturn(*entry_fn) (struct reloc_info *);
+    struct reloc_info ri;
+    uint32_t esp;
+    char *dest;
+
+    /* Calculate relocation address, preserve current stack */
+    asm volatile ("movl %%esp, %0\n\t":"=m" (esp));
+    dest = (char *)((esp - STACK_SIZE - total_size) & ~3);
+
+    /* Calculate entry point in relocated code */
+    entry_fn = (void *)(dest + ((char *)reloc_entry - _start));
+
+    /* Copy all sections to relocation address */
+    printf("Relocating %d bytes from %p to %p\n", total_size, _start, dest);
+    memcpy(dest, _start, total_size);
+
+    /* Call into relocated code */
+    ri.data = ptr;
+    ri.len = len;
+    ri.old_esp = __entry_esp;
+    ri.reloc_base = (uint32_t) dest;
+    entry_fn(&ri);
+}
+
+int main(int argc, char *argv[])
+{
+    void *data;
+    size_t data_len;
+
+    openconsole(&dev_null_r, &dev_stdcon_w);
+
+    if (argc < 2) {
+	error("Usage: gdbstub.c32 com32_file arguments...\n");
+	return 1;
+    }
+
+    if (loadfile(argv[1], &data, &data_len)) {
+	error("Unable to load file\n");
+	return 1;
+    }
+
+    serial_init();
+
+    /* No more lib calls after this point */
+    reloc(data, data_len);
+}
Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.c
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.c	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,193 @@
+/*
+ * The serial port interface routines implement a simple polled i/o
+ * interface to a standard serial port.  Due to the space restrictions
+ * for the boot blocks, no BIOS support is used (since BIOS requires
+ * expensive real/protected mode switches), instead the rudimentary
+ * BIOS support is duplicated here.
+ *
+ * The base address and speed for the i/o port are passed from the
+ * Makefile in the COMCONSOLE and CONSPEED preprocessor macros.  The
+ * line control parameters are currently hard-coded to 8 bits, no
+ * parity, 1 stop bit (8N1).  This can be changed in init_serial().
+ */
+
+#include <stddef.h>
+#include <sys/io.h>
+#include "serial.h"
+
+/* Set default values if none specified */
+
+#ifndef COMCONSOLE
+#define COMCONSOLE	0x3f8
+#endif
+
+#ifndef COMSPEED
+#define COMSPEED	9600
+#endif
+
+#ifndef COMDATA
+#define COMDATA		8
+#endif
+
+#ifndef COMPARITY
+#define COMPARITY	0
+#endif
+
+#ifndef COMSTOP
+#define COMSTOP		1
+#endif
+
+#undef UART_BASE
+#define UART_BASE ( COMCONSOLE )
+
+#undef UART_BAUD
+#define UART_BAUD ( COMSPEED )
+
+#if ((115200%UART_BAUD) != 0)
+#error Bad ttys0 baud rate
+#endif
+
+#define COMBRD (115200/UART_BAUD)
+
+/* Line Control Settings */
+#define UART_LCS ( ( ( (COMDATA) - 5 )	<< 0 ) | \
+		   ( ( (COMPARITY) )	<< 3 ) | \
+		   ( ( (COMSTOP) - 1 )	<< 2 ) )
+
+/* Data */
+#define UART_RBR 0x00
+#define UART_TBR 0x00
+
+/* Control */
+#define UART_IER 0x01
+#define UART_IIR 0x02
+#define UART_FCR 0x02
+#define UART_LCR 0x03
+#define UART_MCR 0x04
+#define UART_DLL 0x00
+#define UART_DLM 0x01
+
+/* Status */
+#define UART_LSR 0x05
+#define  UART_LSR_TEMPT 0x40	/* Transmitter empty */
+#define  UART_LSR_THRE  0x20	/* Transmit-hold-register empty */
+#define  UART_LSR_BI	0x10	/* Break interrupt indicator */
+#define  UART_LSR_FE	0x08	/* Frame error indicator */
+#define  UART_LSR_PE	0x04	/* Parity error indicator */
+#define  UART_LSR_OE	0x02	/* Overrun error indicator */
+#define  UART_LSR_DR	0x01	/* Receiver data ready */
+
+#define UART_MSR 0x06
+#define UART_SCR 0x07
+
+#define uart_readb(addr) inb(addr)
+#define uart_writeb(val,addr) outb((val),(addr))
+
+/*
+ * void serial_putc(int ch);
+ *	Write character `ch' to port UART_BASE.
+ */
+void serial_putc(int ch)
+{
+    int status;
+    for (;;) {
+	status = uart_readb(UART_BASE + UART_LSR);
+	if (status & UART_LSR_THRE) {
+	    /* TX buffer emtpy */
+	    uart_writeb(ch, UART_BASE + UART_TBR);
+	    break;
+	}
+    }
+}
+
+/*
+ * int serial_getc(void);
+ *	Read a character from port UART_BASE.
+ */
+int serial_getc(void)
+{
+    int status;
+    int ch;
+    do {
+	status = uart_readb(UART_BASE + UART_LSR);
+    } while ((status & 1) == 0);
+    ch = uart_readb(UART_BASE + UART_RBR);	/* fetch (first) character */
+    ch &= 0x7f;			/* remove any parity bits we get */
+    if (ch == 0x7f) {		/* Make DEL... look like BS */
+	ch = 0x08;
+    }
+    return ch;
+}
+
+/*
+ * int serial_init(void);
+ *	Initialize port UART_BASE to speed COMSPEED, line settings 8N1.
+ */
+void serial_init(void)
+{
+    int status;
+    int divisor, lcs;
+
+    divisor = COMBRD;
+    lcs = UART_LCS;
+
+#ifdef COMPRESERVE
+    lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
+    uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+    divisor =
+	(uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE +
+							     UART_DLL);
+    uart_writeb(lcs, UART_BASE + UART_LCR);
+#endif
+
+    /* Set Baud Rate Divisor to COMSPEED, and test to see if the
+     * serial port appears to be present.
+     */
+    uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+    uart_writeb(0xaa, UART_BASE + UART_DLL);
+    if (uart_readb(UART_BASE + UART_DLL) != 0xaa) {
+	goto out;
+    }
+    uart_writeb(0x55, UART_BASE + UART_DLL);
+    if (uart_readb(UART_BASE + UART_DLL) != 0x55) {
+	goto out;
+    }
+    uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
+    if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) {
+	goto out;
+    }
+    uart_writeb(0xaa, UART_BASE + UART_DLM);
+    if (uart_readb(UART_BASE + UART_DLM) != 0xaa) {
+	goto out;
+    }
+    uart_writeb(0x55, UART_BASE + UART_DLM);
+    if (uart_readb(UART_BASE + UART_DLM) != 0x55) {
+	goto out;
+    }
+    uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
+    if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) {
+	goto out;
+    }
+    uart_writeb(lcs, UART_BASE + UART_LCR);
+
+    /* disable interrupts */
+    uart_writeb(0x0, UART_BASE + UART_IER);
+
+    /* disable fifo's */
+    uart_writeb(0x00, UART_BASE + UART_FCR);
+
+    /* Set clear to send, so flow control works... */
+    uart_writeb((1 << 1), UART_BASE + UART_MCR);
+
+    /* Flush the input buffer. */
+    do {
+	/* rx buffer reg
+	 * throw away (unconditionally the first time)
+	 */
+	(void)uart_readb(UART_BASE + UART_RBR);
+	/* line status reg */
+	status = uart_readb(UART_BASE + UART_LSR);
+    } while (status & UART_LSR_DR);
+out:
+    return;
+}
Index: bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.h
===================================================================
--- bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.h	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
+++ bootcd/isolinux/syslinux-6.03/com32/gdbstub/serial.h	(revision e16e8f2a41938d263af54a9fd6b4020d6c0d69fa)
@@ -0,0 +1,14 @@
+#ifndef _GPXE_SERIAL_H
+#define _GPXE_SERIAL_H
+
+/** @file
+ *
+ * Serial driver functions
+ *
+ */
+
+extern void serial_putc(int ch);
+extern int serial_getc(void);
+extern void serial_init(void);
+
+#endif /* _GPXE_SERIAL_H */
