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