5.8. Console Framework
The TF-A console framework is used to register consoles for different boot states so that user’s output can be displayed on physical consoles throughout the different boot stages. The framework also supports debug mode for general debugging purposes.
The console framework supports a number of different UARTs, it is highly likely that the driver of the UART that is needed is already implemented. If not, a driver will need to be written for the new UART in TF-A. Current supported UARTs are:
Amlogic Meson
Arm PL011
Cadence CDNS
Coreboot CBMEM
Marvell A3700
- NXP
i.MX LPUART
i.MX UART
Linflex
Nvidia SPE
Qualcomm UARTDM
Renesas RCAR
STMicroelectronics STM32
Texas Instruments 16550
Note
The supported UART list is non-exhaustive. Check if the UART driver has already been written before writing a new one.
Console scopes and flags
Scope : Flag
BOOT : CONSOLE_FLAG_BOOT
RUNTIME : CONSOLE_FLAG_RUNTIME
CRASH : CONSOLE_FLAG_CRASH
The console framework supports multiple consoles. Multiple instances of a UART can be registered at any given moment. Any registered console can have a single scope or multiple scopes. In single scope for example, setting three different consoles with each having BOOT, RUNTIME, and CRASH states respectively, the boot console will display only boot logs, the runtime console will display only the runtime output, while the crash console will be used to print the crash log in the event of a crash. Similarly, a console with all three scopes will display any and all output destined for BOOT, RUNTIME, or CRASH consoles.
These multiple scopes can be useful in many ways, for example:
Having different consoles for Boot and Runtime messages
Having a single console for both Runtime and Boot messages
Having no runtime console at all and just having a single Boot console.
Having a separate console for crash reporting when debugging.
5.8.1. Registering a console
To register a console in TF-A check if the hardware (UART) that is going to be used
is already defined, if not we will need to define it, for example, the PL011
UART driver API is defined in include/drivers/arm/pl011.h
.
A skeleton console driver (assembly) is provided in TF-A drivers/console/aarch64/
skeleton_console.S
, this skeleton sets the rules for writing a new console_driver.
Have a look at drivers/arm/pl011/aarch64/pl011_console.S
for an actual
implementation using this skeleton.
5.8.1.1. Function : console_xxx_register
Argument : console_t *, ...
Return : int
This ASM Function is used to initialize and register a console. The caller needs
to pass an empty console_t
struct which MUST be allocated in persistent
memory (e.g. a global or static local variable, NOT on the stack).
This function takes a console_t
struct placed in x0 and additional
arguments placed in x1 - x7. It returns x0 with either a 0 on failure or 1
on success.
See console_pl011_register
ASM function for an implementation of this
function.
Note
The
xxx
in the function name is replaced with the console driver name, for example,console_xxx_register
becomesconsole_pl011_register
in the driver for pl011.
5.8.1.2. Function : console_xxx_putc
Argument : int, console_t *
Return : int
This ASM function is used to send a character to the UART’s Transmit FIFO. It takes
two arguments, a character as int stored in w0, and the console_t
struct pointer
stored in x1. It returns w0 with either the character on successs or a negative
value on error. In a crash context this function must only clobber x0 - x2, x16 - x17.
See console_pl011_putc
ASM function for an implementation.
Note
Avoid the direct use of this function for printing to the console, instead use the
debug.h
print macros, such as: VERBOSE(…), INFO(…), WARN(…), NOTICE(…) and ERROR(…).
5.8.1.3. Function : console_xxx_getc
Argument : console_t *
Return : int
This ASM function is used to read a character from the receive FIFO. It takes a pointer
to the console_struct as an argument and returns a character on success or a negative
value below -2 on failure. This function is dependent on the ENABLE_CONSOLE_GETC
flag,
which is optional and is left to the platform because there may be security implications.
See console_pl011_getc
ASM function for an implementation.
5.8.1.4. Function : console_xxx_flush
Argument : console_t *
Return : void
This ASM function flushes any characters, that are still in the Transmit FIFO but haven’t been printed yet to the console. It takes a pointer to the console_struct but doesn’t return any value. In a crash context this function must only clobber x0 - x5, x16 - x17.
See console_pl011_flush
ASM function for an implementation.
5.8.1.5. Macro : finish_console_register xxx putc=1 getc=ENABLE_CONSOLE_GETC flush=1
Callbacks
xxx : name of the console driver
putc : 0 for off, 1 to turn on putc function
getc : 0 for off, ENABLE_CONSOLE_GETC to turn on the getc function
flush : 0 for off, 1 to turn on flush function
This assembly macro function is called by the console_xxx_register
to
encapsulate the common setup that has to be done at the end of a console
driver’s register function. It takes putc
, getc
and flush
macro
arguments. It will register all of the driver’s callbacks in the console_t
struct and initialize the flags
field (by default consoles are enabled for
“boot” and “crash” states, this can be changed after registration using the
console_set_scope
function). This macro ends with a tail call that will
include return to the caller.
This macro requires console_t
pointer in x0 and a valid return address in x30.
See include/arch/aarch64/console_macros.S
.
5.8.2. Registering a console using C
A console can be implemented in pure C, which is much easier than using assembly.
Currently there is no C template for implementing a console driver in C but it can
easily be implemented using existing examples. See drivers/arm/dcc/dcc_console.c
for an implementation of a console driver in C.
The assembly functions in Registering a console section can be written in C when implementing a console driver using C.
Note
A crash callback needs to be able to run without a stack. If crash mode support is required then the console driver will need to be written in Assembly (only the putc and flush functions are needed in a crash context).
5.8.3. Multi Console API
TF-A uses the multi-console API to manage the registered console instances and the
characters print queue. This can be found in drivers/console/multi_console.c
.
The multi-console API stores all registered consoles in a struct list console_list
.
Consoles can be removed from the console_list if no longer needed.
Consoles are registered with BOOT and CRASH scopes by default. These scopes can be
changed after registration using console_set_scope
function, as per the platform
requirement.
This API also helps print characters to the specified consoles, characters can also be retrieved from the receive FIFO (this implementation is disabled by default but can be enabled if there is a need for it). The API can also help flush the transmit FIFO to get rid of any lingering characters from the queue when switching from secure world to the non-secure world.
The following functions are defined in the multi_console API.
5.8.3.1. Function : console_register()
Argument : console_t*
Return : int
This function adds a console to the console_list
declared in
include/drivers/console.h
and makes sure that there is only one instance
of a specific console in this list. This function is called by the
finish_console_register
asm macro function, at the end of the console
registration process.
This function always return 1. If the console is already present in the
console_list
it will return immediately with a value of 1, otherwise
it will add the console to the console_list
and then return 1.
Note
The
console_list
is a list of typeconsole_t
, it is an extern variable declared ininclude/drivers/console.h
.
5.8.3.2. Function : console_unregister()
Argument : console_t*
Return : console_t* or NULL
This function removes a console from the console_list
. It will return the
removed console on success or a NULL
character upon failure.
5.8.3.3. Function : console_set_scope()
Argument : console_t*, int
Return : void
This function is used to set the scope of the registered console. A console can be registered with upto three states (called the scope). These states are
BOOT - set using the flag
CONSOLE_FLAG_BOOT
RUNTIME - set using the flag
CONSOLE_FLAG_RUNTIME
CRASH - set using the flag
CONSOLE_FLAG_CRASH
It takes a pointer to the console and an int value (which is provided as the FLAG value) as its arguments. This function does not return anything.
5.8.3.4. Function : console_switch_state()
Argument : int
Return : void
This function sets the console state (scope) for printing, i.e, TF-A will
start sending all logs (INFO, WARNING, ERROR, NOTICE, VERBOSE) to the consoles
that are registered with this new state (scope). For example, calling
console_switch_state(CONSOLE_FLAG_RUNTIME)
, TF-A will start sending all log
messages to all consoles marked with the RUNTIME flag. BOOT is the default
console state.
This function takes a console state as the function’s only argument. This function does not return a value.
5.8.3.5. Function : console_putc()
Argument : int
Return : int
Invoking this function sends a character to the console->putc
(struct
member) function of all consoles registered for the current scope, for example,
BOOT logs will only be printed on consoles set with a BOOT scope. In the PL011
implementation console->putc
call points to the console_pl011_putc()
function.
This function takes the int value of a character as an argument and returns the int value of the character back on success or a negative int value on error.
Note
Do not use this function in TF-A release builds, instead use the log prefixes, for example,
INFO("Print information here.")
to print messages on the active console.
5.8.3.6. Function : console_getc()
Argument : void
Return : int
This function is used to fetch a character from the receive FIFO that has
not been printed to the console yet. This function is disabled by default for
security reasons but can be enabled using the ENABLE_CONSOLE_GETC
macro
if there is a need for it.
This function doesn’t take any argument but returns a character as an int.
5.8.3.7. Function : console_flush()
Argument : void
Return : void
This function flushes all the characters pending in the transmit FIFO of the active UART thus removing them from the print queue.
This function has no arguments and do not return a value.
5.8.3.8. Function : putchar()
Argument : int
Return : int
This function overrides the weak implementation of the putchar library. It is
used to send a character to the console_putc()
function to be printed to
the active console.
This function will either return the character on success or an EOF character otherwise.
Copyright (c) 2024-2025, Arm Limited and Contributors. All rights reserved.