/****************************************************************************** * Copyright 2015 Espressif Systems * * Description: Assembly routines for the gdbstub * * License: ESPRESSIF MIT License *******************************************************************************/ #include "gdbstub-cfg.h" #include #include #include #define DEBUG_PC (EPC + XCHAL_DEBUGLEVEL) #define DEBUG_EXCSAVE (EXCSAVE + XCHAL_DEBUGLEVEL) #define DEBUG_PS (EPS + XCHAL_DEBUGLEVEL) .global gdbstub_savedRegs #if GDBSTUB_USE_OWN_STACK .global gdbstub_exceptionStack #endif .text .literal_position .text .align 4 /* The savedRegs struct: uint32_t pc; uint32_t ps; uint32_t sar; uint32_t vpri; uint32_t a0; uint32_t a[14]; //a2..a15 uint32_t litbase; uint32_t sr176; uint32_t sr208; uint32_t a1; uint32_t reason; */ /* This is the debugging exception routine; it's called by the debugging vector We arrive here with all regs intact except for a2. The old contents of A2 are saved into the DEBUG_EXCSAVE special function register. EPC is the original PC. */ gdbstub_debug_exception_entry: /* //Minimum no-op debug exception handler, for debug rsr a2,DEBUG_PC addi a2,a2,3 wsr a2,DEBUG_PC xsr a2, DEBUG_EXCSAVE rfi XCHAL_DEBUGLEVEL */ //Save all regs to structure movi a2, gdbstub_savedRegs s32i a0, a2, 0x10 s32i a1, a2, 0x58 rsr a0, DEBUG_PS s32i a0, a2, 0x04 rsr a0, DEBUG_EXCSAVE //was R2 s32i a0, a2, 0x14 s32i a3, a2, 0x18 s32i a4, a2, 0x1c s32i a5, a2, 0x20 s32i a6, a2, 0x24 s32i a7, a2, 0x28 s32i a8, a2, 0x2c s32i a9, a2, 0x30 s32i a10, a2, 0x34 s32i a11, a2, 0x38 s32i a12, a2, 0x3c s32i a13, a2, 0x40 s32i a14, a2, 0x44 s32i a15, a2, 0x48 rsr a0, SAR s32i a0, a2, 0x08 rsr a0, LITBASE s32i a0, a2, 0x4C rsr a0, 176 s32i a0, a2, 0x50 rsr a0, 208 s32i a0, a2, 0x54 rsr a0, DEBUGCAUSE s32i a0, a2, 0x5C rsr a4, DEBUG_PC s32i a4, a2, 0x00 #if GDBSTUB_USE_OWN_STACK //Move to our own stack movi a1, exceptionStack+255*4 #endif //If ICOUNT is -1, disable it by setting it to 0, otherwise we will keep triggering on the same instruction. rsr a2, ICOUNT movi a3, -1 bne a2, a3, noIcountReset movi a3, 0 wsr a3, ICOUNT noIcountReset: rsr a2, ps addi a2, a2, -PS_EXCM_MASK wsr a2, ps rsync //Call into the C code to do the actual handling. call0 gdbstub_handle_debug_exception DebugExceptionExit: rsr a2, ps addi a2, a2, PS_EXCM_MASK wsr a2, ps rsync //Restore registers from the gdbstub_savedRegs struct movi a2, gdbstub_savedRegs l32i a0, a2, 0x00 wsr a0, DEBUG_PC // l32i a0, a2, 0x54 // wsr a0, 208 l32i a0, a2, 0x50 //wsr a0, 176 //Some versions of gcc do not understand this... .byte 0x00, 176, 0x13 //so we hand-assemble the instruction. l32i a0, a2, 0x4C wsr a0, LITBASE l32i a0, a2, 0x08 wsr a0, SAR l32i a15, a2, 0x48 l32i a14, a2, 0x44 l32i a13, a2, 0x40 l32i a12, a2, 0x3c l32i a11, a2, 0x38 l32i a10, a2, 0x34 l32i a9, a2, 0x30 l32i a8, a2, 0x2c l32i a7, a2, 0x28 l32i a6, a2, 0x24 l32i a5, a2, 0x20 l32i a4, a2, 0x1c l32i a3, a2, 0x18 l32i a0, a2, 0x14 wsr a0, DEBUG_EXCSAVE //was R2 l32i a0, a2, 0x04 wsr a0, DEBUG_PS l32i a1, a2, 0x58 l32i a0, a2, 0x10 //Read back vector-saved a2 value, put back address of this routine. movi a2, gdbstub_debug_exception_entry xsr a2, DEBUG_EXCSAVE //All done. Return to where we came from. rfi XCHAL_DEBUGLEVEL #if GDBSTUB_FREERTOS /* FreeRTOS exception handling code. For some reason or another, we can't just hook the main exception vector: it seems FreeRTOS uses that for something else too (interrupts). FreeRTOS has its own fatal exception handler, and we hook that. Unfortunately, that one is called from a few different places (eg directly in the DoubleExceptionVector) so the precise location of the original register values are somewhat of a mystery when we arrive here... As a 'solution', we'll just decode the most common case of the user_fatal_exception_handler being called from the user exception handler vector: - excsave1 - orig a0 - a1: stack frame: sf+16: orig a1 sf+8: ps sf+4: epc sf+12: orig a0 sf: magic no? */ .global gdbstub_handle_user_exception .global gdbstub_user_exception_entry .align 4 gdbstub_user_exception_entry: //Save all regs to structure movi a0, gdbstub_savedRegs s32i a1, a0, 0x14 //was a2 s32i a3, a0, 0x18 s32i a4, a0, 0x1c s32i a5, a0, 0x20 s32i a6, a0, 0x24 s32i a7, a0, 0x28 s32i a8, a0, 0x2c s32i a9, a0, 0x30 s32i a10, a0, 0x34 s32i a11, a0, 0x38 s32i a12, a0, 0x3c s32i a13, a0, 0x40 s32i a14, a0, 0x44 s32i a15, a0, 0x48 rsr a2, SAR s32i a2, a0, 0x08 rsr a2, LITBASE s32i a2, a0, 0x4C rsr a2, 176 s32i a2, a0, 0x50 rsr a2, 208 s32i a2, a0, 0x54 rsr a2, EXCCAUSE s32i a2, a0, 0x5C //Get the rest of the regs from the stack struct l32i a3, a1, 12 s32i a3, a0, 0x10 l32i a3, a1, 16 s32i a3, a0, 0x58 l32i a3, a1, 8 s32i a3, a0, 0x04 l32i a3, a1, 4 s32i a3, a0, 0x00 #if GDBSTUB_USE_OWN_STACK movi a1, exceptionStack+255*4 #endif rsr a2, ps addi a2, a2, -PS_EXCM_MASK wsr a2, ps rsync call0 gdbstub_handle_user_exception UserExceptionExit: /* Okay, from here on, it Does Not Work. There's not really any continuing from an exception in the FreeRTOS case; there isn't any effort put in reversing the mess the exception code made yet. Maybe this is still something we need to implement later, if there's any demand for it, or maybe we should modify FreeRTOS to allow this in the future. (Which will then kill backwards compatibility... hmmm.) */ j UserExceptionExit .global gdbstub_handle_uart_int .global gdbstub_uart_entry .align 4 gdbstub_uart_entry: //On entry, the stack frame is at SP+16. //This is a small stub to present that as the first arg to the gdbstub_handle_uart function. movi a2, 16 add a2, a2, a1 movi a3, gdbstub_handle_uart_int jx a3 #endif .global gdbstub_save_extra_sfrs_for_exception .align 4 //The Xtensa OS HAL does not save all the special function register things. This bit of assembly //fills the gdbstub_savedRegs struct with them. gdbstub_save_extra_sfrs_for_exception: movi a2, gdbstub_savedRegs rsr a3, LITBASE s32i a3, a2, 0x4C rsr a3, 176 s32i a3, a2, 0x50 rsr a3, 208 s32i a3, a2, 0x54 rsr a3, EXCCAUSE s32i a3, a2, 0x5C ret .global gdbstub_init_debug_entry .global _DebugExceptionVector .align 4 gdbstub_init_debug_entry: //This puts the following 2 instructions into the debug exception vector: // xsr a2, DEBUG_EXCSAVE // jx a2 movi a2, _DebugExceptionVector movi a3, 0xa061d220 s32i a3, a2, 0 movi a3, 0x00000002 s32i a3, a2, 4 //Tell the just-installed debug vector where to go. movi a2, gdbstub_debug_exception_entry wsr a2, DEBUG_EXCSAVE ret //Set up ICOUNT register to step one single instruction .global gdbstub_icount_ena_single_step .align 4 gdbstub_icount_ena_single_step: movi a3, XCHAL_DEBUGLEVEL //Only count steps in non-debug mode movi a2, -2 wsr a3, ICOUNTLEVEL wsr a2, ICOUNT isync ret //These routines all assume only one breakpoint and watchpoint is available, which //is the case for the ESP8266 Xtensa core. .global gdbstub_set_hw_breakpoint gdbstub_set_hw_breakpoint: //a2 - addr, a3 - len (unused here) rsr a4, IBREAKENABLE bbsi a4, 0, return_w_error wsr a2, IBREAKA movi a2, 1 wsr a2, IBREAKENABLE isync movi a2, 1 ret .global gdbstub_del_hw_breakpoint gdbstub_del_hw_breakpoint: //a2 - addr rsr a5, IBREAKENABLE bbci a5, 0, return_w_error rsr a3, IBREAKA bne a3, a2, return_w_error movi a2,0 wsr a2, IBREAKENABLE isync movi a2, 1 ret .global gdbstub_set_hw_watchpoint //a2 - addr, a3 - mask, a4 - type (1=read, 2=write, 3=access) gdbstub_set_hw_watchpoint: //Check if any of the masked address bits are set. If so, that is an error. movi a5,0x0000003F xor a5, a5, a3 bany a2, a5, return_w_error //Check if watchpoint already is set rsr a5, DBREAKC movi a6, 0xC0000000 bany a6, a5, return_w_error //Set watchpoint wsr a2, DBREAKA //Combine type and mask movi a6, 0x3F and a3, a3, a6 slli a4, a4, 30 or a3, a3, a4 wsr a3, DBREAKC // movi a2, 1 mov a2, a3 isync ret .global gdbstub_del_hw_watchpoint //a2 - addr gdbstub_del_hw_watchpoint: //See if the address matches rsr a3, DBREAKA bne a3, a2, return_w_error //See if the bp actually is set rsr a3, DBREAKC movi a2, 0xC0000000 bnone a3, a2, return_w_error //Disable bp movi a2,0 wsr a2,DBREAKC movi a2,1 isync ret return_w_error: movi a2, 0 ret //Breakpoint, with an attempt at a functional function prologue and epilogue... .global gdbstub_do_break_breakpoint_addr .global gdbstub_do_break .align 4 gdbstub_do_break: addi a1, a1, -16 s32i a15, a1, 12 mov a15, a1 gdbstub_do_break_breakpoint_addr: break 0,0 mov a1, a15 l32i a15, a1, 12 addi a1, a1, 16 ret