]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[gdb] Add support for x86_64
authorMichael Brown <mcb30@ipxe.org>
Sun, 20 Mar 2016 12:00:15 +0000 (12:00 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 22 Mar 2016 08:44:32 +0000 (08:44 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/i386/core/gdbidt.S
src/arch/i386/core/gdbmach.c [deleted file]
src/arch/i386/include/gdbmach.h
src/arch/x86/core/gdbmach.c [new file with mode: 0644]
src/arch/x86/include/bits/errfile.h
src/arch/x86_64/core/gdbidt.S [new file with mode: 0644]
src/arch/x86_64/include/gdbmach.h
src/core/gdbstub.c

index a1e309d7c88554880a2c28641676d967ced20901..666ecce3ca5eee7471ae498933c651afd85b7480 100644 (file)
 /* POSIX signal numbers for reporting traps to GDB */
 #define SIGILL 4
 #define SIGTRAP 5
-#define SIGBUS 7
 #define SIGFPE 8
-#define SIGSEGV 11
 #define SIGSTKFLT 16
 
-       .globl  gdbmach_nocode_sigfpe
-gdbmach_nocode_sigfpe:
+       .globl  gdbmach_sigfpe
+gdbmach_sigfpe:
        pushl   $SIGFPE
        jmp     gdbmach_interrupt
 
-       .globl  gdbmach_nocode_sigtrap
-gdbmach_nocode_sigtrap:
+       .globl  gdbmach_sigtrap
+gdbmach_sigtrap:
        pushl   $SIGTRAP
        jmp     gdbmach_interrupt
 
-       .globl  gdbmach_nocode_sigstkflt
-gdbmach_nocode_sigstkflt:
+       .globl  gdbmach_sigstkflt
+gdbmach_sigstkflt:
        pushl   $SIGSTKFLT
        jmp     gdbmach_interrupt
 
-       .globl  gdbmach_nocode_sigill
-gdbmach_nocode_sigill:
+       .globl  gdbmach_sigill
+gdbmach_sigill:
        pushl   $SIGILL
        jmp     gdbmach_interrupt
 
-       .globl  gdbmach_withcode_sigbus
-gdbmach_withcode_sigbus:
-       movl    $SIGBUS, (%esp)
-       jmp     gdbmach_interrupt
-
-       .globl  gdbmach_withcode_sigsegv
-gdbmach_withcode_sigsegv:
-       movl    $SIGSEGV, (%esp)
-       jmp     gdbmach_interrupt
-
 /* 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 )
diff --git a/src/arch/i386/core/gdbmach.c b/src/arch/i386/core/gdbmach.c
deleted file mode 100644 (file)
index d92a4ac..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stddef.h>
-#include <stdio.h>
-#include <assert.h>
-#include <ipxe/uaccess.h>
-#include <ipxe/gdbstub.h>
-#include <librm.h>
-#include <gdbmach.h>
-
-/** @file
- *
- * GDB architecture-specific bits for i386
- *
- */
-
-enum {
-       DR7_CLEAR = 0x00000400,    /* disable hardware breakpoints */
-       DR6_CLEAR = 0xffff0ff0,    /* clear breakpoint status */
-};
-
-/** 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 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 ) {
-       unsigned int regnum = bp - hwbps;
-
-       /* Set breakpoint address */
-       assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) );
-       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 */
-
-       /* Calculate linear address by adding segment base */
-       addr += virt_offset;
-
-       /* 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 ) );
-}
-
-__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
-       gdbmach_disable_hwbps();
-       gdbstub_handler ( signo, regs );
-       gdbmach_enable_hwbps();
-}
-
-static void * gdbmach_interrupt_vectors[] = {
-       gdbmach_nocode_sigfpe,          /* Divide by zero */
-       gdbmach_nocode_sigtrap,         /* Debug trap */
-       NULL,                           /* Non-maskable interrupt */
-       gdbmach_nocode_sigtrap,         /* Breakpoint */
-       gdbmach_nocode_sigstkflt,       /* Overflow */
-       gdbmach_nocode_sigstkflt,       /* Bound range exceeded */
-       gdbmach_nocode_sigill,          /* Invalid opcode */
-       NULL,                           /* Device not available */
-       gdbmach_withcode_sigbus,        /* Double fault */
-       NULL,                           /* Coprocessor segment overrun */
-       gdbmach_withcode_sigsegv,       /* Invalid TSS */
-       gdbmach_withcode_sigsegv,       /* Segment not present */
-       gdbmach_withcode_sigsegv,       /* Stack segment fault */
-       gdbmach_withcode_sigsegv,       /* General protection fault */
-       gdbmach_withcode_sigsegv,       /* Page fault */
-};
-
-void gdbmach_init ( void ) {
-       unsigned int i;
-
-       for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) /
-                           sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) {
-               set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] );
-       }
-}
index 416ae341a82c1a902d44af3b30a80111302e2e81..52cce7833e68da3f0091965e82e6f61b4d942f0c 100644 (file)
@@ -47,12 +47,10 @@ enum {
 };
 
 /* Interrupt vectors */
-extern void gdbmach_nocode_sigfpe ( void );
-extern void gdbmach_nocode_sigtrap ( void );
-extern void gdbmach_nocode_sigstkflt ( void );
-extern void gdbmach_nocode_sigill ( void );
-extern void gdbmach_withcode_sigbus ( void );
-extern void gdbmach_withcode_sigsegv ( void );
+extern void gdbmach_sigfpe ( void );
+extern void gdbmach_sigtrap ( void );
+extern void gdbmach_sigstkflt ( void );
+extern void gdbmach_sigill ( void );
 
 static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
        regs [ GDBMACH_EIP ] = pc;
diff --git a/src/arch/x86/core/gdbmach.c b/src/arch/x86/core/gdbmach.c
new file mode 100644 (file)
index 0000000..af6abfe
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/gdbstub.h>
+#include <librm.h>
+#include <gdbmach.h>
+
+/** @file
+ *
+ * GDB architecture-specific bits for x86
+ *
+ */
+
+/** Number of hardware breakpoints */
+#define NUM_HWBP 4
+
+/** Debug register 7: Global breakpoint enable */
+#define DR7_G( bp ) ( 2 << ( 2 * (bp) ) )
+
+/** Debug register 7: Global exact breakpoint enable */
+#define DR7_GE ( 1 << 9 )
+
+/** Debug register 7: Break on data writes */
+#define DR7_RWLEN_WRITE 0x11110000
+
+/** Debug register 7: Break on data access */
+#define DR7_RWLEN_ACCESS 0x33330000
+
+/** Debug register 7: One-byte length */
+#define DR7_RWLEN_1 0x00000000
+
+/** Debug register 7: Two-byte length */
+#define DR7_RWLEN_2 0x44440000
+
+/** Debug register 7: Four-byte length */
+#define DR7_RWLEN_4 0xcccc0000
+
+/** Debug register 7: Eight-byte length */
+#define DR7_RWLEN_8 0x88880000
+
+/** Debug register 7: Breakpoint R/W and length mask */
+#define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) )
+
+/** Hardware breakpoint addresses (debug registers 0-3) */
+static unsigned long dr[NUM_HWBP];
+
+/** Active value of debug register 7 */
+static unsigned long dr7 = DR7_GE;
+
+/**
+ * Update debug registers
+ *
+ */
+static void gdbmach_update ( void ) {
+
+       /* Set debug registers */
+       __asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) );
+       __asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) );
+       __asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) );
+       __asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) );
+       __asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) );
+}
+
+/**
+ * Find reusable or available hardware breakpoint
+ *
+ * @v addr             Linear address
+ * @v rwlen            Control bits
+ * @ret bp             Hardware breakpoint, or negative error
+ */
+static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) {
+       unsigned int i;
+       int bp = -ENOENT;
+
+       /* Look for a reusable or available breakpoint */
+       for ( i = 0 ; i < NUM_HWBP ; i++ ) {
+
+               /* If breakpoint is not enabled, then it is available */
+               if ( ! ( dr7 & DR7_G ( i ) ) ) {
+                       bp = i;
+                       continue;
+               }
+
+               /* If breakpoint is enabled and has the same address
+                * and control bits, then reuse it.
+                */
+               if ( ( dr[i] == addr ) &&
+                    ( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) {
+                       bp = i;
+                       break;
+               }
+       }
+
+       return bp;
+}
+
+/**
+ * Set hardware breakpoint
+ *
+ * @v type             GDB breakpoint type
+ * @v addr             Virtual address
+ * @v len              Length
+ * @v enable           Enable (not disable) breakpoint
+ * @ret rc             Return status code
+ */
+int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
+                            int enable ) {
+       unsigned int rwlen;
+       unsigned long mask;
+       int bp;
+
+       /* Parse breakpoint type */
+       switch ( type ) {
+       case GDBMACH_WATCH:
+               rwlen = DR7_RWLEN_WRITE;
+               break;
+       case GDBMACH_AWATCH:
+               rwlen = DR7_RWLEN_ACCESS;
+               break;
+       default:
+               return -ENOTSUP;
+       }
+
+       /* Parse breakpoint length */
+       switch ( len ) {
+       case 1:
+               rwlen |= DR7_RWLEN_1;
+               break;
+       case 2:
+               rwlen |= DR7_RWLEN_2;
+               break;
+       case 4:
+               rwlen |= DR7_RWLEN_4;
+               break;
+       case 8:
+               rwlen |= DR7_RWLEN_8;
+               break;
+       default:
+               return -ENOTSUP;
+       }
+
+       /* Convert to linear address */
+       if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) )
+               addr = virt_to_phys ( ( void * ) addr );
+
+       /* Find reusable or available hardware breakpoint */
+       bp = gdbmach_find ( addr, rwlen );
+       if ( bp < 0 )
+               return ( enable ? -ENOBUFS : 0 );
+
+       /* Configure this breakpoint */
+       DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n",
+              bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) );
+       dr[bp] = addr;
+       mask = DR7_RWLEN_MASK ( bp );
+       dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) );
+       mask = DR7_G ( bp );
+       dr7 &= ~mask;
+       if ( enable )
+               dr7 |= mask;
+
+       /* Update debug registers */
+       gdbmach_update();
+
+       return 0;
+}
+
+/**
+ * Handle exception
+ *
+ * @v signo            GDB signal number
+ * @v regs             Register dump
+ */
+__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
+       unsigned long dr7_disabled = DR7_GE;
+       unsigned long dr6_clear = 0;
+
+       /* Temporarily disable breakpoints */
+       __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) );
+
+       /* Handle exception */
+       DBGC ( &dr[0], "GDB signal %d\n", signo );
+       DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
+       gdbstub_handler ( signo, regs );
+       DBGC ( &dr[0], "GDB signal %d returning\n", signo );
+       DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
+
+       /* Clear breakpoint status register */
+       __asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) );
+
+       /* Re-enable breakpoints */
+       __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) );
+}
+
+/**
+ * CPU exception vectors
+ *
+ * Note that we cannot intercept anything from INT8 (double fault)
+ * upwards, since these overlap by default with IRQ0-7.
+ */
+static void * gdbmach_vectors[] = {
+       gdbmach_sigfpe,         /* Divide by zero */
+       gdbmach_sigtrap,        /* Debug trap */
+       NULL,                   /* Non-maskable interrupt */
+       gdbmach_sigtrap,        /* Breakpoint */
+       gdbmach_sigstkflt,      /* Overflow */
+       gdbmach_sigstkflt,      /* Bound range exceeded */
+       gdbmach_sigill,         /* Invalid opcode */
+};
+
+/**
+ * Initialise GDB
+ */
+void gdbmach_init ( void ) {
+       unsigned int i;
+
+       /* Hook CPU exception vectors */
+       for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) /
+                           sizeof ( gdbmach_vectors[0] ) ) ; i++ ) {
+               if ( gdbmach_vectors[i] )
+                       set_interrupt_vector ( i, gdbmach_vectors[i] );
+       }
+}
index 0d1617d20829a49af8c5f412e59bc9f636742bc6..9eb4b54881e6b94e3883ea6a95b85192ddd48409 100644 (file)
@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_apm            ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 )
 #define ERRFILE_vesafb         ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 )
 #define ERRFILE_int13con       ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 )
+#define ERRFILE_gdbmach                ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 )
 
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_bzimage               ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
diff --git a/src/arch/x86_64/core/gdbidt.S b/src/arch/x86_64/core/gdbidt.S
new file mode 100644 (file)
index 0000000..89280bf
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * GDB exception handlers
+ *
+ */
+
+/* Size of a register */
+#define SIZEOF_REG 8
+
+/* POSIX signal numbers for reporting traps to GDB */
+#define SIGILL 4
+#define SIGTRAP 5
+#define SIGFPE 8
+#define SIGSTKFLT 16
+
+       .section ".text.gdbmach_interrupt", "ax", @progbits
+       .code64
+
+       .struct 0
+/* Register dump created for GDB stub */
+regs:
+regs_rax:      .space  SIZEOF_REG
+regs_rbx:      .space  SIZEOF_REG
+regs_rcx:      .space  SIZEOF_REG
+regs_rdx:      .space  SIZEOF_REG
+regs_rsi:      .space  SIZEOF_REG
+regs_rdi:      .space  SIZEOF_REG
+regs_rbp:      .space  SIZEOF_REG
+regs_rsp:      .space  SIZEOF_REG
+regs_r8:       .space  SIZEOF_REG
+regs_r9:       .space  SIZEOF_REG
+regs_r10:      .space  SIZEOF_REG
+regs_r11:      .space  SIZEOF_REG
+regs_r12:      .space  SIZEOF_REG
+regs_r13:      .space  SIZEOF_REG
+regs_r14:      .space  SIZEOF_REG
+regs_r15:      .space  SIZEOF_REG
+regs_rip:      .space  SIZEOF_REG
+regs_rflags:   .space  SIZEOF_REG
+regs_cs:       .space  SIZEOF_REG
+regs_ss:       .space  SIZEOF_REG
+regs_ds:       .space  SIZEOF_REG
+regs_es:       .space  SIZEOF_REG
+regs_fs:       .space  SIZEOF_REG
+regs_gs:       .space  SIZEOF_REG
+regs_end:
+/* GDB signal code */
+gdb:
+gdb_code:      .space  SIZEOF_REG
+gdb_end:
+/* Long-mode exception frame */
+frame:
+frame_rip:     .space  SIZEOF_REG
+frame_cs:      .space  SIZEOF_REG
+frame_rflags:  .space  SIZEOF_REG
+frame_rsp:     .space  SIZEOF_REG
+frame_ss:      .space  SIZEOF_REG
+frame_end:
+       .previous
+
+       .globl  gdbmach_sigfpe
+gdbmach_sigfpe:
+       push    $SIGFPE
+       jmp     gdbmach_interrupt
+
+       .globl  gdbmach_sigtrap
+gdbmach_sigtrap:
+       push    $SIGTRAP
+       jmp     gdbmach_interrupt
+
+       .globl  gdbmach_sigstkflt
+gdbmach_sigstkflt:
+       push    $SIGSTKFLT
+       jmp     gdbmach_interrupt
+
+       .globl  gdbmach_sigill
+gdbmach_sigill:
+       push    $SIGILL
+       jmp     gdbmach_interrupt
+
+gdbmach_interrupt:
+
+       /* Create register dump */
+       pushq   %gs
+       pushq   %fs
+       pushq   $0              /* %es unused in long mode */
+       pushq   $0              /* %ds unused in long mode */
+       pushq   ( frame_ss      - regs_ss       - SIZEOF_REG )(%rsp)
+       pushq   ( frame_cs      - regs_cs       - SIZEOF_REG )(%rsp)
+       pushq   ( frame_rflags  - regs_rflags   - SIZEOF_REG )(%rsp)
+       pushq   ( frame_rip     - regs_rip      - SIZEOF_REG )(%rsp)
+       pushq   %r15
+       pushq   %r14
+       pushq   %r13
+       pushq   %r12
+       pushq   %r11
+       pushq   %r10
+       pushq   %r9
+       pushq   %r8
+       pushq   ( frame_rsp     - regs_rsp      - SIZEOF_REG )(%rsp)
+       pushq   %rbp
+       pushq   %rdi
+       pushq   %rsi
+       pushq   %rdx
+       pushq   %rcx
+       pushq   %rbx
+       pushq   %rax
+
+       /* Call GDB stub exception handler */
+       movq    gdb_code(%rsp), %rdi
+       movq    %rsp, %rsi
+       call    gdbmach_handler
+
+       /* Restore from register dump */
+       popq    %rax
+       popq    %rbx
+       popq    %rcx
+       popq    %rdx
+       popq    %rsi
+       popq    %rdi
+       popq    %rbp
+       popq    ( frame_rsp     - regs_rsp      - SIZEOF_REG )(%rsp)
+       popq    %r8
+       popq    %r9
+       popq    %r10
+       popq    %r11
+       popq    %r12
+       popq    %r13
+       popq    %r14
+       popq    %r15
+       popq    ( frame_rip     - regs_rip      - SIZEOF_REG )(%rsp)
+       popq    ( frame_rflags  - regs_rflags   - SIZEOF_REG )(%rsp)
+       popq    ( frame_cs      - regs_cs       - SIZEOF_REG )(%rsp)
+       popq    ( frame_ss      - regs_ss       - SIZEOF_REG )(%rsp)
+       addq    $( regs_fs - regs_ds ), %rsp    /* skip %ds, %es */
+       popq    %fs
+       popq    %gs
+
+       /* Skip code */
+       addq    $( gdb_end - gdb_code ), %rsp   /* skip code */
+
+       /* Return */
+       iretq
index 6dadbbdd3252c8e8dc8adb76596a2294879e2e2b..367405fd6db22074f320d67175ddfcdacdc76a81 100644 (file)
 
 typedef unsigned long gdbreg_t;
 
-/* The register snapshot, this must be in sync with interrupt handler and the
- * GDB protocol. */
+/* Register snapshot */
 enum {
-       // STUB: don't expect this to work!
-       GDBMACH_EIP,
-       GDBMACH_EFLAGS,
+       GDBMACH_RAX,
+       GDBMACH_RBX,
+       GDBMACH_RCX,
+       GDBMACH_RDX,
+       GDBMACH_RSI,
+       GDBMACH_RDI,
+       GDBMACH_RBP,
+       GDBMACH_RSP,
+       GDBMACH_R8,
+       GDBMACH_R9,
+       GDBMACH_R10,
+       GDBMACH_R11,
+       GDBMACH_R12,
+       GDBMACH_R13,
+       GDBMACH_R14,
+       GDBMACH_R15,
+       GDBMACH_RIP,
+       GDBMACH_RFLAGS,
+       GDBMACH_CS,
+       GDBMACH_SS,
+       GDBMACH_DS,
+       GDBMACH_ES,
+       GDBMACH_FS,
+       GDBMACH_GS,
        GDBMACH_NREGS,
-       GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t )
 };
 
+#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
+
 /* Breakpoint types */
 enum {
        GDBMACH_BPMEM,
@@ -33,21 +54,27 @@ enum {
        GDBMACH_AWATCH,
 };
 
+/* Exception vectors */
+extern void gdbmach_sigfpe ( void );
+extern void gdbmach_sigtrap ( void );
+extern void gdbmach_sigstkflt ( void );
+extern void gdbmach_sigill ( void );
+
 static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
-       regs [ GDBMACH_EIP ] = pc;
+       regs[GDBMACH_RIP] = 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 );
+       regs[GDBMACH_RFLAGS] &= ~( 1 << 8 ); /* Trace Flag (TF) */
+       regs[GDBMACH_RFLAGS] |= ( step << 8 );
 }
 
 static inline void gdbmach_breakpoint ( void ) {
        __asm__ __volatile__ ( "int $3\n" );
 }
 
-extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable );
-
+extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
+                                   int enable );
 extern void gdbmach_init ( void );
 
 #endif /* GDBMACH_H */
index 6ad52d1a65d97aa484b6ba3faaa27ee26dba20d4..8b57ddf5645091a5af3b83e739d095923e72f72e 100644 (file)
@@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 enum {
        POSIX_EINVAL = 0x1c,  /* used to report bad arguments to GDB */
-       SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */
+       SIZEOF_PAYLOAD = 512, /* buffer size of GDB payload data */
 };
 
 struct gdbstub {
@@ -255,17 +255,20 @@ static void gdbstub_continue ( struct gdbstub *stub, int single_step ) {
 static void gdbstub_breakpoint ( struct gdbstub *stub ) {
        unsigned long args [ 3 ];
        int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0;
+       int rc;
+
        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 {
+       if ( ( rc = gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ],
+                                            args [ 2 ], enable ) ) != 0 ) {
                /* Not supported */
                stub->len = 0;
                gdbstub_tx_packet ( stub );
+               return;
        }
+       gdbstub_send_ok ( stub );
 }
 
 static void gdbstub_rx_packet ( struct gdbstub *stub ) {