]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
gdb over serial by Lubomir Kundrak and cleaned-up/updated by me (phcoder)
authorLubomir Kundrak <lkundrak@redhat.com>
Mon, 14 Nov 2011 21:58:11 +0000 (22:58 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 14 Nov 2011 21:58:11 +0000 (22:58 +0100)
Also-By: Vladimir Serbinenko <phcoder@gmail.com>
grub-core/Makefile.core.def
grub-core/gdb/cstub.c [new file with mode: 0644]
grub-core/gdb/gdb.c [new file with mode: 0644]
grub-core/gdb/i386/idt.c [new file with mode: 0644]
grub-core/gdb/i386/machdep.S [new file with mode: 0644]
grub-core/gdb/i386/signal.c [new file with mode: 0644]
grub-core/kern/i386/realmode.S
include/grub/gdb.h [new file with mode: 0644]
include/grub/i386/gdb.h [new file with mode: 0644]

index cae79b381f881de5cdfc9696a1171f1c77c308a0..c19381e6233265ae69c67851b2e91e314b264930 100644 (file)
@@ -1741,3 +1741,13 @@ module = {
   name = crc64;
   common = lib/crc64.c;
 };
+
+module = {
+  name = gdb;
+  common = gdb/cstub.c;
+  common = gdb/gdb.c;
+  i386 = gdb/i386/idt.c;
+  i386 = gdb/i386/machdep.S;
+  i386 = gdb/i386/signal.c;
+  enable = i386;
+};  
\ No newline at end of file
diff --git a/grub-core/gdb/cstub.c b/grub-core/gdb/cstub.c
new file mode 100644 (file)
index 0000000..a13c822
--- /dev/null
@@ -0,0 +1,357 @@
+/* cstub.c - machine independent portion of remote GDB stub */
+/*
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#include <grub/misc.h>
+#include <grub/cpu/gdb.h>
+#include <grub/gdb.h>
+#include <grub/serial.h>
+
+static const char hexchars[] = "0123456789abcdef";
+int grub_gdb_regs[GRUB_MACHINE_NR_REGS];
+
+#define GRUB_GDB_COMBUF_SIZE 400       /* At least sizeof(grub_gdb_regs)*2 are needed for
+                                          register packets.  */
+static char grub_gdb_inbuf[GRUB_GDB_COMBUF_SIZE];
+static char grub_gdb_outbuf[GRUB_GDB_COMBUF_SIZE];
+
+struct grub_serial_port *grub_gdb_port;
+
+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>#<checksum>.  */
+static char *
+grub_gdb_getpacket (void)
+{
+  char *buffer = &grub_gdb_inbuf[0];
+  unsigned char checksum;
+  unsigned char xmitcsum;
+  int count;
+  int ch;
+
+  while (1)
+    {
+      /* Wait around for the start character, ignore all other
+         characters.  */
+      while ((ch = grub_serial_port_fetch (grub_gdb_port)) != '$');
+
+    retry:
+      checksum = 0;
+      xmitcsum = -1;
+      count = 0;
+
+      /* Now read until a # or end of buffer is found.  */
+      while (count < GRUB_GDB_COMBUF_SIZE)
+       {
+         do
+           ch = grub_serial_port_fetch (grub_gdb_port);
+         while (ch < 0);
+         if (ch == '$')
+           goto retry;
+         if (ch == '#')
+           break;
+         checksum += ch;
+         buffer[count] = ch;
+         count = count + 1;
+       }
+      buffer[count] = 0;
+      if (ch == '#')
+       {
+         do
+           ch = grub_serial_port_fetch (grub_gdb_port);
+         while (ch < 0);
+         xmitcsum = hex (ch) << 4;
+         do
+           ch = grub_serial_port_fetch (grub_gdb_port);
+         while (ch < 0);
+         xmitcsum += hex (ch);
+
+         if (checksum != xmitcsum)
+           grub_serial_port_put (grub_gdb_port, '-');  /* Failed checksum.  */
+         else
+           {
+             grub_serial_port_put (grub_gdb_port, '+');        /* Successful transfer.  */
+
+             /* If a sequence char is present, reply the sequence ID.  */
+             if (buffer[2] == ':')
+               {
+                 grub_serial_port_put (grub_gdb_port, buffer[0]);
+                 grub_serial_port_put (grub_gdb_port, buffer[1]);
+
+                 return &buffer[3];
+               }
+             return &buffer[0];
+           }
+       }
+    }
+}
+
+/* Send the packet in buffer.  */
+static void
+grub_gdb_putpacket (char *buffer)
+{
+  grub_uint8_t checksum;
+
+  /* $<packet info>#<checksum>.  */
+  do
+    {
+      char *ptr;
+      grub_serial_port_put (grub_gdb_port, '$');
+      checksum = 0;
+
+      for (ptr = buffer; *ptr; ptr++)
+       {
+         grub_serial_port_put (grub_gdb_port, *ptr);
+         checksum += *ptr;
+       }
+
+      grub_serial_port_put (grub_gdb_port, '#');
+      grub_serial_port_put (grub_gdb_port, hexchars[checksum >> 4]);
+      grub_serial_port_put (grub_gdb_port, hexchars[checksum & 0xf]);
+    }
+  while (grub_serial_port_fetch (grub_gdb_port) != '+');
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+   Return a pointer to the last char put in buf (NULL).  */
+static char *
+grub_gdb_mem2hex (char *mem, char *buf, int count)
+{
+  int i;
+  unsigned char ch;
+
+  for (i = 0; i < count; i++)
+    {
+      ch = *mem++;
+      *buf++ = hexchars[ch >> 4];
+      *buf++ = hexchars[ch % 16];
+    }
+  *buf = 0;
+  return (buf);
+}
+
+/* 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.  */
+static char *
+grub_gdb_hex2mem (char *buf, char *mem, int count)
+{
+  int i;
+  unsigned char ch;
+
+  for (i = 0; i < count; i++)
+    {
+      ch = hex (*buf++) << 4;
+      ch = ch + hex (*buf++);
+      *mem++ = ch;
+    }
+  return (mem);
+}
+
+/* Convert hex characters to int and return the number of characters
+   processed.  */
+static int
+grub_gdb_hex2int (char **ptr, int *int_value)
+{
+  int num_chars = 0;
+  int hex_value;
+
+  *int_value = 0;
+
+  while (**ptr)
+    {
+      hex_value = hex (**ptr);
+      if (hex_value >= 0)
+       {
+         *int_value = (*int_value << 4) | hex_value;
+         num_chars++;
+       }
+      else
+       break;
+
+      (*ptr)++;
+    }
+
+  return (num_chars);
+}
+
+/* This function does all command procesing for interfacing to gdb.  */
+void
+grub_gdb_trap (int trap_no)
+{
+  int sig_no;
+  int stepping;
+  int addr;
+  int length;
+  char *ptr;
+  int newPC;
+
+  sig_no = grub_gdb_trap2sig (trap_no);
+
+  ptr = grub_gdb_outbuf;
+
+  /* Reply to host that an exception has occurred.  */
+
+  *ptr++ = 'T';        /* Notify gdb with signo, PC, FP and SP.  */
+
+  *ptr++ = hexchars[sig_no >> 4];
+  *ptr++ = hexchars[sig_no & 0xf];
+
+  /* Stack pointer.  */
+  *ptr++ = hexchars[SP];
+  *ptr++ = ':';
+  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[ESP], ptr, 4);
+  *ptr++ = ';';
+
+  /* Frame pointer.  */
+  *ptr++ = hexchars[FP];
+  *ptr++ = ':';
+  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[EBP], ptr, 4);
+  *ptr++ = ';';
+
+  /* Program counter.  */
+  *ptr++ = hexchars[PC];
+  *ptr++ = ':';
+  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[PC], ptr, 4);
+  *ptr++ = ';';
+
+  *ptr = '\0';
+
+  grub_gdb_putpacket (grub_gdb_outbuf);
+
+  stepping = 0;
+
+  while (1)
+    {
+      grub_gdb_outbuf[0] = 0;
+      ptr = grub_gdb_getpacket ();
+
+      switch (*ptr++)
+       {
+       case '?':
+         grub_gdb_outbuf[0] = 'S';
+         grub_gdb_outbuf[1] = hexchars[sig_no >> 4];
+         grub_gdb_outbuf[2] = hexchars[sig_no % 16];
+         grub_gdb_outbuf[3] = 0;
+         break;
+
+       /* Return values of the CPU registers.  */
+       case 'g':
+         grub_gdb_mem2hex ((char *) grub_gdb_regs, grub_gdb_outbuf,
+                           sizeof (grub_gdb_regs));
+         break;
+
+       /* Set values of the CPU registers -- return OK.  */
+       case 'G':
+         grub_gdb_hex2mem (ptr, (char *) grub_gdb_regs,
+                           sizeof (grub_gdb_regs));
+         grub_strcpy (grub_gdb_outbuf, "OK");
+         break;
+
+       /* Set the value of a single CPU register -- return OK.  */
+       case 'P':
+         {
+           int regno;
+
+           if (grub_gdb_hex2int (&ptr, &regno) && *ptr++ == '=')
+             if (regno >= 0 && regno < GRUB_MACHINE_NR_REGS)
+               {
+                 grub_gdb_hex2mem (ptr, (char *) &grub_gdb_regs[regno], 4);
+                 grub_strcpy (grub_gdb_outbuf, "OK");
+                 break;
+               }
+           /* FIXME: GDB requires setting orig_eax. I don't know what's
+              this register is about. For now just simulate setting any
+              registers.  */
+           grub_strcpy (grub_gdb_outbuf, /*"E01"*/ "OK");
+           break;
+         }
+
+       /* mAA..AA,LLLL: Read LLLL bytes at address AA..AA.  */
+       case 'm':
+         /* Try to read %x,%x.  Set ptr = 0 if successful.  */
+         if (grub_gdb_hex2int (&ptr, &addr))
+           if (*(ptr++) == ',')
+             if (grub_gdb_hex2int (&ptr, &length))
+               {
+                 ptr = 0;
+                 grub_gdb_mem2hex ((char *) addr, grub_gdb_outbuf, length);
+               }
+         if (ptr)
+           grub_strcpy (grub_gdb_outbuf, "E01");
+         break;
+
+       /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA -- return OK.  */
+       case 'M':
+         /* Try to read %x,%x.  Set ptr = 0 if successful.  */
+         if (grub_gdb_hex2int (&ptr, &addr))
+           if (*(ptr++) == ',')
+             if (grub_gdb_hex2int (&ptr, &length))
+               if (*(ptr++) == ':')
+                 {
+                   grub_gdb_hex2mem (ptr, (char *) addr, length);
+                   grub_strcpy (grub_gdb_outbuf, "OK");
+                   ptr = 0;
+                 }
+         if (ptr)
+           {
+             grub_strcpy (grub_gdb_outbuf, "E02");
+           }
+         break;
+
+       /* sAA..AA: Step one instruction from AA..AA(optional).  */
+       case 's':
+         stepping = 1;
+
+       /* cAA..AA: Continue at address AA..AA(optional).  */
+       case 'c':
+         /* try to read optional parameter, pc unchanged if no parm */
+         if (grub_gdb_hex2int (&ptr, &addr))
+           grub_gdb_regs[PC] = addr;
+
+         newPC = grub_gdb_regs[PC];
+
+         /* Clear the trace bit.  */
+         grub_gdb_regs[PS] &= 0xfffffeff;
+
+         /* Set the trace bit if we're stepping.  */
+         if (stepping)
+           grub_gdb_regs[PS] |= 0x100;
+
+         return;
+
+       /* Kill the program.  */
+       case 'k':
+         /* Do nothing.  */
+         return;
+       }
+
+      /* Reply to the request.  */
+      grub_gdb_putpacket (grub_gdb_outbuf);
+    }
+}
+
diff --git a/grub-core/gdb/gdb.c b/grub-core/gdb/gdb.c
new file mode 100644 (file)
index 0000000..5f30109
--- /dev/null
@@ -0,0 +1,90 @@
+/* gdb.c - gdb remote stub module */
+/*
+ *  Copyright (C) 2003  Free Software Foundation, Inc.
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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 3 of the License, 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.
+ *
+ *  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.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/term.h>
+#include <grub/cpu/gdb.h>
+#include <grub/gdb.h>
+#include <grub/serial.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_cmd_gdbstub (struct grub_command *cmd __attribute__ ((unused)),
+                 int argc, char **args)
+{
+  struct grub_serial_port *port;
+  if (argc < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "port required");
+  port = grub_serial_find (args[0]);
+  if (!port)
+    return grub_errno;
+  grub_gdb_port = port;
+  grub_puts_ (N_("Now connect the remote debugger, please."));
+  grub_gdb_breakpoint ();
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_gdbstop (struct grub_command *cmd __attribute__ ((unused)),
+                 int argc __attribute__ ((unused)),
+                 char **args __attribute__ ((unused)))
+{
+  grub_gdb_port = NULL;
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_gdb_break (struct grub_command *cmd __attribute__ ((unused)),
+                   int argc __attribute__ ((unused)),
+                   char **args __attribute__ ((unused)))
+{
+  if (!grub_gdb_port)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "No GDB stub is running");
+  grub_gdb_breakpoint ();
+  return 0;
+}
+
+static grub_command_t cmd, cmd_stop, cmd_break;
+
+GRUB_MOD_INIT (gdb)
+{
+  grub_gdb_idtinit ();
+  cmd = grub_register_command ("gdbstub", grub_cmd_gdbstub,
+                              N_("PORT"), N_("Start GDB stub on given port"));
+  cmd_break = grub_register_command ("gdbstub_break", grub_cmd_gdb_break,
+                                    0, N_("Break into GDB"));
+  cmd_stop = grub_register_command ("gdbstub_stop", grub_cmd_gdbstop,
+                                   0, N_("Stop GDB stub"));
+}
+
+GRUB_MOD_FINI (gdb)
+{
+  grub_unregister_command (cmd);
+  grub_unregister_command (cmd_stop);
+  grub_gdb_idtrestore ();
+}
+
diff --git a/grub-core/gdb/i386/idt.c b/grub-core/gdb/i386/idt.c
new file mode 100644 (file)
index 0000000..abdc2ce
--- /dev/null
@@ -0,0 +1,80 @@
+/* idt.c - routines for constructing IDT fot the GDB stub */
+/*
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#include <grub/machine/memory.h>
+#include <grub/misc.h>
+#include <grub/cpu/gdb.h>
+#include <grub/gdb.h>
+
+static struct grub_cpu_interrupt_gate grub_gdb_idt[GRUB_GDB_LAST_TRAP + 1]
+  __attribute__ ((aligned(16)));
+
+/* Sets up a gate descriptor in the IDT table.  */
+static void
+grub_idt_gate (struct grub_cpu_interrupt_gate *gate, void (*offset) (void),
+              grub_uint16_t selector, grub_uint8_t type, grub_uint8_t dpl)
+{
+  gate->offset_lo = (int) offset & 0xffff;
+  gate->selector = selector & 0xffff;
+  gate->unused = 0;
+  gate->gate = (type & 0x1f) | ((dpl & 0x3) << 5) | 0x80;
+  gate->offset_hi = ((int) offset >> 16) & 0xffff;
+}
+
+static struct grub_cpu_idt_descriptor grub_gdb_orig_idt_desc
+  __attribute__ ((aligned(16)));
+static struct grub_cpu_idt_descriptor grub_gdb_idt_desc
+  __attribute__ ((aligned(16)));
+
+/* Set up interrupt and trap handler descriptors in IDT.  */
+void
+grub_gdb_idtinit (void)
+{
+  int i;
+  grub_uint16_t seg;
+
+  asm volatile ("xorl %%eax, %%eax\n"
+               "mov %%cs, %%ax\n" :"=a" (seg));
+
+  for (i = 0; i <= GRUB_GDB_LAST_TRAP; i++)
+    {
+      grub_idt_gate (&grub_gdb_idt[i],
+                    grub_gdb_trapvec[i], seg,
+                     GRUB_CPU_TRAP_GATE, 0);
+    }
+
+  grub_gdb_idt_desc.base = (grub_addr_t) grub_gdb_idt;
+  grub_gdb_idt_desc.limit = sizeof (grub_gdb_idt) - 1;
+  asm volatile ("sidt %0" : : "m" (grub_gdb_orig_idt_desc));
+  asm volatile ("lidt %0" : : "m" (grub_gdb_idt_desc));
+}
+
+void
+grub_gdb_idtrestore (void)
+{
+  asm volatile ("lidt %0" : : "m" (grub_gdb_orig_idt_desc));
+}
+
+void
+grub_gdb_breakpoint (void)
+{
+  int x = 1, y = 0;
+  (void) (x / y);
+  asm volatile ("int $3");
+}
diff --git a/grub-core/gdb/i386/machdep.S b/grub-core/gdb/i386/machdep.S
new file mode 100644 (file)
index 0000000..01ce0b4
--- /dev/null
@@ -0,0 +1,173 @@
+/* machdep.S - machine dependent assembly routines for the GDB stub */
+/*
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#define ASM    1
+#include <grub/cpu/gdb.h>
+
+#define EC_PRESENT     1
+#define EC_ABSENT      0
+
+#define GRUB_GDB_STACKSIZE     40000
+
+/*
+ * The .data index for the address vector.
+ */
+
+#define        VECTOR          1
+
+       .globl grub_gdb_trap
+       .globl grub_gdb_regs
+
+       .bss
+       .globl grub_gdb_stack
+       .space GRUB_GDB_STACKSIZE
+grub_gdb_stack:
+
+/*
+ * Supplemental macros for register saving/restoration
+ * on exception handler entry/leave.
+ */
+
+.macro save32 reg ndx
+       movl \reg, grub_gdb_regs+(\ndx * 4)
+.endm
+
+.macro save16 reg ndx
+       mov $0, %eax
+       movw \reg, grub_gdb_regs+(\ndx * 4)
+       movw %ax, grub_gdb_regs+(\ndx * 4 + 2)
+       movl grub_gdb_regs+(EAX * 4), %eax
+.endm
+
+.macro load32 ndx reg
+       movl grub_gdb_regs+(\ndx * 4), \reg
+.endm
+
+.macro load16 ndx reg
+       movw grub_gdb_regs+(\ndx * 4), \reg
+.endm
+
+.macro save_context
+       save32 %eax EAX
+       save32 %ecx ECX
+       save32 %edx EDX
+       save32 %ebx EBX
+       save32 %ebp EBP
+       save32 %esi ESI
+       save32 %edi EDI
+
+       popl %ebx
+       save32 %ebx EIP
+       popl %ebx
+       save32 %ebx CS
+       popl %ebx
+       save32 %ebx EFLAGS
+
+       save32 %esp ESP
+
+       save16 %ds DS
+       save16 %es ES
+       save16 %fs FS
+       save16 %gs GS
+       save16 %ss SS
+.endm
+
+.macro load_context
+       load16 SS %ss
+       load32 ESP %esp
+
+       load32 EBP %ebp
+       load32 ESI %esi
+       load32 EDI %edi
+
+       load16 DS %ds
+       load16 ES %es
+       load16 FS %fs
+       load16 GS %gs
+
+       load32 EFLAGS %eax
+       pushl %eax
+       load32 CS %eax
+       pushl %eax
+       load32 EIP %eax
+       pushl %eax
+
+       load32 EBX %ebx
+       load32 EDX %edx
+       load32 ECX %ecx
+       load32 EAX %eax
+.endm
+
+/*
+ * This macro creates handlers for a given range of exception numbers
+ * and adds their addresses to the grub_gdb_trapvec array.
+ */
+
+.macro ent ec beg end=0
+
+       /*
+        * Wrapper body itself.
+        */
+
+       .text
+1:     
+       .if \ec
+               add $4,%esp
+       .endif
+
+       save_context
+       mov     $grub_gdb_stack, %esp
+       mov     $\beg, %eax     /* trap number */
+       call    grub_gdb_trap
+       load_context
+       iret
+
+       /*
+        * Address entry in trapvec array.
+        */
+
+       .data VECTOR
+       .long 1b
+
+       /*
+        * Next... (recursion).
+        */
+
+       .if \end-\beg > 0
+               ent \ec "(\beg+1)" \end
+       .endif
+.endm
+
+/*
+ * Here does the actual construction of the address array and handlers
+ * take place.
+ */
+
+       .data VECTOR
+       .globl grub_gdb_trapvec
+grub_gdb_trapvec:
+       ent EC_ABSENT   0 7
+       ent EC_PRESENT  8
+       ent EC_ABSENT   9
+       ent EC_PRESENT  10 14
+       /*
+        * You may have to split this further or as(1)
+        * will complain about nesting being too deep.
+        */
+       ent EC_ABSENT   15 GRUB_GDB_LAST_TRAP
diff --git a/grub-core/gdb/i386/signal.c b/grub-core/gdb/i386/signal.c
new file mode 100644 (file)
index 0000000..23b7c35
--- /dev/null
@@ -0,0 +1,53 @@
+/* idt.c - routines for constructing IDT fot the GDB stub */
+/*
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#include <grub/machine/memory.h>
+#include <grub/misc.h>
+#include <grub/cpu/gdb.h>
+#include <grub/gdb.h>
+
+/* Converting CPU trap number to UNIX signal number as
+   described in System V ABI i386 Processor Supplement, 3-25.  */
+int
+grub_gdb_trap2sig (int trap_no)
+{
+  const int signals[] = {
+    SIGFPE,                    /* 0:   Divide error fault              */
+    SIGTRAP,                   /* 1:   Single step trap fault          */
+    SIGABRT,                   /* 2:   # Nonmaskable interrupt         */
+    SIGTRAP,                   /* 3:   Breakpoint trap                 */
+    SIGSEGV,                   /* 4:   Overflow trap                   */
+    SIGSEGV,                   /* 5:   Bounds check fault              */
+    SIGILL,                    /* 6:   Invalid opcode fault            */
+    SIGFPE,                    /* 7:   No coprocessor fault            */
+    SIGABRT,                   /* 8:   # Double fault abort            */
+    SIGSEGV,                   /* 9:   Coprocessor overrun abort       */
+    SIGSEGV,                   /* 10:  Invalid TSS fault               */
+    SIGSEGV,                   /* 11:  Segment not present fault       */
+    SIGSEGV,                   /* 12:  Stack exception fault           */
+    SIGSEGV,                   /* 13:  General protection fault abort  */
+    SIGSEGV,                   /* 14:  Page fault                      */
+    SIGABRT,                   /* 15:  (reserved)                      */
+    SIGFPE,                    /* 16:  Coprocessor error fault         */
+    SIGUSR1                    /* other                                */
+  };
+
+  return signals[trap_no < 17 ? trap_no : 17];
+}
+
index 2e16847c8bed19a20ed092fe5d785177c9cda478..ecfdf4f198496268e249d33eedb6cfb9cad41a5a 100644 (file)
@@ -117,6 +117,12 @@ gdt:
 gdtdesc:
        .word   0x27                    /* limit */
        .long   gdt                     /* addr */
+realidt:
+       .word 0
+       .long 0
+protidt:
+       .word 0
+       .long 0
 
 /*
  *  These next two routines, "real_to_prot" and "prot_to_real" are structured
@@ -168,6 +174,9 @@ protcseg:
        /* zero %eax */
        xorl    %eax, %eax
 
+       sidt realidt
+       lidt protidt
+
        /* return on the old (or initialized) stack! */
        ret
 /*
@@ -194,6 +203,9 @@ prot_to_real:
        /* just in case, set GDT */
        lgdt    gdtdesc
 
+       sidt    protidt
+       lidt    realidt
+
        /* save the protected mode stack */
        movl    %esp, %eax
        movl    %eax, protstack
diff --git a/include/grub/gdb.h b/include/grub/gdb.h
new file mode 100644 (file)
index 0000000..59fd456
--- /dev/null
@@ -0,0 +1,39 @@
+/* gdb.h - Various definitions for the remote GDB stub */
+/*
+ *  Copyright (C) 2006  Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#ifndef GRUB_GDB_HEADER
+#define GRUB_GDB_HEADER                1
+
+#define SIGFPE         8
+#define SIGTRAP                5
+#define SIGABRT                6
+#define SIGSEGV                11
+#define SIGILL         4
+#define SIGUSR1                30
+/* We probably don't need other ones.  */
+
+struct grub_serial_port;
+
+extern struct grub_serial_port *grub_gdb_port;
+
+void grub_gdb_breakpoint (void);
+int grub_gdb_trap2sig (int);
+
+#endif /* ! GRUB_GDB_HEADER */
+
diff --git a/include/grub/i386/gdb.h b/include/grub/i386/gdb.h
new file mode 100644 (file)
index 0000000..e37a8c7
--- /dev/null
@@ -0,0 +1,79 @@
+/* i386/gdb.h - i386 specific definitions for the remote GDB stub */
+/*
+ *  Copyright (C) 2006 Lubomir Kundrak
+ *
+ *  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
+ *  (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.
+ *
+ *  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.
+ */
+
+#ifndef GRUB_GDB_CPU_HEADER
+#define GRUB_GDB_CPU_HEADER    1
+
+#define GRUB_GDB_LAST_TRAP     31
+/* You may have to edit the bottom of machdep.S when adjusting
+   GRUB_GDB_LAST_TRAP. */
+#define GRUB_MACHINE_NR_REGS   16
+
+#define        EAX     0
+#define        ECX     1
+#define        EDX     2
+#define        EBX     3
+#define        ESP     4
+#define        EBP     5
+#define        ESI     6
+#define        EDI     7
+#define        EIP     8
+#define        EFLAGS  9
+#define        CS      10
+#define        SS      11
+#define        DS      12
+#define        ES      13
+#define        FS      14
+#define        GS      15
+
+#define PC     EIP
+#define FP     EBP
+#define SP     ESP
+#define PS     EFLAGS
+
+#ifndef ASM
+
+#include <grub/gdb.h>
+
+#define GRUB_CPU_TRAP_GATE     15
+
+struct grub_cpu_interrupt_gate
+{
+  grub_uint16_t offset_lo;
+  grub_uint16_t selector;
+  grub_uint8_t unused;
+  grub_uint8_t gate;
+  grub_uint16_t offset_hi;
+} __attribute__ ((packed));
+
+struct grub_cpu_idt_descriptor
+{
+  grub_uint16_t limit;
+  grub_uint32_t base;
+} __attribute__ ((packed));
+
+extern void (*grub_gdb_trapvec[]) (void);
+void grub_gdb_breakpoint (void);
+void grub_gdb_idtinit (void);
+void grub_gdb_idtrestore (void);
+void grub_gdb_trap (int trap_no);
+
+#endif /* ! ASM */
+#endif /* ! GRUB_GDB_CPU_HEADER */
+