xref: /rk3399_ARM-atf/bl31/aarch64/crash_reporting.S (revision 7303319b3823e9e33748d963e9173f3678aba4da)
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	prepare_crash_buf_save_x0_x1
205	adr	x0, excpt_msg
206	mov	sp, x0
207	/* This call will not return */
208	b	do_crash_reporting
209endfunc report_unhandled_exception
210
211	/* -----------------------------------------------------
212	 * This function allows to report a crash (if crash
213	 * reporting is enabled) when an unhandled interrupt
214	 * occurs. It prints the CPU state via the crash console
215	 * making use of the crash buf. This function will
216	 * not return.
217	 * -----------------------------------------------------
218	 */
219func report_unhandled_interrupt
220	prepare_crash_buf_save_x0_x1
221	adr	x0, intr_excpt_msg
222	mov	sp, x0
223	/* This call will not return */
224	b	do_crash_reporting
225endfunc report_unhandled_interrupt
226
227	/* -----------------------------------------------------
228	 * This function allows to report a crash from the lower
229	 * exception level (if crash reporting is enabled) when
230	 * lower_el_panic() is invoked from C Runtime.
231	 * It prints the CPU state via the crash console making
232	 * use of 'cpu_context' structure where general purpose
233	 * registers are saved and the crash buf.
234	 * This function will not return.
235	 * -----------------------------------------------------
236	 */
237func report_elx_panic
238	msr	spsel, #MODE_SP_ELX
239
240	/* Print the crash message */
241	adr	x4, excpt_msg_el
242	bl	asm_print_str
243
244	/* Report x0 - x29 values stored in 'gpregs_ctx' structure */
245	/* Store the ascii list pointer in x6 */
246	adr	x6, gp_regs
247	add	x7, sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0
248
249print_next:
250	ldrb	w4, [x6]
251	/* Test whether we are at end of list */
252	cbz	w4, print_x30
253	mov	x4, x6
254	/* asm_print_str updates x4 to point to next entry in list */
255	bl	asm_print_str
256	/* x0 = number of symbols printed + 1 */
257	sub	x0, x4, x6
258	/* Update x6 with the updated list pointer */
259	mov	x6, x4
260	bl	print_alignment
261	ldr	x4, [x7], #REGSZ
262	bl	asm_print_hex
263	bl	asm_print_newline
264	b	print_next
265
266print_x30:
267	adr	x4, x30_msg
268	bl	asm_print_str
269
270	/* Print spaces to align "x30" string */
271	mov	x0, #4
272	bl	print_alignment
273
274	/* Report x30 */
275	ldr	x4, [x7]
276
277	/* ----------------------------------------------------------------
278	 * Different virtual address space size can be defined for each EL.
279	 * Ensure that we use the proper one by reading the corresponding
280	 * TCR_ELx register.
281	 * ----------------------------------------------------------------
282	 */
283	cmp	x8, #MODE_EL2
284	b.lt	from_el1	/* EL1 */
285	mrs	x2, sctlr_el2
286	mrs	x1, tcr_el2
287
288	/* ----------------------------------------------------------------
289	 * Check if pointer authentication is enabled at the specified EL.
290	 * If it isn't, we can then skip stripping a PAC code.
291	 * ----------------------------------------------------------------
292	 */
293test_pauth:
294	tst	x2, #(SCTLR_EnIA_BIT | SCTLR_EnIB_BIT)
295	b.eq	no_pauth
296
297	/* Demangle address */
298	and	x1, x1, #0x3F	/* T0SZ = TCR_ELx[5:0] */
299	sub	x1, x1, #64
300	neg	x1, x1		/* bottom_pac_bit = 64 - T0SZ */
301	mov	x2, #-1
302	lsl	x2, x2, x1
303	bic	x4, x4, x2
304
305no_pauth:
306	bl	asm_print_hex
307	bl	asm_print_newline
308
309	/* tpidr_el3 contains the address to cpu_data structure */
310	per_cpu_cur percpu_data, x0, x2
311	/* Calculate the Crash buffer offset in cpu_data */
312	add	x0, x0, #CPU_DATA_CRASH_BUF
313	/* Store crash buffer address in tpidr_el3 */
314	msr	tpidr_el3, x0
315
316	/* Print the rest of crash dump */
317	b	print_el3_sys_regs
318
319from_el1:
320	mrs	x2, sctlr_el1
321	mrs	x1, tcr_el1
322	b	test_pauth
323endfunc	report_elx_panic
324
325	/* -----------------------------------------------------
326	 * This function allows to report a crash (if crash
327	 * reporting is enabled) when panic() is invoked from
328	 * C Runtime. It prints the CPU state via the crash
329	 * console making use of the crash buf. This function
330	 * will not return.
331	 * -----------------------------------------------------
332	 */
333func report_el3_panic
334	msr	spsel, #MODE_SP_ELX
335	prepare_crash_buf_save_x0_x1
336	adr	x0, panic_msg
337	mov	sp, x0
338	/* Fall through to 'do_crash_reporting' */
339
340	/* ------------------------------------------------------------
341	 * The common crash reporting functionality. It requires x0
342	 * and x1 has already been stored in crash buf, sp points to
343	 * crash message and tpidr_el3 contains the crash buf address.
344	 * The function does the following:
345	 *   - Retrieve the crash buffer from tpidr_el3
346	 *   - Store x2 to x6 in the crash buffer
347	 *   - Initialise the crash console.
348	 *   - Print the crash message by using the address in sp.
349	 *   - Print x30 value to the crash console.
350	 *   - Print x0 - x7 from the crash buf to the crash console.
351	 *   - Print x8 - x29 (in groups of 8 registers) using the
352	 *     crash buf to the crash console.
353	 *   - Print el3 sys regs (in groups of 8 registers) using the
354	 *     crash buf to the crash console.
355	 *   - Print non el3 sys regs (in groups of 8 registers) using
356	 *     the crash buf to the crash console. A group may be
357	 *     interrupted in case a potential group of 128-bit
358	 *     sys regs needs to be printed.
359	 * ------------------------------------------------------------
360	 */
361do_crash_reporting:
362	/* Retrieve the crash buf from tpidr_el3 */
363	mrs	x0, tpidr_el3
364	/* Store x2 - x6, x30 in the crash buffer */
365	stp	x2, x3, [x0, #REGSZ * 2]
366	stp	x4, x5, [x0, #REGSZ * 4]
367	stp	x6, x30, [x0, #REGSZ * 6]
368	/* Initialize the crash console */
369	bl	plat_crash_console_init
370	/* Verify the console is initialized */
371	cbz	x0, crash_panic
372	/* Print the crash message. sp points to the crash message */
373	mov	x4, sp
374	bl	asm_print_str
375	/* Print spaces to align "x30" string */
376	mov	x0, #4
377	bl	print_alignment
378	/* Load the crash buf address */
379	mrs	x0, tpidr_el3
380	/* Report x30 first from the crash buf */
381	ldr	x4, [x0, #REGSZ * 7]
382
383#if ENABLE_PAUTH
384#if ENABLE_PAUTH == 2
385	/* Skip if not present in hardware */
386	is_feat_pauth_present_asm x0, x1
387	beq	1f
388#endif
389	/*
390	 * The assembler must see support for xpaci. So turn the compiler
391	 * extension on. GCC prior to 10 doesn't understand the PAuth extension
392	 * but it does understand armv8.3-a in general. Avoid using 8.3 if
393	 * the compiler understands "pauth" so we don't downgrade a higher
394	 * -march that was specified on the commandline.
395	 */
396#if __GNUC__ < 10
397	.arch armv8.3-a
398#else
399	.arch_extension pauth
400#endif
401	/* Demangle address */
402	xpaci	x4
4031:
404#endif
405	bl	asm_print_hex
406	bl	asm_print_newline
407	/* Load the crash buf address */
408	mrs	x0, tpidr_el3
409	/* Now mov x7 into crash buf */
410	str	x7, [x0, #REGSZ * 7]
411
412	/* Report x0 - x29 values stored in crash buf */
413	/* Store the ascii list pointer in x6 */
414	adr	x6, gp_regs
415	/* Print x0 to x7 from the crash buf */
416	bl	size_controlled_print
417	/* Store x8 - x15 in crash buf and print */
418	bl	str_in_crash_buf_print
419	/* Load the crash buf address */
420	mrs	x0, tpidr_el3
421	/* Store the rest of gp regs and print */
422	stp	x16, x17, [x0]
423	stp	x18, x19, [x0, #REGSZ * 2]
424	stp	x20, x21, [x0, #REGSZ * 4]
425	stp	x22, x23, [x0, #REGSZ * 6]
426	bl	size_controlled_print
427	/* Load the crash buf address */
428	mrs	x0, tpidr_el3
429	stp	x24, x25, [x0]
430	stp	x26, x27, [x0, #REGSZ * 2]
431	stp	x28, x29, [x0, #REGSZ * 4]
432	bl	size_controlled_print
433
434	/* Print the el3 sys registers */
435print_el3_sys_regs:
436	adr	x6, el3_sys_regs
437	mrs	x8, scr_el3
438	mrs	x9, sctlr_el3
439	mrs	x10, cptr_el3
440	mrs	x11, tcr_el3
441	mrs	x12, daif
442	mrs	x13, mair_el3
443	mrs	x14, spsr_el3
444	mrs	x15, elr_el3
445	bl	str_in_crash_buf_print
446	mrs	x8, ttbr0_el3
447	mrs	x9, esr_el3
448	mrs	x10, far_el3
449	bl	str_in_crash_buf_print
450
451	/* Print the non el3 sys registers */
452	adr	x6, non_el3_sys_regs
453	mrs	x8, mpidr_el1
454	mrs	x9, sp_el0
455	mrs	x10, isr_el1
456	bl	str_in_crash_buf_print
457
458#if CTX_INCLUDE_AARCH32_REGS
459	/* Print the AArch32 registers */
460	adr	x6, aarch32_regs
461	mrs	x8, dacr32_el2
462	mrs	x9, ifsr32_el2
463	bl	str_in_crash_buf_print
464#endif /* CTX_INCLUDE_AARCH32_REGS */
465
466	/* Get the cpu specific registers to report */
467	bl	do_cpu_reg_dump
468	bl	str_in_crash_buf_print
469
470	/* Print some platform registers */
471	plat_crash_print_regs
472
473	bl	plat_crash_console_flush
474
475	/* Done reporting */
476	no_ret	plat_panic_handler
477endfunc report_el3_panic
478
479#else	/* CRASH_REPORTING */
480func report_unhandled_exception
481report_unhandled_interrupt:
482	no_ret	plat_panic_handler
483endfunc report_unhandled_exception
484#endif	/* CRASH_REPORTING */
485
486func crash_panic
487	no_ret	plat_panic_handler
488endfunc crash_panic
489