Making SVD-Loader for Ghidra play nice with RP2040

Problem statement

When reversing ARM-based firmware in Ghidra, it’s very useful to be able to load up peripheral / memory blocks from the supplied SVD file1.

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:

missing references missing references

broken decompile 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:

memory map prior mod (part of) memory map prior the ARA mod

ends up looking more crowded:

memory map after mod (part of) memory map after the ARA mod

Fortunately with a little bit of code restructuring (and help of the derivedFrom 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 (UART0, PIO0, I2C0, …) to a name without (UART, …).

The end result are correct references and working decompile:

correct references correct references

working decompile working decompile

I also:

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.

Closing words

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:

Maybe someone will come up with those… in time3. :-)

  1. CMSIS-SVD, System View Description, is a description of ARM-based MCUs. The SVD file for RP2040 is in the SDK.

  2. TypeError: unsupported operand type(s) for /: 'NoneType' and 'int' (in calculate_peripheral_size)

  3. You, dear reader? No pressure.