Skip to content

Instantly share code, notes, and snippets.

@ganboing
Last active March 4, 2025 02:59
Show Gist options
  • Save ganboing/ad6079f4da85d586d9cbad661ba7fd34 to your computer and use it in GitHub Desktop.
Save ganboing/ad6079f4da85d586d9cbad661ba7fd34 to your computer and use it in GitHub Desktop.

EIC7700 firmware (bootchain)

First off, EIC7700 has several CPUs. MCPU (P550 cluster) + SCPU (E21) + LCPU (E21) + NPU and DSPs. The SCPU (Secure CPU) is used to run the Masked ROM after power on. The Masked ROM has the logic of loading different payloads from the selected boot source image and kickstarts other CPUs such as MCPU. Optionally it can also validate the cryptographic key/signature of the payload against OTP, thus implementing secure-boot. ESWIN calls the container of the payloads bootchain By default the boot source is QSPI boot flash. The bootchain is stored at the beginning of the flash (offset 0). The boot flash contains the following on my Hifive P550

  • DDR init: Vendor binary blob to initialize DDR (closed source)
  • "firmware": AFAIK, a very small piece of code that does little to nothing. Perhaps only to keep SCPU active.
  • bootloader: The usual opensbi+u-boot stuff that runs on MCPU. (open source, ESWIN maintains the patched opensbi/u-boot)

As it's shown, the DDR init blob is supplied by vendor, and opensbi+u-boot is handed off to with DDR already initialized. Therefore, there's no u-boot SPL required

The bootchain follows the format defined by ESWIN. reference It looks something like this:

00000000  45 53 57 42 03 00 00 00  01 00 00 00 00 00 40 00  |ESWB..........@.|  <--- bootchain header
00000010  00 00 00 00 4c 05 00 00  00 00 00 00 00 00 70 00  |....L.........p.|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 01 00 00 00  |................|
00000030  00 00 10 00 00 00 00 00  20 d4 19 00 00 00 00 00  |........ .......|
00000040  00 00 10 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  01 00 00 00 00 00 48 00  00 00 00 00 18 f7 3b 00  |......H.......;.|
00000060  00 00 00 00 00 00 30 01  00 00 00 00 00 00 00 00  |......0.........|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000e0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00100000  57 53 45 bb 00 00 00 00  00 00 00 00 00 00 00 00  |WSE.............|  <--- paylodad header
00100010  60 0a 00 00 00 00 00 00  20 d4 19 00 00 00 00 00  |`....... .......|
00100020  00 00 00 59 00 00 00 00  00 00 00 59 00 00 00 00  |...Y.......Y....|
00100030  00 00 00 00 00 65 6e 67  00 00 00 00 00 00 00 00  |.....eng........|
00100040  10 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00100050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100100  b7 07 90 50 17 d7 19 00  23 2a f7 42 b7 07 81 51  |...P....#*.B...Q|
00100110  17 d7 19 00 23 22 f7 42  b7 87 82 51 17 d7 19 00  |....#".B...Q....|
...
00400000  57 53 45 bb 00 00 00 00  00 00 00 00 00 00 00 00  |WSE.............|  <--- another payload header
00400010  00 02 00 00 00 00 00 00  4c 05 00 00 00 00 00 00  |........L.......|
00400020  00 00 00 59 00 00 00 00  00 00 00 59 00 00 00 00  |...Y.......Y....|
00400030  00 00 00 00 00 65 6e 67  00 00 00 00 00 00 00 00  |.....eng........|
00400040  70 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |p...............|
00400050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00400100  41 11 22 c4 37 04 b4 21  06 c6 13 04 c4 03 00 40  |A.".7..!.......@|
00400110  0f 00 20 08 21 80 13 74  f4 0f 1d e8 b7 87 82 51  |.. .!..t.......Q|

The bootchain header is merely an array of pointers to different payload headers, and decoding the image gives:

Number of entries: 3
Entry 0: offset=0x400000 size=1356 (0x54c) payload_type=112 (FIRMWARE) last=0
  payload_off=0x200 payload_size=1356 (0x54c) load_addr=0x59000000 entry_addr=0x59000000 boot=0 (SCPU)
Entry 1: offset=0x100000 size=1692704 (0x19d420) payload_type=16 (DDR) last=0
  payload_off=0xa60 payload_size=1692704 (0x19d420) load_addr=0x59000000 entry_addr=0x59000000 boot=0 (SPCU)
Entry 2: offset=0x480000 size=3929880 (0x3bf718) payload_type=48 (BOOTLOADER) last=1
  payload_off=0x19e1a0 payload_size=3929880 (0x3bf718) load_addr=0x80000000 entry_addr=0x80000000 boot=1 (MCPU)

This image is assembled by nsign with a config file that looks like this:

nsign.cfg
 cmd=chief_sign
 out=bootloader_ddr5_secboot.bin
 {
 in=second_boot_fw.bin
 boot_flags=SCPU
 payload_type=FIRMWARE
 sign_algorithm=plaintext
 keyid=00
 version=00000001
 link_addr=00000000
 load_addr=59000000
 entry_addr=59000000
 payload_flags=plaintext
 digest_mthd=SHA
 encrypted_mthd=plaintext
 devid=0000000000000000
 vid=00
 lang=656e67
 mid=0000000000000000
 params=00000000000000000000000000000000
 dl_load_addr=00000000
 dl_init_ofs=00000000
 dl_destory_ofs=00000000
 dl_ioctl_ofs=00000000
 dl_load_flags=00000000
 dl_irq_num=00000000
 dl_irq_ofs=00000000
 }
 {
 in=ddr_fw.bin
 boot_flags=SCPU
 payload_type=DDR
 sign_algorithm=plaintext
 keyid=00
 version=00000001
 link_addr=00000000
 load_addr=59000000
 entry_addr=59000000
 payload_flags=plaintext
 digest_mthd=SHA
 encrypted_mthd=plaintext
 devid=0000000000000000
 vid=00
 lang=656e67
 mid=0000000000000000
 params=00000000000000000000000000000000
 dl_load_addr=00000000
 dl_init_ofs=00000000
 dl_destory_ofs=00000000
 dl_ioctl_ofs=00000000
 dl_load_flags=00000000
 dl_irq_num=00000000
 dl_irq_ofs=00000000
 }
 {
 in=fw_payload.bin
 boot_flags=MCPU
 payload_type=BOOTLOADER
 sign_algorithm=plaintext
 keyid=00
 version=00000001
 link_addr=00000000
 load_addr=80000000
 entry_addr=80000000
 payload_flags=plaintext
 digest_mthd=SHA
 encrypted_mthd=plaintext
 devid=0000000000000000
 vid=00
 lang=656e67
 mid=0000000000000000
 params=00000000000000000000000000000000
 dl_load_addr=00000000
 dl_init_ofs=00000000
 dl_destory_ofs=00000000
 dl_ioctl_ofs=00000000
 dl_load_flags=00000000
 dl_irq_num=00000000
 dl_irq_ofs=00000000
 }

Update firmware (bootchain)

es_burn command in the u-boot is the recommended way to update bootchain on the QSPI flash. It basically place the payloads at some predefined offsets and updates the bootchain header to point to the re-located payloads. Compared to the bootchain file you passed to es_burn, the QSPI flash version basically got some holes added between the payloads, and the layloads can be aligned to block size of QSPI flash, making them easier to load for ROM.

Building you bootchain

Prepare the payloads. Either extract the payload from ESWIN published bootchain image, by using the dump utilitiy from my forked nsign repo, or you must find the correct payloads from different ESWIN/Sifive repos. The "official" way is to build it via https://github.com/sifive/meta-sifive/tree/rel/meta-sifive/hifive-premier-p550/recipes-bsp, but I prefer to do it manually.

Prepare a nsign.cfg file as described in previous section, and do nsign nsign.cfg

UART boot

Disclaimer: the instructions are given by ESWIN. I have not tested it yet.

Prepare the payloads as the previous section. Change cmd=chief_sign in nsign.cfg to uart_sign, and run nsign nsign.cfg. There'll be a ihex file created for UART boot. Switch the bootsel DIP switch to UART mode, and then send the ihex file as is to UART port. The board should now boot from this bootchain image uploaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment