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