Background

FIP introduction

Most people call the data chunk that makes an Amlogic device bootable a “bootloader”, or “u-boot”, but that’s far from the truth. The data chunk is actually a Firmware Image Package, or simply FIP. And U-Boot is only part of it (BL33).

As per ARM Trusted Firmware Design, FIP is an archive that contains multiple parts of Trusted Firmware-A (TF-A)

Using a Firmware Image Package (FIP) allows for packing bootloader images (and potentially other payloads) into a single archive that can be loaded by TF-A from non-volatile platform storage. A driver to load images from a FIP has been added to the storage layer and allows a package to be read from supported platform storage.

A modern ARM device must has FIP on its persistent storage so it could boot. Basically SoCs embedds only a small chunk of code (BL1/BootROM) that’s responsible for early SoC initialization and loading FIP. All later logic happens in different parts of FIP (BL2, BL2, BL30, etc).

Amlogic FIP storage

On Amlogic platform, the FIP is usually stored on the embedded MMC (eMMC) device, or NAND. It could also be stored on SD card as it’s pretty much the same thing as eMMC user partition.

For simplicity, let’s only focus on the eMMC scenarios.

eMMC hardware partition

All eMMC devices have multiple hardware partitions: two boot hwpartitions that’re usually 4MiB in size and read-only, one RPMB (Replay Protected Memory Block) hwpartition that’s used for safe data transfer between safe and unsafe worlds, and lastly one user hwpartition that’s pretty much the same as a SD card.

The partition layout is usually like the following:

name offset size linux dev e.g.
boot0 0 4MiB mmcblk1boot0 (block)
boot1 4MiB 4MiB mmcblk1boot1 (block)
rpmb 8MiB 4MiB mmcblk1rpmb (char)
user 12MiB - mmcblk1 (block)

User-defined partitions (MBR, GPT, whatever they are) only live in user hardware partition and Linux kernel treats all four hardware partitions as if they’re four different devices.

FIP partition

Before GXL family, until GXBB family, i.e. all SoCs until S905 no suffix, FIP could only be stored on eMMC user partition, even though there are two boot partitions. Since GXL family, i.e. all SoCs since S905X/D/W/L, FIP could also be stored on eMMC boot partitions.

The following chart describes the boot flow on different eMMC hareware partitions on different Amlogic SoCs

target <= GXBB (S905) >= GXL (s905X/D/W/L)
user boot boot 1
boot0 skip boot 2
boot1 skip boot 3

Whichever the partition is, FIP image is always stored with a 512 byte offset, to avoid conflicting with MBR on eMMC user partition (even when it’s stored on boot partition).

FIP dumping

from Amlogic USB burning image

Amlogic USB burning image, usually with a .img suffix, is Amlogic’s proprietary format that’s used by their Amlogic USB Bunring Tool to burn system images onto devices with Amlogic SoCs.

You can use my ampack tool to unpack such image. The FIP images would be dumped along other partitions as bootloader.PARTITION

ampack unpack mibox3.img mibox3-unpacked

from eMMC user partition

This is usually the place on a running device that you would want to dump FIP from.

Be sure you dumped from the right block device (the one with boot0 and boot1 companions) and with correct offset (512 bytes). The maximum data you would want to get is usually 4MiB - 512B.

On Android with root permission

Enable ADB network debugging and get a shell, then run su to gain root permission:

adb connect [IP address]
adb shell
su

Dump the FIP on device and exit

dd if=/dev/mmcblk1 of=/sdcard/fip bs=512 skip=1 count=8191
exit
exit

Then pull the fip image

adb pull /sdcard/fip fip

On generic Linux distro

For generic Linux scenarios dd directly without adb pulling.

dd if=/dev/mmcblk1 of=fip bs=512 skip=1 count=8191

from eMMC boot partitions

This is a speical case, as eMMC boot partitions are never used by any stock Amlogic Android image or third party Linux images for Amlogic devices. If this is the case (like on my tinkered box), dump from the corresponding boot0 or boot1 block device instead.

dd if=/dev/mmcblk1boot0 of=fip.img bs=512 skip=1

FIP tinkering

FIPs for SoCs since GXL

For SoCs since GXL, all parts in a FIP are signed and encrypted, and then the FIP image is signed and encrypted as a whole. It would be in Amlogic’s proprietary format so you need a dedicated gxlimg tool to unsign/decrypt/encrypt/sign.

FIP decryption

Use gxlimg to decrypt and extract all BL parts from FIP

mkdir fip-parts
gxlimg --extract unpacked/bootloader.PARTITION fip-parts

E.g. for R3300L, the output folder would contain the following members:

> ls fip-parts/
bl2.sign  bl301.enc  bl30.enc  bl31.enc  bl33.enc  fip  fip.enc

Bl33 (u-boot) encryption

Use the gxlimg tool to encrypt your own BL33 image before substitute the original BL33, assuming ../u-boot.bin is your own u-boot binary

gxlimg --type bl3x ../u-boot.bin bl33.enc

FIP repack

Use the gxlimg tool to pack everything (your new bl33.enc and all other original parts) into a new FIP image

gxlimg --type fip --bl2 bl2.sign --bl30 bl30.enc --bl301 bl301.enc --bl31 bl31.enc --bl33 bl33.enc ../new-fip.img

FIPs for SoCs until GXBB

A FIP image for SoCs since GXL is signed and encrypted as a whole, all parts are stored plainly inside it. It would be in slightly modified TF-A’s reference FIP format when not encrypted. You only need the meson-tools to unsign/sign the FIP.

FIP unsigning

Use unamlbootsig from meson-tools to unsign the FIP image

unamlbootsig original original.dec

This would also hint on the offset and size of different parts.

Bl33 (u-boot) substitution

Use my script to replace BL33 inside FIP image, it assumes the follows:

  • unsigned BL33 is at original.dec
  • a folder parts exist
  • new u-boot.bin is at ../../u-boot/u-boot.bin
  • BL2 size is 0xC000

It works without modification on mibox3, but you’ll need to modify the script accordingly for other devices.

mkdir parts
python new_fip.py

The substituted, unsigned new FIP image would be with_mainline

FIP signing

Use amlbootsig from meson-tools to sign the FIP image

amlbootsig with_mainline with_mainline.enc

FIP writing

Direct write

You can write the new FIP image to either one of the following places:

  • eMMC user partition (mmcblkN)
  • eMMC boot partition 1 (mmcblkNboot0)
  • eMMC boot partition 2 (mmcblkNboot1)

If you write it to later ones then be sure the earlier ones don’t have valid FIP on them (e.g. if you write to boot partition 2, then be sure to erase boot partition 1 and first 4M of user partition)

If you’re writing FIP image prepared using the methods here then remember to leave a 512 offset (reserved for MBR, yes, even for boot partitions).

E.g. to write to eMMC user hwpartition:

sudo dd if=new-fip.img of=/dev/mmcblkN bs=512 seek=1

E.g. to write to eMMC boot hwpartition 1:

echo 0 | sudo tee /sys/block/mmcblkNboot0/force_ro 
sudo dd if=new-fip.img of=/dev/mmcblkNboot0 bs=512 seek=1

If you’re writing u-boot.bin.sd.bin built with amlogic-boot-fip then there’s no need for the 512 offset, but remember to restore the MBR partitions (sfdisk -d then sfdisk) you had on eMMC user partition.

Fake Burning image

For SoCs until GXBB family, it’s possible to replace the bootloader.PARTITION part in the burning image, remove unneeded Android parts, and repack it up.

E.g. for mibox3, only the following parts are needed to get an Amlogic USB Burning Image that boots the box into a state where booting mainline kernel from USB is possible.

> ls unpacked
aml_sdc_burn.ini  bootloader.PARTITION  DDR.USB  meson1.dtb  platform.conf  UBOOT.USB

using ampack to pack the above parts into an Amlogic USB Burning Image

ampack pack mibox3-unpacked mibox3-bare-but-mainline.img