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