From dadc3c215358af7c847deecacc14789605bc2ca9 Mon Sep 17 00:00:00 2001 From: Sam Povilus Date: Mon, 8 Jul 2024 10:48:11 -0600 Subject: doc: Remove FIT documentation that is elsewhere Before 9d0750064e (doc: Move external FIT docs into the main body), the FIT property data-size was not a mandatory property and still it is not expected to be set alongside the data property. Move the data-size property to the "Conditionally mandatory property" section, where it actually belongs. Signed-off-by: Sam Povilus Reviewed-by: Simon Glass --- doc/usage/fit/index.rst | 4 +- doc/usage/fit/source_file_format.rst | 684 +---------------------------------- 2 files changed, 6 insertions(+), 682 deletions(-) (limited to 'doc/usage') diff --git a/doc/usage/fit/index.rst b/doc/usage/fit/index.rst index bd25bd30b28..68231e93c7c 100644 --- a/doc/usage/fit/index.rst +++ b/doc/usage/fit/index.rst @@ -4,8 +4,8 @@ Flat Image Tree (FIT) ===================== U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging -images that it it reads and boots. Documentation about FIT is available at -doc/uImage.FIT +images that it reads and boots. Documentation about FIT is available in +`the Flattened Image Tree project `_. .. toctree:: :maxdepth: 1 diff --git a/doc/usage/fit/source_file_format.rst b/doc/usage/fit/source_file_format.rst index 15990e3ff54..2bd8e792350 100644 --- a/doc/usage/fit/source_file_format.rst +++ b/doc/usage/fit/source_file_format.rst @@ -1,684 +1,8 @@ -.. SPDX-License-Identifier: GPL-2.0+ +.. SPDX-License-Identifier: GPL-2.0-or-later Flattened Image Tree (FIT) Format ================================= -Introduction ------------- - -The number of elements playing a role in the kernel booting process has -increased over time and now typically includes the devicetree, kernel image and -possibly a ramdisk image. Generally, all must be placed in the system memory and -booted together. - -For firmware images a similar process has taken place, with various binaries -loaded at different addresses, such as ARM's ATF, OpenSBI, FPGA and U-Boot -itself. - -FIT provides a flexible and extensible format to deal with this complexity. It -provides support for multiple components. It also supports multiple -configurations, so that the same FIT can be used to boot multiple boards, with -some components in common (e.g. kernel) and some specific to that board (e.g. -devicetree). - -Terminology -~~~~~~~~~~~ - -This document defines FIT by providing FDT (Flat Device Tree) bindings. These -describe the final form of the FIT at the moment when it is used. The user -perspective may be simpler, as some of the properties (like timestamps and -hashes) are filled in automatically by the U-Boot mkimage tool. - -To avoid confusion with the kernel FDT the following naming convention is used: - -FIT - Flattened Image Tree - -FIT is formally a flattened devicetree (in the libfdt meaning), which conforms -to bindings defined in this document. - -.its - image tree source - -.itb - flattened image tree blob - -Image-building procedure -~~~~~~~~~~~~~~~~~~~~~~~~ - -The following picture shows how the FIT is prepared. Input consists of -image source file (.its) and a set of data files. Image is created with the -help of standard U-Boot mkimage tool which in turn uses dtc (device tree -compiler) to produce image tree blob (.itb). The resulting .itb file is the -actual binary of a new FIT:: - - tqm5200.its - + - vmlinux.bin.gz mkimage + dtc xfer to target - eldk-4.2-ramdisk --------------> tqm5200.itb --------------> boot - tqm5200.dtb /|\ - | - 'new FIT' - -Steps: - -#. Create .its file, automatically filled-in properties are omitted - -#. Call mkimage tool on a .its file - -#. mkimage calls dtc to create .itb image and assures that - missing properties are added - -#. .itb (new FIT) is uploaded onto the target and used therein - - -Unique identifiers -~~~~~~~~~~~~~~~~~~ - -To identify FIT sub-nodes representing images, hashes, configurations (which -are defined in the following sections), the "unit name" of the given sub-node -is used as it's identifier as it assures uniqueness without additional -checking required. - - -External data -~~~~~~~~~~~~~ - -FIT is normally built initially with image data in the 'data' property of each -image node. It is also possible for this data to reside outside the FIT itself. -This allows the 'FDT' part of the FIT to be quite small, so that it can be -loaded and scanned without loading a large amount of data. Then when an image is -needed it can be loaded from an external source. - -External FITs use 'data-offset' or 'data-position' instead of 'data'. - -The mkimage tool can convert a FIT to use external data using the `-E` argument, -optionally using `-p` to specific a fixed position. - -It is often desirable to align each image to a block size or cache-line size -(e.g. 512 bytes), so that there is no need to copy it to an aligned address when -reading the image data. The mkimage tool provides a `-B` argument to support -this. - -Root-node properties --------------------- - -The root node of the FIT should have the following layout:: - - / o image-tree - |- description = "image description" - |- timestamp = <12399321> - |- #address-cells = <1> - | - o images - | | - | o image-1 {...} - | o image-2 {...} - | ... - | - o configurations - |- default = "conf-1" - | - o conf-1 {...} - o conf-2 {...} - ... - -Optional property -~~~~~~~~~~~~~~~~~ - -description - Textual description of the FIT - -Mandatory property -~~~~~~~~~~~~~~~~~~ - -timestamp - Last image modification time being counted in seconds since - 1970-01-01 00:00:00 - to be automatically calculated by mkimage tool. - -Conditionally mandatory property -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#address-cells - Number of 32bit cells required to represent entry and - load addresses supplied within sub-image nodes. May be omitted when no - entry or load addresses are used. - -Mandatory nodes -~~~~~~~~~~~~~~~ - -images - This node contains a set of sub-nodes, each of them representing - single component sub-image (like kernel, ramdisk, etc.). At least one - sub-image is required. - -configurations - Contains a set of available configuration nodes and - defines a default configuration. - - -'/images' node --------------- - -This node is a container node for component sub-image nodes. Each sub-node of -the '/images' node should have the following layout:: - - o image-1 - |- description = "component sub-image description" - |- data = /incbin/("path/to/data/file.bin") - |- type = "sub-image type name" - |- arch = "ARCH name" - |- os = "OS name" - |- compression = "compression name" - |- load = <00000000> - |- entry = <00000000> - | - o hash-1 {...} - o hash-2 {...} - ... - -Mandatory properties -~~~~~~~~~~~~~~~~~~~~ - -description - Textual description of the component sub-image - -type - Name of component sub-image type. Supported types are: - - ==================== ================== - Sub-image type Meaning - ==================== ================== - invalid Invalid Image - aisimage Davinci AIS image - atmelimage ATMEL ROM-Boot Image - copro Coprocessor Image - fdt_legacy legacy Image with Flat Device Tree - filesystem Filesystem Image - firmware Firmware - firmware_ivt Firmware with HABv4 IVT - flat_dt Flat Device Tree - fpga FPGA Device Image (bitstream file, vendor specific) - gpimage TI Keystone SPL Image - imx8image NXP i.MX8 Boot Image - imx8mimage NXP i.MX8M Boot Image - imximage Freescale i.MX Boot Image - kernel Kernel Image - kernel_noload Kernel Image (no loading done) - kwbimage Kirkwood Boot Image - lpc32xximage LPC32XX Boot Image - mtk_image MediaTek BootROM loadable Image - multi Multi-File Image - mxsimage Freescale MXS Boot Image - omapimage TI OMAP SPL With GP CH - pblimage Freescale PBL Boot Image - pmmc TI Power Management Micro-Controller Firmware - ramdisk RAMDisk Image - rkimage Rockchip Boot Image - rksd Rockchip SD Boot Image - rkspi Rockchip SPI Boot Image - script Script - socfpgaimage Altera SoCFPGA CV/AV preloader - socfpgaimage_v1 Altera SoCFPGA A10 preloader - spkgimage Renesas SPKG Image - standalone Standalone Program - stm32image STMicroelectronics STM32 Image - sunxi_egon Allwinner eGON Boot Image - sunxi_toc0 Allwinner TOC0 Boot Image - tee Trusted Execution Environment Image - ublimage Davinci UBL image - vybridimage Vybrid Boot Image - x86_setup x86 setup.bin - zynqimage Xilinx Zynq Boot Image - zynqmpbif Xilinx ZynqMP Boot Image (bif) - zynqmpimage Xilinx ZynqMP Boot Image - ==================== ================== - -compression - Compression used by included data. If no compression is used, the - compression property should be set to "none". If the data is compressed but - it should not be uncompressed by the loader (e.g. compressed ramdisk), this - should also be set to "none". - - Supported compression types are: - - ==================== ================== - Compression type Meaning - ==================== ================== - none uncompressed - bzip2 bzip2 compressed - gzip gzip compressed - lz4 lz4 compressed - lzma lzma compressed - lzo lzo compressed - zstd zstd compressed - ==================== ================== - - -Conditionally mandatory property -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -data - Path to the external file which contains this node's binary data. Within - the FIT this is the contents of the file. This is mandatory unless - external data is used. - -data-offset - Offset of the data in a separate image store. The image store is placed - immediately after the last byte of the device tree binary, aligned to a - 4-byte boundary. This is mandatory if external data is used, with an offset. - -data-position - Machine address at which the data is to be found. This is a fixed address - not relative to the loading of the FIT. This is mandatory if external data - used with a fixed address. - -data-size - Size of the data in bytes. This is mandatory if external data is used. - -os - OS name, mandatory for types "kernel". Valid OS names are: - - ==================== ================== - OS name Meaning - ==================== ================== - invalid Invalid OS - 4_4bsd 4_4BSD - arm-trusted-firmware ARM Trusted Firmware - dell Dell - efi EFI Firmware - esix Esix - freebsd FreeBSD - integrity INTEGRITY - irix Irix - linux Linux - ncr NCR - netbsd NetBSD - openbsd OpenBSD - openrtos OpenRTOS - opensbi RISC-V OpenSBI - ose Enea OSE - plan9 Plan 9 - psos pSOS - qnx QNX - rtems RTEMS - sco SCO - solaris Solaris - svr4 SVR4 - tee Trusted Execution Environment - u-boot U-Boot - vxworks VxWorks - ==================== ================== - -arch - Architecture name, mandatory for types: "standalone", "kernel", - "firmware", "ramdisk" and "fdt". Valid architecture names are: - - ==================== ================== - Architecture type Meaning - ==================== ================== - invalid Invalid ARCH - alpha Alpha - arc ARC - arm64 AArch64 - arm ARM - avr32 AVR32 - blackfin Blackfin - ia64 IA64 - m68k M68K - microblaze MicroBlaze - mips64 MIPS 64 Bit - mips MIPS - nds32 NDS32 - nios2 NIOS II - or1k OpenRISC 1000 - powerpc PowerPC - ppc PowerPC - riscv RISC-V - s390 IBM S390 - sandbox Sandbox - sh SuperH - sparc64 SPARC 64 Bit - sparc SPARC - x86_64 AMD x86_64 - x86 Intel x86 - xtensa Xtensa - ==================== ================== - -entry - entry point address, address size is determined by - '#address-cells' property of the root node. - Mandatory for types: "firmware", and "kernel". - -load - load address, address size is determined by '#address-cells' - property of the root node. - Mandatory for types: "firmware", and "kernel". - -compatible - compatible method for loading image. - Mandatory for types: "fpga", and images that do not specify a load address. - Supported compatible methods: - - ========================== ========================================= - Compatible string Meaning - ========================== ========================================= - u-boot,fpga-legacy Generic fpga loading routine. - u-boot,zynqmp-fpga-ddrauth Signed non-encrypted FPGA bitstream for - Xilinx Zynq UltraScale+ (ZymqMP) device. - u-boot,zynqmp-fpga-enc Encrypted FPGA bitstream for Xilinx Zynq - UltraScale+ (ZynqMP) device. - ========================== ========================================= - -phase - U-Boot phase for which the image is intended. - - "spl" - image is an SPL image - - "u-boot" - image is a U-Boot image - -Optional nodes: - -hash-1 - Each hash sub-node represents separate hash or checksum - calculated for node's data according to specified algorithm. - -signature-1 - Each signature sub-node represents separate signature - calculated for node's data according to specified algorithm. - - -Hash nodes ----------- - -:: - - o hash-1 - |- algo = "hash or checksum algorithm name" - |- value = [hash or checksum value] - -Mandatory properties -~~~~~~~~~~~~~~~~~~~~ - -algo - Algorithm name. Supported algoriths and their value sizes are: - - ==================== ============ ========================================= - Sub-image type Size (bytes) Meaning - ==================== ============ ========================================= - crc16-ccitt 2 Cyclic Redundancy Check 16-bit - (Consultative Committee for International - Telegraphy and Telephony) - crc32 4 Cyclic Redundancy Check 32-bit - md5 16 Message Digest 5 (MD5) - sha1 20 Secure Hash Algorithm 1 (SHA1) - sha256 32 Secure Hash Algorithm 2 (SHA256) - sha384 48 Secure Hash Algorithm 2 (SHA384) - sha512 64 Secure Hash Algorithm 2 (SHA512) - ==================== ============ ========================================= - -value - Actual checksum or hash value. - -Image-signature nodes ---------------------- - -:: - - o signature-1 - |- algo = "algorithm name" - |- key-name-hint = "key name" - |- value = [hash or checksum value] - - -Mandatory properties -~~~~~~~~~~~~~~~~~~~~ - -_`FIT Algorithm`: - -algo - Algorithm name. Supported algoriths and their value sizes are shown below. - Note that the hash is specified separately from the signing algorithm, so - it is possible to mix and match any SHA algorithm with any signing - algorithm. The size of the signature relates to the signing algorithm, not - the hash, since it is the hash that is signed. - - ==================== ============ ========================================= - Sub-image type Size (bytes) Meaning - ==================== ============ ========================================= - sha1,rsa2048 256 SHA1 hash signed with 2048-bit - Rivest–Shamir–Adleman algorithm - sha1,rsa3072 384 SHA1 hash signed with 2048-bit RSA - sha1,rsa4096 512 SHA1 hash signed with 2048-bit RSA - sha1,ecdsa256 32 SHA1 hash signed with 256-bit Elliptic - Curve Digital Signature Algorithm - sha256,... - sha384,... - sha512,... - ==================== ============ ========================================= - -key-name-hint - Name of key to use for signing. The keys will normally be in - a single directory (parameter -k to mkimage). For a given key , its - private key is stored in .key and the certificate is stored in - .crt. - -sign-images - A list of images to sign, each being a property of the conf - node that contains then. The default is "kernel,fdt" which means that these - two images will be looked up in the config and signed if present. This is - used by mkimage to determine which images to sign. - -The following properies are added as part of signing, and are mandatory: - -value - Actual signature value. This is added by mkimage. - -hashed-nodes - A list of nodes which were hashed by the signer. Each is - a string - the full path to node. A typical value might be:: - - hashed-nodes = "/", "/configurations/conf-1", "/images/kernel", - "/images/kernel/hash-1", "/images/fdt-1", - "/images/fdt-1/hash-1"; - -hashed-strings - The start and size of the string region of the FIT that was hashed. The - start is normally 0, indicating the first byte of the string table. The size - indicates the number of bytes hashed as part of signing. - -The following properies are added as part of signing, and are optional: - -timestamp - Time when image was signed (standard Unix time_t format) - -signer-name - Name of the signer (e.g. "mkimage") - -signer-version - Version string of the signer (e.g. "2013.01") - -comment - Additional information about the signer or image - -padding - The padding algorithm, it may be pkcs-1.5 or pss, - if no value is provided we assume pkcs-1.5 - - -'/configurations' node ----------------------- - -The 'configurations' node creates convenient, labeled boot configurations, -which combine together kernel images with their ramdisks and fdt blobs. - -The 'configurations' node has the following structure:: - - o configurations - |- default = "default configuration sub-node unit name" - | - o config-1 {...} - o config-2 {...} - ... - - -Optional property -~~~~~~~~~~~~~~~~~ - -default - Selects one of the configuration sub-nodes as a default configuration. - -Mandatory nodes -~~~~~~~~~~~~~~~ - -configuration-sub-node-unit-name - At least one of the configuration sub-nodes is required. - -Optional nodes -~~~~~~~~~~~~~~ - -signature-1 - Each signature sub-node represents separate signature - calculated for the configuration according to specified algorithm. - - -Configuration nodes -------------------- - -Each configuration has the following structure:: - - o config-1 - |- description = "configuration description" - |- kernel = "kernel sub-node unit name" - |- fdt = "fdt sub-node unit-name" [, "fdt overlay sub-node unit-name", ...] - |- loadables = "loadables sub-node unit-name" - |- script = " - |- compatible = "vendor,board-style device tree compatible string" - o signature-1 {...} - -Mandatory properties -~~~~~~~~~~~~~~~~~~~~ - -description - Textual configuration description. - -kernel or firmware - Unit name of the corresponding kernel or firmware - (u-boot, op-tee, etc) image. If both "kernel" and "firmware" are specified, - control is passed to the firmware image. - -Optional properties -~~~~~~~~~~~~~~~~~~~ - -fdt - Unit name of the corresponding fdt blob (component image node of a - "fdt type"). Additional fdt overlay nodes can be supplied which signify - that the resulting device tree blob is generated by the first base fdt - blob with all subsequent overlays applied. - -fpga - Unit name of the corresponding fpga bitstream blob - (component image node of a "fpga type"). - -loadables - Unit name containing a list of additional binaries to be - loaded at their given locations. "loadables" is a comma-separated list - of strings. U-Boot will load each binary at its given start-address and - may optionally invoke additional post-processing steps on this binary based - on its component image node type. - -script - The image to use when loading a U-Boot script (for use with the - source command). - -compatible - The root compatible string of the U-Boot device tree that - this configuration shall automatically match when CONFIG_FIT_BEST_MATCH is - enabled. If this property is not provided, the compatible string will be - extracted from the fdt blob instead. This is only possible if the fdt is - not compressed, so images with compressed fdts that want to use compatible - string matching must always provide this property. - -The FDT blob is required to properly boot FDT based kernel, so the minimal -configuration for 2.6 FDT kernel is (kernel, fdt) pair. - -Older, 2.4 kernel and 2.6 non-FDT kernel do not use FDT blob, in such cases -'struct bd_info' must be passed instead of FDT blob, thus fdt property *must -not* be specified in a configuration node. - -Configuration-signature nodes ------------------------------ - -:: - - o signature-1 - |- algo = "algorithm name" - |- key-name-hint = "key name" - |- sign-images = "path1", "path2"; - |- value = [hash or checksum value] - |- hashed-strings = <0 len> - - -Mandatory properties -~~~~~~~~~~~~~~~~~~~~ - -algo - See `FIT Algorithm`_. - -key-name-hint - Name of key to use for signing. The keys will normally be in - a single directory (parameter -k to mkimage). For a given key , its - private key is stored in .key and the certificate is stored in - .crt. - -The following properies are added as part of signing, and are mandatory: - -value - Actual signature value. This is added by mkimage. - -The following properies are added as part of signing, and are optional: - -timestamp - Time when image was signed (standard Unix time_t format) - -signer-name - Name of the signer (e.g. "mkimage") - -signer-version - Version string of the signer (e.g. "2013.01") - -comment - Additional information about the signer or image - -padding - The padding algorithm, it may be pkcs-1.5 or pss, - if no value is provided we assume pkcs-1.5 - - - -Examples --------- - -Some example files are available here, showing various scenarios - -.. toctree:: - :maxdepth: 1 - - kernel - kernel_fdt - kernel_fdts_compressed - multi - multi_spl - multi-with-fpga - multi-with-loadables - sec_firmware_ppa - sign-configs - sign-images - uefi - update3 - update_uboot - -.. sectionauthor:: Marian Balakowicz -.. sectionauthor:: External data additions, 25/1/16 Simon Glass +FIT format documentation has been moved to +`a separate project `_. Updates to the +format/specification should be submitted there. -- cgit v1.3.1 From 7aab2b6c1f9cff9d7f1192651ea81b6855b28a33 Mon Sep 17 00:00:00 2001 From: Sam Povilus Date: Mon, 8 Jul 2024 10:48:12 -0600 Subject: doc: add missing table of content links add missing table of content links, make alphabetical Signed-off-by: Sam Povilus Reviewed-by: Simon Glass --- doc/usage/fit/index.rst | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'doc/usage') diff --git a/doc/usage/fit/index.rst b/doc/usage/fit/index.rst index 68231e93c7c..a822bf20cb2 100644 --- a/doc/usage/fit/index.rst +++ b/doc/usage/fit/index.rst @@ -10,10 +10,23 @@ images that it reads and boots. Documentation about FIT is available in .. toctree:: :maxdepth: 1 - source_file_format + beaglebone_vboot howto - x86-fit-boot + kernel_fdt + kernel_fdts_compressed + kernel + multi + multi_spl + multi-with-fpga + multi-with-loadables + overlay-fdt-boot + sec_firmware_ppa signature + sign-configs + sign-images + source_file_format + uefi + update3 + update_uboot verified-boot - beaglebone_vboot - overlay-fdt-boot + x86-fit-boot \ No newline at end of file -- cgit v1.3.1 From c684db989e86cc041da30114c4d7f8d549260875 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 17 Jul 2024 09:30:52 +0100 Subject: doc: Move bootstd into its own directory Before adding more files, move the bootstd docs into a new directory, with an index. Signed-off-by: Simon Glass Reviewed-by: Quentin Schulz Reviewed-by: Mattijs Korpershoek --- MAINTAINERS | 2 +- doc/board/starfive/milk-v_mars_cm.rst | 2 +- doc/develop/board_best_practices.rst | 2 +- doc/develop/bootstd.rst | 808 ---------------------------------- doc/develop/bootstd/index.rst | 9 + doc/develop/bootstd/overview.rst | 808 ++++++++++++++++++++++++++++++++++ doc/develop/index.rst | 2 +- doc/usage/cmd/bootdev.rst | 2 +- doc/usage/cmd/bootflow.rst | 2 +- doc/usage/cmd/bootmeth.rst | 2 +- doc/usage/environment.rst | 2 +- 11 files changed, 825 insertions(+), 816 deletions(-) delete mode 100644 doc/develop/bootstd.rst create mode 100644 doc/develop/bootstd/index.rst create mode 100644 doc/develop/bootstd/overview.rst (limited to 'doc/usage') diff --git a/MAINTAINERS b/MAINTAINERS index f9953944483..a6e47e8a217 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -927,7 +927,7 @@ F: boot/bootmeth*.c F: boot/bootstd.c F: cmd/bootdev.c F: cmd/bootflow.c -F: doc/develop/bootstd.rst +F: doc/develop/bootstd/ F: doc/usage/bootdev.rst F: doc/usage/bootflow.rst F: doc/usage/bootmeth.rst diff --git a/doc/board/starfive/milk-v_mars_cm.rst b/doc/board/starfive/milk-v_mars_cm.rst index b31de6043bb..52d4e5e9098 100644 --- a/doc/board/starfive/milk-v_mars_cm.rst +++ b/doc/board/starfive/milk-v_mars_cm.rst @@ -89,7 +89,7 @@ provide a default value. The variable *$fdtfile* is used in the boot process to automatically load a device-tree provided by the operating system. For details of the boot -process refer to the :doc:`U-Boot Standard Boot <../../../develop/bootstd>` +process refer to the :doc:`/develop/bootstd/index` description. Boot source selection diff --git a/doc/develop/board_best_practices.rst b/doc/develop/board_best_practices.rst index f44401eab7d..09632c80ce7 100644 --- a/doc/develop/board_best_practices.rst +++ b/doc/develop/board_best_practices.rst @@ -7,7 +7,7 @@ In addition to the regular best practices such as using :doc:`checkpatch` and following the :doc:`docstyle` and the :doc:`codingstyle` there are some things which are specific to creating a new board port. -* Implement :doc:`bootstd` to ensure that most operating systems will be +* Implement :doc:`bootstd/index` to ensure that most operating systems will be supported by the platform. * The platform defconfig file must be generated via `make savedefconfig`. diff --git a/doc/develop/bootstd.rst b/doc/develop/bootstd.rst deleted file mode 100644 index bdda90fae3f..00000000000 --- a/doc/develop/bootstd.rst +++ /dev/null @@ -1,808 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+: - -U-Boot Standard Boot -==================== - -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. - - -Available bootmeth drivers --------------------------- - -Bootmeth drivers are provided for: - - - extlinux / syslinux boot from a disk - - extlinux boot from a network (PXE) - - U-Boot scripts from disk, network or SPI flash - - EFI boot using bootefi from disk - - VBE - - EFI boot using boot manager - - Android bootflow (boot image v4) - - -Command interface ------------------ - -Three commands are available: - -`bootdev` - Allows listing of available bootdevs, selecting a particular one and - getting information about it. See :doc:`../usage/cmd/bootdev` - -`bootflow` - Allows scanning one or more bootdevs for bootflows, listing available - bootflows, selecting one, obtaining information about it and booting it. - See :doc:`../usage/cmd/bootflow` - -`bootmeth` - Allow listing of available bootmethds and setting the order in which they - are tried. See :doc:`../usage/cmd/bootmeth` - -.. _BootflowStates: - -Bootflow states ---------------- - -Here is a list of states that a bootflow can be in: - -======= ======================================================================= -State Meaning -======= ======================================================================= -base Starting-out state, indicates that no media/partition was found. For an - SD card socket it may indicate that the card is not inserted. -media Media was found (e.g. SD card is inserted) but no partition information - was found. It might lack a partition table or have a read error. -part Partition was found but a filesystem could not be read. This could be - because the partition does not hold a filesystem or the filesystem is - very corrupted. -fs Filesystem was found but the file could not be read. It could be - missing or in the wrong subdirectory. -file File was found and its size detected, but it could not be read. This - could indicate filesystem corruption. -ready File was loaded and is ready for use. In this state the bootflow is - ready to be booted. -======= ======================================================================= - - -Migrating from distro_boot --------------------------- - -To migrate from distro_boot: - -#. Update your board header files to remove the BOOTENV and BOOT_TARGET_xxx - defines. Standard boot finds available boot devices automatically. - -#. Remove the "boot_targets" variable unless you need it. Standard boot uses a - default order from fastest to slowest, which generally matches the order used - by boards. - -#. Make sure that CONFIG_BOOTSTD_DEFAULTS is enabled by your board, so it can - boot common Linux distributions. - -An example patch is at migrate_patch_. - -If you are using custom boot scripts for your board, consider creating your -own bootmeth to hold the logic. There are various examples at -`boot/bootmeth_...`. - - -Theory of operation -------------------- - -This describes how standard boot progresses through to booting an operating -system. - -To start, all the necessary devices must be bound, including bootstd, which -provides the top-level `struct bootstd_priv` containing optional configuration -information. The bootstd device also holds the various lists used while -scanning. This step is normally handled automatically by driver model, as -described in `Automatic Devices`_. - -Bootdevs are also required, to provide access to the media to use. These are not -useful by themselves: bootmeths are needed to provide the means of scanning -those bootdevs. So, all up, we need a single bootstd device, one or more bootdev -devices and one or more bootmeth devices. - -Once these are ready, typically a `bootflow scan` command is issued. This kicks -off the iteration process, which involves hunting for bootdevs and looking -through the bootdevs and their partitions one by one to find bootflows. - -Iteration is kicked off using `bootflow_scan_first()`. - -The iterator is set up with `bootflow_iter_init()`. This simply creates an -empty one with the given flags. Flags are used to control whether each -iteration is displayed, whether to return iterations even if they did not result -in a valid bootflow, whether to iterate through just a single bootdev, etc. - -Then the iterator is set up to according to the parameters given: - -- When `dev` is provided, then a single bootdev is scanned. In this case, - `BOOTFLOWIF_SKIP_GLOBAL` and `BOOTFLOWIF_SINGLE_DEV` are set. No hunters are - used in this case - -- Otherwise, when `label` is provided, then a single label or named bootdev is - scanned. In this case `BOOTFLOWIF_SKIP_GLOBAL` is set and there are three - options (with an effect on the `iter_incr()` function described later): - - - If `label` indicates a numeric bootdev number (e.g. "2") then - `BOOTFLOW_METHF_SINGLE_DEV` is set. In this case, moving to the next bootdev - simply stops, since there is only one. No hunters are used. - - If `label` indicates a particular media device (e.g. "mmc1") then - `BOOTFLOWIF_SINGLE_MEDIA` is set. In this case, moving to the next bootdev - processes just the children of the media device. Hunters are used, in this - example just the "mmc" hunter. - - If `label` indicates a particular partition in a particular media device - (e.g. "mmc1:3") then `BOOTFLOWIF_SINGLE_PARTITION` is set. In this case, - only a single partition within a bootdev is processed. Hunters are used, in - this example just the "mmc" hunter. - - If `label` indicates a media uclass (e.g. "mmc") then - `BOOTFLOWIF_SINGLE_UCLASS` is set. In this case, all bootdevs in that uclass - are used. Hunters are used, in this example just the "mmc" hunter - -- Otherwise, none of the above flags is set and iteration is set up to work - through `boot_targets` environment variable (or `bootdev-order` device tree - property) in order, running the relevant hunter first. In this case - `cur_label` is used to indicate the label being processed. If there is no list - of labels, then all bootdevs are processed in order of priority, running the - hunters as it goes. - -With the above it is therefore possible to iterate in a variety of ways. - -No attempt is made to determine the ordering of bootdevs, since this cannot be -known in advance if we are using the hunters. Any hunter might discover a new -bootdev and disturb the original ordering. - -Next, the ordering of bootmeths is determined, by `bootmeth_setup_iter_order()`. -By default the ordering is again by sequence number, i.e. the `/aliases` node, -or failing that the order in the Device Tree. But the `bootmeth order` command -or `bootmeths` environment variable can be used to set up an ordering. If that -has been done, the ordering is in `struct bootstd_priv`, so that ordering is -simply copied into the iterator. Either way, the `method_order` array it set up, -along with `num_methods`. - -Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. - -At this point the iterator is ready to use, with the first bootmeth selected. -Most of the other fields are 0. This means that the current partition -is 0, which is taken to mean the whole device, since partition numbers start at -1. It also means that `max_part` is 0, i.e. the maximum partition number we know -about is 0, meaning that, as far as we know, there is no partition table on this -bootdev. - -With the iterator ready, `bootflow_scan_first()` checks whether the current -settings produce a valid bootflow. This is handled by `bootflow_check()`, which -either returns 0 (if it got something) or an error if not (more on that later). -If the `BOOTFLOWIF_ALL` iterator flag is set, even errors are returned as -incomplete bootflows, but normally an error results in moving onto the next -iteration. - -Note that `bootflow_check()` handles global bootmeths explicitly, by calling -`bootmeth_get_bootflow()` on each one. The `doing_global` flag indicates when -the iterator is in that state. - -The `bootflow_scan_next()` function handles moving onto the next iteration and -checking it. In fact it sits in a loop doing that repeatedly until it finds -something it wants to return. - -The actual 'moving on' part is implemented in `iter_incr()`. This is a fairly -simple function. It increments the first counter. If that hits its maximum, it -sets it to zero and increments the second counter. You can think of all the -counters together as a number with three digits which increment in order, with -the least-sigificant digit on the right, counting like this: - - ======== ======= ======= - bootdev part method - ======== ======= ======= - 0 0 0 - 0 0 1 - 0 0 2 - 0 1 0 - 0 1 1 - 0 1 2 - 1 0 0 - 1 0 1 - ... - ======== ======= ======= - -The maximum value for `method` is `num_methods - 1` so when it exceeds that, it -goes back to 0 and the next `part` is considered. The maximum value for that is -`max_part`, which is initially zero for all bootdevs. If we find a partition -table on that bootdev, `max_part` can be updated during the iteration to a -higher value - see `bootdev_find_in_blk()` for that, described later. If that -exceeds its maximum, then the next bootdev is used. In this way, iter_incr() -works its way through all possibilities, moving forward one each time it is -called. - -Note that global bootmeths introduce a subtlety into the above description. -When `doing_global` is true, the iteration takes place only among the bootmeths, -i.e. the last column above. The global bootmeths are at the end of the list. -Assuming that they are entries 3 and 4 in the list, the iteration then looks -like this: - - ======== ======= ======= ======================================= - bootdev part method notes - ======== ======= ======= ======================================= - . . 3 doing_global = true, method_count = 5 - . . 4 - 0 0 0 doing_global = false, method_count = 3 - 0 0 1 - 0 0 2 - 0 1 0 - 0 1 1 - 0 1 2 - 1 0 0 - 1 0 1 - ... - ======== ======= ======= ======================================= - -The changeover of the value of `doing_global` from true to false is handled in -`iter_incr()` as well. - -Note that the value in the `bootdev` column above is not actually stored - it is -just for illustration. In practice, `iter_incr()` uses the flags to determine -whether to move to the next bootdev in the uclass, the next child of the media -device, the next label, or the next priority level, depending on the flag -settings (see `BOOTFLOW_METHF_SINGLE_DEV`, etc. above). - -There is no expectation that iteration will actually finish. Quite often a -valid bootflow is found early on. With `bootflow scan -b`, that causes the -bootflow to be immediately booted. Assuming it is successful, the iteration never -completes. - -Also note that the iterator holds the **current** combination being considered. -So when `iter_incr()` is called, it increments to the next one and returns it, -the new **current** combination. - -Note also the `err` field in `struct bootflow_iter`. This is normally 0 and has -thus no effect on `iter_inc()`. But if it is non-zero, signalling an error, -it indicates to the iterator what it should do when called. It can force moving -to the next partition, or bootdev, for example. The special values -`BF_NO_MORE_PARTS` and `BF_NO_MORE_DEVICES` handle this. When `iter_incr` sees -`BF_NO_MORE_PARTS` it knows that it should immediately move to the next bootdev. -When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do -so it should immediately return. The caller of `iter_incr()` is responsible for -updating the `err` field, based on the return value it sees. - -The above describes the iteration process at a high level. It is basically a -very simple increment function with a checker called `bootflow_check()` that -checks the result of each iteration generated, to determine whether it can -produce a bootflow. - -So what happens inside of `bootflow_check()`? It simply calls the uclass -method `bootdev_get_bootflow()` to ask the bootdev to return a bootflow. It -passes the iterator to the bootdev method, so that function knows what we are -talking about. At first, the bootflow is set up in the state `BOOTFLOWST_BASE`, -with just the `method` and `dev` initialised. But the bootdev may fill in more, -e.g. updating the state, depending on what it finds. For global bootmeths the -`bootmeth_get_bootflow()` function is called instead of -`bootdev_get_bootflow()`. - -Based on what the bootdev or bootmeth responds with, `bootflow_check()` either -returns a valid bootflow, or a partial one with an error. A partial bootflow -is one that has some fields set up, but did not reach the `BOOTFLOWST_READY` -state. As noted before, if the `BOOTFLOWIF_ALL` iterator flag is set, then all -bootflows are returned, even partial ones. This can help with debugging. - -So at this point you can see that total control over whether a bootflow can -be generated from a particular iteration, or not, rests with the bootdev (or -global bootmeth). Each one can adopt its own approach. - -Going down a level, what does the bootdev do in its `get_bootflow()` method? -Let us consider the MMC bootdev. In that case the call to -`bootdev_get_bootflow()` ends up in `default_get_bootflow()`. It locates the -parent device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds -the block device associated with it. It then calls the helper function -`bootdev_find_in_blk()` to do all the work. This is common with just about any -bootdev that is based on a media device. - -The `bootdev_find_in_blk()` helper is implemented in the bootdev uclass. It -names the bootflow and copies the partition number in from the iterator. Then it -calls the bootmeth device to check if it can support this device. This is -important since some bootmeths only work with network devices, for example. If -that check fails, it stops. - -Assuming the bootmeth is happy, or at least indicates that it is willing to try -(by returning 0 from its `check()` method), the next step is to try the -partition. If that works it tries to detect a file system. If that works then it -calls the bootmeth device once more, this time to read the bootflow. - -Note: Normally a filesystem is needed for the bootmeth to be called on block -devices, but bootmeths which don't need that can set the BOOTMETHF_ANY_PART -flag to indicate that they can scan any partition. An example is the ChromiumOS -bootmeth which can store a kernel in a raw partition. Note also that sandbox is -a special case, since in that case the host filesystem can be accessed even -though the block device is NULL. - -If we take the example of the `bootmeth_extlinux` driver, this call ends up at -`extlinux_read_bootflow()`. It has the filesystem ready, so tries various -filenames to try to find the `extlinux.conf` file, reading it if possible. If -all goes well the bootflow ends up in the `BOOTFLOWST_READY` state. - -At this point, we fall back from the bootmeth driver, to -`bootdev_find_in_blk()`, then back to `default_get_bootflow()`, then to -`bootdev_get_bootflow()`, then to `bootflow_check()` and finally to its caller, -either `bootflow_scan_first()` or `bootflow_scan_next()`. In either case, -the bootflow is returned as the result of this iteration, assuming it made it to -the `BOOTFLOWST_READY` state. - -That is the basic operation of scanning for bootflows. The process of booting a -bootflow is handled by the bootmeth driver for that bootflow. In the case of -extlinux boot, this parses and processes the `extlinux.conf` file that was read. -See `extlinux_boot()` for how that works. The processing may involve reading -additional files, which is handled by the `read_file()` method, which is -`extlinux_read_file()` in this case. All bootmeths should support reading -files, since the bootflow is typically only the basic instructions and does not -include the operating system itself, ramdisk, device tree, etc. - -The vast majority of the bootstd code is concerned with iterating through -partitions on bootdevs and using bootmeths to find bootflows. - -How about bootdevs which are not block devices? They are handled by the same -methods as above, but with a different implementation. For example, the bootmeth -for PXE boot (over a network) uses `tftp` to read files rather than `fs_read()`. -But other than that it is very similar. - - -Tests ------ - -Tests are located in `test/boot` and cover the core functionality as well as -the commands. All tests use sandbox so can be run on a standard Linux computer -and in U-Boot's CI. - -For testing, a DOS-formatted disk image is used with a FAT partition on it and -a second unused partition. This is created in `setup_bootflow_image()`, with a -canned one from the source tree used if it cannot be created (e.g. in CI). - - -Bootflow internals ------------------- - -The bootstd device holds a linked list of scanned bootflows as well as the -currently selected bootdev and bootflow (for use by commands). This is in -`struct bootstd_priv`. - -Each bootdev device has its own `struct bootdev_uc_plat` which holds a -list of scanned bootflows just for that device. - -The bootflow itself is documented in bootflow_h_. It includes various bits of -information about the bootflow and a buffer to hold the file. - - -Future ------- - -Apart from the to-do items below, different types of bootflow files may be -implemented in future, e.g. Chromium OS support which is currently only -available as a script in chromebook_coral. - - -To do ------ - -Some things that need to be done to completely replace the distro-boot scripts: - -- implement extensions (devicetree overlays with add-on boards) -- implement legacy (boot image v2) android boot flow - -Other ideas: - -- `bootflow prep` to load everything preparing for boot, so that `bootflow boot` - can just do the boot. -- automatically load kernel, FDT, etc. to suitable addresses so the board does - not need to specify things like `pxefile_addr_r` - - -.. _distro_bootcmd: https://github.com/u-boot/u-boot/blob/master/include/config_distro_bootcmd.h -.. _BootLoaderSpec: http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ -.. _distro_boot: https://github.com/u-boot/u-boot/blob/master/boot/distro.c -.. _bootflow_h: https://github.com/u-boot/u-boot/blob/master/include/bootflow.h -.. _migrate_patch: https://patchwork.ozlabs.org/project/uboot/patch/20230727215433.578830-2-sjg@chromium.org/ diff --git a/doc/develop/bootstd/index.rst b/doc/develop/bootstd/index.rst new file mode 100644 index 00000000000..f4f87c7787c --- /dev/null +++ b/doc/develop/bootstd/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +Standard Boot +============= + +.. toctree:: + :maxdepth: 2 + + overview diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst new file mode 100644 index 00000000000..aa38ad9a0ea --- /dev/null +++ b/doc/develop/bootstd/overview.rst @@ -0,0 +1,808 @@ +.. 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. + + +Available bootmeth drivers +-------------------------- + +Bootmeth drivers are provided for: + + - extlinux / syslinux boot from a disk + - extlinux boot from a network (PXE) + - U-Boot scripts from disk, network or SPI flash + - EFI boot using bootefi from disk + - VBE + - EFI boot using boot manager + - Android bootflow (boot image v4) + + +Command interface +----------------- + +Three commands are available: + +`bootdev` + Allows listing of available bootdevs, selecting a particular one and + getting information about it. See :doc:`/usage/cmd/bootdev` + +`bootflow` + Allows scanning one or more bootdevs for bootflows, listing available + bootflows, selecting one, obtaining information about it and booting it. + See :doc:`/usage/cmd/bootflow` + +`bootmeth` + Allow listing of available bootmethds and setting the order in which they + are tried. See :doc:`/usage/cmd/bootmeth` + +.. _BootflowStates: + +Bootflow states +--------------- + +Here is a list of states that a bootflow can be in: + +======= ======================================================================= +State Meaning +======= ======================================================================= +base Starting-out state, indicates that no media/partition was found. For an + SD card socket it may indicate that the card is not inserted. +media Media was found (e.g. SD card is inserted) but no partition information + was found. It might lack a partition table or have a read error. +part Partition was found but a filesystem could not be read. This could be + because the partition does not hold a filesystem or the filesystem is + very corrupted. +fs Filesystem was found but the file could not be read. It could be + missing or in the wrong subdirectory. +file File was found and its size detected, but it could not be read. This + could indicate filesystem corruption. +ready File was loaded and is ready for use. In this state the bootflow is + ready to be booted. +======= ======================================================================= + + +Migrating from distro_boot +-------------------------- + +To migrate from distro_boot: + +#. Update your board header files to remove the BOOTENV and BOOT_TARGET_xxx + defines. Standard boot finds available boot devices automatically. + +#. Remove the "boot_targets" variable unless you need it. Standard boot uses a + default order from fastest to slowest, which generally matches the order used + by boards. + +#. Make sure that CONFIG_BOOTSTD_DEFAULTS is enabled by your board, so it can + boot common Linux distributions. + +An example patch is at migrate_patch_. + +If you are using custom boot scripts for your board, consider creating your +own bootmeth to hold the logic. There are various examples at +`boot/bootmeth_...`. + + +Theory of operation +------------------- + +This describes how standard boot progresses through to booting an operating +system. + +To start, all the necessary devices must be bound, including bootstd, which +provides the top-level `struct bootstd_priv` containing optional configuration +information. The bootstd device also holds the various lists used while +scanning. This step is normally handled automatically by driver model, as +described in `Automatic Devices`_. + +Bootdevs are also required, to provide access to the media to use. These are not +useful by themselves: bootmeths are needed to provide the means of scanning +those bootdevs. So, all up, we need a single bootstd device, one or more bootdev +devices and one or more bootmeth devices. + +Once these are ready, typically a `bootflow scan` command is issued. This kicks +off the iteration process, which involves hunting for bootdevs and looking +through the bootdevs and their partitions one by one to find bootflows. + +Iteration is kicked off using `bootflow_scan_first()`. + +The iterator is set up with `bootflow_iter_init()`. This simply creates an +empty one with the given flags. Flags are used to control whether each +iteration is displayed, whether to return iterations even if they did not result +in a valid bootflow, whether to iterate through just a single bootdev, etc. + +Then the iterator is set up to according to the parameters given: + +- When `dev` is provided, then a single bootdev is scanned. In this case, + `BOOTFLOWIF_SKIP_GLOBAL` and `BOOTFLOWIF_SINGLE_DEV` are set. No hunters are + used in this case + +- Otherwise, when `label` is provided, then a single label or named bootdev is + scanned. In this case `BOOTFLOWIF_SKIP_GLOBAL` is set and there are three + options (with an effect on the `iter_incr()` function described later): + + - If `label` indicates a numeric bootdev number (e.g. "2") then + `BOOTFLOW_METHF_SINGLE_DEV` is set. In this case, moving to the next bootdev + simply stops, since there is only one. No hunters are used. + - If `label` indicates a particular media device (e.g. "mmc1") then + `BOOTFLOWIF_SINGLE_MEDIA` is set. In this case, moving to the next bootdev + processes just the children of the media device. Hunters are used, in this + example just the "mmc" hunter. + - If `label` indicates a particular partition in a particular media device + (e.g. "mmc1:3") then `BOOTFLOWIF_SINGLE_PARTITION` is set. In this case, + only a single partition within a bootdev is processed. Hunters are used, in + this example just the "mmc" hunter. + - If `label` indicates a media uclass (e.g. "mmc") then + `BOOTFLOWIF_SINGLE_UCLASS` is set. In this case, all bootdevs in that uclass + are used. Hunters are used, in this example just the "mmc" hunter + +- Otherwise, none of the above flags is set and iteration is set up to work + through `boot_targets` environment variable (or `bootdev-order` device tree + property) in order, running the relevant hunter first. In this case + `cur_label` is used to indicate the label being processed. If there is no list + of labels, then all bootdevs are processed in order of priority, running the + hunters as it goes. + +With the above it is therefore possible to iterate in a variety of ways. + +No attempt is made to determine the ordering of bootdevs, since this cannot be +known in advance if we are using the hunters. Any hunter might discover a new +bootdev and disturb the original ordering. + +Next, the ordering of bootmeths is determined, by `bootmeth_setup_iter_order()`. +By default the ordering is again by sequence number, i.e. the `/aliases` node, +or failing that the order in the Device Tree. But the `bootmeth order` command +or `bootmeths` environment variable can be used to set up an ordering. If that +has been done, the ordering is in `struct bootstd_priv`, so that ordering is +simply copied into the iterator. Either way, the `method_order` array it set up, +along with `num_methods`. + +Note that global bootmeths are always put at the end of the ordering. If any are +present, `cur_method` is set to the first one, so that global bootmeths are done +first. Once all have been used, these bootmeths are dropped from the iteration. +When there are no global bootmeths, `cur_method` is set to 0. + +At this point the iterator is ready to use, with the first bootmeth selected. +Most of the other fields are 0. This means that the current partition +is 0, which is taken to mean the whole device, since partition numbers start at +1. It also means that `max_part` is 0, i.e. the maximum partition number we know +about is 0, meaning that, as far as we know, there is no partition table on this +bootdev. + +With the iterator ready, `bootflow_scan_first()` checks whether the current +settings produce a valid bootflow. This is handled by `bootflow_check()`, which +either returns 0 (if it got something) or an error if not (more on that later). +If the `BOOTFLOWIF_ALL` iterator flag is set, even errors are returned as +incomplete bootflows, but normally an error results in moving onto the next +iteration. + +Note that `bootflow_check()` handles global bootmeths explicitly, by calling +`bootmeth_get_bootflow()` on each one. The `doing_global` flag indicates when +the iterator is in that state. + +The `bootflow_scan_next()` function handles moving onto the next iteration and +checking it. In fact it sits in a loop doing that repeatedly until it finds +something it wants to return. + +The actual 'moving on' part is implemented in `iter_incr()`. This is a fairly +simple function. It increments the first counter. If that hits its maximum, it +sets it to zero and increments the second counter. You can think of all the +counters together as a number with three digits which increment in order, with +the least-sigificant digit on the right, counting like this: + + ======== ======= ======= + bootdev part method + ======== ======= ======= + 0 0 0 + 0 0 1 + 0 0 2 + 0 1 0 + 0 1 1 + 0 1 2 + 1 0 0 + 1 0 1 + ... + ======== ======= ======= + +The maximum value for `method` is `num_methods - 1` so when it exceeds that, it +goes back to 0 and the next `part` is considered. The maximum value for that is +`max_part`, which is initially zero for all bootdevs. If we find a partition +table on that bootdev, `max_part` can be updated during the iteration to a +higher value - see `bootdev_find_in_blk()` for that, described later. If that +exceeds its maximum, then the next bootdev is used. In this way, iter_incr() +works its way through all possibilities, moving forward one each time it is +called. + +Note that global bootmeths introduce a subtlety into the above description. +When `doing_global` is true, the iteration takes place only among the bootmeths, +i.e. the last column above. The global bootmeths are at the end of the list. +Assuming that they are entries 3 and 4 in the list, the iteration then looks +like this: + + ======== ======= ======= ======================================= + bootdev part method notes + ======== ======= ======= ======================================= + . . 3 doing_global = true, method_count = 5 + . . 4 + 0 0 0 doing_global = false, method_count = 3 + 0 0 1 + 0 0 2 + 0 1 0 + 0 1 1 + 0 1 2 + 1 0 0 + 1 0 1 + ... + ======== ======= ======= ======================================= + +The changeover of the value of `doing_global` from true to false is handled in +`iter_incr()` as well. + +Note that the value in the `bootdev` column above is not actually stored - it is +just for illustration. In practice, `iter_incr()` uses the flags to determine +whether to move to the next bootdev in the uclass, the next child of the media +device, the next label, or the next priority level, depending on the flag +settings (see `BOOTFLOW_METHF_SINGLE_DEV`, etc. above). + +There is no expectation that iteration will actually finish. Quite often a +valid bootflow is found early on. With `bootflow scan -b`, that causes the +bootflow to be immediately booted. Assuming it is successful, the iteration never +completes. + +Also note that the iterator holds the **current** combination being considered. +So when `iter_incr()` is called, it increments to the next one and returns it, +the new **current** combination. + +Note also the `err` field in `struct bootflow_iter`. This is normally 0 and has +thus no effect on `iter_inc()`. But if it is non-zero, signalling an error, +it indicates to the iterator what it should do when called. It can force moving +to the next partition, or bootdev, for example. The special values +`BF_NO_MORE_PARTS` and `BF_NO_MORE_DEVICES` handle this. When `iter_incr` sees +`BF_NO_MORE_PARTS` it knows that it should immediately move to the next bootdev. +When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do +so it should immediately return. The caller of `iter_incr()` is responsible for +updating the `err` field, based on the return value it sees. + +The above describes the iteration process at a high level. It is basically a +very simple increment function with a checker called `bootflow_check()` that +checks the result of each iteration generated, to determine whether it can +produce a bootflow. + +So what happens inside of `bootflow_check()`? It simply calls the uclass +method `bootdev_get_bootflow()` to ask the bootdev to return a bootflow. It +passes the iterator to the bootdev method, so that function knows what we are +talking about. At first, the bootflow is set up in the state `BOOTFLOWST_BASE`, +with just the `method` and `dev` initialised. But the bootdev may fill in more, +e.g. updating the state, depending on what it finds. For global bootmeths the +`bootmeth_get_bootflow()` function is called instead of +`bootdev_get_bootflow()`. + +Based on what the bootdev or bootmeth responds with, `bootflow_check()` either +returns a valid bootflow, or a partial one with an error. A partial bootflow +is one that has some fields set up, but did not reach the `BOOTFLOWST_READY` +state. As noted before, if the `BOOTFLOWIF_ALL` iterator flag is set, then all +bootflows are returned, even partial ones. This can help with debugging. + +So at this point you can see that total control over whether a bootflow can +be generated from a particular iteration, or not, rests with the bootdev (or +global bootmeth). Each one can adopt its own approach. + +Going down a level, what does the bootdev do in its `get_bootflow()` method? +Let us consider the MMC bootdev. In that case the call to +`bootdev_get_bootflow()` ends up in `default_get_bootflow()`. It locates the +parent device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds +the block device associated with it. It then calls the helper function +`bootdev_find_in_blk()` to do all the work. This is common with just about any +bootdev that is based on a media device. + +The `bootdev_find_in_blk()` helper is implemented in the bootdev uclass. It +names the bootflow and copies the partition number in from the iterator. Then it +calls the bootmeth device to check if it can support this device. This is +important since some bootmeths only work with network devices, for example. If +that check fails, it stops. + +Assuming the bootmeth is happy, or at least indicates that it is willing to try +(by returning 0 from its `check()` method), the next step is to try the +partition. If that works it tries to detect a file system. If that works then it +calls the bootmeth device once more, this time to read the bootflow. + +Note: Normally a filesystem is needed for the bootmeth to be called on block +devices, but bootmeths which don't need that can set the BOOTMETHF_ANY_PART +flag to indicate that they can scan any partition. An example is the ChromiumOS +bootmeth which can store a kernel in a raw partition. Note also that sandbox is +a special case, since in that case the host filesystem can be accessed even +though the block device is NULL. + +If we take the example of the `bootmeth_extlinux` driver, this call ends up at +`extlinux_read_bootflow()`. It has the filesystem ready, so tries various +filenames to try to find the `extlinux.conf` file, reading it if possible. If +all goes well the bootflow ends up in the `BOOTFLOWST_READY` state. + +At this point, we fall back from the bootmeth driver, to +`bootdev_find_in_blk()`, then back to `default_get_bootflow()`, then to +`bootdev_get_bootflow()`, then to `bootflow_check()` and finally to its caller, +either `bootflow_scan_first()` or `bootflow_scan_next()`. In either case, +the bootflow is returned as the result of this iteration, assuming it made it to +the `BOOTFLOWST_READY` state. + +That is the basic operation of scanning for bootflows. The process of booting a +bootflow is handled by the bootmeth driver for that bootflow. In the case of +extlinux boot, this parses and processes the `extlinux.conf` file that was read. +See `extlinux_boot()` for how that works. The processing may involve reading +additional files, which is handled by the `read_file()` method, which is +`extlinux_read_file()` in this case. All bootmeths should support reading +files, since the bootflow is typically only the basic instructions and does not +include the operating system itself, ramdisk, device tree, etc. + +The vast majority of the bootstd code is concerned with iterating through +partitions on bootdevs and using bootmeths to find bootflows. + +How about bootdevs which are not block devices? They are handled by the same +methods as above, but with a different implementation. For example, the bootmeth +for PXE boot (over a network) uses `tftp` to read files rather than `fs_read()`. +But other than that it is very similar. + + +Tests +----- + +Tests are located in `test/boot` and cover the core functionality as well as +the commands. All tests use sandbox so can be run on a standard Linux computer +and in U-Boot's CI. + +For testing, a DOS-formatted disk image is used with a FAT partition on it and +a second unused partition. This is created in `setup_bootflow_image()`, with a +canned one from the source tree used if it cannot be created (e.g. in CI). + + +Bootflow internals +------------------ + +The bootstd device holds a linked list of scanned bootflows as well as the +currently selected bootdev and bootflow (for use by commands). This is in +`struct bootstd_priv`. + +Each bootdev device has its own `struct bootdev_uc_plat` which holds a +list of scanned bootflows just for that device. + +The bootflow itself is documented in bootflow_h_. It includes various bits of +information about the bootflow and a buffer to hold the file. + + +Future +------ + +Apart from the to-do items below, different types of bootflow files may be +implemented in future, e.g. Chromium OS support which is currently only +available as a script in chromebook_coral. + + +To do +----- + +Some things that need to be done to completely replace the distro-boot scripts: + +- implement extensions (devicetree overlays with add-on boards) +- implement legacy (boot image v2) android boot flow + +Other ideas: + +- `bootflow prep` to load everything preparing for boot, so that `bootflow boot` + can just do the boot. +- automatically load kernel, FDT, etc. to suitable addresses so the board does + not need to specify things like `pxefile_addr_r` + + +.. _distro_bootcmd: https://github.com/u-boot/u-boot/blob/master/include/config_distro_bootcmd.h +.. _BootLoaderSpec: http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ +.. _distro_boot: https://github.com/u-boot/u-boot/blob/master/boot/distro.c +.. _bootflow_h: https://github.com/u-boot/u-boot/blob/master/include/bootflow.h +.. _migrate_patch: https://patchwork.ozlabs.org/project/uboot/patch/20230727215433.578830-2-sjg@chromium.org/ diff --git a/doc/develop/index.rst b/doc/develop/index.rst index f9c4bf839ee..c0107a783fc 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -29,7 +29,7 @@ Implementation directories bloblist - bootstd + bootstd/index ci_testing commands config_binding diff --git a/doc/usage/cmd/bootdev.rst b/doc/usage/cmd/bootdev.rst index f759abab354..98a0f43c580 100644 --- a/doc/usage/cmd/bootdev.rst +++ b/doc/usage/cmd/bootdev.rst @@ -22,7 +22,7 @@ Description The `bootdev` command is used to manage bootdevs. It can list available bootdevs, select one and obtain information about it. -See :doc:`../../develop/bootstd` for more information about bootdevs in general. +See :doc:`/develop/bootstd/index` for more information about bootdevs in general. bootdev list diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index 6519e4880a9..5d41fe37a7a 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -26,7 +26,7 @@ Description The `bootflow` command is used to manage bootflows. It can scan bootdevs to locate bootflows, list them and boot them. -See :doc:`../../develop/bootstd` for more information. +See :doc:`/develop/bootstd/index` for more information. Note that `CONFIG_BOOTSTD_FULL` (which enables `CONFIG_CMD_BOOTFLOW_FULL) must be enabled to obtain full functionality with this command. Otherwise, it only diff --git a/doc/usage/cmd/bootmeth.rst b/doc/usage/cmd/bootmeth.rst index bac9fdf85cd..c3d2ec1574b 100644 --- a/doc/usage/cmd/bootmeth.rst +++ b/doc/usage/cmd/bootmeth.rst @@ -21,7 +21,7 @@ Description The `bootmeth` command is used to manage bootmeths. It can list them and change the order in which they are used. -See :doc:`../../develop/bootstd` for more information. +See :doc:`/develop/bootstd/index` for more information. .. _bootmeth_order: diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst index 7d4b448cb30..cc33d3ec0f2 100644 --- a/doc/usage/environment.rst +++ b/doc/usage/environment.rst @@ -100,7 +100,7 @@ to add environment variables. Board maintainers are encouraged to migrate to the text-based environment as it is easier to maintain. The distro-board script still requires the old-style -environments, so use :doc:`../develop/bootstd` instead. +environments, so use :doc:`/develop/bootstd/index` instead. List of environment variables -- cgit v1.3.1