123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- /******************************************************************************
- * Copyright 2015 Espressif Systems
- *
- * Description: Assembly routines for the gdbstub
- *
- * License: ESPRESSIF MIT License
- *******************************************************************************/
- #include "gdbstub-cfg.h"
- #include <xtensa/config/specreg.h>
- #include <xtensa/config/core-isa.h>
- #include <xtensa/corebits.h>
- #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
|