wiki:ventana/bootloader/falcon-mode

Version 4 (modified by Tim Harvey, 6 years ago) ( diff )

replace udisks with mount/umount

Speeding Up Boot Times

In addition to Falcon mode, please also review the boot speed Wiki page

Falcon Mode

Note that Falcon mode is currently only supported in Gateworks U-Boot 2015.04 branch which is not considered stable and thus not yet available as a pre-built binary. You can build it and install it using the instructions on the ventana/bootloader

Falcon mode refers to having the Secondary Program Loader (SPL) boot the OS directly and bypass the U-Boot bootloader entirely. The purpose of this is to speed up booting and it can easily shave 3 to 5 seconds off the boot process depending on your boot medium, kernel/initrd/fdt size, and filesystem.

The Gateworks Ventana SPL supports loading images from:

  • NAND Flash
  • micro-SD (baseboard IMX6 micro-SD controller, not a USB based micro-SD reader/writer such as what is on the GW16103)

Note that it is technically possible to support booting from other media using the SPL but that requires additional work: The IMX6 BOOT ROM can additionally boot from SATA which is supported via mSATA on man Ventana boards with an IMX6Q SoC however that support is not currently in the U-Boot SPL codebase. Additionally booting from devices that are not supported by the IMX6 BOOT ROM such as Ethernet, CDROM, or USB can technically be achieved by first booting to something like the SPL from a supported boot device and adding support to the SPL. Freescale has a method discussed in the IMX reference manuals called a 'plugin' image for this kind of thing.

In order to enable falcon mode to boot Linux you first need to configure and store the data that gets passed to the Linux kernel. This configuration is typically done via the U-Boot bootm command and involves customizing the device-tree for the board characteristics such as memory size, kernel params (bootargs), device-tree fixups, and MTD partitions. For falcon mode this configuration can be done by loading the kernel and device-tree then using the spl export fdt command instead of the bootm command.

Typically one would custom build the SPL for falcon mode and have something like a GPIO pushbutton that can allow you to select booting to U-Boot for configuration instead of booting falcon mode. Because not all Gateworks Ventana boards have pushbuttons, we chose to have the SPL read the U-Boot env variables and use them for configuration.

It is important to realize that if you choose to use Falcon mode you should do this once you are fairly far along in your development cycle as once you set the env to boot directly to the OS the only way to get back into U-Boot is to alter your environment (and if you've bricked your firmware for example, you would need to do this via JTAG for NAND flash and by mounting micro-SD on another system if using micro-SD). Also, you will need to be clear what kernel bootargs you should use, which you can get from a running system via cat /proc/cmdline. For this reason get your firmware running the way you want, then invoke Falcon mode if you want to use Falcon make it boot quicker.

To understand Falcon mode its useful to understand what a bootloader like U-Boot usually does for you. The steps towards booting to Linux via U-Boot can be summarized as:

  1. load the kernel from non-volatile storage into RAM (this can be from raw flash or block dev sectors, or via high level filesystems)
  2. load the kernel device-tree binary (dtb) into RAM (again, this can be from raw flash or block dev sectors or via high level filesystems)
  3. prepare the args data to pass to the kernel. For modern ARM linux, this is a device-tree binary with its address passed in via a register. The 'prepare' step here is often taken for granted. This step involvs modifying the loaded device-tree with properties that may be board or environment dependent such as the memory size, and peripheral set. The U-Boot board support function ft_board_setup does most of this customization although some of the more common things like setting up the kernel cmdline are done before this is called in common bootm code.
  4. optionally an initial ramdisk is loaded as well
  5. registers are setup with the RAM location of the flattened device-tree binary, the kernel, and the optional initial ramdisk then the kernel is jumped to.

Most of the above is done by boot scripts which implement complex behavior based on env variables.

When Falcon mode is used all these things must be done by the SPL, which is why it saves time by not loading the code for and invoking U-Boot. When Falcon mode is used you do steps 1-4 above then invoke the spl export fdt command which prepares the flattened device-tree but does not jump to the kernel. Then you store this pre-prepared args structure in non-volatile memory where the SPL expects to find it and set the env var that tells the SPL to load it and the kernel and jump to the kernel.

Note that there are certain properties within the device-tree which are 'board specific' meaning they vary from board to board. For example the board serial-number, and Ethernet controller mac addresses come from the on-board EEPROM. For this reason you do not want to 'provision' multiple boards using the args data created from a different board.

For more information about Falcon mode:

booting Linux on NAND flash

To use falcon mode on NAND flash for Ventana we need to store the kernel and args in raw flash. This is because typically we store the kernel in the mtd rootfs partition using ubifs, yet the SPL doesn't support ubifs and adding ubifs support to the SPL would likely exceed the maximum code-space of the SPL as well as completely defeat the purpose of booting fast.

We choose the following flash layout (dictated by compile-time options in include/configs/gw_ventana.h):

Start Size Usage
0MB 14MB SPL
14MB 2MB U-Boot
16MB 256KB env
17MB 1MB args
18MB 10MB kernel
28MB rootfs rootfs

To configure falcon mode on a board that boots to NAND and is flashed with latest bootloader supporting Falcon mode do the following:

  1. setup a TFTP server with the kernel (uImage), device-tree (ie imx6q-gw52xx), and rootfs ubi (ie openwrt-imx6-ventana-rootfs_normal.ubi) (these are all located in the build artifacts of your BSP).
  2. make sure the board has the latest SPL and U-Boot supporitng Falcon mode on it (ie JTAG u-boot_spl.bin)
  3. power on the board with serial console and ethernet, break into U-boot, and re-configure flash with:
    # change mtd partitions to the above mapping
    setenv mtdparts 'mtdparts=nand:14m(spl),2m(uboot),1m(env),1m(args),10m(kernel),-(rootfs)'
    # re-flash rootfs at 28MB
    tftp ${loadaddr} ventana/openwrt-imx6-ventana-rootfs_${flash_layout}.ubi && \
      nand erase.part rootfs && nand write ${loadaddr} rootfs ${filesize}
    # load the device-tree
    tftp ${fdt_addr} ventana/${fdt_file2}
    # load the kernel
    tftp ${loadaddr} ventana/uImage
    # flash kernel at 18MB
    nand erase.part kernel && nand write ${loadaddr} kernel ${filesize}
    # set kernel args for the console and rootfs (used by spl export)
    setenv bootargs 'console=ttymxc1,115200 root=ubi0:rootfs ubi.mtd=5 rootfstype=ubifs quiet'
    # use the spl export command to create args based on env, board, EEPROM, and dtb
    spl export fdt ${loadaddr} - ${fdt_addr}
    # flash args at 17MB
    nand erase.part args && nand write 18000000 args 100000
    # set boot_os env var
    setenv boot_os 1
    saveenv
    
  • Note the bootargs above which you may want or need to customize for your purpose. If you are not sure what bootargs to use, you should boot without falcon mode and get them from /proc/cmdline

Once you have switched to Falcon mode you can update the kernel in a number of ways (now that you can't do it via U-Boot):

  • via Linux booted on the IMX6 based target board:
    cd /tmp
    wget http://192.168.1.146/uImage
    flash_erase /dev/mtd4 0 0
    nandwrite -p /dev/mtd4 uImage
    
  • On a Linux host with the micro-SD as /dev/sdc:
    sudo dd if=uImage of=/dev/sdc bs=1m seek=2 && sync
    
  • via JTAG:
    mkimage_jtag -s uImage@18M > image.bin
    jtag_usbv4 -p image.bin
    

If you need to get back into the bootloader you need to clear the U-Boot env area:

  • via Linux booted on the IMX6 based target board:
    flash_erase /dev/mtd2 0 0
    
  • via JTAG
    dd if=/dev/zero of=env bs=1k count=1 && ./mkimage_jtag -s env@16M > env.bin
    jtag_usbv4 -p env.bin
    

Note that the above flash-map is not set in stone but it is set at compile time from defines in include/configs/gw_ventana.h

booting Linux on a NAND-less board booting from a micro-SD device

To use falcon mode to boot directly to Linux on a micro-SD device for Ventana we need to store the kernel and args on the microSD. Note that we typically we store the kernel in an ext partition yet we have chosen to not enable filesystem support in the SPL (doing so will increase the size of the SPL, and the time necessary to boot, thus defeating the purpose of Falcon mode al-together).

To store the kernel, and args on a block storage device, we will use the following partitioning scheme:

Start KB / blocks Size KB / blocks Usage
0K 1KB (2) Partition Table etc
1KB (2)) 68KB (0x88) SPL
69KB (0x8a) 640KB (0x500) U-Boot
709KB (0x58a) 256KB (0x512) env (redundant)
965KB 59KB unused
1MB (0x800) 1MB (0x800) args
2MB (0x1000) 8M (0x4000) kernel
10M (0x5000) partitions rootfs
  • Note that U-Boot defines MMC offsets and size in sectors which are 512 bytes and that U-Boot cmdline parameters are in hex

To configure a micro-SD to boot directly to Linux from the SPL for a NAND-less board you will need the following firmware artifacts:

Procedure:

  1. Insert your micro-SD card/reader into your Linux host and determine what device it is (here we assume /dev/sdc)
  2. prepare the micro-SD per the specifications above:
    DEV=/dev/sdc # adjust per your system!
    MNT=/mnt # adjust per your system!
    # zero out 1MB of device
    sudo dd if=/dev/zero of=$DEV count=1 bs=1M oflag=sync status=none && sync
    # copy SPL to 1KB offset
    sudo dd if=SPL of=$DEV bs=1K seek=1 oflag=sync status=none && sync
    # copy U-Boot to 69KB offset
    sudo dd if=u-boot.img of=$DEV bs=1K seek=69 oflag=sync status=none && sync
    # create a partition table with a single rootfs partition starting at 10MB
    printf "10,,L\n" | sudo sfdisk --in-order --no-reread -L -uM $DEV && sync
    # format partition
    sudo mkfs.ext4 -O ^64bit -L root ${DEV}1
    # mount the partition
    sudo mount ${DEV}1 $MNT
    # extract filesystem
    sudo tar xvf rootfs.tar.gz -C $MNT/
    # unmount
    sudo umount ${DEV}1
    
  3. Boot the board with the prepared micro-SD and break out in U-Boot::
    # load device-tree from rootfs
    ext2load mmc 0:1 ${fdt_addr} boot/${fdt_file2}
    # load kernel from rootfs
    ext2load mmc 0:1 ${loadaddr} boot/uImage
    # write kernel at 2MB offset
    mmc write ${loadaddr} 0x1000 0x4000
    # setup kernel args bootargs
    setenv bootargs 'console=ttymxc1,115200 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw quiet'
    # prepare args and write the 1MB data (0x800 sectors) to 1MB offset (0x800 sectors)
    spl export fdt ${loadaddr} - ${fdt_addr} && mmc write 18000000 0x800 0x800
    # set boot_os to enable falcon mode
    setenv boot_os 1 && saveenv
    
  • Note the bootargs above which you may want or need to customize for your purpose. If you are not sure what bootargs to use, you should boot without falcon mode and get them from /proc/cmdline

Once you have switched to Falcon mode you can update the kernel in a number of ways (now that you can't do it via U-Boot):

  • via Linux booted on the IMX6 based target board:
    cd /tmp
    wget http://192.168.1.146/uImage
    dd if=uImage of=/dev/mmcblk0 bs=1m seek=2 && sync
    
  • On a Linux host with the micro-SD as /dev/sdc:
    sudo dd if=uImage of=/dev/sdc bs=1m seek=2 && sync
    
  • via JTAG:
    mkimage_jtag -s uImage@18M > image.bin
    jtag_usbv4 -p image.bin
    

If you need to get back into the bootloader you need to clear the U-Boot env area:

  • via Linux booted on the IMX6 based target board:
    dd if=/dev/zero of=/dev/mmcblk0 bs=1k seek=709 count=256 && sync
    
  • On a Linux host with the micro-SD as /dev/sdc:
    sudo dd if=/dev/zero of=/dev/sdc bs=1k seek=709 count=256 && sync
    

Keep in mind, that because there are certain properties within the device-tree which are 'board specific' (such as board serial-number, and Ethernet controller mac addresses which come from the on-board EEPROM) once a micro-SD is imaged with the procedure above, you should not move it to another board without re-creating the args data.

Note: See TracWiki for help on using the wiki.