Skip to content

Commit 80d102d

Browse files
authored
Merge pull request #665 from yoheiueda/zipl
install: Use zipl to install bootloader on s390x
2 parents 8e5d76b + 03812c1 commit 80d102d

File tree

3 files changed

+102
-15
lines changed

3 files changed

+102
-15
lines changed

lib/src/bootloader.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::Result;
1+
use anyhow::{anyhow, bail, Context, Result};
22
use camino::{Utf8Path, Utf8PathBuf};
33
use fn_error_context::context;
44

@@ -51,3 +51,80 @@ pub(crate) fn install_via_bootupd(
5151
.verbose()
5252
.run()
5353
}
54+
55+
#[context("Installing bootloader using zipl")]
56+
pub(crate) fn install_via_zipl(device: &PartitionTable, boot_uuid: &str) -> Result<()> {
57+
// Identify the target boot partition from UUID
58+
let fs = crate::mount::inspect_filesystem_by_uuid(boot_uuid)?;
59+
let boot_dir = Utf8Path::new(&fs.target);
60+
let maj_min = fs.maj_min;
61+
62+
// Ensure that the found partition is a part of the target device
63+
let device_path = device.path();
64+
65+
let partitions = crate::blockdev::list_dev(device_path)?
66+
.children
67+
.with_context(|| format!("no partition found on {device_path}"))?;
68+
let boot_part = partitions
69+
.iter()
70+
.find(|part| part.maj_min.as_deref() == Some(maj_min.as_str()))
71+
.with_context(|| format!("partition device {maj_min} is not on {device_path}"))?;
72+
let boot_part_offset = boot_part.start.unwrap_or(0);
73+
74+
// Find exactly one BLS configuration under /boot/loader/entries
75+
// TODO: utilize the BLS parser in ostree
76+
let bls_dir = boot_dir.join("boot/loader/entries");
77+
let bls_entry = bls_dir
78+
.read_dir_utf8()?
79+
.try_fold(None, |acc, e| -> Result<_> {
80+
let e = e?;
81+
let name = Utf8Path::new(e.file_name());
82+
if let Some("conf") = name.extension() {
83+
if acc.is_some() {
84+
bail!("more than one BLS configurations under {bls_dir}");
85+
}
86+
Ok(Some(e.path().to_owned()))
87+
} else {
88+
Ok(None)
89+
}
90+
})?
91+
.with_context(|| format!("no BLS configuration under {bls_dir}"))?;
92+
93+
let bls_path = bls_dir.join(bls_entry);
94+
let bls_conf =
95+
std::fs::read_to_string(&bls_path).with_context(|| format!("reading {bls_path}"))?;
96+
97+
let mut kernel = None;
98+
let mut initrd = None;
99+
let mut options = None;
100+
101+
for line in bls_conf.lines() {
102+
match line.split_once(char::is_whitespace) {
103+
Some(("linux", val)) => kernel = Some(val.trim().trim_start_matches('/')),
104+
Some(("initrd", val)) => initrd = Some(val.trim().trim_start_matches('/')),
105+
Some(("options", val)) => options = Some(val.trim()),
106+
_ => (),
107+
}
108+
}
109+
110+
let kernel = kernel.ok_or_else(|| anyhow!("missing 'linux' key in default BLS config"))?;
111+
let initrd = initrd.ok_or_else(|| anyhow!("missing 'initrd' key in default BLS config"))?;
112+
let options = options.ok_or_else(|| anyhow!("missing 'options' key in default BLS config"))?;
113+
114+
let image = boot_dir.join(kernel).canonicalize_utf8()?;
115+
let ramdisk = boot_dir.join(initrd).canonicalize_utf8()?;
116+
117+
// Execute the zipl command to install bootloader
118+
let zipl_desc = format!("running zipl to install bootloader on {device_path}");
119+
let zipl_task = Task::new(&zipl_desc, "zipl")
120+
.args(["--target", boot_dir.as_str()])
121+
.args(["--image", image.as_str()])
122+
.args(["--ramdisk", ramdisk.as_str()])
123+
.args(["--parameters", options])
124+
.args(["--targetbase", device_path.as_str()])
125+
.args(["--targettype", "SCSI"])
126+
.args(["--targetblocksize", "512"])
127+
.args(["--targetoffset", &boot_part_offset.to_string()])
128+
.args(["--add-files", "--verbose"]);
129+
zipl_task.verbose().run().context(zipl_desc)
130+
}

lib/src/install.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -573,15 +573,9 @@ async fn initialize_ostree_root_from_self(
573573
crate::lsm::ensure_dir_labeled(rootfs_dir, "boot", None, 0o755.into(), sepolicy)?;
574574
}
575575

576-
// Default to avoiding grub2-mkconfig etc., but we need to use zipl on s390x.
577-
// TODO: Lower this logic into ostree proper.
578-
let bootloader = if cfg!(target_arch = "s390x") {
579-
"zipl"
580-
} else {
581-
"none"
582-
};
583576
for (k, v) in [
584-
("sysroot.bootloader", bootloader),
577+
// Default to avoiding grub2-mkconfig etc.
578+
("sysroot.bootloader", "none"),
585579
// Always flip this one on because we need to support alongside installs
586580
// to systems without a separate boot partition.
587581
("sysroot.bootprefix", "true"),
@@ -1239,12 +1233,17 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
12391233
})
12401234
.context("Writing aleph version")?;
12411235
}
1236+
if cfg!(target_arch = "s390x") {
1237+
// TODO: Integrate s390x support into install_via_bootupd
1238+
crate::bootloader::install_via_zipl(&rootfs.device_info, boot_uuid)?;
1239+
} else {
1240+
crate::bootloader::install_via_bootupd(
1241+
&rootfs.device_info,
1242+
&rootfs.rootfs,
1243+
&state.config_opts,
1244+
)?;
1245+
}
12421246

1243-
crate::bootloader::install_via_bootupd(
1244-
&rootfs.device_info,
1245-
&rootfs.rootfs,
1246-
&state.config_opts,
1247-
)?;
12481247
tracing::debug!("Installed bootloader");
12491248

12501249
// Finalize mounted filesystems
@@ -1688,7 +1687,9 @@ fn test_gather_root_args() {
16881687
// A basic filesystem using a UUID
16891688
let inspect = Filesystem {
16901689
source: "/dev/vda4".into(),
1690+
target: "/".into(),
16911691
fstype: "xfs".into(),
1692+
maj_min: "252:4".into(),
16921693
options: "rw".into(),
16931694
uuid: Some("965eb3c7-5a3f-470d-aaa2-1bcf04334bc6".into()),
16941695
};

lib/src/mount.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use crate::task::Task;
1212
pub(crate) struct Filesystem {
1313
// Note if you add an entry to this list, you need to change the --output invocation below too
1414
pub(crate) source: String,
15+
pub(crate) target: String,
16+
#[serde(rename = "maj:min")]
17+
pub(crate) maj_min: String,
1518
pub(crate) fstype: String,
1619
pub(crate) options: String,
1720
pub(crate) uuid: Option<String>,
@@ -29,7 +32,7 @@ fn run_findmnt(args: &[&str], path: &str) -> Result<Filesystem> {
2932
"-J",
3033
"-v",
3134
// If you change this you probably also want to change the Filesystem struct above
32-
"--output=SOURCE,FSTYPE,OPTIONS,UUID",
35+
"--output=SOURCE,TARGET,MAJ:MIN,FSTYPE,OPTIONS,UUID",
3336
])
3437
.args(args)
3538
.arg(path)
@@ -49,6 +52,12 @@ pub(crate) fn inspect_filesystem(path: &Utf8Path) -> Result<Filesystem> {
4952
run_findmnt(&["--mountpoint"], path.as_str())
5053
}
5154

55+
#[context("Inspecting filesystem by UUID {uuid}")]
56+
/// Inspect a filesystem by partition UUID
57+
pub(crate) fn inspect_filesystem_by_uuid(uuid: &str) -> Result<Filesystem> {
58+
run_findmnt(&["--source"], &(format!("UUID={uuid}")))
59+
}
60+
5261
/// Mount a device to the target path.
5362
pub(crate) fn mount(dev: &str, target: &Utf8Path) -> Result<()> {
5463
Task::new_and_run(

0 commit comments

Comments
 (0)