Don’t fear the bricking (unbricking Unmatched via JTAG)

First published at: https://forums.sifive.com/t/dont-fear-the-bricking-unbricking-unmatched-via-jtag/5449

Despite, as i think, this is quite obvious, some people miss the point.

So let me show you how to unbrick the HiFive Unmatched board, that's usefull in case of:

  • installing BSP without the need of preparing the SD card in advance (you still have to insert it through)
  • unbricking the board in case of deleting U-Boot
  • testing U-Boot + OpenSBI on fly hijacking the load addresses

The scheme works independently of selected MSEL value.

If we study the board boot proccess it will look like:

  • ZSBL starts on hartid 0 and initializes PCLK
  • ZSBL starts scanning partions and looks for 5B193300-FC78-40CD-8002-E86C45580B47 GUID
  • ZSBL loads U-Boot's SPL into L2 LIM (scratchpad TCM L2) at the start of 0x08000000
  • U-Boot SPL among other things initializes DDR and loads u-boot.bin to 0x80200000 and OpenSBI to 0x80000000
CONFIG_SPL_TEXT_BASE=0x08000000
CONFIG_SPL_LOAD_FIT_ADDRESS=0x84000000
CONFIG_SYS_TEXT_BASE=0x80200000
CONFIG_SPL_OPENSBI_LOAD_ADDR=0x80000000

There is one interesting thing in original (meta-sifive SDK) U-boot config, as we can see CONFIG_SPL_LOAD_FIT_ADDRESS is set and it is used only for spl_ram loading.

The rest is out of our scope. As we can see - it is possible to replace U-Boot + OpenSBI by intercepting CONFIG_SPL_TEXT_BASE address, loading our RAM U-Boot SPL, allowing it to do all init works and then intercept CONFIG_SPL_LOAD_FIT_ADDRESS, load U-Boot into it and continue execution.

We need to patch U-Boot and produce our special RAM version (patch attached).

I used a fork of OpenOCD which resides here https://github.com/riscv/riscv-openocd (not sure if vanilla OpenOCD will work correctly) and the following config https://gist.github.com/a4lg/df51da397b72299042182ccc19f75371 with a small function added:

proc load_uboot {} {
        reset init
        reg pc 0x00010000
        bp 0x08000000 0x1000 hw
        resume
        after 10000
        regexp {(0x[a-fA-F0-9]*)} [reg pc] pc
        echo "PC=$pc"
        load_image u-boot-spl.bin 0x8000000 bin
        rbp all
        wp 0x84000000 0x1000
        resume
        after 5000
        regexp {(0x[a-fA-F0-9]*)} [reg pc] pc
        echo "PC=$pc"
        rwp 0x84000000
        load_image u-boot.itb 0x84000000 bin
        verify_image u-boot.itb 0x84000000 bin
        resume
}

The reason for "after" is that bp and wp hits are not reported and we have to "poll" the pc reg - once reg pc output is not empty - we have hit the breakpoint.
The reason for rbp all, rwp 0x84000000 is that currently bp and wp behaves strangely on Unmatched board with current OpenOCD version.

Works even if MSEL is in debug mode.

Expected result:

U-Boot SPL 2021.01-00053-g8afbe20524-dirty (Jul 21 2021 - 04:00:02 +0000)
Trying to boot from RAM

U-Boot 2021.01-00053-g8afbe20524-dirty (Jul 21 2021 - 04:00:02 +0000)

Now you can load anything from network, for example a kernel with initramfs to deploy the BSP. Most distribution will fit into 16GB memory, i advise against those that don't.