.. SPDX-License-Identifier: GPL-2.0+: Standard Boot Overview ====================== Introduction ------------ Standard boot provides a built-in way for U-Boot to automatically boot an Operating System without custom scripting and other customisation. It introduces the following concepts: - bootdev - a device which can hold or access a distro (e.g. MMC, Ethernet) - bootmeth - a method to scan a bootdev to find bootflows (e.g. distro boot) - bootflow - a description of how to boot (provided by the distro) For Linux, the distro (Linux distribution, e.g. Debian, Fedora) is responsible for creating a bootflow for each kernel combination that it wants to offer. These bootflows are stored on media so they can be discovered by U-Boot. This feature is typically called `distro boot` (see :doc:`../distro`) because it is a way for distributions to boot on any hardware. Traditionally U-Boot has relied on scripts to implement this feature. See distro_bootcmd_ for details. This is done because U-Boot has no native support for scanning devices. While the scripts work remarkably well, they can be hard to understand and extend, and the feature does not include tests. They are also making it difficult to move away from ad-hoc CONFIGs, since they are implemented using the environment and a lot of #defines. Standard boot is a generalisation of distro boot. It provides a more built-in way to boot with U-Boot. The feature is extensible to different Operating Systems (such as Chromium OS) and devices (beyond just block and network devices). It supports EFI boot and EFI bootmgr too. Finally, standard boot supports the operation of :doc:`../vbe`. Bootflow -------- A bootflow is a file that describes how to boot a distro. Conceptually there can be different formats for that file but at present U-Boot only supports the BootLoaderSpec_ format which looks something like this:: menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. menu hidden label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) kernel /vmlinuz-5.3.7-301.fc31.armv7hl append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB fdtdir /dtb-5.3.7-301.fc31.armv7hl/ initrd /initramfs-5.3.7-301.fc31.armv7hl.img As you can see it specifies a kernel, a ramdisk (initrd) and a directory from which to load Device Tree files. The details are described in distro_bootcmd_. The bootflow is provided by the distro. It is not part of U-Boot. U-Boot's job is simply to interpret the file and carry out the instructions. This allows distros to boot on essentially any device supported by U-Boot. Typically the first available bootflow is selected and booted. If that fails, then the next one is tried. Bootdev ------- Where does U-Boot find the media that holds the operating systems? That is the job of bootdev. A bootdev is simply a layer on top of a media device (such as MMC, NVMe). The bootdev accesses the device, including partitions and filesystems that might contain things related to an operating system. For example, an MMC bootdev provides access to the individual partitions on the MMC device. It scans through these to find filesystems with the boot flag set, then provides a list of these for consideration. Some bootdevs are not visible until a bus is enumerated, e.g. flash sticks attached via USB. To deal with this, each bootdev has an associated 'hunter' which can hunt for bootdevs of a particular uclass type. For example, the SCSI bootdev scans the SCSI bus looking for devices, creating a bootdev for each Logical Unit Number (LUN) that it finds. Bootmeth -------- Once the list of filesystems is provided, how does U-Boot find the bootflow files in these filesystems? That is the job of bootmeth. Each boot method has its own way of doing this. For example, the distro bootmeth simply looks through the provided filesystem for a file called `extlinux/extlinux.conf`. This files constitutes a bootflow. If the distro bootmeth is used on multiple partitions it may produce multiple bootflows. Note: it is possible to have a bootmeth that uses a partition or a whole device directly, but it is more common to use a filesystem. For example, the Android bootmeth uses a whole device. Note that some bootmeths are 'global', meaning that they select the bootdev themselves. Examples include VBE and EFI boot manager. In this case, they provide a `read_bootflow()` method which checks whatever bootdevs it likes, then returns the bootflow, if found. Some of these bootmeths may be very slow, if they scan a lot of devices. Boot process ------------ U-Boot tries to use the 'lazy init' approach wherever possible and distro boot is no exception. The algorithm is:: while (get next bootdev) while (get next bootmeth) while (get next bootflow) try to boot it So U-Boot works its way through the bootdevs, trying each bootmeth in turn to obtain bootflows, until it either boots or exhausts the available options. Instead of 500 lines of #defines and a 4KB boot script, all that is needed is the following command:: bootflow scan -lb which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b). When global bootmeths are available, these are typically checked before the above bootdev scanning. Controlling ordering -------------------- By default, faster bootdevs (or those which are assumed to be faster) are used first, since they are more likely to be able to boot the device quickly. Several options are available to control the ordering of boot scanning: boot_targets ~~~~~~~~~~~~ This environment variable can be used to control the list of bootdevs searched and their ordering, for example:: setenv boot_targets "mmc0 mmc1 usb pxe" Entries may be removed or re-ordered in this list to affect the boot order. If the variable is empty, the default ordering is used, based on the priority of bootdevs and their sequence numbers. bootmeths ~~~~~~~~~ By default bootmeths are checked in name order. Use `bootmeth list` to see the ordering. Note that the `extlinux` and `script` bootmeth is first, to preserve the behaviour used by the old distro scripts. This environment variable can be used to control the list of bootmeths used and their ordering for example:: setenv bootmeths "extlinux efi" Entries may be removed or re-ordered in this list to affect the order the bootmeths are tried on each bootdev. If the variable is empty, the default ordering is used, based on the bootmeth sequence numbers, which can be controlled by aliases. The :ref:`usage/cmd/bootmeth:bootmeth command` (`bootmeth order`) operates in the same way as setting this variable. Bootdev uclass -------------- The bootdev uclass provides a simple API call to obtain a bootflow from a device:: int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, struct bootflow *bflow); This takes an iterator which indicates the bootdev, partition and bootmeth to use. It returns a bootflow. This is the core of the bootdev implementation. The bootdev drivers that implement this differ depending on the media they are reading from, but each is responsible for returning a valid bootflow if available. A helper called `bootdev_find_in_blk()` makes it fairly easy to implement this function for each media device uclass, in a few lines of code. For many types of bootdevs, the `get_bootflow` member can be NULL, indicating that the default handler is used. This is called `default_get_bootflow()` and it only works with block devices. Bootdev drivers --------------- A bootdev driver is typically fairly simple. Here is one for MMC:: static int mmc_bootdev_bind(struct udevice *dev) { struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); ucp->prio = BOOTDEVP_2_INTERNAL_FAST; return 0; } struct bootdev_ops mmc_bootdev_ops = { }; static const struct udevice_id mmc_bootdev_ids[] = { { .compatible = "u-boot,bootdev-mmc" }, { } }; U_BOOT_DRIVER(mmc_bootdev) = { .name = "mmc_bootdev", .id = UCLASS_BOOTDEV, .ops = &mmc_bootdev_ops, .bind = mmc_bootdev_bind, .of_match = mmc_bootdev_ids, }; You may notice that the `get_bootflow` memory is not provided, so is NULL. This means that `default_get_bootflow()` is used. This simply obtains the block device and calls a bootdev helper function to do the rest. The implementation of `bootdev_find_in_blk()` checks the partition table, and attempts to read a file from a filesystem on the partition number given by the `@iter->part` parameter. If there are any bootable partitions in the table, then only bootable partitions are considered. Each bootdev has a priority, which indicates the order in which it is used, if `boot_targets` is not used. Faster bootdevs are used first, since they are more likely to be able to boot the device quickly. Environment Variables --------------------- Various environment variables are used by standard boot. These allow the board to control where things are placed when booting the OS. You should ensure that your boards sets values for these. fdtfile Name of the flattened device tree (FDT) file to load, e.g. "rockchip/rk3399-rockpro64.dtb" fdt_addr_r Address at which to load the FDT, e.g. 0x01f00000 fdtoverlay_addr_r (needed if overlays are used) Address at which to load the overlay for the FDT, e.g. 0x02000000 kernel_addr_r Address at which to load the kernel, e.g. 0x02080000 kernel_comp_addr_r Address to which to decompress the kernel, e.g. 0x08000000 kernel_comp_size Size of available space for decompressed kernel, e.g. 0x2000000 pxefile_addr_r Address at which to load the PXE file, e.g. 0x00600000 ramdisk_addr_r Address at which to load the ramdisk, e.g. 0x06000000 scriptaddr Address at which to load the U-Boot script, e.g. 0x00500000 script_offset_f SPI flash offset from which to load the U-Boot script, e.g. 0xffe000 script_size_f Size of the script to load, e.g. 0x2000 vendor_boot_comp_addr_r Address to which to load the vendor_boot Android image, e.g. 0xe0000000 Some variables are set by script bootmeth: devtype Device type being used for boot, e.g. mmc devnum Device number being used for boot, e.g. 1 distro_bootpart Partition being used for boot, e.g. 2 prefix Directory containing the script mmc_bootdev Device number being used for boot (e.g. 1). This is only used by MMC on sunxi boards. Device hierarchy ---------------- A bootdev device is a child of the media device. In this example, you can see that the bootdev is a sibling of the block device and both are children of media device:: mmc 0 [ + ] bcm2835-sdhost | |-- mmc@7e202000 blk 0 [ + ] mmc_blk | | |-- mmc@7e202000.blk bootdev 0 [ ] mmc_bootdev | | `-- mmc@7e202000.bootdev mmc 1 [ + ] sdhci-bcm2835 | |-- sdhci@7e300000 blk 1 [ ] mmc_blk | | |-- sdhci@7e300000.blk bootdev 1 [ ] mmc_bootdev | | `-- sdhci@7e300000.bootdev The bootdev device is typically created automatically in the media uclass' `post_bind()` method by calling `bootdev_setup_for_dev()` or `bootdev_setup_for_sibling_blk()`. The code typically something like this:: /* dev is the Ethernet device */ ret = bootdev_setup_for_dev(dev, "eth_bootdev"); if (ret) return log_msg_ret("bootdev", ret); or:: /* blk is the block device (child of MMC device) ret = bootdev_setup_for_sibling_blk(blk, "mmc_bootdev"); if (ret) return log_msg_ret("bootdev", ret); Here, `eth_bootdev` is the name of the Ethernet bootdev driver and `dev` is the Ethernet device. This function is safe to call even if standard boot is not enabled, since it does nothing in that case. It can be added to all uclasses which implement suitable media. The bootstd device ------------------ Standard boot requires a single instance of the bootstd device to make things work. This includes global information about the state of standard boot. See `struct bootstd_priv` for this structure, accessed with `bootstd_get_priv()`. Within the Device Tree, if you add bootmeth devices, they should be children of the bootstd device. See `arch/sandbox/dts/test.dts` for an example of this. .. _`Automatic Devices`: Automatic devices ----------------- It is possible to define all the required devices in the Device Tree manually, but it is not necessary. The bootstd uclass includes a `dm_scan_other()` function which creates the bootstd device if not found. If no bootmeth devices are found at all, it creates one for each available bootmeth driver. If your Device Tree has any bootmeth device it must have all of them that you want to use, since no bootmeth devices will be created automatically in that case. Using devicetree ---------------- If a bootdev is complicated or needs configuration information, it can be added to the Device Tree as a child of the media device. For example, imagine a bootdev which reads a bootflow from SPI flash. The Device Tree fragment might look like this:: spi@0 { flash@0 { reg = <0>; compatible = "spansion,m25p16", "jedec,spi-nor"; spi-max-frequency = <40000000>; bootdev { compatible = "u-boot,sf-bootdev"; offset = <0x2000>; size = <0x1000>; }; }; }; The `sf-bootdev` driver can implement a way to read from the SPI flash, using the offset and size provided, and return that bootflow file back to the caller. When distro boot wants to read the kernel it calls distro_getfile() which must provide a way to read from the SPI flash. See `distro_boot()` at distro_boot_ for more details. Of course this is all internal to U-Boot. All the distro sees is another way to boot. Configuration ------------- Standard boot is enabled with `CONFIG_BOOTSTD`. Each bootmeth has its own CONFIG option also. For example, `CONFIG_BOOTMETH_EXTLINUX` enables support for booting from a disk using an `extlinux.conf` file. To enable all features of standard boot, use `CONFIG_BOOTSTD_FULL`. This includes the full set of commands, more error messages when things go wrong and bootmeth ordering with the bootmeths environment variable. You should probably also enable `CONFIG_BOOTSTD_DEFAULTS`, which provides several filesystem and network features (if `CONFIG_NET` is enabled) so that a good selection of boot options is available. Some devicetree properties are supported in the bootstd node when `CONFIG_BOOTSTD_FULL` is enabled: filename-prefixes List of prefixes to use when searching for files on block devices. This defaults to {"/", "/boot/"} if not provided. bootdev-order Lists the bootdev ordering to use. Note that the deprecated `boot_targets` environment variable overrides this, if present. theme (subnode) Sets the theme to use for menus. See :doc:`/develop/expo`. Available bootmeth drivers -------------------------- Bootmeth drivers are provided for booting from various media: - :doc:`Android ` bootflow (boot image v4) - :doc:`ChromiumOS ` ChromiumOS boot from a disk - EFI boot using bootefi from disk - EFI boot using boot manager - :doc:`extlinux / syslinux ` boot from a storage device - :doc:`extlinux / syslinux ` boot from a network (PXE) - :doc:`sandbox ` used only for testing - :doc:`U-Boot scripts