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