xref: /rk3399_ARM-atf/bl31/aarch64/crash_reporting.S (revision a4defaefe65379554f464e9cf2b4f4d9818740aa)
1/*
2 * Copyright (c) 2014-2025, 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	.globl	report_elx_panic
21
22#if CRASH_REPORTING
23
24	/* ------------------------------------------------------
25	 * The below section deals with dumping the system state
26	 * when an unhandled exception is taken in EL3.
27	 * The layout and the names of the registers which will
28	 * be dumped during a unhandled exception is given below.
29	 * ------------------------------------------------------
30	 */
31.section .rodata.crash_prints, "aS"
32print_spacer:
33	.asciz	"             = 0x"
34
35gp_regs:
36	.asciz	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",\
37		"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",\
38		"x16", "x17", "x18", "x19", "x20", "x21", "x22",\
39		"x23", "x24", "x25", "x26", "x27", "x28", "x29", ""
40el3_sys_regs:
41	.asciz	"scr_el3", "sctlr_el3", "cptr_el3", "tcr_el3",\
42		"daif", "mair_el3", "spsr_el3", "elr_el3", "ttbr0_el3",\
43		"esr_el3", "far_el3", ""
44
45non_el3_sys_regs:
46	.asciz	"mpidr_el1", "sp_el0", "isr_el1", ""
47
48#if CTX_INCLUDE_AARCH32_REGS
49aarch32_regs:
50	.asciz	"dacr32_el2", "ifsr32_el2", ""
51#endif /* CTX_INCLUDE_AARCH32_REGS */
52
53panic_msg:
54	.asciz "PANIC in EL3.\nx30"
55excpt_msg:
56	.asciz "Unhandled Exception in EL3.\nx30"
57intr_excpt_msg:
58	.ascii "Unhandled Interrupt Exception in EL3.\n"
59x30_msg:
60	.asciz "x30"
61excpt_msg_el:
62	.asciz "Unhandled Exception from lower EL.\n"
63
64	/*
65	 * Helper function to print from crash buf.
66	 * The print loop is controlled by the buf size and
67	 * ascii reg name list which is passed in x6. The
68	 * function returns the crash buf address in x0.
69	 * Clobbers : x0 - x7, x20, sp
70	 */
71func size_controlled_print_helper
72#if ENABLE_FEAT_D128
73size_controlled_print_128:
74	/* Set flag to print 128-bit registers */
75	mov	x20, #1
76	b	1f
77
78size_controlled_print:
79	/* Set flag to print 64-bit registers */
80	mov	x20, #0
811:
82#else
83size_controlled_print:
84#endif
85	/* Save the lr */
86	mov	sp, x30
87	/* load the crash buf address */
88	mrs	x7, tpidr_el3
89test_size_list:
90	/* Calculate x5 always as it will be clobbered by asm_print_hex */
91	mrs	x5, tpidr_el3
92	add	x5, x5, #CPU_DATA_CRASH_BUF_BYTES
93	/* Test whether we have reached end of crash buf */
94	cmp	x7, x5
95	b.eq	exit_size_print
96	ldrb	w4, [x6]
97	/* Test whether we are at end of list */
98	cbz	w4, exit_size_print
99	mov	x4, x6
100	/* asm_print_str updates x4 to point to next entry in list */
101	bl	asm_print_str
102	/* x0 = number of symbols printed + 1 */
103	sub	x0, x4, x6
104	/* update x6 with the updated list pointer */
105	mov	x6, x4
106	bl	print_alignment
107	/* Print the high 64 bits (or whole 64-bit register) */
108	ldr	x4, [x7], #REGSZ
109	bl	asm_print_hex
110#if ENABLE_FEAT_D128
111	cbz	x20, 2f
112	/* Print the low 64 bits in case of a 128-bit register */
113	ldr	x4, [x7], #REGSZ
114	bl	asm_print_hex
1152:
116#endif
117	bl	asm_print_newline
118	b	test_size_list
119exit_size_print:
120	mov	x30, sp
121	ret
122endfunc size_controlled_print_helper
123
124	/* -----------------------------------------------------
125	 * This function calculates and prints required number
126	 * of space characters followed by "= 0x", based on the
127	 * length of ascii register name.
128 	 * x0: length of ascii register name + 1
129	 * ------------------------------------------------------
130 	 */
131func print_alignment
132	/* The minimum ascii length is 3, e.g. for "x0" */
133	adr	x4, print_spacer - 3
134	add	x4, x4, x0
135	b	asm_print_str
136endfunc print_alignment
137
138	/*
139	 * Helper function to store x8 - x15 registers to
140	 * the crash buf. The system registers values are
141	 * copied to x8 to x15 by the caller which are then
142	 * copied to the crash buf by this function.
143	 * x0 points to the crash buf. It then calls
144	 * size_controlled_print to print to console.
145	 * Clobbers : x0 - x7, x20, sp
146	 */
147func str_in_crash_buf_print
148	/* restore the crash buf address in x0 */
149	mrs	x0, tpidr_el3
150	stp	x8, x9, [x0]
151	stp	x10, x11, [x0, #REGSZ * 2]
152	stp	x12, x13, [x0, #REGSZ * 4]
153	stp	x14, x15, [x0, #REGSZ * 6]
154	b	size_controlled_print
155endfunc str_in_crash_buf_print
156
157	/*
158	 * An equivalent helper function for storing x8 - x15
159	 * registers in a different order inside the crash buf.
160	 * In the end the function size_controlled_print_128 is
161	 * called to print the registers to the console.
162	 * Clobbers : x0 - x7, x20, sp
163	 */
164func str_in_crash_buf_print_128
165	/* restore the crash buf address in x0 */
166	mrs	x0, tpidr_el3
167	stp	x8, x9, [x0]
168	stp	x10, x11, [x0, #REGSZ * 2]
169	stp	x12, x13, [x0, #REGSZ * 4]
170	stp	x14, x15, [x0, #REGSZ * 6]
171	b	size_controlled_print_128
172endfunc str_in_crash_buf_print_128
173
174	/* ------------------------------------------------------
175	 * This macro calculates the offset to crash buf from
176	 * cpu_data and stores it in tpidr_el3. It also saves x0
177	 * and x1 in the crash buf by using sp as a temporary
178	 * register.
179	 * ------------------------------------------------------
180	 */
181	.macro prepare_crash_buf_save_x0_x1
182	/* we can corrupt this reg to free up x0 */
183	mov	sp, x0
184	/* tpidr_el3 contains the address to cpu_data structure */
185	per_cpu_cur percpu_data, x0, x2
186	/* Calculate the Crash buffer offset in cpu_data */
187	add	x0, x0, #CPU_DATA_CRASH_BUF
188	/* Store crash buffer address in tpidr_el3 */
189	msr	tpidr_el3, x0
190	str	x1, [x0, #REGSZ]
191	mov	x1, sp
192	str	x1, [x0]
193	.endm
194
195	/* -----------------------------------------------------
196	 * This function allows to report a crash (if crash
197	 * reporting is enabled) when an unhandled exception
198	 * occurs. It prints the CPU state via the crash console
199	 * making use of the crash buf. This function will
200	 * not return.
201	 * -----------------------------------------------------
202	 */
203func report_unhandled_exception
204	/* Switch to SP_ELx */
205	msr     spsel, #MODE_SP_ELX
206	prepare_crash_buf_save_x0_x1
207	adr	x0, excpt_msg
208	mov	sp, x0
209	/* This call will not return */
210	b	do_crash_reporting
211endfunc report_unhandled_exception
212
213	/* -----------------------------------------------------
214	 * This function allows to report a crash (if crash
215	 * reporting is enabled) when an unhandled interrupt
216	 * occurs. It prints the CPU state via the crash console
217	 * making use of the crash buf. This function will
218	 * not return.
219	 * -----------------------------------------------------
220	 */
221func report_unhandled_interrupt
222	prepare_crash_buf_save_x0_x1
223	adr	x0, intr_excpt_msg
224	mov	sp, x0
225	/* This call will not return */
226	b	do_crash_reporting
227endfunc report_unhandled_interrupt
228
229	/* -----------------------------------------------------
230	 * This function allows to report a crash from the lower
231	 * exception level (if crash reporting is enabled) when
232	 * lower_el_panic() is invoked from C Runtime.
233	 * It prints the CPU state via the crash console making
234	 * use of 'cpu_context' structure where general purpose
235	 * registers are saved and the crash buf.
236	 * This function will not return.
237	 * -----------------------------------------------------
238	 */
239func report_elx_panic
240	msr	spsel, #MODE_SP_ELX
241
242	/* Print the crash message */
243	adr	x4, excpt_msg_el
244	bl	asm_print_str
245
246	/* Report x0 - x29 values stored in 'gpregs_ctx' structure */
247	/* Store the ascii list pointer in x6 */
248	adr	x6, gp_regs
249	add	x7, sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0
250
251print_next:
252	ldrb	w4, [x6]
253	/* Test whether we are at end of list */
254	cbz	w4, print_x30
255	mov	x4, x6
256	/* asm_print_str updates x4 to point to next entry in list */
257	bl	asm_print_str
258	/* x0 = number of symbols printed + 1 */
259	sub	x0, x4, x6
260	/* Update x6 with the updated list pointer */
261	mov	x6, x4
262	bl	print_alignment
263	ldr	x4, [x7], #REGSZ
264	bl	asm_print_hex
265	bl	asm_print_newline
266	b	print_next
267
268print_x30:
269	adr	x4, x30_msg
270	bl	asm_print_str
271
272	/* Print spaces to align "x30" string */
273	mov	x0, #4
274	bl	print_alignment
275
276	/* Report x30 */
277	ldr	x4, [x7]
278
279	/* ----------------------------------------------------------------
280	 * Different virtual address space size can be defined for each EL.
281	 * Ensure that we use the proper one by reading the corresponding
282	 * TCR_ELx register.
283	 * ----------------------------------------------------------------
284	 */
285	cmp	x8, #MODE_EL2
286	b.lt	from_el1	/* EL1 */
287	mrs	x2, sctlr_el2
288	mrs	x1, tcr_el2
289
290	/* ----------------------------------------------------------------
291	 * Check if pointer authentication is enabled at the specified EL.
292	 * If it isn't, we can then skip stripping a PAC code.
293	 * ----------------------------------------------------------------
294	 */
295test_pauth:
296	tst	x2, #(SCTLR_EnIA_BIT | SCTLR_EnIB_BIT)
297	b.eq	no_pauth
298
299	/* Demangle address */
300	and	x1, x1, #0x3F	/* T0SZ = TCR_ELx[5:0] */
301	sub	x1, x1, #64
302	neg	x1, x1		/* bottom_pac_bit = 64 - T0SZ */
303	mov	x2, #-1
304	lsl	x2, x2, x1
305	bic	x4, x4, x2
306
307no_pauth:
308	bl	asm_print_hex
309	bl	asm_print_newline
310
311	/* tpidr_el3 contains the address to cpu_data structure */
312	per_cpu_cur percpu_data, x0, x2
313	/* Calculate the Crash buffer offset in cpu_data */
314	add	x0, x0, #CPU_DATA_CRASH_BUF
315	/* Store crash buffer address in tpidr_el3 */
316	msr	tpidr_el3, x0
317
318	/* Print the rest of crash dump */
319	b	print_el3_sys_regs
320
321from_el1:
322	mrs	x2, sctlr_el1
323	mrs	x1, tcr_el1
324	b	test_pauth
325endfunc	report_elx_panic
326
327	/* -----------------------------------------------------
328	 * This function allows to report a crash (if crash
329	 * reporting is enabled) when panic() is invoked from
330	 * C Runtime. It prints the CPU state via the crash
331	 * console making use of the crash buf. This function
332	 * will not return.
333	 * -----------------------------------------------------
334	 */
335func report_el3_panic
336	msr	spsel, #MODE_SP_ELX
337	prepare_crash_buf_save_x0_x1
338	adr	x0, panic_msg
339	mov	sp, x0
340	/* Fall through to 'do_crash_reporting' */
341
342	/* ------------------------------------------------------------
343	 * The common crash reporting functionality. It requires x0
344	 * and x1 has already been stored in crash buf, sp points to
345	 * crash message and tpidr_el3 contains the crash buf address.
346	 * The function does the following:
347	 *   - Retrieve the crash buffer from tpidr_el3
348	 *   - Store x2 to x6 in the crash buffer
349	 *   - Initialise the crash console.
350	 *   - Print the crash message by using the address in sp.
351	 *   - Print x30 value to the crash console.
352	 *   - Print x0 - x7 from the crash buf to the crash console.
353	 *   - Print x8 - x29 (in groups of 8 registers) using the
354	 *     crash buf to the crash console.
355	 *   - Print el3 sys regs (in groups of 8 registers) using the
356	 *     crash buf to the crash console.
357	 *   - Print non el3 sys regs (in groups of 8 registers) using
358	 *     the crash buf to the crash console. A group may be
359	 *     interrupted in case a potential group of 128-bit
360	 *     sys regs needs to be printed.
361	 * ------------------------------------------------------------
362	 */
363do_crash_reporting:
364	/* Retrieve the crash buf from tpidr_el3 */
365	mrs	x0, tpidr_el3
366	/* Store x2 - x6, x30 in the crash buffer */
367	stp	x2, x3, [x0, #REGSZ * 2]
368	stp	x4, x5, [x0, #REGSZ * 4]
369	stp	x6, x30, [x0, #REGSZ * 6]
370	/* Initialize the crash console */
371	bl	plat_crash_console_init
372	/* Verify the console is initialized */
373	cbz	x0, crash_panic
374	/* Print the crash message. sp points to the crash message */
375	mov	x4, sp
376	bl	asm_print_str
377	/* Print spaces to align "x30" string */
378	mov	x0, #4
379	bl	print_alignment
380	/* Load the crash buf address */
381	mrs	x0, tpidr_el3
382	/* Report x30 first from the crash buf */
383	ldr	x4, [x0, #REGSZ * 7]
384
385#if ENABLE_PAUTH
386#if ENABLE_PAUTH == 2
387	/* Skip if not present in hardware */
388	is_feat_pauth_present_asm x0, x1
389	beq	1f
390#endif
391	/*
392	 * The assembler must see support for xpaci. So turn the compiler
393	 * extension on. GCC prior to 10 doesn't understand the PAuth extension
394	 * but it does understand armv8.3-a in general. Avoid using 8.3 if
395	 * the compiler understands "pauth" so we don't downgrade a higher
396	 * -march that was specified on the commandline.
397	 */
398#if __GNUC__ < 10
399	.arch armv8.3-a
400#else
401	.arch_extension pauth
402#endif
403	/* Demangle address */
404	xpaci	x4
4051:
406#endif
407	bl	asm_print_hex
408	bl	asm_print_newline
409	/* Load the crash buf address */
410	mrs	x0, tpidr_el3
411	/* Now mov x7 into crash buf */
412	str	x7, [x0, #REGSZ * 7]
413
414	/* Report x0 - x29 values stored in crash buf */
415	/* Store the ascii list pointer in x6 */
416	adr	x6, gp_regs
417	/* Print x0 to x7 from the crash buf */
418	bl	size_controlled_print
419	/* Store x8 - x15 in crash buf and print */
420	bl	str_in_crash_buf_print
421	/* Load the crash buf address */
422	mrs	x0, tpidr_el3
423	/* Store the rest of gp regs and print */
424	stp	x16, x17, [x0]
425	stp	x18, x19, [x0, #REGSZ * 2]
426	stp	x20, x21, [x0, #REGSZ * 4]
427	stp	x22, x23, [x0, #REGSZ * 6]
428	bl	size_controlled_print
429	/* Load the crash buf address */
430	mrs	x0, tpidr_el3
431	stp	x24, x25, [x0]
432	stp	x26, x27, [x0, #REGSZ * 2]
433	stp	x28, x29, [x0, #REGSZ * 4]
434	bl	size_controlled_print
435
436	/* Print the el3 sys registers */
437print_el3_sys_regs:
438	adr	x6, el3_sys_regs
439	mrs	x8, scr_el3
440	mrs	x9, sctlr_el3
441	mrs	x10, cptr_el3
442	mrs	x11, tcr_el3
443	mrs	x12, daif
444	mrs	x13, mair_el3
445	mrs	x14, spsr_el3
446	mrs	x15, elr_el3
447	bl	str_in_crash_buf_print
448	mrs	x8, ttbr0_el3
449	mrs	x9, esr_el3
450	mrs	x10, far_el3
451	bl	str_in_crash_buf_print
452
453	/* Print the non el3 sys registers */
454	adr	x6, non_el3_sys_regs
455	mrs	x8, mpidr_el1
456	mrs	x9, sp_el0
457	mrs	x10, isr_el1
458	bl	str_in_crash_buf_print
459
460#if CTX_INCLUDE_AARCH32_REGS
461	/* Print the AArch32 registers */
462	adr	x6, aarch32_regs
463	mrs	x8, dacr32_el2
464	mrs	x9, ifsr32_el2
465	bl	str_in_crash_buf_print
466#endif /* CTX_INCLUDE_AARCH32_REGS */
467
468	/* Get the cpu specific registers to report */
469	bl	do_cpu_reg_dump
470	bl	str_in_crash_buf_print
471
472	/* Print some platform registers */
473	plat_crash_print_regs
474
475	bl	plat_crash_console_flush
476
477	/* Done reporting */
478	no_ret	plat_panic_handler
479endfunc report_el3_panic
480
481#else	/* CRASH_REPORTING */
482func report_unhandled_exception
483report_unhandled_interrupt:
484	no_ret	plat_panic_handler
485endfunc report_unhandled_exception
486#endif	/* CRASH_REPORTING */
487
488func crash_panic
489	no_ret	plat_panic_handler
490endfunc crash_panic
491