From 311a5732c8baa7ceb4f23db51dcbb5015e2ef965 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 12:00:15 +0000 Subject: [PATCH] [gdb] Add support for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/core/gdbidt.S | 28 +--- src/arch/i386/core/gdbmach.c | 184 -------------------- src/arch/i386/include/gdbmach.h | 10 +- src/arch/x86/core/gdbmach.c | 251 ++++++++++++++++++++++++++++ src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86_64/core/gdbidt.S | 168 +++++++++++++++++++ src/arch/x86_64/include/gdbmach.h | 49 ++++-- src/core/gdbstub.c | 11 +- 8 files changed, 477 insertions(+), 225 deletions(-) delete mode 100644 src/arch/i386/core/gdbmach.c create mode 100644 src/arch/x86/core/gdbmach.c create mode 100644 src/arch/x86_64/core/gdbidt.S diff --git a/src/arch/i386/core/gdbidt.S b/src/arch/i386/core/gdbidt.S index a1e309d7..666ecce3 100644 --- a/src/arch/i386/core/gdbidt.S +++ b/src/arch/i386/core/gdbidt.S @@ -15,41 +15,29 @@ /* 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 index d92a4ac0..00000000 --- a/src/arch/i386/core/gdbmach.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2008 Stefan Hajnoczi . - * - * 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 -#include -#include -#include -#include -#include -#include - -/** @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] ); - } -} diff --git a/src/arch/i386/include/gdbmach.h b/src/arch/i386/include/gdbmach.h index 416ae341..52cce783 100644 --- a/src/arch/i386/include/gdbmach.h +++ b/src/arch/i386/include/gdbmach.h @@ -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 index 00000000..af6abfed --- /dev/null +++ b/src/arch/x86/core/gdbmach.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/** @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] ); + } +} diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 0d1617d2..9eb4b548 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -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 index 00000000..89280bf8 --- /dev/null +++ b/src/arch/x86_64/core/gdbidt.S @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 diff --git a/src/arch/x86_64/include/gdbmach.h b/src/arch/x86_64/include/gdbmach.h index 6dadbbdd..367405fd 100644 --- a/src/arch/x86_64/include/gdbmach.h +++ b/src/arch/x86_64/include/gdbmach.h @@ -14,16 +14,37 @@ 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 */ diff --git a/src/core/gdbstub.c b/src/core/gdbstub.c index 6ad52d1a..8b57ddf5 100644 --- a/src/core/gdbstub.c +++ b/src/core/gdbstub.c @@ -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 ) {