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