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/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 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" 60 61 /* 62 * Helper function to print from crash buf. 63 * The print loop is controlled by the buf size and 64 * ascii reg name list which is passed in x6. The 65 * function returns the crash buf address in x0. 66 * Clobbers : x0 - x7, x20, sp 67 */ 68func size_controlled_print_helper 69#if ENABLE_FEAT_D128 70size_controlled_print_128: 71 /* Set flag to print 128-bit registers */ 72 mov x20, #1 73 b 1f 74 75size_controlled_print: 76 /* Set flag to print 64-bit registers */ 77 mov x20, #0 781: 79#else 80size_controlled_print: 81#endif 82 /* Save the lr */ 83 mov sp, x30 84 /* load the crash buf address */ 85 mrs x7, tpidr_el3 86test_size_list: 87 /* Calculate x5 always as it will be clobbered by asm_print_hex */ 88 mrs x5, tpidr_el3 89 add x5, x5, #CPU_DATA_CRASH_BUF_BYTES 90 /* Test whether we have reached end of crash buf */ 91 cmp x7, x5 92 b.eq exit_size_print 93 ldrb w4, [x6] 94 /* Test whether we are at end of list */ 95 cbz w4, exit_size_print 96 mov x4, x6 97 /* asm_print_str updates x4 to point to next entry in list */ 98 bl asm_print_str 99 /* x0 = number of symbols printed + 1 */ 100 sub x0, x4, x6 101 /* update x6 with the updated list pointer */ 102 mov x6, x4 103 bl print_alignment 104 /* Print the high 64 bits (or whole 64-bit register) */ 105 ldr x4, [x7], #REGSZ 106 bl asm_print_hex 107#if ENABLE_FEAT_D128 108 cbz x20, 2f 109 /* Print the low 64 bits in case of a 128-bit register */ 110 ldr x4, [x7], #REGSZ 111 bl asm_print_hex 1122: 113#endif 114 bl asm_print_newline 115 b test_size_list 116exit_size_print: 117 mov x30, sp 118 ret 119endfunc size_controlled_print_helper 120 121 /* ----------------------------------------------------- 122 * This function calculates and prints required number 123 * of space characters followed by "= 0x", based on the 124 * length of ascii register name. 125 * x0: length of ascii register name + 1 126 * ------------------------------------------------------ 127 */ 128func print_alignment 129 /* The minimum ascii length is 3, e.g. for "x0" */ 130 adr x4, print_spacer - 3 131 add x4, x4, x0 132 b asm_print_str 133endfunc print_alignment 134 135 /* 136 * Helper function to store x8 - x15 registers to 137 * the crash buf. The system registers values are 138 * copied to x8 to x15 by the caller which are then 139 * copied to the crash buf by this function. 140 * x0 points to the crash buf. It then calls 141 * size_controlled_print to print to console. 142 * Clobbers : x0 - x7, x20, sp 143 */ 144func str_in_crash_buf_print 145 /* restore the crash buf address in x0 */ 146 mrs x0, tpidr_el3 147 stp x8, x9, [x0] 148 stp x10, x11, [x0, #REGSZ * 2] 149 stp x12, x13, [x0, #REGSZ * 4] 150 stp x14, x15, [x0, #REGSZ * 6] 151 b size_controlled_print 152endfunc str_in_crash_buf_print 153 154 /* 155 * An equivalent helper function for storing x8 - x15 156 * registers in a different order inside the crash buf. 157 * In the end the function size_controlled_print_128 is 158 * called to print the registers to the console. 159 * Clobbers : x0 - x7, x20, sp 160 */ 161func str_in_crash_buf_print_128 162 /* restore the crash buf address in x0 */ 163 mrs x0, tpidr_el3 164 stp x8, x9, [x0] 165 stp x10, x11, [x0, #REGSZ * 2] 166 stp x12, x13, [x0, #REGSZ * 4] 167 stp x14, x15, [x0, #REGSZ * 6] 168 b size_controlled_print_128 169endfunc str_in_crash_buf_print_128 170 171 /* ------------------------------------------------------ 172 * This macro calculates the offset to crash buf from 173 * cpu_data and stores it in tpidr_el3. It also saves x0 174 * and x1 in the crash buf by using sp as a temporary 175 * register. 176 * ------------------------------------------------------ 177 */ 178 .macro prepare_crash_buf_save_x0_x1 179 /* we can corrupt this reg to free up x0 */ 180 mov sp, x0 181 /* tpidr_el3 contains the address to cpu_data structure */ 182 per_cpu_cur percpu_data, x0, x2 183 /* Calculate the Crash buffer offset in cpu_data */ 184 add x0, x0, #CPU_DATA_CRASH_BUF 185 /* Store crash buffer address in tpidr_el3 */ 186 msr tpidr_el3, x0 187 str x1, [x0, #REGSZ] 188 mov x1, sp 189 str x1, [x0] 190 .endm 191 192 /* ----------------------------------------------------- 193 * This function allows to report a crash (if crash 194 * reporting is enabled) when an unhandled exception 195 * occurs. It prints the CPU state via the crash console 196 * making use of the crash buf. This function will 197 * not return. 198 * ----------------------------------------------------- 199 */ 200func report_unhandled_exception 201 /* Switch to SP_ELx */ 202 msr spsel, #MODE_SP_ELX 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 (if crash 228 * reporting is enabled) when panic() is invoked from 229 * C Runtime. It prints the CPU state via the crash 230 * console making use of the crash buf. This function 231 * will not return. 232 * ----------------------------------------------------- 233 */ 234func report_el3_panic 235 msr spsel, #MODE_SP_ELX 236 prepare_crash_buf_save_x0_x1 237 adr x0, panic_msg 238 mov sp, x0 239 /* Fall through to 'do_crash_reporting' */ 240 241 /* ------------------------------------------------------------ 242 * The common crash reporting functionality. It requires x0 243 * and x1 has already been stored in crash buf, sp points to 244 * crash message and tpidr_el3 contains the crash buf address. 245 * The function does the following: 246 * - Retrieve the crash buffer from tpidr_el3 247 * - Store x2 to x6 in the crash buffer 248 * - Initialise the crash console. 249 * - Print the crash message by using the address in sp. 250 * - Print x30 value to the crash console. 251 * - Print x0 - x7 from the crash buf to the crash console. 252 * - Print x8 - x29 (in groups of 8 registers) using the 253 * crash buf to the crash console. 254 * - Print el3 sys regs (in groups of 8 registers) using the 255 * crash buf to the crash console. 256 * - Print non el3 sys regs (in groups of 8 registers) using 257 * the crash buf to the crash console. A group may be 258 * interrupted in case a potential group of 128-bit 259 * sys regs needs to be printed. 260 * ------------------------------------------------------------ 261 */ 262do_crash_reporting: 263 /* Retrieve the crash buf from tpidr_el3 */ 264 mrs x0, tpidr_el3 265 /* Store x2 - x6, x30 in the crash buffer */ 266 stp x2, x3, [x0, #REGSZ * 2] 267 stp x4, x5, [x0, #REGSZ * 4] 268 stp x6, x30, [x0, #REGSZ * 6] 269 /* Initialize the crash console */ 270 bl plat_crash_console_init 271 /* Verify the console is initialized */ 272 cbz x0, crash_panic 273 /* Print the crash message. sp points to the crash message */ 274 mov x4, sp 275 bl asm_print_str 276 /* Print spaces to align "x30" string */ 277 mov x0, #4 278 bl print_alignment 279 /* Load the crash buf address */ 280 mrs x0, tpidr_el3 281 /* Report x30 first from the crash buf */ 282 ldr x4, [x0, #REGSZ * 7] 283 284#if ENABLE_PAUTH 285#if ENABLE_PAUTH == 2 286 /* Skip if not present in hardware */ 287 is_feat_pauth_present_asm x0, x1 288 beq 1f 289#endif 290 /* 291 * The assembler must see support for xpaci. So turn the compiler 292 * extension on. GCC prior to 10 doesn't understand the PAuth extension 293 * but it does understand armv8.3-a in general. Avoid using 8.3 if 294 * the compiler understands "pauth" so we don't downgrade a higher 295 * -march that was specified on the commandline. 296 */ 297#if __GNUC__ < 10 298 .arch armv8.3-a 299#else 300 .arch_extension pauth 301#endif 302 /* Demangle address */ 303 xpaci x4 3041: 305#endif 306 bl asm_print_hex 307 bl asm_print_newline 308 /* Load the crash buf address */ 309 mrs x0, tpidr_el3 310 /* Now mov x7 into crash buf */ 311 str x7, [x0, #REGSZ * 7] 312 313 /* Report x0 - x29 values stored in crash buf */ 314 /* Store the ascii list pointer in x6 */ 315 adr x6, gp_regs 316 /* Print x0 to x7 from the crash buf */ 317 bl size_controlled_print 318 /* Store x8 - x15 in crash buf and print */ 319 bl str_in_crash_buf_print 320 /* Load the crash buf address */ 321 mrs x0, tpidr_el3 322 /* Store the rest of gp regs and print */ 323 stp x16, x17, [x0] 324 stp x18, x19, [x0, #REGSZ * 2] 325 stp x20, x21, [x0, #REGSZ * 4] 326 stp x22, x23, [x0, #REGSZ * 6] 327 bl size_controlled_print 328 /* Load the crash buf address */ 329 mrs x0, tpidr_el3 330 stp x24, x25, [x0] 331 stp x26, x27, [x0, #REGSZ * 2] 332 stp x28, x29, [x0, #REGSZ * 4] 333 bl size_controlled_print 334 335 /* Print the el3 sys registers */ 336print_el3_sys_regs: 337 adr x6, el3_sys_regs 338 mrs x8, scr_el3 339 mrs x9, sctlr_el3 340 mrs x10, cptr_el3 341 mrs x11, tcr_el3 342 mrs x12, daif 343 mrs x13, mair_el3 344 mrs x14, spsr_el3 345 mrs x15, elr_el3 346 bl str_in_crash_buf_print 347 mrs x8, ttbr0_el3 348 mrs x9, esr_el3 349 mrs x10, far_el3 350 bl str_in_crash_buf_print 351 352 /* Print the non el3 sys registers */ 353 adr x6, non_el3_sys_regs 354 mrs x8, mpidr_el1 355 mrs x9, sp_el0 356 mrs x10, isr_el1 357 bl str_in_crash_buf_print 358 359#if CTX_INCLUDE_AARCH32_REGS 360 /* Print the AArch32 registers */ 361 adr x6, aarch32_regs 362 mrs x8, dacr32_el2 363 mrs x9, ifsr32_el2 364 bl str_in_crash_buf_print 365#endif /* CTX_INCLUDE_AARCH32_REGS */ 366 367 /* Get the cpu specific registers to report */ 368 bl do_cpu_reg_dump 369 bl str_in_crash_buf_print 370 371 /* Print some platform registers */ 372 plat_crash_print_regs 373 374 bl plat_crash_console_flush 375 376 /* Done reporting */ 377 no_ret plat_panic_handler 378endfunc report_el3_panic 379 380#else /* CRASH_REPORTING */ 381func report_unhandled_exception 382report_unhandled_interrupt: 383 no_ret plat_panic_handler 384endfunc report_unhandled_exception 385#endif /* CRASH_REPORTING */ 386 387func crash_panic 388 no_ret plat_panic_handler 389endfunc crash_panic 390