[[PageOutline]] = 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 [http://dev.gateworks.com/venice/boot_firmware/flash.bin flash.bin] ready to flash onto boot devices as well as source for building and/or modifying the boot firmware yourself. [=#bootrom] == 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. [=#firmware-image] == Boot Firmware Image The firmware image contains all of the components of the 'Boot Firmware': - MBR partition table - ATF - Device Tree Binary - Note device tree source code (eg GW7200) is located in kernel source code at path: linux-venice/arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx.dtsi - Link for the source code [https://github.com/Gateworks/linux-venice here] - 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. [=#disk-images] == 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 [https://github.com/Gateworks/bsp-venice/blob/master/Makefile Venice BSP makefile] Once you have a compressed disk image you can install this using the methods described in [wiki:venice#firmware-update venice/firware-update]. For example in the bootloader: {{{#!bash 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 [https://github.com/Gateworks/ubuntu-rootfs/blob/master/ubuntu-rootfs.sh here] (search for growpart_once) To install the kernel and root filesystem on a removable block storage device see [wiki:linux/blockdev]. [=#u-boot] == 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] == 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 [#bootscript 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 [=#bootscript] == Boot Scripts When writing bootscripts compatible with [#distro-config 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 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: {{{#!bash 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: {{{#!bash mkimage -A arm64 -T script -C none -d boot.txt /boot/boot.scr }}} [=#boot_targets] === 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 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: {{{#!bash setenv boot_targets usb0 usb1 saveenv }}} [=#fit] == 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 :}}} # source a script by name from FIT image in memory - {{{iminfo }}} # print all the info contained in a FIT image in memory and verify (just not boot it) - {{{imextract }}} # extract item (ie kernel@1) to addr - {{{bootm [#conf] - $fdtcontroladdr}}} # boot default or 'conf' configuration (ie #config@1) - {{{bootm start [#conf] - $fdtcontroladdr}}} # boot from memory a specific configuration (or default configuration) from FIT image Example: * kernel.its with a single compressed kernel for ARM64 {{{#!bash /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: {{{#!bash cp arch/arm64/boot/Image . gzip Image mkimage -f kernel.its /tftpboot/kernel.itb }}} * boot the default configuration from U-Boot: {{{#!bash tftpboot $loadaddr kernel.itb && bootm $loadaddr - $fdtcontroladdr }}} References: * [http://git.denx.de/?p=u-boot.git;a=tree;f=doc/uImage.FIT doc/uImage.FIT] * http://www.denx.de/wiki/pub/U-Boot/Documentation/multi_image_booting_scenarios.pdf * http://elinux.org/images/f/f4/Elc2013_Fernandes.pdf = U-boot env tools A detailed description of u-boot-tools usage can be found [wiki:/ventana/bootloader#U-Bootenvtoolsfw_printenvfw_setenv here]. In order to configure u-boot-tools to work correctly for Venice you will need a fw_env.config file the appropriate values. To create this file: {{{#!bash cat << EOF > /etc/fw_env.config # Device offset Env. size /dev/mmcblk2 0xff0000 0x8000 /dev/mmcblk2 0xff8000 0x8000 EOF }}}