Skip to content

Commit f4cddb4

Browse files
committed
install: Add discoverable-partitions config option
Right now we default to DPS for composefs + systemd-boot. In Fedora 43+, GRUB has the `bli` module and supports this, so it *can* be used there. Make this configurable (mainly intended for base image builders) so that those with new enough GRUB can flip it on by default. We had a hacky thing here that removed the auto-injected `root=` arg if we detected composefs + sdboot; that can now instead flip on this flag, and then we ensure we don't inject it at all. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent d250000 commit f4cddb4

File tree

7 files changed

+151
-22
lines changed

7 files changed

+151
-22
lines changed

contrib/packaging/install-rpm-and-setup

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,16 @@ env DRACUT_NO_XATTR=1 dracut --add bootc -vf /usr/lib/modules/$kver/initramfs.im
2121
# tests to know we're doing upstream CI.
2222
touch /usr/lib/.bootc-dev-stamp
2323

24+
# Fedora 43+ ships a GRUB with the BLI module, so enable DPS
25+
# auto-discovery for root. This must run after our RPM is installed
26+
# since older bootc doesn't recognize the discoverable-partitions key.
27+
. /usr/lib/os-release
28+
if [ "${ID}" = "fedora" ] && [ "${VERSION_ID}" -ge 43 ] 2>/dev/null; then
29+
cat > /usr/lib/bootc/install/20-discoverable-partitions.toml <<'EOF'
30+
[install]
31+
discoverable-partitions = true
32+
EOF
33+
fi
34+
2435
# Workaround for https://github.com/bootc-dev/bootc/issues/1546
2536
rm -rf /root/buildinfo /var/roothome/buildinfo

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use std::io::Write;
6767
use std::path::Path;
6868

6969
use anyhow::{Context, Result, anyhow, bail};
70-
use bootc_kernel_cmdline::utf8::{Cmdline, Parameter, ParameterKey};
70+
use bootc_kernel_cmdline::utf8::{Cmdline, Parameter};
7171
use bootc_mount::tempmount::TempMount;
7272
use camino::{Utf8Path, Utf8PathBuf};
7373
use cap_std_ext::{
@@ -585,12 +585,6 @@ pub(crate) fn setup_composefs_bls_boot(
585585
}
586586
};
587587

588-
// Remove "root=" from kernel cmdline as systemd-auto-gpt-generator should use DPS
589-
// UUID
590-
if bootloader == Bootloader::Systemd {
591-
cmdline_refs.remove(&ParameterKey::from("root"));
592-
}
593-
594588
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
595589

596590
let current_root = if is_upgrade {

crates/lib/src/install/baseline.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,42 @@ use clap::ValueEnum;
2323
use fn_error_context::context;
2424
use serde::{Deserialize, Serialize};
2525

26+
use super::config::Filesystem;
2627
use super::MountSpec;
27-
use super::RUN_BOOTC;
28-
use super::RW_KARG;
2928
use super::RootSetup;
3029
use super::State;
31-
use super::config::Filesystem;
30+
use super::RUN_BOOTC;
31+
use super::RW_KARG;
3232
use crate::task::Task;
3333
use bootc_kernel_cmdline::utf8::Cmdline;
3434
#[cfg(feature = "install-to-disk")]
3535
use bootc_mount::is_mounted_in_pid1_mountns;
3636

37+
/// Check whether DPS auto-discovery is enabled. When `true`,
38+
/// `root=UUID=` is omitted and `systemd-gpt-auto-generator` discovers
39+
/// the root partition via its DPS type GUID instead.
40+
///
41+
/// Defaults to `true` for systemd-boot (which always implements BLI).
42+
/// For GRUB the default is `false` because we cannot know at install
43+
/// time whether the GRUB build includes the `bli` module — the module
44+
/// is baked into the signed EFI binary with no external manifest.
45+
/// Distros shipping a BLI-capable GRUB should set
46+
/// `discoverable-partitions = true` in their install config.
47+
#[cfg(feature = "install-to-disk")]
48+
fn use_discoverable_partitions(state: &State) -> bool {
49+
// Explicit config takes priority
50+
if let Some(ref config) = state.install_config {
51+
if let Some(v) = config.discoverable_partitions {
52+
return v;
53+
}
54+
}
55+
// systemd-boot always supports BLI
56+
matches!(
57+
state.config_opts.bootloader,
58+
Some(crate::spec::Bootloader::Systemd)
59+
)
60+
}
61+
3762
// This ensures we end up under 512 to be small-sized.
3863
pub(crate) const BOOTPN_SIZE_MB: u32 = 510;
3964
pub(crate) const EFIPN_SIZE_MB: u32 = 512;
@@ -415,7 +440,6 @@ pub(crate) fn install_create_rootfs(
415440
opts.wipe,
416441
mkfs_options.iter().copied(),
417442
)?;
418-
let rootarg = format!("root=UUID={root_uuid}");
419443
let bootsrc = boot_uuid.as_ref().map(|uuid| format!("UUID={uuid}"));
420444
let bootarg = bootsrc.as_deref().map(|bootsrc| format!("boot={bootsrc}"));
421445
let boot = bootsrc.map(|bootsrc| MountSpec {
@@ -434,8 +458,16 @@ pub(crate) fn install_create_rootfs(
434458
}
435459
}
436460

437-
// Add root= and rw argument
438-
kargs.extend(&Cmdline::from(format!("{rootarg} {RW_KARG}")));
461+
// When discoverable-partitions is enabled, omit root= so that
462+
// systemd-gpt-auto-generator discovers root by its DPS type GUID.
463+
if use_discoverable_partitions(state) {
464+
println!("Root discovery: DPS auto-discovery (discoverable-partitions = true)");
465+
kargs.extend(&Cmdline::from(RW_KARG));
466+
} else {
467+
let rootarg = format!("root=UUID={root_uuid}");
468+
println!("Root discovery: {rootarg}");
469+
kargs.extend(&Cmdline::from(format!("{rootarg} {RW_KARG}")));
470+
}
439471

440472
// Add boot= argument if present
441473
if let Some(bootarg) = bootarg {

crates/lib/src/install/config.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ pub(crate) struct InstallConfiguration {
119119
pub(crate) bootupd: Option<Bootupd>,
120120
/// Bootloader to use (grub, systemd, none)
121121
pub(crate) bootloader: Option<Bootloader>,
122+
/// Use the Discoverable Partitions Specification for root partition
123+
/// discovery. When true, the `root=` kernel argument is omitted
124+
/// and `systemd-gpt-auto-generator` discovers root via its DPS
125+
/// type GUID. Requires the bootloader to implement the Boot Loader
126+
/// Interface (systemd-boot always does, GRUB needs the `bli` module).
127+
/// Defaults to false for broad compatibility.
128+
pub(crate) discoverable_partitions: Option<bool>,
122129
}
123130

124131
fn merge_basic<T>(s: &mut Option<T>, o: Option<T>, _env: &EnvProperties) {
@@ -203,6 +210,11 @@ impl Mergeable for InstallConfiguration {
203210
merge_basic(&mut self.boot_mount_spec, other.boot_mount_spec, env);
204211
self.bootupd.merge(other.bootupd, env);
205212
merge_basic(&mut self.bootloader, other.bootloader, env);
213+
merge_basic(
214+
&mut self.discoverable_partitions,
215+
other.discoverable_partitions,
216+
env,
217+
);
206218
if let Some(other_kargs) = other.kargs {
207219
self.kargs
208220
.get_or_insert_with(Default::default)
@@ -876,3 +888,30 @@ bootloader = "grub"
876888
install.merge(other, &env);
877889
assert_eq!(install.bootloader, Some(Bootloader::None));
878890
}
891+
892+
#[test]
893+
fn test_parse_discoverable_partitions() {
894+
let c: InstallConfigurationToplevel = toml::from_str(
895+
r##"[install]
896+
discoverable-partitions = true
897+
"##,
898+
)
899+
.unwrap();
900+
assert_eq!(c.install.unwrap().discoverable_partitions, Some(true));
901+
902+
let c: InstallConfigurationToplevel = toml::from_str(
903+
r##"[install]
904+
discoverable-partitions = false
905+
"##,
906+
)
907+
.unwrap();
908+
assert_eq!(c.install.unwrap().discoverable_partitions, Some(false));
909+
910+
let c: InstallConfigurationToplevel = toml::from_str(
911+
r##"[install]
912+
root-fs-type = "xfs"
913+
"##,
914+
)
915+
.unwrap();
916+
assert_eq!(c.install.unwrap().discoverable_partitions, None);
917+
}

docs/src/man/bootc-install-config.5.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ The `install` section supports these subfields:
3333
- `boot-mount-spec`: A string specifying the /boot filesystem mount specification.
3434
If not provided and /boot is a separate mount, its UUID will be used.
3535
An empty string signals to omit boot mount kargs entirely.
36+
- `discoverable-partitions`: Boolean. When `true`, root discovery uses the
37+
Discoverable Partitions Specification via `systemd-gpt-auto-generator` and
38+
the `root=` kernel argument is omitted. This requires the bootloader to
39+
implement the Boot Loader Interface (BLI); systemd-boot always does, GRUB
40+
needs the `bli` module (available in newer builds). Defaults to `true`
41+
when using systemd-boot, `false` otherwise.
3642

3743
# filesystem
3844

@@ -78,6 +84,13 @@ boot-mount-spec = "UUID=abcd-1234"
7884
bls-append-except-default = 'grub_users=""'
7985
```
8086

87+
Enable DPS auto-discovery for root (requires a BLI-capable bootloader):
88+
89+
```toml
90+
[install]
91+
discoverable-partitions = true
92+
```
93+
8194
# SEE ALSO
8295

8396
**bootc(1)**

docs/src/man/bootc-install-to-disk.8.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,25 @@ use `install to-filesystem` if you need precise control over the partition layou
4141

4242
### Root filesystem discovery
4343

44-
Note that by default when used with "type 1" bootloader setups (i.e. non-UKI)
45-
a kernel argument `root=UUID=<uuid of filesystem>` is injected by default.
46-
This provides compatibility with existing initramfs implementations.
47-
48-
When used with the composefs backend and UKIs, it's recommended that
49-
a bootloader implementing the DPS specification is used and that the root
50-
partition is auto-discovered. In this configuration, `systemd-gpt-auto-generator`
51-
in the initramfs will automatically find and mount the root partition based on
52-
its DPS type GUID, without requiring an explicit `root=` kernel argument.
44+
The root partition can be discovered at boot time in two ways:
45+
46+
- **UUID mode** (default): A kernel argument `root=UUID=<uuid>` is
47+
injected, providing broad compatibility with all initramfs
48+
implementations and bootloaders.
49+
50+
- **DPS auto-discovery**: The `root=` kernel argument is omitted
51+
entirely. `systemd-gpt-auto-generator` in the initramfs discovers
52+
the root partition by its DPS type GUID. This enables transparent
53+
block-layer changes (such as adding LUKS encryption) without
54+
updating kernel arguments. DPS auto-discovery requires the
55+
bootloader to implement the Boot Loader Interface (BLI).
56+
systemd-boot always supports this; GRUB supports it only with
57+
newer builds that include the `bli` module.
58+
59+
When using systemd-boot, DPS auto-discovery is enabled by default.
60+
For GRUB, container base images that ship a BLI-capable build should
61+
set `discoverable-partitions = true` in their install configuration
62+
(see **bootc-install-config**(5)).
5363

5464
# OPTIONS
5565

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use std assert
2+
use tap.nu
3+
4+
tap begin "DPS root discovery when partition-uuids is false"
5+
6+
# Parse os-release
7+
let os = open /usr/lib/os-release
8+
| lines
9+
| filter {|l| $l != "" and not ($l | str starts-with "#") }
10+
| parse "{key}={value}"
11+
| reduce -f {} {|it, acc| $acc | upsert $it.key ($it.value | str trim -c '"') }
12+
13+
let os_id = ($os.ID? | default "unknown")
14+
let version_id = ($os.VERSION_ID? | default "0" | into int)
15+
16+
# We inject this in our builds, but hopefully C10S gets this too at some point
17+
if not ($os_id == "fedora" and $version_id >= 43) {
18+
print $"# skip: only applies to Fedora 43+ \(found ($os_id) ($version_id)\)"
19+
tap ok
20+
exit 0
21+
}
22+
23+
print $"Running on ($os_id) ($version_id), checking DPS root discovery"
24+
25+
let cmdline = (open /proc/cmdline)
26+
let has_root_karg = ($cmdline | str contains "root=")
27+
28+
assert (not $has_root_karg) "Fedora 43+ should use DPS auto-discovery (no root= in cmdline)"
29+
30+
tap ok

0 commit comments

Comments
 (0)