Debugging bare-metal on a Raspberry Pi
I’ve recently released an early version of a GDB remote serial protocol implementation that runs on bare-metal on a Raspberry Pi that allows you do debug bare metal applications without the use of a JTAG. Once running you can connect with GDB as a remote target and perform all of the normal debugging that you’d expect and you can even single step through interrupt handlers and exceptions.
The implementation uses the security extensions of the ARM core so that the target application requires no modification. The security extensions are present in v6K and later revisions of the ARM architecture and are marketed as TrustZone. These extensions allow the CPU and peripherals to be partitioned into secure and non-secure regions so that it is possible to run a small trusted system and prevent a larger OS like Linux from accessing secret data. The Broadcom SoC on the Raspberry Pi doesn’t implement enough (or have sufficient documentation – who knows?) to do any useful security hardening with these extensions – there would usually be a protection controller (TZPC) for gating peripheral accesses and a secure memory adapter along with onchip memory that the non-secure world can’t access such as TCM’s which this SoC doesn’t have.
So it’s no good security, but the extra operating modes mean we can create a debugger. The security extensions introduce a new operating state – secure monitor mode, and a new set of vectors which the CPU can be configured to branch to on certain, well defined events. A new CPU instruction SMC (Secure Monitor Call) provides a way for the CPU to enter the monitor mode, and the CPU can be configured to branch to the FIQ/IRQ vectors in monitor mode rather than the normal vectors. So to implement a debugger we can use the FIQ entry for handling input from the serial port and the SMC entry for breakpoints then the rest is just glue.
This does impose a couple of limitations though:
- The security extensions can’t be used by the application being debugged – that’s probably fine as they aren’t useful on this SoC anyway.
- The core can only handle 1 FIQ and that’s used by the debugger for the UART
- the application will have to make do with IRQ’s. The nice thing about using the FIQ is that this can effectively be turned into an NMI to the application.
- The UART is used for communication with the debugger – the application can’t use it.
- The monitor mode can’t really single step the core – GDB is clever enough to emulate this with breakpoints at the next instruction, but this does mean that whilst single stepping the core can jump to the IRQ vector and handle a whole IRQ unlike where a JTAG single steps the whole core in lockstep.
- Watchpoints can’t be configured to branch to the monitor mode without application support so hardware watchpoints don’t work.
The code is available on my github page, please give it a go – patches are welcome!