Dump Amlogic S905D3 BootROM from Khadas VIM3L board

Posted on Tue 09 February 2021 in Article

This post describes how to dump bootROM from Amlogic S905D3 SoC using Khadas VIM3L board. Since this board doesn't use Secure Boot, we can execute custom code in Secure World (a.k.a TrustZone) without exploiting any vulnerability. In addition, the board exposes an UART connector, which is convenient for communicating with baremetal code running on target.

Collect bootroms

Ressources

Khadas VIM3L

The following resources were abused to complete this project :

  • VIM3L board : SBC based on Amlogic S905D3 System-On-Chip.
  • USB to TTL Converter : to enable communication between host and target using UART port.
  • S905D3 SoC datasheet : Khadas published a datasheet for S905D3. It contains a memory map, including the bootROM address.
  • khadas-uboot : U-Boot code released by Khadas contains a signing tool to package a bootloader image.
  • khadas-utils : Khadas released a tool to communicate with the bootROM via USB.

Procedure

In order to dump the bootROM, we need to execute custom code in Secure World.

On most firmwares provided by Khadas, U-Boot shell is accessible via UART port. However, U-Boot runs in Non-Secure World (a.k.a. Normal World) and therefore isn't helpful.

Instead, we need to execute our code earlier in the boot chain. Since Secure Boot is disabled on this board, we could modify the BL2 bootloader stored in flash memory. However, there's a more convienent solution : the bootROM implements a feature to load BL2 bootloader via the USB Type-C port. This in-RAM solution has the advantage of not altering the flash memory.

Finally, we also take advantage of the UART port to transfer data to our host.

Code

The code to dump the bootROM is straightforward : each byte is read then sent to UART port.

The datasheet published by Khadas indicates that bootROM memory is at address [0xFFFF0000-0xFFFFFFFF] (region named a53_rom). To send data to UART port, we reuse open-source code from U-Boot.

Early tests revealed an issue : UART data transfers would stop inconsistently before completion, and the board would then reset. This issue was caused by a watchdog timeout. U-Boot source repository contains the code to reset the watchdog, which solves that issue.

Build

The code is built using GNU C cross-compiler for the arm64 architecture (packages gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu on Debian) :

$ aarch64-linux-gnu-gcc -O3 -nostdlib -Wl,--build-id=none -o S905D3_dump_bootrom.elf S905D3_dump_bootrom.c
$ aarch64-linux-gnu-objcopy -O binary -j .text S905D3_dump_bootrom.elf S905D3_dump_bootrom.bin

Then, the binary is packaged as regular BL2 image for this target using the aml_encrypt_g12a tool from khadas-uboot repository:

$ ./khadas-uboot/fip/g12a/aml_encrypt_g12a --bl2sig --input ./S905D3_dump_bootrom.bin --output ./S905D3_dump_bootrom.img

Connect

Khadas documentation explains how to connect UART port on VIM3L board. On host side, we use minicom software to communicate with this port :

$ sudo minicom -D /dev/ttyUSB0

Then, we boot the board in USB Upgrade mode (a.k.a. TST mode or USB Download mode) as explained in Khadas documentation. In minicom console, we can see :

SM1:BL:511f6b:81ca2f;FEAT:A0F83180:20282000;POC:D;RCY:0;USB:0;

This string ending with USB:0; means the board has started in USB Upgrade mode.

On the host side, we see a new USB device :

[10504.840173] usb 1-4.3.1: new high-speed USB device number 16 using xhci_hcd
[10504.979469] usb 1-4.3.1: New USB device found, idVendor=1b8e, idProduct=c003, bcdDevice= 0.20
[10504.979495] usb 1-4.3.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[10504.979514] usb 1-4.3.1: Product: GX-CHIP
[10504.979525] usb 1-4.3.1: Manufacturer: Amlogic

In minicom, we use the capture feature (CTRL-A L) to save data sent by our program into a file minicom.cap.

Run

Our code is uploaded and run on the board using the update tool from khadas-utils repository.

$ ./khadas-utils/aml-flash-tool/tools/linux-x86/update write ./S905D3_dump_bootrom.img 0xfffa0000
..
Transfer Complete! total size is 65536 Bytes

$ ./khadas-utils/aml-flash-tool/tools/linux-x86/update run 0xfffa0000
[update]Run at Addr fffa0000
AmlUsbRunBinCode:ram_addr=fffa0000

In minicom console, we received the bootROM dump sent by our code :

e0031faae1031faae2031faae3031faae4031faae5031faae6031faae7031faae8031fa
ae9031faaea031faaeb031faaec031faaed031faaee031faaef031faaf0031faaf1031f
aaf2031faaf3031faaf4031faaf5031faaf6031faaf7031faaf8031faaf9031faafa031
[...]

Since the dump is encoded in hexadecimal representation, the last step is to convert it back to binary :

$ cat minicom.cap | xxd -r -p > VIM3L.bootrom.bin

$ sha1sum VIM3L.bootrom.bin
5de02d2958d4d7214b28521239ec9b63fe9a2dbe  VIM3L.bootrom.bin

$ wc -c VIM3L.bootrom.bin
65536   VIM3L.bootrom.bin

$ strings -13 VIM3L.bootrom.bin
auth failed, reboot...
511f6b60cfd40c6
ken.amlogic.com
03/26/19_12:20:42
gcc version 4.8
511f6b60cfd40c6
INDXCHIPOPS_ROMVfb_e1
FAILkey error
FAILkey len error
FAILmissing var
max-download-size
FAILmissing var!
FAILUnkonw chipinfo id
FAILVariable not implemented
FAILdata invalid size
FAILdata too large
FAILkey_len error
FAILunknow command
New World Cup
usb_dnl_fastboot

The strings in the bootROM dump reveal that Fastboot protocol is implemented (different protocol than the one we just used to upload our code).