150 lines
4.0 KiB
C
150 lines
4.0 KiB
C
/*
|
|
* Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <ipxe/uaccess.h>
|
|
#include <ipxe/gdbstub.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();
|
|
}
|