131 lines
4.3 KiB
ArmAsm
131 lines
4.3 KiB
ArmAsm
/*
|
|
* ESP32 CALL0 boot + Xtensa vector table
|
|
*
|
|
* VECBASE is set to the base of .vectors (flash-XIP or IRAM depending
|
|
* on linker config). The vector table layout for ESP32 Xtensa LX6:
|
|
*
|
|
* VECBASE + 0x000 Window overflow 4
|
|
* VECBASE + 0x040 Window underflow 4
|
|
* VECBASE + 0x080 Window overflow 8
|
|
* VECBASE + 0x0C0 Window underflow 8
|
|
* VECBASE + 0x100 Window overflow 12
|
|
* VECBASE + 0x140 Window underflow 12
|
|
* VECBASE + 0x180 Level 2 interrupt
|
|
* VECBASE + 0x1C0 Level 3 interrupt
|
|
* VECBASE + 0x200 Level 4 interrupt
|
|
* VECBASE + 0x240 Level 5 interrupt
|
|
* VECBASE + 0x280 Level 6 / Debug exception ← THIS IS THE GDB STUB ENTRY
|
|
* VECBASE + 0x2C0 NMI (level 7)
|
|
* VECBASE + 0x300 Kernel exception
|
|
* VECBASE + 0x340 User exception
|
|
* VECBASE + 0x3C0 Double exception
|
|
* VECBASE + 0x400 Reset vector / _start
|
|
*
|
|
* Each vector slot is 64 bytes (0x40). The debug vector at 0x280 must:
|
|
* 1. Save a0 into EXCSAVE6 (so the handler can recover it)
|
|
* 2. Jump to the main debug handler (_uos_gdbstub_debug_entry)
|
|
*
|
|
* This matches the IDF _DebugExceptionVector pattern from xtensa_vectors.S.
|
|
*/
|
|
|
|
.section .vectors, "ax", @progbits
|
|
.global _start
|
|
.type _start, @function
|
|
.align 4
|
|
|
|
__vectors_base:
|
|
/* Window vectors (0x000 - 0x17F) — not used in CALL0 but must exist */
|
|
.fill 0x180, 1, 0x00
|
|
|
|
/* Level 2 interrupt (0x180) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* Level 3 interrupt (0x1C0) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* Level 4 interrupt (0x200) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* Level 5 interrupt (0x240) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* === Level 6 / Debug Exception (0x280) === */
|
|
.global _xt_level6_vector
|
|
.type _xt_level6_vector, @function
|
|
.align 4
|
|
_xt_level6_vector:
|
|
/* Save a0 into EXCSAVE6 (SR 214) — only 3 instructions fit.
|
|
* The debug exception automatically sets PS to EPS6.
|
|
* a0 is the ONLY register we can clobber here. */
|
|
wsr a0, 214 /* EXCSAVE6 = a0 */
|
|
j _uos_gdbstub_debug_entry
|
|
|
|
/* Pad to 0x2C0 */
|
|
.fill 0x2C0 - (. - __vectors_base), 1, 0x00
|
|
|
|
/* NMI (0x2C0) — not handled */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* Kernel exception (0x300) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* User exception (0x340) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* Double exception (0x3C0) */
|
|
.fill 0x40, 1, 0x00
|
|
|
|
/* === Reset vector / _start (0x400) ===
|
|
* Ensure we're exactly at VECBASE + 0x400 */
|
|
. = __vectors_base + 0x400
|
|
.align 4
|
|
_start:
|
|
/* Set VECBASE to the start of .vectors section.
|
|
* The linker places .vectors at the beginning of irom_seg (0x40820000
|
|
* for flash-XIP build, or 0x40080000 for IRAM build).
|
|
* VECBASE must point to __vectors_base. */
|
|
movi a2, __vectors_base
|
|
wsr a2, 231 /* VECBASE */
|
|
isync
|
|
|
|
/* Set up stack pointer: top of DRAM.
|
|
* ESP32 DRAM: 0x3FFB0000..0x40000000 (320KB usable).
|
|
* Use 0x3FFD0000 (128KB above DRAM base) as initial sp.
|
|
* This matches the linker_flash.ld DRAM segment. */
|
|
movi a1, 0x3FFD0000
|
|
|
|
/* Disable interrupts during setup — but NOT above debug level (6).
|
|
* Setting INTLEVEL > 6 would mask debug exceptions, preventing
|
|
* the 'break' instruction from trapping.
|
|
* Use INTLEVEL=5 (max normal interrupt level, below debug). */
|
|
rsil a2, 5
|
|
|
|
/* Clear ICOUNT and ICOUNTLEVEL to prevent stray debug exceptions */
|
|
movi a3, 0
|
|
wsr a3, 236 /* ICOUNT */
|
|
wsr a3, 237 /* ICOUNTLEVEL */
|
|
|
|
/* DIAGNOSTIC: Write 'S' to UART0 FIFO to prove we reached _start.
|
|
* If 'S' appears on UART, execution IS reaching this point.
|
|
* If not, the problem is earlier (MCUboot handoff). */
|
|
movi a4, 0x3FF40000 /* UART0 FIFO (APB) */
|
|
movi a5, 0x53 /* 'S' */
|
|
s32i a5, a4, 0
|
|
|
|
/* DIAGNOSTIC: Instead of break 0,0, write '2' to prove we pass this point.
|
|
* Then fall through to esp32_app_entry to see if the app starts.
|
|
* This tests whether the code path AFTER break works. */
|
|
movi a5, 0x32 /* '2' */
|
|
s32i a5, a4, 0
|
|
|
|
/* Skip the break for now — test linear execution */
|
|
/* break 0, 0 */
|
|
|
|
/* After GDB continue: enter the microkernel */
|
|
call0 esp32_app_entry
|
|
|
|
/* If app_entry returns, idle loop */
|
|
1: waiti 0
|
|
j 1b
|
|
|
|
.size _start, . - _start
|