Changes between Version 16 and Version 17 of venice/secure_boot


Ignore:
Timestamp:
03/23/2024 12:45:03 AM (8 months ago)
Author:
Tim Harvey
Comment:

add Full Disk Encryption section

Legend:

Unmodified
Added
Removed
Modified
  • venice/secure_boot

    v16 v17  
    11[[PageOutline]]
    22
     3= Venice Secure boot
     4== General Notes
     5Secure boot, particularly in the context of our Venice family of Single Board Computers (SBCs), involves several key considerations and procedures. Here are some general notes to keep in mind:
     6 - Signature Node: The process of signing a FIT (Flattened Image Tree) image, as described in our wiki on secure boot, is not overly complex. However, a challenge arises in integrating the signature into the device tree (dt) control for Venice SBCs. Unlike Newport SBCs, where the dtbs are augmented and injected back in, Venice dtbs are contained within the U-Boot ITB (Image Tree Blob) and are built using Binman. As a result, injecting the signature node may require an alternate approach.
     7 - U-Boot Versions: It's crucial to note that this guide was written for U-Boot version v2024.03-venice (or a compatible version), as different U-Boot branches may necessitate distinct configurations.
     8 - SoC specifics: The Gateworks Venice family of Single Board Computers supports IMX8MM, IMX8MN, and IMX8MP SoC's which vary quite a bit and create different binary builds and images for boot firmware. Most of the examples here are for the IMX8MM SoC; if you are using the IMX8MN or IMX8MP it is assumed that you will understand to edit those device-tree files and use those defconfigs for example: arch/arm/dts/imx8m{m,n,p}* configs/imx8m{m,n,p}_venice_defconfig
     9 - Support for Additional Security Measures: Unlike some other systems that support features like Trusted Platform Module (TPM), OP-TEE (Open Portable Trusted Execution Environment), or CAAM (Cryptographic Acceleration and Assurance Module) keys, Venice SBCs primarily rely on the signature node for secure boot operations.
     10 - Building boot firmware stand-alone vs modifying the Venice BSP directory: In the examples here we feel it adds clarity to build the 'secure' boot firmware components ouside of the Venice BSP yet we use the toolchain, environment, and some artifacts of the Venice BSP and thus refer to '$VENICE_BSP' as the directory where you have the Venice BSP built on your development host.
     11 - FIT Verification: If a signature node is absent in the controlling device tree blob (dtb), FIT verification will not occur. This behavior is by design, emphasizing the user's responsibility to ensure the presence of a valid signature node. It's advisable to consider patching U-Boot to prevent this if LEGACY mode is not enabled, thus mitigating potential security vulnerabilities.
     12
     13
     14=== Chain of Trust
     15The secure boot process on Venice SBCs relies on a robust chain of trust to ensure the integrity and authenticity of the boot process. Here's an overview of the chain of trust and the key steps involved:
     16 - Power On: The boot process initiates with the IMX BOOT_ROM, which utilizes One-Time Programmable (OTP) fuses to verify the signed Secondary Program Loader (SPL) image loaded into the SRAM/cache. This verification process necessitates the blowing of fuses into OTP and requires the device to be 'locked'.
     17 - U-Boot SPL: Once the early power management configuration and DRAM configuration are completed, U-Boot SPL loads U-Boot proper's FIT (u-boot.itb) image into DRAM and begins processing it. This FIT image includes device tree blobs (dtbs) for all supported boards, as well as Arm Trusted Firmware (ATF) and U-Boot itself. Verification of this FIT image occurs through HABv4 (High Assurance Boot version 4) functionality, enabled during the signing step.
     18 - U-Boot FIT Image Authentication: The authenticity of the FIT image used by U-Boot itself (u-boot.itb) containing U-Boot proper, the Arm Trusted Firmware (ATF) and the device tree blobs (DTB's) is validated through HABv4, extending the authentication mechanism used by the BOOT_ROM to U-Boot. This authentication process involves signing the flash.bin during the signing step. NXP has extended the use of this authentication mechanism, theoretically allowing for the utilization of CAAM (Cryptographic Acceleration and Assurance Module) functions on a command-line basis.
     19 - Generation of Binaries via Binman: The creation of various binaries used to generate the flash.bin file via Binman has evolved significantly over the years. The Binman tool generates these binaries based on device tree syntax defined in a 'binman' device tree node. This syntax can be found in the imx8mm-u-boot.dtsi file for those that want to know more.
     20 - Control Device Tree: U-Boot SPL utilizes a generic device tree (DT) named 'imx8mm-venice' (CONFIG_DEFAULT_DEVICE_TREE) for control. This DT includes essential components common to all Venice boards (imx8mm, imx8mn, imx8mp) needed for early PMIC and DRAM configuration.
     21 - FIT Image Verification: The FIT image is processed by U-Boot SPL, which passes each encountered dt name to a function determining if the dt should be used. This function selects the appropriate dt based on the EEPROM model of the base/som. The FIT image contains every dtb supported by the flash.bin.
     22 - Signature Node Integration: The 'control dtb' used by U-Boot proper must contain the signature node for external FIT image validation. A external FIT image can be used to contain a kernel, ramdisk, and kernel device-tree blobs (DTB's)
     23
     24
    325See also [http://trac.gateworks.com/wiki/secure_boot Generic Secure Boot Wiki Page] for information on securing the rest of your firmware.
    426
    5 = i.MX8M High Assurance Boot (HAB)
     27== i.MX8M High Assurance Boot (HAB)
    628The i.MX family of processors provides a High Assurance Boot (HAB) feature in the on-chip BOOT ROM responsible for loading the initial program image from the boot media. HAB enables the BOOT ROM to authenticate and/or decrypt the program image by using crypto operations.
    729
     
    169191  - Note the 'Authenticate image from DDR location' messages which shows that image authentication is able to be used
    170192 5. Program SRK Hash fuses from Step 1 into IMX OTP (using U-Boot and the keys from fuse bin)
     193   * **Do not use the above fuse values - use values generated above from your serial/passphrase**
     194   * **OTP fuses can only be programmed once - be careful to use the correct values**
     195   * **IF YOU WISH TO IMPLEMENT DISK ENCRYPTION AS WELL: FINISH FDE SETUP AND COME BACK TO THIS**
    171196{{{#!bash
    172197fuse prog -y 6 0 0xDCE644DB
     
    179204fuse prog -y 7 3 0xDB9B5895
    180205}}}
    181    * **Do not use the above fuse values - use values generated above from your serial/passphrase**
    182    * **OTP fuses can only be programmed once - be careful to use the correct values**
    183  6. Boot it again and verify no HAB events:
     206 6. Boot it again and verify no HAB events:
    184207{{{#!bash
    185208u-boot=> hab_status
     
    198221 - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/introduction_habv4.txt
    199222 - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/guides/mx8m_spl_secure_boot.txt
     223
    200224
    201225[=#tee]
     
    207231- e-commerce digital wallet code and sensitive data
    208232- DRM credentials
     233
    209234
    210235[=#optee]
     
    253278  - CONFIG_IMX_HAB=y
    254279  - CONFIG_CMD_DEKBLOB=y
    255   - CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000
     280  - CONFIG_SPL_LOAD_FIT_ADDRESS=0x48000000
    256281  - CONFIG_OPTEE=y
    257282  - CONFIG_OPTEE_LOAD_ADDRESS= the (link/load) address for TEE that should match BL32_BASE for ATF and CFG_TZDRAM_START for OPTEE
     
    334359make imx8mm_venice_defconfig # choose imx8mm/imx8mn/imx8mp depending on board SOC
    335360# enable IMX_HAB/OPTEE
    336 make menuconfig # search for (with /) and set CONFIG_IMX_HAB=y CONFIG_CMD_DEKBLOB=y CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 CONFIG_OPTEE=y and CONFIG_OPTEE_LOAD_ADDRESS= set to the value of CFG_TZDRAM_START
     361make menuconfig # search for (with /) and set CONFIG_IMX_HAB=y CONFIG_CMD_DEKBLOB=y CONFIG_SPL_LOAD_FIT_ADDRESS=0x48000000 CONFIG_OPTEE=y and CONFIG_OPTEE_LOAD_ADDRESS= set to the value of CFG_TZDRAM_START
    337362make -j8 flash.bin
    338 }}}
    339   - for clarity here are the differences in defconfig for imx8mm with 4GB configuration:
    340 {{{#!bash
    341 $ make savedefconfig && diff defconfig configs/imx8mm_venice_defconfig
    342 scripts/kconfig/conf  --savedefconfig=defconfig Kconfig
    343 21d20
    344 < CONFIG_IMX_HAB=y
    345 30d28
    346 < CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000
    347 139d136
    348 < CONFIG_OPTEE_LOAD_ADDRESS=0xfe000000
    349 161d157
    350 < # CONFIG_OPTEE_LIB is not set
    351363}}}
    352364 1. Sign it:
     
    370382}}}
    371383
     384
     385[=#fde]
     386== Full Disk Encryption (FDE)
     387While i.MX secure boot ensures a secure boot process and prevents unauthorized code from executing during the initial stages, maintaining a secure environment throughout the entire system lifecycle requires additional measures. One crucial aspect is protecting sensitive data stored on persistent storage devices by encrypting the file system. To achieve this, we will utilize Full Disk Encryption (FDE) using Linux Unifified Key Setup (LUKS). For this we need an initial ramdisk (aka 'initrd') containing an init script that can be used for provisioning an encrypted storage device, or unlocking the encrypted storage device for use. This approach ensures data-at-rest protection and establishes a secure Linux environment, complementing the security foundation established by i.MX secure boot.
     388
     389=== Components of FDE using LUKS
     390In our approach to achieving Full Disk Encryption (FDE), we employ Linux Unified Key Setup (LUKS) paired with a ramdisk using a custom init script to mount the encrypted filesystem. LUKS serves as a robust encryption tool, offering comprehensive solutions for securing data on Linux systems. By utilizing LUKS, we ensure the entire storage device is encrypted, thereby safeguarding sensitive information from unauthorized access.
     391
     392- Why use LUKS?
     393
     394 Robust Encryption Mechanisms: LUKS offers a comprehensive solution for Full Disk Encryption, providing robust encryption mechanisms that balance security, usability, and compatibility effectively.
     395
     396 Trusted Tool: As a prominent tool within Linux systems, LUKS is widely trusted and adopted, ensuring reliability and compatibility across various platforms and distributions.-
     397
     398- Why use a ramdisk?
     399 
     400 We need a 'shim' between the kernel and the encrypted root filesystem as the kernel does not have the custom support needed to mount an encrypted filesystem (such as the key).
     401
     402 Volatility: RAM disks are volatile, meaning their contents are lost when the system powers down. As a result, any sensitive data stored in them, such as encryption keys, won't persist across reboots. This limits the window of opportunity for attackers to extract the keys.
     403
     404 Isolation: By storing the encryption key in a RAM disk, you isolate it from the main storage device. This reduces the likelihood of unauthorized access or tampering since the key isn't stored alongside the encrypted data.
     405
     406 Speed: Accessing data from a RAM disk is faster compared to accessing data from traditional storage devices like hard drives or SSDs. This can potentially speed up the decryption process.
     407
     408This demonstration's main steps for implementing FDE will include:
     409 1) Using Buildroot for Ramdisk Creation
     410 
     411 2) Using the Ramdisk for Provisioning and unlocking the storage device via LUKS
     412
     413=== Kernel and DTB
     414We are going to need a kernel and DTB for your board. For this demonstration we will use the kernel and DTB's from the pre-built Gateworks Linux tarball:
     415{{{#!bash
     416mkdir blobs
     417# grab the prebuilt gateworks kernel
     418wget https://dev.gateworks.com/venice/kernel/linux-venice-6.6.8.tar.xz
     419tar -C blobs/ --strip-components=2 -xvf linux-venice-6.6.8.tar.xz ./boot
     420# gzip the kernel
     421gzip -fk blobs/Image # creates blob/Image.gz
     422}}}
     423
     424you can choose your own kernel/dtb/rootfs as long as they provide support for dm-crypt
     425
     426
     427=== Ramdisk Creation
     428We offer two pathways for obtaining a RAM disk tailored for our Full Disk Encryption (FDE) approach:
     429 1. Conveniently download a prebuilt ramdisk from our dev.gateworks website and inject our custom init script.
     430 2. Construct it through Buildroot, offering greater customization capabilities.
     431
     432Both of these methods require having our custom init script and a key to be used for locking and unlocking the filesystem.
     433
     434For our demonstration, we will just create some random data to be used as our filesystem encryption key via:
     435{{{#!bash
     436dd if=/dev/urandom of=fs.key bs=1 count=4096
     437}}}
     438
     439The init script we will use can be created like this:
     440{{{#!bash
     441cat <<\EOF> init
     442#!/bin/sh
     443
     444# This is to be used by a buildroot minimal rootfs on the target board
     445# as an init script to:
     446#   - provision flash filesystem
     447#   - mount flash filesystem
     448#
     449# the flash storage (ie create disk partition, filesystems, with/without encryption, and populate them)
     450
     451# Some basic vars
     452DEV=/dev/mmcblk2 # storage device to be used for the encrypted file system
     453P1_OFFSETMB=64
     454PART=p1
     455NAME=rootfs # name for the encrypted file system (only used here)
     456KEY=/fs.key
     457NIC=eth0
     458INIT=/sbin/init # init of final OS
     459
     460# Mount things needed by this script
     461mount -n -t devtmpfs devtmpfs /dev
     462mount -n -t proc proc /proc
     463mount -n -t sysfs sysfs /sys
     464mount -n -t tmpfs tmpfs /run
     465
     466error() {
     467        printf "\n\nErro: $@"
     468        while [ 1 ]; do sleep 1; done
     469}
     470
     471user_data() {
     472        echo "Applying user changes..."
     473
     474        # in any board specific changes to rootfs
     475        case "$(cat /proc/device-tree/board)" in
     476                GW74*)
     477                        mkdir -p /mnt/etc/udev/rules.d
     478                        echo 'SUBSYSTEM=="net", ACTION=="add", DEVPATH=="/devices/platform/soc@0/30800000.bus/30bf0000.ethernet/net/eth*", NAME="en0"' > /mnt/etc/udev/rules.d/70-persistent-net.rules
     479                        echo 'SUBSYSTEM=="net", ACTION=="add", DEVPATH=="/devices/platform/soc@0/30800000.bus/30be0000.ethernet/net/eth*", NAME="en1"' >> /mnt/etc/udev/rules.d/70-persistent-net.rules
     480                        sed -i 's/eth0/en0/' /mnt/etc/network/interfaces
     481                        ;;
     482        esac
     483}
     484
     485# example1: rootfs via a list of tarballs
     486#  - gateworks ubuntu tarbarll and linux kernel tarball
     487ROOTFS_TARS="\
     488        https://dev.gateworks.com/ubuntu/jammy/jammy-venice.tar.xz \
     489        https://dev.gateworks.com/venice/kernel/linux-venice-6.6.8.tar.xz \
     490"
     491rootfs1() {
     492        # Create an ext4 file system on the opened LUKS device
     493        echo "Creating filesystem..."
     494        mkfs.ext4 -q -F -L rootfs /dev/mapper/$NAME || error "mkfs.ext4 failed"
     495
     496        # Mount the LUKS device to /mnt
     497        mount /dev/mapper/$NAME /mnt || error "mount failed"
     498
     499        for url in $ROOTFS_TARS; do
     500                echo "Downloading $url..."
     501                wget -q --show-progress --no-check-certificate -O data $url || error "wget failed"
     502                echo "Extracting..."
     503                pv data | tar -C /mnt -mxJ --keep-directory-symlink || error "tar extract failed"
     504        done
     505
     506        user_data
     507
     508        # Unmount the LUKS device
     509        echo "Unmounting LUKS device..."
     510        umount /dev/mapper/$NAME || error "umount failed"
     511}
     512
     513# example2: rootfs via compressed disk image
     514rootfs2() {
     515        FILESYSTEM=https://dev.gateworks.com/venice/images/jammy-venice.img.gz
     516        # a compressed disk image contains a partition table as well as
     517        # partitions. Here we assume an MBR partition with the OS on P1.
     518        # we will extract the MBR, determine the start/end of P1 and copy
     519        # it to the LUKS device.
     520        echo "Downloading $FILESYSTEM..."
     521        wget -q --show-progress --no-check-certificate -O data $FILESYSTEM || exit 1
     522        # Extract the MBR
     523        zcat data | dd of=mbr count=1
     524        start=$(sfdisk -l mbr | tail -1 | tr -s ' ' | cut -d ' ' -f3)
     525        end=$(sfdisk -l mbr | tail -1 | tr -s ' ' | cut -d ' ' -f4)
     526        # copy the filesystem in P1 to the LUKS device
     527        echo "Copying..."
     528        pv data | zcat | dd of=/dev/mapper/$NAME skip=$start count=$((end-start))
     529
     530        # apply user data
     531        mount /dev/mapper/$NAME /mnt || error "mount failed"
     532        user_data
     533        umount /dev/mapper/$NAME || error "umount failed"
     534}
     535
     536# example3: rootfs via existing filesystem
     537rootfs3() {
     538        FILESYSTEM=https://dev.gateworks.com/buildroot/venice/minimal/rootfs.ext2.xz
     539        echo "Downloading $FILESYSTEM..."
     540        wget -q --show-progress --no-check-certificate -O data $FILESYSTEM || exit 1
     541        # copy the filesystem in P1 to the LUKS device
     542        echo "Copying..."
     543        pv data | xzcat | dd of=/dev/mapper/$NAME bs=16M
     544
     545        # apply user data
     546        mount /dev/mapper/$NAME /mnt || error "mount failed"
     547        user_data
     548        umount /dev/mapper/$NAME || error "umount failed"
     549}
     550
     551provision() {
     552        rootfs=${1:-rootfs1}
     553
     554        echo "Provision $DEV via $rootfs..."
     555
     556        # board specific customizations
     557        case "$(cat /proc/device-tree/board)" in
     558                GW74*) NIC=eth1;;
     559        esac
     560
     561        # Mount the /tmp directory as a tmpfs with 75% of the available memory
     562        mount -t tmpfs /tmp -o size=75%
     563
     564        # Change the current working directory to /tmp
     565        cd /tmp
     566
     567        # bring up network
     568        echo "Bringing up network $NIC..."
     569        udhcpc -i $NIC
     570
     571        echo "Creating partition table..."
     572        # partition disk:
     573        #   - you could use MBR or GPT based on your needs
     574        #   - ensure that you do not start your partition data until after your boot firmware
     575        # Create MBR with a single partition taking up the entire device
     576        printf "$((P1_OFFSETMB*1024*1024/512)),,L,*" | sfdisk -u S $DEV || exit 1
     577        DEV=${DEV}${PART}
     578
     579        # Format the partition as a LUKS encrypted file system using the encryption key
     580        echo "Formating LUKS device..."
     581        echo "YES" | cryptsetup luksFormat $DEV $KEY - || exit 1
     582
     583        # Open the LUKS encrypted partition and map it to the specified name ($NAME)
     584        echo "Opening LUKS device..."
     585        cryptsetup luksOpen $DEV $NAME --key-file=$KEY || exit 1
     586
     587        # call rootfs option
     588        $rootfs
     589
     590        # Close the LUKS device and remove the mapping
     591        echo "Closing LUKS device..."
     592        cryptsetup luksClose $NAME || exit 1
     593
     594        echo "Provisioning complete"
     595
     596        # Wait forever
     597        #while [ 1 ]; do sleep 1; done
     598
     599        # power cycle
     600        echo 2 > /sys/bus/i2c/devices/0-0020/powerdown
     601}
     602
     603for x in $(cat /proc/cmdline); do
     604        case "$x" in
     605                bypass)
     606echo "Booting straight to ramdisk..."
     607umount /proc
     608umount /sys
     609umount /run
     610if (exec 0</dev/console) 2>/dev/null; then
     611    exec 0</dev/console
     612    exec 1>/dev/console
     613    exec 2>/dev/console
     614fi
     615exec /sbin/init "$@"
     616                        ;;
     617                provision=*) provision ${x//provision=};;
     618        esac
     619done
     620
     621echo "Using $DEV..."
     622
     623# Wait for device to exist
     624echo "Waiting for $DEV..."
     625while [ ! -b "$DEV" ]; do
     626        sleep 1
     627        echo -n .
     628done
     629
     630# Open the LUKS encrypted partition and map it to the specified name ($NAME)
     631DEV=${DEV}${PART}
     632echo "Opening $DEV..."
     633cryptsetup luksOpen $DEV $NAME --key-file=$KEY || exit 1
     634
     635# Mount the LUKS device to /mnt
     636echo "Mounting $DEV..."
     637mount /dev/mapper/$NAME /mnt || exit 1
     638
     639# Switch to the new root and execute init
     640echo "Switching to new root and running $INIT $@..."
     641cd /mnt
     642[ -c dev/console ] || mknod -m 600 dev/console c 5 1
     643exec switch_root . "$INIT" "$@"
     644
     645# This will only be run if the above line failed
     646echo "Failed to switch_root"
     647EOF
     648}}}
     649
     650This init script has the following features:
     651 - demonstrates provisioning an encrypted filesystem via a kernel cmdline argument allowing three different provisioning options (described below)
     652 - demonstrates mounting an encrypted filesystem
     653
     654Note: If you wish to encrypt the default storage device using the init script, it is configured for the eMMC on Venice boards (/dev/mmcblk2) by default. You are free to modify this according to your requirements. Feel free to specify partitions, change the default storage device, or add any other customization you desire to the script.
     655
     656
     657
     658==== Option1: Downloading a prebuilt ramdisk
     659To download the prebuilt root file system and incorporate your script manually, follow these steps:
     660{{{#!bash
     661# download the ramdisk
     662wget https://dev.gateworks.com/buildroot/venice/minimal/rootfs.cpio.xz
     663# extract files from an existing gzipped cpio:
     664mkdir initrd
     665(cd initrd; xz -cd ../rootfs.cpio.xz | fakeroot -s ../initrd.fakeroot cpio -idmv)
     666# copy key and init to it
     667cp fs.key init initrd/
     668# re-create initrd
     669(cd initrd; find | fakeroot -i ../initrd.fakeroot cpio -o -H newc | xz --check=crc32 -c > ../rootfs.cpio.xz)
     670}}}
     671
     672This will produce the desired rootfs.cpio.xz artifact which we will copy to our blobs directory for safe keeping:
     673{{{#!bash
     674cp rootfs.cpio.xz blobs/
     675}}}
     676
     677==== Option2: Creating Buildroot Ramdisk
     678Typically you should choose this option over downloading the prebuilt Gateworks ramdisk only if you want to add additional functionality to the init script that your ramdisk uses, and that functionality is dependent on configuration options our ramdisk does not have enabled.
     679 
     680To create the root file system manually, follow these steps:
     681 - Clone the Buildroot repository:
     682{{{#!bash
     683git clone https://github.com/buildroot/buildroot.git
     684cd buildroot
     685}}}
     686 - Get a copy of Gateworks buildroot defconfigs
     687{{{#!bash
     688wget -O configs/venice_example_defconfig https://dev.gateworks.com/buildroot/venice/minimal/venice_example_defconfig
     689wget https://dev.gateworks.com/buildroot/venice/minimal/venice_minimal_kernel_6.1_defconfig
     690}}}
     691 - Make an overlay directory and get the custom init script as well as the filesystem key there
     692{{{#!bash
     693mkdir overlay
     694cp init overlay/init
     695cp fs.key overlay/
     696}}}
     697
     698When specifying the overlay directory path in the Buildroot configuration (BR2_ROOTFS_OVERLAY), you simply provide the path to the directory you've chosen, regardless of its name. As long as the path is correctly specified, Buildroot will incorporate the contents of the overlay directory into the final root filesystem image during the build process.
     699 - Buildroot's make command generates multiple artifacts, however, our focus is solely on the rootfs.cpio.xz file it produces
     700{{{#!bash
     701# make the custom filesystem
     702make venice_example_defconfig
     703make
     704}}}
     705
     706This will produce output/images/rootfs.cpio.xz which we will copy to our blobs directory for safe keeping:
     707{{{#!bash
     708cp output/images/rootfs.cpio.xz blobs/
     709}}}
     710
     711=== Using the Ramdisk and custom init script
     712
     713==== Configuring Bootargs for Different init script use cases
     714Regardless of what option you choose for Secure System Setup in the next section, you will need to be familiar with how the example init script is intended to be used once you get your board to u-boot: this is a preliminary section describing how to use the script.
     715
     716In order to utilize the custom init script properly, you can configure the bootargs variable at the U-Boot prompt to specify different provisioning options for setting up the Full Disk Encryption (FDE) and unlocking your FDE. Below are the available options:
     717 - rootfs1 (provision option):
     718Use this option to provision the emmc by creating an ext4 filesystem on the LUKS device and downloading and extracting root file system components (kernel and rootfs) from specified URLs.
     719{{{#!bash
     720setenv bootargs provision=rootfs1
     721}}}
     722 - rootfs2 (provision option):
     723This option provisions the emmc from a compressed disk image containing a partition with the root file system. The image is extracted, and the root file system is copied to the LUKS device.
     724{{{#!bash
     725setenv bootargs provision=rootfs2
     726}}}
     727 - rootfs3 (provision option):
     728This option provisions the emmc from an existing compressed file system that can be downloaded and directly copied to the LUKS device.
     729{{{#!bash
     730setenv bootargs provision=rootfs3
     731}}}
     732 - bypass (debugging):
     733Set bootargs to bypass to bypass normal provisioning/mounting and drop you into a bash shell with the buildroot root filesystem. This is useful for debugging and prototyping custom provisioning commands.
     734{{{#!bash
     735setenv bootargs bypass
     736}}}
     737 - Unlocking the FDE (Default):
     738Leaving bootargs blank or not specifying any provisioning option will configure the system for regular use and unlocking the Full Disk Encryption. This is the default case where you have already provisioned your storage device and the FDE is switched to be used as the root file system.
     739
     740Note: Provisioning options are intended to be used as a one-time process during system setup.
     741
     742=== Confirming working operation of individual components
     743We strongly suggest to do a confirmation test that your kernel image, device tree, and ramdisk all work as intended before moving on.
     744
     745At U-Boot, you have the option to choose the behavior:
     746 1. Using the ramdisk for encrypting a storage device (typically done only once)
     747{{{#!bash
     748setenv bootargs provision=rootfs1
     749}}}
     750 2. Using the ramdisk for unlocking a storage device (default)
     751{{{#!bash
     752setenv bootargs
     753}}}
     754
     755Once you have your Kernel, ramdisk and device tree blob successfully copied over to a tftp server, we suggest confirming their working operation before continuing. You should be able to boot your ramdisk as follows
     756
     757on host:
     758 - copy the kernel Image, your dtb, and your rootfs.cpio.xz ramdisk to your tftp server venice directory
     759{{{#!bash
     760cp blobs/imx8mm-venice-gw73xx-0x.dtb blobs/Image blobs/rootfs.cpio.xz /tftpboot/venice/
     761}}}
     762 - target; boot them via tftp
     763{{{#!bash
     764u-boot=> dhcp && setenv serverip <serverip>
     765u-boot=> setenv bootargs bypass; tftpboot $kernel_addr_r venice/Image && tftpboot $fdt_addr_r venice/imx8mm-venice-gw73xx-0x.dtb && tftpboot $ramdisk_addr_r venice/rootfs.cpio.xz && booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdt_addr_r
     766}}}
     767 - it is important here that you tftp the ramdisk last as we use the filesize variable which is set by tftp to give booti the size of the ramdisk which is requires
     768
     769==== Choosing the Boot Method for Secure System Setup
     770Now that we have our ramdisk, kernel, and device tree blob we have a few different options to continue on our path to having a secure system.
     771 - Option 1: Creating a signed FIT image and transferring it over TFTP, and flashing the fit image to memory.
     772 - Option 2: Incorporating the ramdisk, kernel, and device tree into the firmware image itself with HABv4 secure boot via u-boots fit image, and flashing the fit image to memory.
     773
     774In comparing Option 1 and Option 2, both provide robust firmware security through code signing and verification. However, Option 2 goes a step further by integrating the verification into the hardware root-of-trust using platform security features like HABv4.
     775
     776Option 1 provides more versatility in terms of being able to construct different FIT image payloads signed by the same key that U-Boot can universally verify and boot from. This modularity can be advantageous, especially for deployment scenarios that need frequent kernel/filesystem updates without modifying the secure bootloader components.
     777
     778
     779[=#external-fit]
     780==== Option 1 - using an External FIT Image
     781This approach involves creating an external signed FIT (Flat Image Tree) image containing the ramdisk, kernel, and device tree which U-Boot can then verify (via the signature) before booting.
     782
     783The benefit of this is:
     784 - this is how its usually done
     785 - this allows you to leave your boot firmware fairly small so that it still fits in the eMMC boot0 hardware partition
     786
     787The downside is:
     788 - it requires another key for the external FIT image vs using the SRK fuse settings and HABv4
     789
     790==== FIT image background information
     791U-Boot supports signed FIT images which allow you to cryptographically validate various binaries and configurations within the FIT image.
     792
     793The FIT format is already widely used in U-Boot. It is a flattened device tree (FDT) in a particular format, with images contained within. FIT images include hashes to verify images, so it is relatively straightforward to add signatures as well.
     794
     795The public key can be stored in U-Boot's CONFIG_OF_CONTROL device tree in a standard place. Then when a FIT is loaded it can be verified using that public key. Multiple keys and multiple signatures are supported.
     796
     797FIT supports hashing of images so that these hashes can be checked on loading. This protects against corruption of the image. However it does not prevent the substitution of one image for another.
     798
     799The signature feature allows the hash to be signed with a private key such that it can be verified using a public key later. Provided that the private key is kept secret and the public key is stored in a non-volatile place, any image can be verified in this way.
     800
     801While signing images is useful, it does not provide complete protection against several types of attack. For example, it is possible to create a FIT with the same signed images, but with the configuration changed such that a different one is selected (mix and match attack). It is also possible to substitute a signed image from an older FIT version into a newer FIT (roll-back attack).
     802
     803To solve this problem, we support signed configurations. In this case it is the configurations that are signed, not the image. Each image has its own hash, and we include the hash in the configuration signature.
     804
     805The procedure for signing is as follows:
     806 - hash an image in the FIT
     807 - sign the hash with a private key to produce a signature
     808 - store the resulting signature in the FIT
     809
     810The procedure for verification is:
     811 - read the FIT
     812 - obtain the public key
     813 - extract the signature from the FIT
     814 - hash the image from the FIT
     815 - verify (with the public key) that the extracted signature matches the hash
     816
     817The verification is done in U-Boot on the device using a /signature node in U-Boot’s control FDT. If U-Boot is building the DTB’s the signature node must be added to your specific board DTB by inserting it into the dts source files (which can easily be done by putting it in the root node of arch/arm/dts/imx8mm-u-boot.dtsi, arch/arm/dts/imx8mn-u-boot.dtsi or arch/arm/dts/imx8mp-u-boot.dtsi depending on your board's SoC).
     818
     819For more info on verified boot and FIT images see:
     820 - https://elixir.bootlin.com/u-boot/v2023.04/source/doc/uImage.FIT/verified-boot.txt
     821 - https://elixir.bootlin.com/u-boot/v2023.04/source/doc/uImage.FIT/signature.txt
     822 - https://elixir.bootlin.com/u-boot/v2023.04/source/doc/uImage.FIT/sign-configs.its
     823
     824==== Generation of Signed FIT Image
     825We will need a key to used for image signing and verification.
     826
     827To create a new public/private 2048 bit key pair:
     828{{{#!bash
     829# on host machine
     830openssl genpkey -algorithm RSA -out fit.key -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537
     831# to create a certificate for this containing the public key:
     832openssl req -batch -new -x509 -key fit.key -out fit.crt
     833# you can see the public key:
     834openssl rsa -in fit.key -pubout
     835}}}
     836
     837Create the ITS template where each image is defined to have a hash and each configuration is signed:
     838{{{#!bash
     839cat << EOF > fit.its
     840/dts-v1/;
     841
     842/ {
     843        description = "Image for Linux Kernel";
     844        #address-cells = <1>;
     845
     846        images {
     847                kernel {
     848                        description = "Linux Kernel";
     849                        data = /incbin/("blobs/Image.gz");
     850                        type = "kernel";
     851                        arch = "arm64";
     852                        os = "linux";
     853                        compression = "gzip";
     854                        load =<0x48200000> ;
     855                        entry = <0x48200000>;
     856                        hash-1 {
     857                                algo = "sha256";
     858                        };
     859                };
     860                ramdisk {
     861                        description = "ramdisk";
     862                        data = /incbin/("blobs/rootfs.cpio.xz");
     863                        type = "ramdisk";
     864                        arch = "arm64";
     865                        os = "linux";
     866                        compression = "none";
     867                        load = <0x50300000>;
     868                        hash-1 {
     869                                algo = "sha256";
     870                        };
     871                };
     872                fdt {
     873                        description = "fdt";
     874                        data = /incbin/("blobs/imx8mm-venice-gw72xx-0x.dtb");
     875                        type = "flat_dt";
     876                        arch = "arm64";
     877                        compression = "none";
     878                        load = <0x50200000>;
     879                        hash-1 {
     880                                algo = "sha256";
     881                        };
     882                };
     883        };
     884        configurations {
     885                default = "config-1";
     886                config-1 {
     887                        description = "Linux configuration";
     888                        kernel = "kernel";
     889                        ramdisk = "ramdisk";
     890                        fdt = "fdt";
     891                        signature-1 {
     892                                algo = "sha1,rsa2048";
     893                                key-name-hint = "fit";
     894                                sign-images = "kernel", "fdt", "ramdisk";
     895                        };
     896                };
     897        };
     898};
     899EOF
     900}}}
     901
     902At this point we have a key (fit.key, fit.crt) and a template to create the FIT image from (fit.its). Note that we have elected above to use a compressed kernel (why not?) so we need to make sure we gzip the kernel to match the 'gzip' property value specified in its 'compression' node above.
     903
     904
     905Now we can now use the U-Boot mkimage tool (from the u-boot-tools package or from the u-boot build dir) to build the Image. We are going to use the '-K <dtb>' option to add a signature for the key into an existing DTB but U-Boot builds its control DTB itself so we will create a dummy DTB to put the key in then decompile it:
     906{{{#!bash
     907# create a dummy its file
     908cat <<EOF> blobs/signature.dts
     909 /dts-v1/;
     910
     911/ {
     912};
     913EOF
     914# compile the dts to a dtb with device-tree compiler
     915dtc -I dts -O dtb blobs/signature.dts > blobs/signature.dtb
     916# create the fit image and add the signature to the dtb
     917mkimage -f fit.its -k . -K blobs/signature.dtb -r fit.itb
     918# decompile the dummy dtb
     919dtc -I dtb -O dts blobs/signature.dtb > blobs/signature.dts
     920# now we have a signed fit.itb you can put on your board or tftp
     921cp fit.itb /tftpboot/venice
     922}}}
     923 - Note: the -k parameter is the directory that the fit image key resides that is specified by key-name-hint. So for example with key-name-hint "fit" as above; fit.key will be in the directory specified by the -k parameter.
     924
     925Before continuing with securing uboot, a quick check to make sure this fit image boots is a good idea. This step is strictly to help with debugging if there is an issue with your image blobs or an issue with u-boot's build configuration.
     926{{{#!bash
     927setenv bootargs bypass; tftpboot 0x40200000 venice/fit.itb && bootm 0x40200000
     928}}}
     929 - Note: we are loading into 0x40200000 instead of $loadaddr because loadaddr is the same as kerneladdr and our fit image is loaded there and they conflict.
     930
     931==== Getting FIT key into U-Boot proper and verifying signed images
     932In order for U-boot to validate a signed FIT image it must have access to the key used to sign that image. This is why we created a signature.dtb in the previous step by using ‘mkimage -K’ to ‘inject’ a node into our signature.dtb.
     933
     934Now we need to modify U-Boot to allow it to verify FIT images against that key:
     935 - copy the signature into the imx8mm-u-boot.dtsi, imx8mn-u-boot.dtsi or imx8mp-u-boot.dtsi file depending on your SoC.
     936 - enable CONFIG_FIT_SIGNATURE and RSA_VERIFY
     937 - disable CONFIG_LEGACY_IMAGE_FORMAT so that unsigned (or signed images that failed verification!) are not allowed to boot
     938
     939Procedure:
     940 1. Checkout U-Boot (or use your existing U-Boot that you have been working with):
     941{{{#!bash
     942git clone https://github.com/Gateworks/uboot-venice.git
     943cd uboot-venice
     944# copy necessary artifacts from bsp
     945cp $VENICE_BSP/u-boot/lpddr4*.bin . # DDR firmware
     946cp $VENICE_BSP/atf/build/imx8mm/release/bl31.bin . # ATF
     947}}}
     948 2. Make sure your environment is configured for cross-compiling:
     949{{{#!bash
     950(cd $VENICE_BSP; . ./setup-environment)
     951make imx8mm_venice_defconfig
     952}}}
     953 3. Add the signature node from ../blobs/signature.dts to 'arch/arm/dts/imx8mm-venice-u-boot.dtsi' within the root node. You can position it either directly above or below the 'wdt-reboot' node. The 'imx8mm-venice-u-boot.dtsi' is included with all imx8mm-venice boards, which ensures that the signature node appears in all device tree blobs (dtbs) listed in CONFIG_OF_LIST. Placing it in 'imx8mm-venice-u-boot.dtsi' simplifies the process as there's no need to consider which specific board the user has.
     954 4. Use 'make menuconfig' to make the following changes:
     955 - CONFIG_FIT_SIGNATURE=y - enable signature verification of FIT uImages using a hash signed and verified using RSA.
     956 - RSA=y
     957 - CONFIG_LEGACY_IMAGE_FORMAT is undefined - otherwise this allows booting unsigned images or any image if you lack a signature node
     958{{{#!bash
     959make menuconfig # enable FIT_SIGNATURE/RSA, disable LEGACY_IMAGE_FORMAT
     960make savedefconfig && cp defconfig configs/imx8mm_venice_defconfig
     961}}}
     962 5. Its a good idea for troubleshooting to enable DEBUG in common/spl/spl_fit.c by adding '#define DEBUG' to the top of that file
     963
     964When complete your differences should look like this:
     965{{{#!bash
     966$ git diff
     967diff --git a/arch/arm/dts/imx8mm-u-boot.dtsi b/arch/arm/dts/imx8mm-u-boot.dtsi
     968index 04a7093a66..6d1f7e7907 100644
     969--- a/arch/arm/dts/imx8mm-u-boot.dtsi
     970+++ b/arch/arm/dts/imx8mm-u-boot.dtsi
     971@@ -4,6 +4,20 @@
     972  */
     973 
     974 / {
     975+       signature {
     976+
     977+               key-fit {
     978+                       required = "conf";
     979+                       algo = "sha1,rsa2048";
     980+                       rsa,r-squared = <0x5123aa0b 0x92792834 0x3f805a1b 0xa85c3894 0x817b72af 0xe5f737ef 0x9c97b0b0 0xe25dc8e5 0xfa
     9816a455f 0x18112fca 0xc5cfdd34 0xcc9e62ee 0x53401b1 0xceb41498 0xd3697f64 0x95a198c1 0x568b83b 0xb6cfedde 0x719783df 0x85724f45 0xc0c7b
     98283a 0x6d692e33 0x46a8e77e 0xf72fba00 0x5f294060 0xa3b8ab2f 0xcdc10f08 0xdec24553 0xccd09d3e 0xfed61495 0xb6f034bc 0x5d7a3e1e 0x5a8ee4
     9834c 0xc58fd62 0xee38127d 0x7be833c0 0xa43e308e 0xaf26959f 0x286f45ed 0xe0d95788 0x9818f079 0xaf4bdcf3 0x88ee0caf 0x7fdcd2b7 0x41f1217b
     984 0xaf1ce320 0x1582997f 0x17232232 0x4794518d 0x79746211 0x2c0adb7f 0xc0288fc2 0xc4851840 0x85bf410a 0xc05fbb3f 0x8f38e96f 0x6d6b5b43
     9850xb195adc7 0xcb786525 0x1bb9f2b 0xff26d159 0xb2eee232 0xbea15340 0xff9c4086>;
     986+                       rsa,modulus = <0xb1430c9e 0x101ef408 0x66e818f5 0xde892ee1 0x7ec0ed73 0xd5a899b7 0x2f5d9012 0x5cefdc25 0x9e3f
     9878697 0x5c06cc4e 0x368cb44a 0x97d1d99a 0x25c6bf39 0x2bbc1028 0x306cc237 0x74f9adfd 0xf9811fe8 0x3efa493c 0xcc5e97bb 0x3d55977d 0x1f918
     98832a 0xb6f300d0 0x4b00182a 0x9ee2e97f 0x89d2f8cd 0x95b3a15d 0xc0c28d86 0x14dfce86 0x42b16994 0xbbca5180 0x273cea3a 0x97a6ee90 0x26db41
     98938 0xf30ac22a 0xc34ce324 0xc02e160f 0xfbc14603 0xa7f95016 0xe73d7f3b 0x2e6f15b7 0x78e54e1 0xaa98b25a 0xdbc2ea6d 0x598b03a8 0x9a5f14e4
     990 0xbface2c0 0xb1726a07 0xb2848286 0x3c071c5b 0xedc37018 0xb4f00631 0x30b23649 0xd03c0477 0x6573dbe7 0xe2617b39 0x1fa5407d 0xeac68d0f
     9910xc08f4fb2 0x28d36d15 0x1ce825bd 0xf792c214 0xb48f369 0x146c427e 0xa74f0177>;
     992+                       rsa,exponent = <0x00 0x10001>;
     993+                       rsa,n0-inverse = <0xa2fbd7b9>;
     994+                       rsa,num-bits = <0x800>;
     995+                       key-name-hint = "fit";
     996+               };
     997+       };
     998+
     999        binman: binman {
     1000                multiple-images;
     1001        };
     1002diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
     1003index 730639f756..f6955eabbd 100644
     1004--- a/common/spl/spl_fit.c
     1005+++ b/common/spl/spl_fit.c
     1006@@ -3,6 +3,7 @@
     1007  * Copyright (C) 2016 Google, Inc
     1008  * Written by Simon Glass <sjg@chromium.org>
     1009  */
     1010+#define DEBUG
     1011 
     1012 #include <common.h>
     1013 #include <errno.h>
     1014diff --git a/configs/imx8mm_venice_defconfig b/configs/imx8mm_venice_defconfig
     1015index d254e2a012..d9511538c0 100644
     1016--- a/configs/imx8mm_venice_defconfig
     1017+++ b/configs/imx8mm_venice_defconfig
     1018@@ -18,6 +18,8 @@ CONFIG_SPL_DRIVERS_MISC=y
     1019 CONFIG_SPL_STACK=0x920000
     1020 CONFIG_SPL=y
     1021 CONFIG_ENV_OFFSET_REDUND=0x3f8000
     1022+CONFIG_IMX_HAB=y
     1023+# CONFIG_CMD_DEKBLOB is not set
     1024 CONFIG_SYS_LOAD_ADDR=0x48200000
     1025 CONFIG_SYS_MEMTEST_START=0x40000000
     1026 CONFIG_SYS_MEMTEST_END=0x80000000
     1027@@ -25,7 +27,9 @@ CONFIG_LTO=y
     1028 CONFIG_SYS_MONITOR_LEN=524288
     1029 CONFIG_FIT=y
     1030 CONFIG_FIT_EXTERNAL_OFFSET=0x3000
     1031+CONFIG_FIT_SIGNATURE=y
     1032 CONFIG_SPL_LOAD_FIT=y
     1033+CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000
     1034 CONFIG_OF_BOARD_SETUP=y
     1035 CONFIG_OF_SYSTEM_SETUP=y
     1036 CONFIG_DISTRO_DEFAULTS=y
     1037}}}
     1038
     1039Now build it and update your board (by creating a JTAG image or using the 'update_firmware' script on the resulting flash.bin)
     1040
     1041When uboot boots verify that the signature node is in the new controlling fdt:
     1042{{{#!bash
     1043u=boot-> fdt addr $fdtcontroladdr && fdt print /signature
     1044Working FDT set to fdef4680
     1045signature {
     1046        key-fit {
     1047                required = "conf";
     1048                algo = "sha1,rsa2048";
     1049                rsa,r-squared = <0x10cb82b5 0xc0d123dd 0x369b77e4 0xfc5fe7b4 0x34e1b663 0xac7ccbd1 0x2089fcb8 0xd473ccdb 0x79e857
     1050af 0x67c88056 0xb9cfc0bd 0x2f854d0c 0xc9908774 0x4690496a 0x24140c3f 0xb15f60cf 0x661576bb 0x788700b7 0x31e31f54 0xc929c98b 0xe75
     1051472b5 0x1c83c812 0xbdc68033 0xb96ebbaf 0xc2c1160c 0xfd737350 0xbfbdafba 0xe4a8c600 0x5c7dcf9b 0x02090083 0x428b42f4 0xafe211c1 0x
     10521924ce2f 0xdcb959f3 0xce5edeb4 0x38864bfe 0xc1100fef 0x5e697704 0x89560798 0x15f4dd46 0xc72bf173 0xbf83f373 0x47960ba8 0x6e49beda
     1053 0x04fc6263 0x91fd2ac4 0x28975bbd 0x5e7cc6a9 0x857cb2db 0x3af856a7 0xc14bbde4 0x64710fbd 0xbc69c38a 0x03b65791 0x8aeac041 0x1e50b
     10540c5 0x9fb0c4f4 0x1fbe97e3 0xdaa7dcf7 0x80817ad7 0x5195514c 0x2a19ad58 0x66eb2870 0x05f78731>;
     1055                rsa,modulus = <0xc95ff26c 0xad27a889 0x6bb89478 0x0e38cda9 0x29085777 0x1e896717 0x1e95d2ed 0x4cd49f17 0x63f49947
     1056 0x5d98b3d5 0xd5833340 0x18fbe768 0x45ae09ed 0x2f86b951 0xc41c067f 0xd85f8d1b 0x76e427a3 0xe25a9f1d 0x754a5242 0x4f26037a 0x5ecd2
     10573e2 0x4c9c629c 0x4f4db6b9 0x1c21bf95 0xa276f126 0x469f4989 0x35a7c3d8 0x7c89c0c4 0x26675817 0x352c1f61 0xe3a3e594 0xce04653a 0xa9
     1058d1ece4 0xba3ed4ac 0x9b35d81f 0xb1a3371d 0xc473ca80 0x8ce9bc54 0x81109b91 0x320f0244 0x7144346c 0xebd97bed 0x57757d18 0x0a0dc99e 0
     1059x1fbc8e4d 0x20f15eec 0x2e80cc41 0x0748270d 0x054e1f67 0xe4f21cdf 0x1a6e56fd 0x857d51f6 0x219c6494 0x7c8e8a36 0xf3ecda1c 0xad866a3
     10600 0xd1142431 0x3afab3a9 0x723891cc 0x9774b093 0x05051de2 0x6a7e4167 0xd3e2c5e5 0x27fcc949>;
     1061                rsa,exponent = <0x00000000 0x00010001>;
     1062                rsa,n0-inverse = <0xb3698707>;
     1063                rsa,num-bits = <0x00000800>;
     1064                key-name-hint = "fit";
     1065        };
     1066};
     1067}}}
     1068
     1069Verify you can boot your signed image:
     1070{{{#!bash
     1071setenv bootargs bypass; tftpboot 0x40200000 venice/fit.itb && bootm 0x40200000
     1072}}}
     1073 - Note: we are loading into 0x40200000 instead of $loadaddr because loadaddr is the same as kerneladdr and our fit image is loaded there and they conflict.
     1074 - Note U-boot will not verify signature if it can not find the signature node but will continue to load the image if CONFIG_LEGACY_IMAGE_FORMAT is enabled; in order to secure U-Boot you want to disable that.
     1075
     1076At this point, you will need to decide where to store the signed FIT image on flash. You can either hard-code it to an offset outside the partitioned space (e.g., between the partition table and the first partition), or place it in an unencrypted partition that U-Boot can load from.
     1077
     1078For the emmc on venice boards, the partition table starts at 0x0 and we like to leave 1MiB of space for ample room for the partition table. The custom init script we are using to provision the eMMC that creates the partition table and the partitions is configured to start P1's data at 64MiB offset so we can essentially use the eMMC user hardware partition from 1MB to 64MB allowing for 63MB (64-1) of loading space for the fit image (if this is not large enough for your FIT image, just modify the start of the P1 data offset in the provisioning step).
     1079
     1080U-Boot's mmc commands use 512byte blocks in hex notation so we figure out what 63MB is for a blk count:
     1081{{{#!bash
     1082printf "0x%x\n" $((63*1024*1024/512)) # hex 512byte blocks
     10830x1f800
     1084}}}
     1085
     1086Now to tftp transfer and flash the fit image to memory
     1087{{{#!bash
     1088u=boot-> tftpboot $loadaddr venice/fit.itb && setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 && mmc dev 2 0 && mmc write $loadaddr 0x800 $blkcnt
     1089}}}
     1090 - note we use setexpr to round up to the next block size to make sure we get all the data
     1091
     1092You can now boot with this:
     1093{{{#!bash
     1094u=boot-> mmc dev 2 0 && mmc read 0x40200000 0x800 0x1f800 && bootm 0x40200000
     1095}}}
     1096
     1097Now that your secure ramdisk is written to memory: you are at the point of wanting to lock down your u-boot environment
     1098
     1099
     1100[=#embedded-fit]
     1101==== Option 2 - Embed the kernel/dtb/ramdisk in the U-Boot ITB
     1102For IMX you can utilize the fact that the SPL is using HABv4 and the SRK keys to verify the entire U-Boot FIT image (u-boot.itb) which already contains the ARM Trusted Firmware, U-Boot propper, and the U-Boot control DTB's.
     1103 
     1104This approach involves integrating the ramdisk, kernel, and device tree into U-Boot's FIT image (u-boot.itb) and taking advantage of the High Assurance Boot (HABv4) feature to authenticate the entire firmware image before execution. The u-boot.itb is the u-boot FIT image which is built by binman at the end of the U-Boot make target and described in the binman/itb/fit node. This option provides robust security by leveraging hardware-based security features and extending the trusted boot chain.
     1105
     1106The benefits of this method:
     1107 - no additional key needed
     1108
     1109The downsides of this method:
     1110 - bloats your boot firmware to where you need to put it on the eMMC user hardware partition which is an extra complication
     1111 - requires you to modify where you store U-Boot env on the eMMC (or disable it)
     1112
     1113Procedure:
     1114 1. Checkout U-Boot (or use your existing U-Boot that you have been working with):
     1115{{{#!bash
     1116git clone https://github.com/Gateworks/uboot-venice.git
     1117cd uboot-venice
     1118# copy necessary artifacts from bsp
     1119cp $VENICE_BSP/u-boot/lpddr4*.bin . # DDR firmware
     1120cp $VENICE_BSP/atf/build/imx8mm/release/bl31.bin . # ATF
     1121}}}
     1122 1. Make sure your environment is configured for cross-compiling:
     1123{{{#!bash
     1124(cd $VENICE_BSP; . ./setup-environment)
     1125make imx8mm_venice_defconfig
     1126}}}
     1127 1. Use 'make menuconfig' to make the following changes:
     1128  - CONFIG_LEGACY_IMAGE_FORMAT=y to allow packaging the ramdisk with a header.
     1129  - CONFIG_ENV_IS_IN_MMC=n to prevent U-Boot environment storage conflicts.
     1130{{{#!bash
     1131make menuconfig # enable LEGACY_IMAGE_FORMAT, enable CONFIG_ENV_IS_NOWHERE disable CONFIG_ENV_IS_IN_MMC
     1132make savedefconfig && cp defconfig configs/imx8mm_venice_defconfig
     1133}}}
     1134 1. Its a good idea for troubleshooting to enable DEBUG in common/spl/spl_fit.c by adding '#define DEBUG' to the top of that file
     1135 - Create a uramdisk image via mkimage
     1136U-Boot needs a special format for its ramdisk image to ensure proper booting and functionality of the system, and the mkimage command is used to create such images according to U-Boot's requirements.
     1137{{{#!bash
     1138mkimage -A arm64 -T ramdisk -C none -d blobs/rootfs.cpio.xz blobs/uramdisk
     1139}}}
     1140 1. Modify the U-Boot device tree source per SoC (ie arch/arm/dts/imx8mm-u-boot.dtsi) and:
     1141  - add the following in the itb/fit/images node:
     1142{{{#!bash
     1143                                os_kernel {
     1144                                        arch = "arm64";
     1145                                        compression = "none";
     1146                                        description = "Kernel image";
     1147                                        load = <0x48200000>;
     1148                                        type = "kernel";
     1149
     1150                                        uboot-blob {
     1151                                                filename = "blobs/Image";
     1152                                                type = "blob-ext";
     1153                                        };
     1154                                };
     1155
     1156                                os_fdt {
     1157                                        arch = "arm64";
     1158                                        compression = "none";
     1159                                        description = "flat_dt";
     1160                                        load = <0x50200000>;
     1161                                        type = "firmware";
     1162
     1163                                        uboot-blob {
     1164                                                filename = "blobs/imx8mm-venice-gw72xx-0x.dtb";
     1165                                                type = "blob-ext";
     1166                                        };
     1167                                };
     1168
     1169                                os_ramdisk {
     1170                                        arch = "arm64";
     1171                                        compression = "none";
     1172                                        description = "ramdisk";
     1173                                        load = <0x50300000>;
     1174                                        type = "firmware";
     1175
     1176                                        uboot-blob {
     1177                                                filename = "blobs/uramdisk";
     1178                                                type = "blob-ext";
     1179                                        };
     1180                                };
     1181}}}
     1182  - update the loadables to include the 'os_kernel', 'os_fdt', 'os_ramdisk' you added
     1183 1. U-Boot needs a special format for its ramdisk image to ensure proper booting and functionality of the system, and the mkimage command is used to create such images according to U-Boot's requirements.
     1184{{{#!bash
     1185mkimage -A arm64 -T ramdisk -C none -d ../blobs/rootfs.cpio.xz ../blobs/uramdisk
     1186}}}
     1187
     1188When complete your differences should look like this:
     1189{{{#!bash
     1190$ git diff
     1191diff --git a/arch/arm/dts/imx8mm-u-boot.dtsi b/arch/arm/dts/imx8mm-u-boot.dtsi
     1192index 04a7093a6694..0563bfa23ed2 100644
     1193--- a/arch/arm/dts/imx8mm-u-boot.dtsi
     1194+++ b/arch/arm/dts/imx8mm-u-boot.dtsi
     1195@@ -155,6 +155,45 @@
     1196                                        type = "firmware";
     1197                                };
     1198 
     1199+                               os_kernel {
     1200+                                       arch = "arm64";
     1201+                                       compression = "none";
     1202+                                       description = "Kernel image";
     1203+                                       load = <0x48200000>;
     1204+                                       type = "kernel";
     1205+
     1206+                                       uboot-blob {
     1207+                                               filename = "../blobs/Image";
     1208+                                               type = "blob-ext";
     1209+                                       };
     1210+                               };
     1211+
     1212+                               os_fdt {
     1213+                                       arch = "arm64";
     1214+                                       compression = "none";
     1215+                                       description = "flat_dt";
     1216+                                       load = <0x50200000>;
     1217+                                       type = "firmware";
     1218+
     1219+                                       uboot-blob {
     1220+                                               filename = "../blobs/imx8mm-venice-gw73xx-0x.dtb";
     1221+                                               type = "blob-ext";
     1222+                                       };
     1223+                               };
     1224+
     1225+                               os_ramdisk {
     1226+                                       arch = "arm64";
     1227+                                       compression = "none";
     1228+                                       description = "ramdisk";
     1229+                                       load = <0x50300000>;
     1230+                                       type = "firmware";
     1231+
     1232+                                       uboot-blob {
     1233+                                               filename = "../blobs/uramdisk";
     1234+                                               type = "blob-ext";
     1235+                                       };
     1236+                               };
     1237+
     1238                                @fdt-SEQ {
     1239                                        compression = "none";
     1240                                        description = "NAME";
     1241@@ -176,9 +215,9 @@
     1242                                        firmware = "uboot";
     1243 #ifndef CONFIG_ARMV8_PSCI
     1244 #ifdef CONFIG_OPTEE_LOAD_ADDRESS
     1245-                                       loadables = "atf", "tee";
     1246+                                       loadables = "atf", "tee", "os_kernel", "os_fdt", "os_ramdisk";
     1247 #else
     1248-                                       loadables = "atf";
     1249+                                       loadables = "atf", "os_kernel", "os_fdt", "os_ramdisk";
     1250 #endif
     1251 #endif
     1252                                };
     1253diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
     1254index 730639f7562c..deb77356c940 100644
     1255--- a/common/spl/spl_fit.c
     1256+++ b/common/spl/spl_fit.c
     1257@@ -3,7 +3,7 @@
     1258  * Copyright (C) 2016 Google, Inc
     1259  * Written by Simon Glass <sjg@chromium.org>
     1260  */
     1261-
     1262+#define DEBUG
     1263 #include <common.h>
     1264 #include <errno.h>
     1265 #include <fpga.h>
     1266diff --git a/configs/imx8mm_venice_defconfig b/configs/imx8mm_venice_defconfig
     1267index dc391e47dac1..241ecc99992f 100644
     1268--- a/configs/imx8mm_venice_defconfig
     1269+++ b/configs/imx8mm_venice_defconfig
     1270@@ -6,7 +6,6 @@ CONFIG_SPL_GPIO=y
     1271 CONFIG_SPL_LIBCOMMON_SUPPORT=y
     1272 CONFIG_SPL_LIBGENERIC_SUPPORT=y
     1273 CONFIG_ENV_SIZE=0x8000
     1274-CONFIG_ENV_OFFSET=0x3f0000
     1275 CONFIG_DM_GPIO=y
     1276 CONFIG_DEFAULT_DEVICE_TREE="imx8mm-venice"
     1277 CONFIG_SPL_TEXT_BASE=0x7E1000
     1278@@ -17,7 +16,6 @@ CONFIG_SPL_SERIAL=y
     1279 CONFIG_SPL_DRIVERS_MISC=y
     1280 CONFIG_SPL_STACK=0x920000
     1281 CONFIG_SPL=y
     1282-CONFIG_ENV_OFFSET_REDUND=0x3f8000
     1283 CONFIG_SYS_LOAD_ADDR=0x48200000
     1284 CONFIG_SYS_MEMTEST_START=0x40000000
     1285 CONFIG_SYS_MEMTEST_END=0x80000000
     1286@@ -77,9 +75,7 @@ CONFIG_CMD_EXT4_WRITE=y
     1287 CONFIG_OF_CONTROL=y
     1288 CONFIG_SPL_OF_CONTROL=y
     1289 CONFIG_OF_LIST="imx8mm-venice imx8mm-venice-gw71xx-0x imx8mm-venice-gw72xx-0x imx8mm-venice-gw73xx-0x imx8mm-venice-gw7901 imx8mm-ve
     1290nice-gw7902 imx8mm-venice-gw7903 imx8mm-venice-gw7904 imx8mm-venice-gw75xx-0x"
     1291-CONFIG_ENV_IS_IN_MMC=y
     1292 CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
     1293-CONFIG_SYS_MMC_ENV_DEV=2
     1294 CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
     1295 CONFIG_USE_ETHPRIME=y
     1296 CONFIG_ETHPRIME="eth0"
     1297}}}
     1298
     1299Now build it and update your board (by creating a JTAG image or using the 'update_firmware' script on the resulting flash.bin)
     1300
     1301Important: Avoid directly copying your device tree blob (DTB) to U-Boot's directory. Doing so may unexpectedly corrupt your flash.bin file. Instead, adopt the approach we use here by placing the DTB in a subdirectory, like 'blobs'.
     1302
     1303To flash this onto the eMMC we need to change the eMMC to boot to the user hardware partition and place it there as it no longer will fit in the 4MB boot0 hardware partition:
     1304 - for imx8mm:
     1305{{{#!bash
     1306u-boot=> tftpboot $loadaddr venice/flash.bin && setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 && mmc dev 2 0 && mmc write $loadaddr 0x42 $blkcnt && mmc partconf 2 1 7 0
     1307}}}
     1308 - for imx8mn/imx8mp (different bootblk)
     1309{{{#!bash
     1310u-boot=> tftpboot $loadaddr venice/flash.bin && setexpr blkcnt $filesize + 0x1ff && setexpr blkcnt $blkcnt / 0x200 && mmc dev 2 0 && mmc write $loadaddr 0x40 $blkcnt && mmc partconf 2 1 7 0
     1311}}}
     1312
     1313Now when booting the SPL loads the kernel, ramdisk, fdt to the addresses specified in the binman nodes above and you can boot it with booti:
     1314{{{#!bash
     1315u-boot=> booti 0x48200000 0x50300000 0x50200000
     1316}}}
     1317
     1318Now that your secure ramdisk is written to memory: you are at the point of wanting to lock down your u-boot environment
     1319
     1320
     1321[=#env]
     1322== Locking down U-Boot environment and shell
     1323There isn't much point in securing U-Boot if you leave the U-Boot shell enabled. Additionally if you leave the U-Boot env stored in flash that introduces another attack vector so you might as well disable env storage completely and hard-code the bootcmd:
     1324 - enable CONFIG_ENV_IS_NOWHERE=y to disable env load/save
     1325 - disable CONFIG_ENV_IS_IN_MMC
     1326 - change CONFIG_BOOTCOMMAND to what you need to directly boot (depending on the options above)
     1327
     1328
     1329
    3721330== HABv4 encrypted boot architecture
    3731331The IMX HABv4 also provides an extra optional security operation by using cryptography (AES-CCM) to obscure the boot image so it can not be seen or used by unauthorized users.