diff --git a/docs/release-notes.md b/docs/release-notes.md index 7696a3f7d..cac10aafb 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ nav_order: 8 Major changes: +- install: Add zVM Secure IPL support Minor changes: diff --git a/scripts/coreos-installer-service b/scripts/coreos-installer-service index 71c636a5c..aaa452f4b 100755 --- a/scripts/coreos-installer-service +++ b/scripts/coreos-installer-service @@ -121,6 +121,11 @@ if karg_bool coreos.inst.insecure; then args+=("--insecure") fi +# zVM Secure IPL support +if karg_bool coreos.inst.secure_ipl; then + args+=("--secure-ipl") +fi + # Always retry HTTP requests; we've got nothing to lose since we fail anyway. args+=("--fetch-retries" "infinite") diff --git a/src/cmdline/install.rs b/src/cmdline/install.rs index 000479f69..5369f9d97 100644 --- a/src/cmdline/install.rs +++ b/src/cmdline/install.rs @@ -252,6 +252,10 @@ pub struct InstallConfig { #[serde(skip_serializing_if = "is_default")] #[arg(long, value_name = "N", default_value_t, help_heading = ADVANCED)] pub fetch_retries: FetchRetries, + /// Enable zVM Secure IPL + #[serde(skip_serializing_if = "is_default")] + #[arg(long, help_heading = ADVANCED)] + pub secure_ipl: bool, // positional args /// Destination device @@ -361,6 +365,7 @@ mod test { stream_base_url: Some(Url::parse("http://example.com/t").unwrap()), preserve_on_error: true, fetch_retries: FetchRetries::from_str("3").unwrap(), + secure_ipl: true, dest_device: Some("u".into()), }; let expected = vec![ @@ -412,6 +417,7 @@ mod test { "--preserve-on-error", "--fetch-retries", "3", + "--secure-ipl", "u", ]; assert_eq!(config.to_args().unwrap(), expected); @@ -484,6 +490,7 @@ dest-device: u stream_base_url: Some(Url::parse("http://example.com/t").unwrap()), preserve_on_error: true, fetch_retries: FetchRetries::from_str("3").unwrap(), + secure_ipl: false, dest_device: Some("u".into()), }; let config = InstallConfig::from_args(&["--config-file", f.path().to_str().unwrap()]) diff --git a/src/install.rs b/src/install.rs index 71c116e75..c2a8cc56b 100644 --- a/src/install.rs +++ b/src/install.rs @@ -454,6 +454,9 @@ fn write_disk( None, )?; s390x::chreipl(device)?; + if config.secure_ipl { + s390x::set_loaddev(device)?; + } } } diff --git a/src/s390x/mod.rs b/src/s390x/mod.rs index 8f39c4c75..7a707637f 100644 --- a/src/s390x/mod.rs +++ b/src/s390x/mod.rs @@ -19,6 +19,7 @@ pub mod zipl; pub use dasd::{dasd_try_get_sector_size, image_copy_s390x, prepare_dasd}; pub use zipl::chreipl; +pub use zipl::set_loaddev; pub use zipl::zipl; mod eckd; mod fba; diff --git a/src/s390x/zipl.rs b/src/s390x/zipl.rs index 4b3d8064e..fc1833981 100644 --- a/src/s390x/zipl.rs +++ b/src/s390x/zipl.rs @@ -14,10 +14,11 @@ use crate::blockdev::Mount; use crate::io::{visit_bls_entry, visit_bls_entry_options, Initrd, KargsEditor}; -use crate::runcmd; use crate::s390x::ZiplSecexMode; use crate::util::cmd_output; -use anyhow::{anyhow, Context, Result}; +use crate::{runcmd, runcmd_output}; +use anyhow::{anyhow, bail, Context, Result}; +use lazy_static::lazy_static; use nix::mount::MsFlags; use regex::Regex; use std::fs::{copy, create_dir_all, read_dir, DirEntry, File}; @@ -33,6 +34,57 @@ pub fn chreipl>(dev: P) -> Result<()> { Ok(()) } +/// Sets zVM secure loaddev to `dev`. +pub fn set_loaddev>(dev: P) -> Result<()> { + eprintln!("Setting LOADDEV"); + let output = runcmd_output!( + "lszdev", + "-n", + "--columns", + "TYPE,ID", + "--by-node", + dev.as_ref() + )?; + let (devtype, id) = output + .trim() + .split_once(' ') + .with_context(|| format!("parsing {output}"))?; + + let mut cmd = Command::new("vmcp"); + cmd.arg("set").arg("loaddev"); + match devtype { + "dasd-eckd" => { + lazy_static! { + static ref REGEX: Regex = + Regex::new(r#"[[:digit:]].[[:digit:]].([[:xdigit:]]+)"#).unwrap(); + } + for cap in REGEX.captures_iter(id) { + cmd.arg("eckd").arg("dev").arg(&cap[1]); + } + } + "zfcp-lun" => { + lazy_static! { + static ref REGEX: Regex = Regex::new( + r#"[[:digit:]].[[:digit:]].([[:xdigit:]]+):0x([[:xdigit:]]+):0x([[:xdigit:]]+)"# + ) + .unwrap(); + } + for cap in REGEX.captures_iter(id) { + cmd.arg("dev") + .arg(&cap[1]) + .arg("portname") + .arg(&cap[2]) + .arg("lun") + .arg(&cap[3]); + } + } + _ => bail!("unsupported device: {} id: {}", devtype, id), + }; + cmd.arg("secure"); + cmd_output(&mut cmd)?; + Ok(()) +} + fn secure_execution_is_enabled() -> Result { let sysfs_flag = "/sys/firmware/uv/prot_virt_guest"; match File::open(sysfs_flag) { diff --git a/systemd/coreos-installer-generator b/systemd/coreos-installer-generator index 8611da24d..ba7e893ce 100755 --- a/systemd/coreos-installer-generator +++ b/systemd/coreos-installer-generator @@ -38,4 +38,9 @@ if [ -n "$(karg coreos.inst.install_dev)" -o \ if ! karg_bool coreos.inst.skip_reboot; then touch /run/coreos-installer-reboot fi + + # Create precondition for coreos-installer-reboot-loaddev.service if requested + if karg_bool coreos.inst.secure_ipl; then + touch /run/coreos-installer-loaddev + fi fi diff --git a/systemd/coreos-installer-post.target b/systemd/coreos-installer-post.target index 8013eb6f8..7773cc134 100644 --- a/systemd/coreos-installer-post.target +++ b/systemd/coreos-installer-post.target @@ -5,3 +5,4 @@ AllowIsolate=yes Requires=coreos-installer.target Requires=coreos-installer-reboot.service Requires=coreos-installer-noreboot.service +Requires=coreos-installer-reboot-loaddev.service diff --git a/systemd/coreos-installer-reboot-loaddev.service b/systemd/coreos-installer-reboot-loaddev.service new file mode 100644 index 000000000..3ff474ba3 --- /dev/null +++ b/systemd/coreos-installer-reboot-loaddev.service @@ -0,0 +1,16 @@ +[Unit] +Description=Reboot from zVM LOADDEV after CoreOS Installer +ConditionVirtualization=zvm +ConditionPathExists=/run/coreos-installer-loaddev + +Requires=coreos-installer.target +After=coreos-installer.target +After=coreos-installer-reboot.service +OnFailure=emergency.target +OnFailureJobMode=replace-irreversibly + +[Service] +Type=simple +ExecStart=/usr/sbin/vmcp ipl loaddev +StandardOutput=kmsg+console +StandardError=kmsg+console