How to debug TF-A BL2

1 Article Purpose

This article explains how to debug TF-A BL2 firmware.
Debug is specifically linked to the TrustZone environment.

There are two main ways to debug TF-A, using traces inside the code, or by using JTAG to access the secure world. The focus here is on the solution integrated in OpenSTLinux: debug over gdb (ST-Link or JTAG based)

2 Debugging

2.1 TF-A Version number

The starting point for debugging is to identify the TF-A BL2 version used in the target. Debug and release versions are displayed on the console with the following format:

NOTICE: BL2: v2.0(debug):<tag>
  • v2.0 is the TF-A BL2 version used.
  • (debug) : Build mode enable
  • <tag> : Git reference resulting from the git describe command at build time

2.2 Debug with traces

TF-A allows RELEASE and DEBUG modes to be enabled:

  • RELEASE mode builds with default LOG_LEVEL to 20, which only prints ERROR and NOTICE traces
  • DEBUG mode enables the -g build flag (for debug build object), and defaults LOG_LEVEL to 40

With the debug LOG_LEVEL, you can add console traces such as ERROR, NOTICE, WARNING or INFO. Please refer to include/common/debug.h .

Warning white.png Warning
You cannot build code with LOG_LEVEL set to 50 (the highest level); there are too many traces and TF-A does not fit in the SYSRAM. If required, change some VERBOSE settings to INFO.
Another possibility is to add the following lines at the beginning of the .c file, after the includes:
   #undef VERBOSE
   #define VERBOSE INFO

For both modes, you must ensure that the UART is properly configured:

  • BL2: UART traces are enabled by default

Traces and errors are available on the console defined in the chosen node of the device tree by the stdout-path property:

chosen {
        stdout-path = "serial0:115200n8";

Traces will only be available after the console has been fully registered. Prior to that, the traces are not displayed except panic messages, so it is recommended to use a debugger.

The panic messages are displayed through the crash console. It uses hard-coded UART settings. Those settings should be adapted to your platform, in the file plat/st/stm32mp1/stm32mp1_def.h , after the comment:

/* For UART crash console */

You will have to setup the base address of the UART to use, its clock source frequency and the GPIO configuration of the UART TX pin.

2.3 Debug with GDB

The BL2 boot stage is only executed during a boot sequence. To break into BL2, connect to the target and break.

Load symbols to the correct offset:

(gdb) add-symbol-file <path_to_build_folder>/bl2/bl2.elf <load_address>

BL2 load address can be found in the generated tf-a-<board>.map file:

 *fill*         0x000000002ffc3000    0x20000 
                0x000000002ffea000                __BL2_IMAGE_START__ = .  -> BL2 Load address
 .bl2_image     0x000000002ffea000    0xf4a5 	  build/stm32mp1/stm32mp1.o
                0x000000002fff94a5                __BL2_IMAGE_END__ = .
                0x000000002fff94a5                __DATA_END__ = .
                0x000000002fff94a5                __TF_END__ = .

In this example:

  • BL2 load address is 0x2ffea000.

You can load all your symbols directly:

(gdb) add-symbol-file <path_to_build_folder>/bl2/bl2.elf 0x2ffea000

2.3.1 Boot from a flashed storage

TF-A BL2 runs in SYSRAM and is executed in CPU secure state. One can debug TF-A BL2 through JTAG using an ST-Link or the JTAG output, depending on the board. It is recommended to use the Wrapper_for_FSBL_images to be able to interrupt the initial boot sequence at the early beginning (before BL2 code execution).

Set your hardware breakpoint and reset:

(gdb) hb bl2_entrypoint
(gdb) monitor reset halt
(gdb) continue

2.3.2 Boot from programmer mode

If TF-A BL2 is not yet flashed on an external storage, it is possible to debug the programmer (USB or UART) boot sequence. The ROM code is opening non secure debug during the programmer wait loop. It is possible to connect GDB during this stage.
You can load all your symbols directly:

(gdb) add-symbol-file <path_to_build_folder>/bl2/bl2.elf 0x2ffea000

Set your hardware breakpoint and continue:

(gdb) hb bl2_entrypoint
(gdb) continue

Load the TF-A BL2 built with the USB/UART support using the STM32CubeProgrammer.
Ex in USB mode:

    STM32_Programmer_CLI -c port=usb1 -w tf-a-<board>.stm32 0x01 -s

GDB will break at the first BL2 instruction ready for step by step debugging session.

Breakpoint 1, bl2_entrypoint () at bl2/aarch32/bl2_el3_entrypoint.S:18
18		mov	r9, r0