Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

s390x: add zVM Secure IPL support #1338

Merged
merged 1 commit into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
Comment on lines +43 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is obviously totally fine, but just noting I think it's a one-liner to parse this without regexps:

line.split('.').nth(2).ok_or_else(|| anyhow!("Bad EKCD")).map(|s| Loaddev::Eckd(s.to_string()))

or so. (Though validation is looser)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, the reason to use regex here was to validate format of ID, which probably not that necessary.


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
nikita-dubrovskii marked this conversation as resolved.
Show resolved Hide resolved
// 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(());
}
nikita-dubrovskii marked this conversation as resolved.
Show resolved Hide resolved
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
15 changes: 15 additions & 0 deletions systemd/coreos-installer-secure-ipl-reboot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=Reboot in Secure IPL mode 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
# On z/VM, we need to use 'vmcp ipl loaddev'. Otherwise, for the LPAR case, we can just reboot as usual.
ExecStart=/bin/sh -c '[ -e /dev/vmcp ] && vmcp ipl loaddev || systemctl --no-block reboot'
nikita-dubrovskii marked this conversation as resolved.
Show resolved Hide resolved
StandardOutput=kmsg+console
StandardError=kmsg+console