This article outlines how to use DS-5 Development Studio (DS-5) to debug Arm Trusted Firmware (ATF) from cold reset through to normal world handover.
Specifically, this article discusses two of the most common obstacles encountered when trying to debug ATF:
These instructions are primarily written targeting the Armv8 Foundation Model FVP, with delta instructions for the Juno development platform also provided at the end.
Follow the instructions for using the Linaro software deliverables on an FVP to download the workspace initialisation script (article written using version 1707) and then select the following configuration when prompted:
+-------------+------------------------------------+ | Workspace | <workspace> | | Platform | [64-bit] AEMv8-A Base Platform FVP | | Build | Build from source | | Environment | Linux/Android | | Kernel | latest-armlt | | Filesystem | BusyBox Built from source | +-------------+------------------------------------+
The ATF sources can then be found in the `<workspace>/arm-tf/' directory.
`<workspace>/arm-tf/'
While it is possible to manually fetch the sources from GitHub, we recommend the above automated method as the instructions below depend on other files provided as part of those deliverables, such as a Linux kernel image, ramdisk image, and normal world bootloader BL33 image.
The ATF cold boot flow comprises up to five individual boot stages running at different exception levels:
With these stages run in the following order:
BL1 --> BL2 --> BL1 --> BL31 --> BL32 --> BL31 --> BL33
We recommend reading the Arm Trusted Firmware Design document for more information (can also be found in `<workspace>/arm-tf/docs/').
`<workspace>/arm-tf/docs/'
This article outlines how to debug ATF:
(*) The `bl1/', `bl2/', and `bl31/' directories in `<workspace>/arm-tf/'.
`bl1/'
`bl2/'
`bl31/'
When debugging ATF it is important to know which boot stage(s) contain the functionality that you are interested in; this way the correct symbols and debug information can be loaded, allowing us to set breakpoints on textual symbol names rather than raw addresses, see function call target names rather than PC relative offsets, and so on. It also means we can skip unnecessary parts of the boot flow.
To this end we have generated the following table of "interesting" functionality with corresponding boot stage and symbol name(s):
plat_get_my_entrypoint
reset_handler
bl1_entrypoint
bl1_main
bl1_load_bl2
-->
bl1_prepare_next_imageel3_exit
bl2_entrypoint
bl2_main
bl2_load_images
smc
bl1_plat_prepare_exit
bl31_entrypoint
bl31_warm_entrypoint
bl31_main
init_cpu_ops
populate_power_domain_treepsci_init_pwr_domain_nodepsci_set_pwr_domains_to_run
prepare_cpu_pwr_dwn
bl31_prepare_next_imageel3_exit
Make a note of any of these that interest you.
Continue following the instructions here to build the Linaro software deliverables, including ATF.
Run ATF on the Armv8 Foundation Model model like so (we recommend turning this into a shell script for ease of use):
/path/to/Foundation_Platform \ --cores=4 --secure-memory --visualization --gicv3 \ --data=<workspace>/output/fvp/fvp-busybox/uboot/bl1.bin@0x0 \ --data=<workspace>/output/fvp/fvp-busybox/uboot/fip.bin@0x08000000 \ --data=<workspace>/output/fvp/fvp-busybox/uboot/foundation-v8-gicv3.dtb@0x82000000 \ --data=<workspace>/output/fvp/fvp-busybox/uboot/Image@0x80080000 \ --data=<workspace>/output/fvp/fvp-busybox/uboot/ramdisk.img@0x84000000 \ --cadi-server
Replacing `/path/to/Foundation_Model' with the path to your Armv8 Foundation Model executable and `<workspace>' with the path to your workspace directory.
`/path/to/Foundation_Model'
`<workspace>'
Note that the command references artefacts in `<workspace>' that are only present after invoking the build script referenced in the instructions linked above.
Running the model with the `--cadi-server' flag causes the simulation to pause at the first cycle waiting for a debugger to be connected.
`--cadi-server'
From the DS-5 Debug perspective, navigate to:
File --> New --> Other --> DS-5 Configuration Database --> Configuration Database
Enter a name of your choice, such as "ATF on Armv8 Foundation Model", then click "Finish".
Next, navigate to:
File --> New --> Other --> DS-5 Configuration Database --> Model Configuration
And:
On the window that opens, navigate to the "Debugger" tab, tick "Connect only", tick "Execute debugger commands", and copy-paste the following into the text box to automatically load all symbols into the correct virtual address space each time you connect to the model:
add-symbol-file /arm-tf/build/fvp/debug/bl1/bl1.elf EL3:0 add-symbol-file <workspace>/arm-tf/build/fvp/debug/bl2/bl2.elf EL1S:0 add-symbol-file <workspace>/arm-tf/build/fvp/debug/bl31/bl31.elf EL3:0 add-symbol-file <workspace>/u-boot/output/vexpress_aemv8a_semi/u-boot EL2:0 add-symbol-file <workspace>/linux/out/fvp/mobile_bb/vmlinux EL2:0
Replacing <workspace> with the path to your workspace directory.
<workspace>
The EL and number at the end of each command (e.g. `EL3:0') ensure the symbols are loaded into the correct virtual address space and at the correct memory offset; ATF uses absolute addresses for its symbols so we ensure an offset of 0.
`EL3:0'
Click "Apply" and then "Debug" to connect to the paused model. You can now step through the ATF code or set a breakpoint on the symbol corresponding to the functionality that you are interested in.
This section highlights the differences between the above instructions, which target the Armv8 Foundation Model, and the steps required to debug ATF on the Juno hwardware development platform.
Run the workspace initialisation script to sync a new workspace as outlined earlier, but this time targeting the `[64-bit] Juno' platform.
`[64-bit] Juno'
Unlike the Armv8 Foundation Model, which will be paused on the first cycle of the simulation waiting for a debugger to be connected, the Juno hardware development platform will immediately begin booting ATF when power cycled. Due to the application processor debug access ports (DAPs) not being powered up until that same moment, we cannot connect a debugger until the board has already progressed some of the way through the ATF boot flow.
Due to a known issue, the way you get around this will depend on which boot stage(s) you want to debug.
Navigate to `<workspace>/arm-tf/bl1/aarch64/bl1_entrypoint.S', find the `bl1_entrypoint' function, and add a `b .' instruction:
`<workspace>/arm-tf/bl1/aarch64/bl1_entrypoint.S'
`bl1_entrypoint'
`b .'
func bl1_entrypoint b . // <-- Branch-to-self added here /* --------------------------------------------------------------------- * If the reset address is programmable then bl1_entrypoint() is * executed only on the cold boot path. Therefore, we can skip the warm * boot mailbox mechanism. * --------------------------------------------------------------------- */ el3_entrypoint_common \ _init_sctlr=1 \ _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \ _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ _exception_vectors=bl1_exceptions
NOTE: Ensure you do not have a `b .' instruction in the code path leading up to the BL31 entrypoint; due to the known issue at time of writing this will cause the board to panic.
Navigate to `<workspace>/arm-tf/make_helpers/defaults.mk' and modify this line to set the switch to `1':
`<workspace>/arm-tf/make_helpers/defaults.mk'
`1'
# Flag to introduce an infinite loop in BL1 just before it exits into the next # image. This is meant to help debugging the post-BL2 phase. SPIN_ON_BL1_EXIT := 1
Additionally, add the following new lines:
# Flag to disable the Trusted Watchdog ARM_DISABLE_TRUSTED_WDOG := 1
Then perform a `make realclean' from the `<workspace>/arm-tf/' directory before rebuilding the software in the usual way (using the `<workspace>/build-scripts/build-all all' script).
`make realclean'
`<workspace>/build-scripts/build-all all'
When you connect the debugger, the primary CPU will be "spinning" on a `b .' instruction; either the one you manually added to the `bl1_entrypoint' function or one that was compiled into the BL1 --> BL31 handover as a result of setting the `SPIN_ON_BL1_EXIT' flag.
`SPIN_ON_BL1_EXIT'
Simply interrupt the CPU and enter debug command `set $pc += 4'; you can now step through and debug the ATF boot flow just like on the Armv8 Foundation Model.
`set $pc += 4'