The default entry and the menu timeout that bootctl sets in LoaderEntryDefault,
LoaderEntryPreferred and LoaderConfigTimeout now win over default-entry and
menu-timeout in sprout.toml, as they do over loader.conf in systemd-boot.
--menu-timeout and the one-shot timeout still win over everything.
Start QEMU in the background with a QMP socket and console logs, take
screenshots, press keys and wait for serial output, so the graphical boot
menu can be driven without a terminal or a window. boot.sh gets opt-in
knobs for this and behaves as before otherwise.
Only warn about a devicetree under Secure Boot when the entry has one,
skip the fixup if its protocol can't be opened, and reject a devicetree
whose size is smaller than its header. Announce a reboot before the
delay, advertise the multi-profile UKI feature, and treat a Type 1 entry
with a profile after the first as an extra profile that is not picked as
the fallback default. A failure to read Sprout's own partition no longer
drops the entries of the XBOOTLDR partition, and the README lists every
initrd slot and describes the reboot behaviour more exactly.
Update the feature list for what is implemented now, including the
graphical menu and the generators, add the command line options that
were missing, and document the BLS generator options, boot counting,
loader.conf, and the bootloader interface.
Add an xbootldr option to the BLS generator. When it is enabled, the
partition with the XBOOTLDR type on the same disk as Sprout's partition
is read as well, and its entries and unified kernel images are sorted
with the others. As their files are on that partition, every entry has
an entry-root value, which is the device of the partition or nothing for
Sprout's own, to use in a path like $entry-root\$chainload. It is off
by default, as existing actions would look for the files on the wrong
partition.
A unified kernel image can have several profiles, each started by a
.profile section and made of the sections after it on top of the base.
Every profile is an entry. The first has the id of the file, and the
others add @ and the identifier or the number of the profile, with a
title that names the profile and the profile number as the load options.
The profiles share the boot counter of the file, sort in the order they
are in the image, and a profile after the first is not picked as the
default entry when no default was asked for.
The PE reader keeps every section in order, so repeated names and the
sections that a profile removes can be told apart, and reads images with
more than 96 sections.
Add a devicetree option to the chainload action, set from the devicetree
key of a BLS entry. The file is installed as the EFI devicetree table
with the memory type that a devicetree has to be in, patched by the
fixup protocol when the firmware has one, and the table of the firmware
is put back when the image returns or fails to start. It is ignored when
Secure Boot is enabled, as it can't be verified, and the DeviceTree
loader feature is advertised.
Look for a filesystem on the same disk whose GPT partition type is the
XBOOTLDR type, using the partition info protocol. open_shared moves to
the handle module so the graphical menu and the discovery share it.
Add the reboot-on-error loader.conf setting. With auto, which is the
default, the machine resets after a failed start only if a boot counter
try was just used up and there were tries left, so the entry eventually
runs out of tries and the next boot picks another one. yes always resets
and no never does.
Do not pad the boot counter digits or cap the tries done counter, as
systemd-bless-boot reads a counter with a leading zero as octal and
systemd-boot never writes one. A profile only selects a profile when it
is a number above zero, an unterminated bracket in a pattern matches
nothing, and files that start with auto- are ignored in any case and for
unified kernel images too. The saved entry is compared ignoring case, and
entry settings are only expanded for sources that are looked at.
A menu with a zero timeout or menu-hidden now waits 100 ms for a key press
before booting the default entry, which is how the boot loader interface
lets a user interrupt a hidden menu. menu-disabled still skips the wait.
Entries now have an id, the file name without the boot counter and with
its .conf or .efi extension in lower case, which is what systemd-boot
publishes in LoaderEntries and LoaderEntrySelected and what bootctl
expects. LoaderEntryDefault, LoaderEntryOneShot and loader.conf match by
id, ignoring case, with *, ? and [a-z], and no longer match titles or
numbers. A type 1 and a type 2 entry with the same name no longer clash.
@saved now works from LoaderEntryDefault as well, stops saving when it
is not asked for, and removes a stale saved entry. LoaderEntryPreferred
and the loader.conf preferred key select an entry that skips bad boot
counter entries. An entry with no tries left that is booted by hand is
counted too, and a read-only entry file is left alone.
Type 1 entries can name an architecture, which hides them on another
architecture, and a uki with an optional profile. Files that start with
auto- are ignored. loader.conf is read lossily, and a failed measurement
no longer stops the boot.
* feat(bls): read PE sections without loading the whole image
* feat(bls): produce entries from unified kernel images
* feat(bls): boot unified kernel images as type 2 entries
Scan the EFI/Linux directory, or the new uki-path option of the BLS
generator, for .efi files and turn each one into an entry using the
metadata in its PE sections. Autoconfigure now also detects a disk that
only has unified kernel images. An image with the id of a type 1 entry is
skipped, and boot counting works on the .efi file name.
* fix(boot): rename boot counter files in place instead of copying them
* fix(bls): skip hidden files and keep unified kernel images when the type 1 scan fails
Check file name suffixes without slicing by byte offset, drop the PE
section bytes once an image has been parsed, and only fail the generator
on a type 1 error when unified kernel images are disabled.
Entries now sit in a centered panel instead of spanning the screen, and only
the selected entry gets a pill. The logo and name are in a header above the
panel and always show. Long titles end in an ellipsis, and a scrollbar and
position show when the list doesn't fit. Text scale is capped so 1440p
displays aren't dominated by the titles.
The first graphics output may not be a display anyone is looking at. Open
each output that has a device path, and skip the console splitter's output
unless it is the only one. The cursor is shown at the same relative place on
each display, and hovering over an entry on any of them selects it.
An exclusive open makes the firmware disconnect the drivers that use the
protocol, and on some firmware that call never returns. Prefer the pointer
on the console input handle, which belongs to the console splitter.
Read \loader\loader.conf from the partition Sprout was loaded from and
use its default and timeout as the lowest priority source. Sprout now
picks the timeout in this order: LoaderConfigTimeoutOneShot,
--menu-timeout, the configuration, LoaderConfigTimeout, loader.conf. The
default entry order is the configuration, LoaderEntryDefault, loader.conf,
and a source that matches no entry falls through to the next one.
To tell an explicit menu-timeout from the built-in default, the
configuration option is now optional. Existing files are unaffected.
A loader.conf default of @saved selects the entry in
LoaderEntryLastBooted and saves the booted entry there on every boot.
After consuming a boot counter try, publish the renamed entry file in
LoaderBootCountPath and advertise the boot counting and saved entry
features.
* feat(bls): parse and render boot counters
* feat(bls): sort entries with no boot tries left last
* feat(boot): strip boot counters from bls entry ids
* feat(boot): consume bls boot counter tries before booting
* fix(boot): never default to a bad boot counter entry while a good one exists
* feat(menu): add a graphical boot menu
Adds the graphical menu style, selected with menu-style = "graphical" or
--menu-style=graphical. It draws to the graphics output with the existing
framebuffer, using a built-in 8x8 font, and takes input from the keyboard
and from relative or absolute pointers. The Sprout logo is drawn in the
bottom right corner. If there is no graphics output, the simple menu is
used, which still falls back to the basic menu.
The logo is converted from assets/logo-small.png to raw pixels by the build
crate, which adds the png crate as a build-time dependency only.
The default menu style is still simple. Pointer input is untested, as
OVMF has no mouse driver.
* fix(build): only build the logo conversion on the host
CI builds the whole workspace for the UEFI target, which also builds the
build crate for UEFI. The png dependency pulls in simd-adler32, which
crashes LLVM on that target. The logo conversion only runs in build
scripts on the host, so png and the conversion are now left out when
targeting UEFI.
Generators can now declare variant axes. Every generated entry is
multiplied by each combination of choices, one per axis. A choice sets
the axis name to the choice name and inserts its own values, and the
entry name is suffixed with the choice names.
Generators can also declare exclude rules, which remove generated
entries whose values match every glob pattern in a rule. This covers
unwanted variant combinations and entries like BLS rescue kernels.
Entry matching for default-entry and the bootloader interface now
accepts `*` anywhere in the pattern instead of only at the end, so
generated entry names can be matched regardless of the kernel version
in the middle of the name.
The BLS generator gains a pin-names option (default true, the existing
behavior). Two BLS generators reading the same directory previously
produced entries with identical names; disabling pin-names prefixes
them with the generator name instead. Sprout now warns when more than
one entry has the same name.
* docs(config): note that variants suffix pinned BLS entry names
Pinned BLS entry names match the BLS entry ids, but variants still
append their choice names, so those entries need a pattern like
<id>-* to be selected.
The simple menu failed, and boot was aborted, when the console couldn't
set the cursor position or colors or clear the screen, which some
serial consoles and minimal console drivers don't support. Use the
basic menu, which only prints text, when the simple menu fails.
The Ubuntu guide repeated the Step 7 heading, and the Windows guide went
from Step 1 to Step 3. The guides also said to download
sprout-<arch>.efi, then to copy the downloaded sprout.efi file, which
doesn't exist under that name.
The openSUSE guide said most Debian systems use ext4 for /boot, copied
from the Debian guide. openSUSE uses Btrfs by default, so say so and
point to the Btrfs driver, keeping ext4 as the example.
The ARM64 step copied the arm64-efi GRUB modules into
/boot/grub2/x86_64-efi, where GRUB on ARM64 doesn't look for them, so
insmod chain and insmod fat failed.
The unsigned setup guides passed the EFI System Partition device to
efibootmgr -d, which expects the disk, and left out -p, which defaults
to partition 1. The command failed, or created a boot entry for the
wrong partition when the ESP isn't the first one. Pass the disk and the
partition number, as the signed guides do.
GitHub doesn't mark a pull request as a draft when its title starts
with WIP:, so contributors following the guide got a normal pull
request. Say to open a draft pull request instead.
The aarch64 dev VM always used -cpu cortex-a57, even with -accel hvf or
-accel kvm. QEMU rejects named CPU models with hvf, and with KVM unless
the host has that CPU, so booting natively on Apple Silicon or an
aarch64 Linux host with KVM failed. Use -cpu host when accelerated.
On macOS, common.sh always set QEMU_ACCEL to hvf when it was available,
replacing any value the user set, like tcg. Only pick an accelerator
when QEMU_ACCEL isn't set, as the KVM check already does.
format.sh ran shfmt on hack/**/*.sh without enabling globstar, so ** only
matched a single directory. Scripts directly in hack and in deeper
directories like hack/dev/kernel were never formatted.
The dev build passed RUST_TARGET_SUBDIR to the Docker build, but not
RUST_PROFILE, so the Dockerfile always built the release profile and
then copied from the directory of the requested profile. Any profile
other than release, like ./hack/dev/boot.sh x86_64 debug, failed. Pass
the profile as well.