[[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 [http://dev.gateworks.com/venice/boot_firmware/ pre-built Boot Firmware] 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 boot firmware image contains all of the components of the 'Boot Firmware': - ARM Trusted Firmware (ATF) - SPL - U-Boot - U-Boot environment This firmware is typically put on the eMMC boot0 hardware partition to keep it separate from the eMMC user partition used for the OS. Venice Boot Firmware Image Map: * imx8mm: ||= start-end =||= len =||= item =||= notes =|| || 0x00000000 - 0x00008400 || 33KiB || unused || || 0x00008400 - 0x00060000 || 351KiB || SPL (spl.bin) || Loads and u-boot.itb and transfers control to the ATF || || 0x00060000 - 0x003F0000 || 3648KiB || U-Boot (u-boot.itb) || FIT image containing u-boot, ATF, and fdt || || 0x003F0000 - 0x00400000 || 64KB || U-Boot env || redundant 32KB env || * imx8mn/imx8mp: ||= start-end =||= len =||= item =||= notes =|| || 0x00000000 - 0x00058000 || 352KiB || SPL (spl.bin) || Loads and u-boot.itb and transfers control to the ATF || || 0x00058000 - 0x003F0000 || 3680KiB || U-Boot(u-boot.itb) || FIT image containing u-boot, ATF, and fdt || || 0x003F0000 - 0x00400000 || 64KB || U-Boot env || redundant 32KB env || Note that while the size of the SPL and offset/size of U-Boot can vary based on bootloader configuration the start offset of the SPL is dictated by the SOC-specific BOOTROM and varies based on the boot device and hardware partition: * imx8mm: - eMMC user partition: 33KiB - eMMC boot partition: 33KiB * imx8mn/imx8mp: - eMMC user partition: 32KiB - eMMC boot partition: 0KiB == eMMC boot partition By default since around Jan 2024 Gateworks has been placing boot firmware on the eMMC 'boot0' hardware partition (instead of the 'user' hardware partition) which accomplishes two things: 1. keeps your boot firmware isolated from your OS (filesystems as well as disk partition table) 2. allows compressed disk images now representing the OS only to be shared between the various IMX8M SoC's supported by the venice family (imx8mm, imx8mn, imx8mp) which have different boot firmware images At this same time the U-Boot environment data was moved from an offset of 0xfe0000 (16M - 128K) to an offset of 0x3e0000 (4M - 128K) so that it fit within the minimal size of the 4MB boot hardware partitions. However regardless of what hardware partition the eMMC boots from, the U-Boot environment data is always at the same offset on that hardware partition (the top of the 4MB boundary) If you wish to move the boot firmware between user, boot0, and boot1 eMMC hardware partitions note the following: - the different SoC BOOT ROM's use different offsets depending on the emmc hardware partition. Specifically: * IMX8MM BOOT ROM will always fetch boot code at a 33KB offset for eMMC devices regardless of the hardware partition * IMX8MN/IMX8MP BOOT ROM will fetch boot code at a 32KB offset for eMMC devices booting from user and 0KB offset for eMMC devices booting from boot0 or boot1 - the eMMC PARTITION_CONFIG register (EXT CSD 179) BOOT_PARTITION_ENABLE field specifies what eMMC hardware partition is active on power-up so if you move the boot firmware you must update the PARTITION_CONFIG register (via U-Boot 'mmc partconf' command, via Linux 'mmc bootpart' command (from the 'mmc-utils' package) or via JTAG by specifying the '--partconf' cmdline parameter to the mkimage_jtag script used to create a JTAG image To determine what hardware partition is currently being used: - if using the latest boot firmware U-Boot SPL will announce the hardware partition such as 'Trying to boot from eMMC boot0'. If the hardware partition (boot0, boot1, user) is not shown then you are using an older boot firmware - use the U-Boot 'mmc partconf 2' command (2 is the eMMC device for Venice) and examine the 'BOOT_PARTITION_ENABLE' field. If using a using a newer boot firmware it will show both the numeric value as well as what that value represents, for example 'BOOT_PARTITION_ENABLE: 0x1 (boot0)' (and if just the number is shown you are using an older boot firmware). Note the possible values for BOOT_PARTITION_ENABLE are: 0x1 (boot0), 0x2 (boot1), 0x7 (user). - use the Linux 'mmc extcsd read' command and look at the PARTITION_CONFIG register {{{bits[5:3]}}} Examples: * show the current eMMC boot partition: - from U-Boot: {{{#!bash u-boot=> mmc partconf 2 EXT_CSD[179], PARTITION_CONFIG: BOOT_ACK: 0x0 BOOT_PARTITION_ENABLE: 0x1 (boot0) PARTITION_ACCESS: 0x0 }}} - BOOT_PARTITION_ENABLE 0x1 represents boot0 (0x1=boot0, 0x2=boot1, 0x7=user) - from Linux: {{{#!bash root@jammy-venice:~# mmc extcsd read /dev/mmcblk2 | grep PARTITION_CONFIG Boot configuration bytes [PARTITION_CONFIG: 0x08] root@jammy-venice:~# echo $(($((0x08 >> 3)) & 0x7)) # show bits[5:3] 1 root@jammy-venice:~# echo $(($(( $(mmc extcsd read /dev/mmcblk2 | sed -n 's/.*PARTITION_CONFIG: \(.*\)]/\1/p') >> 3)) & 0x7)) # 1-line version 1 }}} - BOOT_PARTITION_ENABLE is represented by {{{bits[5:3]}}} of PARTITION_CONFIG which can be read from the EXT CSD register 179 named 'PARTITION_CONFIG' (1=boot0, 2=boot1, 7=user) * IMX8MM: Program boot firmware to user: - from U-Boot {{{#!bash # fetch flash.bin boot firmware tftpboot $loadaddr $dir/venice-$soc-flash.bin # set blkcnt to $filesize/512 (convert to 512 byte blocks) setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 # select the emmc (dev=2) user hardware partition (part=0) mmc dev 2 0 # write to 33K (which is 0x42 hex 512 byte blocks) mmc write $loadaddr 0x42 $blkcnt # set PARTITION_CONFIG to BOOT_ACK=1 BOOT_PARTITION_ENABLE=7 to boot from user mmc partconf 2 1 7 0 }}} - from Linux {{{#!bash # fetch flash.bin boot firmware wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mm-flash.bin # write it to mmcblk2 (user) at offset 33KB dd if=venice-imx8mm-flash.bin of=/dev/mmcblk2 bs=1k seek=33 conv=notrunc # set BOOT_PARTITION_ENABLE to 7 for mmcblk2 (eMMC) mmc bootpart enable 7 0 /dev/mmcblk2 }}} - via JTAG: {{{#!bash # start with soc-specific position-independent boot firmware binary wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mm-flash.bin # create a 4MB file and copy the position-independent boot firmware to where the BOOT ROM expects it (33K in this case) truncate -s 4M firmware.img dd if=venice-imx8mm-flash.bin of=firwmare.img bs=1k seek=33 conv=notrunc # use the mkimage_jtag script to create a JTAG binary that flashes this image and sets the PARTITION_CONFIG register ./mkimage_jtag --soc imx8mm --emmc -s --partconf=user \ firmware.img@user:erase_part:0-8192 \ > firmware-venice-imx8mm.bin }}} * IMX8MM: Program boot firmware to boot0: - from U-Boot {{{#!bash # fetch flash.bin boot firmware tftpboot $loadaddr $dir/venice-$soc-flash.bin # set blkcnt to $filesize/512 (convert to 512 byte blocks) setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 # select the emmc (dev=2) boot0 hardware partition (part=1) mmc dev 2 1 # write to 33K (which is 0x42 hex 512 byte blocks) mmc write $loadaddr 0x42 $blkcnt # set PARTITION_CONFIG to BOOT_ACK=1 BOOT_PARTITION_ENABLE=1 to boot from boot0 mmc partconf 2 1 1 0 }}} - from Linux {{{#!bash # fetch flash.bin boot firmware wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mm-flash.bin # write it to mmcblk2boot0 at offset 33KB echo 0 > /sys/class/block/mmcblk2boot0/force_ro dd if=venice-imx8mm-flash.bin of=/dev/mmcblk2boot0 bs=1k seek=33 conv=notrunc # set BOOT_PARTITION_ENABLE to 1 for mmcblk2 (eMMC) mmc bootpart enable 1 0 /dev/mmcblk2 }}} - via JTAG: {{{#!bash # start with soc-specific position-independent boot firmware binary wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mm-flash.bin # create a 4MB file and copy the position-independent boot firmware to where the BOOT ROM expects it (33K in this case) truncate -s 4M firmware.img dd if=venice-imx8mm-flash.bin of=firmware.img bs=1k seek=33 conv=notrunc # use the mkimage_jtag script to create a JTAG binary that flashes this image and sets the PARTITION_CONFIG register ./mkimage_jtag --soc imx8mm --emmc -s --partconf=boot0 \ firmware.img@boot0:erase_part:0-8192 \ > firmware-venice-imx8mm.bin }}} * IMX8MP: Program boot firmware to user: - from U-Boot {{{#!bash # fetch flash.bin boot firmware tftpboot $loadaddr $dir/venice-$soc-flash.bin # set blkcnt to $filesize/512 (convert to 512 byte blocks) setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 # select the emmc (dev=2) user hardware partition (part=0) mmc dev 2 0 # write to 32K (which is 0x40 hex 512 byte blocks) mmc write $loadaddr 0x40 $blkcnt # set PARTITION_CONFIG mmc partconf 2 1 user user }}} - from Linux {{{#!bash # fetch flash.bin boot firmware wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mp-flash.bin # write it to mmcblk2 (user) at offset 32KB dd if=venice-imx8mp-flash.bin of=/dev/mmcblk2 bs=1k seek=32 conv=notrunc # set BOOT_PARTITION_ENABLE to 7 for mmcblk2 (eMMC) mmc bootpart enable 7 0 /dev/mmcblk2 }}} - via JTAG: {{{#!bash # start with soc-specific position-independent boot firmware binary wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mp-flash.bin # create a 4MB file and copy the position-independent boot firmware to where the BOOT ROM expects it (32K in this case) truncate -s 4M firmware.img dd if=venice-imx8mp-flash.bin of=firmware.img bs=1k seek=32 conv=notrunc # use the mkimage_jtag script to create a JTAG binary that flashes this image and sets the PARTITION_CONFIG register ./mkimage_jtag --soc imx8mp --emmc -s --partconf=user \ firmware.img@user:erase_part:0-8192 \ > firmware-venice-imx8mp.bin }}} * IMX8MM: Program boot firmware to boot0: - from U-Boot {{{#!bash # fetch flash.bin boot firmware tftpboot $loadaddr $dir/venice-$soc-flash.bin # set blkcnt to $filesize/512 (conver to 512 byte blocks) setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 # select the emmc (dev=2) boot0 hardware partition (part=1) mmc dev 2 1 # write to 0K mmc write $loadaddr 0x0 $blkcnt # set PARTITION_CONFIG to BOOT_ACK=1 BOOT_PARTITION_ENABLE=1 to boot from boot0 mmc partconf 2 1 1 0 }}} - from Linux {{{#!bash # fetch flash.bin boot firmware wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mp-flash.bin # write it to mmcblk2boot0 at offset 0KB echo 0 > /sys/class/block/mmcblk2boot0/force_ro dd if=venice-imx8mp-flash.bin of=/dev/mmcblk2boot0 bs=1k seek=0 conv=notrunc # set BOOT_PARTITION_ENABLE to 1 (boot0) for mmcblk2 (eMMC) mmc bootpart enable 1 0 /dev/mmcblk2 }}} - via JTAG: {{{#!bash # start with soc-specific position-independent boot firmware binary wget https://dev.gateworks.com/venice/boot_firmware/venice-imx8mp-flash.bin # create a 4MB file and copy the position-independent boot firmware to where the BOOT ROM expects it (0K in this case) truncate -s 4M firmware.img dd if=venice-imx8mp-flash.bin of=firmware.img bs=1k seek=0 conv=notrunc # use the mkimage_jtag script to create a JTAG binary that flashes this image and sets the PARTITION_CONFIG register ./mkimage_jtag --soc imx8mp --emmc -s --partconf=boot0 \ firmware.img@boot0:erase_part:0-8192 \ > firmware-venice-imx8mp.bin }}} The examples above start with the position-independent boot firmware therefore they do not contain the U-Boot environment area which is always stored at the top of the 4MB boundary of the eMMC hardware partition that U-Boot boots from. You can create and update this environment data via: * Create environment data: - from Gateworks default firmware image: {{{#!bash wget https://dev.gateworks.com/venice/images/firmware-venice-imx8mm.img dd if=firmware-venice-imx8mm.img of=env.bin bs=1k skip=4032 count=128 }}} * Update U-Boot env (to $bootpart) from U-Boot {{{#!bash tftpboot $loadaddr env.bin && mmc dev $dev $bootpart && mmc write $loadaddr 0x1f80 0x80 }}} * Update U-Boot env to user from Linux {{{#!bash dd if=env.bin of=/dev/mmcblk2 bs=1k seek=4032 conv=notrunc }}} * Update U-Boot env to boot0 from Linux {{{#!bash echo 0 > /sys/class/block/mmcblk2boot0/force_ro dd if=env.bin of=/dev/mmcblk2boot0 bs=1k seek=4032 conv=notrunc }}} == Legacy Boot Firmware Image The original Gateworks IMX8MM Boot Firmware (now considered legacy) was installed to the eMMC user hardware partition. Legacy Venice IMX8MM Boot Firmware Image Map: ||= start-end =||= len =||= item =||= notes =|| || 0x00000000 - 0x00008400 || 33KiB || MBR Partition Table || only first 512B used || || 0x00008400 - 0x00060000 || 351KiB || SPL (spl.bin) || Loads and u-boot.itb and transfers control to the ATF || || 0x00060000 - 0x00FF0000 || 15MiB || U-Boot (u-boot.itb) || FIT image containing u-boot, ATF, and fdt || || 0x00FF0000 - 0x01000000 || 64KB || U-Boot env || redundant 32KB env || || 0x01000000 - || || Disk Partitions || used by the OS || [=#u-boot] == U-Boot Bootloader Read more on the dedicated bootloader page here: [wiki:venice/bootloader Venice U-Boot Bootloader]