@ -34,11 +34,13 @@
# include < plat_macros.S >
# include < platform_def.h >
.globl dump_state_and_die
.globl dump_intr_state_and_die
.globl init_crash_reporting
.globl report_unhandled_exception
.globl report_unhandled_interrupt
.globl el3_panic
# if CRASH_REPORTING
# define REG_SIZE 0x8
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* The below section deals with dumping the system state
* when an unhandled exception is taken in EL3.
@ -46,267 +48,304 @@
* be dumped during a unhandled exception is given below.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
.section .rodata.dump_reg_name , "aS"
caller_saved_regs: .asciz "x0" , "x1" , "x2" , "x3" , "x4" , "x5" , "x6" , "x7" , \
" x8 " , " x9 " , " x10 " , " x11 " , " x12 " , " x13 " , " x14 " , " x15 " , " x16 " , \
" x17 " , " x18 " , " "
callee_saved_regs: .asciz "x19" , "x20" , "x21" , "x22" , "x23" , "x24" , \
" x25 " , " x26 " , " x27 " , " x28 " , " x29 " , " x30 " , " "
el3_sys_regs: .asciz "scr_el3" , "sctlr_el3" , "cptr_el3" , "tcr_el3" , \
" daif " , " mair_el3 " , " spsr_el3 " , " elr_el3 " , " ttbr0_el3 " , " esr_el3 " , \
" sp_el3 " , " far_el3 " , " "
non_el3_sys_0_regs: .asciz "spsr_el1" , "elr_el1" , "spsr_abt" , "spsr_und" , \
" spsr_irq " , " spsr_fiq " , " sctlr_el1 " , " actlr_el1 " , " cpacr_el1 " , \
" csselr_el1 " , " sp_el1 " , " esr_el1 " , " ttbr0_el1 " , " ttbr1_el1 " , \
" mair_el1 " , " amair_el1 " , " tcr_el1 " , " tpidr_el1 " , " "
non_el3_sys_1_regs: .asciz "tpidr_el0" , "tpidrro_el0" , "dacr32_el2" , \
" ifsr32_el2 " , " par_el1 " , " far_el1 " , " afsr0_el1 " , " afsr1_el1 " , \
" contextidr_el1 " , " vbar_el1 " , " cntp_ctl_el0 " , " cntp_cval_el0 " , \
" cntv_ctl_el0 " , " cntv_cval_el0 " , " cntkctl_el1 " , " fpexc32_el2 " , \
" sp_el0 " , " "
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Currently we are stack limited. Hence make sure that
* we dont try to dump more than 20 registers using the
* stack.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.section .rodata.crash_prints , "aS"
print_spacer:
.asciz " =\t\t0x"
gp_regs:
.asciz "x0" , "x1" , "x2" , "x3" , "x4" , "x5" , "x6" , "x7" , \
" x8 " , " x9 " , " x10 " , " x11 " , " x12 " , " x13 " , " x14 " , " x15 " , \
" x16 " , " x17 " , " x18 " , " x19 " , " x20 " , " x21 " , " x22 " , \
" x23 " , " x24 " , " x25 " , " x26 " , " x27 " , " x28 " , " x29 " , " "
el3_sys_regs:
.asciz "scr_el3" , "sctlr_el3" , "cptr_el3" , "tcr_el3" , \
" daif " , " mair_el3 " , " spsr_el3 " , " elr_el3 " , " ttbr0_el3 " , \
" esr_el3 " , " far_el3 " , " "
non_el3_sys_regs:
.asciz "spsr_el1" , "elr_el1" , "spsr_abt" , "spsr_und" , \
" spsr_irq " , " spsr_fiq " , " sctlr_el1 " , " actlr_el1 " , " cpacr_el1 " , \
" csselr_el1 " , " sp_el1 " , " esr_el1 " , " ttbr0_el1 " , " ttbr1_el1 " , \
" mair_el1 " , " amair_el1 " , " tcr_el1 " , " tpidr_el1 " , " tpidr_el0 " , \
" tpidrro_el0 " , " dacr32_el2 " , " ifsr32_el2 " , " par_el1 " , \
" mpidr_el1 " , " afsr0_el1 " , " afsr1_el1 " , " contextidr_el1 " , \
" vbar_el1 " , " cntp_ctl_el0 " , " cntp_cval_el0 " , " cntv_ctl_el0 " , \
" cntv_cval_el0 " , " cntkctl_el1 " , " fpexc32_el2 " , " sp_el0 " , " "
panic_msg:
.asciz "PANIC in EL3 at x30 = 0x"
excpt_msg:
.asciz "Unhandled Exception in EL3.\nx30 =\t\t0x"
intr_excpt_msg:
.asciz "Unhandled Interrupt Exception in EL3.\nx30 =\t\t0x"
/ *
* Helper function to print newline to console.
* /
func print_newline
mov x0 , ' \ n '
b plat_crash_console_putc
/ *
* Helper function to print from crash buf.
* The print loop is controlled by the buf size and
* ascii reg name list which is passed in x6. The
* function returns the crash buf address in x0.
* Clobbers : x0 - x7 , sp
* /
func size_controlled_print
/ * Save the lr * /
mov sp , x30
/ * load the crash buf address * /
mrs x7 , tpidr_el3
test_size_list:
/ * Calculate x5 always as it will be clobbered by asm_print_hex * /
mrs x5 , tpidr_el3
add x5 , x5 , # CPU_DATA_CRASH_BUF_SIZE
/ * Test whether we have reached end of crash buf * /
cmp x7 , x5
b.eq exit_size_print
ldrb w4 , [ x6 ]
/ * Test whether we are at end of list * /
cbz w4 , exit_size_print
mov x4 , x6
/ * asm_print_str updates x4 to point to next entry in list * /
bl asm_print_str
/ * update x6 with the updated list pointer * /
mov x6 , x4
adr x4 , print_spacer
bl asm_print_str
ldr x4 , [ x7 ], # REG_SIZE
bl asm_print_hex
bl print_newline
b test_size_list
exit_size_print:
mov x30 , sp
ret
/ *
* Helper function to store x8 - x15 registers to
* the crash buf. The system registers values are
* copied to x8 to x15 by the caller which are then
* copied to the crash buf by this function.
* x0 points to the crash buf. It then calls
* size_controlled_print to print to console.
* Clobbers : x0 - x7 , sp
* /
func str_in_crash_buf_print
/ * restore the crash buf address in x0 * /
mrs x0 , tpidr_el3
stp x8 , x9 , [ x0 ]
stp x10 , x11 , [ x0 , # REG_SIZE * 2 ]
stp x12 , x13 , [ x0 , # REG_SIZE * 4 ]
stp x14 , x15 , [ x0 , # REG_SIZE * 6 ]
b size_controlled_print
# define REG_SIZE 0x8
/ * The caller saved registers are X0 to X18 * /
# define CALLER_SAVED_REG_SIZE ( 20 * REG_SIZE )
/ * The caller saved registers are X19 to X30 * /
# define CALLEE_SAVED_REG_SIZE ( 12 * REG_SIZE )
/ * The EL3 sys regs * /
# define EL3_SYS_REG_SIZE ( 12 * REG_SIZE )
/ * The non EL3 sys regs set-0 * /
# define NON_EL3_SYS_0_REG_SIZE ( 18 * REG_SIZE )
/ * The non EL3 sys regs set-1 * /
# define NON_EL3_SYS_1_REG_SIZE ( 18 * REG_SIZE )
.macro print_caller_saved_regs
sub sp , sp , # CALLER_SAVED_REG_SIZE
stp x0 , x1 , [ sp ]
stp x2 , x3 , [ sp , # ( REG_SIZE * 2 )]
stp x4 , x5 , [ sp , # ( REG_SIZE * 4 )]
stp x6 , x7 , [ sp , # ( REG_SIZE * 6 )]
stp x8 , x9 , [ sp , # ( REG_SIZE * 8 )]
stp x10 , x11 , [ sp , # ( REG_SIZE * 10 )]
stp x12 , x13 , [ sp , # ( REG_SIZE * 12 )]
stp x14 , x15 , [ sp , # ( REG_SIZE * 14 )]
stp x16 , x17 , [ sp , # ( REG_SIZE * 16 )]
stp x18 , xzr , [ sp , # ( REG_SIZE * 18 )]
adr x0 , caller_saved_regs
mov x1 , sp
bl print_string_value
add sp , sp , # CALLER_SAVED_REG_SIZE
.endm
.macro print_callee_saved_regs
sub sp , sp , CALLEE_SAVED_REG_SIZE
stp x19 , x20 , [ sp ]
stp x21 , x22 , [ sp , # ( REG_SIZE * 2 )]
stp x23 , x24 , [ sp , # ( REG_SIZE * 4 )]
stp x25 , x26 , [ sp , # ( REG_SIZE * 6 )]
stp x27 , x28 , [ sp , # ( REG_SIZE * 8 )]
stp x29 , x30 , [ sp , # ( REG_SIZE * 10 )]
adr x0 , callee_saved_regs
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This macro calculates the offset to crash buf from
* cpu_data and stores it in tpidr_el3. It also saves x0
* and x1 in the crash buf by using sp as a temporary
* register.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
.macro prepare_crash_buf_save_x0_x1
/ * we can corrupt this reg to free up x0 * /
mov sp , x0
/ * tpidr_el3 contains the address to cpu_data structure * /
mrs x0 , tpidr_el3
/ * Calculate the Crash buffer offset in cpu_data * /
add x0 , x0 , # CPU_DATA_CRASH_BUF_OFFSET
/ * Store crash buffer address in tpidr_el3 * /
msr tpidr_el3 , x0
str x1 , [ x0 , # REG_SIZE ]
mov x1 , sp
bl print_string_value
add sp , sp , # CALLEE_SAVED_REG_SIZE
str x1 , [ x0 ]
.endm
.macro print_el3_sys_regs
sub sp , sp , # EL3_SYS_REG_SIZE
mrs x9 , scr_el3
mrs x10 , sctlr_el3
mrs x11 , cptr_el3
mrs x12 , tcr_el3
mrs x13 , daif
mrs x14 , mair_el3
mrs x15 , spsr_el3 / * save the elr and spsr regs seperately * /
mrs x16 , elr_el3
mrs x17 , ttbr0_el3
mrs x8 , esr_el3
mrs x7 , far_el3
stp x9 , x10 , [ sp ]
stp x11 , x12 , [ sp , # ( REG_SIZE * 2 )]
stp x13 , x14 , [ sp , # ( REG_SIZE * 4 )]
stp x15 , x16 , [ sp , # ( REG_SIZE * 6 )]
stp x17 , x8 , [ sp , # ( REG_SIZE * 8 )]
stp x0 , x7 , [ sp , # ( REG_SIZE * 10 )] / * sp_el3 is in x0 * /
adr x0 , el3_sys_regs
mov x1 , sp
bl print_string_value
add sp , sp , # EL3_SYS_REG_SIZE
.endm
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This function allows to report a crash ( if crash
* rep orting is enabled ) when an unhandled exception
* occurs. It prints the CPU state via the crash console
* making use of the crash buf. This function will
* not return.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func report_unhandled_exception
prepare_crash_buf_save_x0_x1
adr x0 , excpt_msg
mov sp , x0
/ * This call will not return * /
b do_crash_reporting
.macro print_non_el3_sys_0_regs
sub sp , sp , # NON_EL3_SYS_0_REG_SIZE
mrs x9 , spsr_el1
mrs x10 , elr_el1
mrs x11 , spsr_abt
mrs x12 , spsr_und
mrs x13 , spsr_irq
mrs x14 , spsr_fiq
mrs x15 , sctlr_el1
mrs x16 , actlr_el1
mrs x17 , cpacr_el1
mrs x8 , csselr_el1
stp x9 , x10 , [ sp ]
stp x11 , x12 , [ sp , # ( REG_SIZE * 2 )]
stp x13 , x14 , [ sp , # ( REG_SIZE * 4 )]
stp x15 , x16 , [ sp , # ( REG_SIZE * 6 )]
stp x17 , x8 , [ sp , # ( REG_SIZE * 8 )]
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This function allows to report a crash ( if crash
* rep orting is enabled ) when an unhandled interrupt
* occurs. It prints the CPU state via the crash console
* making use of the crash buf. This function will
* not return.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func report_unhandled_interrupt
prepare_crash_buf_save_x0_x1
adr x0 , intr_excpt_msg
mov sp , x0
/ * This call will not return * /
b do_crash_reporting
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This function allows to report a crash ( if crash
* rep orting is enabled ) when panic () is invoked from
* C Runtime. It prints the CPU state via the crash
* console making use of the crash buf. This function
* will not return.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func el3_panic
msr spsel , # 1
prepare_crash_buf_save_x0_x1
adr x0 , panic_msg
mov sp , x0
/ * This call will not return * /
b do_crash_reporting
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* The common crash reporting functionality. It requires x0
* and x1 has already been stored in crash buf , sp points to
* crash message and tpidr_el3 contains the crash buf address.
* The function does the following :
* - Retrieve the crash buffer from tpidr_el3
* - Store x2 to x6 in the crash buffer
* - Initialise the crash console.
* - Print the crash message by using the address in sp.
* - Print x30 value to the crash console.
* - Print x0 - x7 from the crash buf to the crash console.
* - Print x8 - x29 ( in groups of 8 registers ) using the
* crash buf to the crash console.
* - Print el3 sys regs ( in groups of 8 registers ) using the
* crash buf to the crash console.
* - Print non el3 sys regs ( in groups of 8 registers ) using
* the crash buf to the crash console.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func do_crash_reporting
/ * Retrieve the crash buf from tpidr_el3 * /
mrs x0 , tpidr_el3
/ * Store x2 - x6 , x30 in the crash buffer * /
stp x2 , x3 , [ x0 , # REG_SIZE * 2 ]
stp x4 , x5 , [ x0 , # REG_SIZE * 4 ]
stp x6 , x30 , [ x0 , # REG_SIZE * 6 ]
/ * Initialize the crash console * /
bl plat_crash_console_init
/ * Verify the console is initialized * /
cbz x0 , crash_panic
/ * Print the crash message. sp points to the crash message * /
mov x4 , sp
bl asm_print_str
/ * load the crash buf address * /
mrs x0 , tpidr_el3
/ * rep ort x30 first from the crash buf * /
ldr x4 , [ x0 , # REG_SIZE * 7 ]
bl asm_print_hex
bl print_newline
/ * Load the crash buf address * /
mrs x0 , tpidr_el3
/ * Now mov x7 into crash buf * /
str x7 , [ x0 , # REG_SIZE * 7 ]
/ * Report x0 - x29 values stored in crash buf * /
/ * Store the ascii list pointer in x6 * /
adr x6 , gp_regs
/ * Print x0 to x7 from the crash buf * /
bl size_controlled_print
/ * Store x8 - x15 in crash buf and print * /
bl str_in_crash_buf_print
/ * Load the crash buf address * /
mrs x0 , tpidr_el3
/ * Store the rest of gp regs and print * /
stp x16 , x17 , [ x0 ]
stp x18 , x19 , [ x0 , # REG_SIZE * 2 ]
stp x20 , x21 , [ x0 , # REG_SIZE * 4 ]
stp x22 , x23 , [ x0 , # REG_SIZE * 6 ]
bl size_controlled_print
/ * Load the crash buf address * /
mrs x0 , tpidr_el3
stp x24 , x25 , [ x0 ]
stp x26 , x27 , [ x0 , # REG_SIZE * 2 ]
stp x28 , x29 , [ x0 , # REG_SIZE * 4 ]
bl size_controlled_print
/ * Print the el3 sys registers * /
adr x6 , el3_sys_regs
mrs x8 , scr_el3
mrs x9 , sctlr_el3
mrs x10 , cptr_el3
mrs x11 , tcr_el3
mrs x12 , daif
mrs x13 , mair_el3
mrs x14 , spsr_el3
mrs x15 , elr_el3
bl str_in_crash_buf_print
mrs x8 , ttbr0_el3
mrs x9 , esr_el3
mrs x10 , far_el3
bl str_in_crash_buf_print
/ * Print the non el3 sys registers * /
adr x6 , non_el3_sys_regs
mrs x8 , spsr_el1
mrs x9 , elr_el1
mrs x10 , spsr_abt
mrs x11 , spsr_und
mrs x12 , spsr_irq
mrs x13 , spsr_fiq
mrs x14 , sctlr_el1
mrs x15 , actlr_el1
bl str_in_crash_buf_print
mrs x8 , cpacr_el1
mrs x9 , csselr_el1
mrs x10 , sp_el1
mrs x11 , esr_el1
mrs x12 , ttbr0_el1
mrs x13 , ttbr1_el1
mrs x14 , mair_el1
mrs x15 , amair_el1
mrs x16 , tcr_el1
mrs x17 , tpidr_el1
stp x10 , x11 , [ sp , # ( REG_SIZE * 10 )]
stp x12 , x13 , [ sp , # ( REG_SIZE * 12 )]
stp x14 , x15 , [ sp , # ( REG_SIZE * 14 )]
stp x16 , x17 , [ sp , # ( REG_SIZE * 16 )]
adr x0 , non_el3_sys_0_regs
mov x1 , sp
bl print_string_value
add sp , sp , # NON_EL3_SYS_0_REG_SIZE
.endm
.macro print_non_el3_sys_1_regs
sub sp , sp , # NON_EL3_SYS_1_REG_SIZE
mrs x9 , tpidr_el0
mrs x10 , tpidrro_el0
mrs x11 , dacr32_el2
mrs x12 , ifsr32_el2
mrs x13 , par_el1
mrs x14 , far_el1
mrs x15 , afsr0_el1
mrs x16 , afsr1_el1
mrs x17 , contextidr_el1
mrs x8 , vbar_el1
stp x9 , x10 , [ sp ]
stp x11 , x12 , [ sp , # ( REG_SIZE * 2 )]
stp x13 , x14 , [ sp , # ( REG_SIZE * 4 )]
stp x15 , x16 , [ sp , # ( REG_SIZE * 6 )]
stp x17 , x8 , [ sp , # ( REG_SIZE * 8 )]
mrs x10 , cntp_ctl_el0
mrs x11 , cntp_cval_el0
mrs x12 , cntv_ctl_el0
mrs x13 , cntv_cval_el0
mrs x14 , cntkctl_el1
mrs x15 , fpexc32_el2
mrs x8 , sp_el0
stp x10 , x11 , [ sp , # ( REG_SIZE * 10 )]
stp x12 , x13 , [ sp , # ( REG_SIZE * 12 )]
stp x14 , x15 , [ sp , # ( REG_SIZE * 14 )]
stp x8 , xzr , [ sp , # ( REG_SIZE * 16 )]
adr x0 , non_el3_sys_1_regs
mov x1 , sp
bl print_string_value
add sp , sp , # NON_EL3_SYS_1_REG_SIZE
.endm
.macro init_crash_stack
msr cntfrq_el0 , x0 / * we can corrupt this reg to free up x0 * /
mrs x0 , tpidr_el3
/ * Check if tpidr is initialized * /
cbz x0 , infinite_loop
ldr x0 , [ x0 , # CPU_DATA_CRASH_STACK_OFFSET ]
/ * store the x30 and sp to stack * /
str x30 , [ x0 , # -( REG_SIZE )]!
mov x30 , sp
str x30 , [ x0 , # -( REG_SIZE )]!
mov sp , x0
mrs x0 , cntfrq_el0
.endm
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* The below function initializes the crash dump stack ,
* and prints the system state. This function
* will not return.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func dump_state_and_die
init_crash_stack
print_caller_saved_regs
b print_state
func dump_intr_state_and_die
init_crash_stack
print_caller_saved_regs
plat_print_gic_regs / * fall through to print_state * /
print_state:
/ * copy the original x30 from stack * /
ldr x30 , [ sp , # REG_SIZE ]
print_callee_saved_regs
/ * copy the original SP_EL3 from stack to x0 and rewind stack * /
ldr x0 , [ sp ], # ( REG_SIZE * 2 )
print_el3_sys_regs
print_non_el3_sys_0_regs
print_non_el3_sys_1_regs
# else / * CRASH_REPORING * /
func dump_state_and_die
dump_intr_state_and_die:
bl str_in_crash_buf_print
mrs x8 , tcr_el1
mrs x9 , tpidr_el1
mrs x10 , tpidr_el0
mrs x11 , tpidrro_el0
mrs x12 , dacr32_el2
mrs x13 , ifsr32_el2
mrs x14 , par_el1
mrs x15 , mpidr_el1
bl str_in_crash_buf_print
mrs x8 , afsr0_el1
mrs x9 , afsr1_el1
mrs x10 , contextidr_el1
mrs x11 , vbar_el1
mrs x12 , cntp_ctl_el0
mrs x13 , cntp_cval_el0
mrs x14 , cntv_ctl_el0
mrs x15 , cntv_cval_el0
bl str_in_crash_buf_print
mrs x8 , cntkctl_el1
mrs x9 , fpexc32_el2
mrs x10 , sp_el0
bl str_in_crash_buf_print
/ * Print the gic registers * /
plat_print_gic_regs
/ * Done reporting * /
b crash_panic
# else / * CRASH_REPORTING * /
func report_unhandled_exception
report_unhandled_interrupt:
b crash_panic
# endif / * CRASH_REPORING * /
infinite_loop:
b infinite_loop
# define PCPU_CRASH_STACK_SIZE 0x140
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Per-cpu crash stacks in normal memory.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
declare_stack pcpu_crash_stack , tzfw_normal_stacks , \
PCPU_CRASH_STACK_SIZE , PLATFORM_CORE_COUNT
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Provides each CPU with a small stacks for reporting
* unhandled exceptions , and stores the stack address
* in cpu_data
*
* This can be called without a runtime stack
* clobbers: x0 - x4
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* /
func init_crash_reporting
mov x4 , x30
mov x2 , # 0
adr x3 , pcpu_crash_stack
init_crash_loop:
mov x0 , x2
bl _cpu_data_by_index
add x3 , x3 , # PCPU_CRASH_STACK_SIZE
str x3 , [ x0 , # CPU_DATA_CRASH_STACK_OFFSET ]
add x2 , x2 , # 1
cmp x2 , # PLATFORM_CORE_COUNT
b.lo init_crash_loop
ret x4
func crash_panic
b crash_panic