xref: /rk3399_ARM-atf/bl31/aarch64/crash_reporting.S (revision 574db8ec86c88af5e0d132ae171cf9e0599f990b)
1/*
2 * Copyright (c) 2014-2026, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <plat_macros.S>
8#include <platform_def.h>
9
10#include <arch.h>
11#include <asm_macros.S>
12#include <context.h>
13#include <lib/el3_runtime/cpu_data.h>
14#include <lib/per_cpu/per_cpu_macros.S>
15#include <lib/utils_def.h>
16
17	.globl	report_unhandled_exception
18	.globl	report_unhandled_interrupt
19	.globl	report_el3_panic
20
21#if CRASH_REPORTING
22
23	/* ------------------------------------------------------
24	 * The below section deals with dumping the system state
25	 * when an unhandled exception is taken in EL3.
26	 * The layout and the names of the registers which will
27	 * be dumped during a unhandled exception is given below.
28	 * ------------------------------------------------------
29	 */
30.section .rodata.crash_prints, "aS"
31print_spacer:
32	.asciz	"             = 0x"
33
34gp_regs:
35	.asciz	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",\
36		"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",\
37		"x16", "x17", "x18", "x19", "x20", "x21", "x22",\
38		"x23", "x24", "x25", "x26", "x27", "x28", "x29", ""
39el3_sys_regs:
40	.asciz	"scr_el3", "sctlr_el3", "cptr_el3", "tcr_el3",\
41		"daif", "mair_el3", "spsr_el3", "elr_el3", "ttbr0_el3",\
42		"esr_el3", "far_el3", ""
43
44non_el3_sys_regs:
45	.asciz	"mpidr_el1", "sp_el0", "isr_el1", ""
46
47#if CTX_INCLUDE_AARCH32_REGS
48aarch32_regs:
49	.asciz	"dacr32_el2", "ifsr32_el2", ""
50#endif /* CTX_INCLUDE_AARCH32_REGS */
51
52panic_msg:
53	.asciz "PANIC in EL3.\nx30"
54excpt_msg:
55	.asciz "Unhandled Exception in EL3.\nx30"
56intr_excpt_msg:
57	.ascii "Unhandled Interrupt Exception in EL3.\n"
58x30_msg:
59	.asciz "x30"
60
61	/*
62	 * Helper function to print from crash buf.
63	 * The print loop is controlled by the buf size and
64	 * ascii reg name list which is passed in x6. The
65	 * function returns the crash buf address in x0.
66	 * Clobbers : x0 - x7, x20, sp
67	 */
68func size_controlled_print_helper
69#if ENABLE_FEAT_D128
70size_controlled_print_128:
71	/* Set flag to print 128-bit registers */
72	mov	x20, #1
73	b	1f
74
75size_controlled_print:
76	/* Set flag to print 64-bit registers */
77	mov	x20, #0
781:
79#else
80size_controlled_print:
81#endif
82	/* Save the lr */
83	mov	sp, x30
84	/* load the crash buf address */
85	mrs	x7, tpidr_el3
86test_size_list:
87	/* Calculate x5 always as it will be clobbered by asm_print_hex */
88	mrs	x5, tpidr_el3
89	add	x5, x5, #CPU_DATA_CRASH_BUF_BYTES
90	/* Test whether we have reached end of crash buf */
91	cmp	x7, x5
92	b.eq	exit_size_print
93	ldrb	w4, [x6]
94	/* Test whether we are at end of list */
95	cbz	w4, exit_size_print
96	mov	x4, x6
97	/* asm_print_str updates x4 to point to next entry in list */
98	bl	asm_print_str
99	/* x0 = number of symbols printed + 1 */
100	sub	x0, x4, x6
101	/* update x6 with the updated list pointer */
102	mov	x6, x4
103	bl	print_alignment
104	/* Print the high 64 bits (or whole 64-bit register) */
105	ldr	x4, [x7], #REGSZ
106	bl	asm_print_hex
107#if ENABLE_FEAT_D128
108	cbz	x20, 2f
109	/* Print the low 64 bits in case of a 128-bit register */
110	ldr	x4, [x7], #REGSZ
111	bl	asm_print_hex
1122:
113#endif
114	bl	asm_print_newline
115	b	test_size_list
116exit_size_print:
117	mov	x30, sp
118	ret
119endfunc size_controlled_print_helper
120
121	/* -----------------------------------------------------
122	 * This function calculates and prints required number
123	 * of space characters followed by "= 0x", based on the
124	 * length of ascii register name.
125 	 * x0: length of ascii register name + 1
126	 * ------------------------------------------------------
127 	 */
128func print_alignment
129	/* The minimum ascii length is 3, e.g. for "x0" */
130	adr	x4, print_spacer - 3
131	add	x4, x4, x0
132	b	asm_print_str
133endfunc print_alignment
134
135	/*
136	 * Helper function to store x8 - x15 registers to
137	 * the crash buf. The system registers values are
138	 * copied to x8 to x15 by the caller which are then
139	 * copied to the crash buf by this function.
140	 * x0 points to the crash buf. It then calls
141	 * size_controlled_print to print to console.
142	 * Clobbers : x0 - x7, x20, sp
143	 */
144func str_in_crash_buf_print
145	/* restore the crash buf address in x0 */
146	mrs	x0, tpidr_el3
147	stp	x8, x9, [x0]
148	stp	x10, x11, [x0, #REGSZ * 2]
149	stp	x12, x13, [x0, #REGSZ * 4]
150	stp	x14, x15, [x0, #REGSZ * 6]
151	b	size_controlled_print
152endfunc str_in_crash_buf_print
153
154	/*
155	 * An equivalent helper function for storing x8 - x15
156	 * registers in a different order inside the crash buf.
157	 * In the end the function size_controlled_print_128 is
158	 * called to print the registers to the console.
159	 * Clobbers : x0 - x7, x20, sp
160	 */
161func str_in_crash_buf_print_128
162	/* restore the crash buf address in x0 */
163	mrs	x0, tpidr_el3
164	stp	x8, x9, [x0]
165	stp	x10, x11, [x0, #REGSZ * 2]
166	stp	x12, x13, [x0, #REGSZ * 4]
167	stp	x14, x15, [x0, #REGSZ * 6]
168	b	size_controlled_print_128
169endfunc str_in_crash_buf_print_128
170
171	/* ------------------------------------------------------
172	 * This macro calculates the offset to crash buf from
173	 * cpu_data and stores it in tpidr_el3. It also saves x0
174	 * and x1 in the crash buf by using sp as a temporary
175	 * register.
176	 * ------------------------------------------------------
177	 */
178	.macro prepare_crash_buf_save_x0_x1
179	/* we can corrupt this reg to free up x0 */
180	mov	sp, x0
181	/* tpidr_el3 contains the address to cpu_data structure */
182	per_cpu_cur percpu_data, x0, x2
183	/* Calculate the Crash buffer offset in cpu_data */
184	add	x0, x0, #CPU_DATA_CRASH_BUF
185	/* Store crash buffer address in tpidr_el3 */
186	msr	tpidr_el3, x0
187	str	x1, [x0, #REGSZ]
188	mov	x1, sp
189	str	x1, [x0]
190	.endm
191
192	/* -----------------------------------------------------
193	 * This function allows to report a crash (if crash
194	 * reporting is enabled) when an unhandled exception
195	 * occurs. It prints the CPU state via the crash console
196	 * making use of the crash buf. This function will
197	 * not return.
198	 * -----------------------------------------------------
199	 */
200func report_unhandled_exception
201	/* Switch to SP_ELx */
202	msr     spsel, #MODE_SP_ELX
203	prepare_crash_buf_save_x0_x1
204	adr	x0, excpt_msg
205	mov	sp, x0
206	/* This call will not return */
207	b	do_crash_reporting
208endfunc report_unhandled_exception
209
210	/* -----------------------------------------------------
211	 * This function allows to report a crash (if crash
212	 * reporting is enabled) when an unhandled interrupt
213	 * occurs. It prints the CPU state via the crash console
214	 * making use of the crash buf. This function will
215	 * not return.
216	 * -----------------------------------------------------
217	 */
218func report_unhandled_interrupt
219	prepare_crash_buf_save_x0_x1
220	adr	x0, intr_excpt_msg
221	mov	sp, x0
222	/* This call will not return */
223	b	do_crash_reporting
224endfunc report_unhandled_interrupt
225
226	/* -----------------------------------------------------
227	 * This function allows to report a crash (if crash
228	 * reporting is enabled) when panic() is invoked from
229	 * C Runtime. It prints the CPU state via the crash
230	 * console making use of the crash buf. This function
231	 * will not return.
232	 * -----------------------------------------------------
233	 */
234func report_el3_panic
235	msr	spsel, #MODE_SP_ELX
236	prepare_crash_buf_save_x0_x1
237	adr	x0, panic_msg
238	mov	sp, x0
239	/* Fall through to 'do_crash_reporting' */
240
241	/* ------------------------------------------------------------
242	 * The common crash reporting functionality. It requires x0
243	 * and x1 has already been stored in crash buf, sp points to
244	 * crash message and tpidr_el3 contains the crash buf address.
245	 * The function does the following:
246	 *   - Retrieve the crash buffer from tpidr_el3
247	 *   - Store x2 to x6 in the crash buffer
248	 *   - Initialise the crash console.
249	 *   - Print the crash message by using the address in sp.
250	 *   - Print x30 value to the crash console.
251	 *   - Print x0 - x7 from the crash buf to the crash console.
252	 *   - Print x8 - x29 (in groups of 8 registers) using the
253	 *     crash buf to the crash console.
254	 *   - Print el3 sys regs (in groups of 8 registers) using the
255	 *     crash buf to the crash console.
256	 *   - Print non el3 sys regs (in groups of 8 registers) using
257	 *     the crash buf to the crash console. A group may be
258	 *     interrupted in case a potential group of 128-bit
259	 *     sys regs needs to be printed.
260	 * ------------------------------------------------------------
261	 */
262do_crash_reporting:
263	/* Retrieve the crash buf from tpidr_el3 */
264	mrs	x0, tpidr_el3
265	/* Store x2 - x6, x30 in the crash buffer */
266	stp	x2, x3, [x0, #REGSZ * 2]
267	stp	x4, x5, [x0, #REGSZ * 4]
268	stp	x6, x30, [x0, #REGSZ * 6]
269	/* Initialize the crash console */
270	bl	plat_crash_console_init
271	/* Verify the console is initialized */
272	cbz	x0, crash_panic
273	/* Print the crash message. sp points to the crash message */
274	mov	x4, sp
275	bl	asm_print_str
276	/* Print spaces to align "x30" string */
277	mov	x0, #4
278	bl	print_alignment
279	/* Load the crash buf address */
280	mrs	x0, tpidr_el3
281	/* Report x30 first from the crash buf */
282	ldr	x4, [x0, #REGSZ * 7]
283
284#if ENABLE_PAUTH
285#if ENABLE_PAUTH == 2
286	/* Skip if not present in hardware */
287	is_feat_pauth_present_asm x0, x1
288	beq	1f
289#endif
290	/*
291	 * The assembler must see support for xpaci. So turn the compiler
292	 * extension on. GCC prior to 10 doesn't understand the PAuth extension
293	 * but it does understand armv8.3-a in general. Avoid using 8.3 if
294	 * the compiler understands "pauth" so we don't downgrade a higher
295	 * -march that was specified on the commandline.
296	 */
297#if __GNUC__ < 10
298	.arch armv8.3-a
299#else
300	.arch_extension pauth
301#endif
302	/* Demangle address */
303	xpaci	x4
3041:
305#endif
306	bl	asm_print_hex
307	bl	asm_print_newline
308	/* Load the crash buf address */
309	mrs	x0, tpidr_el3
310	/* Now mov x7 into crash buf */
311	str	x7, [x0, #REGSZ * 7]
312
313	/* Report x0 - x29 values stored in crash buf */
314	/* Store the ascii list pointer in x6 */
315	adr	x6, gp_regs
316	/* Print x0 to x7 from the crash buf */
317	bl	size_controlled_print
318	/* Store x8 - x15 in crash buf and print */
319	bl	str_in_crash_buf_print
320	/* Load the crash buf address */
321	mrs	x0, tpidr_el3
322	/* Store the rest of gp regs and print */
323	stp	x16, x17, [x0]
324	stp	x18, x19, [x0, #REGSZ * 2]
325	stp	x20, x21, [x0, #REGSZ * 4]
326	stp	x22, x23, [x0, #REGSZ * 6]
327	bl	size_controlled_print
328	/* Load the crash buf address */
329	mrs	x0, tpidr_el3
330	stp	x24, x25, [x0]
331	stp	x26, x27, [x0, #REGSZ * 2]
332	stp	x28, x29, [x0, #REGSZ * 4]
333	bl	size_controlled_print
334
335	/* Print the el3 sys registers */
336print_el3_sys_regs:
337	adr	x6, el3_sys_regs
338	mrs	x8, scr_el3
339	mrs	x9, sctlr_el3
340	mrs	x10, cptr_el3
341	mrs	x11, tcr_el3
342	mrs	x12, daif
343	mrs	x13, mair_el3
344	mrs	x14, spsr_el3
345	mrs	x15, elr_el3
346	bl	str_in_crash_buf_print
347	mrs	x8, ttbr0_el3
348	mrs	x9, esr_el3
349	mrs	x10, far_el3
350	bl	str_in_crash_buf_print
351
352	/* Print the non el3 sys registers */
353	adr	x6, non_el3_sys_regs
354	mrs	x8, mpidr_el1
355	mrs	x9, sp_el0
356	mrs	x10, isr_el1
357	bl	str_in_crash_buf_print
358
359#if CTX_INCLUDE_AARCH32_REGS
360	/* Print the AArch32 registers */
361	adr	x6, aarch32_regs
362	mrs	x8, dacr32_el2
363	mrs	x9, ifsr32_el2
364	bl	str_in_crash_buf_print
365#endif /* CTX_INCLUDE_AARCH32_REGS */
366
367	/* Get the cpu specific registers to report */
368	bl	do_cpu_reg_dump
369	bl	str_in_crash_buf_print
370
371	/* Print some platform registers */
372	plat_crash_print_regs
373
374	bl	plat_crash_console_flush
375
376	/* Done reporting */
377	no_ret	plat_panic_handler
378endfunc report_el3_panic
379
380#else	/* CRASH_REPORTING */
381func report_unhandled_exception
382report_unhandled_interrupt:
383	no_ret	plat_panic_handler
384endfunc report_unhandled_exception
385#endif	/* CRASH_REPORTING */
386
387func crash_panic
388	no_ret	plat_panic_handler
389endfunc crash_panic
390