Making SVD-Loader for Ghidra play nice with RP2040
To that end, Leveldown security created the SVD Loader Ghidra script.
But, the original script didn’t work for RP2040 at all due to a bug2. And, more importantly, Raspberry Pi’s Pico board (that uses RP2040 chip) has a rather unique feature in its address space: Atomic Register Access:
The section 2.1.2 Atomic Register Access in the RP2040 datasheet explains:
Each peripheral register block is allocated 4kB of address space, with registers accessed using one of 4 methods, selected by address decode.
Addr + 0x0000: normal read write access
Addr + 0x1000: atomic XOR on write
Addr + 0x2000: atomic bitmask set on write
Addr + 0x3000: atomic bitmask clear on write
This allows individual fields of a control register to be modified without performing a read-modify-write sequence in software: instead the changes are posted to the peripheral, and performed in-situ. Without this capability, it is difficult to safely access IO registers when an interrupt service routine is concurrent with code running in the foreground, or when the two processors are running code in parallel.
And the original script doesn’t support that either, resulting in missing references (red) and broken decompile:
So I set out to make the SVD-Loader play nice with RP2040.
tl;dr: Get it from my github fork, what follows is a brief write-up about the change, and “after” pictures.
So apart from fixing the initial bug, (with a one-liner) I decided to basically replicate all the peripherals at the above mentioned offsets when RP2040’s SVD is detected.
Downside of that is that the memory map:
(part of) memory map prior the ARA mod
ends up looking more crowded:
(part of) memory map after the ARA mod
Fortunately with a little bit of code restructuring (and help of the
field from the SVD), I was able to re-use the data structures (so e.g. both UARTs
reference single data structure). Plus as an additional tweak, I aliased names
ending with zero (
I2C0, …) to a name without (
The end result are correct references and working decompile:
- auto-added pointers for the created structures
- added better instal instructions (in the repo)
- made the script run idempotent (subsequent runs should just add missing peripherials/blocks, instead of throwing exception)
All in all, I think this should make RP2040 firmware reversing in Ghidra a tad easier.
Read the full delta (so far), if that’s interesting to you.
This is a relatively quick hack to improve quality of life for anyone reverse-engineering a RP2040 (Raspberry Pico) firmware in Ghidra.
What is still missing (and unrelated) is:
- support for bitfields in Ghidra
- auto-import of some structures (vector map, things like
Maybe someone will come up with those… in time3. :-)