123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- * Copyright (c) 2018-2024, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- /*
- * If a platform wishes to use the functions in this file it has to be added to
- * the Makefile of the platform. It is not included in the common Makefile.
- */
- #include <asm_macros.S>
- #include <drivers/console.h>
- .globl plat_crash_console_init
- .globl plat_crash_console_putc
- .globl plat_crash_console_flush
- /*
- * Spinlock to syncronize access to crash_console_triggered. We cannot
- * acquire spinlocks when the cache is disabled, so in some cases (like
- * late during CPU suspend) some risk remains.
- */
- .section .data.crash_console_spinlock
- define_asm_spinlock crash_console_spinlock
- /*
- * Flag to make sure that only one CPU can write a crash dump even if
- * multiple crash at the same time. Interleaving crash dumps on the same
- * console would just make the output unreadable, so it's better to only
- * get a single but uncorrupted dump. This also means that we don't have
- * to duplicate the reg_stash below for each CPU.
- */
- .section .data.crash_console_triggered
- crash_console_triggered: .byte 0
- /*
- * Space to stash away some register values while we're calling into
- * console drivers and don't have a real stack available. We need x14,
- * x15 and x30 for bookkeeping within the plat_crash_console functions
- * themselves, and some console drivers use x16 and x17 as additional
- * scratch space that is not preserved by the main crash reporting
- * framework. (Note that x16 and x17 should really never be expected to
- * retain their values across any function call, even between carefully
- * designed assembly functions, since the linker is always free to
- * insert a function call veneer that uses these registers as scratch
- * space at any time. The current crash reporting framework doesn't
- * really respect that, but since TF is usually linked as a single
- * contiguous binary of less than 128MB, it seems to work in practice.)
- */
- .section .data.crash_console_reg_stash
- .align 3
- crash_console_reg_stash: .quad 0, 0, 0, 0, 0
- /* --------------------------------------------------------------------
- * int plat_crash_console_init(void)
- * Takes the crash console spinlock (if possible) and checks the trigger
- * flag to make sure we're the first CPU to dump. If not, return an
- * error (so crash dumping will fail but the CPU will still call
- * plat_panic_handler() which may do important platform-specific tasks
- * that may be needed on all crashing CPUs). In either case, the lock
- * will be released so other CPUs can make forward progress on this.
- * Clobbers: x0 - x4, x30
- * --------------------------------------------------------------------
- */
- func plat_crash_console_init
- #if defined(IMAGE_BL31)
- mov x4, x30 /* x3 and x4 are not clobbered by spin_lock() */
- mov x3, #0 /* return value */
- adrp x0, crash_console_spinlock
- add x0, x0, :lo12:crash_console_spinlock
- mrs x1, sctlr_el3
- tst x1, #SCTLR_C_BIT
- beq skip_spinlock /* can't synchronize when cache disabled */
- bl spin_lock
- skip_spinlock:
- adrp x1, crash_console_triggered
- add x1, x1, :lo12:crash_console_triggered
- ldarb w2, [x1]
- cmp w2, #0
- bne init_error
- mov x3, #1 /* set return value to success */
- stlrb w3, [x1]
- init_error:
- bl spin_unlock /* harmless if we didn't acquire the lock */
- mov x0, x3
- ret x4
- #else /* Only one CPU in BL1/BL2, no need to synchronize anything */
- mov x0, #1
- ret
- #endif
- endfunc plat_crash_console_init
- /* --------------------------------------------------------------------
- * int plat_crash_console_putc(char c)
- * Prints the character on all consoles registered with the console
- * framework that have CONSOLE_FLAG_CRASH set. Note that this is only
- * helpful for crashes that occur after the platform initialization code
- * has registered a console. Platforms using this implementation need to
- * ensure that all console drivers they use that have the CRASH flag set
- * support this (i.e. are written in assembly and comply to the register
- * clobber requirements of plat_crash_console_putc().
- * --------------------------------------------------------------------
- */
- func plat_crash_console_putc
- adrp x1, crash_console_reg_stash
- add x1, x1, :lo12:crash_console_reg_stash
- stp x14, x15, [x1]
- stp x16, x17, [x1, #16]
- str x30, [x1, #32]
- mov w14, w0 /* W14 = character to print */
- adrp x15, console_list
- ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */
- putc_loop:
- cbz x15, putc_done
- ldr w1, [x15, #CONSOLE_T_FLAGS]
- tst w1, #CONSOLE_FLAG_CRASH
- b.eq putc_continue
- ldr x2, [x15, #CONSOLE_T_PUTC]
- cbz x2, putc_continue
- cmp w14, #'\n'
- b.ne putc
- tst w1, #CONSOLE_FLAG_TRANSLATE_CRLF
- b.eq putc
- mov x1, x15
- mov w0, #'\r'
- blr x2
- ldr x2, [x15, #CONSOLE_T_PUTC]
- putc:
- mov x1, x15
- mov w0, w14
- blr x2
- putc_continue:
- ldr x15, [x15] /* X15 = next struct */
- b putc_loop
- putc_done:
- adrp x1, crash_console_reg_stash
- add x1, x1, :lo12:crash_console_reg_stash
- ldp x14, x15, [x1]
- ldp x16, x17, [x1, #16]
- ldr x30, [x1, #32]
- ret
- endfunc plat_crash_console_putc
- /* --------------------------------------------------------------------
- * int plat_crash_console_flush(char c)
- * Flushes all consoles registered with the console framework that have
- * CONSOLE_FLAG_CRASH set. Same requirements as putc().
- * --------------------------------------------------------------------
- */
- func plat_crash_console_flush
- adrp x1, crash_console_reg_stash
- add x1, x1, :lo12:crash_console_reg_stash
- stp x30, x15, [x1]
- stp x16, x17, [x1, #16]
- adrp x15, console_list
- ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */
- flush_loop:
- cbz x15, flush_done
- ldr w1, [x15, #CONSOLE_T_FLAGS]
- tst w1, #CONSOLE_FLAG_CRASH
- b.eq flush_continue
- ldr x2, [x15, #CONSOLE_T_FLUSH]
- cbz x2, flush_continue
- mov x0, x15
- blr x2
- flush_continue:
- ldr x15, [x15] /* X15 = next struct */
- b flush_loop
- flush_done:
- adrp x1, crash_console_reg_stash
- add x1, x1, :lo12:crash_console_reg_stash
- ldp x30, x15, [x1]
- ldp x16, x17, [x1, #16]
- ret
- endfunc plat_crash_console_flush
|