diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 4f1638a..f1a26f4 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -2550,9 +2550,6 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 7f076c6..95423b0 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 55e7abb..8f8ca11 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