From 3d5bdf9fba6442332030e68df2aa7880a894842d Mon Sep 17 00:00:00 2001 From: TC Wan Date: Wed, 1 Dec 2010 13:40:54 +0800 Subject: restructure repository --- Debugger/_c_arm_macros.h | 149 +++++ Debugger/debug_comm.S | 286 +++++++++ Debugger/debug_stack.ld | 15 + Debugger/debug_stub.S | 1469 ++++++++++++++++++++++++++++++++++++++++++++++ Debugger/debug_stub.h | 183 ++++++ Debugger/types.h | 46 ++ Debugger/undef_handler.S | 71 +++ 7 files changed, 2219 insertions(+) create mode 100644 Debugger/_c_arm_macros.h create mode 100644 Debugger/debug_comm.S create mode 100644 Debugger/debug_stack.ld create mode 100644 Debugger/debug_stub.S create mode 100644 Debugger/debug_stub.h create mode 100644 Debugger/types.h create mode 100644 Debugger/undef_handler.S (limited to 'Debugger') diff --git a/Debugger/_c_arm_macros.h b/Debugger/_c_arm_macros.h new file mode 100644 index 0000000..f356a25 --- /dev/null +++ b/Debugger/_c_arm_macros.h @@ -0,0 +1,149 @@ +/** @file _c_arm_macros.h + * @brief Define macros to support shared C and ASM headers + * + */ + +/* Copyright (C) 2007,2009 the NxOS developers + * Thanks to Bartli (forum post @ embdev.net ARM programming with GCC/GNU tools forum) + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ + +#ifndef __NXOS_BASE_C_ARM_MACROS__ +#define __NXOS_BASE_C_ARM_MACROS__ + +#ifdef __ASSEMBLY__ + +#define NULL 0x0 /* Stick the definition here instead of making types.h messy */ +#define FALSE 0 +#define TRUE ~FALSE + +#define TYPEDEF @ +#define FUNCDEF @ + + .set last_enum_value, 0 + .macro enum_val name + .equiv \name, last_enum_value + .set last_enum_value, last_enum_value + 1 + .endm + +#define ENUM_BEGIN .set last_enum_value, 0 + +#define ENUM_VAL(name) enum_val name +#define ENUM_VALASSIGN(name, value) \ + .set last_enum_value, value ;\ + enum_val name +#define ENUM_END(enum_name) + +/** Macro to define driver ioctl table + * First five table entries are predefined + * 0: init + * 1: shutdown + * 2: sleep + * 3: wakeup + * 4: poll + * + */ +#define DRIVER_IOCTL(driver) \ +/* Dummy sleep and wakeup routines for now */ ;\ + .set driver ## _sleep, NULL ;\ + .set driver ## _wakeup, NULL ;\ + .set driver ## _poll, NULL ;\ +.data ;\ + .align 4 ;\ + .global driver ## _ioctl ;\ +driver ## _ioctl: ;\ + .word driver ## _init ;\ + .word driver ## _shutdown ;\ + .word driver ## _sleep ;\ + .word driver ## _wakeup ;\ + .word driver ## _poll ;\ + .set num_ ## driver ## _cmds, 5 + +/** Macro to define additional driver ioctl commands + * Be careful to follow the sequence defined for the CMD enums + * The first CMD should have an enum value of 5 + * + */ +#define DRIVER_CMD(driver, cmd) \ + .word driver ## _ ## cmd ;\ + .set num_ ## driver ## _cmds, num_ ## driver ## _cmds + 1 + +/** Macro to define driver state + * MUST BE DEFINED AFTER DRIVER_IOCTL section + * @param driver name of driver + * @param driverenum enum value of driver (in [31:25]) + * + * The number of commands for driver (in [24:17]) -- derived from num_driver_cmds + * + * Format of driver_state table: + * driver signature (driverenum << 24 | numcommands << 16) + * driver parameters (per device instance) + */ +#define DRIVER_STATE(driver, driverenum) \ +.bss ;\ + .global driver ## _state ;\ +driver ## _state: ;\ + .set driver ## _signature, (driverenum << 24) | (num_ ## driver ## _cmds << 16) ;\ + .word NULL /* driver_signature */ + + +/** Macro to define actual driver routine in .S + * On entry: + * r0 - address of driver_state + * r1-r3 - parameters (variable) + * r12 - number of parameters (IPC scratch register) + * Stack - parameters (variable) + */ +#define DRIVER_ROUTINE(driver, cmd) \ + .global driver ## _ ## cmd ## ;\ +driver ## _ ## cmd ## : + +#else +/** Macro to control typedef generation + * + */ +#define TYPEDEF typedef + +/** Macro to control extern generation + * + */ +#ifndef FUNCDEF +#define FUNCDEF extern +#endif + +/** Macro to control typedef enum generation + * + */ +#define ENUM_BEGIN typedef enum { + +/** Macro to specify enum instance (auto value assignment) + * + */ +#define ENUM_VAL(name) name, + +/** Macro to control enum specification and value assignment +* +*/ +#define ENUM_VALASSIGN(name, value) name = value, + +/** Macro to control enum named type generation + * + */ +#define ENUM_END(enum_name) } enum_name; + +#endif + +ENUM_BEGIN +ENUM_VAL(INIT) /**< Driver Init Routine. */ +ENUM_VAL(SHUTDOWN) /**< Driver Shutdown Routine. */ +ENUM_VAL(SLEEP) /**< Driver Sleep Routine. */ +ENUM_VAL(WAKEUP) /**< Driver Wakeup Routine. */ +ENUM_VAL(POLL) /**< Driver Poll Routine. */ +ENUM_END(nx_driver_default_cmd) + + +#endif /* __NXOS_BASE_C_ARM_MACROS__ */ diff --git a/Debugger/debug_comm.S b/Debugger/debug_comm.S new file mode 100644 index 0000000..af69391 --- /dev/null +++ b/Debugger/debug_comm.S @@ -0,0 +1,286 @@ + +/* Copyright (C) 2007-2010 the NxOS developers + * + * Module Developed by: TC Wan + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ + +#define __ASSEMBLY__ +#include "debug_stub.h" + +.data +.align 4 + +hex2char_lut: + .ascii "0123456789ABCDEF" + +/* Macros + */ + +/* _asciiz + * Terminate string given string buffer pointer in \addrptr + * reg is used as a scratch register (destroyed) + * + */ + .macro _asciiz reg, strptr + mov \reg, #0 /* NULL character */ + strb \reg, [\strptr] /* Terminate ASCIIZ string */ + .endm + +/* _hex2char_lut + * Internal routine to intialize the LUT address pointer + */ + .macro _hex2char_lut addrptr + ldr \addrptr, =hex2char_lut + .endm + +/* _hex2char_cont + * Internal routine that assumes that the LUT has been loaded. + * This macro accepts a byte sized hex value as a parameter register(7:0) and returns the + * ASCII equivalent in in the same register(7:0) + * The second parameter is the LUT address pointer register to use (assumed to be initialized) + * WARNING: Assumes that the value in register is sanity checked before invoking macro + */ + .macro _hex2char_cont reg, addrptr + ldrb \reg, [\addrptr, \reg] + .endm + +/* _hex2char + * This macro accepts a byte sized hex value as a parameter register(7:0) and returns the + * ASCII equivalent in in the same register(7:0) + * The second parameter is the LUT address pointer register to use (register content is destroyed) + * WARNING: Assumes that the value in register is sanity checked before invoking macro + */ + .macro _hex2char reg, addrptr + _hex2char_lut \addrptr + _hex2char_cont \reg, \addrptr + .endm + +/* _char2hex + * This macro accepts an ASCII char as a parameter register(7:0) and returns the + * equivalent byte sized hex value in in the same register(7:0) + * WARNING: Assumes that the char in register is a valid hex char before invoking macro + */ + .macro _char2hex reg + cmp \reg, #'A' /* If Alpha */ + bichs \reg, \reg, #ASCII_LOWER2UPPER_MASK /* Convert to Uppercase */ + subhs \reg, \reg, #7 /* Adjustment to allow for subtraction with 0x30 */ + sub \reg, \reg, #0x30 /* get final hex value */ + .endm + + +.code 32 +.text +.align 4 + + +/* Utility Routines + */ + +/* hex2char + * This routine accepts a byte sized hex value in R0(7:0) and returns the + * ASCII equivalent in R0(7:0) + */ + .global hex2char + +hex2char: + stmfd sp!, {r1,lr} + and r0, #NIBBLE0 /* make sure that input is sane */ + _hex2char r0, r1 + ldmfd sp!, {r1,pc} + +/* char2hex + * This routine accepts an ASCII character in R0(7:0) and returns the + * equivalent byte sized hex value in R0(7:0) + */ + .global char2hex + +char2hex: + and r0, #BYTE0 /* make sure that input is sane */ + cmp r0, #'0' + blo exit_char2hex + cmp r0, #'F' + bhi exit_char2hex + _char2hex r0 +exit_char2hex: + bx lr + +/* byte2ascii_cont + * This routine accepts a byte value in R0(7:0), and a ASCII buffer pointer in R1, + * and stores the ASCII equivalent byte value in the buffer pointed to by R1. + * Note: On return, R1 points to next empty char slot in buffer (i.e., R1 is modified) + * and R0 is destroyed. + */ +byte2ascii_cont: + stmfd sp!, {r2,r3,r4, lr} + mov r2, r0, lsl #24 /* Keep copy of input byte value R0(7:0), shifted to MSB R2(31:24) */ + mov r4, #2 /* Loop counter */ + _hex2char_lut r3 /* initialize LUT pointer */ +1: mov r0, r2, ror #28 /* Rotate MSNibble R2(31:28) into LSNibble position R0(3:0) */ + and r0, r0, #NIBBLE0 /* Mask out everything else */ + _hex2char_cont r0, r3 /* Convert nibble to ASCII char */ + strb r0, [r1], #1 + subs r4, r4, #1 /* decrement loop counter */ + bne 1b + ldmfd sp!, {r2,r3,r4, pc} + +/* byte2ascii + * This routine accepts a byte value in R0(7:0), and a ASCII buffer pointer in R1, + * and stores the ASCII equivalent byte value in the buffer pointed to by R1. + * Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character) + */ + .global byte2ascii + +byte2ascii: + stmfd sp!, {r1, lr} /* Keep ASCII buffer pointer */ + and r0, #BYTE0 /* sanitize input */ + bl byte2ascii_cont + _asciiz r0, r1 + ldmfd sp!, {r0, pc} /* return string pointer in R0 */ + +/* halfword2ascii + * This routine accepts a halfword value in R0(15:0), and a ASCII buffer pointer in R1, + * and returns the ASCIIZ equivalent byte value in the buffer pointed to by R0. + * Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character) + */ + .global halfword2ascii +halfword2ascii: + stmfd sp!, {r1,r2,r3, lr} /* Keep ASCII buffer pointer */ + mov r2, r0, lsl #16 /* copy of input halfword value R0(15:0), shifted to MSH R2(31:16) */ + mov r3, #2 /* Loop Counter */ + b _conv_byte2ascii /* goto Byte conversion loop */ + +/* word2ascii + * This routine accepts a word value in R0(31:0), and a ASCII buffer pointer in R1, + * and returns the ASCIIZ equivalent byte value in the buffer pointed to by R0. + * Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character) + */ + .global word2ascii +word2ascii: + stmfd sp!, {r1,r2,r3, lr} /* Keep ASCII buffer pointer */ + mov r2, r0 /* copy of input word value R0(31:0) */ + mov r3, #4 /* Loop Counter */ + + /* Fall through to byte coversion loop */ + +_conv_byte2ascii: + mov r0, r2, ror #24 /* Rotate MSB R2(31:24) into LSB position R0(7:0) */ + and r0, #BYTE0 /* Mask out everything else */ + bl byte2ascii_cont + subs r3, r3, #1 + bne _conv_byte2ascii + _asciiz r0, r1 + ldmfd sp!, {r0,r2,r3, pc} + + +/* ascii2byte + * This routine accepts an ASCII buffer pointer in R0, + * and returns the byte value in R0(7:0). + * Note: On return, R1 points to the ASCII buffer location after the current 2 chars. + * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, + * otherwise it will return invalid results. + */ + .global ascii2byte + +ascii2byte: + stmfd sp!, {r2,r3, lr} + mov r3, #2 /* Loop counter */ + b _conv_ascii2byte + +/* ascii2halfword + * This routine accepts an ASCII buffer pointer in R0, + * and returns the word value in R0(15:0). + * Note: On return, R1 points to the ASCII buffer location after the current 4 chars. + * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, + * otherwise it will return invalid results. + */ + .global ascii2halfword + +ascii2halfword: + stmfd sp!, {r2,r3, lr} + mov r3, #4 /* Loop counter */ + b _conv_ascii2byte + + +/* ascii2word + * This routine accepts an ASCII buffer pointer in R0, + * and returns the word value in R0(31:0). + * Note: On return, R1 points to the ASCII buffer location after the current 8 chars. + * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, + * otherwise it will return invalid results. + */ + .global ascii2word + +ascii2word: + stmfd sp!, {r2,r3, lr} + mov r3, #8 /* Loop counter */ + + /* Fall through to byte coversion loop */ + +_conv_ascii2byte: + teq r0, #0 + beq _exit_conv_ascii2byte /* exit if NULL pointer in R0 */ + mov r0, r1 /* Copy of ASCII buffer pointer */ + mov r2, #0 /* Initialize results */ +2: ldrb r0, [r1], #1 /* Load ASCII char */ + bl char2hex /* on return, hex value in R0 */ + orr r2, r0, r2, lsl #4 /* merge Nibble into results */ + subs r3, r3, #1 + bne 2b + mov r0, r2 /* Copy it to R0 as return value */ +_exit_conv_ascii2byte: + ldmfd sp!, {r2,r3, pc} /* return hex value in R0 */ + + + + +/* Debugger Communications Routines + * It does not make sense to pass information from the Debugger Module to the Comm. link one character + * at a time, especially if we're not using a native serial interface (e.g., EIA-232). Consequently + * a Message interface has been defined. This can still call getChar() and putChar() subroutines + * if so desired, but it'll be a purely internal matter. + * + */ + .global dbg__hasDebugMsg +/* dbg__hasDebugMsg + * Checks for pending Debugger Message (Non-Blocking). + * On exit: + * r0: Boolean (0: no pending message, 1: has pending message) + */ +dbg__hasDebugMsg: + bx lr + + .global dbg__getDebugMsg +/* dbg__getDebugMsg + * Returns Debugger Message to calling routine after verifying and removing checksum (Blocking). + * On entry: + * r0: address of message buffer + * r1: maximum size of message buffer (incl NULL character) + * On exit: + * r0: address of message buffer with NULL terminated message, excluding '#' + * (NULL if message error) + * + */ +dbg__getDebugMsg: + bx lr + + .global dbg__putDebugMsg +/* dbg__putDebugMsg + * Sends Debugger Message from calling routine after appending checksum (Blocking) . + * On entry: + * r0: address of message buffer with NULL terminated message, without '#' + * On exit: + * r0: status (0: success, -1: error) + */ +dbg__putDebugMsg: + bx lr + + +/* Private functions (if needed) */ +_dbg__getChar: +_dbg__putChar: + bx lr diff --git a/Debugger/debug_stack.ld b/Debugger/debug_stack.ld new file mode 100644 index 0000000..df27512 --- /dev/null +++ b/Debugger/debug_stack.ld @@ -0,0 +1,15 @@ +/* The following linker definitions should be placed in the stack section */ + + /* debugger state */ + __debugger_stack_bottom__ = . ; + . += 0x48; /* 16 user mode registers + SPSR + BKPT Instr Addr */ + __debugger_stack__ = .; + __debugger_stack_top__ = . ; + + /* breakpoints */ + __breakpoints_start__ = . ; + . += 0x40; /* Single Stepping Breakpoint + 7 Breakpoints */ + __breakpoints_end__ = . ; + +/* Symbols */ + __breakpoints_num__ = (__breakpoints_end__ - __breakpoints_start__) / 8; diff --git a/Debugger/debug_stub.S b/Debugger/debug_stub.S new file mode 100644 index 0000000..98bc968 --- /dev/null +++ b/Debugger/debug_stub.S @@ -0,0 +1,1469 @@ + +/* Copyright (C) 2007-2010 the NxOS developers + * + * Module Developed by: TC Wan + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ + + /* GDB sparc-stub.c comments header included below to document GDB Server Remote protocol */ + /* This header has been modified to include additional commands not documented in the header stub */ + + /**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or it's performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * 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 trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * GrrrrRRRR.. set the value of the CPU registers OK or ENN + * where register values are given as + * 32-bit hex values in the sequence: + * User CPSR, R0, R1, ..., R15 + * px get the value of one register (x) hex data or ENN + * Px=rrrr set the value of one register (x) to OK or ENN + * 32-bit hex value rrrr. + * x = ['0','F'] for R0-R15, '!' for User CPSR + * + * 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) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * 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#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + /* Modified GDB Server Remote Protocol definition from GDB's sparc-stub.c Comment Header included above */ + + +/* FIXME: What about setting/clearing Breakpoints? */ + +#define __ASSEMBLY__ +#include "debug_stub.h" + +.bss +.align 4 +debug_state: + .word 0x0 +debug_curr_breakpoint: + .word 0x0 +debug_InMsgBuf: + .space MSGBUF_SIZE,0 +debug_OutMsgBuf: + .space MSGBUF_SIZE,0 + +.data +.align 4 +debug_ValidResponsePrefix: + .byte '+','$',0 + +debug_ErrorResponsePrefix: + .byte '-','$','E',0 + +debug_SignalResponsePrefix: + .byte '+','$','S',0 + +debug_OkResponse: + .byte '+','$','O','K',0 + +/* The CmdIndexTable and CmdJumpTable must be kept in sync */ +debug_cmdIndexTable: + .byte 'g','G','p','P','m','M','c','s','k','?',0 + +/* Command Handlers + * On entry: + * R0: Input Message Parameter Buffer address pointer (points to contents after '$' and '') + */ +debug_cmdJumpTable: + .word _dbg__procGetRegs /* 'g' */ + .word _dbg__procSetRegs /* 'G' */ + .word _dbg__procGetOneReg /* 'p' */ + .word _dbg__procSetOneReg /* 'P' */ + .word _dbg__nop /* 'm' */ + .word _dbg__nop /* 'M' */ + .word _dbg__nop /* 'c' */ + .word _dbg__nop /* 's' */ + .word _dbg__nop /* 'k' */ + .word _dbg__nop /* '?' */ + .word 0 + + +.code 32 +.text +.align 4 + .extern __breakpoints_num__ + .extern dbg__hasDebugMsg /* Check for message from the communications link */ + .extern dbg__getDebugMsg /* Read a message from the communications link */ + .extern dbg__putDebugMsg /* Write a message to the communications link */ + +/* _dbg_jumpTableHandler + * Call Jump Table Routine based on Index + * On entry: + * jumptableaddr is the address (constant) of the Jump Table + * jumpreg is the register used to perform the indirect jump + * indexreg contains jump table index value + */ + .macro _dbg_jumpTableHandler jumptableaddr, jumpreg, indexreg + + ldr \jumpreg, =\jumptableaddr + ldr \jumpreg, [\jumpreg, \indexreg, lsl #2] + mov lr, pc + bx \jumpreg /* Call Command Handler Routine */ + .endm + + +/* _dbg_stpcpy + * _dbg_stpcpy macro + * On entry: + * deststrptr: Destination string [Cannot be R0] + * sourcestrptr: Source string [Cannot be R0] + * On exit: + * deststrptr: Pointer to NULL character in destination string + * R0: destroyed + */ + .macro _dbg_stpcpy deststrptr, sourcestrptr +1: ldrb r0, [\sourcestrptr], #1 + strb r0, [\deststrptr], #1 + teq r0, #0 + bne 1b + sub \deststrptr, \deststrptr, #1 /* Adjust Destination string pointer to point at NULL character */ + .endm + +/* _dbg_outputMsgValidResponse + * Return Message with valid response ('+$') + * On exit: + * R0: destroyed + * R1: points to NULL character after the prefix + * R2: destroyed + */ + .macro _dbg_outputMsgValidResponse + ldr r1, =debug_OutMsgBuf + ldr r2, =debug_ValidResponsePrefix + _dbg_stpcpy r1, r2 + .endm + + +/* _dbg_outputMsgStatusOk + * Return Message with Ok ('+OK') status + * On exit: + * R0: destroyed + * R1: destroyed + * R2: destroyed + */ + .macro _dbg_outputMsgStatusOk + ldr r1, =debug_OutMsgBuf + ldr r2, =debug_OkResponse + _dbg_stpcpy r1, r2 + .endm + +/* _dbg_outputMsgStatusErr + * Return Message with Error ('-ENN') status + * On entry: + * R0: register containing error value (byte) + * On exit: + * R0: destroyed + * R1: destroyed + * R2: destroyed + * R3: destroyed + */ + .macro _dbg_outputMsgStatusErr + mov r3, r0 + ldr r1, =debug_OutMsgBuf + ldr r2, =debug_ErrorResponsePrefix + _dbg_stpcpy r1, r2 + mov r0, r3 + bl byte2ascii /* R1 points to NULL character after the prefix */ + .endm + +/* _dbg_outputMsgStatusSig + * Return Message with Signal ('+SNN') status + * On entry: + * R0: register containing error value (byte) + * On exit: + * R0: destroyed + * R1: destroyed + * R2: destroyed + * R3: destroyed + */ + .macro _dbg_outputMsgStatusSig + mov r3, r0 + ldr r1, =debug_OutMsgBuf + ldr r2, =debug_SignalResponsePrefix + _dbg_stpcpy r1, r2 + mov r0, r3 + bl byte2ascii /* R1 points to NULL character after the prefix */ + .endm + +/* _index2dbgstackaddr + * Convert debugger stack index to Debugger Stack register address + * + * On entry: + * indexreg contains debugger stack index value (0-max entries) + * On exit: + * indexreg: Breakpoint index (preserved) + * addrreg: Debugger Stack Register Address + */ + .macro _index2dbgstackaddr indexreg, addrreg + ldr \addrreg, =__debugger_stack_bottom__ + add \addrreg, \addrreg, \indexreg, lsl #2 /* Calculate Debugger Stack Register Address */ + .endm + +/* _index2bkptindex_addr + * Convert Breakpoint index to breakpoing entry address + * + * On entry: + * indexreg contains breakpoint index value + * On exit: + * indexreg: Breakpoint index (preserved) + * addrreg: Breakpoint Entry Address + */ + .macro _index2bkptindex_addr indexreg, addrreg + ldr \addrreg, =__breakpoints_start__ + add \addrreg, \addrreg, \indexreg, lsl #3 /* Calculate Breakpoint Entry Address */ + .endm + +/* _dbg_getstate + * Get Debugger State + * On exit: + * reg: Debugger State enum + */ + .macro _dbg_getstate reg + ldr \reg, =debug_state + ldr \reg, [\reg] + .endm + +/* _dbg_setstate + * Set Debugger State to given value + * On exit: + * r0, r1: destroyed + */ + .macro _dbg_setstate state + ldr r0, =\state + ldr r1, =debug_state + str r0, [r1] + .endm + +/* _dbg_getcurrbkpt_index + * Get current breakpoint index + * On exit: + * reg: Breakpoint index + */ + .macro _dbg_getcurrbkpt_index reg + ldr \reg, =debug_curr_breakpoint + ldr \reg, [\reg] + .endm + +/* _dbg_setcurrbkpt_index + * Set current breakpoint index + * On exit: + * r1: destroyed + */ + .macro _dbg_setcurrbkpt_index reg + ldr r1, =debug_curr_breakpoint + str \reg, [r1] + .endm + +/* _dbg_getabortedinstr_addr + * Get aborted instruction address + * On exit: + * reg: aborted instruction address + */ + .macro _dbg_getabortedinstr_addr reg + ldr \reg, =__debugger_stack_bottom__ + ldr \reg, [\reg] + .endm + +/* _dbg_setabortedinstr_addr + * Set aborted instruction address + * On exit: + * r1: destroyed + */ + .macro _dbg_setabortedinstr_addr reg + ldr r1, =__debugger_stack_bottom__ + str \reg, [r1] + .endm + + + +/* The Debugger Interface can handle a total of (n-1) Breakpoint States and 1 Single Stepping State, + * where n is a power of 2. The value of n is given by __breakpoints_num__ defined in the linker file. + * + * In addition, a Debugger Stack contains the User Mode Register Stack Frame + SPSR + Bkpt Instr Addr. + * These are currently stored in the .stack area in RAM, so there is no fixed address + * location that is used for this purpose. + * + * The Breakpoint feature assumes that the program is executed in RAM. It is not possible + * to set dynamic breakpoints for programs executed from Flash in the AT91SAM7S which lacks + * instruction breakpointing support in hardware without using JTAG. The only type of breakpoints + * that can be supported in Flash based programs are Static (predefined) breakpoints inserted into + * the code. + * + * Each Breakpoint State i is a struct comprising the Breakpoint Address + Memory Contents + * stored in 8 bytes as: + * [High Memory Address] + * ADDR [i*8+4]: Memory Contents (32 bits) + * ADDR [i*8]: Breakpoint Address (31 bits, b0 = THUMB flag [not implemented yet]) + * [Low Memory Address] + * + * A Non-zero Breakpoint Address means that the breakpoint is active, whereas the memory contents + * contains the instruction which resided at that address initially (now replaced by a BKPT + * instruction). + * Note: Currently it is not possible to resume execution of a program with breakpoints enabled + * after a RESET, since the RESET will clear all contents of the stack, destroying the instruction + * contained in a given breakpoint. + * Fortunately the NXT will also need to reload the program into RAM so this is not expected to be + * an issue. + * + * The Memory Map for the Debugger State is as follows: + * + * [High Memory Address] __breakpoints_end__ + * Breakpoint 07 State + * Breakpoint 06 State + * ... + * Breakpoint 02 State + * Breakpoint 01 State + * Single Step State __debugger_stack__ / __breakpoints_start__ + * User Mode R15 + * User Mode R14 + * ... + * User Mode R02 + * User Mode R01 + * User Mode R00 + * User Mode CPSR (UNDEF SPSR) + * Bkpt Instr Addr __debugger_stack_bottom__ + * [Low Memory Address] + * + * Each Breakpoint State will initially be zeroed. + * + */ + +/**************************************************************************** + * + * GDB Debugger Init and Breakpoint Handler Routines + * + ****************************************************************************/ + + .global dbg__bkpt_init +/* dbg__bkpt_init + * GDB set_debug_traps() routine + */ +dbg__bkpt_init: + stmfd sp!, {lr} + bl _dbg__clear_breakpoints + mov r0, #0 + ldr r1, =debug_curr_breakpoint + str r0, [r1] + ldr r1, =debug_InMsgBuf + strb r0, [r1] + ldr r1, =debug_OutMsgBuf + strb r0, [r1] + +/* FIXME: Initialize other stuff here */ + _dbg_setstate DBG_INIT + ldmfd sp!, {pc} + + +/* _dbg__flush_icache + * Flush the Instruction cache + * Defined by GDB Stub, but not needed for ARMv4T architecture + */ +_dbg__flush_icache: + /* nop */ + bx lr + + + .global dbg__thumb_bkpt_handler +/* dbg__thumb_bkpt_handler + * GDB handle_exception() routine (Thumb Mode) + */ +dbg__thumb_bkpt_handler: +/* On entry, r0 contains breakpoint index value */ + mov r4, #BKPT16_AUTO_BKPT + and r4, r0, #BKPT16_AUTO_BKPT /* keep AUTO flag value in r4 */ + bic r0, r0, #BKPT16_AUTO_BKPT /* mask out AUTO flag */ + _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */ + ldr r1, =BKPT16_MANUAL_BKPT + teq r0, r1 + beq _process_manual_breakpoint_thumb + ldr r1, =__breakpoints_num__ + cmp r0, r1 /* Sanity check that index is in range */ + bhs dbg__bkpt_offset_outofrange +/* Valid index value found */ + teq r4, #0 /* Check if AUTO flag set */ + bne _process_auto_breakpoint +/* else */ + _dbg_setstate DBG_NORMAL_BKPT_THUMB + b _process_normal_breakpoint + + .global dbg__arm_bkpt_handler +/* dbg__arm_bkpt_handler + * GDB handle_exception() routine (ARM Mode) + */ +dbg__arm_bkpt_handler: +/* On entry, r0 contains breakpoint index value */ + mov r4, #BKPT32_AUTO_BKPT + and r4, r0, #BKPT32_AUTO_BKPT /* keep AUTO flag value in r4 */ + bic r0, r0, #BKPT32_AUTO_BKPT /* mask out AUTO flag */ + _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */ + ldr r1, =BKPT32_MANUAL_BKPT + teq r0, r1 + beq _process_manual_breakpoint_arm + ldr r1, =__breakpoints_num__ + cmp r0, r1 /* Sanity check that index is in range */ + bhs dbg__bkpt_offset_outofrange +/* Valid index value found */ + teq r4, #0 /* Check if AUTO flag set */ + bne _process_auto_breakpoint +/* else */ + _dbg_setstate DBG_NORMAL_BKPT_ARM +/* b _process_normal_breakpoint */ + +_process_normal_breakpoint: + bl _dbg__restore_breakpoints + bl _dbg__restore_singlestep + bl _dbg__clear_singlestep + bl _dbg__flush_icache + b dbg__bkpt_waitCMD + +_process_auto_breakpoint: +/* Load Auto BKPT for Breakpoint index given in r0 */ + _index2bkptindex_addr r0, r1 /* Calculate Breakpoint Entry Address */ + ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ + teq r1, #0 /* Check that Breakpoint is active */ + beq dbg__bkpt_inactive + bl _dbg__activate_one_breakpoint + bl _dbg__restore_singlestep + bl _dbg__clear_singlestep + b __dbg__resume_execution + +_process_manual_breakpoint_thumb: + _dbg_setstate DBG_MANUAL_BKPT_THUMB + b dbg__bkpt_waitCMD + +_process_manual_breakpoint_arm: + _dbg_setstate DBG_MANUAL_BKPT_ARM +/* b dbg__bkpt_waitCMD */ + +dbg__bkpt_inactive: +/* b dbg__bkpt_waitCMD */ + +dbg__bkpt_offset_outofrange: +/* b dbg__bkpt_waitCMD */ + + .global dbg__bkpt_waitCMD +/* dbg__bkpt_waitCMD + * GDB Stub Remote Command Handler + */ + +/**************************************************************************** + * + * GDB Server Command Processing Routines + * + ****************************************************************************/ +dbg__bkpt_waitCMD: +1: bl dbg__hasDebugMsg /* Check for messages */ + beq 1b /* Busy wait */ + bl dbg__getDebugMsg /* Read new message from Debugger, message buffer addr in R0, NULL if error */ + teq r0, #0 + moveq r0, #MSG_ERRCHKSUM /* Message invalid, checksum error? */ + beq _dbg__cmdError /* Send response to GDB server */ +/* Message now has $\0 */ + mov r4, r0 /* Use R4 as Message Buffer pointer */ + ldrb r0, [r4], #1 /* Look for '$' */ + teq r0, #MSGBUF_STARTCHAR + movne r0, #MSG_ERRFORMAT /* Message Format invalid (not '$') */ + bne _dbg__cmdError /* Shouldn't happen */ + ldrb r0, [r4], #1 /* Look for command char */ + bl _dbg__cmdChar2Index /* Index in R0 */ + ldr r1, =MSGBUF_CMDINDEX_OUTOFRANGE_VAL + teq r0, r1 + moveq r0, #MSG_UNKNOWNCMD /* Out of range, Command character not recognized */ + beq _dbg__cmdError /* Send response to GDB server */ + +_dbg__cmdExists: + mov r3, r0 /* put Command Handler Index in R3 */ + mov r0, r4 /* R0 now contains Input Message Buffer Parameter Pointer (previously in R4) */ + _dbg_jumpTableHandler debug_cmdJumpTable, r2, r3 /* Call Command Handler Routine, use R2 as jump address pointer */ + b dbg__bkpt_waitCMD + +_dbg__cmdError: + _dbg_outputMsgStatusErr + bl dbg__putDebugMsg /* Send error response to the GDB server */ + b dbg__bkpt_waitCMD + + +/* _dbg__cmdChar2Index + * Convert Command Character to Jump Table Index + * On entry: + * r0: command character + * On exit: + * r0: jump table index (-1 for command not found) + * R1: destroyed + * R2: destroyed + * R3: destroyed + */ +_dbg__cmdChar2Index: + mov r1, r0 /* Copy command character to r1 */ + mov r0, #0 /* Clear return value */ + ldr r3, =debug_cmdIndexTable /* Convert command to index using r3 as Index Lookup Address Pointer */ +1: ldrb r2, [r3, r0] /* Get table entry */ + teq r2, #0 + moveq r0, #MSGBUF_CMDINDEX_OUTOFRANGE_VAL /* End of Index Table, Not found */ + beq _exit_cmdIndexTable + teq r1, r2 + addne r0, #1 /* Increment Index */ + bne 1b /* No match, skip to next command char */ +_exit_cmdIndexTable: + bx lr + +/* __dbg__procCmdParamError + * Common subroutine exit stub to handle Command Parameter Error for Command Handlers + * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. + * + */ + +__dbg__procCmdParamError: + mov r0, #MSG_UNKNOWNPARAM + _dbg_outputMsgStatusErr + bl dbg__putDebugMsg /* Send error response to the GDB server */ + ldmfd sp!, {pc} + + + +/* _dbg__procGetOneReg + * Get One Register Value Command Handler + * Valid register parameter is from '0' to 'F' for User Mode Registers R0-R15 + * CPSR register parameer is '!' + * On entry: + * r0: parameter buffer pointer (contents after '$' and '') + * + */ +_dbg__procGetOneReg: + stmfd sp!, {lr} + ldrb r2, [r0, #1] /* char after parameter value (Should be NULL character) */ + ldrb r0, [r0] /* Retrieve register index parameter to R0 */ + teq r2, #0 /* Check for NULL */ + bne __dbg__procCmdParamError /* Unexpected input, report error */ + teq r0, #MSGBUF_CPSRREG /* Check for CPSR register indicator */ + moveq r0, #DBGSTACK_USERCPSR_OFFSET /* Put offset from User Registers (-1) into index, so that after adjustment it points to CPSR slot */ + beq _dbg__procRegister /* Handle User CPSR */ + bl char2hex /* Convert to Hex value (assume input is valid) */ + cmp r0, #NIBBLE0 /* sanity check, (though it is not foolproof as input char in 0x0-0xF (ctrl-chars) will pass through) */ + bhi __dbg__procCmdParamError /* Non-hex char, report error */ + +_dbg__procRegister: + mov r3, r0 /* Keep register index safe */ + _dbg_outputMsgValidResponse /* Setup R1 with address of output message buffer data pointer (after response prefix) */ + mov r0, r3 /* Restore register index value */ + bl _dbg_outputOneRegValue /* update output buffer */ + bl dbg__putDebugMsg /* Send response to the GDB server */ + ldmfd sp!, {pc} + +/* _dbg_outputOneRegValue + * Given Register Index (-1: CPSR, 0-F: R0-R15), output hex char to buffer + * On entry: + * r0: register index (-1, 0-F) + * r1: output message buffer pointer + * On exit: + * r0: output message buffer pointer + * r1: updated (points to NULL character at end of Output Buffer) + * r2: destroyed + */ +_dbg_outputOneRegValue: + stmfd sp!, {lr} + add r2, r0, #DBGSTACK_USERREG_INDEX /* Convert register index to Debug Stack index */ + _index2dbgstackaddr r2, r0 /* Calculate address pointer to relevant register, result in R0 */ + ldr r0, [r0] /* Retrieve Register contents into R0 */ + bl word2ascii /* Convert and put hex chars into Output Message Buffer */ + ldmfd sp!, {pc} + +/* _dbg__procGetRegs + * Get All Register Values Command Handler + * Output Buffer returns register values in the order: User CPSR, R0, R1, R2, ..., R15 + * On entry: + * r0: parameter buffer pointer (contents after '$' and '') + */ +_dbg__procGetRegs: + stmfd sp!, {lr} + ldrb r0, [r0] /* Retrieve register index parameter to R0 */ + teq r0, #0 /* Check for NULL */ + bne __dbg__procCmdParamError /* Unexpected input, report error */ + + _dbg_outputMsgValidResponse /* Setup R1 with address of output message buffer data pointer (after response prefix) */ + mov r3, #DBGSTACK_USERCPSR_OFFSET /* Output User CPSR Value first */ +1: mov r0, r3 + bl _dbg_outputOneRegValue /* update output buffer */ + add r3, r3, #1 /* increment index */ + cmp r3, #0xF + ble 1b /* process all the registers */ + + bl dbg__putDebugMsg /* Send response to the GDB server */ + ldmfd sp!, {pc} + + +/* _dbg__nop + * NOP Command Handler (placeholder) + * On entry: + * r0: parameter buffer (contents after '$' and '') + */ +_dbg__nop: + stmfd sp!, {lr} + mov r0, #MSG_ERRIMPL /* Stub, not implemented yet */ + _dbg_outputMsgStatusErr + bl dbg__putDebugMsg /* Send error response to the GDB server */ + ldmfd sp!, {pc} + + +/* dbg__cmd_install_breakpoint + * Configure Breakpoint + * On entry: + * r0: index of breakpoint to install + * r1: instruction address to install + */ +dbg__cmd_install_breakpoint: + bl _dbg__install_one_breakpoint /* r0: index, r1: instruction address */ + b dbg__bkpt_waitCMD + +/* dbg__cmd_clear_breakpoint + * Clear Breakpoint + * On entry: + * r0: index of breakpoint to clear + */ +dbg__cmd_clear_breakpoint: + _index2bkptindex_addr r0, r0 /* Calculate Breakpoint Entry Address */ + bl _dbg__clear_one_breakpoint + b dbg__bkpt_waitCMD + + +/* dbg__cmd_run + * Continue execution of program + */ +dbg__cmd_run: + bl _dbg__activate_breakpoints + b __dbg__resume_execution + +/* dbg__cmd_step + * Single Step execution of program + */ +dbg__cmd_step: + bl _dbg_next_instruction_addr /* next instruction address returned in r1 */ + bl _dbg__install_singlestep /* Setup Single Step */ + bl _dbg__activate_singlestep + b __dbg__resume_execution + +/* dbg__cmd_cont + * Continue execution of program. + * If this is a Normal Breakpoint, then we need to install an Autobreakpoint at next instruction address + * and resume from current (Breakpoint) exception address + * Else (it is a Manual Breakpoint) + * We need to resume from the next instruction address + */ +dbg__cmd_cont: +/* FIXME: What happens if we call this when we did not stop at a Breakpoint previously? */ + _dbg_getstate r0 + ldr r1, =DBG_MANUAL_BKPT_ARM + teq r0, r1 + beq __dbg_is_manual_breakpoint + + bl _dbg_next_instruction_addr /* next instruction address returned in r1 */ + bl _dbg__install_singlestep /* Setup Single Step, next instruction address returned in r1 */ + _dbg_getcurrbkpt_index r0 /* load current breakpoint index in memory */ + bl _dbg__activate_autobreakpoint /* pass next instruction address in r1 */ + b __dbg__resume_execution + +__dbg_is_manual_breakpoint: + bl _dbg_next_instruction_addr /* Skip Manual Breakpoint Instruction(s) */ + bl _dbg__activate_breakpoints + b __dbg__resume_execution + +/**************************************************************************** +// Selected Routines from the eCos arm_stub.c related to next instruction address +// determination in ARM processors. + +//======================================================================== +// +// arm_stub.c +// +// Helper functions for stub, generic to all ARM processors +// +//======================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//======================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Red Hat, gthomas +// Contributors: Red Hat, gthomas, jskov +// Date: 1998-11-26 +// Purpose: +// Description: Helper functions for stub, generic to all ARM processors +// Usage: +// +//####DESCRIPTIONEND#### +// +//======================================================================== + + +static int +ins_will_execute(unsigned long ins) +{ + unsigned long psr = get_register(PS); // condition codes + int res = 0; + switch ((ins & 0xF0000000) >> 28) { + case 0x0: // EQ + res = (psr & PS_Z) != 0; + break; + case 0x1: // NE + res = (psr & PS_Z) == 0; + break; + case 0x2: // CS + res = (psr & PS_C) != 0; + break; + case 0x3: // CC + res = (psr & PS_C) == 0; + break; + case 0x4: // MI + res = (psr & PS_N) != 0; + break; + case 0x5: // PL + res = (psr & PS_N) == 0; + break; + case 0x6: // VS + res = (psr & PS_V) != 0; + break; + case 0x7: // VC + res = (psr & PS_V) == 0; + break; + case 0x8: // HI + res = ((psr & PS_C) != 0) && ((psr & PS_Z) == 0); + break; + case 0x9: // LS + res = ((psr & PS_C) == 0) || ((psr & PS_Z) != 0); + break; + case 0xA: // GE + res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) || + ((psr & (PS_N|PS_V)) == 0); + break; + case 0xB: // LT + res = ((psr & (PS_N|PS_V)) == PS_N) || + ((psr & (PS_N|PS_V)) == PS_V); + break; + case 0xC: // GT + res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) || + ((psr & (PS_N|PS_V)) == 0); + res = ((psr & PS_Z) == 0) && res; + break; + case 0xD: // LE + res = ((psr & (PS_N|PS_V)) == PS_N) || + ((psr & (PS_N|PS_V)) == PS_V); + res = ((psr & PS_Z) == PS_Z) || res; + break; + case 0xE: // AL + res = TRUE; + break; + case 0xF: // NV + if (((ins & 0x0E000000) >> 24) == 0xA) + res = TRUE; + else + res = FALSE; + break; + } + return res; +} + +static unsigned long +RmShifted(int shift) +{ + unsigned long Rm = get_register(shift & 0x00F); + int shift_count; + if ((shift & 0x010) == 0) { + shift_count = (shift & 0xF80) >> 7; + } else { + shift_count = get_register((shift & 0xF00) >> 8); + } + switch ((shift & 0x060) >> 5) { + case 0x0: // Logical left + Rm <<= shift_count; + break; + case 0x1: // Logical right + Rm >>= shift_count; + break; + case 0x2: // Arithmetic right + Rm = (unsigned long)((long)Rm >> shift_count); + break; + case 0x3: // Rotate right + if (shift_count == 0) { + // Special case, RORx + Rm >>= 1; + if (get_register(PS) & PS_C) Rm |= 0x80000000; + } else { + Rm = (Rm >> shift_count) | (Rm << (32-shift_count)); + } + break; + } + return Rm; +} + +// Decide the next instruction to be executed for a given instruction +static unsigned long * +target_ins(unsigned long *pc, unsigned long ins) +{ + unsigned long new_pc, offset, op2; + unsigned long Rn; + int i, reg_count, c; + + switch ((ins & 0x0C000000) >> 26) { + case 0x0: + // BX or BLX + if ((ins & 0x0FFFFFD0) == 0x012FFF10) { + new_pc = (unsigned long)get_register(ins & 0x0000000F); + return ((unsigned long *)new_pc); + } + // Data processing + new_pc = (unsigned long)(pc+1); + if ((ins & 0x0000F000) == 0x0000F000) { + // Destination register is PC + if ((ins & 0x0FBF0000) != 0x010F0000) { + Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); + if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch! + if ((ins & 0x02000000) == 0) { + op2 = RmShifted(ins & 0x00000FFF); + } else { + op2 = ins & 0x000000FF; + i = (ins & 0x00000F00) >> 8; // Rotate count + op2 = (op2 >> (i*2)) | (op2 << (32-(i*2))); + } + switch ((ins & 0x01E00000) >> 21) { + case 0x0: // AND + new_pc = Rn & op2; + break; + case 0x1: // EOR + new_pc = Rn ^ op2; + break; + case 0x2: // SUB + new_pc = Rn - op2; + break; + case 0x3: // RSB + new_pc = op2 - Rn; + break; + case 0x4: // ADD + new_pc = Rn + op2; + break; + case 0x5: // ADC + c = (get_register(PS) & PS_C) != 0; + new_pc = Rn + op2 + c; + break; + case 0x6: // SBC + c = (get_register(PS) & PS_C) != 0; + new_pc = Rn - op2 + c - 1; + break; + case 0x7: // RSC + c = (get_register(PS) & PS_C) != 0; + new_pc = op2 - Rn +c - 1; + break; + case 0x8: // TST + case 0x9: // TEQ + case 0xA: // CMP + case 0xB: // CMN + break; // PC doesn't change + case 0xC: // ORR + new_pc = Rn | op2; + break; + case 0xD: // MOV + new_pc = op2; + break; + case 0xE: // BIC + new_pc = Rn & ~op2; + break; + case 0xF: // MVN + new_pc = ~op2; + break; + } + } + } + return ((unsigned long *)new_pc); + case 0x1: + if ((ins & 0x02000010) == 0x02000010) { + // Undefined! + return (pc+1); + } else { + if ((ins & 0x00100000) == 0) { + // STR + return (pc+1); + } else { + // LDR + if ((ins & 0x0000F000) != 0x0000F000) { + // Rd not PC + return (pc+1); + } else { + Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); + if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch! + if (ins & 0x01000000) { + // Add/subtract offset before + if ((ins & 0x02000000) == 0) { + // Immediate offset + if (ins & 0x00800000) { + // Add offset + Rn += (ins & 0x00000FFF); + } else { + // Subtract offset + Rn -= (ins & 0x00000FFF); + } + } else { + // Offset is in a register + if (ins & 0x00800000) { + // Add offset + Rn += RmShifted(ins & 0x00000FFF); + } else { + // Subtract offset + Rn -= RmShifted(ins & 0x00000FFF); + } + } + } + return ((unsigned long *)*(unsigned long *)Rn); + } + } + } + return (pc+1); + case 0x2: // Branch, LDM/STM + if ((ins & 0x02000000) == 0) { + // LDM/STM + if ((ins & 0x00100000) == 0) { + // STM + return (pc+1); + } else { + // LDM + if ((ins & 0x00008000) == 0) { + // PC not in list + return (pc+1); + } else { + Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); + if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch! + offset = ins & 0x0000FFFF; + reg_count = 0; + for (i = 0; i < 15; i++) { + if (offset & (1<> 12) { + case 0x4: + // Check for BX or BLX + if ((ins & 0xff07) == 0x4700) + new_pc = (unsigned long)get_register((ins & 0x00078) >> 3); + break; + case 0xb: + // push/pop + // Look for "pop {...,pc}" + if ((ins & 0xf00) == 0xd00) { + // find PC + sp = (unsigned long)get_register(SP); + + for (offset = i = 0; i < 8; i++) + if (ins & (1 << i)) + offset += 4; + + new_pc = *(cyg_uint32 *)(sp + offset); + + if (!v5T_semantics()) + new_pc = MAKE_THUMB_ADDR(new_pc); + } + break; + case 0xd: + // Bcc | SWI + // Use ARM function to check condition + arm_ins = ((unsigned long)(ins & 0x0f00)) << 20; + if ((arm_ins & 0xF0000000) == 0xF0000000) { + // SWI + new_pc = CYGNUM_HAL_VECTOR_SOFTWARE_INTERRUPT * 4; + } else if (ins_will_execute(arm_ins)) { + offset = (ins & 0x00FF) << 1; + if (ins & 0x0080) offset |= 0xFFFFFE00; // sign extend + new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset); + } + break; + case 0xe: + // check for B + if ((ins & 0x0800) == 0) { + offset = (ins & 0x07FF) << 1; + if (ins & 0x0400) offset |= 0xFFFFF800; // sign extend + new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset); + } + break; + case 0xf: + // BL/BLX (4byte instruction!) + // First instruction (bit 11 == 0) holds top-part of offset + if ((ins & 0x0800) == 0) { + offset = (ins & 0x07FF) << 12; + if (ins & 0x0400) offset |= 0xFF800000; // sign extend + // Get second instruction + // Second instruction (bit 11 == 1) holds bottom-part of offset + ins = *(unsigned short*)(pc+2); + // Check for BL/BLX + if ((ins & 0xE800) == 0xE800) { + offset |= (ins & 0x07ff) << 1; + new_pc = (unsigned long)(pc+4) + offset; + // If its BLX, force a full word alignment + // Otherwise, its a thumb address. + if (!(ins & 0x1000)) + new_pc &= ~3; + else + new_pc = MAKE_THUMB_ADDR(new_pc); + } + } + break; + } + + return new_pc; +} + +void __single_step (void) +{ + unsigned long pc = get_register(PC); + unsigned long cpsr = get_register(PS); + + // Calculate address of next instruction to be executed + if (cpsr & CPSR_THUMB_ENABLE) { + // thumb + ss_saved_pc = target_thumb_ins(pc, *(unsigned short*)pc); + } else { + // ARM + unsigned long curins = *(unsigned long*)pc; + if (ins_will_execute(curins)) { + // Decode instruction to decide what the next PC will be + ss_saved_pc = (unsigned long) target_ins((unsigned long*)pc, + curins); + } else { + // The current instruction will not execute (the conditions + // don't hold) + ss_saved_pc = pc+4; + } + } + + // Set breakpoint according to type + if (IS_THUMB_ADDR(ss_saved_pc)) { + // Thumb instruction + unsigned long t_pc = UNMAKE_THUMB_ADDR(ss_saved_pc); + ss_saved_thumb_instr = *(unsigned short*)t_pc; + *(unsigned short*)t_pc = HAL_BREAKINST_THUMB; + } else { + // ARM instruction + ss_saved_instr = *(unsigned long*)ss_saved_pc; + *(unsigned long*)ss_saved_pc = HAL_BREAKINST_ARM; + } +} + + ****************************************************************************/ + + +/* _dbg_next_instruction_addr + * Determine the address of the next instruction to execute. + * On exit: + * R1: Instruction Address (31 bits, b0 = THUMB flag) + * + * Here we make use of the Debugger Stack which contains the address of the aborted instruction that will be reexecuted + * when we resume the program. + * + * If it is a Manual Breakpoint inserted into the code, then we will need to update the aborted instruction + * address to skip the current aborted instruction and resume execution at the next instruction address, + * and the next instruction address to be returned to the calling routine is the following instruction + * address instead. + * + * We need to check the aborted instruction type, to see if it is a branch instruction, before we can determine + * the next instruction address (for inserting a Breakpoint). + */ +_dbg_next_instruction_addr: +/* We assume that any BKPT instructions in the code will be Manual Breakpoints, + * i.e., the Debugger does not leave stray Single Step / Auto / Normal breakpoints in memory + */ + + mov r2, #DBGSTACK_USERCPSR_INDEX /* Retrieve User CPSR */ + _index2dbgstackaddr r2, r0 /* Calculate address pointer to relevant register, result in R0 */ + ldr r0, [r0] /* Retrieve Register contents into R0 */ + and r4, r0, #CPSR_THUMB /* store Thumb Mode status in R4 */ + + _dbg_getabortedinstr_addr r2 /* Retrieve aborted instruction address */ +1: teq r4, #0 /* Check if it is ARM or Thumb instruction */ + ldrneh r0, [r2] + ldrne r1, =(BKPT16_INSTR | BKPT16_MANUAL_BKPT) /* check for Thumb Manual Breakpoint Instruction */ + ldreq r0, [r2] + ldreq r1, =(BKPT32_INSTR | BKPT32_MANUAL_BKPT) /* check for ARM Manual Breakpoint Instruction */ + teq r0, r1 + bne 2f /* Not Manual breakpoint */ + teq r4, #0 /* Check if it is ARM or Thumb instruction */ + addne r2, r2, #2 /* Is Manual Breakpoint, Skip to next Thumb instruction */ + addeq r2, r2, #4 /* Is Manual Breakpoint, Skip to next ARM instruction */ + _dbg_setabortedinstr_addr r2 /* Update aborted instruction address */ + b 1b /* To protect against a sequence of Manual Breakpoint Instructions */ + +/* Here, r0 contains the instruction which will be reexecuted when program resumes. We need to dissect it to see if + * it is a branch instruction. + */ +2: +@@@@@@@@@ + bx lr + +/* __dbg__resume_execution + * cleanup, resume execution of program. + * Restore User Mode Regsiters from Debugger Stack, and resume execution from aborted instruction + */ +__dbg__resume_execution: +@@@@@@ + bl _dbg__flush_icache + b __dbg__resume_execution + + + +/**************************************************************************** + * + * Breakpoint Manipulation Routines + * + ****************************************************************************/ + +/* _dbg__clear_singlestep + * Clear the Single Step Breakpoint + */ +_dbg__clear_singlestep: + ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */ +/* b _dbg__clear_one_breakpoint */ + +/* _dbg__clear_one_breakpoint + * On entry, R0 contains the Breakpoint State slot address to be cleared + * + */ +_dbg__clear_one_breakpoint: + mov r1, #0 + mov r2, #0 + stmea r0!, {r1, r2} /* clear Breakpoint state */ + bx lr + +/* _dbg__clear_breakpoints + * Routine iterates through the array of breakpoints (incl single step breakpoint) and clears the breakpoint + */ +_dbg__clear_breakpoints: + stmfd sp!, {lr} + ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */ + ldr r3, =__breakpoints_end__ /* start from top of the table */ +3: bl _dbg__clear_one_breakpoint + cmp r0, r3 + blo 3b + ldmfd sp!, {pc} + +/* _dbg__install_singlestep + * Install the Single Step Breakpoint + * On entry: + * R1: Instruction Address (31 bits, b0 = THUMB flag) + */ +_dbg__install_singlestep: + mov r0, #0 +/* b _dbg__install_one_breakpoint */ + +/* _dbg__install_one_breakpoint + * Install breakpoint entry into Breakpoint State Table + * On entry: + * R0: Breakpoint index (assumed valid) + * R1: Instruction Address (31 bits, b0 = THUMB flag) + * + * On exit: + * R2: Breakpoint Instruction + * R3: Breakpoint Entry address + */ +_dbg__install_one_breakpoint: +/* Check for Thumb bit */ + tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ +/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ + ldreq r2, [r1] /* if 0: load ARM instruction from address location */ + ldrneh r2, [r1] /* else load Thumb instruction */ + _index2bkptindex_addr r0, r3 /* Calculate Breakpoint Entry Address */ + stm r3, {r1, r2} + bx lr + + +/* _dbg__restore_singlestep + * Restores the contents of the single step breakpoint to memory + */ +_dbg__restore_singlestep: + mov r0, #0 /* single step breakpoint index */ + _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */ + ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ + teq r1, #0 + bxeq lr /* Exit if not active */ +/* b _dbg__restore_one_breakpoint */ + +/* _dbg__restore_one_breakpoint + * Restores the contents to memory for one breakpoint + * On entry: + * R0: Breakpoint index (assumed valid) [not used -- can be used for validating BKPT] + * R1: Breakpoint Address (assumed valid) + * R2: Breakpoint Instruction (assumed valid) + */ +_dbg__restore_one_breakpoint: +/* Check for Thumb bit */ + tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ +/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ + streq r2, [r1] /* if 0: restore ARM instruction to address location */ + bicne r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */ + strneh r2, [r1] /* store Thumb instruction */ + bx lr + +/* _dbg__restore_breakpoints + * Routine iterates through the array of breakpoints (incl single step breakpoint) and restores the contents to memory + * Only Active breakpoints (i.e., Non-zero Address) are processed. + */ +_dbg__restore_breakpoints: + stmfd sp!, {lr} + ldr r5, =_dbg__restore_one_breakpoint + b __dbg__iterate_breakpoint_array + +/* _dbg__activate_singlestep + * Activate the single step breakpoint to memory + */ +_dbg__activate_singlestep: + mov r0, #0 /* single step breakpoint index */ + _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */ + ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ + teq r1, #0 + bxeq lr /* Exit if not active */ +/* b _dbg__activate_one_breakpoint */ + +/* _dbg__activate_one_breakpoint + * Activate one breakpoint to memory + * On entry: + * R0: Breakpoint index (assumed valid) + * R1: Breakpoint Address (assumed valid) + * R2: Breakpoint Instruction (assumed valid) + */ +_dbg__activate_one_breakpoint: +/* Check for Thumb bit */ + tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ + bne _nx_is_thumb_bp +_nx_is_arm_bp: +/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ + ldr r3, [r1] /* if 0: load ARM instruction from address location */ + teq r2, r3 /* check that the two instructions are identical */ + bne _dbg__breakpoint_invalid_arm + ldr r2, =BKPT32_INSTR /* ARM BKPT instruction */ + and r2, r2, r0 /* Merge Breakpoint index */ + str r2, [r1] /* Store it into memory location */ +_dbg__breakpoint_invalid_arm: + bx lr +_nx_is_thumb_bp: + bic r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */ + ldrh r3, [r1] /* load Thumb instruction from address location */ + teq r2, r3 /* check that the two instructions are identical */ + bne _dbg__breakpoint_invalid_thumb + ldr r2, =BKPT16_INSTR /* Thumb BKPT instruction */ + and r2, r2, r0 /* Merge Breakpoint index */ + strh r2, [r1] /* Store it into memory location */ +_dbg__breakpoint_invalid_thumb: + bx lr + +/* _dbg__activate_breakpoints + * Routine iterates through the array of breakpoints (incl single step breakpoint) and activates them + * Only Active breakpoints (i.e., Non-zero Address) are processed. + */ +_dbg__activate_breakpoints: + stmfd sp!, {lr} + ldr r5, =_dbg__activate_one_breakpoint + b __dbg__iterate_breakpoint_array + + +/* __dbg__iterate_breakpoint_array + * Common routine iterates through the array of breakpoints (incl single step breakpoint) + * and executes routine given in R5, passing: + * R0: Breakpoint index + * R1: Breakpoint Address + * R2: Breakpoint Instruction + * + * On Entry: + * Assumes that lr has been push to stack (routine can't be called directly) + * + * Only Active breakpoints (i.e., Non-zero Address entries) are processed. + */ +__dbg__iterate_breakpoint_array: + ldr r4, =__breakpoints_end__ /* start from top of the table (Assume __breakpoints_end__ > __breakpoints_start__) */ + ldr r3, =__breakpoints_start__ /* end address check */ + ldr r0, =__breakpoints_num__ /* Number of Breakpoints (incl Single Step) (Assume Non-Zero) */ +4: sub r0, r0, #1 /* Decrement breakpoint index in r0 */ + ldmea r4!, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ + teq r1, #0 /* Is it active? */ + movne lr, pc + bxne r5 /* active entry */ + cmp r4, r3 + bhi 4b /* if (pointer > start of Breakpoint Table address), get next slot */ + ldmfd sp!, {pc} + +/* _dbg__activate_autobreakpoint + * Activate all other breakpoints except current breakpoint, activate auto breakpoint in next instr slot + * On entry: + * R0: Current Breakpoint index (assumed valid) + * R1: Next Instruction address (for AUTO Breakpoint) [Not used, assume Single Step Breakpoint already has correct info] + */ +_dbg__activate_autobreakpoint: + stmfd sp!, {lr} + mov r5, r0 /* Keep Current Breakpoint Index in r5 */ + ldr r4, =__breakpoints_end__ /* start from top of the table */ + ldr r0, =__breakpoints_num__ /* Number of Breakpoints (incl Single Step) (Assume Non-Zero) */ +4: subs r0, r0, #1 /* Decrement breakpoint index in r0 */ + ldmea r4!, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ + bls 5f /* Flag set by subs instruction previously. Reached Single Step, go activate AUTO Breakpoint */ + teq r0, r5 /* Is it the Current Breakpoint? */ + beq 4b /* Yes, so skip */ + teq r1, #0 /* Is it active? */ + blne _dbg__activate_one_breakpoint /* active entry */ + b 4b /* Next iteration */ +5: +/* Here, r1: Breakpoint Address, r2: Breakpoint Instruction */ + tst r1, #BKPT_STATE_THUMB_FLAG /* Check for Thumb bit -- 1: Thumb instruction */ + orreq r0, r5, #BKPT32_AUTO_BKPT /* Is ARM Instruction, merge AUTO flag with Current Breakpoint Index */ + orrne r0, r5, #BKPT16_AUTO_BKPT /* Is Thumb Instruction, merge AUTO flag with Current Breakpoint Index */ + bl _dbg__activate_one_breakpoint /* Activate AUTO Breakpoint */ + ldmfd sp!, {pc} + diff --git a/Debugger/debug_stub.h b/Debugger/debug_stub.h new file mode 100644 index 0000000..7849d56 --- /dev/null +++ b/Debugger/debug_stub.h @@ -0,0 +1,183 @@ +/** @file debug_stub.h + * @brief Shared C/ASM header file for debugger stub + * + */ + +/* Copyright (C) 2007-2010 the NxOS developers + * + * Module Developed by: TC Wan + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ + +#ifndef __DEBUG_STUB_H__ +#define __DEBUG_STUB_H__ + +#include "_c_arm_macros.h" + +#ifndef __ASSEMBLY__ +#include "types.h" +#endif + +/** @addtogroup debugger */ +/*@{*/ + + +/* Declarations go here. */ +/** @name Debug Message Constants. + * + * Debug Message Values + */ +/*@{*/ +#define MSGBUF_SIZE 256 /* Debug Message Buffer Size */ +#define MSGBUF_STARTCHAR '$' +#define MSGBUF_ACKCHAR '+' +#define MSGBUF_NAKCHAR '-' +#define MSGBUF_ERRCHAR 'E' +#define MSGBUF_SIGCHAR 'S' +#define MSGBUF_CPSRREG '!' +#define MSGBUF_SETCHAR '=' +#define MSGBUF_CMDINDEX_OUTOFRANGE_VAL -1 + +/*@}*/ +/** @name Debug Stack Constants. + * + * Debug Stack Manipulation Values + */ +/*@{*/ +#define DBGSTACK_USERCPSR_OFFSET (DBGSTACK_USERCPSR_INDEX-DBGSTACK_USERREG_INDEX) /* = -1, offset for calculating Debug Stack index */ +#define DBGSTACK_USERCPSR_INDEX 1 /* User CPSR (SPSR_UNDEF) is at index 1 from bottom of Debug Stack */ +#define DBGSTACK_USERREG_INDEX 2 /* R0 starts at index 2 from bottom of Debug Stack */ +/*@}*/ + +/** @name Bitmask Definitions. + * + * Various Bitmasks used for data manipulation. + */ +/*@{*/ +#define BKPT_STATE_THUMB_FLAG 0x01 /* Flag Thumb Breakpoint */ +#define ASCII_LOWER2UPPER_MASK 0x20 /* ASCII Conversion bitmask */ +#define NIBBLE0 0x0000000F /* Nibble 0 word(3:0) */ +#define NIBBLE1 0x000000F0 /* Nibble 1 word(7:4) */ +#define NIBBLE2 0x00000F00 /* Nibble 2 word(11:8) */ +#define NIBBLE3 0x0000F000 /* Nibble 3 word(15:12) */ +#define NIBBLE4 0x000F0000 /* Nibble 4 word(19:16) */ +#define NIBBLE5 0x00F00000 /* Nibble 5 word(23:20) */ +#define NIBBLE6 0x0F000000 /* Nibble 6 word(27:24) */ +#define NIBBLE7 0xF0000000 /* Nibble 7 word(31:28) */ +#define BYTE0 0x000000FF /* Byte 0 word(7:0) */ +#define BYTE1 0x0000FF00 /* Byte 1 word(15:8) */ +#define BYTE2 0x00FF0000 /* Byte 2 word(23:16) */ +#define BYTE3 0xFF000000 /* Byte 3 word(31:24) */ +#define HLFWRD0 0x0000FFFF /* Halfword 0 word(15:0) */ +#define HLFWRD1 0xFFFF0000 /* Halfword 0 word(31:16) */ +/*@}*/ + +/** @name CPSR Bit Definitions. + * + * Various Bit definitions for accessing the CPSR register. + */ +/*@{*/ +#define CPSR_THUMB 0x00000020 +#define CPSR_FIQ 0x00000040 +#define CPSR_IRQ 0x00000080 + +/*@}*/ + +/** @name BKPT suppport constants + * + * ARM and Thumb Breakpoint Instructions. + */ +/*@{*/ +#define BKPT32_INSTR 0xE1200070 /* ARM BKPT instruction */ +#define BKPT32_ENUM_MASK 0x000FFF0F /* ARM BKPT Enum Mask */ +#define BKPT32_AUTO_BKPT 0x00080000 /* ARM BKPT Auto-Step Flag (for CONT support) */ +#define BKPT32_MANUAL_BKPT 0x0007FF0F /* Manually inserted ARM Breakpoint */ + +#define BKPT16_INSTR 0xBE00 /* Thumb BKPT instruction (not supported currently) */ +#define BKPT16_ENUM_MASK 0x00FF /* Thumb BKPT Enum Mask */ +#define BKPT16_AUTO_BKPT 0x0080 /* Thumb BKPT Auto-Step Flag (for CONT support) */ +#define BKPT16_MANUAL_BKPT 0x007F /* Manually inserted Thumb Breakpoint */ +/*@}*/ + +/** Debugger State Enums + * + * Debugger State. + * The enums must be consecutive, starting from 0 + */ +ENUM_BEGIN +ENUM_VALASSIGN(DBG_RESET, 0) /**< Initial State. */ +ENUM_VAL(DBG_INIT) /**< Debugger Initialized. */ +ENUM_VAL(DBG_MANUAL_BKPT_ARM) /**< Manual ARM Breakpoint. */ +ENUM_VAL(DBG_NORMAL_BKPT_ARM) /**< Normal ARM Breakpoint (Single Step, Normal). */ +ENUM_VAL(DBG_MANUAL_BKPT_THUMB) /**< Manual Thumb Breakpoint. */ +ENUM_VAL(DBG_NORMAL_BKPT_THUMB) /**< Normal Thumb Breakpoint (Single Step, Normal). */ +ENUM_END(dbg_state_t) + +/** Debugger Message Error Enums + * + * Debugger Error Message Enums. + * The enums must be consecutive, starting from 1 + */ +ENUM_BEGIN +ENUM_VALASSIGN(MSG_ERRIMPL, 0) /**< Stub (not implemented) Error. */ +ENUM_VAL(MSG_ERRCHKSUM) /**< Checksum Error. */ +ENUM_VAL(MSG_ERRFORMAT) /**< Message Format Error. */ +ENUM_VAL(MSG_UNKNOWNCMD) /**< Unrecognized Command Error. */ +ENUM_VAL(MSG_UNKNOWNPARAM) /**< Unrecognized Parameter Error. */ +ENUM_END(dbg_msg_errno) + + +#ifndef __ASSEMBLY__ + +/* Define C stuff */ +/** @defgroup debug_public */ +/*@{*/ + + +/** Initialize Debugger. + * Equivalent to GDB set_debug_traps() routine + */ +FUNCDEF void dbg__bkpt_init(void); + +/** Debugger Handler Routine (called by Exception Handler Trap). + * Equivalent to GDB handle_exception() routine + */ +FUNCDEF void dbg__bkpt_handler(void); + +/** dbg_breakpoint_arm. + * Equivalent to GDB breakpoint() routine for ARM code + */ +FUNCDEF inline void dbg_breakpoint_arm(void) { asm volatile (".word BKPT32_INSTR | BKPT32_MANUAL_BKPT") } + +/** dbg_breakpoint_thumb. + * Equivalent to GDB breakpoint() routine for Thumb code + */ +FUNCDEF inline void dbg_breakpoint_thumb(void) { asm volatile (".hword BKPT16_INSTR | BKPT16_MANUAL_BKPT") } + +/*@}*/ + +#else +/* Define Assembly stuff */ + +/* dbg__bkpt_arm + * GDB breakpoint() for ARM mode + */ + .macro dbg__bkpt_arm + .word (BKPT32_INSTR | BKPT32_MANUAL_BKPT) + .endm + +/* dbg__bkpt_arm + * GDB breakpoint() for Thumb mode + */ + .macro dbg__bkpt_thumb + .hword (BKPT16_INSTR | BKPT16_MANUAL_BKPT) + .endm + +#endif + /*@}*/ + +#endif /* __DEBUG_STUB_H__ */ diff --git a/Debugger/types.h b/Debugger/types.h new file mode 100644 index 0000000..3a1d4cb --- /dev/null +++ b/Debugger/types.h @@ -0,0 +1,46 @@ +/** @file types.h + * @brief Basic type definitions for the Arm7 platform. + */ + +/* Copyright (c) 2007,2008 the NxOS developers + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ + +#ifndef __NXOS_BASE_TYPES_H__ +#define __NXOS_BASE_TYPES_H__ + +/** @addtogroup typesAndUtils */ +/*@{*/ + +typedef unsigned char U8; /**< Unsigned 8-bit integer. */ +typedef signed char S8; /**< Signed 8-bit integer. */ +typedef unsigned short U16; /**< Unsigned 16-bit integer. */ +typedef signed short S16; /**< Signed 16-bit integer. */ +typedef unsigned long U32; /**< Unsigned 32-bit integer. */ +typedef signed long S32; /**< Signed 32-bit integer. */ + +#ifndef __SIZE_TYPE__ +#define __SIZE_TYPE__ U32 /**< Used to go conform with gcc, otherwise we are + risking an error because of conflicting types for size_t */ +#endif +typedef __SIZE_TYPE__ size_t; /**< Abstract size type, needed by the memory allocator. */ + +typedef U8 bool; /**< Boolean data type. */ +#define FALSE (0) /**< False boolean value. */ +#define TRUE (!FALSE) /**< True boolean value. */ + +#ifndef NULL +/** Definition of the NULL pointer. */ +#define NULL ((void*)0) +#endif + +/** A function that takes no arguments and returns nothing. */ +typedef void (*nx_closure_t)(void); + +/*@}*/ + +#endif diff --git a/Debugger/undef_handler.S b/Debugger/undef_handler.S new file mode 100644 index 0000000..c160179 --- /dev/null +++ b/Debugger/undef_handler.S @@ -0,0 +1,71 @@ + +/* Copyright (C) 2007-2010 the NxOS developers + * + * Module Developed by: TC Wan + * + * See AUTHORS for a full list of the developers. + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) version 2. + */ +#define __ASSEMBLY__ +#include "debug_stub.h" + +#define MODE_SVC 0x13 /* Supervisor mode. */ + +.text +.code 32 +.align 0 + + .extern dbg__thumb_bkpt_handler + .extern dbg__arm_bkpt_handler + + .global undef_handler + +undef_handler: +/* Remote GDB Debugger relies on BKPT instruction being trapped here + In ARMv4t, it is an Illegal (Undefined) Instruction. + On triggering, lr (R14) contains the previous mode's pc (R15). + Based on example in Hohl, "ARM Assembly Language: Fundamentals and Techniques" + Chapter 11, Example 11.1. + Note: The handler is not AAPCS compliant (8 byte-alignment and stack, etc.) + */ + /* We assume that the UNDEF stack has been setup previously + On entry, LR_undef points to one instruction past the UNDEF instruction + */ + ldr sp, =__debugger_stack__ + stmfd sp, {r0-r15}^ /* Save workspace, user mode's pc via 'S' flag */ + sub sp, sp, #(4*16) /* Need to manually update SP(undef) */ + mrs r1, spsr /* Copy SPSR to r0 */ + sub r0, lr, #-4 /* LR points to instruction after UNDEF instruction */ + stmfd sp!, {r0,r1} /* Save UNDEF instruction addr and previous mode's CPSR to stack */ + tst r1, #CPSR_THUMB /* Check for Thumb Mode */ + beq _is_arm /* Clear, so it's ARM mode */ +_is_thumb: + ldrh r0, [r0] /* load UNDEF instruction into r0 */ + ldr r1, =BKPT16_ENUM_MASK /* Thumb BKPT enum mask */ + bic r2, r0, r1 /* leave only opcode */ + ldr r1, =BKPT16_INSTR /* check for Thumb Breakpoint Instruction */ + teq r2, r1 + bne default_undef_handler + ldr r1, =BKPT16_ENUM_MASK /* get Thumb BKPT Enum Mask */ + and r0, r1, r0 /* Keep index value */ + msr cpsr_c, #(MODE_SVC) /* Configure Supervisor Mode */ + ldr lr, =dbg__thumb_bkpt_handler /* handle BKPT, BKPT index in r0 */ + mov pc, lr /* Invoke Debugger State (Supervisor Mode) */ +_is_arm: + ldr r0, [r0] /* load UNDEF instruction into r0 */ + ldr r1, =BKPT32_ENUM_MASK /* ARM BKPT enum mask */ + bic r2, r0, r1 /* leave only opcode */ + ldr r1, =BKPT32_INSTR /* check for ARM Breakpoint Instruction */ + teq r2, r1 + bne default_undef_handler + ldr r1, =BKPT32_ENUM_MASK /* get ARM BKPT Enum Mask */ + and r0, r1, r0 /* Keep index value */ + msr cpsr_c, #(MODE_SVC) /* Configure Supervisor Mode */ + ldr lr, =dbg__arm_bkpt_handler /* handle BKPT, BKPT index in r0 */ + mov pc, lr /* Invoke Debugger State (Supervisor Mode) */ + + +default_undef_handler: + b default_undef_handler /* Infinite loop */ -- cgit v1.2.3