All Collections
Custom Firmware
Firmware
Creating a Custom Firmware
Creating a Custom Firmware

Let's see how to make a custom firmware!

avhsupport avatar
Written by avhsupport
Updated over a week ago

On AVH, two types of firmware images are supported:

  1. A zip file containing multiple images such as a disk image, kernel or binary firmware etc

  2. A raw binary file, ELF executable or kernel loaded into RAM

The Coreimg Zip Package

The format of the coreimg zip package looks like this:

Required files

  • Info.plist (meta information)

  • If what you are booting is Linux

    • kernel (a Linux kernel in the Image format)

    • devicetree (the device tree for Linux in binary .dtb format)

  • Or if it's a raw firmware (e.g. used by Cortex-M machines)

    • firmware (Binary, ELF executable file, ZIP archive with load instructions)

Storage

Most devices have flash specific files that are required. See the relevant storage files page for that specific device.

What does the Info.plist look like?

An Info.plist file containing the version, type, build, unique identifier, device identifier, and optionally boot arguments. This can also be a JSON file with the same format.

Example

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>iot</string>
<key>UniqueIdentifier</key>
<string>WifiBasics</string>
<key>DeviceIdentifier</key>
<string>stm32u5-b-u585i-iot02a</string>
<key>Version</key>
<string>WifiBasics</string>
<key>Build</key>
<string>WB</string>
</dict>
</plist>

Bootargs

The default bootargs are provided by the model, but can be overriden via the Info.plist or Info.json file. The "normal" key is used for every regular boot. The "restore" key, if present, is used for a "firstboot" prior to the device being declared ready. It is expected that the device will reboot itself to indicate that this phase is complete. It is used, for example, on the Raspberry Pi to expand the root fs as shown below:

<key>bootargs</key>
<dict>
<key>normal</key>
<string>earlycon=uart8250,mmio32,0xfe215040 console=ttyS0,115200n8 rw rootwait root=/dev/mmcblk0p2 coherent_pool=1M 8250.nr_uarts=1 cma=64M</string>
<key>restore</key>
<string>earlycon=uart8250,mmio32,0xfe215040 console=ttyS0,115200n8 console=tty0 rw rootwait root=/dev/mmcblk0p2 coherent_pool=1M 8250.nr_uarts=1 cma=64M init=/usr/lib/raspi-config/init_resize.sh</string>
</dict>

Raw Firmware

There are three supported formats for executable firmware:

  • ELF executable: 32-bit ELF program files (fully linked, no relocation required) typically produced by IoT vendor tools: recommended - they contain all information needed to load the program, even if it has multiple memory ranges;

  • binary: loaded at a predefined location in memory (typically start of Flash for devices with built-in executable Flash, otherwise start of RAM);

  • ZIP archive: an archive containing a few binary files, and a file called LOAD.TXT that instructs the firmware loader to distribute them to different load locations in memory; a LOAD.TXT file could look like this:

load:0x00000000 name:bl2.bin
load:0x01000000 name:tfm_s_ns_signed.bin

This LOAD.TXT file would cause file bl2.bin in the same ZIP archive to be loaded at address 0x00000000, and tfm_s_ns_signed.bin at 0x01000000. This format is required when all that's available is binary files, and there's more than one of them.

Sample Firmware Packaging Script for Raspberry Pi

#!/bin/bash
set -e
[ -d pi ] || mkdir pi
cd pi
# Grab the raspberry pi firmware
[ -f 2022-01-28-raspios-bullseye-arm64.zip ] || wget https://downloads.raspberrypi.org/raspios_arm64/images/raspios_arm64-2022-01-28/2022-01-28-raspios-bullseye-arm64.zip
rm -rf {nand,devicetree,kernel,Info.plist,boot,rootfs}
unzip 2022-01-28-raspios-bullseye-arm64.zip
mv 2022-01-28-raspios-bullseye-arm64.img nand

# Mount the firmware image and extract the kernel and device tree
LO="$(losetup -f)"
mkdir boot
losetup -P "${LO}" nand
mount "${LO}p1" boot
# Enable ssh
touch boot/ssh
cp boot/bcm2711-rpi-4-b.dtb devicetree
zcat boot/kernel8.img > kernel
umount boot
rm -rf boot
mkdir rootfs
mount "${LO}p2" rootfs
# Don't run dhcpcd on docker interfaces
echo 'denyinterfaces veth*' >> rootfs/etc/dhcpcd.conf
umount rootfs
rm -rf rootfs
losetup -d "${LO}"

# create the Info plist that describes the model image
cat << EOF > Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>iot</string>
<key>UniqueIdentifier</key>
<string>Raspberry Pi OS Desktop</string>
<key>DeviceIdentifier</key>
<string>rpi4b</string>
<key>Version</key>
<string>11.2.0</string>
<key>Build</key>
<string>desktop</string>
</dict>
</plist>
EOF

# zip image and its ready for use
zip -r ../rpi4b-11.2-desktop.zip Info.plist nand devicetree kernel ramdisk.img
Did this answer your question?