[Concept,02/13] bootmeth: bls: Scan every partition for entries

Message ID 20260507221507.505998-3-sjg@u-boot.org
State New
Headers
Series bootstd: bls: Scan every partition; Ubuntu autoinstall via BLS |

Commit Message

Simon Glass May 7, 2026, 10:14 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

The Boot Loader Specification places loader/entries/*.conf on the ESP
or XBOOTLDR partition, and bootmeth_bls relies on the iterator's
BLS-target carve-out to reach those even when they are not marked
bootable. In practice, however, kernel-install (the systemd helper
that distros such as Ubuntu use for kernel package hooks) writes its
entries to /boot/loader/entries on whichever partition holds /boot,
which is typically the ext4 rootfs.

Set BOOTMETHF_ANY_PART on the BLS bootmeth so the iterator hands it
every partition on each bootdev rather than just bootable and
BLS-target ones, matching bootmeth_android, bootmeth_rauc and
bootmeth_cros. The default filename prefixes ('/' and '/boot/')
already cover both ESP-relative and rootfs-relative entry paths, so
the scan picks up entries on the ESP and on the rootfs.

Visiting every partition means BLS now probes filesystems on raw
data slots too (e.g. ChromeOS kernel partitions, which are a single
LBA), and the ext4 superblock probe at sector 2 trips fs_devread()'s
"Read outside partition" error on those. Filter out partitions below
a small threshold up front so the FS probes only run on partitions
that could plausibly hold one.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 boot/bootmeth_bls.c              | 24 ++++++++++++++++-
 doc/develop/bootstd/bls.rst      | 34 ++++++++++++++++-------
 doc/develop/bootstd/overview.rst | 11 +++++---
 doc/usage/bls.rst                | 46 ++++++++++++++++++++++++++------
 4 files changed, 93 insertions(+), 22 deletions(-)
  

Patch

diff --git a/boot/bootmeth_bls.c b/boot/bootmeth_bls.c
index 85d8dfa91df..6f6d03d286b 100644
--- a/boot/bootmeth_bls.c
+++ b/boot/bootmeth_bls.c
@@ -48,6 +48,13 @@ 
 #define BLS_ENTRIES_DIR		"loader/entries"
 #define BLS_ENTRY_FILE		"loader/entry.conf"
 
+/*
+ * Minimum partition size, in blocks, that is plausibly a filesystem.
+ * Partitions below this are skipped before FS probing to avoid
+ * out-of-bounds superblock reads on raw data slots
+ */
+#define BLS_MIN_PART_BLOCKS	64
+
 /**
  * struct bls_info - context information for BLS getfile callback
  *
@@ -320,6 +327,21 @@  static int bls_read_bootflow(struct udevice *dev, struct bootflow *bflow)
 	prefixes = bootstd_get_prefixes(bootstd);
 	desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL;
 
+	/*
+	 * BOOTMETHF_ANY_PART makes the iterator visit every partition,
+	 * including small non-filesystem ones (e.g. ChromeOS kernel slots).
+	 * Skip partitions too small to hold any filesystem before probing,
+	 * so probe-time reads do not trip the "Read outside partition"
+	 * error path
+	 */
+	if (desc) {
+		struct disk_partition info;
+
+		if (!part_get_info(desc, bflow->part, &info) &&
+		    info.size < BLS_MIN_PART_BLOCKS)
+			return -ENOENT;
+	}
+
 	/* Try each prefix: first scan entries/, then fall back to entry.conf */
 	i = 0;
 	ret = -ENOENT;
@@ -521,7 +543,7 @@  static int bls_bootmeth_bind(struct udevice *dev)
 
 	plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
 		"Boot Loader Specification (BLS) Type #1" : "bls";
-	plat->flags = BOOTMETHF_MULTI;
+	plat->flags = BOOTMETHF_MULTI | BOOTMETHF_ANY_PART;
 
 	return 0;
 }
diff --git a/doc/develop/bootstd/bls.rst b/doc/develop/bootstd/bls.rst
index 327ab7c1c55..37eddd455f3 100644
--- a/doc/develop/bootstd/bls.rst
+++ b/doc/develop/bootstd/bls.rst
@@ -9,15 +9,31 @@  Fedora, RHEL and other distributions.
 
 .. _Boot Loader Specification: https://uapi-group.org/specifications/specs/boot_loader_specification/
 
-The entry file ``loader/entry.conf`` is searched for under each boot prefix
-(``{"/", "/boot"}`` by default). These prefixes can be selected with the
-`filename-prefixes` property in the bootstd device.
-
-The bootmeth sets ``BOOTMETHF_MULTI`` so that each ``.conf`` file in the
-entries directory produces a separate bootflow. The ``bflow->entry`` field
-selects which file to use during scanning.
-
-When invoked on a bootdev, the ``bls_read_bootflow()`` function searches for the
+For each partition the bootmeth tries every boot prefix (``{"/", "/boot/"}``
+by default; selectable via the ``filename-prefixes`` property on the bootstd
+device). Within each prefix it scans ``loader/entries/`` for ``.conf`` files
+first, then falls back to the singular ``loader/entry.conf`` if the directory
+is missing or empty.
+
+The bootmeth sets two flags at bind time:
+
+* ``BOOTMETHF_MULTI`` -- each ``.conf`` file in the entries directory produces
+  a separate bootflow, with ``bflow->entry`` indexing which one is returned.
+* ``BOOTMETHF_ANY_PART`` -- the iterator visits every partition rather than
+  only bootable partitions and the ESP / XBOOTLDR / MBR-0xea targets carved out
+  by ``part_is_bls_target()``. Distros that drive their entries through
+  ``kernel-install`` typically place ``loader/entries/`` on whichever partition
+  holds ``/boot``, which on Debian/Ubuntu layouts is the ext4 rootfs rather
+  than the ESP, and ``BOOTMETHF_ANY_PART`` is what lets BLS reach those.
+
+Because ``BOOTMETHF_ANY_PART`` invites the bootmeth onto every partition,
+``bls_read_bootflow()`` looks the partition up before mounting and returns
+``-ENOENT`` for partitions below ``BLS_MIN_PART_BLOCKS`` blocks. This avoids
+running the ext4 superblock probe (which reads sector 2) on raw single-LBA
+slots like ChromeOS kernel partitions, where the probe would otherwise trip
+``fs_devread()``'s "Read outside partition" error path.
+
+When invoked on a bootdev, the ``bls_read_bootflow()`` function searches for an
 entry file, reads it and passes it to ``bls_parse_entry()`` which processes
 the key-value pairs into a ``struct bls_entry``. The parser uses an enum-based
 token lookup to map field names, with most values pointing directly into the
diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst
index b69156cb51d..89fd77cf695 100644
--- a/doc/develop/bootstd/overview.rst
+++ b/doc/develop/bootstd/overview.rst
@@ -815,10 +815,13 @@  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.
+flag to indicate that they can scan any partition. The ChromiumOS bootmeth
+sets it because it stores a kernel in a raw partition; the BLS bootmeth sets
+it so it can find ``loader/entries/`` on the rootfs partition that distros
+mount at ``/boot`` (in addition to the ESP and XBOOTLDR partitions called out
+by the BLS spec). 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
diff --git a/doc/usage/bls.rst b/doc/usage/bls.rst
index 128092ae25b..8d8e7246243 100644
--- a/doc/usage/bls.rst
+++ b/doc/usage/bls.rst
@@ -13,11 +13,13 @@  Overview
 
 BLS provides a standardised way to describe boot entries. U-Boot's BLS support
 allows it to boot operating systems configured with BLS entries, which is used
-by Fedora, RHEL, and other distributions.
+by Fedora, RHEL, Ubuntu (via ``kernel-install``) and other distributions.
 
-The current implementation supports a single BLS entry file at
-``loader/entry.conf``. Future versions may support multiple entries in
-``loader/entries/``.
+U-Boot scans both the standard ``loader/entries/<machine-id>-<version>.conf``
+layout used by ``kernel-install`` and a single ``loader/entry.conf`` fallback
+under each boot prefix. When an entries directory is present, each ``.conf``
+file becomes a separate bootflow; ``loader/entry.conf`` is consulted only when
+the directory is absent or empty.
 
 Configuration
 -------------
@@ -28,6 +30,30 @@  Enable BLS support with::
 
 This automatically selects ``CONFIG_PXE_UTILS`` for booting.
 
+Discovery
+---------
+
+The BLS bootmeth sets ``BOOTMETHF_ANY_PART``, so the bootflow iterator hands
+it every partition on each bootdev rather than just the bootable partition or
+those typed as ESP / XBOOTLDR / MBR-0xea. This matches the way ``kernel-install``
+places entries: distros that mount the ESP at ``/boot/efi`` keep
+``loader/entries/`` on the ext4 rootfs at ``/boot/loader/entries/``, while
+distros that mount the ESP at ``/boot`` keep them on the ESP at
+``loader/entries/``. Both cases are reached without any board configuration.
+
+For each partition the bootmeth tries each filename prefix configured on the
+bootstd device (``/`` and ``/boot/`` by default; see ``filename-prefixes`` in
+:doc:`/develop/bootstd/overview`) and within each prefix tries the entries
+directory first, then the singular ``entry.conf`` fallback::
+
+    <prefix>loader/entries/*.conf
+    <prefix>loader/entry.conf
+
+Partitions too small to hold any filesystem are filtered out before the
+filesystem probe runs, so raw data partitions (for example ChromeOS kernel
+slots, which are a single LBA) do not produce spurious "Read outside
+partition" probe errors.
+
 BLS Entry Format
 ----------------
 
@@ -128,23 +154,27 @@  BLS boot entries are discovered automatically by standard boot::
     => bootflow select 0
     => bootflow boot
 
-The BLS entry at ``loader/entry.conf`` on any available media is recognised as
-a bootflow.
+Each ``.conf`` file in a discovered ``loader/entries/`` directory, or the
+single ``loader/entry.conf`` fallback, becomes its own bootflow.
 
 Implementation Notes
 --------------------
 
-* Single BLS entry file support (``loader/entry.conf``)
 * Boot execution reuses U-Boot's PXE infrastructure for kernel loading
 * Unknown fields are ignored for forward compatibility
 * The bootmethod is ordered as ``bootmeth_2bls`` (after extlinux)
 * Zero-copy parsing: most fields point into bootflow buffer (except ``options``
   which is allocated for concatenation)
+* ``BOOTMETHF_MULTI`` is set, so each ``.conf`` file in the entries directory
+  produces a separate bootflow
+* ``BOOTMETHF_ANY_PART`` is set, so the iterator visits every partition rather
+  than only those tagged as bootable or as BLS-target
 
 Current Limitations
 -------------------
 
-* Only single entry file, not multiple entries directory scanning
+* Only the first ``.conf`` file in ``loader/entries/`` is loaded per call;
+  the iterator advances ``bflow->entry`` to retrieve subsequent ones
 * No devicetree-overlay support
 * No architecture/machine-id filtering
 * No version-based or sort-key sorting