Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0+: |
| 2 | |
Simon Glass | 16047dc | 2024-07-17 09:30:52 +0100 | [diff] [blame] | 3 | Standard Boot Overview |
| 4 | ====================== |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 5 | |
| 6 | Introduction |
| 7 | ------------ |
| 8 | |
| 9 | Standard boot provides a built-in way for U-Boot to automatically boot |
| 10 | an Operating System without custom scripting and other customisation. It |
| 11 | introduces the following concepts: |
| 12 | |
| 13 | - bootdev - a device which can hold or access a distro (e.g. MMC, Ethernet) |
| 14 | - bootmeth - a method to scan a bootdev to find bootflows (e.g. distro boot) |
| 15 | - bootflow - a description of how to boot (provided by the distro) |
| 16 | |
| 17 | For Linux, the distro (Linux distribution, e.g. Debian, Fedora) is responsible |
| 18 | for creating a bootflow for each kernel combination that it wants to offer. |
| 19 | These bootflows are stored on media so they can be discovered by U-Boot. This |
Simon Glass | 16047dc | 2024-07-17 09:30:52 +0100 | [diff] [blame] | 20 | feature is typically called `distro boot` (see :doc:`../distro`) because it is |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 21 | a way for distributions to boot on any hardware. |
| 22 | |
| 23 | Traditionally U-Boot has relied on scripts to implement this feature. See |
Paul Barker | 6c55d0d | 2022-07-29 14:31:58 +0100 | [diff] [blame] | 24 | distro_bootcmd_ for details. This is done because U-Boot has no native support |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 25 | for scanning devices. While the scripts work remarkably well, they can be hard |
| 26 | to understand and extend, and the feature does not include tests. They are also |
| 27 | making it difficult to move away from ad-hoc CONFIGs, since they are implemented |
| 28 | using the environment and a lot of #defines. |
| 29 | |
| 30 | Standard boot is a generalisation of distro boot. It provides a more built-in |
| 31 | way to boot with U-Boot. The feature is extensible to different Operating |
| 32 | Systems (such as Chromium OS) and devices (beyond just block and network |
| 33 | devices). It supports EFI boot and EFI bootmgr too. |
| 34 | |
Simon Glass | 16047dc | 2024-07-17 09:30:52 +0100 | [diff] [blame] | 35 | Finally, standard boot supports the operation of :doc:`../vbe`. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 36 | |
| 37 | Bootflow |
| 38 | -------- |
| 39 | |
| 40 | A bootflow is a file that describes how to boot a distro. Conceptually there can |
| 41 | be different formats for that file but at present U-Boot only supports the |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 42 | BootLoaderSpec_ format which looks something like this:: |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 43 | |
| 44 | menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. |
| 45 | menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. |
| 46 | menu hidden |
| 47 | |
| 48 | label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) |
| 49 | kernel /vmlinuz-5.3.7-301.fc31.armv7hl |
| 50 | append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB |
| 51 | fdtdir /dtb-5.3.7-301.fc31.armv7hl/ |
| 52 | initrd /initramfs-5.3.7-301.fc31.armv7hl.img |
| 53 | |
| 54 | As you can see it specifies a kernel, a ramdisk (initrd) and a directory from |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 55 | which to load Device Tree files. The details are described in distro_bootcmd_. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 56 | |
| 57 | The bootflow is provided by the distro. It is not part of U-Boot. U-Boot's job |
| 58 | is simply to interpret the file and carry out the instructions. This allows |
| 59 | distros to boot on essentially any device supported by U-Boot. |
| 60 | |
| 61 | Typically the first available bootflow is selected and booted. If that fails, |
| 62 | then the next one is tried. |
| 63 | |
| 64 | |
| 65 | Bootdev |
| 66 | ------- |
| 67 | |
| 68 | Where does U-Boot find the media that holds the operating systems? That is the |
| 69 | job of bootdev. A bootdev is simply a layer on top of a media device (such as |
| 70 | MMC, NVMe). The bootdev accesses the device, including partitions and |
| 71 | filesystems that might contain things related to an operating system. |
| 72 | |
| 73 | For example, an MMC bootdev provides access to the individual partitions on the |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 74 | MMC device. It scans through these to find filesystems with the boot flag set, |
| 75 | then provides a list of these for consideration. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 76 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 77 | Some bootdevs are not visible until a bus is enumerated, e.g. flash sticks |
| 78 | attached via USB. To deal with this, each bootdev has an associated 'hunter' |
| 79 | which can hunt for bootdevs of a particular uclass type. For example, the SCSI |
| 80 | bootdev scans the SCSI bus looking for devices, creating a bootdev for each |
| 81 | Logical Unit Number (LUN) that it finds. |
| 82 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 83 | |
| 84 | Bootmeth |
| 85 | -------- |
| 86 | |
| 87 | Once the list of filesystems is provided, how does U-Boot find the bootflow |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 88 | files in these filesystems? That is the job of bootmeth. Each boot method has |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 89 | its own way of doing this. |
| 90 | |
| 91 | For example, the distro bootmeth simply looks through the provided filesystem |
| 92 | for a file called `extlinux/extlinux.conf`. This files constitutes a bootflow. |
| 93 | If the distro bootmeth is used on multiple partitions it may produce multiple |
| 94 | bootflows. |
| 95 | |
| 96 | Note: it is possible to have a bootmeth that uses a partition or a whole device |
| 97 | directly, but it is more common to use a filesystem. |
Mattijs Korpershoek | b30baa9 | 2024-07-10 10:40:05 +0200 | [diff] [blame] | 98 | For example, the Android bootmeth uses a whole device. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 99 | |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 100 | Note that some bootmeths are 'global', meaning that they select the bootdev |
| 101 | themselves. Examples include VBE and EFI boot manager. In this case, they |
| 102 | provide a `read_bootflow()` method which checks whatever bootdevs it likes, then |
| 103 | returns the bootflow, if found. Some of these bootmeths may be very slow, if |
| 104 | they scan a lot of devices. |
| 105 | |
Martyn Welch | 93a0d16 | 2024-10-09 14:15:40 +0100 | [diff] [blame] | 106 | The extlinux bootmeth also allows for bootmeth specific configuration to be |
| 107 | set. A bootmeth that wishes to support this provides the `set_property()` |
| 108 | method. This allows string properties and values to be passed to the bootmeth. |
| 109 | It is up to the bootmeth to determine what action to take when this method is |
| 110 | called. |
| 111 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 112 | |
| 113 | Boot process |
| 114 | ------------ |
| 115 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 116 | U-Boot tries to use the 'lazy init' approach wherever possible and distro boot |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 117 | is no exception. The algorithm is:: |
| 118 | |
| 119 | while (get next bootdev) |
| 120 | while (get next bootmeth) |
| 121 | while (get next bootflow) |
| 122 | try to boot it |
| 123 | |
| 124 | So U-Boot works its way through the bootdevs, trying each bootmeth in turn to |
| 125 | obtain bootflows, until it either boots or exhausts the available options. |
| 126 | |
| 127 | Instead of 500 lines of #defines and a 4KB boot script, all that is needed is |
| 128 | the following command:: |
| 129 | |
| 130 | bootflow scan -lb |
| 131 | |
| 132 | which scans for available bootflows, optionally listing each find it finds (-l) |
| 133 | and trying to boot it (-b). |
| 134 | |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 135 | When global bootmeths are available, these are typically checked before the |
| 136 | above bootdev scanning. |
| 137 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 138 | |
| 139 | Controlling ordering |
| 140 | -------------------- |
| 141 | |
Simon Glass | 2f27e47 | 2023-08-19 16:49:35 -0600 | [diff] [blame] | 142 | By default, faster bootdevs (or those which are assumed to be faster) are used |
| 143 | first, since they are more likely to be able to boot the device quickly. |
| 144 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 145 | Several options are available to control the ordering of boot scanning: |
| 146 | |
| 147 | |
| 148 | boot_targets |
| 149 | ~~~~~~~~~~~~ |
| 150 | |
| 151 | This environment variable can be used to control the list of bootdevs searched |
| 152 | and their ordering, for example:: |
| 153 | |
| 154 | setenv boot_targets "mmc0 mmc1 usb pxe" |
| 155 | |
| 156 | Entries may be removed or re-ordered in this list to affect the boot order. If |
| 157 | the variable is empty, the default ordering is used, based on the priority of |
| 158 | bootdevs and their sequence numbers. |
| 159 | |
| 160 | |
| 161 | bootmeths |
| 162 | ~~~~~~~~~ |
| 163 | |
Simon Glass | 2f27e47 | 2023-08-19 16:49:35 -0600 | [diff] [blame] | 164 | By default bootmeths are checked in name order. Use `bootmeth list` to see the |
| 165 | ordering. Note that the `extlinux` and `script` bootmeth is first, to preserve the behaviour |
| 166 | used by the old distro scripts. |
| 167 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 168 | This environment variable can be used to control the list of bootmeths used and |
| 169 | their ordering for example:: |
| 170 | |
Simon Glass | b71d7f7 | 2023-05-10 16:34:46 -0600 | [diff] [blame] | 171 | setenv bootmeths "extlinux efi" |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 172 | |
| 173 | Entries may be removed or re-ordered in this list to affect the order the |
| 174 | bootmeths are tried on each bootdev. If the variable is empty, the default |
| 175 | ordering is used, based on the bootmeth sequence numbers, which can be |
| 176 | controlled by aliases. |
| 177 | |
| 178 | The :ref:`usage/cmd/bootmeth:bootmeth command` (`bootmeth order`) operates in |
| 179 | the same way as setting this variable. |
| 180 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 181 | Bootdev uclass |
| 182 | -------------- |
| 183 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 184 | The bootdev uclass provides a simple API call to obtain a bootflow from a |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 185 | device:: |
| 186 | |
| 187 | int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, |
| 188 | struct bootflow *bflow); |
| 189 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 190 | This takes an iterator which indicates the bootdev, partition and bootmeth to |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 191 | use. It returns a bootflow. This is the core of the bootdev implementation. The |
| 192 | bootdev drivers that implement this differ depending on the media they are |
| 193 | reading from, but each is responsible for returning a valid bootflow if |
| 194 | available. |
| 195 | |
| 196 | A helper called `bootdev_find_in_blk()` makes it fairly easy to implement this |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 197 | function for each media device uclass, in a few lines of code. For many types |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 198 | of bootdevs, the `get_bootflow` member can be NULL, indicating that the default |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 199 | handler is used. This is called `default_get_bootflow()` and it only works with |
| 200 | block devices. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 201 | |
| 202 | |
| 203 | Bootdev drivers |
| 204 | --------------- |
| 205 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 206 | A bootdev driver is typically fairly simple. Here is one for MMC:: |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 207 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 208 | static int mmc_bootdev_bind(struct udevice *dev) |
| 209 | { |
| 210 | struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); |
| 211 | |
Simon Glass | 7e1f6a4 | 2023-01-17 10:48:08 -0700 | [diff] [blame] | 212 | ucp->prio = BOOTDEVP_2_INTERNAL_FAST; |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 213 | |
| 214 | return 0; |
| 215 | } |
| 216 | |
| 217 | struct bootdev_ops mmc_bootdev_ops = { |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 218 | }; |
| 219 | |
| 220 | static const struct udevice_id mmc_bootdev_ids[] = { |
| 221 | { .compatible = "u-boot,bootdev-mmc" }, |
| 222 | { } |
| 223 | }; |
| 224 | |
| 225 | U_BOOT_DRIVER(mmc_bootdev) = { |
| 226 | .name = "mmc_bootdev", |
| 227 | .id = UCLASS_BOOTDEV, |
| 228 | .ops = &mmc_bootdev_ops, |
| 229 | .bind = mmc_bootdev_bind, |
| 230 | .of_match = mmc_bootdev_ids, |
| 231 | }; |
| 232 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 233 | You may notice that the `get_bootflow` memory is not provided, so is NULL. This |
| 234 | means that `default_get_bootflow()` is used. This simply obtains the |
| 235 | block device and calls a bootdev helper function to do the rest. The |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 236 | implementation of `bootdev_find_in_blk()` checks the partition table, and |
| 237 | attempts to read a file from a filesystem on the partition number given by the |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 238 | `@iter->part` parameter. If there are any bootable partitions in the table, |
| 239 | then only bootable partitions are considered. |
| 240 | |
| 241 | Each bootdev has a priority, which indicates the order in which it is used, |
| 242 | if `boot_targets` is not used. Faster bootdevs are used first, since they are |
| 243 | more likely to be able to boot the device quickly. |
| 244 | |
| 245 | |
| 246 | Environment Variables |
| 247 | --------------------- |
| 248 | |
| 249 | Various environment variables are used by standard boot. These allow the board |
| 250 | to control where things are placed when booting the OS. You should ensure that |
| 251 | your boards sets values for these. |
| 252 | |
| 253 | fdtfile |
| 254 | Name of the flattened device tree (FDT) file to load, e.g. |
| 255 | "rockchip/rk3399-rockpro64.dtb" |
| 256 | |
Heinrich Schuchardt | 75e86db | 2023-11-16 10:09:07 +0100 | [diff] [blame] | 257 | fdt_addr_r |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 258 | Address at which to load the FDT, e.g. 0x01f00000 |
| 259 | |
| 260 | fdtoverlay_addr_r (needed if overlays are used) |
| 261 | Address at which to load the overlay for the FDT, e.g. 0x02000000 |
| 262 | |
| 263 | kernel_addr_r |
| 264 | Address at which to load the kernel, e.g. 0x02080000 |
| 265 | |
| 266 | kernel_comp_addr_r |
| 267 | Address to which to decompress the kernel, e.g. 0x08000000 |
| 268 | |
| 269 | kernel_comp_size |
| 270 | Size of available space for decompressed kernel, e.g. 0x2000000 |
| 271 | |
| 272 | pxefile_addr_r |
| 273 | Address at which to load the PXE file, e.g. 0x00600000 |
| 274 | |
| 275 | ramdisk_addr_r |
| 276 | Address at which to load the ramdisk, e.g. 0x06000000 |
| 277 | |
| 278 | scriptaddr |
| 279 | Address at which to load the U-Boot script, e.g. 0x00500000 |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 280 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 281 | script_offset_f |
| 282 | SPI flash offset from which to load the U-Boot script, e.g. 0xffe000 |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 283 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 284 | script_size_f |
| 285 | Size of the script to load, e.g. 0x2000 |
| 286 | |
Mattijs Korpershoek | b30baa9 | 2024-07-10 10:40:05 +0200 | [diff] [blame] | 287 | vendor_boot_comp_addr_r |
| 288 | Address to which to load the vendor_boot Android image, e.g. 0xe0000000 |
| 289 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 290 | Some variables are set by script bootmeth: |
| 291 | |
| 292 | devtype |
| 293 | Device type being used for boot, e.g. mmc |
| 294 | |
| 295 | devnum |
| 296 | Device number being used for boot, e.g. 1 |
| 297 | |
| 298 | distro_bootpart |
| 299 | Partition being used for boot, e.g. 2 |
| 300 | |
| 301 | prefix |
| 302 | Directory containing the script |
| 303 | |
| 304 | mmc_bootdev |
| 305 | Device number being used for boot (e.g. 1). This is only used by MMC on |
| 306 | sunxi boards. |
| 307 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 308 | |
| 309 | Device hierarchy |
| 310 | ---------------- |
| 311 | |
| 312 | A bootdev device is a child of the media device. In this example, you can see |
| 313 | that the bootdev is a sibling of the block device and both are children of |
| 314 | media device:: |
| 315 | |
| 316 | mmc 0 [ + ] bcm2835-sdhost | |-- mmc@7e202000 |
| 317 | blk 0 [ + ] mmc_blk | | |-- mmc@7e202000.blk |
| 318 | bootdev 0 [ ] mmc_bootdev | | `-- mmc@7e202000.bootdev |
| 319 | mmc 1 [ + ] sdhci-bcm2835 | |-- sdhci@7e300000 |
| 320 | blk 1 [ ] mmc_blk | | |-- sdhci@7e300000.blk |
| 321 | bootdev 1 [ ] mmc_bootdev | | `-- sdhci@7e300000.bootdev |
| 322 | |
| 323 | The bootdev device is typically created automatically in the media uclass' |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 324 | `post_bind()` method by calling `bootdev_setup_for_dev()` or |
Simon Glass | b1d581d | 2023-07-30 11:15:14 -0600 | [diff] [blame] | 325 | `bootdev_setup_for_sibling_blk()`. The code typically something like this:: |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 326 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 327 | /* dev is the Ethernet device */ |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 328 | ret = bootdev_setup_for_dev(dev, "eth_bootdev"); |
| 329 | if (ret) |
| 330 | return log_msg_ret("bootdev", ret); |
| 331 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 332 | or:: |
| 333 | |
| 334 | /* blk is the block device (child of MMC device) |
Simon Glass | b1d581d | 2023-07-30 11:15:14 -0600 | [diff] [blame] | 335 | ret = bootdev_setup_for_sibling_blk(blk, "mmc_bootdev"); |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 336 | if (ret) |
| 337 | return log_msg_ret("bootdev", ret); |
| 338 | |
| 339 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 340 | Here, `eth_bootdev` is the name of the Ethernet bootdev driver and `dev` |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 341 | is the Ethernet device. This function is safe to call even if standard boot is |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 342 | not enabled, since it does nothing in that case. It can be added to all uclasses |
| 343 | which implement suitable media. |
| 344 | |
| 345 | |
| 346 | The bootstd device |
| 347 | ------------------ |
| 348 | |
| 349 | Standard boot requires a single instance of the bootstd device to make things |
| 350 | work. This includes global information about the state of standard boot. See |
| 351 | `struct bootstd_priv` for this structure, accessed with `bootstd_get_priv()`. |
| 352 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 353 | Within the Device Tree, if you add bootmeth devices, they should be children of |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 354 | the bootstd device. See `arch/sandbox/dts/test.dts` for an example of this. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 355 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 356 | |
| 357 | .. _`Automatic Devices`: |
| 358 | |
| 359 | Automatic devices |
| 360 | ----------------- |
| 361 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 362 | It is possible to define all the required devices in the Device Tree manually, |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 363 | but it is not necessary. The bootstd uclass includes a `dm_scan_other()` |
| 364 | function which creates the bootstd device if not found. If no bootmeth devices |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 365 | are found at all, it creates one for each available bootmeth driver. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 366 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 367 | If your Device Tree has any bootmeth device it must have all of them that you |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 368 | want to use, since no bootmeth devices will be created automatically in that |
| 369 | case. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 370 | |
| 371 | |
| 372 | Using devicetree |
| 373 | ---------------- |
| 374 | |
| 375 | If a bootdev is complicated or needs configuration information, it can be |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 376 | added to the Device Tree as a child of the media device. For example, imagine a |
| 377 | bootdev which reads a bootflow from SPI flash. The Device Tree fragment might |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 378 | look like this:: |
| 379 | |
| 380 | spi@0 { |
| 381 | flash@0 { |
| 382 | reg = <0>; |
| 383 | compatible = "spansion,m25p16", "jedec,spi-nor"; |
| 384 | spi-max-frequency = <40000000>; |
| 385 | |
| 386 | bootdev { |
| 387 | compatible = "u-boot,sf-bootdev"; |
| 388 | offset = <0x2000>; |
| 389 | size = <0x1000>; |
| 390 | }; |
| 391 | }; |
| 392 | }; |
| 393 | |
| 394 | The `sf-bootdev` driver can implement a way to read from the SPI flash, using |
| 395 | the offset and size provided, and return that bootflow file back to the caller. |
Dario Binacchi | 3c9c6d7 | 2022-08-26 15:15:41 +0200 | [diff] [blame] | 396 | When distro boot wants to read the kernel it calls distro_getfile() which must |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 397 | provide a way to read from the SPI flash. See `distro_boot()` at distro_boot_ |
| 398 | for more details. |
| 399 | |
| 400 | Of course this is all internal to U-Boot. All the distro sees is another way |
| 401 | to boot. |
| 402 | |
| 403 | |
| 404 | Configuration |
| 405 | ------------- |
| 406 | |
| 407 | Standard boot is enabled with `CONFIG_BOOTSTD`. Each bootmeth has its own CONFIG |
Simon Glass | b71d7f7 | 2023-05-10 16:34:46 -0600 | [diff] [blame] | 408 | option also. For example, `CONFIG_BOOTMETH_EXTLINUX` enables support for |
| 409 | booting from a disk using an `extlinux.conf` file. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 410 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 411 | To enable all features of standard boot, use `CONFIG_BOOTSTD_FULL`. This |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 412 | includes the full set of commands, more error messages when things go wrong and |
| 413 | bootmeth ordering with the bootmeths environment variable. |
| 414 | |
Simon Glass | 2e5161eb | 2023-01-28 15:00:21 -0700 | [diff] [blame] | 415 | You should probably also enable `CONFIG_BOOTSTD_DEFAULTS`, which provides |
| 416 | several filesystem and network features (if `CONFIG_NET` is enabled) so that |
| 417 | a good selection of boot options is available. |
| 418 | |
Simon Glass | f1d8e0f | 2024-07-17 09:31:04 +0100 | [diff] [blame] | 419 | Some devicetree properties are supported in the bootstd node when |
| 420 | `CONFIG_BOOTSTD_FULL` is enabled: |
| 421 | |
| 422 | filename-prefixes |
| 423 | List of prefixes to use when searching for files on block devices. This |
| 424 | defaults to {"/", "/boot/"} if not provided. |
| 425 | |
| 426 | bootdev-order |
| 427 | Lists the bootdev ordering to use. Note that the deprecated |
| 428 | `boot_targets` environment variable overrides this, if present. |
| 429 | |
| 430 | theme (subnode) |
| 431 | Sets the theme to use for menus. See :doc:`/develop/expo`. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 432 | |
| 433 | Available bootmeth drivers |
| 434 | -------------------------- |
| 435 | |
Simon Glass | 6d5d121 | 2024-07-17 09:30:54 +0100 | [diff] [blame] | 436 | Bootmeth drivers are provided for booting from various media: |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 437 | |
Mattijs Korpershoek | cb6fa2f | 2024-07-24 14:41:25 +0200 | [diff] [blame] | 438 | - :doc:`Android <android>` bootflow (boot image v4) |
Simon Glass | 53898fb | 2024-07-17 09:31:02 +0100 | [diff] [blame] | 439 | - :doc:`ChromiumOS <cros>` ChromiumOS boot from a disk |
| 440 | - EFI boot using bootefi from disk |
| 441 | - EFI boot using boot manager |
Simon Glass | 6d5d121 | 2024-07-17 09:30:54 +0100 | [diff] [blame] | 442 | - :doc:`extlinux / syslinux <extlinux>` boot from a storage device |
Simon Glass | 08379df | 2024-07-17 09:30:55 +0100 | [diff] [blame] | 443 | - :doc:`extlinux / syslinux <extlinux>` boot from a network (PXE) |
Simon Glass | 53898fb | 2024-07-17 09:31:02 +0100 | [diff] [blame] | 444 | - :doc:`sandbox <sandbox>` used only for testing |
Simon Glass | 257ddc2 | 2024-07-17 09:31:01 +0100 | [diff] [blame] | 445 | - :doc:`U-Boot scripts <script>` from disk, network or SPI flash |
Simon Glass | 01bd6f6 | 2024-07-17 09:30:56 +0100 | [diff] [blame] | 446 | - :doc:`QFW <qfw>`: QEMU firmware interface |
Simon Glass | 53898fb | 2024-07-17 09:31:02 +0100 | [diff] [blame] | 447 | - :doc:`VBE </develop/vbe>`: Verified Boot for Embedded |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 448 | |
Simon Glass | dc1917d | 2024-07-17 09:30:53 +0100 | [diff] [blame] | 449 | Each driver is controlled by a Kconfig option. If no bootmeth driver is |
| 450 | selected by a compatible string in the devicetree, all available bootmeth |
| 451 | drivers are bound automatically. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 452 | |
| 453 | Command interface |
| 454 | ----------------- |
| 455 | |
Simon Glass | c6a52e7 | 2024-11-15 16:19:23 -0700 | [diff] [blame] | 456 | Four commands are available: |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 457 | |
| 458 | `bootdev` |
| 459 | Allows listing of available bootdevs, selecting a particular one and |
Simon Glass | 16047dc | 2024-07-17 09:30:52 +0100 | [diff] [blame] | 460 | getting information about it. See :doc:`/usage/cmd/bootdev` |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 461 | |
| 462 | `bootflow` |
| 463 | Allows scanning one or more bootdevs for bootflows, listing available |
| 464 | bootflows, selecting one, obtaining information about it and booting it. |
Simon Glass | 16047dc | 2024-07-17 09:30:52 +0100 | [diff] [blame] | 465 | See :doc:`/usage/cmd/bootflow` |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 466 | |
| 467 | `bootmeth` |
Martyn Welch | 93a0d16 | 2024-10-09 14:15:40 +0100 | [diff] [blame] | 468 | Allow listing of available bootmethds, setting the order in which they are |
| 469 | tried and bootmeth specific configuration. See :doc:`/usage/cmd/bootmeth` |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 470 | |
Simon Glass | c6a52e7 | 2024-11-15 16:19:23 -0700 | [diff] [blame] | 471 | `bootstd` |
| 472 | Allow access to standard boot itself, so far only for listing images across |
| 473 | all bootflows. See :doc:`/usage/cmd/bootstd` |
| 474 | |
| 475 | Images |
| 476 | ------ |
| 477 | |
| 478 | Standard boot keeps track of images which can or have been loaded. These are |
| 479 | kept in a list attached to each bootflow. They can be listed using the |
| 480 | ``bootstd images`` command (see :doc:`/usage/cmd/bootstd`). |
| 481 | |
| 482 | For now most bootmeths load their images when scanning. Over time, some may |
| 483 | adjust to load them only when needed, but in this case the images will still |
| 484 | be visible. |
| 485 | |
| 486 | Once a bootflow has been selected, images for those that are not selected can |
| 487 | potentially be dropped from the memory map. For now, this is not implemented. |
| 488 | |
| 489 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 490 | .. _BootflowStates: |
| 491 | |
| 492 | Bootflow states |
| 493 | --------------- |
| 494 | |
| 495 | Here is a list of states that a bootflow can be in: |
| 496 | |
| 497 | ======= ======================================================================= |
| 498 | State Meaning |
| 499 | ======= ======================================================================= |
| 500 | base Starting-out state, indicates that no media/partition was found. For an |
| 501 | SD card socket it may indicate that the card is not inserted. |
| 502 | media Media was found (e.g. SD card is inserted) but no partition information |
| 503 | was found. It might lack a partition table or have a read error. |
| 504 | part Partition was found but a filesystem could not be read. This could be |
| 505 | because the partition does not hold a filesystem or the filesystem is |
| 506 | very corrupted. |
| 507 | fs Filesystem was found but the file could not be read. It could be |
| 508 | missing or in the wrong subdirectory. |
| 509 | file File was found and its size detected, but it could not be read. This |
| 510 | could indicate filesystem corruption. |
| 511 | ready File was loaded and is ready for use. In this state the bootflow is |
| 512 | ready to be booted. |
| 513 | ======= ======================================================================= |
| 514 | |
| 515 | |
Simon Glass | e27d7df | 2023-09-14 10:55:55 -0600 | [diff] [blame] | 516 | Migrating from distro_boot |
| 517 | -------------------------- |
| 518 | |
| 519 | To migrate from distro_boot: |
| 520 | |
| 521 | #. Update your board header files to remove the BOOTENV and BOOT_TARGET_xxx |
| 522 | defines. Standard boot finds available boot devices automatically. |
| 523 | |
| 524 | #. Remove the "boot_targets" variable unless you need it. Standard boot uses a |
| 525 | default order from fastest to slowest, which generally matches the order used |
| 526 | by boards. |
| 527 | |
| 528 | #. Make sure that CONFIG_BOOTSTD_DEFAULTS is enabled by your board, so it can |
| 529 | boot common Linux distributions. |
| 530 | |
| 531 | An example patch is at migrate_patch_. |
| 532 | |
| 533 | If you are using custom boot scripts for your board, consider creating your |
| 534 | own bootmeth to hold the logic. There are various examples at |
| 535 | `boot/bootmeth_...`. |
| 536 | |
| 537 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 538 | Theory of operation |
| 539 | ------------------- |
| 540 | |
| 541 | This describes how standard boot progresses through to booting an operating |
| 542 | system. |
| 543 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 544 | To start, all the necessary devices must be bound, including bootstd, which |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 545 | provides the top-level `struct bootstd_priv` containing optional configuration |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 546 | information. The bootstd device also holds the various lists used while |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 547 | scanning. This step is normally handled automatically by driver model, as |
| 548 | described in `Automatic Devices`_. |
| 549 | |
| 550 | Bootdevs are also required, to provide access to the media to use. These are not |
| 551 | useful by themselves: bootmeths are needed to provide the means of scanning |
| 552 | those bootdevs. So, all up, we need a single bootstd device, one or more bootdev |
| 553 | devices and one or more bootmeth devices. |
| 554 | |
| 555 | Once these are ready, typically a `bootflow scan` command is issued. This kicks |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 556 | off the iteration process, which involves hunting for bootdevs and looking |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 557 | through the bootdevs and their partitions one by one to find bootflows. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 558 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 559 | Iteration is kicked off using `bootflow_scan_first()`. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 560 | |
| 561 | The iterator is set up with `bootflow_iter_init()`. This simply creates an |
| 562 | empty one with the given flags. Flags are used to control whether each |
| 563 | iteration is displayed, whether to return iterations even if they did not result |
| 564 | in a valid bootflow, whether to iterate through just a single bootdev, etc. |
| 565 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 566 | Then the iterator is set up to according to the parameters given: |
| 567 | |
| 568 | - When `dev` is provided, then a single bootdev is scanned. In this case, |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 569 | `BOOTFLOWIF_SKIP_GLOBAL` and `BOOTFLOWIF_SINGLE_DEV` are set. No hunters are |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 570 | used in this case |
| 571 | |
| 572 | - Otherwise, when `label` is provided, then a single label or named bootdev is |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 573 | scanned. In this case `BOOTFLOWIF_SKIP_GLOBAL` is set and there are three |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 574 | options (with an effect on the `iter_incr()` function described later): |
| 575 | |
| 576 | - If `label` indicates a numeric bootdev number (e.g. "2") then |
| 577 | `BOOTFLOW_METHF_SINGLE_DEV` is set. In this case, moving to the next bootdev |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 578 | simply stops, since there is only one. No hunters are used. |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 579 | - If `label` indicates a particular media device (e.g. "mmc1") then |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 580 | `BOOTFLOWIF_SINGLE_MEDIA` is set. In this case, moving to the next bootdev |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 581 | processes just the children of the media device. Hunters are used, in this |
| 582 | example just the "mmc" hunter. |
Nam Cao | cb0d3fb | 2024-02-21 13:41:44 +0100 | [diff] [blame] | 583 | - If `label` indicates a particular partition in a particular media device |
| 584 | (e.g. "mmc1:3") then `BOOTFLOWIF_SINGLE_PARTITION` is set. In this case, |
| 585 | only a single partition within a bootdev is processed. Hunters are used, in |
| 586 | this example just the "mmc" hunter. |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 587 | - If `label` indicates a media uclass (e.g. "mmc") then |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 588 | `BOOTFLOWIF_SINGLE_UCLASS` is set. In this case, all bootdevs in that uclass |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 589 | are used. Hunters are used, in this example just the "mmc" hunter |
| 590 | |
| 591 | - Otherwise, none of the above flags is set and iteration is set up to work |
| 592 | through `boot_targets` environment variable (or `bootdev-order` device tree |
| 593 | property) in order, running the relevant hunter first. In this case |
| 594 | `cur_label` is used to indicate the label being processed. If there is no list |
| 595 | of labels, then all bootdevs are processed in order of priority, running the |
| 596 | hunters as it goes. |
| 597 | |
| 598 | With the above it is therefore possible to iterate in a variety of ways. |
| 599 | |
| 600 | No attempt is made to determine the ordering of bootdevs, since this cannot be |
| 601 | known in advance if we are using the hunters. Any hunter might discover a new |
| 602 | bootdev and disturb the original ordering. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 603 | |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 604 | Next, the ordering of bootmeths is determined, by `bootmeth_setup_iter_order()`. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 605 | By default the ordering is again by sequence number, i.e. the `/aliases` node, |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 606 | or failing that the order in the Device Tree. But the `bootmeth order` command |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 607 | or `bootmeths` environment variable can be used to set up an ordering. If that |
| 608 | has been done, the ordering is in `struct bootstd_priv`, so that ordering is |
| 609 | simply copied into the iterator. Either way, the `method_order` array it set up, |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 610 | along with `num_methods`. |
| 611 | |
| 612 | Note that global bootmeths are always put at the end of the ordering. If any are |
| 613 | present, `cur_method` is set to the first one, so that global bootmeths are done |
| 614 | first. Once all have been used, these bootmeths are dropped from the iteration. |
| 615 | When there are no global bootmeths, `cur_method` is set to 0. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 616 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 617 | At this point the iterator is ready to use, with the first bootmeth selected. |
| 618 | Most of the other fields are 0. This means that the current partition |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 619 | is 0, which is taken to mean the whole device, since partition numbers start at |
| 620 | 1. It also means that `max_part` is 0, i.e. the maximum partition number we know |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 621 | about is 0, meaning that, as far as we know, there is no partition table on this |
| 622 | bootdev. |
| 623 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 624 | With the iterator ready, `bootflow_scan_first()` checks whether the current |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 625 | settings produce a valid bootflow. This is handled by `bootflow_check()`, which |
| 626 | either returns 0 (if it got something) or an error if not (more on that later). |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 627 | If the `BOOTFLOWIF_ALL` iterator flag is set, even errors are returned as |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 628 | incomplete bootflows, but normally an error results in moving onto the next |
| 629 | iteration. |
| 630 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 631 | Note that `bootflow_check()` handles global bootmeths explicitly, by calling |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 632 | `bootmeth_get_bootflow()` on each one. The `doing_global` flag indicates when |
| 633 | the iterator is in that state. |
| 634 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 635 | The `bootflow_scan_next()` function handles moving onto the next iteration and |
| 636 | checking it. In fact it sits in a loop doing that repeatedly until it finds |
| 637 | something it wants to return. |
| 638 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 639 | The actual 'moving on' part is implemented in `iter_incr()`. This is a fairly |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 640 | simple function. It increments the first counter. If that hits its maximum, it |
| 641 | sets it to zero and increments the second counter. You can think of all the |
| 642 | counters together as a number with three digits which increment in order, with |
| 643 | the least-sigificant digit on the right, counting like this: |
| 644 | |
| 645 | ======== ======= ======= |
| 646 | bootdev part method |
| 647 | ======== ======= ======= |
| 648 | 0 0 0 |
| 649 | 0 0 1 |
| 650 | 0 0 2 |
| 651 | 0 1 0 |
| 652 | 0 1 1 |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 653 | 0 1 2 |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 654 | 1 0 0 |
| 655 | 1 0 1 |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 656 | ... |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 657 | ======== ======= ======= |
| 658 | |
| 659 | The maximum value for `method` is `num_methods - 1` so when it exceeds that, it |
| 660 | goes back to 0 and the next `part` is considered. The maximum value for that is |
| 661 | `max_part`, which is initially zero for all bootdevs. If we find a partition |
| 662 | table on that bootdev, `max_part` can be updated during the iteration to a |
| 663 | higher value - see `bootdev_find_in_blk()` for that, described later. If that |
| 664 | exceeds its maximum, then the next bootdev is used. In this way, iter_incr() |
| 665 | works its way through all possibilities, moving forward one each time it is |
| 666 | called. |
| 667 | |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 668 | Note that global bootmeths introduce a subtlety into the above description. |
| 669 | When `doing_global` is true, the iteration takes place only among the bootmeths, |
| 670 | i.e. the last column above. The global bootmeths are at the end of the list. |
| 671 | Assuming that they are entries 3 and 4 in the list, the iteration then looks |
| 672 | like this: |
| 673 | |
| 674 | ======== ======= ======= ======================================= |
| 675 | bootdev part method notes |
| 676 | ======== ======= ======= ======================================= |
| 677 | . . 3 doing_global = true, method_count = 5 |
| 678 | . . 4 |
| 679 | 0 0 0 doing_global = false, method_count = 3 |
| 680 | 0 0 1 |
| 681 | 0 0 2 |
| 682 | 0 1 0 |
| 683 | 0 1 1 |
| 684 | 0 1 2 |
| 685 | 1 0 0 |
| 686 | 1 0 1 |
| 687 | ... |
| 688 | ======== ======= ======= ======================================= |
| 689 | |
| 690 | The changeover of the value of `doing_global` from true to false is handled in |
| 691 | `iter_incr()` as well. |
| 692 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 693 | Note that the value in the `bootdev` column above is not actually stored - it is |
| 694 | just for illustration. In practice, `iter_incr()` uses the flags to determine |
| 695 | whether to move to the next bootdev in the uclass, the next child of the media |
| 696 | device, the next label, or the next priority level, depending on the flag |
| 697 | settings (see `BOOTFLOW_METHF_SINGLE_DEV`, etc. above). |
| 698 | |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 699 | There is no expectation that iteration will actually finish. Quite often a |
| 700 | valid bootflow is found early on. With `bootflow scan -b`, that causes the |
| 701 | bootflow to be immediately booted. Assuming it is successful, the iteration never |
| 702 | completes. |
| 703 | |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 704 | Also note that the iterator holds the **current** combination being considered. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 705 | So when `iter_incr()` is called, it increments to the next one and returns it, |
| 706 | the new **current** combination. |
| 707 | |
| 708 | Note also the `err` field in `struct bootflow_iter`. This is normally 0 and has |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 709 | thus no effect on `iter_inc()`. But if it is non-zero, signalling an error, |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 710 | it indicates to the iterator what it should do when called. It can force moving |
| 711 | to the next partition, or bootdev, for example. The special values |
| 712 | `BF_NO_MORE_PARTS` and `BF_NO_MORE_DEVICES` handle this. When `iter_incr` sees |
| 713 | `BF_NO_MORE_PARTS` it knows that it should immediately move to the next bootdev. |
| 714 | When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do |
| 715 | so it should immediately return. The caller of `iter_incr()` is responsible for |
| 716 | updating the `err` field, based on the return value it sees. |
| 717 | |
| 718 | The above describes the iteration process at a high level. It is basically a |
| 719 | very simple increment function with a checker called `bootflow_check()` that |
| 720 | checks the result of each iteration generated, to determine whether it can |
| 721 | produce a bootflow. |
| 722 | |
| 723 | So what happens inside of `bootflow_check()`? It simply calls the uclass |
| 724 | method `bootdev_get_bootflow()` to ask the bootdev to return a bootflow. It |
| 725 | passes the iterator to the bootdev method, so that function knows what we are |
| 726 | talking about. At first, the bootflow is set up in the state `BOOTFLOWST_BASE`, |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 727 | with just the `method` and `dev` initialised. But the bootdev may fill in more, |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 728 | e.g. updating the state, depending on what it finds. For global bootmeths the |
| 729 | `bootmeth_get_bootflow()` function is called instead of |
| 730 | `bootdev_get_bootflow()`. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 731 | |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 732 | Based on what the bootdev or bootmeth responds with, `bootflow_check()` either |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 733 | returns a valid bootflow, or a partial one with an error. A partial bootflow |
| 734 | is one that has some fields set up, but did not reach the `BOOTFLOWST_READY` |
Simon Glass | 99e6818 | 2023-02-22 12:17:03 -0700 | [diff] [blame] | 735 | state. As noted before, if the `BOOTFLOWIF_ALL` iterator flag is set, then all |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 736 | bootflows are returned, even partial ones. This can help with debugging. |
| 737 | |
| 738 | So at this point you can see that total control over whether a bootflow can |
Simon Glass | afaeb77 | 2022-07-30 15:52:35 -0600 | [diff] [blame] | 739 | be generated from a particular iteration, or not, rests with the bootdev (or |
| 740 | global bootmeth). Each one can adopt its own approach. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 741 | |
| 742 | Going down a level, what does the bootdev do in its `get_bootflow()` method? |
| 743 | Let us consider the MMC bootdev. In that case the call to |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 744 | `bootdev_get_bootflow()` ends up in `default_get_bootflow()`. It locates the |
| 745 | parent device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds |
| 746 | the block device associated with it. It then calls the helper function |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 747 | `bootdev_find_in_blk()` to do all the work. This is common with just about any |
| 748 | bootdev that is based on a media device. |
| 749 | |
| 750 | The `bootdev_find_in_blk()` helper is implemented in the bootdev uclass. It |
| 751 | names the bootflow and copies the partition number in from the iterator. Then it |
| 752 | calls the bootmeth device to check if it can support this device. This is |
| 753 | important since some bootmeths only work with network devices, for example. If |
| 754 | that check fails, it stops. |
| 755 | |
| 756 | Assuming the bootmeth is happy, or at least indicates that it is willing to try |
| 757 | (by returning 0 from its `check()` method), the next step is to try the |
| 758 | partition. If that works it tries to detect a file system. If that works then it |
| 759 | calls the bootmeth device once more, this time to read the bootflow. |
| 760 | |
Simon Glass | 0fca7e8 | 2023-08-24 13:55:43 -0600 | [diff] [blame] | 761 | Note: Normally a filesystem is needed for the bootmeth to be called on block |
| 762 | devices, but bootmeths which don't need that can set the BOOTMETHF_ANY_PART |
| 763 | flag to indicate that they can scan any partition. An example is the ChromiumOS |
| 764 | bootmeth which can store a kernel in a raw partition. Note also that sandbox is |
| 765 | a special case, since in that case the host filesystem can be accessed even |
| 766 | though the block device is NULL. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 767 | |
Simon Glass | b71d7f7 | 2023-05-10 16:34:46 -0600 | [diff] [blame] | 768 | If we take the example of the `bootmeth_extlinux` driver, this call ends up at |
| 769 | `extlinux_read_bootflow()`. It has the filesystem ready, so tries various |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 770 | filenames to try to find the `extlinux.conf` file, reading it if possible. If |
| 771 | all goes well the bootflow ends up in the `BOOTFLOWST_READY` state. |
| 772 | |
| 773 | At this point, we fall back from the bootmeth driver, to |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 774 | `bootdev_find_in_blk()`, then back to `default_get_bootflow()`, then to |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 775 | `bootdev_get_bootflow()`, then to `bootflow_check()` and finally to its caller, |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 776 | either `bootflow_scan_first()` or `bootflow_scan_next()`. In either case, |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 777 | the bootflow is returned as the result of this iteration, assuming it made it to |
| 778 | the `BOOTFLOWST_READY` state. |
| 779 | |
| 780 | That is the basic operation of scanning for bootflows. The process of booting a |
| 781 | bootflow is handled by the bootmeth driver for that bootflow. In the case of |
Simon Glass | b71d7f7 | 2023-05-10 16:34:46 -0600 | [diff] [blame] | 782 | extlinux boot, this parses and processes the `extlinux.conf` file that was read. |
| 783 | See `extlinux_boot()` for how that works. The processing may involve reading |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 784 | additional files, which is handled by the `read_file()` method, which is |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 785 | `extlinux_read_file()` in this case. All bootmeths should support reading |
Simon Glass | b71d7f7 | 2023-05-10 16:34:46 -0600 | [diff] [blame] | 786 | files, since the bootflow is typically only the basic instructions and does not |
| 787 | include the operating system itself, ramdisk, device tree, etc. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 788 | |
| 789 | The vast majority of the bootstd code is concerned with iterating through |
Quentin Schulz | e0ff5cc | 2024-06-12 16:58:48 +0200 | [diff] [blame] | 790 | partitions on bootdevs and using bootmeths to find bootflows. |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 791 | |
| 792 | How about bootdevs which are not block devices? They are handled by the same |
| 793 | methods as above, but with a different implementation. For example, the bootmeth |
| 794 | for PXE boot (over a network) uses `tftp` to read files rather than `fs_read()`. |
| 795 | But other than that it is very similar. |
| 796 | |
| 797 | |
| 798 | Tests |
| 799 | ----- |
| 800 | |
| 801 | Tests are located in `test/boot` and cover the core functionality as well as |
| 802 | the commands. All tests use sandbox so can be run on a standard Linux computer |
| 803 | and in U-Boot's CI. |
| 804 | |
Simon Glass | 736612e | 2023-01-17 10:48:19 -0700 | [diff] [blame] | 805 | For testing, a DOS-formatted disk image is used with a FAT partition on it and |
| 806 | a second unused partition. This is created in `setup_bootflow_image()`, with a |
| 807 | canned one from the source tree used if it cannot be created (e.g. in CI). |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 808 | |
| 809 | |
| 810 | Bootflow internals |
| 811 | ------------------ |
| 812 | |
| 813 | The bootstd device holds a linked list of scanned bootflows as well as the |
| 814 | currently selected bootdev and bootflow (for use by commands). This is in |
| 815 | `struct bootstd_priv`. |
| 816 | |
| 817 | Each bootdev device has its own `struct bootdev_uc_plat` which holds a |
| 818 | list of scanned bootflows just for that device. |
| 819 | |
| 820 | The bootflow itself is documented in bootflow_h_. It includes various bits of |
| 821 | information about the bootflow and a buffer to hold the file. |
| 822 | |
| 823 | |
| 824 | Future |
| 825 | ------ |
| 826 | |
| 827 | Apart from the to-do items below, different types of bootflow files may be |
| 828 | implemented in future, e.g. Chromium OS support which is currently only |
| 829 | available as a script in chromebook_coral. |
| 830 | |
| 831 | |
| 832 | To do |
| 833 | ----- |
| 834 | |
| 835 | Some things that need to be done to completely replace the distro-boot scripts: |
| 836 | |
Simon Glass | 5fd4816 | 2023-08-24 19:39:24 -0600 | [diff] [blame] | 837 | - implement extensions (devicetree overlays with add-on boards) |
Mattijs Korpershoek | b30baa9 | 2024-07-10 10:40:05 +0200 | [diff] [blame] | 838 | - implement legacy (boot image v2) android boot flow |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 839 | |
| 840 | Other ideas: |
| 841 | |
| 842 | - `bootflow prep` to load everything preparing for boot, so that `bootflow boot` |
| 843 | can just do the boot. |
| 844 | - automatically load kernel, FDT, etc. to suitable addresses so the board does |
| 845 | not need to specify things like `pxefile_addr_r` |
| 846 | |
| 847 | |
Paul Barker | 6c55d0d | 2022-07-29 14:31:58 +0100 | [diff] [blame] | 848 | .. _distro_bootcmd: https://github.com/u-boot/u-boot/blob/master/include/config_distro_bootcmd.h |
Simon Glass | 83b9be6 | 2022-04-24 23:31:26 -0600 | [diff] [blame] | 849 | .. _BootLoaderSpec: http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ |
| 850 | .. _distro_boot: https://github.com/u-boot/u-boot/blob/master/boot/distro.c |
| 851 | .. _bootflow_h: https://github.com/u-boot/u-boot/blob/master/include/bootflow.h |
Simon Glass | e27d7df | 2023-09-14 10:55:55 -0600 | [diff] [blame] | 852 | .. _migrate_patch: https://patchwork.ozlabs.org/project/uboot/patch/20230727215433.578830-2-sjg@chromium.org/ |