mirror of https://github.com/ipxe/ipxe.git
[gdb] Add support for x86_64
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/51/head
parent
1afcccd5fd
commit
311a5732c8
|
@ -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 )
|
||||
|
|
|
@ -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] );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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] );
|
||||
}
|
||||
}
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
Loading…
Reference in New Issue