URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [gdb-5.0/] [gdb/] [testsuite/] [gdb.trace/] [gdb_c_test.c] - Rev 1765
Compare with Previous | Blame | View Log
/* ****************************************************************************** ****************************************************************************** * * COPYRIGHT (C) by EMC Corporation, 1997 All rights reserved. * $Id: gdb_c_test.c,v 1.1.1.1 2001-05-18 11:18:40 markom Exp $ * DESCRIPTION: This module has been provided for the purpose of testing GDB. * * NOTES: * ****************************************************************************** *****************************************************************************/ /*============================================================================= * INCLUDE FILES *===========================================================================*/ #ifdef DO_IT_BY_THE_BOOK #include "symtypes_defs.h" #include "printp.h" #include "adbg_expression.h" #include "common_hw_ds.h" #include "common_hw_defs.h" #include "evnttrac.h" #include "sym_scratch_ds.h" #include "symglob_ds.h" #include "sym_protglob_ds.h" #include "ether.h" #include <ctype.h> #else #include "adbg_dtc.h" #define YES 1 #define NO 0 #define TRUE 1 #define FALSE 0 #define ENABLED 1 #define DISABLED 0 #define CONTROL_C 3 /* ASCII 'ETX' */ /* * Faked after ctype.h */ #define isxdigit(X) (((X) >= '0' && (X) <= '9') || \ ((X) >= 'A' && (X) <= 'F') || \ ((X) >= 'a' && (X) <= 'f')) /* * Borrowed from string.h */ extern unsigned int strlen ( const char * ); /* * Extracted from symtypes.h: */ typedef char BOOL; /* 8 Bits */ typedef unsigned char UCHAR; /* 8 Bits */ typedef unsigned short USHORT; /* 16 Bits */ typedef unsigned long ULONG; /* 32 Bits */ /* * for struct t_expr_tag and * decl of build_and_add_expression */ #include "adbg_expression.h" #define NULL 0 /* * Extracted from printp.h: */ extern void printp ( const char * fptr, ... ); extern void sprintp ( const char * fptr, ... ); /* * Extracted from ether.h: */ extern long eth_to_gdb ( UCHAR *buf, long length ); /* * Derived from hwequs.s: */ #define CS_CODE_START 0x100000 #define CS_CODE_SIZE 0x200000 #define LAST_CS_WORD (CS_CODE_START + CS_CODE_SIZE - 2) #define sh_genstat1 (*((volatile ULONG *) 0xFFFFFE54)) #define rs232_mode1 0 /* rs-232 mode 1 reg. */ #define rs232_mode2 rs232_mode1 /* rs-232 mode 2 reg. */ #define rs232_stat 4 /* rs-232 status reg. */ #define rs232_clk rs232_stat /* rs-232 clock select reg. */ #define rs232_cmd 8 /* rs-232 command reg */ #define rs232_transmit 12 /* rs-232 transmit reg. */ #define rs232_receive rs232_transmit /* rs-232 transmit reg. */ #define rs232_aux 16 /* rs-232 aux control reg. */ #define rs232_isr 20 /* rs-232 interrupt status reg. */ #define rs232_imr rs232_isr /* rs-232 interrupt mask reg. */ #define rs232_tc_high 24 /* rs-232 timer/counter high reg. */ #define rs232_tc_low 28 /* rs-232 timer/counter low reg. */ #endif /*============================================================================ * MODULE DEFINES *===========================================================================*/ #define P_RST_LAN_UART_REG ((volatile UCHAR *) 0xFFFFFE45) #define M_RST_LAN_UART 0x80 /* Bit 7 */ #define P_LAN0TR_REG P_RST_LAN_UART_REG #define M_LAN0TR 0x20 /* Bit 5 */ #define M_SH_GENCON_LAN0TR 0x00200000 /* Bit 21 */ #define MAX_RS232_CHARS 512 #define LAN_Q_MOD(X) ((X) % MAX_RS232_CHARS) /*---------------------------------------* * LAN UART Registers * *---------------------------------------*/ #define LAN_UART_BASE ((ULONG) 0xfffffc22) /* Write-Read */ #define P_LAN_MR1 ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_mode1 ))) #define P_LAN_MR2 ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_mode2 ))) /* Write-Only */ #define P_LAN_ACR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_aux ))) #define P_LAN_CR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_cmd ))) #define P_LAN_CSR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_clk ))) #define P_LAN_CTLR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_tc_low ))) #define P_LAN_CTUR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_tc_high ))) #define P_LAN_IMR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_imr ))) /* Read-Only */ #define P_LAN_SR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_stat ))) #define P_LAN_ISR ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_isr ))) #define P_LAN_XMT ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_transmit))) #define P_LAN_RCV ((volatile UCHAR *) (LAN_UART_BASE + ((ULONG) rs232_receive ))) /* * Bit Values for Write-Read and Write-Only Registers */ #define DEFAULT_LAN_MR1 ((UCHAR) 0x13) #define DEFAULT_LAN_MR2 ((UCHAR) 0x07) #define DEFAULT_LAN_CSR ((UCHAR) 0xcc) #define DEFAULT_LAN_ACR ((UCHAR) 0x38) #define DEFAULT_LAN_CTUR ((UCHAR) 0xff) #define DEFAULT_LAN_CTLR ((UCHAR) 0xff) #define LAN_ACR_SELECT_BRG_0 DEFAULT_LAN_ACR #define LAN_ACR_SELECT_BRG_1 (DEFAULT_LAN_ACR | 0x80) #define UART_CR_RESET_MR_PTR ((UCHAR) 0x10) /* Reset MR pointer (points to MR1). */ #define UART_CR_RESET_RVCR ((UCHAR) 0x20) /* Reset receiver (disabled). */ #define UART_CR_RESET_XMTR ((UCHAR) 0x30) /* Reset transmitter (disabled). */ #define UART_CR_RESET_ERROR_STATUS ((UCHAR) 0x40) /* Reset error status. */ #define UART_CR_RESET_BRK_CHG_INT ((UCHAR) 0x50) /* Reset break change interrupt. */ #define UART_CR_START_CNTR_TIMER ((UCHAR) 0x80) /* Start counter/timer. */ #define UART_CR_STOP_CNTR ((UCHAR) 0x90) /* Stop counter. */ #define UART_CR_DISABLE_XMTR ((UCHAR) 0x08) /* Disable transmitter. */ #define UART_CR_ENABLE_XMTR ((UCHAR) 0x04) /* Enable transmitter. */ #define UART_CR_DISABLE_RCVR ((UCHAR) 0x02) /* Disable receiver. */ #define UART_CR_ENABLE_RCVR ((UCHAR) 0x01) /* Enable receiver. */ #define UART_CSR_BR_4800 ((UCHAR) 0x99) /* With either BRG Set selected (via ACR). */ #define UART_CSR_BR_9600 ((UCHAR) 0xbb) /* With either BRG Set selected (via ACR). */ #define UART_CSR_BR_19200 ((UCHAR) 0xcc) /* With BRG Set '1' selected (via ACR). */ #define UART_CSR_BR_38400 ((UCHAR) 0xcc) /* With BRG Set '0' selected (via ACR). */ #define UART_IMR_RxRDY ((UCHAR) 0x04) /* Enable 'RxRDY' interrupt. */ #define UART_IMR_TxEMT ((UCHAR) 0x02) /* Enable 'TxEMT' interrupt. */ #define UART_IMR_TxRDY ((UCHAR) 0x01) /* Enable 'TxRDY' interrupt. */ /* * Bit Masks for Read-Only Registers */ #define M_UART_SR_RCVD_BRK 0x80 /* Bit 7 */ #define M_UART_SR_FE 0x40 /* Bit 6 */ #define M_UART_SR_PE 0x20 /* Bit 5 */ #define M_UART_SR_OE 0x10 /* Bit 4 */ #define M_UART_SR_TxEMT 0x08 /* Bit 3 */ #define M_UART_SR_TxRDY 0x04 /* Bit 2 */ #define M_UART_SR_FFULL 0x02 /* Bit 1 */ #define M_UART_SR_RxRDY 0x01 /* Bit 0 */ #define M_UART_ISR_RxRDY 0x04 /* Bit 2 */ #define M_UART_ISR_TxEMT 0x02 /* Bit 1 */ #define M_UART_ISR_TxRDY 0x01 /* Bit 0 */ /*---------------------------------------* * Support for 'Utility 83'. * *---------------------------------------*/ #define LAN_UTIL_CODE 0x83 #define LAN_INIT ((ULONG) (('I' << 24) | ('N' << 16) | ('I' << 8) | 'T')) #define LAN_BAUD ((ULONG) (('B' << 24) | ('A' << 16) | ('U' << 8) | 'D')) #define LAN_INTR ((ULONG) (('I' << 24) | ('N' << 16) | ('T' << 8) | 'R')) #define LAN_XMT ((ULONG) (('X' << 16) | ('M' << 8) | 'T')) #define LAN_ECHO ((ULONG) (('E' << 24) | ('C' << 16) | ('H' << 8) | 'O')) #define LAN_STAT ((ULONG) (('S' << 24) | ('T' << 16) | ('A' << 8) | 'T')) #define LAN_IN ((ULONG) (('I' << 8) | 'N')) #define LAN_OUT ((ULONG) (('O' << 16) | ('U' << 8) | 'T')) #define LAN_PUTC ((ULONG) (('P' << 24) | ('U' << 16) | ('T' << 8) | 'C')) #define LAN_WPM ((ULONG) (('W' << 16) | ('P' << 8) | 'M')) #define STATUS(X) ( ( ( X ) == 0 ) ? "disabled" : "enabled" ) #define XMT_VIA_BP_ENABLED() ( *P_LAN0TR_REG & M_LAN0TR ? 1 : 0 ) #define TRAP_1_INST 0x4E41 /* * Bit #13 of shared genstat 1 indicates * which processor we are as follows. * * 0 => X (side A) * 1 => Y (side B) */ #define M_PROC_ID 0x00002000 #define IS_SIDE_A() ( ( (sh_genstat1) & M_PROC_ID ) == 0 ) #define IS_SIDE_B() ( (sh_genstat1) & M_PROC_ID ) #ifdef STANDALONE /* Compile this module stand-alone for debugging */ #define LAN_PUT_CHAR(X) printf("%c", X) #else #define LAN_PUT_CHAR(X) while ( lan_put_char( X ) ) #endif #define VIA_RS232 0 #define VIA_ETHERNET 1 #define MAX_IO_BUF_SIZE 400 #define MAX_BYTE_CODES 200 /* maximum length for bytecode string */ static ULONG gdb_host_comm; static ULONG gdb_cat_ack; static char eth_outbuffer[ MAX_IO_BUF_SIZE + 1 ]; #ifdef STANDALONE #define ACK_PKT() LAN_PUT_CHAR( '+' ) #define NACK_PKT() LAN_PUT_CHAR( '-' ) #else #define ACK_PKT() { \ if ( VIA_ETHERNET == gdb_host_comm ) \ { \ gdb_cat_ack = YES; \ } \ else \ { \ LAN_PUT_CHAR( '+' ); \ } \ } #define NACK_PKT() { \ if ( VIA_ETHERNET == gdb_host_comm ) \ { \ eth_outbuffer[ 0 ] = '-'; \ eth_to_gdb( (UCHAR *) eth_outbuffer, 1 ); \ } \ else \ { \ LAN_PUT_CHAR( '-' ); \ } \ } #endif /*============================================================================ * MODULE TYPEDEFS *===========================================================================*/ typedef struct rs232_queue { long head_index; long tail_index; ULONG overflows; long gdb_packet_start; long gdb_packet_end; long gdb_packet_csum1; long gdb_packet_csum2; UCHAR buf[ MAX_RS232_CHARS ]; } T_RS232_QUEUE; /*============================================================================= * EXTERNAL GLOBAL VARIABLES *===========================================================================*/ extern volatile UCHAR sss_trace_flag; /*============================================================================= * STATIC MODULE DECLARATIONS *===========================================================================*/ static T_RS232_QUEUE lan_input_queue, lan_output_queue; static BOOL test_echo; #if 0 /* The stub no longer seems to use this. */ static BOOL write_access_enabled; #endif static int baud_rate_idx; static ULONG tx_by_intr, tx_by_poll; static UCHAR lan_shadow_imr; /*============================================================================= * EXTERNAL FUNCTION PROTOTYPES *===========================================================================*/ extern long write_to_protected_mem( void *address, unsigned short value ); /*============================================================================= * MODULE GLOBAL FUNCTIONS PROTOTYPES *===========================================================================*/ ULONG gdb_c_test( ULONG *parm ); void lan_init( void ); void lan_isr( void ); long lan_get_char( void ); long lan_put_char( UCHAR c ); ULONG lan_util( ULONG *parm ); /*============================================================================= * MODULE LOCAL FUNCTION PROTOTYPES *===========================================================================*/ static void lan_reset( void ); static void lan_configure( void ); static void lan_init_queue( T_RS232_QUEUE *p_queue ); static void lan_add_to_queue( long c, T_RS232_QUEUE *p_queue ); static UCHAR lan_next_queue_char( T_RS232_QUEUE *p_queue ); static void lan_util_menu( void ); static long get_gdb_input( long c, T_RS232_QUEUE *p_input_q ); /*============================================================================= * GDB STUB FUNCTION PROTOTYPES *===========================================================================*/ void gdb_trap_1_handler( void ); void gdb_trace_handler ( void ); void gdb_get_eth_input( unsigned char *buf, long length ); static void getpacket ( void ); static void putpacket ( char * ); static void discard_packet ( void ); #ifdef STANDALONE /* Compile this module stand-alone for debugging */ #include <stdio.h> #define printp printf /* easier than declaring a local varargs stub func. */ #endif /* STANDALONE */ /*============================================================================= * MODULE BODY *===========================================================================*/ /* ------------------- Things that belong in a header file --------------- */ extern char *memset (char *, int, int); /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%* * * * Global Module Functions * * * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ static char gdb_char_test; static short gdb_short_test; static long gdb_long_test; static char gdb_arr_test[25]; static struct GDB_STRUCT_TEST { char c; short s; long l; int bfield : 11; /* collect bitfield */ char arr[25]; struct GDB_STRUCT_TEST *next; } gdb_struct1_test, gdb_struct2_test, *gdb_structp_test, **gdb_structpp_test; static union GDB_UNION_TEST { char c; short s; long l; int bfield : 11; /* collect bitfield */ char arr[4]; union GDB_UNION_TEST *next; } gdb_union1_test; void gdb_recursion_test (int, int, int, int, int, int, int); void gdb_recursion_test (int depth, int q1, int q2, int q3, int q4, int q5, int q6) { /* gdb_recursion_test line 0 */ int q = q1; /* gdbtestline 1 */ q1 = q2; /* gdbtestline 2 */ q2 = q3; /* gdbtestline 3 */ q3 = q4; /* gdbtestline 4 */ q4 = q5; /* gdbtestline 5 */ q5 = q6; /* gdbtestline 6 */ q6 = q; /* gdbtestline 7 */ if (depth--) /* gdbtestline 8 */ gdb_recursion_test (depth, q1, q2, q3, q4, q5, q6); /* gdbtestline 9 */ } ULONG gdb_c_test( ULONG *parm ) { char *p = "gdb_c_test"; char *rediculously_long_variable_name_with_equally_long_string_assignment; register long local_reg = 7; static unsigned long local_static, local_static_sizeof; long local_long; unsigned long *stack_ptr; unsigned long end_of_stack; rediculously_long_variable_name_with_equally_long_string_assignment = "rediculously long variable name with equally long string assignment"; local_static = 9; local_static_sizeof = sizeof (struct GDB_STRUCT_TEST); local_long = local_reg + 1; stack_ptr = (unsigned long *) &local_long; end_of_stack = (unsigned long) &stack_ptr + sizeof(stack_ptr) + sizeof(end_of_stack) - 1; printp ("\n$Id: gdb_c_test.c,v 1.1.1.1 2001-05-18 11:18:40 markom Exp $\n"); printp( "%s: arguments = %X, %X, %X, %X, %X, %X\n", p, parm[ 1 ], parm[ 2 ], parm[ 3 ], parm[ 4 ], parm[ 5 ], parm[ 6 ] ); gdb_char_test = gdb_struct1_test.c = (char) ((long) parm[1] & 0xff); gdb_short_test = gdb_struct1_test.s = (short) ((long) parm[2] & 0xffff); gdb_long_test = gdb_struct1_test.l = (long) ((long) parm[3] & 0xffffffff); gdb_union1_test.l = (long) parm[4]; gdb_arr_test[0] = gdb_struct1_test.arr[0] = (char) ((long) parm[1] & 0xff); gdb_arr_test[1] = gdb_struct1_test.arr[1] = (char) ((long) parm[2] & 0xff); gdb_arr_test[2] = gdb_struct1_test.arr[2] = (char) ((long) parm[3] & 0xff); gdb_arr_test[3] = gdb_struct1_test.arr[3] = (char) ((long) parm[4] & 0xff); gdb_arr_test[4] = gdb_struct1_test.arr[4] = (char) ((long) parm[5] & 0xff); gdb_arr_test[5] = gdb_struct1_test.arr[5] = (char) ((long) parm[6] & 0xff); gdb_struct1_test.bfield = 144; gdb_struct1_test.next = &gdb_struct2_test; gdb_structp_test = &gdb_struct1_test; gdb_structpp_test = &gdb_structp_test; gdb_recursion_test (3, (long) parm[1], (long) parm[2], (long) parm[3], (long) parm[4], (long) parm[5], (long) parm[6]); gdb_char_test = gdb_short_test = gdb_long_test = 0; gdb_structp_test = (void *) 0; gdb_structpp_test = (void *) 0; memset ((char *) &gdb_struct1_test, 0, sizeof (gdb_struct1_test)); memset ((char *) &gdb_struct2_test, 0, sizeof (gdb_struct2_test)); local_static_sizeof = 0; local_static = 0; return ( (ULONG) 0 ); } /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_init * * * DESCRIPTION: * * * RETURN VALUE: * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * * *---------------------------------------------------------------------------*/ void lan_init( void ) { if ( IS_SIDE_A( ) ) { lan_reset( ); lan_init_queue( &lan_input_queue ); lan_init_queue( &lan_output_queue ); lan_configure( ); } return; } /* end of 'lan_init' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_isr * * * DESCRIPTION: * * * RETURN VALUE: None. * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * *---------------------------------------------------------------------------*/ void lan_isr( void ) { UCHAR c; lan_shadow_imr = 0; /* Disable all UART interrupts. */ *P_LAN_IMR = lan_shadow_imr; if ( *P_LAN_ISR & M_UART_ISR_RxRDY ) { gdb_host_comm = VIA_RS232; c = *P_LAN_RCV; if ( test_echo ) { /* ????? */ } if ( c == CONTROL_C ) { /* can't stop the target, but we can tell gdb to stop waiting... */ discard_packet( ); putpacket( "S03" ); /* send back SIGINT to the debugger */ } else { lan_add_to_queue( (long) c, &lan_input_queue ); get_gdb_input( (long) c, &lan_input_queue ); } } if ( XMT_VIA_BP_ENABLED( ) ) { c = 0; while ( (*P_LAN_ISR & M_UART_ISR_TxRDY) && (c = lan_next_queue_char( &lan_output_queue )) ) { *P_LAN_XMT = c; ++tx_by_intr; } if ( c ) { lan_shadow_imr |= UART_IMR_TxRDY; /* (Re-)Enable 'TxRDY' interrupt from UART. */ } } lan_shadow_imr |= UART_IMR_RxRDY; /* Re-Enable 'RxRDY' interrupt from UART. */ *P_LAN_IMR = lan_shadow_imr; return; } /* end of 'lan_isr' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_get_char * * * DESCRIPTION: Fetches a character from the UART. * * * RETURN VALUE: 0 on success, -1 on failure. * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * *---------------------------------------------------------------------------*/ long lan_get_char( void ) { long status = -2; /* AGD: nothing found in rcv buffer */ if ( *P_LAN_SR & M_UART_SR_RxRDY ) { char c = (char) *P_LAN_RCV; if ( test_echo ) { LAN_PUT_CHAR ( c ); } if ( c == CONTROL_C ) { /* can't stop the target, but we can tell gdb to stop waiting... */ discard_packet( ); putpacket( "S03" ); /* send back SIGINT to the debugger */ status = 0; /* success */ } else { lan_add_to_queue( (long) c, &lan_input_queue ); status = get_gdb_input( (long) c, &lan_input_queue ); } } return( status ); } /* end of 'lan_get_char' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_put_char * * DESCRIPTION: Puts a character out via the UART. * * RETURN VALUE: 0 on success, -1 on failure. * * USED GLOBAL VARIABLES: none. * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * NOTES: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * !! !! * !! If 'XMT_VIA_BP_ENABLED()' is FALSE then output is THROWN AWAY. !! * !! This prevents anyone infinite-looping on this function. !! * !! !! * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * *---------------------------------------------------------------------------*/ long lan_put_char( UCHAR c ) { long status = -1; if ( XMT_VIA_BP_ENABLED( ) ) { if ( *P_LAN_SR & M_UART_SR_TxRDY ) { lan_add_to_queue( (long) c, &lan_output_queue ); c = lan_next_queue_char( &lan_output_queue ); *P_LAN_XMT = c; ++tx_by_poll; status = 0; } #if 0 else { status = 0; lan_shadow_imr |= UART_IMR_TxRDY; /* Enable 'TxRDY' interrupt from UART. */ *P_LAN_IMR = lan_shadow_imr; } #endif } else { status = 0; /* You lose: input character goes to the bit bucket. */ } return( status ); } /* end of 'lan_put_char' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_util * * DESCRIPTION: * * RETURN VALUE: * * USED GLOBAL VARIABLES: * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * NOTES: * *---------------------------------------------------------------------------*/ ULONG lan_util( ULONG *parm ) { static const struct { ULONG rate_code; UCHAR acr_setting; UCHAR csr_setting; } baud_rate_setting [] = { { 0x38400, LAN_ACR_SELECT_BRG_0, UART_CSR_BR_38400 }, { 0x19200, LAN_ACR_SELECT_BRG_1, UART_CSR_BR_19200 }, { 0x9600, LAN_ACR_SELECT_BRG_0, UART_CSR_BR_9600 }, { 0x4800, LAN_ACR_SELECT_BRG_0, UART_CSR_BR_4800 } }; #define BOGUS_P1 0xE1 #define BOGUS_P2 0xE2 ULONG not_done_code; ULONG opcode; ULONG parm_1; ULONG parm_2; int i; UCHAR c; not_done_code = 0; opcode = parm[ 1 ]; parm_1 = parm[ 2 ]; parm_2 = parm[ 3 ]; switch ( opcode ) { case LAN_INIT: { lan_init( ); printp( "\n\n Interface (Re)Initialized ...\n\n" ); break; } case LAN_BAUD: { for ( i = 0; i < (int)(sizeof(baud_rate_setting) / sizeof(baud_rate_setting[0])); i ++ ) { if ( baud_rate_setting[i].rate_code == parm_1 ) { baud_rate_idx = i; *P_LAN_ACR = baud_rate_setting[i].acr_setting; *P_LAN_CSR = baud_rate_setting[i].csr_setting; printp ( "Baud rate set to %X!\n", baud_rate_setting[i].rate_code ); return( not_done_code ); } } printp( "\n\n *** SYNTAX Error - Invalid baudrate (P2)\n\n" ); not_done_code = BOGUS_P2; break; } case LAN_INTR: { switch ( parm_1 ) { case 0x0D: /* Disable 'RxRDY' Interrupts */ { lan_shadow_imr &= ~UART_IMR_RxRDY; *P_LAN_IMR = lan_shadow_imr; printp( "\n\n Receive Ready Interrupts DISABLED ...\n\n" ); break; } case 0x0E: /* Enable 'RxRDY' Interrupts */ { lan_shadow_imr |= UART_IMR_RxRDY; *P_LAN_IMR = lan_shadow_imr; printp( "\n\n Receive Ready Interrupts ENABLED ...\n\n" ); break; } default: { printp( "\n\n *** SYNTAX Error - Invalid P2 (use D or E)\n\n" ); not_done_code = BOGUS_P2; } } break; } case LAN_XMT: { switch ( parm_1 ) { case 0x0E: /* Enable Transmission-via-Backplane */ { if ( !(*P_LAN0TR_REG & M_LAN0TR) ) { *P_LAN0TR_REG |= M_LAN0TR; /* 0 -> 1 */ } printp( "\n\n Transmit-via-Backplane ENABLED ...\n\n" ); break; } case 0x0D: /* Disable Transmission-via-Backplane */ { if ( *P_LAN0TR_REG & M_LAN0TR ) { *P_LAN0TR_REG &= ~M_LAN0TR; /* 1 -> 0 */ } printp( "\n\n Transmit-via-Backplane DISABLED ...\n\n" ); break; } default: { printp( "\n\n *** SYNTAX Error - Invalid P2 (use D or E)\n\n" ); not_done_code = BOGUS_P2; lan_util_menu( ); } } break; } case LAN_STAT: { printp( "\n -- Status --\n\n" ); printp( " Baud Rate: %X *\n", baud_rate_setting[ baud_rate_idx ].rate_code ); printp( " Xmt-via-BP: %s *\n", STATUS( XMT_VIA_BP_ENABLED( ) ) ); printp( " RxRdy Intr: %s *\n", STATUS( (lan_shadow_imr & M_UART_ISR_RxRDY) ) ); /*** printp( " TxRdy Intr: %s\n", STATUS( (lan_shadow_imr & M_UART_ISR_TxRDY) ) ); ***/ printp( " Echo: %s *\n\n", STATUS( test_echo ) ); printp( " IMR: %02X\n", (ULONG) lan_shadow_imr ); printp( " ISR: %02X\n", (ULONG) *P_LAN_ISR ); printp( " SR: %02X\n\n", (ULONG) *P_LAN_SR ); printp( " Input Overflows: %d\n\n", lan_input_queue.overflows ); printp( " Tx by Intr: %d\n", tx_by_intr ); printp( " Tx by Poll: %d\n\n", tx_by_poll ); printp( " * Can be set or toggled via Utility %2X.\n\n", (ULONG) LAN_UTIL_CODE ); break; } case LAN_IN: { switch ( parm_1 ) { case 0x0C: /* Clear and Reset Queue */ { lan_init_queue( &lan_input_queue ); printp( "\n\n Queue CLEARED/RESET ...\n\n" ); break; } case 0x0D: /* Display Queue */ { printp( "\n -- Input Queue --\n" ); printp( "\n Head Index: %8X Tail Index: %8X\n\n ", (ULONG) lan_input_queue.head_index, (ULONG) lan_input_queue.tail_index ); for ( i = 0; i < MAX_RS232_CHARS; ++i ) { printp( " %02X", (ULONG) lan_input_queue.buf[ i ] ); if ( 15 == (i % 16) ) { int j; printp ( " " ); for ( j = i - 15; j <= i; j++ ) { if ( lan_input_queue.buf[ j ] >= ' ' && lan_input_queue.buf[ j ] < 127 ) printp ( "%c", lan_input_queue.buf[ j ] ); else printp ( "." ); } printp( "\n " ); } else if ( 7 == (i % 8) ) { printp( " " ); } } printp( "\n" ); break; } case 0x0F: /* Fetch next character in Queue */ { c = lan_next_queue_char( &lan_input_queue ); if ( c ) { printp( "\n\n Next Character: " ); if ( 0x21 <= c && c <= 0x7F ) { printp( "%c\n\n", (ULONG) c ); } else if ( 0x20 == ((UCHAR) c) ) { printp( "<space>\n\n" ); } else { printp( "%02X\n\n", (ULONG) c ); } } else { printp( "\n\n Input Queue EMPTY ...\n\n" ); } break; } default: { printp( "\n\n *** SYNTAX Error - Invalid P2 ...\n\n" ); not_done_code = BOGUS_P2; break; } } break; } case LAN_OUT: { switch ( parm_1 ) { case 0x0C: /* Clear and Reset Queue */ { lan_init_queue( &lan_output_queue ); printp( "\n\n Queue CLEARED/RESET ...\n\n" ); break; } case 0x0D: /* Display Queue */ { printp( "\n -- Output Queue --\n" ); printp( "\n Head Index: %8X Tail Index: %8X\n\n ", (ULONG) lan_output_queue.head_index, (ULONG) lan_output_queue.tail_index ); for ( i = 0; i < MAX_RS232_CHARS; ++i ) { printp( " %02X", (ULONG) lan_output_queue.buf[ i ] ); if ( 15 == (i % 16) ) { int j; printp ( " " ); for ( j = i - 15; j <= i; j++ ) { if ( lan_output_queue.buf[ j ] >= ' ' && lan_output_queue.buf[ j ] < 127 ) printp ( "%c", lan_output_queue.buf[ j ] ); else printp ( "." ); } printp( "\n " ); } else if ( 7 == (i % 8) ) { printp( " " ); } } printp( "\n" ); break; } case 0x0F: /* Fetch next character in Queue */ { c = lan_next_queue_char( &lan_output_queue ); if ( c ) { printp( "\n\n Next Character: " ); if ( 0x21 <= c && c <= 0x7F ) { printp( "%c\n\n", (ULONG) c ); } else if ( 0x20 == c ) { printp( "<space>\n\n" ); } else { printp( "%02X\n\n", (ULONG) c ); } } else { printp( "\n\n Input Queue EMPTY ...\n\n" ); } break; } default: { printp( "\n\n *** SYNTAX Error - Invalid P2 ...\n\n" ); not_done_code = BOGUS_P2; break; } } break; } case LAN_ECHO: { switch ( parm_1 ) { case 0x0E: { test_echo = ENABLED; printp( "\n\n Test echo ENABLED ...\n\n" ); break; } case 0x0D: { test_echo = DISABLED; printp( "\n\n Test echo DISABLED ...\n\n" ); break; } default: { printp( "\n\n *** SYNTAX Error - Invalid P2 ...\n\n" ); not_done_code = BOGUS_P2; break; } } break; } case LAN_PUTC: { if ( 0x20 < parm_1 && parm_1 < 0x7F ) { if ( lan_put_char( (UCHAR) parm_1 ) ) { printp( "\n\n *** 'lan_put_char' Error ...\n" ); } else { printp( "\n\n O.K. ...\n" ); } } else { printp( "\n\n *** Error - character must be in the 0x21-0x7E range ...\n" ); not_done_code = BOGUS_P2; } break; } /*** case LAN_WPM: { if ( write_to_protected_mem( (void *) parm_1, (unsigned short) parm_2 ) ) { printp( "\n Write to protected memory FAILED ...\n" ); } break; } ***/ case 0: /* no argument -- print menu */ { lan_util_menu( ); break; } default: { parm_2 = 0; /* to supress compiler warning with 'LAN_WPM' case disabled */ printp( "\n\n *** SYNTAX Error - Invalid P1 ...\n\n" ); not_done_code = BOGUS_P1; break; } } /* End of 'switch ( opcode )'. */ return( not_done_code ); } /* end of 'lan_util' *===========================================================================*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%* * * * Local Module Functions * * * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_reset * * DESCRIPTION: Resets the LAN UART by strobing the 'RST_LAN_UART' bit in the * Shared Control 1 area. * * 1 _| ______ * | | | * Bit | | | * | | | * 0 _|______| |______ * |---------------------> t * * RETURN VALUE: None. * * USED GLOBAL VARIABLES: * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * NOTES: H/W configuration requires that a byte in the shared * control 1 area must be read before being written. * *---------------------------------------------------------------------------*/ static void lan_reset( void ) { while ( *P_RST_LAN_UART_REG & M_RST_LAN_UART ) { *P_RST_LAN_UART_REG &= ~M_RST_LAN_UART; /* 0 */ } while ( !(*P_RST_LAN_UART_REG & M_RST_LAN_UART) ) { *P_RST_LAN_UART_REG |= M_RST_LAN_UART; /* 1 */ } while ( *P_RST_LAN_UART_REG & M_RST_LAN_UART ) { *P_RST_LAN_UART_REG &= ~M_RST_LAN_UART; /* 0 */ } } /* end of 'lan_reset' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_configure * * * DESCRIPTION: * * * RETURN VALUE: * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * * *---------------------------------------------------------------------------*/ static void lan_configure( void ) { *P_LAN_CR = UART_CR_RESET_MR_PTR; /* Points to MR1. */ *P_LAN_CR = UART_CR_RESET_RVCR; /* Receiver disabled. */ *P_LAN_CR = UART_CR_RESET_XMTR; /* Transmitter disabled. */ *P_LAN_CR = UART_CR_RESET_ERROR_STATUS; *P_LAN_CR = UART_CR_RESET_BRK_CHG_INT; *P_LAN_MR1 = DEFAULT_LAN_MR1; *P_LAN_MR2 = DEFAULT_LAN_MR2; *P_LAN_ACR = DEFAULT_LAN_ACR; *P_LAN_CSR = UART_CSR_BR_9600; baud_rate_idx = 2; *P_LAN_CTUR = DEFAULT_LAN_CTUR; *P_LAN_CTLR = DEFAULT_LAN_CTLR; *P_LAN_CR = (UART_CR_START_CNTR_TIMER | UART_CR_ENABLE_XMTR | UART_CR_ENABLE_RCVR); lan_shadow_imr = UART_IMR_RxRDY; /* Enable only 'RxRDY' interrupt from UART. */ *P_LAN_IMR = lan_shadow_imr; tx_by_intr = 0; tx_by_poll = 0; return; } /* end of 'lan_configure' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_init_queue * * DESCRIPTION: * * RETURN VALUE: None. * * USED GLOBAL VARIABLES: * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * NOTES: * *---------------------------------------------------------------------------*/ static void lan_init_queue( T_RS232_QUEUE *p_queue ) { long i; /* * We set "head" equal to "tail" implying the queue is empty, * BUT the "head" and "tail" should each point to valid queue * positions. */ p_queue->head_index = 0; p_queue->tail_index = 0; p_queue->overflows = 0; p_queue->gdb_packet_start = -1; p_queue->gdb_packet_end = -1; p_queue->gdb_packet_csum1 = -1; p_queue->gdb_packet_csum2 = -1; for ( i = 0; i < MAX_RS232_CHARS; ++i ) { p_queue->buf[ i ] = 0; } return; } /* end of 'lan_init_queue' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_add_to_queue * * * DESCRIPTION: Adds the specified character to the tail of the * specified queue. Observes "oldest thrown on floor" * rule (i.e. the queue is allowed to "wrap" and the * input character is unconditionally placed at the * tail of the queue. * * * RETURN VALUE: None. * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * *---------------------------------------------------------------------------*/ static void lan_add_to_queue( long c, T_RS232_QUEUE *p_queue ) { if ( p_queue ) /* Sanity check. */ { if ( c & 0x000000FF ) /* We don't allow NULL characters to be added to a queue. */ { /* Insert the new character at the tail of the queue. */ p_queue->buf[ p_queue->tail_index ] = (UCHAR) (c & 0x000000FF); /* Increment the tail index. */ if ( MAX_RS232_CHARS <= ++(p_queue->tail_index) ) { p_queue->tail_index = 0; } /* Check for wrapping (i.e. overflow). */ if ( p_queue->head_index == p_queue->tail_index ) { /* If the tail has caught up to the head record the overflow . . . */ ++(p_queue->overflows); /* . . . then increment the head index. */ if ( MAX_RS232_CHARS <= ++(p_queue->head_index) ) { p_queue->head_index = 0; } } } /* End of 'if ( c & 0x000000FF )'. */ } /* End of 'if ( p_queue )'. */ return; } /* end of 'lan_add_to_queue' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_next_queue_char * * DESCRIPTION: * * RETURN VALUE: * * USED GLOBAL VARIABLES: * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * NOTES: * *---------------------------------------------------------------------------*/ static UCHAR lan_next_queue_char( T_RS232_QUEUE *p_queue ) { UCHAR c; c = 0; if ( p_queue ) { if ( p_queue->head_index != p_queue->tail_index ) { /* Return the 'oldest' character in the queue. */ c = p_queue->buf[ p_queue->head_index ]; /* Increment the head index. */ if ( MAX_RS232_CHARS <= ++(p_queue->head_index) ) { p_queue->head_index = 0; } } } /* End of 'if ( p_queue )'. */ return( c ); } /* end of 'lan_next_queue_char' *===========================================================================*/ /*----------------------------------------------------------------------------- * * FUNCTION NAME: lan_util_menu * * DESCRIPTION: Prints out a brief help on the LAN UART control utility. * * RETURN VALUE: None. * * USED GLOBAL VARIABLES: None. * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: None. * * NOTES: None. * *---------------------------------------------------------------------------*/ static void lan_util_menu( void ) { /* * Multiply calling printp() below is made due to the limitations * of printp(), incapable of handling long formatting constants: */ printp( "\n -- Options --\n\n" ); printp( " %2X,'INIT' ............... Reset & (Re)INITIALIZE Interface.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'BAUD',<rate> ........ Set BAUD Rate.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'INTR',<mode> ........ Toggle 'RxRDY' Interrupts.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'XMT',<mode> ......... Toggle TRANSMIT-via-backplane.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'STAT' ............... Display STATUS.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'ECHO',<mode> ........ Enable/Disable Test ECHO.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'IN',<action> ........ Access INPUT Queue.\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'OUT',<action> ....... Access OUTPUT Queue.\n\n", (ULONG) LAN_UTIL_CODE ); printp( " %2X,'PUTC',<char> ........ Output a Character (i.e. <char>).\n\n", (ULONG) LAN_UTIL_CODE ); /*** printp( " %2X,'WPM',address,word ... Write Protected Memory Test.\n\n", (ULONG) LAN_UTIL_CODE ); ***/ printp( " <rate>: 4800 <mode>: E - enable <action>: C - clear/reset\n" ); printp( " 9600 D - disable D - display\n" ); printp( " 19200 F - fetch next char\n" ); printp( " 38400\n" ); } /* end of 'lan_util_menu' *===========================================================================*/ /* Thu Feb 5 17:14:41 EST 1998 CYGNUS...CYGNUS...CYGNUS...CYGNUS...CYGNUS...CYGNUS...CYGNUS...CYGNUS */ static long get_gdb_input( long c, T_RS232_QUEUE * p_input_q ) { /* Now to detect when we've got a gdb packet... */ if ( '$' == c ) { /* char marks beginning of a packet */ if ( -1 != p_input_q->gdb_packet_start || -1 != p_input_q->gdb_packet_end || -1 != p_input_q->gdb_packet_csum1 || -1 != p_input_q->gdb_packet_csum2 ) { /* PROTOCOL ERROR */ /* NEW: Actually, this probably means that we muffed a packet, and GDB has already resent it. The thing to do now is to throw away the one we WERE working on, but immediately start accepting the new one. Don't NAK, or GDB will have to try and send it yet a third time! */ /*NACK_PKT( );*/ /*<ETHERNET>*/ discard_packet( ); /* throw away old packet */ lan_add_to_queue ('$', p_input_q); /* put the new "$" back in */ return 0; } else { /* match new "$" */ p_input_q->gdb_packet_start = p_input_q->tail_index; p_input_q->gdb_packet_end = p_input_q->gdb_packet_csum1 = p_input_q->gdb_packet_csum2 = -1; } } else if ( '#' == c ) { /* # marks end of packet (except for checksum) */ if ( -1 == p_input_q->gdb_packet_start || -1 != p_input_q->gdb_packet_end || -1 != p_input_q->gdb_packet_csum1 || -1 != p_input_q->gdb_packet_csum2 ) { /* PROTOCOL ERROR */ /* Garbled packet. Discard, but do not NAK. */ /*NACK_PKT( );*/ /*<ETHERNET>*/ discard_packet( ); return -1; } p_input_q->gdb_packet_end = p_input_q->tail_index; p_input_q->gdb_packet_csum1 = p_input_q->gdb_packet_csum2 = -1; } else if ( -1 != p_input_q->gdb_packet_start && -1 != p_input_q->gdb_packet_end) { if ( isxdigit( c ) ) { /* char is one of two checksum digits for packet */ if ( -1 == p_input_q->gdb_packet_csum1 && LAN_Q_MOD( p_input_q->gdb_packet_end + 1 ) == p_input_q->tail_index ) { /* first checksum digit */ p_input_q->gdb_packet_csum1 = p_input_q->tail_index; p_input_q->gdb_packet_csum2 = -1; } else if ( -1 == p_input_q->gdb_packet_csum2 && LAN_Q_MOD( p_input_q->gdb_packet_end + 2 ) == p_input_q->tail_index ) { /* second checksum digit: packet is complete! */ p_input_q->gdb_packet_csum2 = p_input_q->tail_index; getpacket(); /* got a packet -- extract it */ } else { /* probably can't happen (um... three hex digits?) */ /* PROTOCOL ERROR */ /* Not sure how this can happen, but ... discard it, but do not NAK it. */ /*NACK_PKT( );*/ /*<ETHERNET>*/ discard_packet( ); return -1; } } else { /* '#' followed by non-hex char */ /* PROTOCOL ERROR */ /* Bad packet -- discard but do not NAK */ /*NACK_PKT( );*/ /*<ETHERNET>*/ discard_packet( ); return -1; } } return 0; } #ifdef STANDALONE /* stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone Enable stand-alone build, for ease of debugging stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone stand-alone */ long write_to_protected_mem (addr, word) void *addr; unsigned short word; { return 0; } char dummy_memory[0x4000]; int main ( void ) { long c; lan_init_queue( &lan_input_queue ); printf( "Stand-alone EMC 'stub', pid = %d\n", getpid( ) ); printf( "Start of simulated 'memory': 0x%08x\n", &dummy_memory); while ( (c = getc( stdin ) ) != EOF ) { if ( c == '\\' ) /* escape char */ break; lan_add_to_queue( c, &lan_input_queue ); get_gdb_input (c, &lan_input_queue); fflush( stdout ); } printf( "Goodbye!\n" ); exit( 0 ); } #define SRAM_START ((void *) (&dummy_memory[0] + 0x00000000)) #define SRAM_END ((void *) (&dummy_memory[0] + 0x00000400)) #define RO_AREA_START ((void *) (&dummy_memory[0] + 0x00000100)) #define RO_AREA_END ((void *) (&dummy_memory[0] + 0x00000300)) #define NVD_START ((void *) (&dummy_memory[0] + 0x00003000)) #define NVD_END ((void *) (&dummy_memory[0] + 0x00003100)) #else /* normal stub (not stand-alone) */ #define SRAM_START ((void *) 0x00000000) #define SRAM_END ((void *) 0x00400000) #define RO_AREA_START ((void *) 0x00100000) #define RO_AREA_END ((void *) 0x00300000) #define NVD_START ((void *) 0x03000000) #define NVD_END ((void *) 0x03100000) #endif /* STANDALONE */ /* gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb Here begins the gdb stub section. gdb gdb The following functions were added by Cygnus, gdb gdb to make this thing act like a gdb stub. gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb gdb */ /* ------------------- global defines and data decl's -------------------- */ #define hexchars "0123456789abcdef" /* there are 180 bytes of registers on a 68020 w/68881 */ /* many of the fpa registers are 12 byte (96 bit) registers */ #define NUMREGBYTES 180 #define NUMREGS 29 #define REGISTER_BYTE(regno) regno enum regnames { D0, D1, D2, D3, D4, D5, D6, D7, A0, A1, A2, A3, A4, A5, A6, A7, PS, PC, FP0, FP1, FP2, FP3, FP4, FP5, FP6, FP7, FPCONTROL, FPSTATUS, FPIADDR }; unsigned long registers[NUMREGBYTES/4]; static long remote_debug; #define BUFMAX MAX_IO_BUF_SIZE static char inbuffer[BUFMAX], outbuffer[BUFMAX]; static char spare_buffer[BUFMAX]; struct stub_trace_frame { int valid; unsigned long frame_id; unsigned long tdp_id; FRAME_DEF *frame_data; COLLECTION_FORMAT_DEF *format; unsigned long traceregs[NUMREGBYTES/4]; unsigned char *stack_data; unsigned char *memrange_data; } curframe; /* ------------------- function prototypes -------------------- */ void handle_request ( char * ); /* ------------------- Implementation -------------------- */ static void discard_packet( void ) { lan_input_queue.head_index = lan_input_queue.tail_index; lan_input_queue.gdb_packet_start = lan_input_queue.gdb_packet_end = lan_input_queue.gdb_packet_csum1 = lan_input_queue.gdb_packet_csum2 = -1; } /* Utility function: convert an ASCII isxdigit to a hex nybble */ static long hex( char ch ) { if ( (ch >= 'A') && (ch <= 'F') ) return ch - 'A' + 10; if ( (ch >= 'a') && (ch <= 'f') ) return ch - 'a' + 10; if ( (ch >= '0') && (ch <= '9') ) return ch - '0'; return -1; } static void getpacket( void ) { unsigned char our_checksum, their_checksum; char *copy = inbuffer; unsigned char c; our_checksum = 0; /* first find the '$' */ while ((c = lan_next_queue_char ( &lan_input_queue )) != '$') if (c == 0) /* ??? Protocol error? (paranoia) */ { /* PROTOCOL ERROR (missing '$') */ /*NACK_PKT( );*/ /*<ETHERNET>*/ return; } /* Now copy the message (up to the '#') */ for (c = lan_next_queue_char ( &lan_input_queue ); /* skip the '$' */ c != 0 && c != '#'; /* stop at the '#' */ c = lan_next_queue_char ( &lan_input_queue )) { *copy++ = c; our_checksum += c; } *copy++ = '\0'; /* terminate the copy */ if (c == 0) /* ??? Protocol error? (paranoia) */ { /* PROTOCOL ERROR (missing '#') */ /*NACK_PKT( );*/ /*<ETHERNET>*/ return; } their_checksum = hex( lan_next_queue_char ( &lan_input_queue ) ) << 4; their_checksum += hex( lan_next_queue_char ( &lan_input_queue ) ); /* Now reset the queue packet-recognition bits */ discard_packet( ); if ( remote_debug || our_checksum == their_checksum ) { ACK_PKT( ); /* good packet */ /* Parse and process the packet */ handle_request( inbuffer ); } else /* PROTOCOL ERROR (bad check sum) */ NACK_PKT( ); } /* EMC will provide a better implementation (perhaps just of LAN_PUT_CHAR) that does not block. For now, this works. */ static void putpacket( char *str ) { unsigned char checksum; /* '$'<packet>'#'<checksum> */ if ( VIA_ETHERNET == gdb_host_comm ) { char *p_out; long length; p_out = eth_outbuffer; length = 0; if ( YES == gdb_cat_ack ) { *p_out++ = '+'; ++length; } gdb_cat_ack = NO; *p_out++ = '$'; ++length; checksum = 0; while ( *str ) { *p_out++ = *str; ++length; checksum += *str++; } *p_out++ = '#'; *p_out++ = hexchars[checksum >> 4]; *p_out = hexchars[checksum % 16]; length += 3; eth_to_gdb( (UCHAR *) eth_outbuffer, length ); } else { /* via RS-232 */ do { LAN_PUT_CHAR( '$' ); checksum = 0; while ( *str ) { LAN_PUT_CHAR( *str ); checksum += *str++; } LAN_PUT_CHAR( '#' ); LAN_PUT_CHAR( hexchars[checksum >> 4] ); LAN_PUT_CHAR( hexchars[checksum % 16] ); } while ( 0 /* get_debug_char( ) != '+' */ ); /* XXX FIXME: not waiting for the ack. */ } } /*----------------------------------------------------------------------------- * * FUNCTION NAME: gdb_get_eth_input * * * DESCRIPTION: * * * RETURN VALUE: None. * * * USED GLOBAL VARIABLES: * * * AFFECTED GLOBAL VARIABLES/SIDE EFFECTS: * * * NOTES: * * *---------------------------------------------------------------------------*/ void gdb_get_eth_input( unsigned char *buf, long length ) { gdb_host_comm = VIA_ETHERNET; for ( ; 0 < length; ++buf, --length) { if ( *buf == CONTROL_C ) { /* can't stop the target, but we can tell gdb to stop waiting... */ discard_packet( ); putpacket( "S03" ); /* send back SIGINT to the debugger */ } else { lan_add_to_queue( (long) *buf, &lan_input_queue ); get_gdb_input( (long) *buf, &lan_input_queue ); } } return; } /* end of 'gdb_get_eth_input' *===========================================================================*/ /* STDOUT STDOUT STDOUT STDOUT STDOUT STDOUT STDOUT STDOUT STDOUT STDOUT Stuff pertaining to simulating stdout by sending chars to gdb to be echoed. Dear reader: This code is based on the premise that if GDB receives a packet from the stub that begins with the character CAPITAL-OH, GDB will echo the rest of the packet to GDB's console / stdout. This gives the stub a way to send a message directly to the user. In practice, (as currently implemented), GDB will only accept such a packet when it believes the target to be running (ie. when you say STEP or CONTINUE); at other times it does not expect it. This will probably change as a side effect of the "asynchronous" behavior. Functions: gdb_putchar(char ch) gdb_write(char *str, int len) gdb_puts(char *str) gdb_error(char *format, char *parm) */ #if 0 /* avoid compiler warning while this is not used */ /* Function: gdb_putchar(int) Make gdb write a char to stdout. Returns: the char */ static int gdb_putchar( long ch ) { char buf[4]; buf[0] = 'O'; buf[1] = hexchars[ch >> 4]; buf[2] = hexchars[ch & 0x0F]; buf[3] = 0; putpacket( buf ); return ch; } #endif /* Function: gdb_write(char *, int) Make gdb write n bytes to stdout (not assumed to be null-terminated). Returns: number of bytes written */ static int gdb_write( char *data, long len ) { char *buf, *cpy; long i; buf = outbuffer; buf[0] = 'O'; i = 0; while ( i < len ) { for ( cpy = buf+1; i < len && cpy < buf + BUFMAX - 3; i++ ) { *cpy++ = hexchars[data[i] >> 4]; *cpy++ = hexchars[data[i] & 0x0F]; } *cpy = 0; putpacket( buf ); } return len; } /* Function: gdb_puts(char *) Make gdb write a null-terminated string to stdout. Returns: the length of the string */ static int gdb_puts( char *str ) { return gdb_write( str, strlen( str ) ); } /* Function: gdb_error(char *, char *) Send an error message to gdb's stdout. First string may have 1 (one) optional "%s" in it, which will cause the optional second string to be inserted. */ #if 0 static void gdb_error( char *format, char *parm ) { static char buf[400]; char *cpy; long len; if ( remote_debug ) { if ( format && *format ) len = strlen( format ); else return; /* empty input */ if ( parm && *parm ) len += strlen( parm ); for ( cpy = buf; *format; ) { if ( format[0] == '%' && format[1] == 's' ) /* include 2nd string */ { format += 2; /* advance two chars instead of just one */ while ( parm && *parm ) *cpy++ = *parm++; } else *cpy++ = *format++; } *cpy = '\0'; gdb_puts( buf ); } } #endif static void gdb_note (char *, int); static int error_ret (int, char *, int); static unsigned long elinum_to_index (unsigned long elinum) { if ((elinum & 0xf0) == 0xd0) return (elinum & 0x0f); else if ((elinum & 0xf0) == 0xa0) return (elinum & 0x0f) + 8; else return -1; } static long index_to_elinum (unsigned long index) { if (index <= 7) return index + 0xd0; else if (index <= 15) return (index - 8) + 0xa0; else return -1; } /* READMEM READMEM READMEM READMEM READMEM READMEM READMEM READMEM READMEM The following code pertains to reading memory from the target. Some sort of exception handling should be added to make it safe. READMEM READMEM READMEM READMEM READMEM READMEM READMEM READMEM READMEM Safe Memory Access: All reads and writes into the application's memory will pass thru get_uchar() or set_uchar(), which check whether accessing their argument is legal before actual access (thus avoiding a bus error). */ enum { SUCCESS = 0, FAIL = -1 }; #if 0 static long get_uchar ( const unsigned char * ); #endif static long set_uchar ( unsigned char *, unsigned char ); static long read_access_violation ( const void * ); static long write_access_violation ( const void * ); static long read_access_range(const void *, long); static DTC_RESPONSE find_memory(unsigned char *,long,unsigned char **,long *); static int dtc_error_ret (int ret, char *src, DTC_RESPONSE code) { if (src) sprintp (spare_buffer, "'%s' returned DTC error '%s'.\n", src, get_err_text (code)); else sprintp (spare_buffer, "DTC error '%s'.\n", get_err_text (code)); gdb_puts (spare_buffer); return ret; } #if 0 /* I think this function is unnecessary since the introduction of adbg_find_memory_addr_in_frame. */ /* Return the number of expressions in the format associated with a given trace frame. */ static int count_frame_exprs (FRAME_DEF *frame) { CFD *format; T_EXPR *expr; int num_exprs; /* Get the format from the frame. */ get_frame_format_pointer (frame, &format); /* Walk the linked list of expressions, and count the number of expressions we find there. */ num_exprs = 0; for (expr = format->p_cfd_expr; expr; expr = expr->next) num_exprs++; return num_exprs; } #endif #if 0 /* Function: get_frame_addr * * Description: If the input memory address was collected in the * current trace frame, then lookup and return the address * from within the trace buffer from which the collected byte * may be retrieved. Else return -1. */ unsigned char * get_frame_addr ( const unsigned char *addr ) { unsigned char *base, *regs, *stack, *mem; CFD *dummy; DTC_RESPONSE ret; /* first, see if addr is on the saved piece of stack for curframe */ if (curframe.format->stack_size > 0 && (base = (unsigned char *) curframe.traceregs[A7]) <= addr && addr < base + curframe.format->stack_size) { gdb_puts("STUB: get_frame_addr: call get_addr_to_frame_regs_stack_mem\n"); if ((ret = get_addr_to_frame_regs_stack_mem (curframe.frame_data, &dummy, (void *) ®s, (void *) &stack, (void *) &mem)) != OK_TARGET_RESPONSE) return (void *) dtc_error_ret (-1, "get_addr_to_frame_regs_stack_mem", ret); else return stack + (addr - base); } /* Next, try to find addr in the current frame's expression- collected memory blocks. I'm sure this is at least quadradic in time. */ { int num_exprs = count_frame_exprs (curframe.frame_data); int expr, block; /* Try each expression in turn. */ for (expr = 0; expr < num_exprs; expr++) { for (block = 0; ; block++) { T_EXPR_DATA *data; if (adbg_get_expr_data (curframe.frame_data, 'x', expr, block, &data) != OK_TARGET_RESPONSE) break; else if ((unsigned char *) data->address <= addr && addr < ((unsigned char *) data->address + data->size)) { /* We have found the right block; is it valid data? Upper-case stamps mean bad data. */ if ('A' <= data->stamp && data->stamp <= 'Z') { gdb_puts("STUB: get_frame_addr: adbg_get_expr_data INVALID\n"); return (unsigned char *) -1; } else { if (remote_debug > 1) { sprintp(spare_buffer, "STUB: get_frame_addr: got it [%x,%x)\n", data->address, data->address + data->size); gdb_puts(spare_buffer); } return (((unsigned char *) &data->data) + (addr - (unsigned char *) data->address)); } } } } } /* not found, return error */ return (unsigned char *) -1; } /*============================================================*/ static long get_uchar ( const unsigned char * addr ) { unsigned char *frame_addr; if ( read_access_violation ( addr ) ) return ( -1 ); /* Access error */ if (curframe.valid) /* if debugging a trace frame? */ { /* If the requested address was collected in the current frame, * then fetch and return the data from the trace buffer. */ if ((frame_addr = get_frame_addr (addr)) != (unsigned char *) -1) return ( *frame_addr ); /* If the requested address is in the Code Section, * let's be magnanimous and read it anyway (else we shall * not be able to disassemble, find function prologues, etc.) */ else if (CS_CODE_START <= (unsigned long) addr && (unsigned long) addr < CS_CODE_START + CS_CODE_SIZE) return (*addr); else return ( -1 ); /* "Access error" (the data was not collected) */ } else /* Not debugging a trace frame, read the data from live memory. */ return ( *addr ); /* Meaningful result >= 0 */ } #endif /*============================================================*/ static long set_uchar ( unsigned char * addr, unsigned char val ) { long check_result = write_access_violation ( addr ); if ( check_result != 0L ) return ( check_result ); /* Access error */ return ( *addr = val ); /* Successful writing */ } /*============================================================*/ /* * Function read_access_violation() below returns TRUE if dereferencing * its argument for reading would cause a bus error - and FALSE otherwise: */ static long read_access_violation ( const void * addr ) { return ( ( ( addr < SRAM_START ) || ( addr >= SRAM_END ) ) && ( ( addr < NVD_START ) || ( addr >= NVD_END ) ) ); } /*============================================================*/ /* * Function write_access_violation() below returns zero if dereferencing * its argument for writing is safe, -1 on a soft error (the argument * falls into the write-protected area), -2 on a hard error (the argument * points to a non-existent memory location). In other words, it returns * FALSE when no bus error is expected - and an error code otherwise: */ static long write_access_violation ( const void * addr ) { /* * The boundaries of the write-protected area have to be received via * an API provided in the Symmetrix core code. For now, these limits * are hard-coded: */ if ( ( addr >= RO_AREA_START ) && ( addr < RO_AREA_END ) ) return ( -1 ); /* soft error */ if ( ( ( addr < SRAM_START ) || ( addr >= SRAM_END ) ) && ( ( addr < NVD_START ) || ( addr >= NVD_END ) ) ) return ( -2 ); /* hard error */ return ( 0 ); } /* read_access_range is like read_access_violation, but returns the number of bytes we can read w/o faulting. that is, it checks an address range and tells us what portion (if any) of the prefix is safe to read without a bus error */ static long read_access_range(const void *addr, long count) { if ((addr >= SRAM_START) && (addr < SRAM_END)) { if ((char *)addr + count < (char *)SRAM_END) return (count); else return ((char *)SRAM_END - (char *)addr); } else if (((char *)addr >= (char *)NVD_START) && ((char *)addr < (char *)NVD_END)) { if ((char *)addr + count < (char *)NVD_END) return (count); else return ((char *)NVD_END - (char *)addr); } else return (0); } /* Convert the memory pointed to by mem into hex, placing result in buf. Return SUCCESS or FAIL. If MAY_FAULT is non-zero, then we should return FAIL in response to a fault; if zero treat a fault like any other fault in the stub. */ static long mem2hex(unsigned char *mem, char *buf, long count, long may_fault) { long ndx; long ndx2; long ch; long incr; unsigned char *location; DTC_RESPONSE status; if (may_fault) { for (ndx = 0, incr = 1; (ndx < count) && (incr > 0); ndx += incr) { status = find_memory(mem, count - ndx, &location, &incr); if (status == OK_TARGET_RESPONSE) { if (incr > 0) { for (ndx2 = 0; ndx2 < incr; ndx2++) { ch = *location++; *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } mem += incr; } else if (incr <= 0) /* should never happen */ { *buf = 0; return (0); } } else if (status == NOT_FOUND_TARGET_RESPONSE) { *buf = 0; return (ndx); /* return amount copied */ } else { *buf = 0; return (0); /* XXX: how do we tell the user the status? */ } } *buf = 0; return (count); } else { for (ndx = 0; ndx < count; ndx++) { ch = *mem++; *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } *buf = 0; return (count); /* we copied everything */ } } static DTC_RESPONSE find_memory(unsigned char *mem, long count, unsigned char **location, long *incr) { DTC_RESPONSE retval; long length; /* figure out how much of the memory range we can read w/o faulting */ count = read_access_range(mem, count); if (count == 0) return (NOT_FOUND_TARGET_RESPONSE); if (curframe.valid) { unsigned char *mem_block; unsigned char *mem_addr; unsigned long mem_size; unsigned long mem_stamp; retval = adbg_find_memory_addr_in_frame(curframe.frame_data, mem, (unsigned long **)&mem_block, (unsigned long **)&mem_addr, &mem_size, &mem_stamp); switch (retval) { case OK_TARGET_RESPONSE: #if 0 printp("FOUND: mem %x block %x addr %x size %d stamp %x\n", mem, mem_block, mem_addr, mem_size, mem_stamp); #endif *location = mem_block + (mem - mem_addr); length = mem_size - (mem - mem_addr);; if (length < count) *incr = length; else *incr = count; break; case NOT_FOUND_TARGET_RESPONSE: case NEAR_FOUND_TARGET_RESPONSE: #if 0 printp("NOT FOUND: mem %x, checking code region\n", mem); #endif /* check to see if it's in the code region */ if ((CS_CODE_START <= (long)mem) && ((long)mem < CS_CODE_START + CS_CODE_SIZE)) { /* some or all of the address range is in the code */ *location = mem; if ((long)mem + count <= CS_CODE_START + CS_CODE_SIZE) *incr = count; /* it's totally in the code */ else /* how much is in the code? */ *incr = CS_CODE_START + CS_CODE_SIZE - (long)mem; #if 0 printp("FOUND in code region: %x\n", mem); #endif retval = OK_TARGET_RESPONSE; } else retval = NOT_FOUND_TARGET_RESPONSE; break; default: #if 0 printp("BAD RETURN: %d\n", retval); #endif retval = NOT_FOUND_TARGET_RESPONSE; break; } } else { *location = mem; *incr = count; retval = OK_TARGET_RESPONSE; } return (retval); } /* Convert the hex array pointed to by buf into binary to be placed in mem. Return SUCCESS or FAIL. */ static long hex2mem( char *buf, unsigned char *mem, long count, long may_fault ) { long i, ch; for ( i=0; i<count; i++ ) { ch = hex( *buf++ ) << 4; ch = ch + hex( *buf++ ); if ( may_fault ) { ch = set_uchar( mem++, ch ); if ( ch < 0 ) /* negative return indicates error */ return FAIL; } else *mem++ = ch; } return SUCCESS; } /**********************************************/ /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ /* RETURN NUMBER OF CHARS PROCESSED */ /**********************************************/ static int hexToInt( char **ptr, unsigned long *intValue ) { long numChars = 0; long hexValue; *intValue = 0; while ( **ptr ) { hexValue = hex( **ptr ); if ( hexValue >=0 ) { *intValue = (*intValue << 4) | hexValue; numChars ++; } else break; (*ptr)++; } return numChars; } static volatile long gdb_handling_trap1; static volatile long gdb_handling_sstrace; static volatile long gdb_signo; /* Here is the "callable" stub entry point. Call this function with a GDB request as an argument, and it will service the request and return. May be further broken up as we go along, with individual requests broken out as separate functions. */ static char * handle_trace_query (char *); static char * handle_trace_set (char *); static int handle_format (char **request, CFD *format); static unsigned long crc32 (unsigned char *buf, int len, unsigned long crc); static char * crc_query (char *); static char * handle_test (char *); void handle_request( char *request ) { #if 0 remote_debug = 2; #endif switch( *request++ ) { case 'k': /* "kill" */ curframe.valid = FALSE; putpacket (""); break; case 'D': /* "detach" */ curframe.valid = FALSE; putpacket (""); break; default: /* Unknown code. Return an empty reply message. */ putpacket( "" ); /* return empty packet */ break; case 'H': /* Set thread for subsequent operations. Hct... c = 'c' for thread used in step and continue; t... can be -1 for all threads. c = 'g' for thread used in other operations. If zero, pick a thread, any thread. */ putpacket( "OK" ); break; case 'g': /* Read registers. Each byte of register data is described by two hex digits. registers are in the internal order for GDB, and the bytes in a register are in the same order the machine uses. */ { /* Return the values in (one of) the registers cache(s). Several situations may pertain: 1) We're synchronous, in which case the "registers" array should actually be current. 2) We're asynchronous, in which case the "registers" array holds whatever was cached most recently. 3) We're looking at a trace frame that was collected earlier: we will return those earlier registers. */ /* all registers default to zero */ memset (outbuffer, '0', NUMREGBYTES); outbuffer[NUMREGBYTES] = '\0'; if (curframe.valid) /* debugging a trace frame */ mem2hex( (unsigned char*) curframe.traceregs, outbuffer, NUMREGBYTES, 0 ); else mem2hex( (unsigned char*) registers, outbuffer, NUMREGBYTES, 0 ); putpacket( outbuffer ); } break; case 'G': /* Write registers. Gxxxxxxxx Each byte of register data is described by two hex digits. */ if (curframe.valid) /* debugging a trace frame */ putpacket ("E03"); /* can't write regs into a trace frame! */ else { /* Write the values into the local registers cache... Note that no actual registers are being changed. */ hex2mem( request, (unsigned char *) registers, NUMREGBYTES, 0 ); putpacket( "OK" ); } break; case 'P': /* Write (single) register. Pnn=xxxxxxxx register nn gets value xxxxxxxx; two hex digits for each byte in the register (target byte order). */ if (curframe.valid) putpacket ("E03"); /* can't write regs into a trace frame! */ else { unsigned long regno; if ( hexToInt( &request, ®no ) && *(request++) == '=' ) { if ( regno < NUMREGS ) { hexToInt( &request, (unsigned long *) ®isters[REGISTER_BYTE(regno)]); putpacket( "OK" ); } else putpacket( "E01" ); /* bad packet or regno */ } } break; case 'm': /* Read memory. mAAAAAAAA,LLLL AAAAAAAA is address, LLLL is length. Reply can be fewer bytes than requested if able to read only part of the data. */ { unsigned long addr, len; if ( hexToInt( &request, &addr ) && *(request++) == ',' && hexToInt( &request, &len ) ) { /* better not overwrite outbuffer! */ if ( len > (BUFMAX / 2) - 5 ) len = (BUFMAX / 2) - 5; if (mem2hex((unsigned char *) addr, outbuffer, len, 1) == 0) /* XXX: eventually use returned value */ putpacket( "E03" ); /* read fault (access denied) */ else putpacket( outbuffer ); /* read succeeded */ } else putpacket( "E01" ); /* badly formed read request */ } break; case 'M': /* Write memory. Maaaaaaaa,llll:xxxx aaaaaaaa is address, llll is length; xxxx is data to write. */ { unsigned long addr, len; if (curframe.valid) /* can't write memory into a trace frame! */ putpacket ("E03"); /* "access denied" */ else /*** if ( write_access_enabled ) ***/ { if ( hexToInt( &request, &addr ) && *(request++) == ',' && hexToInt( &request, &len ) && *(request++) == ':' ) { if (len == 2 && addr >= CS_CODE_START && addr <= LAST_CS_WORD) { unsigned long val; if ( !hexToInt( &request, &val ) || write_to_protected_mem( (void *)addr, val ) ) putpacket( "E03" ); /* write fault (access denied) */ else putpacket( "OK" ); /* write succeeded */ } else { if ( hex2mem( request, (unsigned char*) addr, len, 1 ) ) putpacket( "E03" ); /* write fault (access denied) */ else putpacket( "OK" ); /* write succeeded */ } } else putpacket( "E02" ); /* badly formed write request */ } } break; case 'c': /* Continue. cAAAAAAAA AAAAAAAA is address from which to resume. If omitted, resume at current PC. */ { unsigned long addr; if (curframe.valid) { /* Don't continue if debugging a trace frame! */ gdb_puts ("Error: can't continue!\n"); putpacket ("S03"); } else { gdb_signo = 3; if (isxdigit(request[0])) { hexToInt(&request, &addr); registers[REGISTER_BYTE(PC)] = addr; } gdb_handling_trap1 = FALSE; gdb_handling_sstrace = FALSE; sss_trace_flag = '\0'; } } break; case 's': /* Step. sAAAAAAAA AAAAAAAA is address from which to begin stepping. If omitted, begin stepping at current PC. */ { unsigned long addr; if (curframe.valid) { /* Don't step if debugging a trace frame! */ gdb_puts ("Error: can't step!\n"); putpacket ("S03"); } else { gdb_signo = 3; if (isxdigit(request[0])) { hexToInt(&request, &addr); registers[REGISTER_BYTE(PC)] = addr; } gdb_handling_trap1 = FALSE; gdb_handling_sstrace = FALSE; sss_trace_flag = 't'; } } break; case 'C': /* Continue with signal. Cxx;AAAAAAAA xx is signal number in hex; AAAAAAAA is adddress from which to resume. If ;AAAAAAAA omitted, continue from PC. */ { unsigned long addr = 0; if (!gdb_handling_trap1 || curframe.valid) { /* Don't continue if not currently in synchronous mode, or if currently debugging a trace frame! */ gdb_puts( "Error: can't continue!\n" ); putpacket( "S03" ); /* "sigquit" (better idea?) */ } else { gdb_signo = 3; if ( isxdigit( *request ) ) { hex2mem( request, (unsigned char *) &gdb_signo, 2, 0 ); request += 2; if ( *request == ';' && isxdigit( *++request ) ) { hexToInt( &request, &addr ); registers[REGISTER_BYTE(PC)] = addr; } } gdb_handling_trap1 = FALSE; gdb_handling_sstrace = FALSE; sss_trace_flag = '\0'; } } break; case 'S': /* Step with signal. Sxx;AAAAAAAA xx is signal number in hex; AAAAAAAA is adddress from which to begin stepping. If ;AAAAAAAA omitted, begin stepping from PC. */ { unsigned long addr = 0; if (!gdb_handling_trap1 || curframe.valid) { /* Don't step if not currently in synchronous mode, or if currently debugging a trace frame! */ gdb_puts( "Error: can't step!\n" ); putpacket( "S03" ); /* "sigquit" (better idea?) */ } else { gdb_signo = 3; if ( isxdigit( *request ) ) { hex2mem( request, (unsigned char *) &gdb_signo, 2, 0 ); request += 2; if ( *request == ';' && isxdigit( *++request ) ) { hexToInt( &request, &addr ); registers[REGISTER_BYTE(PC)] = addr; } } gdb_handling_trap1 = FALSE; gdb_handling_sstrace = FALSE; sss_trace_flag = 't'; } } break; case '?': /* Query the latest reason for stopping. Should be same reply as was last generated for step or continue. */ if ( gdb_signo == 0 ) gdb_signo = 3; /* default to SIGQUIT */ outbuffer[ 0 ] = 'S'; outbuffer[ 1 ] = hexchars[ gdb_signo >> 4 ]; outbuffer[ 2 ] = hexchars[ gdb_signo & 0xf ]; outbuffer[ 3 ] = 0; putpacket( outbuffer ); break; case 'd': /* Toggle debug mode I'm sure we can think of something interesting. */ remote_debug = !remote_debug; putpacket( "" ); /* return empty packet */ break; case 'q': /* general query */ switch (*request++) { default: putpacket (""); /* nak a request which we don't handle */ break; case 'T': /* trace query */ putpacket (handle_trace_query (request)); break; case 'C': /* crc query (?) */ if (*request++ == 'R' && *request++ == 'C' && *request++ == ':') putpacket (crc_query (request)); else putpacket (""); /* unknown query */ break; } break; case 'Q': /* general set */ switch (*request++) { default: putpacket (""); /* nak a request which we don't handle */ break; case 'T': /* trace */ putpacket (handle_trace_set (request)); break; } break; case 'T': /* call test function: TAAA,BBB,CCC A, B, and C are arguments to pass to gdb_c_test. Reply is "E01" (bad arguments) or "OK" (test function called). */ putpacket (handle_test (request)); break; } } static TDP_SETUP_INFO tdp_temp; static int trace_running; /* * Function msgcmp: * * If second argument (str) is matched in first argument, * then advance first argument past end of str and return "SAME" * else return "DIFFERENT" without changing first argument. * * Return: zero for DIFFERENT, non-zero for SUCCESS */ static int msgcmp (char **msgp, char *str) { char *next; if (msgp != 0 && str != 0) /* input validation */ if ((next = *msgp) != 0) { for (; *next && *str && *next == *str; next++, str++) ; if (*str == 0) /* matched all of str in msg */ return (int) (*msgp = next); /* advance msg ptr past str */ } return 0; /* failure */ } static char * handle_trace_query (char *request) { if (msgcmp (&request, "Status")) { if (adbg_check_if_active ()) { gdb_puts ("Target trace is running.\n"); return "T1"; } else { gdb_puts ("Target trace not running.\n"); trace_running = 0; return "T0"; } } else /* unknown trace query */ { return ""; } } static void gdb_note (char *fmt, int arg1) { if (remote_debug > 1) { sprintp (spare_buffer, fmt, arg1); gdb_puts (spare_buffer); } } static int error_ret (int ret, char *fmt, int arg1) { if (remote_debug > 0) { sprintp (spare_buffer, fmt, arg1); gdb_puts (spare_buffer); } return ret; } static int handle_format (char **request, COLLECTION_FORMAT_DEF *format) { MEMRANGE_DEF m; DTC_RESPONSE ret; int elinum; unsigned long regnum; long bytecodes[(MAX_BYTE_CODES + sizeof (struct t_expr_tag))/ 4]; struct t_expr_tag *t_expr = (struct t_expr_tag *)bytecodes; if (format->id == 0) { if ((ret = get_unused_format_id (&format->id)) != OK_TARGET_RESPONSE) return dtc_error_ret (-1, "get_unused_format_id", ret); if (**request == 'R') { (*request)++; hexToInt (request, &format->regs_mask); } gdb_note ("STUB: call define_format (id = %d, ", format->id); gdb_note ("regs_mask = 0x%X);\n", format->regs_mask); if ((ret = define_format (format)) != OK_TARGET_RESPONSE) { sprintp (spare_buffer, "'define_format': DTC error '%s' for format id %d.\n", get_err_text (ret), format->id); gdb_puts (spare_buffer); return -1; } } while ((**request == 'M') || (**request == 'X')) { switch (**request) { case 'M': /* M<regnum>,<offset>,<size> */ (*request)++; hexToInt(request, ®num); if (regnum == 0 || regnum == (unsigned long) -1) m.typecode = -1; else if ((elinum = index_to_elinum (regnum)) > 0) m.typecode = elinum; else return error_ret (-1, "Memrange register %d is not between 0 and 15\n", regnum); if (*(*request)++ != ',') return error_ret (-1,"Malformed memrange (comma #%d missing)\n",1); hexToInt(request, &m.offset); if (*(*request)++ != ',') return error_ret (-1,"Malformed memrange (comma #%d missing)\n",2); hexToInt(request, &m.size); gdb_note ("STUB: call add_format_mem_range (typecode = 0x%x, ", m.typecode); gdb_note ("offset = 0x%X, ", m.offset); gdb_note ("size = %d);\n", m.size); if ((ret = add_format_mem_ranges (format->id, &m)) != OK_TARGET_RESPONSE) { dtc_error_ret (-1, "add_format_mem_ranges", ret); sprintp (spare_buffer, "format id %d: memrange (0x%x, 0x%x, 0x%x).\n", format->id, m.typecode, m.offset, m.size); gdb_puts (spare_buffer); return -1; } break; case 'X': /* X<length>,<bytecodes> */ { unsigned long length; (*request)++; hexToInt(request, &length); if ((length <= 0) || (length > MAX_BYTE_CODES)) return error_ret (-1, "Bytecode expression length (%d) too large\n", length); if (*(*request)++ != ',') return error_ret (-1, "Malformed bytecode expr (comma#%d missing)\n", 1); t_expr->next = NULL; /* subtract one to account for expr[0] in header */ t_expr->size = sizeof(struct t_expr_tag) + length - 1; t_expr->expr_size = length; hex2mem(*request, &t_expr->expr[0], length, 0); *request += 2 * length; build_and_add_expression(format->id, t_expr); } break; } } return 0; } static char * handle_trace_set (char *request) { long n_frame; unsigned long frameno, tdp, pc, start, stop; DTC_RESPONSE ret = -1; static COLLECTION_FORMAT_DEF tempfmt1; static char enable; static char retbuf[20]; if (msgcmp (&request, "init")) { gdb_note ("STUB: call clear_trace_state();\n", 0); curframe.valid = 0; /* all old frames become invalid now */ if ((ret = clear_trace_state ()) == OK_TARGET_RESPONSE) return "OK"; else { sprintp (retbuf, "E2%x", ret); return (char *) dtc_error_ret ((int) &retbuf, "clear_trace_state", ret); } } else if (msgcmp (&request, "Start")) { trace_running = 1; curframe.valid = 0; /* all old frames become invalid now */ gdb_note ("STUB: call start_trace_experiment();\n", 0); adbg_save_trace_in_nvd (); if ((ret = start_trace_experiment ()) == OK_TARGET_RESPONSE) return "OK"; else { sprintp (retbuf, "E2%x", ret); return (char *) dtc_error_ret ((int) &retbuf, "start_trace_experiment", ret); } } else if (msgcmp (&request, "Stop")) { trace_running = 0; if (adbg_check_if_active ()) { gdb_note ("STUB: call end_trace_experiment();\n", 0); if ((ret = end_trace_experiment ()) == OK_TARGET_RESPONSE) return "OK"; else { sprintp (retbuf, "E2%x", ret); return (char *) dtc_error_ret ((int) &retbuf, "end_trace_experiment", ret); } } else return "OK"; } /* "TDP:" (The 'T' was consumed in handle_request.) */ else if (msgcmp (&request, "DP:")) { /* TDP:<id>:<addr>:{D,E}:<stepcount>:<pass_limit>{R[M,X]+}<tdp-format> {S{R[M,X]+}}<tp-format> D -- disable tracepoint (illegal from EMC's point of view) E -- enable tracepoint? R -- regs format: R<regs-mask> M -- memory format: M<regnum>,<offset>,<size> X -- expr format: X<size>,<bytecodes> S -- fencepost between trap formats and stepping formats. */ /* state variable, required for splitting TDP packets. */ static int doing_step_formats; /* * TDP: packets may now be split into multiple packets. * If a TDP packet is to be continued in another packet, it * must end in a "-" character. The subsequent continuation * packet will then begin with a "-" character, between the * token "TDP:" and the tdp_id field. The ID and address * will be repeated in each sub-packet. The step_count, * pass_count, and 'enabled' field must appear in the first * packet. The boundary between sub-packets may not appear * between the "S" that denotes the start of stepping "formats", * and the regs_mask that follows it. The split may also not * occur in the middle of either a memrange description or a * bytecode string. -- MVS */ if (*request == '-') /* this is a continuation of a trace definition in progress */ { unsigned long temp_id, temp_addr; request++; if (!(hexToInt (&request, &temp_id) && *request++ == ':')) return "E11"; /* badly formed packet, field 1 */ if (!(hexToInt (&request, (unsigned long *) &temp_addr) && *request++ == ':')) return "E12"; /* badly formed packet, field 2 */ if (temp_id != tdp_temp.id) return "E11"; /* something wrong: field 1 doesn't match */ if (temp_addr != (unsigned long) tdp_temp.addr) return "E12"; /* something wrong: field 2 doesn't match */ } else /* This is a new TDP definition */ { memset ((char *) &tdp_temp, 0, sizeof (tdp_temp)); memset ((char *) &tempfmt1, 0, sizeof (tempfmt1)); doing_step_formats = FALSE; if (!(hexToInt (&request, &tdp_temp.id) && *request++ == ':')) return "E11"; /* badly formed packet, field 1 */ if (!(hexToInt (&request, (unsigned long *) &tdp_temp.addr) && *request++ == ':')) return "E12"; /* badly formed packet, field 2 */ if (!(((enable = *request++) == 'D' || enable == 'E') && *request++ == ':')) return "E13"; /* badly formed packet, field 3 */ #if 0 if (enable == 'D') { gdb_puts ("Disabling of tracepoints not supported by EMC target\n"); return "E20"; } #endif if (!(hexToInt (&request, &tdp_temp.stepcount) && *request++ == ':')) return "E14"; /* badly formed packet, field 4 */ if (!hexToInt (&request, &tdp_temp.pass_limit)) return "E15"; /* badly formed packet, field 5 */ } /* Typically, the first group of collection descriptors refers to the trap collection. There is an "S" token to act as a fencepost between collection descriptors for the trap, and those for the single-stepping. However, when the packet is split up into several packets, this "S" token may already have been seen in a previous sub-packet; so we have to remember it in a state variable. */ if (*request == 'R' || *request == 'M' || *request == 'X') { if (handle_format (&request, &tempfmt1)) return "E16"; if (doing_step_formats) tdp_temp.tp_format_p = tempfmt1.id; else tdp_temp.tdp_format_p = tempfmt1.id; } /* When we see the "S" token, we remember it in a state variable (in case the packet is split up and continued in another message), and discard all current state from the collection "format". */ if (*request == 'S') { doing_step_formats = TRUE; /* discard prev format and start a new one */ memset ((char *) &tempfmt1, 0, sizeof (tempfmt1)); request++; /* Having seen the "S" fencepost, it is now possible that we will see some more collection descriptors pertaining to the stepping collection. */ if (*request == 'R' || *request == 'M' || *request == 'X') { if (handle_format (&request, &tempfmt1)) return "E17"; /* new format ID is tp_format */ tdp_temp.tp_format_p = tempfmt1.id; } } if (*request == '-') /* this TDP definition will be continued. */ sprintp (retbuf, "OK"); else if (enable == 'E') /* end of TDP definition: pass to ADBG (if enabled!) */ { gdb_note ("STUB: call define_tdp (id %d, ", tdp_temp.id); gdb_note ("addr 0x%X, ", (int) tdp_temp.addr); gdb_note ("passc %d, ", tdp_temp.pass_limit); gdb_note ("stepc %d, ", tdp_temp.stepcount); gdb_note ("TDP fmt #%d, ", tdp_temp.tdp_format_p); gdb_note ("TP fmt #%d);\n", tdp_temp.tp_format_p); ret = define_tdp (tdp_temp.id, &tdp_temp, 0); if (ret == OK_TARGET_RESPONSE) { sprintp (retbuf, "OK"); } else { sprintp (spare_buffer, "'define_tdp' returned DTC error '%s' for tracepoint %d.\n", get_err_text (ret), tdp_temp.id); gdb_puts (spare_buffer); sprintp (retbuf, "E2%x", ret); } /* Redundant, but let's try to make sure this state gets discarded. */ { memset ((char *) &tdp_temp, 0, sizeof (tdp_temp)); memset ((char *) &tempfmt1, 0, sizeof (tempfmt1)); } } else /* ADBG_DTC does not support disabled tracepoints -- ignore it. */ gdb_note ("STUB: ignoring disabled tracepoint %d.\n", tdp_temp.id); return retbuf; } else if (msgcmp (&request, "Frame:")) { ret = OK_TARGET_RESPONSE; if (msgcmp (&request, "pc:")) { if (!hexToInt (&request, &pc)) return "E10"; /* badly formed packet */ n_frame = curframe.valid ? curframe.frame_id + 1 : 0; gdb_note ("STUB: call fetch_trace_frame_pc (id %d, ", n_frame); gdb_note ("pc 0x%X);\n", pc); ret = fetch_trace_frame_with_pc (&n_frame, (void *) pc, &curframe.format, &curframe.frame_data); } else if (msgcmp (&request, "tdp:")) { if (!hexToInt (&request, &tdp)) return "E10"; /* badly formed packet */ n_frame = curframe.valid ? curframe.frame_id + 1: 0; gdb_note ("STUB: call fetch_trace_frame_tdp (id %d, ", n_frame); gdb_note ("tdp 0x%X);\n", tdp); ret = fetch_trace_frame_with_tdp (&n_frame, tdp, &curframe.format, &curframe.frame_data); } else if (msgcmp (&request, "range:")) { if (!(hexToInt (&request, &start) && *request++ == ':')) return "E11"; /* badly formed packet, field 1 */ else if (!hexToInt (&request, &stop)) return "E12"; /* badly formed packet, field 2 */ n_frame = curframe.valid ? curframe.frame_id + 1: 0; gdb_note ("STUB: call fetch_trace_frame_range (id %d, ", n_frame); gdb_note ("start 0x%X, ", start); gdb_note ("stop 0x%X);\n", stop); ret = fetch_trace_frame_with_pc_in_range (&n_frame, (void *) start, (void *) stop, &curframe.format, &curframe.frame_data); } else if (msgcmp (&request, "outside:")) { if (!(hexToInt (&request, &start) && *request++ == ':')) return "E11"; /* badly formed packet, field 1 */ else if (!hexToInt (&request, &stop)) return "E12"; /* badly formed packet, field 2 */ n_frame = curframe.valid ? curframe.frame_id + 1: 0; gdb_note ("STUB: call fetch_trace_frame_outside (id %d, ", n_frame); gdb_note ("start 0x%X, ", start); gdb_note ("stop 0x%X);\n", stop); ret = fetch_trace_frame_with_pc_outside (&n_frame, (void *) start, (void *) stop, &curframe.format, &curframe.frame_data); } else /* simple TFind by frame number: */ { if (!hexToInt (&request, &frameno)) return "E10"; /* badly formed packet */ if (frameno != (unsigned long) -1) { gdb_note ("STUB: call fetch_trace_frame (id %d);\n", frameno); ret = fetch_trace_frame (n_frame = frameno, &curframe.format, &curframe.frame_data); #if 0 printp("STUB: fetch_trace_frame: return %d\n", ret); #endif } else /* discard any trace frame, debug "the real world" */ { if (curframe.valid) gdb_note ("STUB: discard current trace frame #%d.\n", curframe.frame_id); curframe.valid = 0; return "OK"; } } if (ret == OK_TARGET_RESPONSE) /* fetch_trace_frame succeeded */ { /* setup for debugging the trace frame */ curframe.valid = 1; curframe.frame_id = n_frame; curframe.tdp_id = curframe.frame_data->id; memset ((char *) &curframe.traceregs, 0, sizeof (curframe.traceregs)); curframe.traceregs[PC] = (unsigned long) curframe.frame_data->program_counter; if (curframe.format) { unsigned long regs_mask = curframe.format->regs_mask; unsigned long *regs, *stack, *mem; unsigned long regno, index = 0; CFD *dummy; if ((ret = get_addr_to_frame_regs_stack_mem (curframe.frame_data, &dummy, ®s, &stack, &mem)) != OK_TARGET_RESPONSE) { curframe.valid = 0; sprintp (retbuf, "E2%x", ret); return (char *) dtc_error_ret ((int) &retbuf, "get_addr_to_frame_regs_stack_mem", ret); } if (remote_debug > 1) { /* echo what we've found to gdb console */ sprintp (spare_buffer, "STUB: Found frame %d, TDP %d, format %d (%s):\n", curframe.frame_id, curframe.tdp_id & 0x7fffffff, curframe.format->id, curframe.tdp_id & 0x80000000 ? "trap frame" : "stepping frame"); gdb_puts (spare_buffer); } /* copy trace frame regs into stub's data format */ for (regno = 0, index = 0; regno < 16; regno++, regs_mask >>= 1) if (regs_mask & 1) /* got a collected register */ { curframe.traceregs[regno] = regs[index++]; if (remote_debug > 1) { sprintp (spare_buffer, " Collected 0x%08x for register %d.\n", curframe.traceregs[regno], regno); gdb_puts (spare_buffer); } } if (remote_debug > 1) { long midx, ridx, len; MEMRANGE_DEF *mrange; unsigned char *data, *base; if (curframe.format->stack_size > 0) { len = curframe.format->stack_size; sprintp (spare_buffer, " Collected %d bytes of stack at 0x%x:\n", len, curframe.traceregs[A7]); gdb_puts (spare_buffer); /* print stack data, but stay under msg len */ if (len >= (NUMREGBYTES/2 - 2)) len = (NUMREGBYTES/2 - 3); mem2hex ((unsigned char *) stack, spare_buffer, len, 0); spare_buffer [len * 2] = '\n'; spare_buffer [len * 2 + 1] = '\0'; /* EOS */ gdb_puts (spare_buffer); } else gdb_puts ("Stack not collected\n"); for (midx = 0; get_addr_to_a_mem_range (curframe.frame_data, midx, &mrange, (void **) &data) == OK_TARGET_RESPONSE; midx++) { if ((mrange->typecode == 0) || (mrange->typecode == (unsigned long) -1)) { sprintp (spare_buffer, " Collected %d bytes at MEM: 0x%x:\n", mrange->size, mrange->offset); base = (unsigned char *) mrange->offset; } else { if ((ridx = elinum_to_index (mrange->typecode)) > 0) base = (unsigned char *) curframe.traceregs[ridx] + (long) mrange->offset; else { sprintp (spare_buffer, "STUB: bad typecode in memrange #%d: (0x%x,0x%x,0x%x).\n", midx, mrange->typecode, mrange->offset, mrange->size); gdb_puts (spare_buffer); continue; } sprintp (spare_buffer, " Collected %d bytes at 0x%x (REG %X + %d):\n", mrange->size, base, mrange->typecode, mrange->offset); } gdb_puts (spare_buffer); len = mrange->size; if (len >= (NUMREGBYTES/2 - 2)) len = (NUMREGBYTES/2 - 3); mem2hex (data, spare_buffer, len, 0); spare_buffer [len * 2] = '\n'; spare_buffer [len * 2 + 1] = '\0'; /* EOS */ gdb_puts (spare_buffer); } } } sprintp (retbuf, "F%xT%x", n_frame, curframe.tdp_id & 0x7fffffff); return retbuf; } else if (ret == NOT_FOUND_TARGET_RESPONSE) { /* Here's a question: if the fetch_trace_frame call failed (which probably means a bad "TFIND" command from GDB), should we remain focused on the previous frame (if any), or should we revert to "no current frame"? */ return "F-1"; } else { sprintp (retbuf, "E2%x", ret); return (char *) dtc_error_ret ((int) &retbuf, "fetch_trace_frame[...]", ret); } } else /* unknown trace command */ { return ""; } } /* Table used by the crc32 function to calcuate the checksum. */ static unsigned long crc32_table[256]; static int crc_mem_err; static unsigned long crc32 (buf, len, crc) unsigned char *buf; int len; unsigned long crc; { crc_mem_err = FALSE; if (! crc32_table[1]) { /* Initialize the CRC table and the decoding table. */ int i, j; unsigned int c; for (i = 0; i < 256; i++) { for (c = i << 24, j = 8; j > 0; --j) c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1); crc32_table[i] = c; } } while (len--) { if (read_access_violation (buf)) { crc_mem_err = TRUE; return -1; } crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf++) & 255]; } return crc; } static char * crc_query (cmd) char *cmd; { unsigned long startmem, len, crc; static char buf[32]; if (hexToInt (&cmd, &startmem) && *cmd++ == ',' && hexToInt (&cmd, &len)) { crc = crc32 ((unsigned char *) startmem, len, 0xffffffff); if (!crc_mem_err) { sprintp (buf, "C%08x", crc); return buf; } /* else error, fall thru */ } sprintp (buf, "E01"); return buf; } static char * handle_test (request) char *request; { ULONG args[7]; int i; /* Parse the arguments, a comma-separated list of hex numbers, into ARGS. Parse at most six arguments. */ i = 1; if (*request != '\0') while (i < 7) { if (! hexToInt (&request, &args[i++])) return "E01"; if (*request == '\0') break; if (*request++ != ',') return "E01"; } /* Fill the rest of the args array with zeros. This is what the INLINES command processor does with omitted arguments. */ for (; i < 7; i++) args[i] = 0; gdb_c_test (args); return "OK"; } /* GDB_TRAP_1_HANDLER By the time this is called, the registers have been saved in "registers", and the interrupt priority has been set to permit serial UART interrupts. However, since no gdb request has yet been received, and there is no equivalent of getpacket for us to wait on, we can't sit here waiting for packets and processing them. In fact, the ONLY thing for us to do here is sit and wait. As gdb sends packet requests, they will handle themselves at the interrupt level. When gdb decides we can continue, it will reset the global variable "gdb_handling_trap1", and we will return (whereupon registers will be restored etc.) */ void gdb_trap_1_handler( void ) { gdb_handling_trap1 = TRUE; sss_trace_flag = '\0'; /* shut off "trace bit" (indirectly) */ gdb_signo = 5; putpacket( "S05" ); while ( gdb_handling_trap1 ) ; return; } void gdb_trace_handler( void ) { sss_trace_flag = '\0'; /* shut off "trace bit" (indirectly) */ gdb_handling_trap1 = TRUE; gdb_handling_sstrace = TRUE; gdb_signo = 5; putpacket( "S05" ); while ( gdb_handling_trap1 ) ; return; }