diff --git a/README.md b/README.md index 17c71bf..abff499 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,18 @@ We recommend running Sprout without Secure Boot for development, and with Secure ### Current - [x] Loadable driver support -- [x] Basic [Bootloader specification (BLS)](https://uapi-group.org/specifications/specs/boot_loader_specification/) support +- [x] [Bootloader specification (BLS)](https://uapi-group.org/specifications/specs/boot_loader_specification/) support: + Type #1 entries, Type #2 unified kernel images, and the extended boot loader partition +- [x] [UKI support](https://github.com/edera-dev/sprout/issues/6): beta, including images with multiple profiles +- [x] Boot counting and automatic boot assessment +- [x] `loader.conf` support - [x] Chainload support - [x] Linux boot support via EFI stub - [x] Windows boot support via chainload - [x] Load Linux initrd from disk -- [x] Basic boot menu +- [x] Devicetree support +- [x] Basic, simple, and graphical boot menus +- [x] Generators for BLS entries, lists, and matrices, with variants - [x] BLS autoconfiguration support - [x] [Secure Boot support](https://github.com/edera-dev/sprout/issues/20): beta - [x] [Bootloader interface support](https://github.com/edera-dev/sprout/issues/21): beta @@ -83,7 +89,8 @@ We recommend running Sprout without Secure Boot for development, and with Secure ### Roadmap - [ ] [Full-featured boot menu](https://github.com/edera-dev/sprout/issues/1) -- [ ] [UKI support](https://github.com/edera-dev/sprout/issues/6): partial +- [ ] Network boot of unified kernel images (`uki-url`) and devicetree overlays +- [ ] A random seed for the Linux kernel - [ ] [multiboot2 support](https://github.com/edera-dev/sprout/issues/7) - [ ] [Linux boot protocol (boot without EFI stub)](https://github.com/edera-dev/sprout/issues/8) @@ -124,6 +131,12 @@ $ sprout.efi --autoconfigure $ sprout.efi --menu-style=basic # Use the graphical boot menu, which can be used with the mouse. $ sprout.efi --menu-style=graphical +# Show the boot menu for 10 seconds before booting the default entry. +$ sprout.efi --menu-timeout=10 +# Show the boot menu even if an entry was chosen with --boot. +$ sprout.efi --force-menu +# Keep the boot console as it is when an entry is booted. +$ sprout.efi --retain-boot-console ``` ### Boot Linux from ESP @@ -165,6 +178,113 @@ path = "\\sprout\\drivers\\ext4.efi" autoconfigure = true ``` +Sprout reads Type #1 entries from `\loader\entries` and Type #2 unified kernel images (UKIs) from +`\EFI\Linux`, and sorts them as the specification says. A unified kernel image with several profiles +is one entry for each profile. Entries that are not for the architecture of the machine are hidden. +Type #1 entries can use `linux`, `efi`, `uki`, `initrd`, `options`, `devicetree`, `architecture`, +`profile`, `sort-key`, `version`, `machine-id`, and `title`. + +To set up the generator by hand instead of using autoconfiguration, add a generator and an action +that boots the entries. The entry values `$chainload`, `$options`, `$initrd-0` to `$initrd-7`, +`$devicetree`, `$cmdline`, `$title`, `$version`, and `$entry-root` are available. + +```toml +[generators.bls] +# the directory that has the entries directory. this is the default. +bls.path = "\\loader" +# the directory of unified kernel images. by default, this is \EFI\Linux on +# the device of the path. an empty path turns unified kernel images off. +bls.uki-path = "\\EFI\\Linux" +# also read the extended boot loader partition (XBOOTLDR) of the same disk, +# which is sorted with the other entries. its files are on that partition, so +# the action has to use $entry-root, which is empty for Sprout's own partition. +bls.xbootldr = false +# keep the name of the entry file as the name of the entry, so it matches +# the ids that bootctl uses. +bls.pin-names = true +bls.entry.title = "$title" +bls.entry.actions = ["boot-bls"] + +[actions.boot-bls] +chainload.path = "$entry-root\\$chainload" +chainload.options = ["$options"] +chainload.devicetree = "$entry-root\\$devicetree" +chainload.linux-initrd-chain = [ + "$entry-root\\$initrd-0", + "$entry-root\\$initrd-1", +] +``` + +An entry that names a `devicetree` boots with that devicetree installed for the image, which is put +back when the image returns. It is not used when Secure Boot is enabled, as it can't be verified. + +#### Boot counting + +An entry file named like `fedora+3.conf` or `fedora+3.efi` has three tries. Each time Sprout boots it, the +file is renamed, such as to `fedora+2-1.conf`, and the new path is given to the system in +`LoaderBootCountPath` so `systemd-bless-boot` can remove the counter once the boot works. +Entries that have no tries left are sorted last and are not picked as the default entry, but they can still be +booted by hand. If an entry with tries left fails to start, the machine resets, so the next boot can use +the next try or another entry. This is set by `reboot-on-error` in `loader.conf`. + +#### loader.conf + +Sprout reads `\loader\loader.conf` from the partition it was loaded from, as systemd-boot does. + +| Key | Value | +|-------------------|--------------------------------------------------------------------| +| `default` | A pattern for the id of the default entry, or `@saved`. | +| `preferred` | Like `default`, but entries with no boot counter tries are skipped. | +| `timeout` | Seconds, `menu-hidden`, `menu-disabled`, or `menu-force`. | +| `reboot-on-error` | `auto` (the default), `yes`, or `no`. | + +The id of an entry is the name of its file without the boot counter, such as `fedora.conf` or `fedora.efi`. +Patterns ignore case and can use `*`, `?`, and `[a-z]`. With `@saved`, the entry that was booted last is the +default. With `reboot-on-error`, `yes` always resets after an entry fails to start, which can loop forever, +and `auto` only does when a boot counter try was used up and there were tries left. +Other keys are ignored with a warning. + +A hidden menu, from a timeout of zero or `menu-hidden`, still opens when a key is pressed. `menu-disabled` does not. + +#### Bootloader interface + +Sprout uses the same variables as systemd-boot, so `bootctl`, `systemctl reboot --boot-loader-entry`, and +`systemd-bless-boot` can work with it. It publishes `LoaderEntries`, `LoaderEntrySelected`, +`LoaderBootCountPath`, `LoaderFeatures`, and `LoaderInfo`, and reads `LoaderEntryDefault`, +`LoaderEntryPreferred`, `LoaderEntryOneShot`, `LoaderEntryLastBooted`, `LoaderConfigTimeout`, +and `LoaderConfigTimeoutOneShot`. + +The default entry comes from the first of these that matches an entry: + +1. `default-entry` in `sprout.toml` +2. `LoaderEntryPreferred`, then `preferred` in `loader.conf` +3. `LoaderEntryDefault`, then `default` in `loader.conf` + +The menu timeout comes from the first of the one-shot timeout, `--menu-timeout`, `menu-timeout` in +`sprout.toml`, `LoaderConfigTimeout`, and `loader.conf`. + +### Generators + +Generators make entries when Sprout starts. The `matrix` generator makes an entry for every combination of +its values, the `list` generator makes an entry for each item of a list, and the `bls` generator makes +entries from BLS files. Variants multiply the entries of any generator, and `exclude` removes some of them. + +```toml +# makes an entry for each kernel and each console, such as "Boot \vmlinuz (serial)". +[generators.kernels] +matrix.entry.title = "Boot $kernel ($console)" +matrix.entry.actions = ["boot-kernel"] +matrix.values.kernel = ["\\vmlinuz", "\\vmlinuz-lts"] +variants.console = [ + { name = "serial", values.console-options = "console=ttyS0" }, + { name = "graphics", values.console-options = "console=tty0" }, +] + +[actions.boot-kernel] +chainload.path = "$kernel" +chainload.options = ["$console-options"] +``` + [Edera]: https://edera.dev [Development Guide]: ./DEVELOPMENT.md [Contributing Guide]: ./CONTRIBUTING.md