summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/com32/gdbstub
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/com32/gdbstub')
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/Makefile49
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/gdbstub.c569
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/int.S77
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/main.c142
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/serial.c193
-rw-r--r--contrib/syslinux-4.02/com32/gdbstub/serial.h14
6 files changed, 1044 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/gdbstub/Makefile b/contrib/syslinux-4.02/com32/gdbstub/Makefile
new file mode 100644
index 0000000..5513876
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/Makefile
@@ -0,0 +1,49 @@
+## -----------------------------------------------------------------------
+##
+## 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 = ../..
+include ../MCONFIG
+
+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
diff --git a/contrib/syslinux-4.02/com32/gdbstub/gdbstub.c b/contrib/syslinux-4.02/com32/gdbstub/gdbstub.c
new file mode 100644
index 0000000..bd19add
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/gdbstub.c
@@ -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();
+}
diff --git a/contrib/syslinux-4.02/com32/gdbstub/int.S b/contrib/syslinux-4.02/com32/gdbstub/int.S
new file mode 100644
index 0000000..2a1ff9d
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/int.S
@@ -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
diff --git a/contrib/syslinux-4.02/com32/gdbstub/main.c b/contrib/syslinux-4.02/com32/gdbstub/main.c
new file mode 100644
index 0000000..2ff9f28
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/main.c
@@ -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);
+}
diff --git a/contrib/syslinux-4.02/com32/gdbstub/serial.c b/contrib/syslinux-4.02/com32/gdbstub/serial.c
new file mode 100644
index 0000000..8c69b02
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/serial.c
@@ -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;
+}
diff --git a/contrib/syslinux-4.02/com32/gdbstub/serial.h b/contrib/syslinux-4.02/com32/gdbstub/serial.h
new file mode 100644
index 0000000..bb27a62
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/gdbstub/serial.h
@@ -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 */