Difference between revisions of "TF-A - How to debug"

[quality revision] [pending revision]
m (Update to link with wrapper)
m
 

1 Article Purpose[edit]

This article explains how to debug TF-A 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[edit]

2.1 TF-A Version number[edit]

The starting point for debugging is to identify the TF-A 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 version used.
  • (debug) : Build mode enable
  • <tag> : Git reference resulting from the git describe command at build time

2.2 Debug with traces[edit]

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.png 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.

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

  • BL2: UART traces are enabled by default
  • BL32: 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";
};


When needing early debug in TF-A before the regular trace mechanism is in place, the crash console can be used as the following to understand the code that raises a PANIC.F-A

This needs a patch in of TF-A crash console (assembler code) and usage of sysram address 0x2FFC0000

Modification to apply:

-plat/st/common/include/stm32mp_common.h


extern void TFA_early_debug_print(char * error_str);

-plat/st/common/stm32mp_common.c


char my_exception_start_str[12]{{=}}"TF+";
char my_exception_start_str_end[4]{{=}}"-TF\n";
char * exception_start_str {{=}} (char * ) '''0x2FFC0000''';

void TFA_early_debug_print(char * error_str)

{
strlcpy(my_exception_start_str+3,error_str,5);
strlcpy(my_exception_start_str+7,my_exception_start_str_end,4);
memcpy(exception_start_str, my_exception_start_str,12);
}

-plat/st/stm32mp1/stm32mp1_helper.S


Iine 59 onwards:
other_excpetion_lbl:

/* Other exceptions */

mov        r9, r0
ldr        r4, {{=}} '''0x2FFC0000'''
bl        asm_print_str

Then in any place of source code call TFA_early_debug_print() with 4 characters maximum as the example below.


Example in function bl2_el3_plat_arch_setup call TFA_early_debug_print("up01'"); when PANIC() function is called then latest TFA_early_debug_print log is displayed as follows

PANIC at PC : 0xxxxxx TF+up01-TF00000016 at: 0xfffffffc


2.3 Debug with GDB[edit]

TF-A runs in SYSRAM and is executed in CPU secure state. One can debug TF-A through JTAG using an ST-Link or the JTAG output, depending on the board.

2.3.1 Debug boot sequence[edit]

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>/tf-a-bl2.elf (or bl2.elf) <load_address>

BL2 and BL32 load addresses can be found in the generated tf-a-stm32mp157c-<board>.map file:

...
 *fill*         0x000000002ffce000     0x5000 
                0x000000002ffd3000                __BL2_IMAGE_START__ = .  -> BL2 Load address
 *(.bl2_image*)
 .bl2_image     0x000000002ffd3000    0x1166c build/stm32mp1/stm32mp1.o
                0x000000002ffe466c                __BL2_IMAGE_END__ = .
                0x0000000000024b00                . = 0x24b00
 *fill*         0x000000002ffe466c     0x2994 
                0x000000002ffe7000                __BL32_IMAGE_START__ = . -> BL32 Load address
 *(.bl32_image*)
 .bl32_image    0x000000002ffe7000    0x1744c build/stm32mp1/stm32mp1.o
                0x000000002fffe44c                __BL32_IMAGE_END__ = .
                0x000000002fffe44c                __DATA_END__ = .
                0x000000002fffe44c                __TF_END__ = .
...

In this example:

  • BL2 load address is 0x2ffd3000.
  • BL32 load address is 0x2ffe7000.

You can load all your symbols directly:

(gdb) add-symbol-file <path_to_build_folder>/tf-a-bl2.elf 0x2ffd3000
(gdb) add-symbol-file <path_to_build_folder>/tf-a-bl32.elf 0x2ffe7000

Using the Wrapper_for_FSBL_images, you will be able to debug the initial boot sequence. Set your hardware breakpoint and reset:

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

2.3.2 Debugging during runtime execution[edit]

Once U-Boot or the Linux kernel is running, you cannot access secure memory or regions, but you can break, set a hardware breakpoint into SMC handler (in BL32). GDB breaks once it has switched into the secure world and reached the break instruction. Once halted in the secure monitor, GDB can access secure resources as IPs or memory. For example, one can break into kernel:

(gdb) hb stm32mp1_svc_smc_handler
(gdb) continue

On the first SMC call occurence GDB breaks the stm32mp1_svc_smc_handler() entry in BL32.



__TOC__
== Article Purpose ==
This article explains how to debug TF-A firmware.<br>

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)

== Debugging ==
=== TF-A Version number ===

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

NOTICE: BL2: v2.0(debug):<tag>
</pre>

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

=== 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| 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.}}

For both modes, you must ensure that the UART is properly configured:
* BL2: UART traces are enabled by default
* BL32: UART traces are enabled by default<br>


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

chosen {
        stdout-path = "serial0:115200n8";
};</pre>


=== {{ReviewsComments|MCC 15/09/2020 ECO 14 oct<br />early debug traces using crash console below 
(here used sysram base address, please can you check if better address is suitable, tested with this one seems ok) }}

When needing early debug in TF-A before the regular trace mechanism is in place, the crash console can be used
as the following to understand the code that raises a PANIC.F-A

This needs a patch in of TF-A crash console (assembler code) and usage of sysram address '''0x2FFC0000'''

Modification to apply:

-plat/st/common/include/stm32mp_common.h<pre>

extern void TFA_early_debug_print(char * error_str);</pre>

-plat/st/common/stm32mp_common.c<pre>

char my_exception_start_str[12]{{=}}"TF+";
char my_exception_start_str_end[4]{{=}}"-TF\n";
char * exception_start_str {{=}} (char * ) '''0x2FFC0000''';

void TFA_early_debug_print(char * error_str)

{
strlcpy(my_exception_start_str+3,error_str,5);
strlcpy(my_exception_start_str+7,my_exception_start_str_end,4);
memcpy(exception_start_str, my_exception_start_str,12);
}</pre>

-plat/st/stm32mp1/stm32mp1_helper.S<pre>

Iine 59 onwards:
other_excpetion_lbl:

/* Other exceptions */

mov        r9, r0
ldr        r4, {{=}} '''0x2FFC0000'''
bl        asm_print_str</pre>


Then in any place of source code call 
TFA_early_debug_print() with 4 characters maximum as the example below.

Example in function bl2_el3_plat_arch_setup
call
TFA_early_debug_print("'''''up01''''''");
when PANIC() function is called then latest TFA_early_debug_print  log is displayed as follows

PANIC at PC : 0xxxxxx
'''''TF+up01-TF'''''00000016 at: 0xfffffffc

=== Debug with GDB ===
TF-A runs in SYSRAM and is executed in CPU secure state. One can debug TF-A through JTAG using an ST-Link or the JTAG output, depending on the board.

==== Debug boot sequence ====
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:<pre>

(gdb) add-symbol-file <path_to_build_folder>/tf-a-bl2.elf (or bl2.elf) <load_address>
</pre>


BL2 and BL32 load addresses can be found in the generated '''tf-a-stm32mp157c-<board>.map''' file:<pre>

...
 *fill*         0x000000002ffce000     0x5000 
                0x000000002ffd3000                __BL2_IMAGE_START__ = .  -> BL2 Load address
 *(.bl2_image*)
 .bl2_image     0x000000002ffd3000    0x1166c build/stm32mp1/stm32mp1.o
                0x000000002ffe466c                __BL2_IMAGE_END__ = .
                0x0000000000024b00                . = 0x24b00
 *fill*         0x000000002ffe466c     0x2994 
                0x000000002ffe7000                __BL32_IMAGE_START__ = . -> BL32 Load address
 *(.bl32_image*)
 .bl32_image    0x000000002ffe7000    0x1744c build/stm32mp1/stm32mp1.o
                0x000000002fffe44c                __BL32_IMAGE_END__ = .
                0x000000002fffe44c                __DATA_END__ = .
                0x000000002fffe44c                __TF_END__ = .
...</pre>

In this example:
* BL2 load address is 0x2ffd3000.
* BL32 load address is 0x2ffe7000.

You can load all your symbols directly:<pre>

(gdb) add-symbol-file <path_to_build_folder>/tf-a-bl2.elf 0x2ffd3000
(gdb) add-symbol-file <path_to_build_folder>/tf-a-bl32.elf 0x2ffe7000</pre>


Using the [[Wrapper_for_FSBL_images]], you will be able to debug the initial boot sequence.
Set your hardware breakpoint and reset:<pre>

(gdb) hb bl2_entrypoint
(gdb) monitor reset halt
(gdb) continue</pre>


==== Debugging during runtime execution ====
Once [[U-Boot overview|U-Boot]] or the Linux kernel is running, you cannot access secure memory or regions, but you can break, set a hardware breakpoint into SMC handler (in BL32). [[GDB]] breaks once it has switched into the secure world and reached the break instruction. Once halted in the secure monitor, [[GDB]] can access secure resources as IPs or memory. For example, one can break into kernel:<pre>

(gdb) hb stm32mp1_svc_smc_handler
(gdb) continue</pre>


On the first SMC call occurence [[GDB]] breaks the stm32mp1_svc_smc_handler() entry in BL32.
<noinclude>

{{PublicationRequestId | 9776 | 2018-11-22 | PhilipeS}}
[[Category:Trusted Firmware-A (TF-A)]]
[[Category:Tracing tools]]
[[Category:Debugging tools]]</noinclude>
(4 intermediate revisions by 2 users not shown)
Line 35: Line 35:
 
};
 
};
 
</pre>
 
</pre>
  +
{{ReviewsComments|MCC 15/09/2020 ECO 14 oct<br />early debug traces using crash console below
  +
(here used sysram base address, please can you check if better address is suitable, tested with this one seems ok) }}
  +
  +
When needing early debug in TF-A before the regular trace mechanism is in place, the crash console can be used
  +
as the following to understand the code that raises a PANIC.F-A
  +
  +
This needs a patch in of TF-A crash console (assembler code) and usage of sysram address '''0x2FFC0000'''
  +
  +
Modification to apply:
  +
  +
-plat/st/common/include/stm32mp_common.h
  +
<pre>
  +
extern void TFA_early_debug_print(char * error_str);
  +
</pre>
  +
-plat/st/common/stm32mp_common.c
  +
<pre>
  +
char my_exception_start_str[12]{{=}}"TF+";
  +
char my_exception_start_str_end[4]{{=}}"-TF\n";
  +
char * exception_start_str {{=}} (char * ) '''0x2FFC0000''';
  +
  +
void TFA_early_debug_print(char * error_str)
  +
  +
{
  +
strlcpy(my_exception_start_str+3,error_str,5);
  +
strlcpy(my_exception_start_str+7,my_exception_start_str_end,4);
  +
memcpy(exception_start_str, my_exception_start_str,12);
  +
}
  +
</pre>
  +
-plat/st/stm32mp1/stm32mp1_helper.S
  +
<pre>
  +
Iine 59 onwards:
  +
other_excpetion_lbl:
  +
  +
/* Other exceptions */
  +
  +
mov        r9, r0
  +
ldr        r4, {{=}} '''0x2FFC0000'''
  +
bl        asm_print_str
  +
</pre>
  +
  +
Then in any place of source code call
  +
TFA_early_debug_print() with 4 characters maximum as the example below.
  +
  +
  +
Example in function bl2_el3_plat_arch_setup
  +
call
  +
TFA_early_debug_print("'''''up01''''''");
  +
when PANIC() function is called then latest TFA_early_debug_print  log is displayed as follows
  +
  +
PANIC at PC : 0xxxxxx
  +
'''''TF+up01-TF'''''00000016 at: 0xfffffffc
  +
   
 
=== Debug with GDB ===
 
=== Debug with GDB ===