Package: guix-patches;
Reported by: Lilah Tascheter <lilah <at> lunabee.space>
Date: Fri, 15 Aug 2025 20:06:01 UTC
Severity: normal
Tags: patch
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Lilah Tascheter <lilah <at> lunabee.space> To: guix-patches <at> gnu.org Cc: Lilah Tascheter <lilah <at> lunabee.space> Subject: [PATCH] 006-rebootload: Propose GCD006 "Rewrite Bootloader Subsystem" Date: Fri, 15 Aug 2025 15:03:55 -0500
* 006-rebootload.md: New file. --- 006-rebootload.md | 324 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 006-rebootload.md diff --git a/006-rebootload.md b/006-rebootload.md new file mode 100644 index 0000000..5c1788b --- /dev/null +++ b/006-rebootload.md @@ -0,0 +1,324 @@ +title: Bootloader Subsystem Rewrite +id: 006 +status: draft +discussion: https://issues.guix.gnu.org/<number assigned by issue tracker> +authors: Lilah Tascheter +sponsors: <Sponsor Name> +date: <date when the discussion period starts> +SPDX-License-Identifier: CC-BY-SA-4.0 OR GFDL-1.3-no-invariants-or-later +--- + +# Summary + +Guix's bootloader handling was originally designed solely for GRUB but, with +greater adoption and widened system compatibility, this has proved insufficient. +Unfortunately, core aspects of bootloader handling have remained unchanged, +resulting in a system hard to fit new bootloaders into and confusing for +end-users to configure. + +We propose an in-depth rewrite in order to create a more flexible and easier to +understand interface for both end users and bootloader packagers. + +Prior work has been done as patchsets #69343, #73202, and #72457 on Mumi. This +GCD is a new proposal improving upon these patches. + +# Motivation + +We identify three main ways the current bootloader handling proves insufficient: +1. Structural assumptions of GRUB exist in both user-facing bootloader + configuration options as well as the packager-facing bootloader interface. +2. Multiple special cases exist in bootloader packaging and installation, making + it incredibly easy to package bootloaders that either install onto the host + during disk-image building or fail during system rollback. +3. Bootloader packaging is ill-documented and relies on a confusing interface, + which is especially bad for such a sensitive part of system configuration. + +Take, first, the user-facing side of things. 11/14 fields of the +`<bootloader-configuration>` record are only used in GRUB, with multiple having +no imaginable use for non-GRUB bootloaders in the first place. Theoretically, +new bootloaders could add fields to the record if they need their own +configuration, but any external channels would not be able to do even that. + +Disk image handling is not well-specified, especially when attempting to create +a disk image with a custom bootloader. Examples provide `/dev/vda1`, so a user +would assume that Guix looks for that magic string, but instead bootloader +handling uses a special install procedure. Notably, an unused target must be +specified anyway. + +The `<menu-entry>` record is, also, made solely with GRUB in mind, even though +dual-booting is theoretically something that should be supported by bootloaders +in general. Fields are documented with semantics based solely on how GRUB +interprets them. + +More notably, the current state of affairs hampers bootloader packaging +significantly. The `<bootloader>` record contains the following fields: +* `bootloader-name`: containing a symbol supposed to uniquely identify a + bootloader package, for use in rollbacks. Notably, u-boot bootloaders ignore + this requirement, to potentially disasterous consequences. It doesn't help + that the only time one would notice problems with this (for example, a + channel's bootloader defined outside gnu/bootloader, a bootloader defined as a + procedure, or reused `bootloader-name`s) is when you already need to roll + back to a different bootloader. +* `bootloader-package`: This is only useful for switching which GRUB package is + used for GRUB bootloaders. +* `bootloader-configuration-file`: This is the sole mechanism through which + rollbacks function. Few bootloaders utilize config file semantics, making + bootloader packaging a pain of trying to coerce semantics to fit in this rigid + box. This, also, prevents use of non-deterministic config files, such as those + containing signed data for secure boot systems. +* `bootloader-installer` and `bootloader-disk-image-installer`: `installer` is + meant to be used on mounted filesystems. `disk-image-installer` is meant to be + used on devices. `disk-image-installer` is used for disk image building or as + a fallback. Some bootloaders (even GRUB!) need access to both a filesystem and + disk, and the two separate code paths mean development can lead to poorly + tested bootloading code. The arguments to both procedures are, also, poorly + documented (and weird choices in general; root-index is purely used by GRUB). + +# Detailed Design + +## Bootloader Definition +First, we go over the general architecture of bootloader packaging. Each +bootloader will have two things associated to it: a `<bootloader>` record +denoting what functions it provides, and a configuration record unique to each +type of bootloader. The `<bootloader>` record defines functions taking in the +configuration record, as a way for the bootloader system to extract the +information it needs from each bootloader's config. + +Going into specifics, the `<bootloader>` record will be redefined as such: +* `bootloader-predicate`: A predicate denoting whether the configuration is of + the right type, used to verify whether users provided the correct record. +* `bootloader-installers`: A list of `<bootloader-installer>` records. + +`<bootloader-installer>` is a new record intended to split up individual +target installation methods. This is necessary as disk images do not have all +possible targets accessible at once (as the disk is generated strictly after +partitions). The record is defined as such: +* `<bootloader-installer-tag>`: An arbitrary symbol used by the bootloader to + identify this installer and its targets. A `<bootloader>` may not have two + `<bootloader-installers>` with the same tag. +* `<bootloader-installer-type>`: A symbol indicating the sort of values allowed + as targets. Allowed the following values: + - `'file`: Only strings allowed, interpreted as paths. + - `'block`: Strings (interpreted as block devices), `file-system-label`s, and + `uuid`s allowed. `uuid`s are interpreted as filesystem UUIDs, partition + UUIDs, or partition table UUIDs, attempted in that order. +* `<bootloader-installer-targets>`: A function from the bootloader's + configuration to a list of targets specified by the user. Typically this is + just a configuration record field accessor. +* `<bootloader-installer-installer>`: A function from the bootloader's + configuration, an alist of generation numbers to `<boot-parameters>`, the + current generation number, and the boot-time target map (see below), to a gexp + containing an installer procedure as specified below. + +Installers are gexps containing a procedure taking keyword arguments. The +procedure is called once for each target in the `<bootloader-installer>`, and +provided keyword arguments depending on the `<bootloader-installer-type>`. Each +keyword argument has a side, either boot-side or install-side. Install-side data +is only valid during installation of the bootloader, and should not be used +during bootup. This data is generated from system state at install-time. +Boot-side data is usable during bootup, and generated from a given system's +`<file-system>` records. However, if a `'block` type installer is provided a +path to a block device, boot-time data must be generated at install-time. For +this reason, usage of a path `'block` is not recommended. Keyword arguments are +as follows: +* `#:mount` (`'file`, install-time): Absolute path to the mount point of the + filesystem the file is stored on. +* `#:path` (`'file`, install-time): Absolute path to the specified file. +* `#:device` (`'block`, install-time): Device file for the block device. +* `#:offset` (`'file`, boot-time): Filesystem-relative path. Includes the btrfs + subvolume prefix when applicable, so `#:path` may not always be + `string-append`'ed `#:mount` and `#:offset`. +* `#:label` (all, boot-time): Filesystem label. +* `#:uuid` (all, boot-time): Filesystem uuid, unless `'block` and a partition + UUID or partition table UUID is provided. + +In the future, more installer types and keyword arguments may be provided +fully backwards-compatibly. + +The boot-time target map provieded to `bootloader-installers` is an alist of +tags to lists of boot-time data, one for each target. Boot-time data is +exactly the boot-time keyword arguments provided to installer gexp procedures, +but are provided here for certain installers to know about others (for example, +the GRUB early loader installed to a disk knowing about the full GRUB +installation's partition and location). + +## User Configuration +Users will specify the `<bootloader>` they wish to use directly in the +`operating-system-bootloader` field, which will be changed to allow `#f` (for no +bootloaders, as in virtualized images with direct linux booting). +`<operating-system>` will also receive a new +`operating-system-bootloader-configuration` field. This field will accept a +configuration used by the `operating-system-bootloader`, verified by +`bootloader-predicate`. + +All further bootloader configuration, including targets and additional boot +options, will be provided in the `operating-system-bootloader-configuration`. + +The current `<menu-entry>` and `<bootloader-configuration>` records will be +re-adapted into grub-specific configuration. + +## Rollbacks +As the entire bootloader install process exists in gexps, each generation will +simply save its combined bootloader install script as a GC root to be called on +rollback, the exact way it's called on install. This eliminates the need for +bootloader module search and bootloader configuration guessing (as is currently +used), as well as making bootloader rollbacks more reliable. + +Due to this, the following `<boot-parameters>` fields may be removed: +`boot-parameters-root-device`, `boot-parameter-bootloader-name`, and +`boot-parameter-menu-entries`. The record shall be updated to version 2 to +match. + +## Disk Images +While disk images will work automatically with the above system through the +keyword argument discovery mechanisms, one instance of special-casing is +unfortunately required. Any `<bootloader-installer>` with the tag `'disk` +will, instead of using targets from `bootloader-installer-targets`, provide +a single target, generated from the resulting disk image file. + +All other handling will be done invisibly to both end-users and bootloader +packagers, due to the above defined semantics of target conversion into keyword +arguments. + +## Cascading Changes +These changes will necessitate some edits to specific bootloaders and +supporting code. The current GRUB bootloader installer calls `grub-install`, +which guesses a lot of information about the system it's currently run on. This +approach, of course, does not work for disk images, and so manual image creation +is used there. We propose unifying the two approaches, manually creating disk +images with the information given to us by the new targeting system. + +Furthermore, a new utility procedure will be created to facilitate EFI +bootloaders, handling copying to the ESP, setting EFI boot options, +configuring removability, and ensuring sufficient storage space in the ESP. + +## Documentation +A page will be added to the manual under the `Programming Interface` section +describing the new bootloader interfaces. This page will note the default +modules provided to installer gexps and the special-casing of the `'disk` tag. + +The `Bootloader Configuration` page will be updated to act as a unified +documentation of the peculiarities of each bootloader and how to use it. Manual +pages pertaining to bootloader installation will be updated to document the new +user configuration format, and a blog post will be made to ease the transition. + +## Bootloading Team +Lastly, a bootloading team will be created as a point of contact and +implementation group for the above changes. This team would develop the proposed +changes in a public fork of Guix in order to facilitate user testing across the +wide range of supported hardware. Merging with mainline will be done once patch +review and sufficiently diverse testing as to minimize user impact has been completed. + +## Examples +```scheme +(define-record-type* <simple-configuration> + simple-configuration make-simple-configuration simple-configuration? + (copy-to simple-configuration-copy-to)) +(define (simple-bootloader-installer conf params current targets) + #~(lambda* (#:key path #:allow-other-keys) + (copy-file #$(file-append simple-bootloader-pkg "/share/config") path))) +(define simple-bootloader + (bootloader + (predicate simple-configuration?) + (installers (bootloader-installer + (tag 'copy-to) + (type 'file) + (targets (compose list simple-configuration-copy-to)) + (installer simple-bootloader-installer))))) +``` +```scheme +(operating-system + (bootloader simple-bootloader) + (bootloader-configuration (simple-configuration (copy-to "/boot/config")))) +``` + +# Migration +Even though bootloader management may be considered part of the Guix API +surface, we do not attempt to provide a clean migration path. Thus, upon merge +immediate incompatibilities will occur for all channels packaging bootloaders. +This, thankfully, proves to be a very small number. + +For operating system configuration, however, the existing +`<bootloader-configuration>` and `<menu-entry>` structures shall be deprecated. +`<operating-system>`s will go through a pass converting such structures into the +new format, through a `bootloader-migration` field in `<bootloader>` allowed to +be `#f`. This field may contain a function from `<bootloader-configuration>` to +the bootloader's configuration. The fixup procedure will operate as follows, +taking +```scheme +(operating-system + (bootloader (bootloader-configuration + (bootloader test-bootloader)))) +``` +to +```scheme +(operating-system + (bootloader test-bootloader) + (bootloader-configuration ((bootloader-migrate test-bootloader) + (bootloader-configuration + (bootloader test-bootloader))))) +``` + +Target handling would require little to no special-casing in such migration +functions. + +After a year, this deprecation will expire, and the fixup code, old records, and +`bootloader-migration` field may be removed. This way, existing +`<operating-system>`s may be used as-is for the period necessitated by the +Deprecation Policy. + +## Study of Affected Channels +We have taken an informal study of Guix channels as of 2025-08-13 in order to +estimate the impact of breaking API changes. 98 Guix channels were located +from the following roots: +* [whereiseverything channel toys](https://toys.whereis.social/channels) +* [Github awesome-guix](https://github.com/franzos/awesome-guix) +* [crafted-guix](https://codeberg.org/SystemCrafters/crafted-guix), an + additional channel found through internet search. +* [gooy-guix](https://git.squircle.space/gooy-guix.git), mentioned to me by Ada + Avery and posted on guix-devel. + +Of those 98, we were able to locate exactly 4 which add bootloaders, none of +which managed to fully support Guix's existing bootloader handling: +* [rosenthal](https://codeberg.org/hako/Rosenthal), which just packages GRUB + with an overridden package that supports LUKS2. Does not support rollbacks. +* [gooy-guix](https://git.squircle.space/gooy-guix.git), packaging a + systemd-boot EFI bootloader. It is impossible for this bootloader to support + disk images in the current system. +* [waggle](https://git.lunabee.space/waggle/files.html), my channel, which + adds p-boot for the Pinephone and a UKI bootloader, whose roundabout + implementation necessitated by the current system prompted me to begin this + rewrite in the first place. Does not support rollbacks, and I didn't realize + until after I needed one in the first place. It is impossible for these to + support disk images in the current system. +* [sakura](https://g.freya.cat/freya/sakura). The bootloader added is entirely + copied from my channel and the channel's owner has switched to Nix anyway. + +We thus conclude change to be necessary, and the potential impact of breaking +changes minimal. + +# Cost of Reverting +While these changes will drastically affect internals, the vast majority of +users should see a smooth transition. Similar methods to the above mentioned +migration path may be employed if this changeset needs to be reverted, at the +cost of another breaking change to bootloader-packaging channels. + +However, another roadblock in reverting could take place in the removal of boot +parameter options. Thus, we propose postponing the boot parameter changes until +after reverting is precluded as a possibility. Only then will the version be +incremented and unnecessary fields dropped. + +# Drawbacks and Open Issues +As these are bootloader changes, patches will need to be tested carefully in +order to ensure no devices get bricked. And, even then, there could still be a +risk due to how sensitiive bootloader installation and handling can be. This is +the main obstacle for implementation. It may be useful, then, to have a more +concrete procedure set in place to ensure proper testing after patch +development. + +The deprecation process is not ideal, especially with the removal of the +`bootloader-migrate` field resulting in two breaking changes to +bootloader-packaging channels. + +The special-casing of the `'disk` tag is also not ideal. We could not think of +any better method to provide such information. -- 2.50.0
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.