Skip to content
Merged
134 changes: 131 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, so `systemd-bless-boot` can assess a boot
- [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
Expand All @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -165,6 +178,121 @@ 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"
# an entry can have up to eight initrds. unused ones are skipped.
chainload.linux-initrd-chain = [
"$entry-root\\$initrd-0",
"$entry-root\\$initrd-1",
"$entry-root\\$initrd-2",
"$entry-root\\$initrd-3",
"$entry-root\\$initrd-4",
"$entry-root\\$initrd-5",
"$entry-root\\$initrd-6",
"$entry-root\\$initrd-7",
]
```

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. By default, if an entry fails to start after a try was used up and it had tries left, the machine
resets, so the next boot can use the next try or another entry. This is the `reboot-on-error` setting
in `loader.conf`. Without boot counting, a failure to start returns to the firmware.

#### 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
Expand Down
Loading
Loading