2005-04-17 02:20:36 +04:00
/*
* arch / mips / kernel / gdb - stub . c
*
* Originally written by Glenn Engel , Lake Stevens Instrument Division
*
* Contributed by HP Systems
*
* Modified for SPARC by Stu Grossman , Cygnus Support .
*
* Modified for Linux / MIPS ( and MIPS in general ) by Andreas Busse
* Send complaints , suggestions etc . to < andy @ waldorf - gmbh . de >
*
* Copyright ( C ) 1995 Andreas Busse
*
* Copyright ( C ) 2003 MontaVista Software Inc .
* Author : Jun Sun , jsun @ mvista . com or jsun @ junsun . net
*/
/*
* To enable debugger support , two things need to happen . One , a
* call to set_debug_traps ( ) is necessary in order to allow any breakpoints
* or error conditions to be properly intercepted and reported to gdb .
* Two , a breakpoint needs to be generated to begin communication . This
* is most easily accomplished by a call to breakpoint ( ) . Breakpoint ( )
* simulates a breakpoint by executing a BREAK instruction .
*
*
* The following gdb commands are supported :
*
* command function Return value
*
* g return the value of the CPU registers hex data or ENN
* G set the value of the CPU registers OK or ENN
*
* mAA . . AA , LLLL Read LLLL bytes at address AA . . AA hex data or ENN
* MAA . . AA , LLLL : Write LLLL bytes at address AA . AA OK or ENN
*
* c Resume at current address SNN ( signal NN )
* cAA . . AA Continue at address AA . . AA SNN
*
* s Step one instruction SNN
* sAA . . AA Step one instruction from AA . . AA SNN
*
* k kill
*
* ? What was the last sigval ? SNN ( signal NN )
*
* bBB . . BB Set baud rate to BB . . BB OK or BNN , then sets
* baud rate
*
* All commands and responses are sent with a packet which includes a
* checksum . A packet consists of
*
* $ < packet info > # < checksum > .
*
* where
* < packet info > : : < characters representing the command or response >
* < checksum > : : < two hex digits computed as modulo 256 sum of < packetinfo > >
*
* When a packet is received , it is first acknowledged with either ' + ' or ' - ' .
* ' + ' indicates a successful transfer . ' - ' indicates a failed transfer .
*
* Example :
*
* Host : Reply :
* $ m0 , 10 # 2 a + $ 00010203040506070 809101112131415 # 42
*
*
* = = = = = = = = = = = = = =
* MORE EXAMPLES :
* = = = = = = = = = = = = = =
*
* For reference - - the following are the steps that one
* company took ( RidgeRun Inc ) to get remote gdb debugging
* going . In this scenario the host machine was a PC and the
* target platform was a Galileo EVB64120A MIPS evaluation
* board .
*
* Step 1 :
* First download gdb - 5.0 . tar . gz from the internet .
* and then build / install the package .
*
* Example :
* $ tar zxf gdb - 5.0 . tar . gz
* $ cd gdb - 5.0
* $ . / configure - - target = mips - linux - elf
* $ make
* $ install
* $ which mips - linux - elf - gdb
* / usr / local / bin / mips - linux - elf - gdb
*
* Step 2 :
* Configure linux for remote debugging and build it .
*
* Example :
* $ cd ~ / linux
* $ make menuconfig < go to " Kernel Hacking " and turn on remote debugging >
* $ make
*
* Step 3 :
* Download the kernel to the remote target and start
* the kernel running . It will promptly halt and wait
* for the host gdb session to connect . It does this
* since the " Kernel Hacking " option has defined
* CONFIG_KGDB which in turn enables your calls
* to :
* set_debug_traps ( ) ;
* breakpoint ( ) ;
*
* Step 4 :
* Start the gdb session on the host .
*
* Example :
* $ mips - linux - elf - gdb vmlinux
* ( gdb ) set remotebaud 115200
* ( gdb ) target remote / dev / ttyS1
* . . . at this point you are connected to
* the remote target and can use gdb
* in the normal fasion . Setting
* breakpoints , single stepping ,
* printing variables , etc .
*/
# include <linux/config.h>
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/console.h>
# include <linux/init.h>
# include <linux/smp.h>
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/reboot.h>
# include <asm/asm.h>
# include <asm/cacheflush.h>
# include <asm/mipsregs.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/gdb-stub.h>
# include <asm/inst.h>
/*
* external low - level support routines
*/
extern int putDebugChar ( char c ) ; /* write a single character */
extern char getDebugChar ( void ) ; /* read and return a single char */
extern void trap_low ( void ) ;
/*
* breakpoint and test functions
*/
extern void breakpoint ( void ) ;
extern void breakinst ( void ) ;
extern void async_breakpoint ( void ) ;
extern void async_breakinst ( void ) ;
extern void adel ( void ) ;
/*
* local prototypes
*/
static void getpacket ( char * buffer ) ;
static void putpacket ( char * buffer ) ;
static int computeSignal ( int tt ) ;
static int hex ( unsigned char ch ) ;
static int hexToInt ( char * * ptr , int * intValue ) ;
static int hexToLong ( char * * ptr , long * longValue ) ;
static unsigned char * mem2hex ( char * mem , char * buf , int count , int may_fault ) ;
void handle_exception ( struct gdb_regs * regs ) ;
int kgdb_enabled ;
/*
* spin locks for smp case
*/
2005-10-03 16:40:26 +04:00
static DEFINE_SPINLOCK ( kgdb_lock ) ;
static raw_spinlock_t kgdb_cpulock [ NR_CPUS ] = {
2006-02-01 14:05:05 +03:00
[ 0 . . . NR_CPUS - 1 ] = __RAW_SPIN_LOCK_UNLOCKED ,
2005-10-03 16:40:26 +04:00
} ;
2005-04-17 02:20:36 +04:00
/*
* BUFMAX defines the maximum number of characters in inbound / outbound buffers
* at least NUMREGBYTES * 2 are needed for register packets
*/
# define BUFMAX 2048
static char input_buffer [ BUFMAX ] ;
static char output_buffer [ BUFMAX ] ;
static int initialized ; /* !0 means we've been initialized */
static int kgdb_started ;
static const char hexchars [ ] = " 0123456789abcdef " ;
/* Used to prevent crashes in memory access. Note that they'll crash anyway if
we haven ' t set up fault handlers yet . . . */
int kgdb_read_byte ( unsigned char * address , unsigned char * dest ) ;
int kgdb_write_byte ( unsigned char val , unsigned char * dest ) ;
/*
* Convert ch from a hex digit to an int
*/
static int hex ( unsigned 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 void getpacket ( char * buffer )
{
unsigned char checksum ;
unsigned char xmitcsum ;
int i ;
int count ;
unsigned char ch ;
do {
/*
* wait around for the start character ,
* ignore all other characters
*/
while ( ( ch = ( getDebugChar ( ) & 0x7f ) ) ! = ' $ ' ) ;
checksum = 0 ;
xmitcsum = - 1 ;
count = 0 ;
/*
* now , read until a # or end of buffer is found
*/
while ( count < BUFMAX ) {
ch = getDebugChar ( ) ;
if ( ch = = ' # ' )
break ;
checksum = checksum + ch ;
buffer [ count ] = ch ;
count = count + 1 ;
}
if ( count > = BUFMAX )
continue ;
buffer [ count ] = 0 ;
if ( ch = = ' # ' ) {
xmitcsum = hex ( getDebugChar ( ) & 0x7f ) < < 4 ;
xmitcsum | = hex ( getDebugChar ( ) & 0x7f ) ;
if ( checksum ! = xmitcsum )
putDebugChar ( ' - ' ) ; /* failed checksum */
else {
putDebugChar ( ' + ' ) ; /* successful transfer */
/*
* if a sequence char is present ,
* reply the sequence ID
*/
if ( buffer [ 2 ] = = ' : ' ) {
putDebugChar ( buffer [ 0 ] ) ;
putDebugChar ( buffer [ 1 ] ) ;
/*
* remove sequence chars from buffer
*/
count = strlen ( buffer ) ;
for ( i = 3 ; i < = count ; i + + )
buffer [ i - 3 ] = buffer [ i ] ;
}
}
}
}
while ( checksum ! = xmitcsum ) ;
}
/*
* send the packet in buffer .
*/
static void putpacket ( char * buffer )
{
unsigned char checksum ;
int count ;
unsigned char ch ;
/*
* $ < packet info > # < checksum > .
*/
do {
putDebugChar ( ' $ ' ) ;
checksum = 0 ;
count = 0 ;
while ( ( ch = buffer [ count ] ) ! = 0 ) {
if ( ! ( putDebugChar ( ch ) ) )
return ;
checksum + = ch ;
count + = 1 ;
}
putDebugChar ( ' # ' ) ;
putDebugChar ( hexchars [ checksum > > 4 ] ) ;
putDebugChar ( hexchars [ checksum & 0xf ] ) ;
}
while ( ( getDebugChar ( ) & 0x7f ) ! = ' + ' ) ;
}
/*
* Convert the memory pointed to by mem into hex , placing result in buf .
* Return a pointer to the last char put in buf ( null ) , in case of mem fault ,
* return 0.
* may_fault is non - zero if we are reading from arbitrary memory , but is currently
* not used .
*/
static unsigned char * mem2hex ( char * mem , char * buf , int count , int may_fault )
{
unsigned char ch ;
while ( count - - > 0 ) {
if ( kgdb_read_byte ( mem + + , & ch ) ! = 0 )
return 0 ;
* buf + + = hexchars [ ch > > 4 ] ;
* buf + + = hexchars [ ch & 0xf ] ;
}
* 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
* may_fault is non - zero if we are reading from arbitrary memory , but is currently
* not used .
*/
static char * hex2mem ( char * buf , char * mem , int count , int binary , int may_fault )
{
int i ;
unsigned char ch ;
for ( i = 0 ; i < count ; i + + )
{
if ( binary ) {
ch = * buf + + ;
if ( ch = = 0x7d )
ch = 0x20 ^ * buf + + ;
}
else {
ch = hex ( * buf + + ) < < 4 ;
ch | = hex ( * buf + + ) ;
}
if ( kgdb_write_byte ( ch , mem + + ) ! = 0 )
return 0 ;
}
return mem ;
}
/*
* This table contains the mapping between SPARC hardware trap types , and
* signals , which are primarily what GDB understands . It also indicates
* which hardware traps we need to commandeer when initializing the stub .
*/
static struct hard_trap_info {
unsigned char tt ; /* Trap type code for MIPS R3xxx and R4xxx */
unsigned char signo ; /* Signal that we map this trap into */
} hard_trap_info [ ] = {
{ 6 , SIGBUS } , /* instruction bus error */
{ 7 , SIGBUS } , /* data bus error */
{ 9 , SIGTRAP } , /* break */
{ 10 , SIGILL } , /* reserved instruction */
/* { 11, SIGILL }, */ /* CPU unusable */
{ 12 , SIGFPE } , /* overflow */
{ 13 , SIGTRAP } , /* trap */
{ 14 , SIGSEGV } , /* virtual instruction cache coherency */
{ 15 , SIGFPE } , /* floating point exception */
{ 23 , SIGSEGV } , /* watch */
{ 31 , SIGSEGV } , /* virtual data cache coherency */
{ 0 , 0 } /* Must be last */
} ;
/* Save the normal trap handlers for user-mode traps. */
void * saved_vectors [ 32 ] ;
/*
* Set up exception handlers for tracing and breakpoints
*/
void set_debug_traps ( void )
{
struct hard_trap_info * ht ;
unsigned long flags ;
unsigned char c ;
local_irq_save ( flags ) ;
for ( ht = hard_trap_info ; ht - > tt & & ht - > signo ; ht + + )
saved_vectors [ ht - > tt ] = set_except_vector ( ht - > tt , trap_low ) ;
putDebugChar ( ' + ' ) ; /* 'hello world' */
/*
* In case GDB is started before us , ack any packets
* ( presumably " $?#xx " ) sitting there .
*/
while ( ( c = getDebugChar ( ) ) ! = ' $ ' ) ;
while ( ( c = getDebugChar ( ) ) ! = ' # ' ) ;
c = getDebugChar ( ) ; /* eat first csum byte */
c = getDebugChar ( ) ; /* eat second csum byte */
putDebugChar ( ' + ' ) ; /* ack it */
initialized = 1 ;
local_irq_restore ( flags ) ;
}
void restore_debug_traps ( void )
{
struct hard_trap_info * ht ;
unsigned long flags ;
local_irq_save ( flags ) ;
for ( ht = hard_trap_info ; ht - > tt & & ht - > signo ; ht + + )
set_except_vector ( ht - > tt , saved_vectors [ ht - > tt ] ) ;
local_irq_restore ( flags ) ;
}
/*
* Convert the MIPS hardware trap type code to a Unix signal number .
*/
static int computeSignal ( int tt )
{
struct hard_trap_info * ht ;
for ( ht = hard_trap_info ; ht - > tt & & ht - > signo ; ht + + )
if ( ht - > tt = = tt )
return ht - > signo ;
return SIGHUP ; /* default for things we don't know about */
}
/*
* While we find nice hex chars , build an int .
* Return number of chars processed .
*/
static int hexToInt ( char * * ptr , int * intValue )
{
int numChars = 0 ;
int hexValue ;
* intValue = 0 ;
while ( * * ptr ) {
hexValue = hex ( * * ptr ) ;
if ( hexValue < 0 )
break ;
* intValue = ( * intValue < < 4 ) | hexValue ;
numChars + + ;
( * ptr ) + + ;
}
return ( numChars ) ;
}
static int hexToLong ( char * * ptr , long * longValue )
{
int numChars = 0 ;
int hexValue ;
* longValue = 0 ;
while ( * * ptr ) {
hexValue = hex ( * * ptr ) ;
if ( hexValue < 0 )
break ;
* longValue = ( * longValue < < 4 ) | hexValue ;
numChars + + ;
( * ptr ) + + ;
}
return numChars ;
}
#if 0
/*
* Print registers ( on target console )
* Used only to debug the stub . . .
*/
void show_gdbregs ( struct gdb_regs * regs )
{
/*
* Saved main processor registers
*/
printk ( " $0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx \n " ,
regs - > reg0 , regs - > reg1 , regs - > reg2 , regs - > reg3 ,
regs - > reg4 , regs - > reg5 , regs - > reg6 , regs - > reg7 ) ;
printk ( " $8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx \n " ,
regs - > reg8 , regs - > reg9 , regs - > reg10 , regs - > reg11 ,
regs - > reg12 , regs - > reg13 , regs - > reg14 , regs - > reg15 ) ;
printk ( " $16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx \n " ,
regs - > reg16 , regs - > reg17 , regs - > reg18 , regs - > reg19 ,
regs - > reg20 , regs - > reg21 , regs - > reg22 , regs - > reg23 ) ;
printk ( " $24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx \n " ,
regs - > reg24 , regs - > reg25 , regs - > reg26 , regs - > reg27 ,
regs - > reg28 , regs - > reg29 , regs - > reg30 , regs - > reg31 ) ;
/*
* Saved cp0 registers
*/
printk ( " epc : %08lx \n Status: %08lx \n Cause : %08lx \n " ,
regs - > cp0_epc , regs - > cp0_status , regs - > cp0_cause ) ;
}
# endif /* dead code */
/*
* We single - step by setting breakpoints . When an exception
* is handled , we need to restore the instructions hoisted
* when the breakpoints were set .
*
* This is where we save the original instructions .
*/
static struct gdb_bp_save {
unsigned long addr ;
unsigned int val ;
} step_bp [ 2 ] ;
# define BP 0x0000000d /* break opcode */
/*
* Set breakpoint instructions for single stepping .
*/
static void single_step ( struct gdb_regs * regs )
{
union mips_instruction insn ;
unsigned long targ ;
int is_branch , is_cond , i ;
targ = regs - > cp0_epc ;
insn . word = * ( unsigned int * ) targ ;
is_branch = is_cond = 0 ;
switch ( insn . i_format . opcode ) {
/*
* jr and jalr are in r_format format .
*/
case spec_op :
switch ( insn . r_format . func ) {
case jalr_op :
case jr_op :
targ = * ( & regs - > reg0 + insn . r_format . rs ) ;
is_branch = 1 ;
break ;
}
break ;
/*
* This group contains :
* bltz_op , bgez_op , bltzl_op , bgezl_op ,
* bltzal_op , bgezal_op , bltzall_op , bgezall_op .
*/
case bcond_op :
is_branch = is_cond = 1 ;
targ + = 4 + ( insn . i_format . simmediate < < 2 ) ;
break ;
/*
* These are unconditional and in j_format .
*/
case jal_op :
case j_op :
is_branch = 1 ;
targ + = 4 ;
targ > > = 28 ;
targ < < = 28 ;
targ | = ( insn . j_format . target < < 2 ) ;
break ;
/*
* These are conditional .
*/
case beq_op :
case beql_op :
case bne_op :
case bnel_op :
case blez_op :
case blezl_op :
case bgtz_op :
case bgtzl_op :
case cop0_op :
case cop1_op :
case cop2_op :
case cop1x_op :
is_branch = is_cond = 1 ;
targ + = 4 + ( insn . i_format . simmediate < < 2 ) ;
break ;
}
if ( is_branch ) {
i = 0 ;
if ( is_cond & & targ ! = ( regs - > cp0_epc + 8 ) ) {
step_bp [ i ] . addr = regs - > cp0_epc + 8 ;
step_bp [ i + + ] . val = * ( unsigned * ) ( regs - > cp0_epc + 8 ) ;
* ( unsigned * ) ( regs - > cp0_epc + 8 ) = BP ;
}
step_bp [ i ] . addr = targ ;
step_bp [ i ] . val = * ( unsigned * ) targ ;
* ( unsigned * ) targ = BP ;
} else {
step_bp [ 0 ] . addr = regs - > cp0_epc + 4 ;
step_bp [ 0 ] . val = * ( unsigned * ) ( regs - > cp0_epc + 4 ) ;
* ( unsigned * ) ( regs - > cp0_epc + 4 ) = BP ;
}
}
/*
* If asynchronously interrupted by gdb , then we need to set a breakpoint
* at the interrupted instruction so that we wind up stopped with a
* reasonable stack frame .
*/
static struct gdb_bp_save async_bp ;
/*
* Swap the interrupted EPC with our asynchronous breakpoint routine .
* This is safer than stuffing the breakpoint in - place , since no cache
* flushes ( or resulting smp_call_functions ) are required . The
* assumption is that only one CPU will be handling asynchronous bp ' s ,
* and only one can be active at a time .
*/
extern spinlock_t smp_call_lock ;
2004-12-28 10:49:43 +03:00
2005-04-17 02:20:36 +04:00
void set_async_breakpoint ( unsigned long * epc )
{
/* skip breaking into userland */
if ( ( * epc & 0x80000000 ) = = 0 )
return ;
2004-12-28 10:49:43 +03:00
# ifdef CONFIG_SMP
2005-04-17 02:20:36 +04:00
/* avoid deadlock if someone is make IPC */
if ( spin_is_locked ( & smp_call_lock ) )
return ;
2004-12-28 10:49:43 +03:00
# endif
2005-04-17 02:20:36 +04:00
async_bp . addr = * epc ;
* epc = ( unsigned long ) async_breakpoint ;
}
2005-10-03 16:30:57 +04:00
static void kgdb_wait ( void * arg )
2005-04-17 02:20:36 +04:00
{
unsigned flags ;
int cpu = smp_processor_id ( ) ;
local_irq_save ( flags ) ;
2005-10-03 16:40:26 +04:00
__raw_spin_lock ( & kgdb_cpulock [ cpu ] ) ;
__raw_spin_unlock ( & kgdb_cpulock [ cpu ] ) ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
}
/*
* This function does all command processing for interfacing to gdb . It
* returns 1 if you should skip the instruction at the trap address , 0
* otherwise .
*/
void handle_exception ( struct gdb_regs * regs )
{
int trap ; /* Trap type */
int sigval ;
long addr ;
int length ;
char * ptr ;
unsigned long * stack ;
int i ;
int bflag = 0 ;
kgdb_started = 1 ;
/*
* acquire the big kgdb spinlock
*/
if ( ! spin_trylock ( & kgdb_lock ) ) {
2005-09-04 02:56:17 +04:00
/*
* some other CPU has the lock , we should go back to
2005-04-17 02:20:36 +04:00
* receive the gdb_wait IPC
*/
return ;
}
/*
* If we ' re in async_breakpoint ( ) , restore the real EPC from
* the breakpoint .
*/
if ( regs - > cp0_epc = = ( unsigned long ) async_breakinst ) {
regs - > cp0_epc = async_bp . addr ;
async_bp . addr = 0 ;
}
2005-09-04 02:56:17 +04:00
/*
2005-04-17 02:20:36 +04:00
* acquire the CPU spinlocks
*/
for ( i = num_online_cpus ( ) - 1 ; i > = 0 ; i - - )
2005-10-03 16:40:26 +04:00
if ( __raw_spin_trylock ( & kgdb_cpulock [ i ] ) = = 0 )
2005-04-17 02:20:36 +04:00
panic ( " kgdb: couldn't get cpulock %d \n " , i ) ;
/*
* force other cpus to enter kgdb
*/
smp_call_function ( kgdb_wait , NULL , 0 , 0 ) ;
/*
* If we ' re in breakpoint ( ) increment the PC
*/
trap = ( regs - > cp0_cause & 0x7c ) > > 2 ;
if ( trap = = 9 & & regs - > cp0_epc = = ( unsigned long ) breakinst )
regs - > cp0_epc + = 4 ;
/*
* If we were single_stepping , restore the opcodes hoisted
* for the breakpoint [ s ] .
*/
if ( step_bp [ 0 ] . addr ) {
* ( unsigned * ) step_bp [ 0 ] . addr = step_bp [ 0 ] . val ;
step_bp [ 0 ] . addr = 0 ;
if ( step_bp [ 1 ] . addr ) {
* ( unsigned * ) step_bp [ 1 ] . addr = step_bp [ 1 ] . val ;
step_bp [ 1 ] . addr = 0 ;
}
}
stack = ( long * ) regs - > reg29 ; /* stack ptr */
sigval = computeSignal ( trap ) ;
/*
* reply to host that an exception has occurred
*/
ptr = output_buffer ;
/*
* Send trap type ( converted to signal )
*/
* ptr + + = ' T ' ;
* ptr + + = hexchars [ sigval > > 4 ] ;
* ptr + + = hexchars [ sigval & 0xf ] ;
/*
* Send Error PC
*/
* ptr + + = hexchars [ REG_EPC > > 4 ] ;
* ptr + + = hexchars [ REG_EPC & 0xf ] ;
* ptr + + = ' : ' ;
ptr = mem2hex ( ( char * ) & regs - > cp0_epc , ptr , sizeof ( long ) , 0 ) ;
* ptr + + = ' ; ' ;
/*
* Send frame pointer
*/
* ptr + + = hexchars [ REG_FP > > 4 ] ;
* ptr + + = hexchars [ REG_FP & 0xf ] ;
* ptr + + = ' : ' ;
ptr = mem2hex ( ( char * ) & regs - > reg30 , ptr , sizeof ( long ) , 0 ) ;
* ptr + + = ' ; ' ;
/*
* Send stack pointer
*/
* ptr + + = hexchars [ REG_SP > > 4 ] ;
* ptr + + = hexchars [ REG_SP & 0xf ] ;
* ptr + + = ' : ' ;
ptr = mem2hex ( ( char * ) & regs - > reg29 , ptr , sizeof ( long ) , 0 ) ;
* ptr + + = ' ; ' ;
* ptr + + = 0 ;
putpacket ( output_buffer ) ; /* send it off... */
/*
* Wait for input from remote GDB
*/
while ( 1 ) {
output_buffer [ 0 ] = 0 ;
getpacket ( input_buffer ) ;
switch ( input_buffer [ 0 ] )
{
case ' ? ' :
output_buffer [ 0 ] = ' S ' ;
output_buffer [ 1 ] = hexchars [ sigval > > 4 ] ;
output_buffer [ 2 ] = hexchars [ sigval & 0xf ] ;
output_buffer [ 3 ] = 0 ;
break ;
/*
* Detach debugger ; let CPU run
*/
case ' D ' :
putpacket ( output_buffer ) ;
goto finish_kgdb ;
break ;
case ' d ' :
/* toggle debug flag */
break ;
/*
* Return the value of the CPU registers
*/
case ' g ' :
ptr = output_buffer ;
ptr = mem2hex ( ( char * ) & regs - > reg0 , ptr , 32 * sizeof ( long ) , 0 ) ; /* r0...r31 */
ptr = mem2hex ( ( char * ) & regs - > cp0_status , ptr , 6 * sizeof ( long ) , 0 ) ; /* cp0 */
ptr = mem2hex ( ( char * ) & regs - > fpr0 , ptr , 32 * sizeof ( long ) , 0 ) ; /* f0...31 */
ptr = mem2hex ( ( char * ) & regs - > cp1_fsr , ptr , 2 * sizeof ( long ) , 0 ) ; /* cp1 */
ptr = mem2hex ( ( char * ) & regs - > frame_ptr , ptr , 2 * sizeof ( long ) , 0 ) ; /* frp */
ptr = mem2hex ( ( char * ) & regs - > cp0_index , ptr , 16 * sizeof ( long ) , 0 ) ; /* cp0 */
break ;
/*
* set the value of the CPU registers - return OK
*/
case ' G ' :
{
ptr = & input_buffer [ 1 ] ;
hex2mem ( ptr , ( char * ) & regs - > reg0 , 32 * sizeof ( long ) , 0 , 0 ) ;
ptr + = 32 * ( 2 * sizeof ( long ) ) ;
hex2mem ( ptr , ( char * ) & regs - > cp0_status , 6 * sizeof ( long ) , 0 , 0 ) ;
ptr + = 6 * ( 2 * sizeof ( long ) ) ;
hex2mem ( ptr , ( char * ) & regs - > fpr0 , 32 * sizeof ( long ) , 0 , 0 ) ;
ptr + = 32 * ( 2 * sizeof ( long ) ) ;
hex2mem ( ptr , ( char * ) & regs - > cp1_fsr , 2 * sizeof ( long ) , 0 , 0 ) ;
ptr + = 2 * ( 2 * sizeof ( long ) ) ;
hex2mem ( ptr , ( char * ) & regs - > frame_ptr , 2 * sizeof ( long ) , 0 , 0 ) ;
ptr + = 2 * ( 2 * sizeof ( long ) ) ;
hex2mem ( ptr , ( char * ) & regs - > cp0_index , 16 * sizeof ( long ) , 0 , 0 ) ;
strcpy ( output_buffer , " OK " ) ;
}
break ;
/*
* mAA . . AA , LLLL Read LLLL bytes at address AA . . AA
*/
case ' m ' :
ptr = & input_buffer [ 1 ] ;
if ( hexToLong ( & ptr , & addr )
& & * ptr + + = = ' , '
& & hexToInt ( & ptr , & length ) ) {
if ( mem2hex ( ( char * ) addr , output_buffer , length , 1 ) )
break ;
strcpy ( output_buffer , " E03 " ) ;
} else
strcpy ( output_buffer , " E01 " ) ;
break ;
/*
* XAA . . AA , LLLL : Write LLLL escaped binary bytes at address AA . AA
*/
case ' X ' :
bflag = 1 ;
/* fall through */
/*
* MAA . . AA , LLLL : Write LLLL bytes at address AA . AA return OK
*/
case ' M ' :
ptr = & input_buffer [ 1 ] ;
if ( hexToLong ( & ptr , & addr )
& & * ptr + + = = ' , '
& & hexToInt ( & ptr , & length )
& & * ptr + + = = ' : ' ) {
if ( hex2mem ( ptr , ( char * ) addr , length , bflag , 1 ) )
strcpy ( output_buffer , " OK " ) ;
else
strcpy ( output_buffer , " E03 " ) ;
}
else
strcpy ( output_buffer , " E02 " ) ;
break ;
/*
* cAA . . AA Continue at address AA . . AA ( optional )
*/
case ' c ' :
/* try to read optional parameter, pc unchanged if no parm */
ptr = & input_buffer [ 1 ] ;
if ( hexToLong ( & ptr , & addr ) )
regs - > cp0_epc = addr ;
2005-09-04 02:56:17 +04:00
2005-04-17 02:20:36 +04:00
goto exit_kgdb_exception ;
break ;
/*
* kill the program ; let us try to restart the machine
* Reset the whole machine .
*/
case ' k ' :
case ' r ' :
machine_restart ( " kgdb restarts machine " ) ;
break ;
/*
* Step to next instruction
*/
case ' s ' :
/*
* There is no single step insn in the MIPS ISA , so we
* use breakpoints and continue , instead .
*/
single_step ( regs ) ;
goto exit_kgdb_exception ;
/* NOTREACHED */
break ;
/*
* Set baud rate ( bBB )
* FIXME : Needs to be written
*/
case ' b ' :
{
#if 0
int baudrate ;
extern void set_timer_3 ( ) ;
ptr = & input_buffer [ 1 ] ;
if ( ! hexToInt ( & ptr , & baudrate ) )
{
strcpy ( output_buffer , " B01 " ) ;
break ;
}
/* Convert baud rate to uart clock divider */
switch ( baudrate )
{
case 38400 :
baudrate = 16 ;
break ;
case 19200 :
baudrate = 33 ;
break ;
case 9600 :
baudrate = 65 ;
break ;
default :
baudrate = 0 ;
strcpy ( output_buffer , " B02 " ) ;
goto x1 ;
}
if ( baudrate ) {
putpacket ( " OK " ) ; /* Ack before changing speed */
set_timer_3 ( baudrate ) ; /* Set it */
}
# endif
}
break ;
} /* switch */
/*
* reply to the request
*/
putpacket ( output_buffer ) ;
} /* while */
return ;
finish_kgdb :
restore_debug_traps ( ) ;
exit_kgdb_exception :
/* release locks so other CPUs can go */
for ( i = num_online_cpus ( ) - 1 ; i > = 0 ; i - - )
2005-10-03 16:40:26 +04:00
__raw_spin_unlock ( & kgdb_cpulock [ i ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & kgdb_lock ) ;
__flush_cache_all ( ) ;
return ;
}
/*
* This function will generate a breakpoint exception . It is used at the
* beginning of a program to sync up with a debugger and can be used
* otherwise as a quick means to stop program execution and " break " into
* the debugger .
*/
void breakpoint ( void )
{
if ( ! initialized )
return ;
__asm__ __volatile__ (
2005-09-04 02:56:17 +04:00
" .globl breakinst \n \t "
2005-04-17 02:20:36 +04:00
" .set \t noreorder \n \t "
" nop \n "
" breakinst: \t break \n \t "
" nop \n \t "
" .set \t reorder "
) ;
}
/* Nothing but the break; don't pollute any registers */
void async_breakpoint ( void )
{
__asm__ __volatile__ (
2005-09-04 02:56:17 +04:00
" .globl async_breakinst \n \t "
2005-04-17 02:20:36 +04:00
" .set \t noreorder \n \t "
" nop \n "
" async_breakinst: \t break \n \t "
" nop \n \t "
" .set \t reorder "
) ;
}
void adel ( void )
{
__asm__ __volatile__ (
" .globl \t adel \n \t "
" lui \t $8,0x8000 \n \t "
" lw \t $9,1($8) \n \t "
) ;
}
/*
* malloc is needed by gdb client in " call func() " , even a private one
* will make gdb happy
*/
2005-07-14 11:39:46 +04:00
static void * __attribute_used__ malloc ( size_t size )
2005-04-17 02:20:36 +04:00
{
return kmalloc ( size , GFP_ATOMIC ) ;
}
2005-07-14 11:39:46 +04:00
static void __attribute_used__ free ( void * where )
2005-04-17 02:20:36 +04:00
{
kfree ( where ) ;
}
# ifdef CONFIG_GDB_CONSOLE
void gdb_putsn ( const char * str , int l )
{
char outbuf [ 18 ] ;
if ( ! kgdb_started )
return ;
outbuf [ 0 ] = ' O ' ;
while ( l ) {
int i = ( l > 8 ) ? 8 : l ;
mem2hex ( ( char * ) str , & outbuf [ 1 ] , i , 0 ) ;
outbuf [ ( i * 2 ) + 1 ] = 0 ;
putpacket ( outbuf ) ;
str + = i ;
l - = i ;
}
}
static void gdb_console_write ( struct console * con , const char * s , unsigned n )
{
gdb_putsn ( s , n ) ;
}
static struct console gdb_console = {
. name = " gdb " ,
. write = gdb_console_write ,
. flags = CON_PRINTBUFFER ,
. index = - 1
} ;
static int __init register_gdb_console ( void )
{
register_console ( & gdb_console ) ;
return 0 ;
}
console_initcall ( register_gdb_console ) ;
# endif