diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 4f1638a99..f1a26f42e 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -2550,9 +2550,6 @@ as Group 0 secure interrupt, Group 1 secure interrupt or Group 1 NS interrupt. Crash Reporting mechanism (in BL31) ----------------------------------- -NOTE: This section assumes that your platform is enabling the MULTI_CONSOLE_API -flag in its platform.mk. Not using this flag is deprecated for new platforms. - BL31 implements a crash reporting mechanism which prints the various registers of the CPU to enable quick crash analysis and debugging. This mechanism relies on the platform implementating ``plat_crash_console_init``, @@ -2564,15 +2561,18 @@ makefiles in order to benefit from them. By default, they will cause the crash output to be routed over the normal console infrastructure and get printed on consoles configured to output in crash state. ``console_set_scope()`` can be used to control whether a console is used for crash output. +NOTE: Platforms are responsible for making sure that they only mark consoles for +use in the crash scope that are able to support this, i.e. that are written in +assembly and conform with the register clobber rules for putc() (x0-x2, x16-x17) +and flush() (x0-x3, x16-x17) crash callbacks. In some cases (such as debugging very early crashes that happen before the normal boot console can be set up), platforms may want to control crash output -more explicitly. For these, the following functions can be overridden by -platform code. They are executed outside of a C environment and without a stack. - -If this behaviour is not desirable, the platform may implement functions that -redirect the prints to the console driver (``console_xxx_core_init``, etc). Most -platforms (including Arm platforms) do this and they can be used as an example. +more explicitly. These platforms may instead provide custom implementations for +these. They are executed outside of a C environment and without a stack. Many +console drivers provide functions named ``console_xxx_core_init/putc/flush`` +that are designed to be used by these functions. See Arm platforms (like juno) +for an example of this. Function : plat\_crash\_console\_init [mandatory] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2586,28 +2586,6 @@ This API is used by the crash reporting mechanism to initialize the crash console. It must only use the general purpose registers x0 through x7 to do the initialization and returns 1 on success. -When using the sample implementation, if you are trying to debug crashes before -the console driver would normally get registered, you can use this to register a -driver from assembly with hardcoded parameters. For example, you could register -the 16550 driver like this: - -:: - - .section .data.crash_console /* Reserve space for console structure */ - crash_console: - .zero 6 * 8 /* console_16550_t has 6 8-byte words */ - func plat_crash_console_init - ldr x0, =YOUR_16550_BASE_ADDR - ldr x1, =YOUR_16550_SRCCLK_IN_HZ - ldr x2, =YOUR_16550_TARGET_BAUD_RATE - adrp x3, crash_console - add x3, x3, :lo12:crash_console - b console_16550_register /* tail call, returns 1 on success */ - endfunc plat_crash_console_init - -If you're trying to debug crashes in BL1, you can call the -``console_xxx_core_init`` function exported by some console drivers from here. - Function : plat\_crash\_console\_putc [mandatory] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2621,12 +2599,6 @@ designated crash console. It must only use general purpose registers x1 and x2 to do its work. The parameter and the return value are in general purpose register x0. -If you have registered a normal console driver in ``plat_crash_console_init``, -you can keep the sample implementation here (which calls ``console_putc()``). - -If you're trying to debug crashes in BL1, you can call the -``console_xxx_core_putc`` function exported by some console drivers from here. - Function : plat\_crash\_console\_flush [mandatory] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2640,12 +2612,6 @@ data on the designated crash console. It should only use general purpose registers x0 through x5 to do its work. The return value is 0 on successful completion; otherwise the return value is -1. -If you have registered a normal console driver in ``plat_crash_console_init``, -you can keep the sample implementation here (which calls ``console_flush()``). - -If you're trying to debug crashes in BL1, you can call the console_xx_core_flush -function exported by some console drivers from here. - External Abort handling and RAS Support --------------------------------------- diff --git a/drivers/console/aarch64/multi_console.S b/drivers/console/aarch64/multi_console.S index 7f076c623..95423b027 100644 --- a/drivers/console/aarch64/multi_console.S +++ b/drivers/console/aarch64/multi_console.S @@ -16,6 +16,7 @@ .globl console_putc .globl console_getc .globl console_flush + .globl console_list /* * The console list pointer is in the data section and not in diff --git a/plat/common/aarch64/crash_console_helpers.S b/plat/common/aarch64/crash_console_helpers.S index 55e7abbfc..8f8ca1124 100644 --- a/plat/common/aarch64/crash_console_helpers.S +++ b/plat/common/aarch64/crash_console_helpers.S @@ -20,53 +20,163 @@ #error "This crash console implementation only works with the MULTI_CONSOLE_API!" #endif - /* ----------------------------------------------------- + /* + * Spinlock to syncronize access to crash_console_triggered. We cannot + * acquire spinlocks when the cache is disabled, so in some cases (like + * late during CPU suspend) some risk remains. + */ +.section .data.crash_console_spinlock + define_asm_spinlock crash_console_spinlock + + /* + * Flag to make sure that only one CPU can write a crash dump even if + * multiple crash at the same time. Interleaving crash dumps on the same + * console would just make the output unreadable, so it's better to only + * get a single but uncorrupted dump. This also means that we don't have + * to duplicate the reg_stash below for each CPU. + */ +.section .data.crash_console_triggered + crash_console_triggered: .byte 0 + + /* + * Space to stash away some register values while we're calling into + * console drivers and don't have a real stack available. We need x14, + * x15 and x30 for bookkeeping within the plat_crash_console functions + * themselves, and some console drivers use x16 and x17 as additional + * scratch space that is not preserved by the main crash reporting + * framework. (Note that x16 and x17 should really never be expected to + * retain their values across any function call, even between carefully + * designed assembly functions, since the linker is always free to + * insert a function call veneer that uses these registers as scratch + * space at any time. The current crash reporting framework doesn't + * really respect that, but since TF is usually linked as a single + * contiguous binary of less than 128MB, it seems to work in practice.) + */ +.section .data.crash_console_reg_stash + .align 3 + crash_console_reg_stash: .quad 0, 0, 0, 0, 0 + + /* -------------------------------------------------------------------- * int plat_crash_console_init(void) - * Use normal console by default. Switch it to crash - * mode so serial consoles become active again. - * NOTE: This default implementation will only work for - * crashes that occur after a normal console (marked - * valid for the crash state) has been registered with - * the console framework. To debug crashes that occur - * earlier, the platform has to override these functions - * with an implementation that initializes a console - * driver with hardcoded parameters. See - * docs/porting-guide.rst for more information. - * ----------------------------------------------------- + * Takes the crash console spinlock (if possible) and checks the trigger + * flag to make sure we're the first CPU to dump. If not, return an + * error (so crash dumping will fail but the CPU will still call + * plat_panic_handler() which may do important platform-specific tasks + * that may be needed on all crashing CPUs). In either case, the lock + * will be released so other CPUs can make forward progress on this. + * Clobbers: x0 - x4, x30 + * -------------------------------------------------------------------- */ func plat_crash_console_init -#if defined(IMAGE_BL1) - /* - * BL1 code can possibly crash so early that the data segment is not yet - * accessible. Don't risk undefined behavior by trying to run the normal - * console framework. Platforms that want to debug BL1 will need to - * override this with custom functions that can run from registers only. - */ - mov x0, #0 - ret -#else /* IMAGE_BL1 */ - mov x3, x30 - mov x0, #CONSOLE_FLAG_CRASH - bl console_switch_state +#if defined(IMAGE_BL31) + mov x4, x30 /* x3 and x4 are not clobbered by spin_lock() */ + mov x3, #0 /* return value */ + + mrs x1, sctlr_el3 + tst x1, #SCTLR_C_BIT + beq skip_spinlock /* can't synchronize when cache disabled */ + + adrp x0, crash_console_spinlock + add x0, x0, :lo12:crash_console_spinlock + bl spin_lock + +skip_spinlock: + adrp x1, crash_console_triggered + add x1, x1, :lo12:crash_console_triggered + ldarb w2, [x1] + cmp w2, #0 + bne init_error + + mov x3, #1 /* set return value to success */ + stlrb w3, [x1] + +init_error: + bl spin_unlock /* harmless if we didn't acquire the lock */ + mov x0, x3 + ret x4 +#else /* Only one CPU in BL1/BL2, no need to synchronize anything */ mov x0, #1 - ret x3 + ret #endif endfunc plat_crash_console_init - /* ----------------------------------------------------- - * void plat_crash_console_putc(int character) - * Output through the normal console by default. - * ----------------------------------------------------- + /* -------------------------------------------------------------------- + * int plat_crash_console_putc(char c) + * Prints the character on all consoles registered with the console + * framework that have CONSOLE_FLAG_CRASH set. Note that this is only + * helpful for crashes that occur after the platform intialization code + * has registered a console. Platforms using this implementation need to + * ensure that all console drivers they use that have the CRASH flag set + * support this (i.e. are written in assembly and comply to the register + * clobber requirements of plat_crash_console_putc(). + * -------------------------------------------------------------------- */ func plat_crash_console_putc - b console_putc + adrp x1, crash_console_reg_stash + add x1, x1, :lo12:crash_console_reg_stash + stp x14, x15, [x1] + stp x16, x17, [x1, #16] + str x30, [x1, #32] + + mov w14, w0 /* W14 = character to print */ + adrp x15, console_list + ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */ + +putc_loop: + cbz x15, putc_done + ldr w1, [x15, #CONSOLE_T_FLAGS] + tst w1, #CONSOLE_FLAG_CRASH + b.eq putc_continue + ldr x2, [x15, #CONSOLE_T_PUTC] + cbz x2, putc_continue + mov x1, x15 + blr x2 + mov w0, w14 +putc_continue: + ldr x15, [x15] /* X15 = next struct */ + b putc_loop + +putc_done: + adrp x1, crash_console_reg_stash + add x1, x1, :lo12:crash_console_reg_stash + ldp x14, x15, [x1] + ldp x16, x17, [x1, #16] + ldr x30, [x1, #32] + ret endfunc plat_crash_console_putc - /* ----------------------------------------------------- - * void plat_crash_console_flush(void) - * Flush normal console by default. - * ----------------------------------------------------- + /* -------------------------------------------------------------------- + * int plat_crash_console_flush(char c) + * Flushes all consoles registered with the console framework that have + * CONSOLE_FLAG_CRASH set. Same requirements as putc(). + * -------------------------------------------------------------------- */ func plat_crash_console_flush - b console_flush + adrp x1, crash_console_reg_stash + add x1, x1, :lo12:crash_console_reg_stash + stp x30, x15, [x1] + stp x16, x17, [x1, #16] + + adrp x15, console_list + ldr x15, [x15, :lo12:console_list] /* X15 = first console struct */ + +flush_loop: + cbz x15, flush_done + ldr w1, [x15, #CONSOLE_T_FLAGS] + tst w1, #CONSOLE_FLAG_CRASH + b.eq flush_continue + ldr x2, [x15, #CONSOLE_T_FLUSH] + cbz x2, flush_continue + mov x0, x15 + blr x2 +flush_continue: + ldr x15, [x15] /* X15 = next struct */ + b flush_loop + +flush_done: + adrp x1, crash_console_reg_stash + add x1, x1, :lo12:crash_console_reg_stash + ldp x30, x15, [x1] + ldp x16, x17, [x1, #16] + ret endfunc plat_crash_console_flush