Skip to content

Commit cb07c2e

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 0cfbed7 commit cb07c2e

File tree

7 files changed

+151
-19
lines changed

7 files changed

+151
-19
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::{
@@ -582,12 +582,6 @@ pub(crate) fn setup_composefs_bls_boot(
582582
}
583583
};
584584

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

593587
let current_root = if is_upgrade {

crates/lib/src/install/baseline.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,31 @@ 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;
@@ -226,10 +251,15 @@ pub(crate) fn install_create_rootfs(
226251
};
227252
let serial = device.serial.as_deref().unwrap_or("<unknown>");
228253
let model = device.model.as_deref().unwrap_or("<unknown>");
254+
let discoverable = use_discoverable_partitions(state);
229255
println!("Block setup: {block_setup}");
230256
println!(" Size: {}", device.size);
231257
println!(" Serial: {serial}");
232258
println!(" Model: {model}");
259+
println!(
260+
" Partitions: {}",
261+
if discoverable { "Discoverable" } else { "UUID" }
262+
);
233263

234264
let root_size = opts
235265
.root_size
@@ -415,7 +445,6 @@ pub(crate) fn install_create_rootfs(
415445
opts.wipe,
416446
mkfs_options.iter().copied(),
417447
)?;
418-
let rootarg = format!("root=UUID={root_uuid}");
419448
let bootsrc = boot_uuid.as_ref().map(|uuid| format!("UUID={uuid}"));
420449
let bootarg = bootsrc.as_deref().map(|bootsrc| format!("boot={bootsrc}"));
421450
let boot = bootsrc.map(|bootsrc| MountSpec {
@@ -434,8 +463,14 @@ pub(crate) fn install_create_rootfs(
434463
}
435464
}
436465

437-
// Add root= and rw argument
438-
kargs.extend(&Cmdline::from(format!("{rootarg} {RW_KARG}")));
466+
// When discoverable-partitions is enabled, omit root= so that
467+
// systemd-gpt-auto-generator discovers root by its DPS type GUID.
468+
if discoverable {
469+
kargs.extend(&Cmdline::from(RW_KARG));
470+
} else {
471+
let rootarg = format!("root=UUID={root_uuid}");
472+
kargs.extend(&Cmdline::from(format!("{rootarg} {RW_KARG}")));
473+
}
439474

440475
// Add boot= argument if present
441476
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)