2010-08-03 13:45:08 +04:00
/*
* Microblaze KGDB support
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/kgdb.h>
# include <linux/kdebug.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <asm/cacheflush.h>
# include <asm/asm-offsets.h>
2014-12-18 17:32:57 +03:00
# include <asm/kgdb.h>
2010-08-03 13:45:08 +04:00
# include <asm/pvr.h>
# define GDB_REG 0
# define GDB_PC 32
# define GDB_MSR 33
# define GDB_EAR 34
# define GDB_ESR 35
# define GDB_FSR 36
# define GDB_BTR 37
# define GDB_PVR 38
# define GDB_REDR 50
# define GDB_RPID 51
# define GDB_RZPR 52
# define GDB_RTLBX 53
# define GDB_RTLBSX 54 /* mfs can't read it */
# define GDB_RTLBLO 55
# define GDB_RTLBHI 56
/* keep pvr separately because it is unchangeble */
2015-06-05 11:35:31 +03:00
static struct pvr_s pvr ;
2010-08-03 13:45:08 +04:00
void pt_regs_to_gdb_regs ( unsigned long * gdb_regs , struct pt_regs * regs )
{
2014-12-18 17:56:17 +03:00
unsigned int i ;
2010-08-03 13:45:08 +04:00
unsigned long * pt_regb = ( unsigned long * ) regs ;
int temp ;
2014-12-18 17:56:17 +03:00
2010-08-03 13:45:08 +04:00
/* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
for ( i = 0 ; i < ( sizeof ( struct pt_regs ) / 4 ) - 1 ; i + + )
gdb_regs [ i ] = pt_regb [ i ] ;
/* Branch target register can't be changed */
__asm__ __volatile__ ( " mfs %0, rbtr; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_BTR ] = temp ;
/* pvr part - we have 11 pvr regs */
for ( i = 0 ; i < sizeof ( struct pvr_s ) / 4 ; i + + )
gdb_regs [ GDB_PVR + i ] = pvr . pvr [ i ] ;
/* read special registers - can't be changed */
__asm__ __volatile__ ( " mfs %0, redr; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_REDR ] = temp ;
__asm__ __volatile__ ( " mfs %0, rpid; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_RPID ] = temp ;
__asm__ __volatile__ ( " mfs %0, rzpr; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_RZPR ] = temp ;
__asm__ __volatile__ ( " mfs %0, rtlbx; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_RTLBX ] = temp ;
__asm__ __volatile__ ( " mfs %0, rtlblo; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_RTLBLO ] = temp ;
__asm__ __volatile__ ( " mfs %0, rtlbhi; " : " =r " ( temp ) : ) ;
gdb_regs [ GDB_RTLBHI ] = temp ;
}
void gdb_regs_to_pt_regs ( unsigned long * gdb_regs , struct pt_regs * regs )
{
2014-12-18 17:56:17 +03:00
unsigned int i ;
2010-08-03 13:45:08 +04:00
unsigned long * pt_regb = ( unsigned long * ) regs ;
/* pt_regs and gdb_regs have the same 37 values.
* The rest of gdb_regs are unused and can ' t be changed .
* r0 register value can ' t be changed too . */
for ( i = 1 ; i < ( sizeof ( struct pt_regs ) / 4 ) - 1 ; i + + )
pt_regb [ i ] = gdb_regs [ i ] ;
}
2014-12-18 17:32:57 +03:00
asmlinkage void microblaze_kgdb_break ( struct pt_regs * regs )
2010-08-03 13:45:08 +04:00
{
if ( kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ! = 0 )
2010-08-10 10:19:03 +04:00
return ;
2010-08-03 13:45:08 +04:00
/* Jump over the first arch_kgdb_breakpoint which is barrier to
* get kgdb work . The same solution is used for powerpc */
if ( * ( u32 * ) ( regs - > pc ) = = * ( u32 * ) ( & arch_kgdb_ops . gdb_bpt_instr ) )
regs - > pc + = BREAK_INSTR_SIZE ;
}
/* untested */
void sleeping_thread_to_gdb_regs ( unsigned long * gdb_regs , struct task_struct * p )
{
2014-12-18 17:56:17 +03:00
unsigned int i ;
2010-08-03 13:45:08 +04:00
unsigned long * pt_regb = ( unsigned long * ) ( p - > thread . regs ) ;
/* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
for ( i = 0 ; i < ( sizeof ( struct pt_regs ) / 4 ) - 1 ; i + + )
gdb_regs [ i ] = pt_regb [ i ] ;
/* pvr part - we have 11 pvr regs */
for ( i = 0 ; i < sizeof ( struct pvr_s ) / 4 ; i + + )
gdb_regs [ GDB_PVR + i ] = pvr . pvr [ i ] ;
}
void kgdb_arch_set_pc ( struct pt_regs * regs , unsigned long ip )
{
regs - > pc = ip ;
}
int kgdb_arch_handle_exception ( int vector , int signo , int err_code ,
char * remcom_in_buffer , char * remcom_out_buffer ,
struct pt_regs * regs )
{
char * ptr ;
unsigned long address ;
switch ( remcom_in_buffer [ 0 ] ) {
case ' c ' :
/* handle the optional parameter */
ptr = & remcom_in_buffer [ 1 ] ;
if ( kgdb_hex2long ( & ptr , & address ) )
regs - > pc = address ;
return 0 ;
}
return - 1 ; /* this means that we do not want to exit from the handler */
}
int kgdb_arch_init ( void )
{
get_pvr ( & pvr ) ; /* Fill PVR structure */
return 0 ;
}
void kgdb_arch_exit ( void )
{
/* Nothing to do */
}
/*
* Global data
*/
2013-06-08 20:52:08 +04:00
struct kgdb_arch arch_kgdb_ops = {
2010-09-28 09:49:17 +04:00
# ifdef __MICROBLAZEEL__
. gdb_bpt_instr = { 0x18 , 0x00 , 0x0c , 0xba } , /* brki r16, 0x18 */
# else
2010-08-03 13:45:08 +04:00
. gdb_bpt_instr = { 0xba , 0x0c , 0x00 , 0x18 } , /* brki r16, 0x18 */
2010-09-28 09:49:17 +04:00
# endif
2010-08-03 13:45:08 +04:00
} ;