1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
/*
* Copyright (c) 2018-2019, 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 intialization 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
|