(address . guix-patches@gnu.org)(name . Lilah Tascheter)(address . lilah@lunabee.space)
* 006-rebootload.md: New file.
---
006-rebootload.md | 324 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 324 insertions(+)
create mode 100644 006-rebootload.md
Toggle diff (332 lines)
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