wiki:venice/boot

Version 2 (modified by Tim Harvey, 3 months ago) (diff)

added details about what is supported in U-Boot currently

Venice Boot Firmware

The 'Boot Firmware' for Venice is defined as the combination of the firmware stages through up to and including the bootloader. This can be broken down into the following stages:

  • Boot ROM (internal on i.MX8 SoC): fetch first level boot firmware from boot device (ie MMC or SPI FLASH) into L2 cache
  • SPL (Secondary Program Loader) - (U-Boot)
  • ATF (ARM Trusted Firmware)
  • Bootloader (U-Boot)

Gateworks provides pre-built Boot Firmware flash.bin ready to flash onto boot devices as well as source for building and/or modifying the boot firmware yourself.

i.MX8 BOOT ROM

The BOOT ROM is firmware baked into the SoC and is in charge of loading code from the 'boot device' into an L2 cache scratchpad, verifying signatures (if using trusted boot) and executing it.

The BOOT ROM fetches code from an offset of 33KiB on the boot device which leaves the first 33KiB available for other firmware needs.

Boot Firmware Image

The firmware image contains all of the components of the 'Boot Firmware':

  • MBR partition table
  • ATF
  • Device Tree Binary
  • U-Boot

Venice Boot Firmware Image Map:

start-end len item notes
0x0000000 - 0x0008400 33KiB MBR Partition Table only first 512B used
0x0008400 - 0x0060000 350KiB SPL Loads and u-boot.itb and transfers control to the ATF
0x0060000 - 0x0FF0000 15MiB u-boot.itb FIT image containing u-boot, ATF, and fdt
0x0FF0000 - 0x1000000 64KB U-Boot env redudant 32KB env
0x1000000 - Disk Partitions used by the OS

The firmware image starting at the 33KB offset is the 'flash.bin' file created by U-Boot which contains the u-boot.itb.

Building a Bootable Disk Images

Gateworks releases compressed disk images that can be easily flashed using the U-Boot Bootloader. These disk images contain a partition table so they are tailored to the specific device size they are intended for. Additionally they are commonly configured for a small partition size to keep flash programming time at a minimum. This requires resizing the rootfs filesystem in order to take advantage of the remaining flash space.

Once you have a root filesystem directory/tarball and have a kernel Image you can create a compressed disk image that can be installed easily from the bootloader.

This is process is demonstrated in the Venice BSP makefile

Once you have a compressed disk image you can install this using the methods described in venice/firware-update. For example in the bootloader:

setenv dev 2 # emmc device; use mmc list to see all mmc devs
tftpboot ${loadaddr} focal-venice.img.gz && gzwrite mmc ${dev} ${loadaddr} ${filesize}

If you created a filesystem that did not stretch to the partition it was installed on (as we create a minimally sized filesystem image to fit within the eMMC device for faster programming) you will want to resize it after booting to Linux. This is a one time operation after flashing a compressed disk image and can be done using the 'growpart_once' script that we install on the Ubuntu Root filesystems found in the script here (search for growpart_once)

To install the kernel and root filesystem on a removable block storage device see linux/blockdev.

U-Boot Bootloader

Gateworks supports the U-Boot Bootloader for the Venice product family.

The purpose of a bootloader is to load the Linux kernel and execute it passing it configuration parameters such as an optional kernel command-line, an optional initial ramdisk filesystem, and a device-tree.

The following items are supported in the Gateworks U-Boot for Venice:

  • eth0 (IMX8MM FEC GbE)
  • USB Host mode (USB Mass Storage and USB Networking for ASIX USB 2.0 USB Network device)
  • eMMC / microSD

The following items are not currently supported in the Gateworks U-Boot for Venice:

  • PCIe support (include eth1 PCIe based Gbe)
  • USB device mode support (for USB OTG)

Distro Config

The Venice Bootloader uses U-Boot's 'Distro Config' which is a well defined U-Boot env intended to make it easier for distro maintainers to develop compatible bootscripts. This primarily entails a set of 'boot scripts' and variables that control them.

Ultimately this U-Boot environment is looking for a U-Boot boot script on a 'bootable' partition (partitions with the 'boot' flag enabled). It searches in this order with these rules:

  • boot_targets - list of target device type/nums to search: defaults to mmc1 mmc2 usb0 usb1 pxe
  • devplist - dynamically created list of all partitions flagged as 'bootable'
  • boot_prefixes - list of directories within a partition searched for bootscripts
  • boot_scripts - list of boot script names searched for

Boot Scripts

When writing bootscripts compatible with Distro Config you can assume the following env variables:

  • devtype - the device type the script was loaded from (mmc|usb|sata)
  • devnum - the device number the script was loaded from (ie 0 for mmc0, 1 for mmc1, etc)
  • distro_bootpart - the partition number the script was loaded from (ie 0, 1, etc)
  • fdtcontroladdr - the address the device-tree is at (Note that the Venice bootloader does not load/manipulate the device-tree itself - this is done by the SPL which loads/manipulates the device-tree and passes it to the bootloader)
  • kernel_addr_r - address where kernel can be loaded
  • bootargs - default bootargs to pass to the kernel - you probably want to add to this and not overwrite it
  • console - the serial console device to pass to the kernel

Additionally you should note the following:

  • use load/ls/save commands which support FAT/ext filesystem types automatically instead of the fs specific commands
  • if using a root filesystem that is not supported by the bootloader (ie F2FS or BTRFS) you can place your bootscript and kernel image in the FAT12 filesystem on partition 1 of the boot device. This filesystem is part of the 16MB 'Boot Firmware' image. If doing so you will need to compress the kernel and package it into a FIT image in order to fit it in the available space.

The Distro-Config environment supports legacy uImage scripts (it does not support FIT images with scripts). You can create these with the mkimage tool from U-Boot as such:

mkimage -A arm64 -T script -C none -d boot.txt boot.scr

The bootscript can be updated at runtime on the Linux target. For example:

mkimage -A arm64 -T script -C none -d boot.txt /boot/boot.scr

Boot Device Order (boot_targets)

While the Venice product family can only boot from its primary boot device (typically eMMC), once you are booted to the bootloader you can choose from a wider variety of devices to boot the OS from.

This OS boot device order is specified by the Distro Config environment. Specifically it is controlled by the boot_targets env variable which defaults to mmc1 mmc2 usb0 usb1 pxe.

For example, to limit OS booting to only USB:

setenv boot_targets usb0 usb1
saveenv

Flattened Image Tree (FIT) images

The U-Boot bootloader supports Flattened Image Tree (FIT) images which expand greatly on the legacy U-Boot image (uImage) format by allowing multiple binary blobs within an image. These blobs can be kernel images, ramdisk images, device-tree blobs, and bootloader scripts. Each image can also be optionally compressed (meaning U-Boot will decompress it) and check-sumed with a variety of hash mechanisms (meaning U-Boot will verify the image before using it).

Quick summary of FIT Images:

  • introduced to resolve limitations with original single-image formats and follow-on multi-image format supported by UBoot bootm (boot memory)
  • uses power of the Device-Tree-Compiler (DTC)
  • FIT .itb files can be created with mkimage by passing in a .its file which in device-tree notation describes the images
  • U-Boot supports FIT with several commands:
    • source <addr>:<name> # source a script by name from FIT image in memory
    • iminfo <fitaddress> # print all the info contained in a FIT image in memory and verify (just not boot it)
    • imextract <fitaddress> <item> <addr> # extract item (ie kernel@1) to addr
    • bootm <fitaddress>[#conf] - $fdtcontroladdr # boot default or 'conf' configuration (ie #config@1)
    • bootm start <fitaddress>[#conf] - $fdtcontroladdr # boot from memory a specific configuration (or default configuration) from FIT image

Example:

  • kernel.its with a single compressed kernel for ARM64
    /dts-v1/;
    / {
            description = "Simple image with single Linux kernel";
            #address-cells = <1>;
            images {
                    kernel@1 {
                            description = "Linux kernel";
                            data = /incbin/("./Image.gz");
                            type = "kernel";
                            arch = "arm64";
                            os = "linux";
                            compression = "gzip";
                            load = <0x40200000>;
                            entry = <0x40200000>;
                            hash@1 {
                                    algo = "sha256";
                            };
                    };
            };
    
            configurations {
                    default = "conf@1";
                    conf@1 {
                            description = "Boot Linux kernel";
                            kernel = "kernel@1";
                    };
            };
    };
    
  • create image:
    cp arch/arm64/boot/Image .
    gzip Image
    mkimage -f kernel.its /tftpboot/kernel.itb
    
  • boot the default configuration from U-Boot:
    tftpboot $loadaddr kernel.itb && bootm $loadaddr - $fdtcontroladdr
    

References:

U-boot env tools

A detailed description of u-boot-tools usage can be found here.

In order to configure u-boot-tools to work correctly for Ventana you will need a fw_env.config file the appropriate values.

To create this file:

cat << EOF > /etc/fw_env.config
# Device               offset          Env. size
/dev/mmcblk0           0xff0000        0x8000
/dev/mmcblk0           0xff8000        0x8000
EOF