1/* 2 * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7/* 8 * If a platform wishes to use the functions in this file it has to be added to 9 * the Makefile of the platform. It is not included in the common Makefile. 10 */ 11 12#include <asm_macros.S> 13#include <drivers/console.h> 14 15 .globl plat_crash_console_init 16 .globl plat_crash_console_putc 17 .globl plat_crash_console_flush 18 19#if !MULTI_CONSOLE_API 20#error "This crash console implementation only works with the MULTI_CONSOLE_API!" 21#endif 22 23 /* 24 * Spinlock to syncronize access to crash_console_triggered. We cannot 25 * acquire spinlocks when the cache is disabled, so in some cases (like 26 * late during CPU suspend) some risk remains. 27 */ 28.section .data.crash_console_spinlock 29 define_asm_spinlock crash_console_spinlock 30 31 /* 32 * Flag to make sure that only one CPU can write a crash dump even if 33 * multiple crash at the same time. Interleaving crash dumps on the same 34 * console would just make the output unreadable, so it's better to only 35 * get a single but uncorrupted dump. This also means that we don't have 36 * to duplicate the reg_stash below for each CPU. 37 */ 38.section .data.crash_console_triggered 39 crash_console_triggered: .byte 0 40 41 /* 42 * Space to stash away some register values while we're calling into 43 * console drivers and don't have a real stack available. We need x14, 44 * x15 and x30 for bookkeeping within the plat_crash_console functions 45 * themselves, and some console drivers use x16 and x17 as additional 46 * scratch space that is not preserved by the main crash reporting 47 * framework. (Note that x16 and x17 should really never be expected to 48 * retain their values across any function call, even between carefully 49 * designed assembly functions, since the linker is always free to 50 * insert a function call veneer that uses these registers as scratch 51 * space at any time. The current crash reporting framework doesn't 52 * really respect that, but since TF is usually linked as a single 53 * contiguous binary of less than 128MB, it seems to work in practice.) 54 */ 55.section .data.crash_console_reg_stash 56 .align 3 57 crash_console_reg_stash: .quad 0, 0, 0, 0, 0 58 59 /* -------------------------------------------------------------------- 60 * int plat_crash_console_init(void) 61 * Takes the crash console spinlock (if possible) and checks the trigger 62 * flag to make sure we're the first CPU to dump. If not, return an 63 * error (so crash dumping will fail but the CPU will still call 64 * plat_panic_handler() which may do important platform-specific tasks 65 * that may be needed on all crashing CPUs). In either case, the lock 66 * will be released so other CPUs can make forward progress on this. 67 * Clobbers: x0 - x4, x30 68 * -------------------------------------------------------------------- 69 */ 70func plat_crash_console_init 71#if defined(IMAGE_BL31) 72 mov x4, x30 /* x3 and x4 are not clobbered by spin_lock() */ 73 mov x3, #0 /* return value */ 74 75 mrs x1, sctlr_el3 76 tst x1, #SCTLR_C_BIT 77 beq skip_spinlock /* can't synchronize when cache disabled */ 78 79 adrp x0, crash_console_spinlock 80 add x0, x0, :lo12:crash_console_spinlock 81 bl spin_lock 82 83skip_spinlock: 84 adrp x1, crash_console_triggered 85 add x1, x1, :lo12:crash_console_triggered 86 ldarb w2, [x1] 87 cmp w2, #0 88 bne init_error 89 90 mov x3, #1 /* set return value to success */ 91 stlrb w3, [x1] 92 93init_error: 94 bl spin_unlock /* harmless if we didn't acquire the lock */ 95 mov x0, x3 96 ret x4 97#else /* Only one CPU in BL1/BL2, no need to synchronize anything */ 98 mov x0, #1 99 ret 100#endif 101endfunc plat_crash_console_init 102 103 /* -------------------------------------------------------------------- 104 * int plat_crash_console_putc(char c) 105 * Prints the character on all consoles registered with the console 106 * framework that have CONSOLE_FLAG_CRASH set. Note that this is only 107 * helpful for crashes that occur after the platform intialization code 108 * has registered a console. Platforms using this implementation need to 109 * ensure that all console drivers they use that have the CRASH flag set 110 * support this (i.e. are written in assembly and comply to the register 111 * clobber requirements of plat_crash_console_putc(). 112 * -------------------------------------------------------------------- 113 */ 114func plat_crash_console_putc 115 adrp x1, crash_console_reg_stash 116 add x1, x1, :lo12:crash_console_reg_stash 117 stp x14, x15, [x1] 118 stp x16, x17, [x1, #16] 119 str x30, [x1, #32] 120 121 mov w14, w0 /* W14 = character to print */ 122 adrp x15, console_list 123 ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */ 124 125putc_loop: 126 cbz x15, putc_done 127 ldr w1, [x15, #CONSOLE_T_FLAGS] 128 tst w1, #CONSOLE_FLAG_CRASH 129 b.eq putc_continue 130 ldr x2, [x15, #CONSOLE_T_PUTC] 131 cbz x2, putc_continue 132 mov x1, x15 133 blr x2 134 mov w0, w14 135putc_continue: 136 ldr x15, [x15] /* X15 = next struct */ 137 b putc_loop 138 139putc_done: 140 adrp x1, crash_console_reg_stash 141 add x1, x1, :lo12:crash_console_reg_stash 142 ldp x14, x15, [x1] 143 ldp x16, x17, [x1, #16] 144 ldr x30, [x1, #32] 145 ret 146endfunc plat_crash_console_putc 147 148 /* -------------------------------------------------------------------- 149 * int plat_crash_console_flush(char c) 150 * Flushes all consoles registered with the console framework that have 151 * CONSOLE_FLAG_CRASH set. Same requirements as putc(). 152 * -------------------------------------------------------------------- 153 */ 154func plat_crash_console_flush 155 adrp x1, crash_console_reg_stash 156 add x1, x1, :lo12:crash_console_reg_stash 157 stp x30, x15, [x1] 158 stp x16, x17, [x1, #16] 159 160 adrp x15, console_list 161 ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */ 162 163flush_loop: 164 cbz x15, flush_done 165 ldr w1, [x15, #CONSOLE_T_FLAGS] 166 tst w1, #CONSOLE_FLAG_CRASH 167 b.eq flush_continue 168 ldr x2, [x15, #CONSOLE_T_FLUSH] 169 cbz x2, flush_continue 170 mov x0, x15 171 blr x2 172flush_continue: 173 ldr x15, [x15] /* X15 = next struct */ 174 b flush_loop 175 176flush_done: 177 adrp x1, crash_console_reg_stash 178 add x1, x1, :lo12:crash_console_reg_stash 179 ldp x30, x15, [x1] 180 ldp x16, x17, [x1, #16] 181 ret 182endfunc plat_crash_console_flush 183