mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-18 02:24:18 +00:00

This patch overhauls the console API to allow for multiple console instances of different drivers that are active at the same time. Instead of binding to well-known function names (like console_core_init), consoles now provide a register function (e.g. console_16550_register()) that will hook them into the list of active consoles. All console operations will be dispatched to all consoles currently in the list. The new API will be selected by the build-time option MULTI_CONSOLE_API, which defaults to ${ERROR_DEPRECATED} for now. The old console API code will be retained to stay backwards-compatible to older platforms, but should no longer be used for any newly added platforms and can hopefully be removed at some point in the future. The new console API is intended to be used for both normal (bootup) and crash use cases, freeing platforms of the need to set up the crash console separately. Consoles can be individually configured to be active active at boot (until first handoff to EL2), at runtime (after first handoff to EL2), and/or after a crash. Console drivers should set a sane default upon registration that can be overridden with the console_set_scope() call. Code to hook up the crash reporting mechanism to this framework will be added with a later patch. This patch only affects AArch64, but the new API could easily be ported to AArch32 as well if desired. Change-Id: I35c5aa2cb3f719cfddd15565eb13c7cde4162549 Signed-off-by: Julius Werner <jwerner@chromium.org>
260 lines
7.7 KiB
ArmAsm
260 lines
7.7 KiB
ArmAsm
/*
|
|
* Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <asm_macros.S>
|
|
#include <assert_macros.S>
|
|
#include <console.h>
|
|
|
|
.globl console_register
|
|
.globl console_unregister
|
|
.globl console_set_scope
|
|
.globl console_switch_state
|
|
.globl console_putc
|
|
.globl console_getc
|
|
.globl console_flush
|
|
|
|
/*
|
|
* The console list pointer is in the data section and not in
|
|
* .bss even though it is zero-init. In particular, this allows
|
|
* the console functions to start using this variable before
|
|
* the runtime memory is initialized for images which do not
|
|
* need to copy the .data section from ROM to RAM.
|
|
*/
|
|
.section .data.console_list ; .align 3
|
|
console_list: .quad 0x0
|
|
.section .data.console_state ; .align 0
|
|
console_state: .byte CONSOLE_FLAG_BOOT
|
|
|
|
/* -----------------------------------------------
|
|
* int console_register(console_t *console)
|
|
* Function to insert a new console structure into
|
|
* the console list. Should usually be called by
|
|
* console_<driver>_register implementations. The
|
|
* data structure passed will be taken over by the
|
|
* console framework and *MUST* be allocated in
|
|
* persistent memory (e.g. the data section).
|
|
* In : x0 - address of console_t structure
|
|
* Out: x0 - Always 1 (for easier tail calling)
|
|
* Clobber list: x0, x1, x14
|
|
* -----------------------------------------------
|
|
*/
|
|
func console_register
|
|
#if ENABLE_ASSERTIONS
|
|
cmp x0, #0
|
|
ASM_ASSERT(ne)
|
|
adrp x1, __STACKS_START__
|
|
add x1, x1, :lo12:__STACKS_START__
|
|
cmp x0, x1
|
|
b.lo not_on_stack
|
|
adrp x1, __STACKS_END__
|
|
add x1, x1, :lo12:__STACKS_END__
|
|
cmp x0, x1
|
|
ASM_ASSERT(hs)
|
|
not_on_stack:
|
|
#endif /* ENABLE_ASSERTIONS */
|
|
adrp x14, console_list
|
|
ldr x1, [x14, :lo12:console_list] /* X1 = first struct in list */
|
|
str x0, [x14, :lo12:console_list] /* list head = new console */
|
|
str x1, [x0, #CONSOLE_T_NEXT] /* new console next ptr = X1 */
|
|
mov x0, #1
|
|
ret
|
|
endfunc console_register
|
|
|
|
/* -----------------------------------------------
|
|
* int console_unregister(console_t *console)
|
|
* Function to find a specific console in the list
|
|
* of currently active consoles and remove it.
|
|
* In: x0 - address of console_t struct to remove
|
|
* Out: x0 - removed address, or NULL if not found
|
|
* Clobber list: x0, x1, x14
|
|
* -----------------------------------------------
|
|
*/
|
|
func console_unregister
|
|
adrp x14, console_list
|
|
add x14, x14, :lo12:console_list /* X14 = ptr to first struct */
|
|
ldr x1, [x14] /* X1 = first struct */
|
|
|
|
unregister_loop:
|
|
cbz x1, unregister_not_found
|
|
cmp x0, x1
|
|
b.eq unregister_found
|
|
ldr x14, [x14] /* X14 = next ptr of struct */
|
|
ldr x1, [x14] /* X1 = next struct */
|
|
b unregister_loop
|
|
|
|
unregister_found:
|
|
ldr x1, [x1] /* X1 = next struct */
|
|
str x1, [x14] /* prev->next = cur->next */
|
|
ret
|
|
|
|
unregister_not_found:
|
|
mov x0, #0 /* return NULL if not found */
|
|
ret
|
|
endfunc console_unregister
|
|
|
|
/* -----------------------------------------------
|
|
* void console_switch_state(unsigned int new_state)
|
|
* Function to switch the current console state.
|
|
* The console state determines which of the
|
|
* registered consoles are actually used at a time.
|
|
* In : w0 - global console state to move to
|
|
* Clobber list: x0, x1
|
|
* -----------------------------------------------
|
|
*/
|
|
func console_switch_state
|
|
adrp x1, console_state
|
|
strb w0, [x1, :lo12:console_state]
|
|
ret
|
|
endfunc console_switch_state
|
|
|
|
/* -----------------------------------------------
|
|
* void console_set_scope(console_t *console,
|
|
* unsigned int scope)
|
|
* Function to update the states that a given console
|
|
* may be active in.
|
|
* In : x0 - pointer to console_t struct
|
|
* : w1 - new active state mask
|
|
* Clobber list: x0, x1, x2
|
|
* -----------------------------------------------
|
|
*/
|
|
func console_set_scope
|
|
#if ENABLE_ASSERTIONS
|
|
tst w1, #~CONSOLE_FLAG_SCOPE_MASK
|
|
ASM_ASSERT(eq)
|
|
#endif /* ENABLE_ASSERTIONS */
|
|
ldr w2, [x0, #CONSOLE_T_FLAGS]
|
|
and w2, w2, #~CONSOLE_FLAG_SCOPE_MASK
|
|
orr w2, w2, w1
|
|
str w2, [x0, #CONSOLE_T_FLAGS]
|
|
ret
|
|
endfunc console_set_scope
|
|
|
|
/* ---------------------------------------------
|
|
* int console_putc(int c)
|
|
* Function to output a character. Calls all
|
|
* active console's putc() handlers in succession.
|
|
* In : x0 - character to be printed
|
|
* Out: x0 - printed character on success, or < 0
|
|
if at least one console had an error
|
|
* Clobber list : x0, x1, x2, x12, x13, x14, x15
|
|
* ---------------------------------------------
|
|
*/
|
|
func console_putc
|
|
mov x15, x30
|
|
mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
|
|
mov w12, w0 /* W12 = character to print */
|
|
adrp x14, console_list
|
|
ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
|
|
|
|
putc_loop:
|
|
cbz x14, putc_done
|
|
adrp x1, console_state
|
|
ldrb w1, [x1, :lo12:console_state]
|
|
ldr x2, [x14, #CONSOLE_T_FLAGS]
|
|
tst w1, w2
|
|
b.eq putc_continue
|
|
ldr x2, [x14, #CONSOLE_T_PUTC]
|
|
cbz x2, putc_continue
|
|
mov w0, w12
|
|
mov x1, x14
|
|
blr x2
|
|
cmp w13, #ERROR_NO_VALID_CONSOLE /* update W13 if it's NOVALID */
|
|
ccmp w0, #0, #0x8, ne /* else update it if W0 < 0 */
|
|
csel w13, w0, w13, lt
|
|
putc_continue:
|
|
ldr x14, [x14] /* X14 = next struct */
|
|
b putc_loop
|
|
|
|
putc_done:
|
|
mov w0, w13
|
|
ret x15
|
|
endfunc console_putc
|
|
|
|
/* ---------------------------------------------
|
|
* int console_getc(void)
|
|
* Function to get a character from any console.
|
|
* Keeps looping through all consoles' getc()
|
|
* handlers until one of them returns a
|
|
* character, then stops iterating and returns
|
|
* that character to the caller. Will stop looping
|
|
* if all active consoles report real errors
|
|
* (other than just not having a char available).
|
|
* Out : x0 - read character, or < 0 on error
|
|
* Clobber list : x0, x1, x13, x14, x15
|
|
* ---------------------------------------------
|
|
*/
|
|
func console_getc
|
|
mov x15, x30
|
|
getc_try_again:
|
|
mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
|
|
adrp x14, console_list
|
|
ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
|
|
cbnz x14, getc_loop
|
|
mov w0, w13 /* If no consoles registered */
|
|
ret x15 /* return immediately. */
|
|
|
|
getc_loop:
|
|
adrp x0, console_state
|
|
ldrb w0, [x0, :lo12:console_state]
|
|
ldr x1, [x14, #CONSOLE_T_FLAGS]
|
|
tst w0, w1
|
|
b.eq getc_continue
|
|
ldr x1, [x14, #CONSOLE_T_GETC]
|
|
cbz x1, getc_continue
|
|
mov x0, x14
|
|
blr x1
|
|
cmp w0, #0 /* if X0 >= 0: return */
|
|
b.ge getc_found
|
|
cmp w13, #ERROR_NO_PENDING_CHAR /* may update W13 (NOCHAR has */
|
|
csel w13, w13, w0, eq /* precedence vs real errors) */
|
|
getc_continue:
|
|
ldr x14, [x14] /* X14 = next struct */
|
|
cbnz x14, getc_loop
|
|
cmp w13, #ERROR_NO_PENDING_CHAR /* Keep scanning if at least */
|
|
b.eq getc_try_again /* one console returns NOCHAR */
|
|
mov w0, w13
|
|
|
|
getc_found:
|
|
ret x15
|
|
endfunc console_getc
|
|
|
|
/* ---------------------------------------------
|
|
* int console_flush(void)
|
|
* Function to force a write of all buffered
|
|
* data that hasn't been output. Calls all
|
|
* console's flush() handlers in succession.
|
|
* Out: x0 - 0 on success, < 0 if at least one error
|
|
* Clobber list : x0, x1, x2, x3, x4, x5, x13, x14, x15
|
|
* ---------------------------------------------
|
|
*/
|
|
func console_flush
|
|
mov x15, x30
|
|
mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
|
|
adrp x14, console_list
|
|
ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
|
|
|
|
flush_loop:
|
|
cbz x14, flush_done
|
|
adrp x1, console_state
|
|
ldrb w1, [x1, :lo12:console_state]
|
|
ldr x2, [x14, #CONSOLE_T_FLAGS]
|
|
tst w1, w2
|
|
b.eq flush_continue
|
|
ldr x1, [x14, #CONSOLE_T_FLUSH]
|
|
cbz x1, flush_continue
|
|
mov x0, x14
|
|
blr x1
|
|
cmp w13, #ERROR_NO_VALID_CONSOLE /* update W13 if it's NOVALID */
|
|
ccmp w0, #0, #0x8, ne /* else update it if W0 < 0 */
|
|
csel w13, w0, w13, lt
|
|
flush_continue:
|
|
ldr x14, [x14] /* X14 = next struct */
|
|
b flush_loop
|
|
|
|
flush_done:
|
|
mov w0, w13
|
|
ret x15
|
|
endfunc console_flush
|