Skip to content

Commit

Permalink
s390x: add zVM Secure IPL support
Browse files Browse the repository at this point in the history
  • Loading branch information
nikita-dubrovskii committed Dec 4, 2023
1 parent 77416a8 commit dc4b19d
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 6 deletions.
2 changes: 2 additions & 0 deletions data/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ stream-base-url: URL
preserve-on-error: true
# Fetch retries, or string "infinite"
fetch-retries: N
# Enable IBM Secure IPL
secure-ipl: true
# Destination device
dest-device: path
3 changes: 3 additions & 0 deletions docs/cmd/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,7 @@ Advanced Options:
indefinitely.
[default: 0]
--secure-ipl
Enable IBM Secure IPL
```
2 changes: 2 additions & 0 deletions docs/customizing-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ stream-base-url: URL
preserve-on-error: true
# Fetch retries, or string "infinite"
fetch-retries: N
# Enable IBM Secure IPL
secure-ipl: true
# Destination device
dest-device: path
```
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ nav_order: 8

Major changes:

- install: Add z/VM Secure IPL support

Minor changes:

Expand Down
5 changes: 4 additions & 1 deletion man/coreos-installer-install.8
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
.SH NAME
coreos\-installer\-install \- Install Fedora CoreOS or RHEL CoreOS
.SH SYNOPSIS
\fBcoreos\-installer\-install\fR [\fB\-c\fR|\fB\-\-config\-file\fR] [\fB\-s\fR|\fB\-\-stream\fR] [\fB\-u\fR|\fB\-\-image\-url\fR] [\fB\-f\fR|\fB\-\-image\-file\fR] [\fB\-i\fR|\fB\-\-ignition\-file\fR] [\fB\-I\fR|\fB\-\-ignition\-url\fR] [\fB\-\-ignition\-hash\fR] [\fB\-a\fR|\fB\-\-architecture\fR] [\fB\-p\fR|\fB\-\-platform\fR] [\fB\-\-console\fR] [\fB\-\-append\-karg\fR] [\fB\-\-delete\-karg\fR] [\fB\-n\fR|\fB\-\-copy\-network\fR] [\fB\-\-network\-dir\fR] [\fB\-\-save\-partlabel\fR] [\fB\-\-save\-partindex\fR] [\fB\-\-offline\fR] [\fB\-\-insecure\fR] [\fB\-\-insecure\-ignition\fR] [\fB\-\-stream\-base\-url\fR] [\fB\-\-preserve\-on\-error\fR] [\fB\-\-fetch\-retries\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIDEST_DEVICE\fR]
\fBcoreos\-installer\-install\fR [\fB\-c\fR|\fB\-\-config\-file\fR] [\fB\-s\fR|\fB\-\-stream\fR] [\fB\-u\fR|\fB\-\-image\-url\fR] [\fB\-f\fR|\fB\-\-image\-file\fR] [\fB\-i\fR|\fB\-\-ignition\-file\fR] [\fB\-I\fR|\fB\-\-ignition\-url\fR] [\fB\-\-ignition\-hash\fR] [\fB\-a\fR|\fB\-\-architecture\fR] [\fB\-p\fR|\fB\-\-platform\fR] [\fB\-\-console\fR] [\fB\-\-append\-karg\fR] [\fB\-\-delete\-karg\fR] [\fB\-n\fR|\fB\-\-copy\-network\fR] [\fB\-\-network\-dir\fR] [\fB\-\-save\-partlabel\fR] [\fB\-\-save\-partindex\fR] [\fB\-\-offline\fR] [\fB\-\-insecure\fR] [\fB\-\-insecure\-ignition\fR] [\fB\-\-stream\-base\-url\fR] [\fB\-\-preserve\-on\-error\fR] [\fB\-\-fetch\-retries\fR] [\fB\-\-secure\-ipl\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIDEST_DEVICE\fR]
.SH DESCRIPTION
Install Fedora CoreOS or RHEL CoreOS
.SH OPTIONS
Expand Down Expand Up @@ -123,6 +123,9 @@ Fetch retries, or "infinite"

Number of times to retry network fetches, or the string "infinite" to retry indefinitely.
.TP
\fB\-\-secure\-ipl\fR
Enable IBM Secure IPL
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help (see a summary with \*(Aq\-h\*(Aq)
.TP
Expand Down
5 changes: 5 additions & 0 deletions scripts/coreos-installer-service
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
7 changes: 7 additions & 0 deletions src/cmdline/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 IBM Secure IPL
#[serde(skip_serializing_if = "is_default")]
#[arg(long, help_heading = ADVANCED)]
pub secure_ipl: bool,

// positional args
/// Destination device
Expand Down Expand Up @@ -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![
Expand Down Expand Up @@ -412,6 +417,7 @@ mod test {
"--preserve-on-error",
"--fetch-retries",
"3",
"--secure-ipl",
"u",
];
assert_eq!(config.to_args().unwrap(), expected);
Expand Down Expand Up @@ -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()])
Expand Down
3 changes: 3 additions & 0 deletions src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ fn write_disk(
None,
)?;
s390x::chreipl(device)?;
if config.secure_ipl {
s390x::set_loaddev(device)?;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/s390x/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
116 changes: 111 additions & 5 deletions src/s390x/zipl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -33,11 +34,116 @@ pub fn chreipl<P: AsRef<Path>>(dev: P) -> Result<()> {
Ok(())
}

/// Secure boot (Secure IPL) includes support of SCSI and ECKD DASD boot devices
enum Loaddev {
Eckd(String),
Scsi(String, String, String),
}

fn parse_lszdev_eckd(line: &str) -> Result<Loaddev> {
// ECKD ID looks like: 0.0.5223, we need only last part of it (5223)
lazy_static! {
static ref REGEX: Regex = Regex::new(r#"[[:digit:]].[[:digit:]].([[:xdigit:]]+)"#).unwrap();
}
if let Some(cap) = REGEX.captures_iter(line).next() {
return Ok(Loaddev::Eckd(cap[1].to_string()));
}
bail!("bad ECKD id: {}", line);
}

fn parse_lszdev_zfcp(line: &str) -> Result<Loaddev> {
// SCSI ID looks like: 0.0.8000:0x500507630400d1e3:0x4000401d00000000
// So here is regex to parse required ids: 8000,500507630400d1e3,4000401d00000000
lazy_static! {
static ref REGEX: Regex = Regex::new(
r#"[[:digit:]].[[:digit:]].([[:xdigit:]]+):0x([[:xdigit:]]+):0x([[:xdigit:]]+)"#
)
.unwrap();
}
if let Some(cap) = REGEX.captures_iter(line).next() {
return Ok(Loaddev::Scsi(
cap[1].to_string(),
cap[2].to_string(),
cap[3].to_string(),
));
}
bail!("bad zFCP id: {}", line);
}

fn parse_lszdev<P: AsRef<Path>>(dev: P) -> Result<Loaddev> {
// We don't want to traverse sysfs and do same stuff lszdev does,
// so just call it to get required info. Here is sample output:
// $ lszdev -c TYPE,ID -n
// dasd-eckd 0.0.0190
// zfcp-lun 0.0.8007:0x500507630400d1e3:0x4001404c00000000
// qeth 0.0.bdd0:0.0.bdd1:0.0.bdd2
// generic-ccw 0.0.000c
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 lszdev {output}"))?;
match devtype {
"dasd-eckd" => parse_lszdev_eckd(id),
"zfcp-lun" => parse_lszdev_zfcp(id),
_ => bail!("unsupported device: {} id: {}", devtype, id),
}
}

/// Sets zVM Secure Boot (Secure IPL) boot device to `dev`.
pub fn set_loaddev<P: AsRef<Path>>(dev: P) -> Result<()> {
if !secure_ipl_is_supported()? {
bail!("Secure IPL is not supported");
}
// check if system is zVM guest
if !Path::new("/dev/vmcp").exists() {
return Ok(());
}
eprintln!("Setting LOADDEV");
let mut cmd = Command::new("vmcp");
cmd.arg("set").arg("loaddev");
match parse_lszdev(dev)? {
Loaddev::Eckd(d) => cmd.arg("eckd").arg("dev").arg(d),
Loaddev::Scsi(d, p, l) =>
// CP tool wants portname/lun to be splitted at 8th character:
// $ vmcp set loaddev dev 8007 portname 500507630400d1e3 lun 4001404c00000000 secure
// HCPZPM002E Invalid operand - 500507630400D1E3
// $ vmcp set loaddev dev 8007 portname 50050763 0400d1e3 lun 4001404c 00000000 secure
{
cmd.arg("dev")
.arg(d)
.arg("portname")
.arg(&p[0..8])
.arg(&p[8..])
.arg("lun")
.arg(&l[0..8])
.arg(&l[8..])
}
};
cmd.arg("secure");
cmd_output(&mut cmd)?;
Ok(())
}

fn secure_execution_is_enabled() -> Result<bool> {
let sysfs_flag = "/sys/firmware/uv/prot_virt_guest";
match File::open(sysfs_flag) {
sysfs_flag_enabled("/sys/firmware/uv/prot_virt_guest")
}

fn secure_ipl_is_supported() -> Result<bool> {
sysfs_flag_enabled("/sys/firmware/ipl/has_secure")
}

fn sysfs_flag_enabled<P: AsRef<Path>>(path: P) -> Result<bool> {
match File::open(&path) {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(e).with_context(|| format!("reading {sysfs_flag}")),
Err(e) => Err(e).with_context(|| format!("reading {}", path.as_ref().display())),
Ok(mut f) => {
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
Expand Down
1 change: 1 addition & 0 deletions systemd/coreos-installer-post.target
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ AllowIsolate=yes
Requires=coreos-installer.target
Requires=coreos-installer-reboot.service
Requires=coreos-installer-noreboot.service
Requires=coreos-installer-secure-ipl-reboot.service
1 change: 1 addition & 0 deletions systemd/coreos-installer-reboot.service
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ After=coreos-installer.target
OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly
ConditionPathExists=/run/coreos-installer-reboot
ConditionKernelCommandLine=!coreos.inst.secure_ipl

[Service]
Type=simple
Expand Down
14 changes: 14 additions & 0 deletions systemd/coreos-installer-secure-ipl-reboot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=Reboot from zVM LOADDEV after CoreOS Installer
Requires=coreos-installer.target
After=coreos-installer.target
OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly
ConditionPathExists=/run/coreos-installer-reboot
ConditionKernelCommandLine=coreos.inst.secure_ipl

[Service]
Type=simple
ExecStart=/bin/sh -c '[ -e /dev/vmcp ] && vmcp ipl loaddev || systemctl --no-block reboot'
StandardOutput=kmsg+console
StandardError=kmsg+console

0 comments on commit dc4b19d

Please sign in to comment.