Skip to content

Commit 103a8cd

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 1df16ed commit 103a8cd

7 files changed

Lines changed: 160 additions & 19 deletions

File tree

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::{
@@ -600,12 +600,6 @@ pub(crate) fn setup_composefs_bls_boot(
600600
}
601601
};
602602

603-
// Remove "root=" from kernel cmdline as systemd-auto-gpt-generator should use DPS
604-
// UUID
605-
if bootloader == Bootloader::Systemd {
606-
cmdline_refs.remove(&ParameterKey::from("root"));
607-
}
608-
609603
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
610604

611605
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
@@ -122,6 +122,13 @@ pub(crate) struct InstallConfiguration {
122122
pub(crate) bootupd: Option<Bootupd>,
123123
/// Bootloader to use (grub, systemd, none)
124124
pub(crate) bootloader: Option<Bootloader>,
125+
/// Use the Discoverable Partitions Specification for root partition
126+
/// discovery. When true, the `root=` kernel argument is omitted
127+
/// and `systemd-gpt-auto-generator` discovers root via its DPS
128+
/// type GUID. Requires the bootloader to implement the Boot Loader
129+
/// Interface (systemd-boot always does, GRUB needs the `bli` module).
130+
/// Defaults to false for broad compatibility.
131+
pub(crate) discoverable_partitions: Option<bool>,
125132
}
126133

127134
fn merge_basic<T>(s: &mut Option<T>, o: Option<T>, _env: &EnvProperties) {
@@ -206,6 +213,11 @@ impl Mergeable for InstallConfiguration {
206213
merge_basic(&mut self.boot_mount_spec, other.boot_mount_spec, env);
207214
self.bootupd.merge(other.bootupd, env);
208215
merge_basic(&mut self.bootloader, other.bootloader, env);
216+
merge_basic(
217+
&mut self.discoverable_partitions,
218+
other.discoverable_partitions,
219+
env,
220+
);
209221
if let Some(other_kargs) = other.kargs {
210222
self.kargs
211223
.get_or_insert_with(Default::default)
@@ -901,3 +913,30 @@ bootloader = "grub"
901913
install.merge(other, &env);
902914
assert_eq!(install.bootloader, Some(Bootloader::None));
903915
}
916+
917+
#[test]
918+
fn test_parse_discoverable_partitions() {
919+
let c: InstallConfigurationToplevel = toml::from_str(
920+
r##"[install]
921+
discoverable-partitions = true
922+
"##,
923+
)
924+
.unwrap();
925+
assert_eq!(c.install.unwrap().discoverable_partitions, Some(true));
926+
927+
let c: InstallConfigurationToplevel = toml::from_str(
928+
r##"[install]
929+
discoverable-partitions = false
930+
"##,
931+
)
932+
.unwrap();
933+
assert_eq!(c.install.unwrap().discoverable_partitions, Some(false));
934+
935+
let c: InstallConfigurationToplevel = toml::from_str(
936+
r##"[install]
937+
root-fs-type = "xfs"
938+
"##,
939+
)
940+
.unwrap();
941+
assert_eq!(c.install.unwrap().discoverable_partitions, None);
942+
}

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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use std assert
2+
use tap.nu
3+
4+
tap begin "DPS root discovery when partition-uuids is false"
5+
6+
# In upgrade scenarios, the system was installed by an older bootc that may not
7+
# have had DPS enabled, so root= would still be in the cmdline.
8+
let is_upgrade = ($env.BOOTC_test_upgrade_image? | default "" | is-not-empty)
9+
if $is_upgrade {
10+
print "# skip: DPS check not applicable for upgrades (installed by older bootc)"
11+
tap ok
12+
exit 0
13+
}
14+
15+
# Parse os-release
16+
let os = open /usr/lib/os-release
17+
| lines
18+
| filter {|l| $l != "" and not ($l | str starts-with "#") }
19+
| parse "{key}={value}"
20+
| reduce -f {} {|it, acc| $acc | upsert $it.key ($it.value | str trim -c '"') }
21+
22+
let os_id = ($os.ID? | default "unknown")
23+
let version_id = ($os.VERSION_ID? | default "0" | into int)
24+
25+
# We inject this in our builds, but hopefully C10S gets this too at some point
26+
if not ($os_id == "fedora" and $version_id >= 43) {
27+
print $"# skip: only applies to Fedora 43+ \(found ($os_id) ($version_id)\)"
28+
tap ok
29+
exit 0
30+
}
31+
32+
print $"Running on ($os_id) ($version_id), checking DPS root discovery"
33+
34+
let cmdline = (open /proc/cmdline)
35+
let has_root_karg = ($cmdline | str contains "root=")
36+
37+
assert (not $has_root_karg) "Fedora 43+ should use DPS auto-discovery (no root= in cmdline)"
38+
39+
tap ok

0 commit comments

Comments
 (0)