blob: 1a5009199bd8d2947bace99926d1fb6a44562194 [file] [log] [blame]
/*
* 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