- Applied more additional patches (jsc#PED-11870, jec#PED-11958)
* s390-tools-08-rust-pvimg-Fix-flag-parsing-for-allowing-dump.patch * s390-tools-09-rust-pvimg-Document-the-change-from--comm-key-to--cck.patch OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=247
This commit is contained in:
commit
27cc4620be
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## Default LFS
|
||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.osc
|
13
59-graf.rules.opensuse
Normal file
13
59-graf.rules.opensuse
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Rules for unique 3270 device nodes created in /dev/3270/
|
||||||
|
# This file should be installed in /usr/lib/udev/rules.d
|
||||||
|
#
|
||||||
|
|
||||||
|
SUBSYSTEM!="ccw", GOTO="graf_end"
|
||||||
|
DRIVER!="3270", GOTO="graf_end"
|
||||||
|
|
||||||
|
# Configure 3270 device
|
||||||
|
ACTION=="add", SUBSYSTEM=="ccw", PROGRAM="/usr/sbin/chccwdev -e $kernel"
|
||||||
|
ACTION=="remove", SUBSYSTEM=="ccw", PROGRAM="/usr/sbin/chccwdev -d $kernel"
|
||||||
|
|
||||||
|
LABEL="graf_end"
|
13
59-graf.rules.suse
Normal file
13
59-graf.rules.suse
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Rules for unique 3270 device nodes created in /dev/3270/
|
||||||
|
# This file should be installed in /usr/lib/udev/rules.d
|
||||||
|
#
|
||||||
|
|
||||||
|
SUBSYSTEM!="ccw", GOTO="graf_end"
|
||||||
|
DRIVER!="3270", GOTO="graf_end"
|
||||||
|
|
||||||
|
# Configure 3270 device
|
||||||
|
ACTION=="add", SUBSYSTEM=="ccw", PROGRAM="/sbin/chccwdev -e $kernel"
|
||||||
|
ACTION=="remove", SUBSYSTEM=="ccw", PROGRAM="/sbin/chccwdev -d $kernel"
|
||||||
|
|
||||||
|
LABEL="graf_end"
|
5
59-prng.rules
Normal file
5
59-prng.rules
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#
|
||||||
|
# Rule for prandom character device node permissions
|
||||||
|
# This file should be installed in /usr/lib/udev/rules.d
|
||||||
|
#
|
||||||
|
ACTION=="add", SUBSYSTEM=="misc", KERNEL=="prandom", MODE="0444"
|
23
59-zfcp-compat.rules
Normal file
23
59-zfcp-compat.rules
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Rules for creating the ID_PATH for SCSI devices based on the CCW bus
|
||||||
|
# using the form: ccw-<BUS_ID>-zfcp-<WWPN>:<LUN>
|
||||||
|
#
|
||||||
|
ACTION=="remove", GOTO="zfcp_scsi_device_end"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set environment variable "ID_ZFCP_BUS" to "1" if the devices
|
||||||
|
# (both disk and partition) are SCSI devices based on FCP devices
|
||||||
|
#
|
||||||
|
KERNEL=="sd*", SUBSYSTEMS=="ccw", DRIVERS=="zfcp", ENV{.ID_ZFCP_BUS}="1"
|
||||||
|
|
||||||
|
# For SCSI disks
|
||||||
|
KERNEL=="sd*[!0-9]", SUBSYSTEMS=="scsi", \
|
||||||
|
ENV{.ID_ZFCP_BUS}=="1", ENV{DEVTYPE}=="disk", \
|
||||||
|
SYMLINK+="disk/by-path/ccw-$attr{hba_id}-zfcp-$attr{wwpn}:$attr{fcp_lun}"
|
||||||
|
|
||||||
|
|
||||||
|
# For partitions on a SCSI disk
|
||||||
|
KERNEL=="sd*[0-9]", SUBSYSTEMS=="scsi", \
|
||||||
|
ENV{.ID_ZFCP_BUS}=="1", ENV{DEVTYPE}=="partition", \
|
||||||
|
SYMLINK+="disk/by-path/ccw-$attr{hba_id}-zfcp-$attr{wwpn}:$attr{fcp_lun}-part%n"
|
||||||
|
|
||||||
|
LABEL="zfcp_scsi_device_end"
|
25
90-s390-tools.conf
Normal file
25
90-s390-tools.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Please don't edit this file. Place your settings into
|
||||||
|
# /etc/modprobe.d/99-local.conf instead.
|
||||||
|
#
|
||||||
|
# The dasd_diag_mod kernel module will not function properly
|
||||||
|
# unless the dasd_fba_mod module is also loaded. However,
|
||||||
|
# there are no cross-module symbol dependencies that would
|
||||||
|
# cause and entry to be placed in
|
||||||
|
# /lib/modules/$(uname -r)/modules.dep
|
||||||
|
# So, we're adding this "soft" dependency here to make sure that
|
||||||
|
# any time dasd_diag_mod gets loaded, so will dasd_fba_mod.
|
||||||
|
#
|
||||||
|
# Additionally, DASD devices that are supposed to be used in
|
||||||
|
# DIAG250 mode will have problems because as far as the kernel
|
||||||
|
# is concerned, and hence udev, the driver is dasd_fba_mod. So,
|
||||||
|
# we need to also have the reverse dependency so that when
|
||||||
|
# dasd_fba_mod gets loaded, so will dasd_diag_mod. This will
|
||||||
|
# prevent problems that would show up in the system log as:
|
||||||
|
# Setting the DASD online failed because of missing DIAG discipline
|
||||||
|
#
|
||||||
|
softdep dasd_diag_mod pre: dasd_fba_mod
|
||||||
|
softdep dasd_fba_mod pre: dasd_diag_mod
|
57
README.SUSE.opensuse
Normal file
57
README.SUSE.opensuse
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
ls - Addons by SUSE
|
||||||
|
|
||||||
|
The following utility and its man page have been added to make it
|
||||||
|
easier to determine the machine type on which Linux is running.
|
||||||
|
|
||||||
|
* cputype
|
||||||
|
Usage: cputype
|
||||||
|
|
||||||
|
The following utilities and their man pages have been added by SUSE to
|
||||||
|
ease the activation and deactivation of devices. These scripts are also
|
||||||
|
used by YaST. Functionality not provided by these scripts cannot be
|
||||||
|
provided by YaST.
|
||||||
|
These scripts also create/delete the needed udev rules.
|
||||||
|
Detailed information on some parameters are in the
|
||||||
|
"Device Drivers, Features and Commands" for this release.
|
||||||
|
|
||||||
|
General parameters
|
||||||
|
channel numbers are with lower letters
|
||||||
|
parameters switching things on or off are
|
||||||
|
1 for on and 0 for off
|
||||||
|
|
||||||
|
* ctc_configure
|
||||||
|
Usage: /usr/sbin/ctc_configure <read channel> <write channel> <online> [<protocol>]
|
||||||
|
To configure CTC connections
|
||||||
|
Valid Parameters for the protocal are 0, 1 and 3
|
||||||
|
For a detailed explanation please look in the Device Driver book
|
||||||
|
|
||||||
|
* dasd_configure
|
||||||
|
Usage: dasd_configure <ccwid> <online> <use_diag>
|
||||||
|
To set DASDs online/offline
|
||||||
|
The use_diag makes only sense under z/VM. In an
|
||||||
|
LPAR just set it to 0
|
||||||
|
|
||||||
|
* iucv_configure
|
||||||
|
Usage: /usr/sbin/iucv_configure <router> <online>
|
||||||
|
To set an IUCV IP-network online/offline
|
||||||
|
|
||||||
|
* qeth_configure
|
||||||
|
Usage: /usr/sbin/qeth_configure [options] <read chan> <write chan> <control chan> <online>
|
||||||
|
Set qeth, hipersocket adapter online/offline.
|
||||||
|
options could be one of the following:
|
||||||
|
|
||||||
|
-i Configure IP takeover
|
||||||
|
-l Configure Layer2 support
|
||||||
|
-p NAME QETH Portname to use
|
||||||
|
-n 1/0 QETH port number to use
|
||||||
|
|
||||||
|
|
||||||
|
* zfcp_disk_configure
|
||||||
|
Usage: /usr/sbin/zfcp_disk_configure <ccwid> <wwpn> <lun> <online>
|
||||||
|
set a disk online/offline. This require that the repective
|
||||||
|
Adapter is online. See command below.
|
||||||
|
|
||||||
|
* zfcp_host_configure
|
||||||
|
Usage: /usr/sbin/zfcp_host_configure <ccwid> <online>
|
||||||
|
Set a zfcp Adapter online/offline
|
57
README.SUSE.suse
Normal file
57
README.SUSE.suse
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
ls - Addons by SUSE
|
||||||
|
|
||||||
|
The following utility and its man page have been added to make it
|
||||||
|
easier to determine the machine type on which Linux is running.
|
||||||
|
|
||||||
|
* cputype
|
||||||
|
Usage: cputype
|
||||||
|
|
||||||
|
The following utilities and their man pages have been added by SUSE to
|
||||||
|
ease the activation and deactivation of devices. These scripts are also
|
||||||
|
used by YaST. Functionality not provided by these scripts cannot be
|
||||||
|
provided by YaST.
|
||||||
|
These scripts also create/delete the needed udev rules.
|
||||||
|
Detailed information on some parameters are in the
|
||||||
|
"Device Drivers, Features and Commands" for this release.
|
||||||
|
|
||||||
|
General parameters
|
||||||
|
channel numbers are with lower letters
|
||||||
|
parameters switching things on or off are
|
||||||
|
1 for on and 0 for off
|
||||||
|
|
||||||
|
* ctc_configure
|
||||||
|
Usage: /sbin/ctc_configure <read channel> <write channel> <online> [<protocol>]
|
||||||
|
To configure CTC connections
|
||||||
|
Valid Parameters for the protocal are 0, 1 and 3
|
||||||
|
For a detailed explanation please look in the Device Driver book
|
||||||
|
|
||||||
|
* dasd_configure
|
||||||
|
Usage: dasd_configure <ccwid> <online> <use_diag>
|
||||||
|
To set DASDs online/offline
|
||||||
|
The use_diag makes only sense under z/VM. In an
|
||||||
|
LPAR just set it to 0
|
||||||
|
|
||||||
|
* iucv_configure
|
||||||
|
Usage: /sbin/iucv_configure <router> <online>
|
||||||
|
To set an IUCV IP-network online/offline
|
||||||
|
|
||||||
|
* qeth_configure
|
||||||
|
Usage: /sbin/qeth_configure [options] <read chan> <write chan> <control chan> <online>
|
||||||
|
Set qeth, hipersocket adapter online/offline.
|
||||||
|
options could be one of the following:
|
||||||
|
|
||||||
|
-i Configure IP takeover
|
||||||
|
-l Configure Layer2 support
|
||||||
|
-p NAME QETH Portname to use
|
||||||
|
-n 1/0 QETH port number to use
|
||||||
|
|
||||||
|
|
||||||
|
* zfcp_disk_configure
|
||||||
|
Usage: /sbin/zfcp_disk_configure <ccwid> <wwpn> <lun> <online>
|
||||||
|
set a disk online/offline. This require that the repective
|
||||||
|
Adapter is online. See command below.
|
||||||
|
|
||||||
|
* zfcp_host_configure
|
||||||
|
Usage: /sbin/zfcp_host_configure <ccwid> <online>
|
||||||
|
Set a zfcp Adapter online/offline
|
8
_service
Normal file
8
_service
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<services>
|
||||||
|
<service name="cargo_vendor" mode="manual">
|
||||||
|
<param name="srctar">s390-tools-2.29.0.tar.gz</param>
|
||||||
|
<param name="compression">zst</param>
|
||||||
|
<param name="update">true</param>
|
||||||
|
</service>
|
||||||
|
<service name="cargo_audit" mode="manual" />
|
||||||
|
</services>
|
126
appldata
Normal file
126
appldata
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Copyright (c) 2003 SUSE LINUX AG Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Submit feedback to http://www.suse.de/feedback/
|
||||||
|
|
||||||
|
# Local settings
|
||||||
|
LOCKFILE=/var/lock/appldata
|
||||||
|
CONFIGFILE=/etc/sysconfig/appldata
|
||||||
|
|
||||||
|
# Source config file
|
||||||
|
if [ -f $CONFIGFILE ]; then
|
||||||
|
. $CONFIGFILE
|
||||||
|
else
|
||||||
|
echo "No config file found (should be $CONFIGFILE)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo "Starting \"Linux - z/VM Monitor Stream\" ..."
|
||||||
|
echo -n "(interval $APPLDATA_INTERVAL milliseconds) "
|
||||||
|
echo $APPLDATA_INTERVAL > /proc/sys/appldata/interval
|
||||||
|
if [ "$APPLDATA_MEM" = "yes" ]; then
|
||||||
|
if [ ! -e /proc/sys/appldata/mem ]; then
|
||||||
|
echo -n "(mem) "
|
||||||
|
modprobe appldata_mem 2>&1
|
||||||
|
if [ "$?" -ne 0 ] ; then
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo 1 > /proc/sys/appldata/mem
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$APPLDATA_OS" = "yes" ]; then
|
||||||
|
if [ ! -e /proc/sys/appldata/os ]; then
|
||||||
|
echo -n "(os) "
|
||||||
|
modprobe appldata_os 2>&1
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo 1 > /proc/sys/appldata/os
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$APPLDATA_NET_SUM" = "yes" ]; then
|
||||||
|
if [ ! -e /proc/sys/appldata/net_sum ]; then
|
||||||
|
echo -n "(net_sum) "
|
||||||
|
modprobe appldata_net_sum 2>&1
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo 1 > /proc/sys/appldata/net_sum
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo -n "(timer)"
|
||||||
|
echo 1 > /proc/sys/appldata/timer
|
||||||
|
touch $LOCKFILE
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo -n "Stopping \"Linux - z/VM Monitor Stream\" "
|
||||||
|
echo -n "(timer"
|
||||||
|
echo 0 > /proc/sys/appldata/timer
|
||||||
|
if [ -e /proc/sys/appldata/mem ]; then
|
||||||
|
echo -n ",mem"
|
||||||
|
echo 0 > /proc/sys/appldata/mem
|
||||||
|
rmmod appldata_mem
|
||||||
|
fi
|
||||||
|
if [ -e /proc/sys/appldata/os ]; then
|
||||||
|
echo -n ",os"
|
||||||
|
echo 0 > /proc/sys/appldata/os
|
||||||
|
rmmod appldata_os
|
||||||
|
fi
|
||||||
|
if [ -e /proc/sys/appldata/net_sum ]; then
|
||||||
|
echo -n ",net_sum"
|
||||||
|
echo 0 > /proc/sys/appldata/net_sum
|
||||||
|
rmmod appldata_net_sum
|
||||||
|
fi
|
||||||
|
echo -n ")"
|
||||||
|
rm -f $LOCKFILE
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
echo "\"Linux - z/VM Monitor Stream\" status..."
|
||||||
|
echo -n "interval "
|
||||||
|
cat /proc/sys/appldata/interval
|
||||||
|
echo -n "timer "
|
||||||
|
cat /proc/sys/appldata/timer
|
||||||
|
echo -n "mem "
|
||||||
|
if [ -e /proc/sys/appldata/mem ]; then
|
||||||
|
cat /proc/sys/appldata/mem
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
echo -n "os "
|
||||||
|
if [ -e /proc/sys/appldata/os ]; then
|
||||||
|
cat /proc/sys/appldata/os
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
echo -n "net_sum "
|
||||||
|
if [ -e /proc/sys/appldata/net_sum ]; then
|
||||||
|
cat /proc/sys/appldata/net_sum
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# How are we called?
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
RETVAL=1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
17
appldata.service
Normal file
17
appldata.service
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Linux - z/VM Monitor Stream
|
||||||
|
After=network-online.target remote-fs.target
|
||||||
|
Wants=network-online.target remote-fs.target
|
||||||
|
ConditionPathExists=/proc/sys/appldata/interval
|
||||||
|
ConditionPathExists=!/var/lock/appldata
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
ExecStart=/usr/lib/systemd/scripts/appldata start
|
||||||
|
ExecStartPost=/usr/lib/systemd/scripts/appldata status
|
||||||
|
ExecStop=/usr/lib/systemd/scripts/appldata stop
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
5
cargo_config
Normal file
5
cargo_config
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[source.crates-io]
|
||||||
|
replace-with = "vendored-sources"
|
||||||
|
|
||||||
|
[source.vendored-sources]
|
||||||
|
directory = "vendor/"
|
13
cio_ignore.service
Normal file
13
cio_ignore.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Setup devices for cio_ignore
|
||||||
|
DefaultDependencies=no
|
||||||
|
Before=local-fs.target
|
||||||
|
ConditionKernelCommandLine=cio_ignore
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/usr/lib/systemd/scripts/setup_cio_ignore.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sysinit.target
|
73
cputype
Normal file
73
cputype
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# cputype
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014-2017, 2019, 2023 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Based on the IBM machine model, returns a (hopefully) human understandable
|
||||||
|
# string that identifies the processor.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cputype
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# 1 The script was executed on a system that is a non-IBM mainframe
|
||||||
|
# architecture
|
||||||
|
# 2 The search for the machine type in /proc/cpuinfo returned a null string
|
||||||
|
# 3 The parsing of the machine type returned a null string
|
||||||
|
# 4 The machine type found is (probably) a new one, and the script needs to
|
||||||
|
# be updated to handle it.
|
||||||
|
#
|
||||||
|
|
||||||
|
architecture=$(/bin/uname -m)
|
||||||
|
if [ "${architecture}" != "s390x" -a "${architecture}" != "s390" ]; then
|
||||||
|
echo "This command is only useful on IBM mainframes." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
args=$(/usr/bin/grep machine /proc/cpuinfo | awk '{print $11}' )
|
||||||
|
|
||||||
|
if [ -z "${args}" ]; then
|
||||||
|
echo "I couldn't find the machine type. Please report a bug with this output:" >&2
|
||||||
|
/bin/cat /proc/cpuinfo >&2
|
||||||
|
echo "******************" >&2
|
||||||
|
/usr/bin/grep machine /proc/cpuinfo >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
machine=${args:0:4}
|
||||||
|
|
||||||
|
if [ -z "${machine}" ] ; then
|
||||||
|
echo "The machine type came out null. Please report a bug with this output:" >&2
|
||||||
|
/bin/cat /proc/cpuinfo >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${machine}" in
|
||||||
|
2064) echo "${machine} = z900 IBM eServer zSeries 900" ;;
|
||||||
|
2066) echo "${machine} = z800 IBM eServer zSeries 800" ;;
|
||||||
|
2084) echo "${machine} = z990 IBM eServer zSeries 990" ;;
|
||||||
|
2086) echo "${machine} = z890 IBM eServer zSeries 890" ;;
|
||||||
|
2094) echo "${machine} = z9-EC IBM System z9 Enterprise Class" ;;
|
||||||
|
2096) echo "${machine} = z9-BC IBM System z9 Business Class" ;;
|
||||||
|
2097) echo "${machine} = z10-EC IBM System z10 Enterprise Class" ;;
|
||||||
|
2098) echo "${machine} = z10-BC IBM System z10 Business Class" ;;
|
||||||
|
2817) echo "${machine} = z196 IBM zEnterprise 196" ;;
|
||||||
|
2818) echo "${machine} = z114 IBM zEnterprise 114" ;;
|
||||||
|
2827) echo "${machine} = z12-EC IBM zEnterprise EC12" ;;
|
||||||
|
2828) echo "${machine} = z12-BC IBM zEnterprise BC12" ;;
|
||||||
|
2964) echo "${machine} = z13 IBM z13" ;;
|
||||||
|
2965) echo "${machine} = z13s IBM z13s (single frame)" ;;
|
||||||
|
3906) echo "${machine} = z14 IBM z14" ;;
|
||||||
|
3907) echo "${machine} = z14 ZR1 IBM z14 ZR1" ;;
|
||||||
|
8561) echo "${machine} = z15 T01 IBM z15 T01" ;;
|
||||||
|
8562) echo "${machine} = z15 T02 IBM z15 T02" ;;
|
||||||
|
3931) echo "${machine} = z16 A01 IBM z16 A01" ;;
|
||||||
|
3932) echo "${machine} = z16 A02 IBM z16 A02" ;;
|
||||||
|
*) echo "An unknown machine type was reported: ${machine}" >&2
|
||||||
|
echo "Please file a bug report with this output:" >&2
|
||||||
|
/bin/cat /proc/cpuinfo >&2
|
||||||
|
exit 4
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
50
cputype.1
Normal file
50
cputype.1
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.TH cputype 1 "April 2014" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
cputype \- Based on the IBM machine model, returns a (hopefully) human understandable string that identifies the processor.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cputype
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B cputype
|
||||||
|
is intended to make it easy to find out the type of the mainframe system in use, by examining /proc/cpuinfo and converting that to the name typically known by people familiar with the IBM mainframe.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP None
|
||||||
|
.SH FILES
|
||||||
|
.I /proc/cpuinfo
|
||||||
|
.RS
|
||||||
|
Read to determine the IBM machine model for the running system.
|
||||||
|
.RE
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
The following messages may be issued on stderr:
|
||||||
|
.IP
|
||||||
|
.B This command is only useful on IBM mainframes.
|
||||||
|
.RS
|
||||||
|
The command was executed on a system that is running on a non-IBM mainframe architecture.
|
||||||
|
Return code 1 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B I couldn't find the machine type. Please report a bug with this output:
|
||||||
|
.RS
|
||||||
|
The contents of /proc/cpuinfo are printed as well as the output from the grep command used.
|
||||||
|
Return code 2 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B The machine type came out null. Please report a bug with this output:
|
||||||
|
.RS
|
||||||
|
The contents of /proc/cpuinfo are printed. Return code 3 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B An unknown machine type was reported: mmmm
|
||||||
|
.RS
|
||||||
|
.B Please file a bug report with this output:
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
This is most likely seen because the command was run on a newer generation processor
|
||||||
|
and the script has not been updated with the new model number.
|
||||||
|
The contents of /proc/cpuinfo are printed. Return code 4 is set.
|
||||||
|
.RE
|
||||||
|
.SH Author
|
||||||
|
Mark Post (mpost@suse.com)
|
||||||
|
.SH Copyright
|
||||||
|
Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
128
ctc_configure
Normal file
128
ctc_configure
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# ctc_configure
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Configures a CTC device by calling the IBM-provided chzdev command.
|
||||||
|
# Whereas this script used to validate the parameters provided to it,
|
||||||
|
# we now rely on chzdev to do that instead. The script is intended only
|
||||||
|
# as a "translation layer" to provide backward compatability for older
|
||||||
|
# scripts and tools that invoke it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ctc_configure <read channel> <write channel> <online> [<protocol>]
|
||||||
|
#
|
||||||
|
# read/write channel = x.y.ssss where
|
||||||
|
# x is always 0 until IBM creates something that
|
||||||
|
# uses that number
|
||||||
|
# y is the logical channel subsystem (lcss) number.
|
||||||
|
# Most often this is 0, but it could be non-zero
|
||||||
|
# ssss is the four digit subchannel address of the
|
||||||
|
# device, in hexidecimal, with leading zeros.
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
# protocol = 0 Compatibility with peers other than OS/390®, or z/OS, for
|
||||||
|
# example, a z/VM TCP service machine. This is the default.
|
||||||
|
# 1 Enhanced package checking for Linux peers.
|
||||||
|
# 3 For compatibility with OS/390 or z/OS peers.
|
||||||
|
# 4 For MPC connections to VTAM on traditional mainframe
|
||||||
|
# operating systems.
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# Return codes are determined by the chzdev command.
|
||||||
|
#
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "${DEBUG}" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
add_cio_channel() {
|
||||||
|
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_cio_channel() {
|
||||||
|
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} <read channel> <write channel> <online> [<protocol>]"
|
||||||
|
echo " read/write channel = x.y.ssss where"
|
||||||
|
echo " x is always 0 until IBM creates something that"
|
||||||
|
echo " uses that number"
|
||||||
|
echo " y is the logical channel subsystem (lcss) number."
|
||||||
|
echo " Most often this is 0, but it could be non-zero"
|
||||||
|
echo " ssss is the four digit subchannel address of the"
|
||||||
|
echo " device, in hexidecimal, with leading zeros."
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
echo " protocol = 0 Compatibility with peers other than OS/390®, or z/OS, for"
|
||||||
|
echo " example, a z/VM TCP service machine. This is the default."
|
||||||
|
echo " 1 Enhanced package checking for Linux peers."
|
||||||
|
echo " 3 For compatibility with OS/390 or z/OS peers."
|
||||||
|
echo " 4 For MPC connections to VTAM on traditional mainframe"
|
||||||
|
echo " operating systems."
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
|
||||||
|
CTC_READ_CHAN=${1}
|
||||||
|
CTC_WRITE_CHAN=${2}
|
||||||
|
ON_OFF=${3}
|
||||||
|
CTC_MODE=${4}
|
||||||
|
|
||||||
|
if [ -z "${CTC_READ_CHAN}" ] || [ -z "${CTC_WRITE_CHAN}" ] || [ -z "${ON_OFF}" ]; then
|
||||||
|
mesg "You didn't specify all the needed parameters."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /sys/bus/ccw/devices/${CTC_READ_CHAN}/cutype ]; then
|
||||||
|
read CU_TYPE < /sys/bus/ccw/devices/${CTC_READ_CHAN}/cutype
|
||||||
|
else mesg "Psuedo file/sys/bus/ccw/devices/${CTC_READ_CHAN}/cutype doesn't exist."
|
||||||
|
mesg "Check to see if sysfs is mounted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PARM_LIST=""
|
||||||
|
if [ "${CU_TYPE}" == "3088/01" ] || [ "${CU_TYPE}" == "3088/60" ]; then
|
||||||
|
DEV_TYPE="lcs"
|
||||||
|
else DEV_TYPE="ctc"
|
||||||
|
if [ -z "${CTC_MODE}" ]; then
|
||||||
|
PARM_LIST="${PARM_LIST} protocol=0"
|
||||||
|
else PARM_LIST="${PARM_LIST} protocol=${CTC_MODE}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${ON_OFF}" == 0 ]; then
|
||||||
|
debug_mesg "chzdev -d ${DEV_TYPE} --no-root-update ${CTC_READ_CHAN}"
|
||||||
|
chzdev -d ${DEV_TYPE} --no-root-update ${CTC_READ_CHAN}
|
||||||
|
elif [ "${ON_OFF}" == 1 ]; then
|
||||||
|
debug_mesg "chzdev -e ${DEV_TYPE} --no-root-update ${CTC_READ_CHAN} ${PARM_LIST}"
|
||||||
|
chzdev -e ${DEV_TYPE} --no-root-update ${CTC_READ_CHAN} ${PARM_LIST}
|
||||||
|
else mesg "You must specify a 0 or a 1 for the online/offline attribute."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RC=${?}
|
||||||
|
if [ ${RC} -ne 0 ]; then
|
||||||
|
exit ${RC}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ON_OFF} == 1 ]; then
|
||||||
|
add_cio_channel "${CTC_READ_CHAN},${CTC_WRITE_CHAN}"
|
||||||
|
else remove_cio_channel "${CTC_READ_CHAN}"
|
||||||
|
remove_cio_channel "${CTC_WRITE_CHAN}"
|
||||||
|
fi
|
155
ctc_configure.8
Normal file
155
ctc_configure.8
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
.TH ctc_configure "8" "July 2013" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
ctc_configure \- Configures or deconfigures a Channel-to-Channel adapter (CTC) or LAN Channel Station adapter (LCS)
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B ctc_configure read_channel write_channel online [protocol]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B ctc_configure
|
||||||
|
is intended to make it easy to persistently add and remove IBM CTC and LCS adapters. In addition to bringing the adapter online or offline, it will also create or delete the necessary udev rules for the adapter.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP read_channel
|
||||||
|
The device number of the read channel of the adapter. Takes the form x.y.ssss.
|
||||||
|
.IP write_channel
|
||||||
|
The device number of the write channel of the adapter.Takes the form x.y.ssss.
|
||||||
|
.RS
|
||||||
|
|
||||||
|
where
|
||||||
|
.RS
|
||||||
|
.B x
|
||||||
|
is always 0 until IBM creates something that uses that number.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B y
|
||||||
|
is the logical channel subsystem (lcss) number. Most often this is 0, but it could be non-zero.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B ssss
|
||||||
|
is the four digit subchannel address of the device, in hexidecimal, with leading zeros. If entered in upper/mixed case, this is automatically converted to lower case.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
|
||||||
|
Keep in mind that for a CTC, the read channel needs to be coupled to the write channel of the peer, and vice versa. This does not apply to LCS adapters.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.IP online
|
||||||
|
Either a literal 1 to bring the adapter online or a literal 0 to take it offline
|
||||||
|
.IP protocol
|
||||||
|
.RS
|
||||||
|
0 Compatibility with peers other than OS/390®, or z/OS, for example, a z/VM TCP service machine. This is the default.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
1 Enhanced package checking for Linux peers.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
3 For compatibility with OS/390 or z/OS peers.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
4 For MPC connections to VTAM on traditional mainframe operating systems.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
|
||||||
|
Not needed for LCS adapters.
|
||||||
|
.SH FILES
|
||||||
|
.I /etc/udev/rules.d/51-ctcm-<ccwid>.rules
|
||||||
|
.RE
|
||||||
|
.I /etc/udev/rules.d/51-lcs-<ccwid>.rules
|
||||||
|
.RS
|
||||||
|
These files provide the udev rules necessary to activate a specific CTC or LCS.
|
||||||
|
.RE
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.IP DEBUG
|
||||||
|
If set to "yes" some minimal debugging information is output during execution.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
The following messages may be issued on stdout:
|
||||||
|
.IP
|
||||||
|
.B /sysfs not present
|
||||||
|
.RS
|
||||||
|
The sysfs file system could not be found in /proc/mounts, so there's nothing the script can
|
||||||
|
do. Return code 1 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Invalid device status ${ONLINE}
|
||||||
|
.RS
|
||||||
|
A value other than 0 or 1 was specified for the third parameter, online. Return code 2 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Device ${CTC_READ_CHAN} does not exist
|
||||||
|
.RS
|
||||||
|
A non-existent <ccwid> was specified for the first parameter. Remember the x.y.ssss format is necessary. Return code 3 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Device ${CTC_READ_CHAN} does not exist
|
||||||
|
.RS
|
||||||
|
A non-existent <ccwid> was specified for the second parameter. Remember the x.y.ssss format is necessary. Return code 4 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Not a valid CTC device (cu ${_cutype}, dev ${_devtype})
|
||||||
|
.RS
|
||||||
|
The device number specified does not correspond to a valid CTC or LCS device type. Return code 5 is st.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B CTC type mismatch (read: ${tmp_chan}, write: ${CCW_CHAN_GROUP})
|
||||||
|
.RS
|
||||||
|
The device number specified for the read channel has a different device type than the device number specified for the write channel. Return code 6 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Could not load module ${CCW_CHAN_GROUP}
|
||||||
|
.RS
|
||||||
|
The kernel module for the device type failed to load. Try "dmesg" to see if there is any indication why. Return code 7 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B CCW devices grouped to different devices
|
||||||
|
.RS
|
||||||
|
The read and write channels are already grouped, but not within the same interface. Try again with different devices. Return code 8 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Could not group ${CCW_CHAN_GROUP} devices ${CTC_READ_CHAN}/${CTC_WRITE_CHAN}
|
||||||
|
.RS
|
||||||
|
The attempt to group the read and write channels into an interface failed. Try "dmesg" to see if there is any indication why. Return code 9 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Could not set device ${CCW_CHAN_ID} online
|
||||||
|
.RS
|
||||||
|
The attempt to bring the grouped devices online failed. Try "dmesg" to see if there is any indication why. Return code 10 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Could not set device ${CCW_CHAN_ID} offline
|
||||||
|
.RS
|
||||||
|
The attempt to take the grouped devices offline failed. Try "dmesg" to see if there is any indication why. Return code 11 is set.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
If environment variable DEBUG is set to "yes," the following messages may be issued on stdout:
|
||||||
|
.IP
|
||||||
|
.B
|
||||||
|
Configuring CTC/LCS device ${CTC_READ_CHAN}/${CTC_WRITE_CHAN}
|
||||||
|
.RS
|
||||||
|
Just a little bit of verbosity, since it just indicates that we got past certain error checks and will now try to do something useful.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Group is ${_ccw_groupdir}/drivers/${CCW_CHAN_GROUP}/group
|
||||||
|
.RS
|
||||||
|
Just a little bit of verbosity.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Setting device online
|
||||||
|
.RS
|
||||||
|
Just a little bit of verbosity.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Device ${CCW_CHAN_ID} is already online
|
||||||
|
.RS
|
||||||
|
An attempt was made to bring the adapter online when it was already online.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Setting device offline
|
||||||
|
.RS
|
||||||
|
Just a little bit of verbosity.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Device ${CCW_CHAN_ID} is already offline
|
||||||
|
.RS
|
||||||
|
An attempt was made to take the adapter offline when it was already offline.
|
||||||
|
.RE
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
60
dasd_configure.8
Normal file
60
dasd_configure.8
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
.TH dasd_configure "8" "February 2013" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
dasd_configure \- Configures or deconfigures a Direct Access Storage Device (DASD) volume.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B dasd_configure [-f -t dasd_type ] ccwid online [use_diag]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B dasd_configure
|
||||||
|
is intended to make it easy to persistently add and remove DASD volumes. In addition to bringing the volume online or offline, it will also create or delete the necessary udev rules for the volume.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP -f
|
||||||
|
Force creation of udev rules, do not check values in /sys.
|
||||||
|
.IP -t
|
||||||
|
Must be either dasd-eckd or dasd-fba. Must be provided if -f is used.
|
||||||
|
.IP ccwid
|
||||||
|
The device number of the DASD volume. Takes the form x.y.ssss where
|
||||||
|
.RS
|
||||||
|
.B x
|
||||||
|
is always 0 until IBM creates something that uses that number.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B y
|
||||||
|
is the subchannel set ID (SSID). Most often this is 0, but it could be non-zero.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B ssss
|
||||||
|
is the four digit device address of the subchannel, in hexidecimal, with leading zeros. If entered in upper/mixed case, this is automatically converted to lower case.
|
||||||
|
.RE
|
||||||
|
.IP online
|
||||||
|
Either a literal 1 to bring the volume online or a literal 0 to take it offline
|
||||||
|
.RE
|
||||||
|
.IP use_diag
|
||||||
|
Either a literal 1 to use the DIAG driver for this device, or a literal 0 to use the "normal" driver.
|
||||||
|
.RE
|
||||||
|
.SH FILES
|
||||||
|
Please see the documentation of
|
||||||
|
.B chzdev.
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.IP DEBUG
|
||||||
|
If set to "yes" some minimal debugging information is output during execution.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
Messages and return codes are determined by the
|
||||||
|
.B chzdev
|
||||||
|
command.
|
||||||
|
Except for:
|
||||||
|
.IP
|
||||||
|
.B Device ${CCW_CHAN_ID} is unformatted
|
||||||
|
.RS
|
||||||
|
The DASD volume was brought online, but it has not been formatted with dasdfmt. This condition is really only important for YaST to determine if it should prompt the user to decide if they want to format it or not at that point. Return code 8 is set.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
If environment variable DEBUG is set to "yes," it shows the command line of the invoked
|
||||||
|
.B chzdev.
|
||||||
|
Additionally, the following messages may be issued on stdout:
|
||||||
|
.IP
|
||||||
|
.B DASD ${CCW_CHAN_ID} did not come online.
|
||||||
|
.RS
|
||||||
|
The DASD volume did not come online within the waiting time. Could not check if the DASD is formatted (see above). Return code 17 is set.
|
||||||
|
.RE
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
173
dasd_configure.opensuse
Normal file
173
dasd_configure.opensuse
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# dasd_configure
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Configures a DASD device by calling the IBM-provided chzdev command.
|
||||||
|
# Whereas this script used to validate the parameters provided to it,
|
||||||
|
# we now rely on chzdev to do that instead. The script is intended only
|
||||||
|
# as a "translation layer" to provide backward compatability for older
|
||||||
|
# scripts and tools that invoke it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# dasd_configure [-f -t <dasd_type> ] <ccwid> <online> [use_diag]
|
||||||
|
#
|
||||||
|
# -f Override safety checks
|
||||||
|
# -t DASD type. Must be provided if -f is used. Only dasd-eckd and
|
||||||
|
# dasd-fba are supported - Deprecated
|
||||||
|
# ccwid = x.y.ssss where
|
||||||
|
# x is always 0 until IBM creates something that uses that number
|
||||||
|
# y is the subchannel set ID (SSID). Most often
|
||||||
|
# this is 0, but it could be non-zero
|
||||||
|
# ssss is the four digit device address of the subchannel, in
|
||||||
|
# hexidecimal, with leading zeros.
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
# use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default
|
||||||
|
# 1 to use z/VM DIAG250 I/O
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# Return codes are determined by the chzdev command, with one exception: If a
|
||||||
|
# DASD volume is not formatted, we will issue a return code of 8.
|
||||||
|
#
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "${DEBUG}" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
add_cio_channel() {
|
||||||
|
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_cio_channel() {
|
||||||
|
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} [-f -t <dasd_type> ] <ccwid> <online> [use_diag]"
|
||||||
|
echo
|
||||||
|
echo " -f Override safety checks"
|
||||||
|
echo " -t DASD type. Must be provided if -f is used. Only dasd-eckd and"
|
||||||
|
echo " dasd-fba are supported - Deprecated"
|
||||||
|
echo " ccwid = x.y.ssss where"
|
||||||
|
echo " x is always 0 until IBM creates something that uses that number"
|
||||||
|
echo " y is the subchannel set ID (SSID). Most often"
|
||||||
|
echo " this is 0, but it could be non-zero"
|
||||||
|
echo " ssss is the four digit device address of the subchannel, in"
|
||||||
|
echo " hexidecimal, with leading zeros."
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
echo " use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default"
|
||||||
|
echo " 1 to use z/VM DIAG250 I/O"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
|
||||||
|
DASD_FORCE=0
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt --options ft: -n "dasd_configure" -- "$@")
|
||||||
|
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
debug_mesg "All the parms passed were ${ARGS}"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "${1}" in
|
||||||
|
-f) debug_mesg "This used to mean udev rules will always be generated."
|
||||||
|
debug_mesg "For chzdev, it means safety checks will be overridden."
|
||||||
|
debug_mesg "Kinda sorta the same thing, really."
|
||||||
|
PARM_LIST="${PARM_LIST} -f"
|
||||||
|
DASD_FORCE=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-t) debug_mesg "This used to set the card type to ${2}"
|
||||||
|
debug_mesg "Now it gets ignored."
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--) debug_mesg "Found the end of parms indicator: --"
|
||||||
|
shift 1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*) debug_mesg "At the catch-all select entry"
|
||||||
|
debug_mesg "What was selected was ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
CCW_CHAN_ID=${1}
|
||||||
|
ON_OFF=${2}
|
||||||
|
USE_DIAG=${3}
|
||||||
|
|
||||||
|
if [ -z "${CCW_CHAN_ID}" ] || [ -z "${ON_OFF}" ]; then
|
||||||
|
mesg "You didn't specify all the needed parameters."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${USE_DIAG}" ]; then
|
||||||
|
PARM_LIST="${PARM_LIST} use_diag=${USE_DIAG}"
|
||||||
|
else PARM_LIST="${PARM_LIST} use_diag=0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${ON_OFF}" == 0 ]; then
|
||||||
|
debug_mesg "chzdev -d dasd --no-root-update ${CCW_CHAN_ID}"
|
||||||
|
chzdev -d dasd --no-root-update ${CCW_CHAN_ID}
|
||||||
|
elif [ "${ON_OFF}" == 1 ]; then
|
||||||
|
debug_mesg "chzdev -e dasd --no-root-update ${CCW_CHAN_ID} ${PARM_LIST}"
|
||||||
|
chzdev -e dasd --no-root-update ${CCW_CHAN_ID} ${PARM_LIST}
|
||||||
|
else mesg "You must specify a 0 or a 1 for the online/offline attribute."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RC=${?}
|
||||||
|
if [ ${RC} -ne 0 ]; then
|
||||||
|
exit ${RC}
|
||||||
|
elif [ ${ON_OFF} == 1 ]; then
|
||||||
|
exitcode=0
|
||||||
|
# Extract the full busid so that we can reference the proper entries in /sys
|
||||||
|
BUSID=$(/usr/sbin/lszdev dasd ${CCW_CHAN_ID} | /usr/bin/sed -e 1d | /usr/bin/tr -s " " | /usr/bin/cut -f2 -d" " )
|
||||||
|
# Make sure the DASD volume came online
|
||||||
|
for ((counter=0; counter<30; counter++)); do
|
||||||
|
sleep 0.1
|
||||||
|
read online < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${online} -eq 1 ] ; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${online} -ne 1 ]; then
|
||||||
|
debug_mesg "DASD ${CCW_CHAN_ID} did not come online."
|
||||||
|
exit 17
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check to see if the DASD volume is unformatted. If so, let YaST know.
|
||||||
|
read status < /sys/bus/ccw/devices/${BUSID}/status
|
||||||
|
if [ "${status}" == "unformatted" ]; then
|
||||||
|
mesg "DASD ${CCW_CHAN_ID} is unformatted."
|
||||||
|
exitcode=8
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ON_OFF} == 1 ]; then
|
||||||
|
add_cio_channel "${CCW_CHAN_ID}"
|
||||||
|
else remove_cio_channel "${CCW_CHAN_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit ${exitcode}
|
173
dasd_configure.suse
Normal file
173
dasd_configure.suse
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# dasd_configure
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Configures a DASD device by calling the IBM-provided chzdev command.
|
||||||
|
# Whereas this script used to validate the parameters provided to it,
|
||||||
|
# we now rely on chzdev to do that instead. The script is intended only
|
||||||
|
# as a "translation layer" to provide backward compatability for older
|
||||||
|
# scripts and tools that invoke it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# dasd_configure [-f -t <dasd_type> ] <ccwid> <online> [use_diag]
|
||||||
|
#
|
||||||
|
# -f Override safety checks
|
||||||
|
# -t DASD type. Must be provided if -f is used. Only dasd-eckd and
|
||||||
|
# dasd-fba are supported - Deprecated
|
||||||
|
# ccwid = x.y.ssss where
|
||||||
|
# x is always 0 until IBM creates something that uses that number
|
||||||
|
# y is the subchannel set ID (SSID). Most often
|
||||||
|
# this is 0, but it could be non-zero
|
||||||
|
# ssss is the four digit device address of the subchannel, in
|
||||||
|
# hexidecimal, with leading zeros.
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
# use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default
|
||||||
|
# 1 to use z/VM DIAG250 I/O
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# Return codes are determined by the chzdev command, with one exception: If a
|
||||||
|
# DASD volume is not formatted, we will issue a return code of 8.
|
||||||
|
#
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "${DEBUG}" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
add_cio_channel() {
|
||||||
|
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_cio_channel() {
|
||||||
|
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} [-f -t <dasd_type> ] <ccwid> <online> [use_diag]"
|
||||||
|
echo
|
||||||
|
echo " -f Override safety checks"
|
||||||
|
echo " -t DASD type. Must be provided if -f is used. Only dasd-eckd and"
|
||||||
|
echo " dasd-fba are supported - Deprecated"
|
||||||
|
echo " ccwid = x.y.ssss where"
|
||||||
|
echo " x is always 0 until IBM creates something that uses that number"
|
||||||
|
echo " y is the subchannel set ID (SSID). Most often"
|
||||||
|
echo " this is 0, but it could be non-zero"
|
||||||
|
echo " ssss is the four digit device address of the subchannel, in"
|
||||||
|
echo " hexidecimal, with leading zeros."
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
echo " use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default"
|
||||||
|
echo " 1 to use z/VM DIAG250 I/O"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
|
||||||
|
DASD_FORCE=0
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt --options ft: -n "dasd_configure" -- "$@")
|
||||||
|
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
debug_mesg "All the parms passed were ${ARGS}"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "${1}" in
|
||||||
|
-f) debug_mesg "This used to mean udev rules will always be generated."
|
||||||
|
debug_mesg "For chzdev, it means safety checks will be overridden."
|
||||||
|
debug_mesg "Kinda sorta the same thing, really."
|
||||||
|
PARM_LIST="${PARM_LIST} -f"
|
||||||
|
DASD_FORCE=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-t) debug_mesg "This used to set the card type to ${2}"
|
||||||
|
debug_mesg "Now it gets ignored."
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--) debug_mesg "Found the end of parms indicator: --"
|
||||||
|
shift 1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*) debug_mesg "At the catch-all select entry"
|
||||||
|
debug_mesg "What was selected was ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
CCW_CHAN_ID=${1}
|
||||||
|
ON_OFF=${2}
|
||||||
|
USE_DIAG=${3}
|
||||||
|
|
||||||
|
if [ -z "${CCW_CHAN_ID}" ] || [ -z "${ON_OFF}" ]; then
|
||||||
|
mesg "You didn't specify all the needed parameters."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${USE_DIAG}" ]; then
|
||||||
|
PARM_LIST="${PARM_LIST} use_diag=${USE_DIAG}"
|
||||||
|
else PARM_LIST="${PARM_LIST} use_diag=0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${ON_OFF}" == 0 ]; then
|
||||||
|
debug_mesg "chzdev -d dasd --no-root-update ${CCW_CHAN_ID}"
|
||||||
|
chzdev -d dasd --no-root-update ${CCW_CHAN_ID}
|
||||||
|
elif [ "${ON_OFF}" == 1 ]; then
|
||||||
|
debug_mesg "chzdev -e dasd --no-root-update ${CCW_CHAN_ID} ${PARM_LIST}"
|
||||||
|
chzdev -e dasd --no-root-update ${CCW_CHAN_ID} ${PARM_LIST}
|
||||||
|
else mesg "You must specify a 0 or a 1 for the online/offline attribute."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RC=${?}
|
||||||
|
if [ ${RC} -ne 0 ]; then
|
||||||
|
exit ${RC}
|
||||||
|
elif [ ${ON_OFF} == 1 ]; then
|
||||||
|
exitcode=0
|
||||||
|
# Extract the full busid so that we can reference the proper entries in /sys
|
||||||
|
BUSID=$(/sbin/lszdev dasd ${CCW_CHAN_ID} | /usr/bin/sed -e 1d | /usr/bin/tr -s " " | /usr/bin/cut -f2 -d" " )
|
||||||
|
# Make sure the DASD volume came online
|
||||||
|
for ((counter=0; counter<30; counter++)); do
|
||||||
|
sleep 0.1
|
||||||
|
read online < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${online} -eq 1 ] ; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${online} -ne 1 ]; then
|
||||||
|
debug_mesg "DASD ${CCW_CHAN_ID} did not come online."
|
||||||
|
exit 17
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check to see if the DASD volume is unformatted. If so, let YaST know.
|
||||||
|
read status < /sys/bus/ccw/devices/${BUSID}/status
|
||||||
|
if [ "${status}" == "unformatted" ]; then
|
||||||
|
mesg "DASD ${CCW_CHAN_ID} is unformatted."
|
||||||
|
exitcode=8
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ON_OFF} == 1 ]; then
|
||||||
|
add_cio_channel "${CCW_CHAN_ID}"
|
||||||
|
else remove_cio_channel "${CCW_CHAN_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit ${exitcode}
|
156
dasd_reload.opensuse
Normal file
156
dasd_reload.opensuse
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# dasd_reload
|
||||||
|
# $Id: dasd_reload,v 1.2 2004/05/26 15:17:09 hare Exp $
|
||||||
|
#
|
||||||
|
# Deconfigures all active DASDs, unloads the modules
|
||||||
|
# and activates the configured DASDs again.
|
||||||
|
# Needed to establish an identical device mapping
|
||||||
|
# in the installation system and in the running system.
|
||||||
|
# All DASD access need to be cancelled prior to running
|
||||||
|
# this script.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# dasd_reload
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# 1 Cannot read /proc/modules
|
||||||
|
# 2 Missing module programs
|
||||||
|
# 3 /sys not mounted
|
||||||
|
# 4 Failure on deactivate DASDs
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ ! -r /proc/modules ]; then
|
||||||
|
echo "Cannot read /proc/modules"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x /usr/sbin/rmmod -o ! -x /usr/sbin/modprobe ]; then
|
||||||
|
echo "Missing module programs"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d /sys/bus ]; then
|
||||||
|
echo "sysfs not mounted"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
let anymd=0
|
||||||
|
if [ -f /proc/mdstat ]; then
|
||||||
|
for mddevice in $(grep active /proc/mdstat | cut -f1 -d:); do
|
||||||
|
mdadm -S /dev/${mddevice}
|
||||||
|
let anymd=1
|
||||||
|
done
|
||||||
|
udevadm settle
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setting HyperPAV alias devices offline
|
||||||
|
#
|
||||||
|
dasd_alias=
|
||||||
|
let EXITRC=0
|
||||||
|
for dev in /sys/bus/ccw/devices/*; do
|
||||||
|
if [ -f ${dev}/use_diag ]; then
|
||||||
|
read _online < ${dev}/online
|
||||||
|
read _alias < ${dev}/alias
|
||||||
|
if [ "$_online" -eq 1 -a "$_alias" -eq 1 ]; then
|
||||||
|
echo "setting DASD HyperPAV alias $(basename ${dev}) offline"
|
||||||
|
echo "0" > ${dev}/online
|
||||||
|
read _online < ${dev}/online
|
||||||
|
dasd_alias="${dasd_alias} $(basename ${dev})"
|
||||||
|
if [ "$_online" -eq 1 ]; then
|
||||||
|
echo "failure on setting DASD HyperPAV alias $(basename ${dev}) offline !"
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setting "normal" DASD and HyperPAV base devices offline
|
||||||
|
#
|
||||||
|
dasd_base=
|
||||||
|
for dev in /sys/bus/ccw/devices/*; do
|
||||||
|
if [ -f ${dev}/use_diag ]; then
|
||||||
|
read _online < ${dev}/online
|
||||||
|
read _alias < ${dev}/alias
|
||||||
|
if [ "$_online" -eq 1 -a "$_alias" -eq 0 ]; then
|
||||||
|
echo "setting DASD $(basename ${dev}) offline"
|
||||||
|
echo "0" > ${dev}/online
|
||||||
|
read _online < ${dev}/online
|
||||||
|
dasd_base="${dasd_base} $(basename ${dev})"
|
||||||
|
if [ "$_online" -eq 1 ]; then
|
||||||
|
echo "failure on setting DASD $(basename ${dev}) offline !"
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
udevadm settle
|
||||||
|
|
||||||
|
module_list=
|
||||||
|
module_test_list="dasd_diag_mod dasd_eckd_mod dasd_fba_mod dasd_mod"
|
||||||
|
for module in ${module_test_list}; do
|
||||||
|
if grep -q "${module}" /proc/modules; then
|
||||||
|
module_list="${module} ${module_list}"
|
||||||
|
: Unloading ${module}
|
||||||
|
/usr/sbin/rmmod ${module}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
udevadm settle
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [ -d /etc/udev/rules.d ]; then
|
||||||
|
cd /etc/udev/rules.d
|
||||||
|
#
|
||||||
|
# Re-activating "normal" DASD and HyperPAV base devices
|
||||||
|
#
|
||||||
|
# We need to move all the DASD udev rules out from /etc/udev/rules.d
|
||||||
|
# because if we don't, then when the first DASD volume gets brought
|
||||||
|
# back online, they are all brought back online, in a non-deterministic
|
||||||
|
# order, not the numeric order we expect.
|
||||||
|
#
|
||||||
|
mv -i 41-dasd-*.rules 51-dasd-*.rules /tmp
|
||||||
|
cd /tmp
|
||||||
|
for dasd in ${dasd_base}; do
|
||||||
|
for file in 41-dasd-*-${dasd}.rules 51-dasd-${dasd}.rules; do
|
||||||
|
[ -f "${file}" ] || continue
|
||||||
|
#
|
||||||
|
# Special handling is needed for old udev rules that start with 51-
|
||||||
|
# since the chzdev command won't look for that name
|
||||||
|
#
|
||||||
|
prefix="$(echo ${file} | cut -f1 -d-)"
|
||||||
|
if [ "${prefix}" == "51" ]; then
|
||||||
|
if [ -h /sys/bus/ccw/drivers/dasd-eckd/${dasd} ]; then
|
||||||
|
mv -i ${file} 41-dasd-eckd-${dasd}.rules
|
||||||
|
elif [ -h /sys/bus/ccw/drivers/dasd-fba/${dasd} ]; then
|
||||||
|
mv -i ${file} 41-dasd-fba-${dasd}.rules
|
||||||
|
else echo "DASD volume ${dasd} is neither an ECKD or FBA device."
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo Activating ${dasd}
|
||||||
|
mv -i "${file}" /etc/udev/rules.d/
|
||||||
|
/usr/sbin/chzdev dasd --apply --configured -q --no-root-update ${dasd}
|
||||||
|
lsdasd
|
||||||
|
break
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
#
|
||||||
|
# Re-activating HyperPAV alias devices
|
||||||
|
#
|
||||||
|
for dasd in ${dasd_alias}; do
|
||||||
|
for file in 41-dasd-*-${dasd}.rules 51-dasd-${dasd}.rules; do
|
||||||
|
[ -f "${file}" ] || continue
|
||||||
|
echo Activating ${dasd}
|
||||||
|
mv -i "${file}" /etc/udev/rules.d/
|
||||||
|
/usr/sbin/chzdev dasd --apply --configured -q --no-root-update ${dasd}
|
||||||
|
break
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit ${EXITRC}
|
156
dasd_reload.suse
Normal file
156
dasd_reload.suse
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# dasd_reload
|
||||||
|
# $Id: dasd_reload,v 1.2 2004/05/26 15:17:09 hare Exp $
|
||||||
|
#
|
||||||
|
# Deconfigures all active DASDs, unloads the modules
|
||||||
|
# and activates the configured DASDs again.
|
||||||
|
# Needed to establish an identical device mapping
|
||||||
|
# in the installation system and in the running system.
|
||||||
|
# All DASD access need to be cancelled prior to running
|
||||||
|
# this script.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# dasd_reload
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# 1 Cannot read /proc/modules
|
||||||
|
# 2 Missing module programs
|
||||||
|
# 3 /sys not mounted
|
||||||
|
# 4 Failure on deactivate DASDs
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ ! -r /proc/modules ]; then
|
||||||
|
echo "Cannot read /proc/modules"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x /sbin/rmmod -o ! -x /sbin/modprobe ]; then
|
||||||
|
echo "Missing module programs"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d /sys/bus ]; then
|
||||||
|
echo "sysfs not mounted"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
let anymd=0
|
||||||
|
if [ -f /proc/mdstat ]; then
|
||||||
|
for mddevice in $(grep active /proc/mdstat | cut -f1 -d:); do
|
||||||
|
mdadm -S /dev/${mddevice}
|
||||||
|
let anymd=1
|
||||||
|
done
|
||||||
|
udevadm settle
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setting HyperPAV alias devices offline
|
||||||
|
#
|
||||||
|
dasd_alias=
|
||||||
|
let EXITRC=0
|
||||||
|
for dev in /sys/bus/ccw/devices/*; do
|
||||||
|
if [ -f ${dev}/use_diag ]; then
|
||||||
|
read _online < ${dev}/online
|
||||||
|
read _alias < ${dev}/alias
|
||||||
|
if [ "$_online" -eq 1 -a "$_alias" -eq 1 ]; then
|
||||||
|
echo "setting DASD HyperPAV alias $(basename ${dev}) offline"
|
||||||
|
echo "0" > ${dev}/online
|
||||||
|
read _online < ${dev}/online
|
||||||
|
dasd_alias="${dasd_alias} $(basename ${dev})"
|
||||||
|
if [ "$_online" -eq 1 ]; then
|
||||||
|
echo "failure on setting DASD HyperPAV alias $(basename ${dev}) offline !"
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setting "normal" DASD and HyperPAV base devices offline
|
||||||
|
#
|
||||||
|
dasd_base=
|
||||||
|
for dev in /sys/bus/ccw/devices/*; do
|
||||||
|
if [ -f ${dev}/use_diag ]; then
|
||||||
|
read _online < ${dev}/online
|
||||||
|
read _alias < ${dev}/alias
|
||||||
|
if [ "$_online" -eq 1 -a "$_alias" -eq 0 ]; then
|
||||||
|
echo "setting DASD $(basename ${dev}) offline"
|
||||||
|
echo "0" > ${dev}/online
|
||||||
|
read _online < ${dev}/online
|
||||||
|
dasd_base="${dasd_base} $(basename ${dev})"
|
||||||
|
if [ "$_online" -eq 1 ]; then
|
||||||
|
echo "failure on setting DASD $(basename ${dev}) offline !"
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
udevadm settle
|
||||||
|
|
||||||
|
module_list=
|
||||||
|
module_test_list="dasd_diag_mod dasd_eckd_mod dasd_fba_mod dasd_mod"
|
||||||
|
for module in ${module_test_list}; do
|
||||||
|
if grep -q "${module}" /proc/modules; then
|
||||||
|
module_list="${module} ${module_list}"
|
||||||
|
: Unloading ${module}
|
||||||
|
/sbin/rmmod ${module}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
udevadm settle
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [ -d /etc/udev/rules.d ]; then
|
||||||
|
cd /etc/udev/rules.d
|
||||||
|
#
|
||||||
|
# Re-activating "normal" DASD and HyperPAV base devices
|
||||||
|
#
|
||||||
|
# We need to move all the DASD udev rules out from /etc/udev/rules.d
|
||||||
|
# because if we don't, then when the first DASD volume gets brought
|
||||||
|
# back online, they are all brought back online, in a non-deterministic
|
||||||
|
# order, not the numeric order we expect.
|
||||||
|
#
|
||||||
|
mv -i 41-dasd-*.rules 51-dasd-*.rules /tmp
|
||||||
|
cd /tmp
|
||||||
|
for dasd in ${dasd_base}; do
|
||||||
|
for file in 41-dasd-*-${dasd}.rules 51-dasd-${dasd}.rules; do
|
||||||
|
[ -f "${file}" ] || continue
|
||||||
|
#
|
||||||
|
# Special handling is needed for old udev rules that start with 51-
|
||||||
|
# since the chzdev command won't look for that name
|
||||||
|
#
|
||||||
|
prefix="$(echo ${file} | cut -f1 -d-)"
|
||||||
|
if [ "${prefix}" == "51" ]; then
|
||||||
|
if [ -h /sys/bus/ccw/drivers/dasd-eckd/${dasd} ]; then
|
||||||
|
mv -i ${file} 41-dasd-eckd-${dasd}.rules
|
||||||
|
elif [ -h /sys/bus/ccw/drivers/dasd-fba/${dasd} ]; then
|
||||||
|
mv -i ${file} 41-dasd-fba-${dasd}.rules
|
||||||
|
else echo "DASD volume ${dasd} is neither an ECKD or FBA device."
|
||||||
|
let EXITRC=4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo Activating ${dasd}
|
||||||
|
mv -i "${file}" /etc/udev/rules.d/
|
||||||
|
/sbin/chzdev dasd --apply --configured -q --no-root-update ${dasd}
|
||||||
|
lsdasd
|
||||||
|
break
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
#
|
||||||
|
# Re-activating HyperPAV alias devices
|
||||||
|
#
|
||||||
|
for dasd in ${dasd_alias}; do
|
||||||
|
for file in 41-dasd-*-${dasd}.rules 51-dasd-${dasd}.rules; do
|
||||||
|
[ -f "${file}" ] || continue
|
||||||
|
echo Activating ${dasd}
|
||||||
|
mv -i "${file}" /etc/udev/rules.d/
|
||||||
|
/sbin/chzdev dasd --apply --configured -q --no-root-update ${dasd}
|
||||||
|
break
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit ${EXITRC}
|
20
dasdro
Normal file
20
dasdro
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# checks DASD accessibility in VM and sets Linux-side readonly attributes
|
||||||
|
# accordingly
|
||||||
|
|
||||||
|
modprobe -q vmcp
|
||||||
|
|
||||||
|
vmcp q v dasd 2>/dev/null >/dev/null || exit 0 # not running in VM
|
||||||
|
|
||||||
|
vmcp q v dasd | while read x dev rest
|
||||||
|
do
|
||||||
|
dev=`echo $dev|tr A-F a-f`
|
||||||
|
roattr=/sys/bus/ccw/devices/?.?.$dev/readonly
|
||||||
|
test -e $roattr || continue
|
||||||
|
if echo "$rest"|grep -q R/O
|
||||||
|
then
|
||||||
|
echo 1 >$roattr
|
||||||
|
else
|
||||||
|
echo 0 >$roattr
|
||||||
|
fi
|
||||||
|
done
|
157
detach_disks.sh.opensuse
Normal file
157
detach_disks.sh.opensuse
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DASDFILE=/tmp/dasd.list.$(mcookie)
|
||||||
|
DETFILE=/tmp/detach.disks.$(mcookie)
|
||||||
|
KEEPFILE=/tmp/keep.disks.$(mcookie)
|
||||||
|
NICFILE=/tmp/nic.list.$(mcookie)
|
||||||
|
FAILFILE=/tmp/error.$(mcookie)
|
||||||
|
|
||||||
|
function expand_RANGE(){
|
||||||
|
local RANGE=${1}
|
||||||
|
local RANGE_SAVE=${RANGE}
|
||||||
|
local DEVNO
|
||||||
|
local BEGIN=0
|
||||||
|
local END=0
|
||||||
|
|
||||||
|
RANGE=$(IFS=":-"; echo ${RANGE} | cut -f1-2 -d" " )
|
||||||
|
set -- ${RANGE}
|
||||||
|
let BEGIN=0x$1 2>/dev/null
|
||||||
|
let END=0x$2 2>/dev/null
|
||||||
|
|
||||||
|
if [ ${BEGIN} -eq 0 ] || [ ${END} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number range was specified: ${RANGE_SAVE}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for DEVNO in $(eval echo {${BEGIN}..${END}})
|
||||||
|
do printf "%d\n" ${DEVNO}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage(){
|
||||||
|
echo "Usage: ${0} [ -F ] [ -q ] [ -h ]"
|
||||||
|
echo " -F Exit with failure if any invalid parms are detected."
|
||||||
|
echo " -q Don't generate any output."
|
||||||
|
echo " -h Display this help message."
|
||||||
|
}
|
||||||
|
|
||||||
|
msg="echo"
|
||||||
|
let FORCE_FAIL=0
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt -a --options Fhq -n "detach_devices" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
usage
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
for ARG; do
|
||||||
|
case "${ARG}" in
|
||||||
|
-F) let FORCE_FAIL=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-h) usage;
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-q) msg="/bin/true"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--) shift 1
|
||||||
|
;;
|
||||||
|
*) ${msg} "Extraneous input detected: ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -r /etc/sysconfig/virtsetup ]; then
|
||||||
|
. /etc/sysconfig/virtsetup
|
||||||
|
else ${msg} "No /etc/sysconfig/virtsetup file was found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First, get a list of all the DASD devices we have for this guest, in decimal.
|
||||||
|
# (Trying to handle things in hex gets complicated.)
|
||||||
|
/usr/sbin/vmcp -b1048576 q v dasd | cut -f2 -d" " |\
|
||||||
|
while read HEXNO
|
||||||
|
do let DECNO=0x${HEXNO}
|
||||||
|
echo ${DECNO}
|
||||||
|
done > ${DASDFILE} 2>/dev/null
|
||||||
|
|
||||||
|
# If the system administrator specified certain devices to be detached
|
||||||
|
# let's put those device numbers in a file, one per line.
|
||||||
|
touch ${DETFILE}
|
||||||
|
for ADDR in $(IFS=", " ; echo ${ZVM_DISKS_TO_DETACH})
|
||||||
|
do if $(echo ${ADDR} | grep -iqE ":|-" 2>/dev/null)
|
||||||
|
then expand_RANGE ${ADDR} >> ${DETFILE}
|
||||||
|
else let DEVNO=0
|
||||||
|
let DEVNO=0x${ADDR} 2>/dev/null
|
||||||
|
if [ ${DEVNO} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number was specified: ${ADDR}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
else printf "%d\n" ${DEVNO}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done > ${DETFILE}
|
||||||
|
|
||||||
|
# If the system administrator specified certain devices that should _not_
|
||||||
|
# be detached, let's put those in another file, one per line.
|
||||||
|
touch ${KEEPFILE}
|
||||||
|
for ADDR in $(IFS=", " ; echo ${ZVM_DISKS_TO_NOT_DETACH})
|
||||||
|
do if $(echo ${ADDR} | grep -iqE ":|-" 2>/dev/null)
|
||||||
|
then expand_RANGE ${ADDR} >> ${KEEPFILE}
|
||||||
|
else let DEVNO=0
|
||||||
|
let DEVNO=0x${ADDR} 2>/dev/null
|
||||||
|
if [ ${DEVNO} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number was specified: ${ADDR}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
else printf "%d\n" ${DEVNO}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done > ${KEEPFILE}
|
||||||
|
|
||||||
|
if [ ${FORCE_FAIL} -eq 1 ] && [ -e ${FAILFILE} ]; then
|
||||||
|
let RETURN_CODE=1
|
||||||
|
${msg} "Terminating detach_disk because of input errors."
|
||||||
|
else
|
||||||
|
# If the system administrator specified that all "unused" disks should be
|
||||||
|
# detached, compare the disks lsdasd show as activated to the complete
|
||||||
|
# list of disks we have currently, and add the inactive ones to the
|
||||||
|
# file containing devices to be detached
|
||||||
|
if [ "${ZVM_DETACH_ALL_UNUSED}" == "yes" ]; then
|
||||||
|
lsdasd -s | sed -e 1,2d | cut -f1 -d" " | \
|
||||||
|
while read ADDR
|
||||||
|
do let DEVNO=0x${ADDR}
|
||||||
|
sed -i -e "/^${DEVNO}$/d" ${DASDFILE}
|
||||||
|
done
|
||||||
|
cat ${DASDFILE} >> ${DETFILE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now remove any "to be kept" disks from the detach file
|
||||||
|
while read DEVNO
|
||||||
|
do sed -i -e "/^${DEVNO}/d" ${DETFILE}
|
||||||
|
done < ${KEEPFILE}
|
||||||
|
|
||||||
|
# Get a list of all the virtual NICs since they require an
|
||||||
|
# extra keyword to detach. Contrary to what we've done before
|
||||||
|
# these will be hex values
|
||||||
|
/usr/sbin/vmcp -b1048576 q nic | grep Adapter | cut -f2 -d" " | cut -f1 -d. > ${NICFILE}
|
||||||
|
|
||||||
|
# Now we sort the device numbers and detach them.
|
||||||
|
sort -un ${DETFILE} | \
|
||||||
|
while read DEVNO
|
||||||
|
do HEXNO=$(printf %04X ${DEVNO})
|
||||||
|
if grep -q ^${HEXNO}$ ${NICFILE} 2>/dev/null ; then
|
||||||
|
vmcp detach nic ${HEXNO} 2>/dev/null
|
||||||
|
else vmcp detach ${HEXNO} 2>/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
let RETURN_CODE=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f ${DASDFILE} ${DETFILE} ${KEEPFILE} ${NICFILE} ${FAILFILE}
|
||||||
|
exit ${RETURN_CODE}
|
157
detach_disks.sh.suse
Normal file
157
detach_disks.sh.suse
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DASDFILE=/tmp/dasd.list.$(mcookie)
|
||||||
|
DETFILE=/tmp/detach.disks.$(mcookie)
|
||||||
|
KEEPFILE=/tmp/keep.disks.$(mcookie)
|
||||||
|
NICFILE=/tmp/nic.list.$(mcookie)
|
||||||
|
FAILFILE=/tmp/error.$(mcookie)
|
||||||
|
|
||||||
|
function expand_RANGE(){
|
||||||
|
local RANGE=${1}
|
||||||
|
local RANGE_SAVE=${RANGE}
|
||||||
|
local DEVNO
|
||||||
|
local BEGIN=0
|
||||||
|
local END=0
|
||||||
|
|
||||||
|
RANGE=$(IFS=":-"; echo ${RANGE} | cut -f1-2 -d" " )
|
||||||
|
set -- ${RANGE}
|
||||||
|
let BEGIN=0x$1 2>/dev/null
|
||||||
|
let END=0x$2 2>/dev/null
|
||||||
|
|
||||||
|
if [ ${BEGIN} -eq 0 ] || [ ${END} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number range was specified: ${RANGE_SAVE}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for DEVNO in $(eval echo {${BEGIN}..${END}})
|
||||||
|
do printf "%d\n" ${DEVNO}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage(){
|
||||||
|
echo "Usage: ${0} [ -F ] [ -q ] [ -h ]"
|
||||||
|
echo " -F Exit with failure if any invalid parms are detected."
|
||||||
|
echo " -q Don't generate any output."
|
||||||
|
echo " -h Display this help message."
|
||||||
|
}
|
||||||
|
|
||||||
|
msg="echo"
|
||||||
|
let FORCE_FAIL=0
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt -a --options Fhq -n "detach_devices" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
usage
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
for ARG; do
|
||||||
|
case "${ARG}" in
|
||||||
|
-F) let FORCE_FAIL=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-h) usage;
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-q) msg="/bin/true"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--) shift 1
|
||||||
|
;;
|
||||||
|
*) ${msg} "Extraneous input detected: ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -r /etc/sysconfig/virtsetup ]; then
|
||||||
|
. /etc/sysconfig/virtsetup
|
||||||
|
else ${msg} "No /etc/sysconfig/virtsetup file was found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First, get a list of all the DASD devices we have for this guest, in decimal.
|
||||||
|
# (Trying to handle things in hex gets complicated.)
|
||||||
|
/sbin/vmcp -b1048576 q v dasd | cut -f2 -d" " |\
|
||||||
|
while read HEXNO
|
||||||
|
do let DECNO=0x${HEXNO}
|
||||||
|
echo ${DECNO}
|
||||||
|
done > ${DASDFILE} 2>/dev/null
|
||||||
|
|
||||||
|
# If the system administrator specified certain devices to be detached
|
||||||
|
# let's put those device numbers in a file, one per line.
|
||||||
|
touch ${DETFILE}
|
||||||
|
for ADDR in $(IFS=", " ; echo ${ZVM_DISKS_TO_DETACH})
|
||||||
|
do if $(echo ${ADDR} | grep -iqE ":|-" 2>/dev/null)
|
||||||
|
then expand_RANGE ${ADDR} >> ${DETFILE}
|
||||||
|
else let DEVNO=0
|
||||||
|
let DEVNO=0x${ADDR} 2>/dev/null
|
||||||
|
if [ ${DEVNO} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number was specified: ${ADDR}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
else printf "%d\n" ${DEVNO}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done > ${DETFILE}
|
||||||
|
|
||||||
|
# If the system administrator specified certain devices that should _not_
|
||||||
|
# be detached, let's put those in another file, one per line.
|
||||||
|
touch ${KEEPFILE}
|
||||||
|
for ADDR in $(IFS=", " ; echo ${ZVM_DISKS_TO_NOT_DETACH})
|
||||||
|
do if $(echo ${ADDR} | grep -iqE ":|-" 2>/dev/null)
|
||||||
|
then expand_RANGE ${ADDR} >> ${KEEPFILE}
|
||||||
|
else let DEVNO=0
|
||||||
|
let DEVNO=0x${ADDR} 2>/dev/null
|
||||||
|
if [ ${DEVNO} -eq 0 ]; then
|
||||||
|
${msg} "An invalid device number was specified: ${ADDR}" >&2
|
||||||
|
touch ${FAILFILE}
|
||||||
|
else printf "%d\n" ${DEVNO}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done > ${KEEPFILE}
|
||||||
|
|
||||||
|
if [ ${FORCE_FAIL} -eq 1 ] && [ -e ${FAILFILE} ]; then
|
||||||
|
let RETURN_CODE=1
|
||||||
|
${msg} "Terminating detach_disk because of input errors."
|
||||||
|
else
|
||||||
|
# If the system administrator specified that all "unused" disks should be
|
||||||
|
# detached, compare the disks lsdasd show as activated to the complete
|
||||||
|
# list of disks we have currently, and add the inactive ones to the
|
||||||
|
# file containing devices to be detached
|
||||||
|
if [ "${ZVM_DETACH_ALL_UNUSED}" == "yes" ]; then
|
||||||
|
lsdasd -s | sed -e 1,2d | cut -f1 -d" " | \
|
||||||
|
while read ADDR
|
||||||
|
do let DEVNO=0x${ADDR}
|
||||||
|
sed -i -e "/^${DEVNO}$/d" ${DASDFILE}
|
||||||
|
done
|
||||||
|
cat ${DASDFILE} >> ${DETFILE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now remove any "to be kept" disks from the detach file
|
||||||
|
while read DEVNO
|
||||||
|
do sed -i -e "/^${DEVNO}/d" ${DETFILE}
|
||||||
|
done < ${KEEPFILE}
|
||||||
|
|
||||||
|
# Get a list of all the virtual NICs since they require an
|
||||||
|
# extra keyword to detach. Contrary to what we've done before
|
||||||
|
# these will be hex values
|
||||||
|
/sbin/vmcp -b1048576 q nic | grep Adapter | cut -f2 -d" " | cut -f1 -d. > ${NICFILE}
|
||||||
|
|
||||||
|
# Now we sort the device numbers and detach them.
|
||||||
|
sort -un ${DETFILE} | \
|
||||||
|
while read DEVNO
|
||||||
|
do HEXNO=$(printf %04X ${DEVNO})
|
||||||
|
if grep -q ^${HEXNO}$ ${NICFILE} 2>/dev/null ; then
|
||||||
|
vmcp detach nic ${HEXNO} 2>/dev/null
|
||||||
|
else vmcp detach ${HEXNO} 2>/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
let RETURN_CODE=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f ${DASDFILE} ${DETFILE} ${KEEPFILE} ${NICFILE} ${FAILFILE}
|
||||||
|
exit ${RETURN_CODE}
|
181
hsnc
Normal file
181
hsnc
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# Copyright (c) 2003 SUSE LINUX AG Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Please send feedback to http://www.suse.de/feedback/
|
||||||
|
#
|
||||||
|
# /etc/init.d/hsnc
|
||||||
|
#
|
||||||
|
# and symbolic its link
|
||||||
|
#
|
||||||
|
# /usr/sbin/ip_watcher.pl
|
||||||
|
# /usr/sbin/xcec-bridge
|
||||||
|
# /usr/sbin/start_hsnc.sh
|
||||||
|
# /use/sbin/rchsnc
|
||||||
|
#
|
||||||
|
# System startup script for the HiperSockets Network Concentrator
|
||||||
|
#
|
||||||
|
# /etc/hsnc.conf should contain the following lines:
|
||||||
|
#
|
||||||
|
# operating_mode=[unicast|full|no]
|
||||||
|
# unicast means, only unicast forwarded between the hsint's and osaint's.
|
||||||
|
# this is the default mode
|
||||||
|
# full means, unicast, multicast and broadcast are forwarded, if supported
|
||||||
|
# by the hardware
|
||||||
|
#
|
||||||
|
# hsi_int="<interface> [<interface> [...]]"
|
||||||
|
# described all the HiperSockets interfaces involved in the HSN
|
||||||
|
#
|
||||||
|
# osa_int="<interface>"
|
||||||
|
# describes the OSA interface connecting to other LANs
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
START_HSNC_BIN=/usr/sbin/start_hsnc.sh
|
||||||
|
IP_WATCHER_BIN=/usr/sbin/ip_watcher.pl
|
||||||
|
XCEC_BRIDGE_BIN=/usr/sbin/xcec-bridge
|
||||||
|
|
||||||
|
HSNC_CONFIG_FILE=/etc/sysconfig/hsnc
|
||||||
|
HSNC_CLEANUP_FILE=/var/run/hsnc.cleanup
|
||||||
|
|
||||||
|
test -x $START_HSNC_BIN || exit 5
|
||||||
|
test -x $IP_WATCHER_BIN || exit 5
|
||||||
|
test -x $XCEC_BRIDGE_BIN || exit 5
|
||||||
|
|
||||||
|
# Return values acc. to LSB for all commands but status:
|
||||||
|
# 0 - success
|
||||||
|
# 1 - generic or unspecified error
|
||||||
|
# 2 - invalid or excess argument(s)
|
||||||
|
# 3 - unimplemented feature (e.g. "reload")
|
||||||
|
# 4 - insufficient privilege
|
||||||
|
# 5 - program is not installed
|
||||||
|
# 6 - program is not configured
|
||||||
|
# 7 - program is not running
|
||||||
|
#
|
||||||
|
# Note that starting an already running service, stopping
|
||||||
|
# or restarting a not-running service as well as the restart
|
||||||
|
# with force-reload (in case signalling is not supported) are
|
||||||
|
# considered a success.
|
||||||
|
|
||||||
|
#call with cleanup or not
|
||||||
|
read_config_file() {
|
||||||
|
if [ "$1" == "cleanup" ]; then
|
||||||
|
file=$HSNC_CLEANUP_FILE
|
||||||
|
else
|
||||||
|
file=$HSNC_CONFIG_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s $file ]; then
|
||||||
|
source $file
|
||||||
|
else
|
||||||
|
echo -ne "\nCannot read $file: empty or nonexistant! "
|
||||||
|
# Means not configured:
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#call with cleanup or not
|
||||||
|
set_osa_mode() {
|
||||||
|
# for full mode, we set up the osa as multicast router. otherwise, no
|
||||||
|
# special setup is required for the osa.
|
||||||
|
if [ "$operating_mode" == "full" ]; then
|
||||||
|
if [ "$1" == "cleanup" ]; then
|
||||||
|
echo no_router > /sys/class/net/$osa_int/device/route4
|
||||||
|
else
|
||||||
|
echo multicast_router > /sys/class/net/$osa_int/device/route4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#call with cleanup or not
|
||||||
|
set_hsi_mode() {
|
||||||
|
# set all the involved HiperSockets interfaces as primary_connector. For
|
||||||
|
# special HA setups, some more tweaking is needed, but then a handcarved
|
||||||
|
# solution should be used anyway.
|
||||||
|
for i in $hsi_int ; do
|
||||||
|
if [ "$1" == "cleanup" ]; then
|
||||||
|
echo no_router > /sys/class/net/$i/device/route4
|
||||||
|
else
|
||||||
|
echo primary_connector > /sys/class/net/$i/device/route4
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
do_start_hsnc() {
|
||||||
|
set_osa_mode
|
||||||
|
set_hsi_mode
|
||||||
|
if [ "$operating_mode" == "full" ]; then
|
||||||
|
$IP_WATCHER_BIN --check
|
||||||
|
else
|
||||||
|
$IP_WATCHER_BIN --check $osa_int
|
||||||
|
fi
|
||||||
|
CODE=$?
|
||||||
|
if [ $CODE != 0 ]; then
|
||||||
|
return $CODE
|
||||||
|
else
|
||||||
|
cp $HSNC_CONFIG_FILE $HSNC_CLEANUP_FILE
|
||||||
|
#
|
||||||
|
# To match the LSB spec, startproc returns 0,
|
||||||
|
# even if the program it already running.
|
||||||
|
#
|
||||||
|
if [ "$operating_mode" == "full" ]; then
|
||||||
|
startproc $START_HSNC_BIN
|
||||||
|
else
|
||||||
|
startproc $START_HSNC_BIN $osa_int
|
||||||
|
fi
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
service="HiperSockets Network concentrator"
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
if checkproc $START_HSNC_BIN; then
|
||||||
|
# Starting an already running service is success:
|
||||||
|
echo -n "(already running)"
|
||||||
|
else
|
||||||
|
if read_config_file; then
|
||||||
|
do_start_hsnc
|
||||||
|
RETVAL=$?
|
||||||
|
exit $RETVAL
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
echo -n "Shutting down $service "
|
||||||
|
|
||||||
|
# kill ip_watcher, start_hsnc, which started it needs cleans up
|
||||||
|
# then:
|
||||||
|
killproc -TERM $IP_WATCHER_BIN
|
||||||
|
if [ -f $HSNC_CLEANUP_FILE ]; then
|
||||||
|
read_config_file cleanup
|
||||||
|
# remove all connector settings(not yet implemented):
|
||||||
|
set_osa_mode cleanup
|
||||||
|
set_hsi_mode cleanup
|
||||||
|
|
||||||
|
# remove the file in /var/run
|
||||||
|
rm -f $HSNC_CLEANUP_FILE
|
||||||
|
else
|
||||||
|
echo -n "- no cleanup file found "
|
||||||
|
fi
|
||||||
|
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
echo -n "Checking $service "
|
||||||
|
## Check status with checkproc(8), if process is running
|
||||||
|
## checkproc will return with exit status 0.
|
||||||
|
|
||||||
|
# Status has a slightly different for the status command:
|
||||||
|
# 0 - service running
|
||||||
|
# 1 - service dead, but /var/run/ pid file exists
|
||||||
|
# 2 - service dead, but /var/lock/ lock file exists
|
||||||
|
# 3 - service not running
|
||||||
|
|
||||||
|
# NOTE: checkproc returns LSB compliant status values.
|
||||||
|
checkproc $START_HSNC_BIN
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
16
hsnc.service
Normal file
16
hsnc.service
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Start the qeth HiperSockets Network Concentrator
|
||||||
|
After=network-online.target remote-fs.target
|
||||||
|
Wants=network-online.target remote-fs.target
|
||||||
|
ConditionPathExists=/sys/devices/qeth
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
ExecStart=/usr/lib/systemd/scripts/hsnc start
|
||||||
|
ExecStartPost=/usr/lib/systemd/scripts/hsnc status
|
||||||
|
ExecStop=/usr/lib/systemd/scripts/hsnc stop
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
64
iucv_configure.8
Normal file
64
iucv_configure.8
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
.TH iucv_configure "8" "July 2013" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
iucv_configure \- Configures or deconfigures a z/VM Inter-User Communications Vehicle (IUCV) network adapter
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B iucv_configure peer_userid online
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B iucv_configure
|
||||||
|
is intended to make it easy to persistently add and remove z/VM point-to-point IUCV network adapters. In addition to bringing the adapter online or offline, it will also create or delete the necessary udev rules for the adapter.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP peer_userid
|
||||||
|
The z/VM userid of the virtual machine on the other end of the point-to-point connection.
|
||||||
|
.IP online
|
||||||
|
Either a literal 1 to bring the adapter online or a literal 0 to take it offline
|
||||||
|
.SH FILES
|
||||||
|
.I /etc/udev/rules.d/51-iucv-<peer_userid>.rules
|
||||||
|
.RS
|
||||||
|
This file provide the udev rules necessary to activate a specific IUCV adapter.
|
||||||
|
.RE
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.IP DEBUG
|
||||||
|
If set to "yes" some minimal debugging information is output during execution.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
The following messages may be issued on stdout:
|
||||||
|
.IP
|
||||||
|
.B /sysfs not present
|
||||||
|
.RS
|
||||||
|
The sysfs file system could not be found in /proc/mounts, so there's nothing the script can
|
||||||
|
do. Return code 1 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B No IUCV user name given
|
||||||
|
.RS
|
||||||
|
You didn't specify the z/VM userid of the virtual machine to which you want to connect. Return code 2 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Unable to connect to $PEER_USERID
|
||||||
|
.RS
|
||||||
|
The attempt to connect to the IUCV peer failed. Try "dmesg" to see if there is any indication why. Return code 3 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Unable to remove device $netdev
|
||||||
|
.RS
|
||||||
|
The attempt to remove the IUCV adapter failed. Try "dmesg" to see if there is any indication why. Return code 4 is set.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
If environment variable DEBUG is set to "yes," the following messages may be issued on stdout:
|
||||||
|
.IP
|
||||||
|
.B
|
||||||
|
Configuring IUCV device ${PEER_USERID}
|
||||||
|
.RS
|
||||||
|
Just a little bit of verbosity, since it just indicates that we got past certain error checks and will now try to do something useful.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Configured device $iucvdev
|
||||||
|
.RS
|
||||||
|
The attempt to create the IUCV adapter was successful.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Removed device $iucvdev
|
||||||
|
.RS
|
||||||
|
The attempt to remove the IUCV adapter was successful.
|
||||||
|
.RE
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
133
iucv_configure.opensuse
Normal file
133
iucv_configure.opensuse
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# iucv_configure
|
||||||
|
#
|
||||||
|
# Configures a z/VM IUCV network adapter
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# iucv_configure <peer_userid> <online>
|
||||||
|
#
|
||||||
|
# peer_userid = z/VM userid of the IUCV peer
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# 1 sysfs not mounted
|
||||||
|
# 2 Invalid status for <online>
|
||||||
|
# 3 Could not create iucv device
|
||||||
|
# 4 Could not remove iucv device
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "$DEBUG" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -ne 2 ] ; then
|
||||||
|
echo "Usage: $0 <peer_userid> <online>"
|
||||||
|
echo " peer_userid = z/VM userid of the IUCV peer"
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the mount point for sysfs
|
||||||
|
while read MNTPT MNTDIR MNTSYS MNTTYPE; do
|
||||||
|
if test "$MNTSYS" = "sysfs"; then
|
||||||
|
SYSFS="$MNTDIR"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done </proc/mounts
|
||||||
|
|
||||||
|
if [ -z "$SYSFS" ]; then
|
||||||
|
mesg "/sysfs not present"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PEER_USERID_LOWER=$1
|
||||||
|
PEER_USERID=$(echo $1 | tr "a-z" "A-Z")
|
||||||
|
ONLINE=$2
|
||||||
|
|
||||||
|
if [ -z "$PEER_USERID" ] ; then
|
||||||
|
mesg "No IUCV user name given"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ONLINE" ] ; then
|
||||||
|
ONLINE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_iucv_dir=${SYSFS}/bus/iucv/devices
|
||||||
|
|
||||||
|
_iucv_drv=${SYSFS}/bus/iucv/drivers/netiucv
|
||||||
|
if [ ! -d "$_iucv_drv" ] ; then
|
||||||
|
modprobe -q netiucv
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_mesg "Configuring IUCV device ${PEER_USERID}"
|
||||||
|
|
||||||
|
for _iucv_dev in $_iucv_dir/netiucv?* ; do
|
||||||
|
[ -d $_iucv_dev ] || continue
|
||||||
|
read user < $_iucv_dev/user
|
||||||
|
if [ "$user" = "$PEER_USERID" ] ; then
|
||||||
|
# Already configured, ok
|
||||||
|
iucvdev=${_iucv_dev##*/}
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$iucvdev" -a $ONLINE -eq 1 ] ; then
|
||||||
|
echo $PEER_USERID > $_iucv_drv/connection
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
mesg "Unable to connect to $PEER_USERID"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
for _iucv_dev in $_iucv_dir/netiucv?* ; do
|
||||||
|
[ -d $_iucv_dev ] || continue
|
||||||
|
read user < $_iucv_dev/user
|
||||||
|
if [ "$user" = "$PEER_USERID" ] ; then
|
||||||
|
iucvdev=${_iucv_dev##*/}
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$iucvdev" ] ; then
|
||||||
|
debug_mesg "Configured device $iucvdev"
|
||||||
|
fi
|
||||||
|
elif [ "$iucvdev" -a $ONLINE -eq 0 ] ; then
|
||||||
|
for _net_dev in $_iucv_dir/$iucvdev/net/* ; do
|
||||||
|
[ -d $_net_dev ] || continue
|
||||||
|
netdev=${_net_dev##*/}
|
||||||
|
break;
|
||||||
|
done
|
||||||
|
if [ "$netdev" ] ; then
|
||||||
|
echo $netdev > $_iucv_drv/remove
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
mesg "Unable to remove device $netdev"
|
||||||
|
exit 4
|
||||||
|
else
|
||||||
|
debug_mesg "Removed device $iucvdev"
|
||||||
|
rm -f /etc/udev/rules.d/51-iucv-$PEER_USERID.rules /etc/udev/rules.d/51-iucv-$PEER_USERID_LOWER.rules
|
||||||
|
iucvdev=
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$iucvdev" ] ; then
|
||||||
|
cat > /etc/udev/rules.d/51-iucv-$PEER_USERID.rules <<EOF
|
||||||
|
ACTION=="add", SUBSYSTEM=="subsystem", KERNEL=="iucv", RUN+="/usr/sbin/modprobe netiucv"
|
||||||
|
ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="netiucv", ATTR{connection}="$PEER_USERID"
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit
|
133
iucv_configure.suse
Normal file
133
iucv_configure.suse
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# iucv_configure
|
||||||
|
#
|
||||||
|
# Configures a z/VM IUCV network adapter
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# iucv_configure <peer_userid> <online>
|
||||||
|
#
|
||||||
|
# peer_userid = z/VM userid of the IUCV peer
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# 1 sysfs not mounted
|
||||||
|
# 2 Invalid status for <online>
|
||||||
|
# 3 Could not create iucv device
|
||||||
|
# 4 Could not remove iucv device
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "$DEBUG" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -ne 2 ] ; then
|
||||||
|
echo "Usage: $0 <peer_userid> <online>"
|
||||||
|
echo " peer_userid = z/VM userid of the IUCV peer"
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the mount point for sysfs
|
||||||
|
while read MNTPT MNTDIR MNTSYS MNTTYPE; do
|
||||||
|
if test "$MNTSYS" = "sysfs"; then
|
||||||
|
SYSFS="$MNTDIR"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done </proc/mounts
|
||||||
|
|
||||||
|
if [ -z "$SYSFS" ]; then
|
||||||
|
mesg "/sysfs not present"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PEER_USERID_LOWER=$1
|
||||||
|
PEER_USERID=$(echo $1 | tr "a-z" "A-Z")
|
||||||
|
ONLINE=$2
|
||||||
|
|
||||||
|
if [ -z "$PEER_USERID" ] ; then
|
||||||
|
mesg "No IUCV user name given"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ONLINE" ] ; then
|
||||||
|
ONLINE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_iucv_dir=${SYSFS}/bus/iucv/devices
|
||||||
|
|
||||||
|
_iucv_drv=${SYSFS}/bus/iucv/drivers/netiucv
|
||||||
|
if [ ! -d "$_iucv_drv" ] ; then
|
||||||
|
modprobe -q netiucv
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_mesg "Configuring IUCV device ${PEER_USERID}"
|
||||||
|
|
||||||
|
for _iucv_dev in $_iucv_dir/netiucv?* ; do
|
||||||
|
[ -d $_iucv_dev ] || continue
|
||||||
|
read user < $_iucv_dev/user
|
||||||
|
if [ "$user" = "$PEER_USERID" ] ; then
|
||||||
|
# Already configured, ok
|
||||||
|
iucvdev=${_iucv_dev##*/}
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$iucvdev" -a $ONLINE -eq 1 ] ; then
|
||||||
|
echo $PEER_USERID > $_iucv_drv/connection
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
mesg "Unable to connect to $PEER_USERID"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
for _iucv_dev in $_iucv_dir/netiucv?* ; do
|
||||||
|
[ -d $_iucv_dev ] || continue
|
||||||
|
read user < $_iucv_dev/user
|
||||||
|
if [ "$user" = "$PEER_USERID" ] ; then
|
||||||
|
iucvdev=${_iucv_dev##*/}
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$iucvdev" ] ; then
|
||||||
|
debug_mesg "Configured device $iucvdev"
|
||||||
|
fi
|
||||||
|
elif [ "$iucvdev" -a $ONLINE -eq 0 ] ; then
|
||||||
|
for _net_dev in $_iucv_dir/$iucvdev/net/* ; do
|
||||||
|
[ -d $_net_dev ] || continue
|
||||||
|
netdev=${_net_dev##*/}
|
||||||
|
break;
|
||||||
|
done
|
||||||
|
if [ "$netdev" ] ; then
|
||||||
|
echo $netdev > $_iucv_drv/remove
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
mesg "Unable to remove device $netdev"
|
||||||
|
exit 4
|
||||||
|
else
|
||||||
|
debug_mesg "Removed device $iucvdev"
|
||||||
|
rm -f /etc/udev/rules.d/51-iucv-$PEER_USERID.rules /etc/udev/rules.d/51-iucv-$PEER_USERID_LOWER.rules
|
||||||
|
iucvdev=
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$iucvdev" ] ; then
|
||||||
|
cat > /etc/udev/rules.d/51-iucv-$PEER_USERID.rules <<EOF
|
||||||
|
ACTION=="add", SUBSYSTEM=="subsystem", KERNEL=="iucv", RUN+="/sbin/modprobe netiucv"
|
||||||
|
ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="netiucv", ATTR{connection}="$PEER_USERID"
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit
|
217
killcdl.opensuse
Normal file
217
killcdl.opensuse
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# Released under the GNU General Public License version 2.
|
||||||
|
#
|
||||||
|
|
||||||
|
let FORCE=0
|
||||||
|
DEVPARM=""
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} [ -f ] devno|busid"
|
||||||
|
echo " -f Force unformatting for DASD volumes in the CMS device range of 19x."
|
||||||
|
echo " devno The \"plain\" device number of the volume, e.g. 3184."
|
||||||
|
echo " busid The full specification of the volume, e.g., 0.0.3184."
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCH="$(/usr/bin/uname -m)"
|
||||||
|
if [ "${ARCH}" != "s390x" ] && [ "${ARCH}" != "s390" ]; then
|
||||||
|
echo "This script is only useful on IBM mainframes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt -a --options f -n "killcdl" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
usage
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
for ARG; do
|
||||||
|
case "${ARG}" in
|
||||||
|
-f) FORCE=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--) shift 1
|
||||||
|
;;
|
||||||
|
[0-9a-fA-F]*) if [ ! -z "${DEVPARM}" ]; then
|
||||||
|
echo "More than one parameter specified."
|
||||||
|
usage
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
DEVPARM=${1}
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
*) echo "That looks invalid"
|
||||||
|
usage
|
||||||
|
exit 5
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${DEVPARM}" ]; then
|
||||||
|
echo "You must specify the device number of the DASD volume to be unformatted."
|
||||||
|
usage
|
||||||
|
exit 6
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEVNO=$(echo "${DEVPARM}" | tr A-Z a-z)
|
||||||
|
|
||||||
|
# Validate the device number or busid provided
|
||||||
|
set -- $(IFS='.'; echo ${DEVNO})
|
||||||
|
let NUMPARMS=${#}
|
||||||
|
if [ ${NUMPARMS} -ne 1 ] && [ ${NUMPARMS} -ne 3 ]; then
|
||||||
|
echo "You have not specified the device number in a recognizable format."
|
||||||
|
echo "It must either be the plain device number, e.g., 0123, or in"
|
||||||
|
echo "so-called busid format, e.g., 0.0.0123"
|
||||||
|
exit 7
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Just a device number, SIMPLE=1. A busid, SIMPLE=0
|
||||||
|
SIMPLE=0
|
||||||
|
if [ ${NUMPARMS} -eq 1 ]; then
|
||||||
|
let SIMPLE=1
|
||||||
|
let FIRST=0
|
||||||
|
let FIRSTLEN=1
|
||||||
|
let SECOND=0
|
||||||
|
let SECONDLEN=1
|
||||||
|
DEVNO="${1}"
|
||||||
|
let DEVNOLEN=${#1}
|
||||||
|
else FIRST="${1}"
|
||||||
|
let FIRSTLEN=${#FIRST}
|
||||||
|
SECOND="${2}"
|
||||||
|
let SECONDLEN=${#SECOND}
|
||||||
|
DEVNO="${3}"
|
||||||
|
let DEVNOLEN=${#3}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${FIRSTLEN} -ne 1 ] || [ ${SECONDLEN} -ne 1 ]; then
|
||||||
|
echo "The first and second fields of the busid may only be one digit long."
|
||||||
|
exit 8
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${DEVNOLEN} -gt 4 ]; then
|
||||||
|
echo "The device number may only be 4 digits long."
|
||||||
|
exit 9
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${DEVNOLEN} -lt 4 ]; then
|
||||||
|
DEVNO=$(echo "0000${DEVNO}" | rev | cut -c1-4 | rev)
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUSID="${FIRST}.${SECOND}.${DEVNO}"
|
||||||
|
|
||||||
|
if [ ! -h /sys/bus/ccw/devices/${BUSID} ]; then
|
||||||
|
echo "Busid ${BUSID} was not found."
|
||||||
|
/usr/sbin/cio_ignore -i ${BUSID} > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "That device is in the cio_ignore list."
|
||||||
|
echo "Please remove it with \"cio_ignore -r ${BUSID}\" before trying again."
|
||||||
|
fi
|
||||||
|
exit 10
|
||||||
|
fi
|
||||||
|
|
||||||
|
case ${DEVNO:0:3} in
|
||||||
|
019) if grep -q "version = FF" /proc/cpuinfo 2>/dev/null; then
|
||||||
|
echo "That looks like a CMS disk."
|
||||||
|
if [ ${FORCE} -eq 0 ]; then
|
||||||
|
echo "Specify the -f option to force the operation."
|
||||||
|
exit 11
|
||||||
|
fi
|
||||||
|
echo "But you specified -f so we'll kill it anyway."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
read ORIG_ONLINE_STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
|
||||||
|
DISCIPLINE="none"
|
||||||
|
if [ -r /sys/bus/ccw/devices/${BUSID}/discipline ]; then
|
||||||
|
# We have to bring the device online before the kernel will fill in
|
||||||
|
# the value for discipline.
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/usr/sbin/chccwdev -e ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
fi
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/status
|
||||||
|
if [ "${STATUS}" == "unformatted" ]; then
|
||||||
|
echo "DASD device ${BUSID} is already in an unformatted state."
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/usr/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
read DISCIPLINE < /sys/bus/ccw/devices/${BUSID}/discipline
|
||||||
|
else read CU_TYPE < /sys/bus/ccw/devices/${BUSID}/cutype
|
||||||
|
read DEV_TYPE < /sys/bus/ccw/devices/${BUSID}/devtype
|
||||||
|
case "${CU_TYPE}" in
|
||||||
|
3990/*|2105/*|2107/*|1750/*|9343/*)
|
||||||
|
DISCIPLINE=ECKD
|
||||||
|
;;
|
||||||
|
3880/*)
|
||||||
|
case "${DEV_TYPE}" in
|
||||||
|
3390/*)
|
||||||
|
DISCIPLINE=ECKD
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${DISCIPLINE}" != "ECKD" ]; then
|
||||||
|
echo "This script only works on ECKD DASD."
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/usr/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
fi
|
||||||
|
exit 12
|
||||||
|
fi
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -eq 1 ]; then
|
||||||
|
if [ ! -h /dev/disk/by-path/ccw-${BUSID} ]; then
|
||||||
|
echo "The udev-generated symbolic link in /dev/disk/by-path was not found."
|
||||||
|
exit 13
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -ne 0 ]; then
|
||||||
|
echo "Device number ${DEVNO} didn't go offline. Unable to continue."
|
||||||
|
exit 14
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/sbin/chccwdev -a raw_track_access=1 -e ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -ne 1 ]; then
|
||||||
|
echo "Unable to bring ${DEVNO} online. Unable to continue."
|
||||||
|
exit 15
|
||||||
|
fi
|
||||||
|
|
||||||
|
# After this point, we will kill the formatting on the device
|
||||||
|
perl -e 'for ($h=0;$h<2;$h++){printf "\0\0\0%c\0\0\0\x8%s",$h,(("\0"x8).("\xff"x8).("\0"x65512))}' | dd bs=65536 count=2 oflag=direct of=/dev/disk/by-path/ccw-${BUSID} >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
echo "The writing of the null record 0 failed."
|
||||||
|
exit 16
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setting ${BUSID} back offline with raw track access disabled."
|
||||||
|
/usr/sbin/chccwdev -d -s -a raw_track_access=0 ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 1 ]; then
|
||||||
|
/usr/sbin/chccwdev -e ${BUSID}
|
||||||
|
/usr/sbin/udevadm settle
|
||||||
|
fi
|
217
killcdl.suse
Normal file
217
killcdl.suse
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# Released under the GNU General Public License version 2.
|
||||||
|
#
|
||||||
|
|
||||||
|
let FORCE=0
|
||||||
|
DEVPARM=""
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} [ -f ] devno|busid"
|
||||||
|
echo " -f Force unformatting for DASD volumes in the CMS device range of 19x."
|
||||||
|
echo " devno The \"plain\" device number of the volume, e.g. 3184."
|
||||||
|
echo " busid The full specification of the volume, e.g., 0.0.3184."
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCH="$(/bin/uname -m)"
|
||||||
|
if [ "${ARCH}" != "s390x" ] && [ "${ARCH}" != "s390" ]; then
|
||||||
|
echo "This script is only useful on IBM mainframes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt -a --options f -n "killcdl" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
usage
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
for ARG; do
|
||||||
|
case "${ARG}" in
|
||||||
|
-f) FORCE=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--) shift 1
|
||||||
|
;;
|
||||||
|
[0-9a-fA-F]*) if [ ! -z "${DEVPARM}" ]; then
|
||||||
|
echo "More than one parameter specified."
|
||||||
|
usage
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
DEVPARM=${1}
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
*) echo "That looks invalid"
|
||||||
|
usage
|
||||||
|
exit 5
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${DEVPARM}" ]; then
|
||||||
|
echo "You must specify the device number of the DASD volume to be unformatted."
|
||||||
|
usage
|
||||||
|
exit 6
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEVNO=$(echo "${DEVPARM}" | tr A-Z a-z)
|
||||||
|
|
||||||
|
# Validate the device number or busid provided
|
||||||
|
set -- $(IFS='.'; echo ${DEVNO})
|
||||||
|
let NUMPARMS=${#}
|
||||||
|
if [ ${NUMPARMS} -ne 1 ] && [ ${NUMPARMS} -ne 3 ]; then
|
||||||
|
echo "You have not specified the device number in a recognizable format."
|
||||||
|
echo "It must either be the plain device number, e.g., 0123, or in"
|
||||||
|
echo "so-called busid format, e.g., 0.0.0123"
|
||||||
|
exit 7
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Just a device number, SIMPLE=1. A busid, SIMPLE=0
|
||||||
|
SIMPLE=0
|
||||||
|
if [ ${NUMPARMS} -eq 1 ]; then
|
||||||
|
let SIMPLE=1
|
||||||
|
let FIRST=0
|
||||||
|
let FIRSTLEN=1
|
||||||
|
let SECOND=0
|
||||||
|
let SECONDLEN=1
|
||||||
|
DEVNO="${1}"
|
||||||
|
let DEVNOLEN=${#1}
|
||||||
|
else FIRST="${1}"
|
||||||
|
let FIRSTLEN=${#FIRST}
|
||||||
|
SECOND="${2}"
|
||||||
|
let SECONDLEN=${#SECOND}
|
||||||
|
DEVNO="${3}"
|
||||||
|
let DEVNOLEN=${#3}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${FIRSTLEN} -ne 1 ] || [ ${SECONDLEN} -ne 1 ]; then
|
||||||
|
echo "The first and second fields of the busid may only be one digit long."
|
||||||
|
exit 8
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${DEVNOLEN} -gt 4 ]; then
|
||||||
|
echo "The device number may only be 4 digits long."
|
||||||
|
exit 9
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${DEVNOLEN} -lt 4 ]; then
|
||||||
|
DEVNO=$(echo "0000${DEVNO}" | rev | cut -c1-4 | rev)
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUSID="${FIRST}.${SECOND}.${DEVNO}"
|
||||||
|
|
||||||
|
if [ ! -h /sys/bus/ccw/devices/${BUSID} ]; then
|
||||||
|
echo "Busid ${BUSID} was not found."
|
||||||
|
/sbin/cio_ignore -i ${BUSID} > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "That device is in the cio_ignore list."
|
||||||
|
echo "Please remove it with \"cio_ignore -r ${BUSID}\" before trying again."
|
||||||
|
fi
|
||||||
|
exit 10
|
||||||
|
fi
|
||||||
|
|
||||||
|
case ${DEVNO:0:3} in
|
||||||
|
019) if grep -q "version = FF" /proc/cpuinfo 2>/dev/null; then
|
||||||
|
echo "That looks like a CMS disk."
|
||||||
|
if [ ${FORCE} -eq 0 ]; then
|
||||||
|
echo "Specify the -f option to force the operation."
|
||||||
|
exit 11
|
||||||
|
fi
|
||||||
|
echo "But you specified -f so we'll kill it anyway."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
read ORIG_ONLINE_STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
|
||||||
|
DISCIPLINE="none"
|
||||||
|
if [ -r /sys/bus/ccw/devices/${BUSID}/discipline ]; then
|
||||||
|
# We have to bring the device online before the kernel will fill in
|
||||||
|
# the value for discipline.
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/sbin/chccwdev -e ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
fi
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/status
|
||||||
|
if [ "${STATUS}" == "unformatted" ]; then
|
||||||
|
echo "DASD device ${BUSID} is already in an unformatted state."
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
read DISCIPLINE < /sys/bus/ccw/devices/${BUSID}/discipline
|
||||||
|
else read CU_TYPE < /sys/bus/ccw/devices/${BUSID}/cutype
|
||||||
|
read DEV_TYPE < /sys/bus/ccw/devices/${BUSID}/devtype
|
||||||
|
case "${CU_TYPE}" in
|
||||||
|
3990/*|2105/*|2107/*|1750/*|9343/*)
|
||||||
|
DISCIPLINE=ECKD
|
||||||
|
;;
|
||||||
|
3880/*)
|
||||||
|
case "${DEV_TYPE}" in
|
||||||
|
3390/*)
|
||||||
|
DISCIPLINE=ECKD
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${DISCIPLINE}" != "ECKD" ]; then
|
||||||
|
echo "This script only works on ECKD DASD."
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 0 ]; then
|
||||||
|
/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
fi
|
||||||
|
exit 12
|
||||||
|
fi
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -eq 1 ]; then
|
||||||
|
if [ ! -h /dev/disk/by-path/ccw-${BUSID} ]; then
|
||||||
|
echo "The udev-generated symbolic link in /dev/disk/by-path was not found."
|
||||||
|
exit 13
|
||||||
|
fi
|
||||||
|
|
||||||
|
/sbin/chccwdev -d -s ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -ne 0 ]; then
|
||||||
|
echo "Device number ${DEVNO} didn't go offline. Unable to continue."
|
||||||
|
exit 14
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
/sbin/chccwdev -a raw_track_access=1 -e ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
|
||||||
|
read STATUS < /sys/bus/ccw/devices/${BUSID}/online
|
||||||
|
if [ ${STATUS} -ne 1 ]; then
|
||||||
|
echo "Unable to bring ${DEVNO} online. Unable to continue."
|
||||||
|
exit 15
|
||||||
|
fi
|
||||||
|
|
||||||
|
# After this point, we will kill the formatting on the device
|
||||||
|
perl -e 'for ($h=0;$h<2;$h++){printf "\0\0\0%c\0\0\0\x8%s",$h,(("\0"x8).("\xff"x8).("\0"x65512))}' | dd bs=65536 count=2 oflag=direct of=/dev/disk/by-path/ccw-${BUSID} >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
echo "The writing of the null record 0 failed."
|
||||||
|
exit 16
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setting ${BUSID} back offline with raw track access disabled."
|
||||||
|
/sbin/chccwdev -d -s -a raw_track_access=0 ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
|
||||||
|
if [ ${ORIG_ONLINE_STATUS} -eq 1 ]; then
|
||||||
|
/sbin/chccwdev -e ${BUSID}
|
||||||
|
/sbin/udevadm settle
|
||||||
|
fi
|
335
lgr_check
Normal file
335
lgr_check
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
function check_sysoper(){
|
||||||
|
local SYSOPER=$(vmcp q sysoper | cut -f4 -d" ")
|
||||||
|
local USERID=$(vmcp q userid | cut -f1 -d" ")"."
|
||||||
|
if [ "${SYSOPER}" == "${USERID}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_disc(){
|
||||||
|
local USERID=$(vmcp q userid | cut -f1 -d" ")
|
||||||
|
local DISCONNECTED=$(vmcp q ${USERID} | cut -f2 -d-)
|
||||||
|
if [ "${DISCONNECTED}" == " DSC" ]; then
|
||||||
|
return 1
|
||||||
|
else return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_3270(){
|
||||||
|
local CONMODE=$(vmcp q term | sed -n -e '/CONMODE/ {s/CONMODE \([0-9]*\), .*$/\1/;p}')
|
||||||
|
if [ "${CONMODE}" == "3270" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_graf(){
|
||||||
|
local GRAF
|
||||||
|
GRAF=$(vmcp -b 1048576 q v graf 2>/dev/null | grep -v "^CONS" | grep "ON LDEV" )
|
||||||
|
if [ ${?} -eq 0 ] && [ -n "${GRAF}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_ascii_console(){
|
||||||
|
local SYSASCII=$(vmcp q v sysascii 2>/dev/null | grep "not attached to you")
|
||||||
|
if [ -z "${SYSASCII}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_tdisk(){
|
||||||
|
local TDISK=$(vmcp -b 1048576 q v dasd 2>/dev/null | grep TEMP)
|
||||||
|
if [ -n "${TDISK}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_ctc(){
|
||||||
|
local CTC
|
||||||
|
CTC=$(vmcp -b 1048576 q v ctc 2>/dev/null)
|
||||||
|
if [ ${?} -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_dynamic_switch(){
|
||||||
|
local SWCH
|
||||||
|
SWCH=$(vmcp -b 1048576 q v switches 2>/dev/null)
|
||||||
|
if [ ${?} -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_maint_mdisks(){
|
||||||
|
local MDISKS
|
||||||
|
MDISKS=$(vmcp -b 1048576 q v dasd | grep -E "0190|0191|0193|019D|019E")
|
||||||
|
if [ -n "${MDISKS}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_wrkalleg(){
|
||||||
|
local WRKALLEG
|
||||||
|
WRKALLEG=$(vmcp -b 1048576 q wrkalleg | grep "is not simulated")
|
||||||
|
if [ -z "${WRKALLEG}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_isolated_vswitch(){
|
||||||
|
local ISOLATED=0
|
||||||
|
local VSWITCH
|
||||||
|
|
||||||
|
# Find out if we have any NICs coupled to any VSWITCH or not. If not, we're OK.
|
||||||
|
VSWITCH=$(vmcp -b 1048576 q nic | sed -e '1~2 {N;s/\n//;}' | grep VSWITCH)
|
||||||
|
if [ -z "${VSWITCH}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ISOLATED=$(vmcp -b 1048576 q nic | sed -e '1~2 {N;s/\n//;}' | \
|
||||||
|
grep VSWITCH | \
|
||||||
|
sed -e 's/^.* VSWITCH: //' | \
|
||||||
|
while read owner name
|
||||||
|
do VSWITCH=$(vmcp -b 1048576 q vswitch $name | grep RDEV)
|
||||||
|
if [ -z "${VSWITCH}" ]; then
|
||||||
|
echo 1
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
|
||||||
|
if [ "${ISOLATED}" == "1" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_chpidvirt(){
|
||||||
|
local CHPIDV
|
||||||
|
CHPIDV=$(vmcp q chpidv 2>/dev/null | grep "CHPID Virtualization is on")
|
||||||
|
if [ -z "${CHPIDV}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_pci_functions(){
|
||||||
|
local PCIF
|
||||||
|
local RETCODE
|
||||||
|
PCIF=$(vmcp -b 1048576 q v pcif 2>/dev/null | grep "A PCI function was not found.")
|
||||||
|
RETCODE=${?}
|
||||||
|
if [ ${RETCODE} -eq 0 ] && [ -z "${PCIF}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_tape_assign(){
|
||||||
|
local TAPES
|
||||||
|
local RETCODE=1
|
||||||
|
TAPES=$(vmcp -b 1048576 q v tapes 2>/dev/null | grep "Device TAPE does not exist")
|
||||||
|
if [ -n "${TAPES}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAPES=$(vmcp -b 1048576 q v tapes 2>/dev/null | grep "NOASSIGN")
|
||||||
|
if [ -n "${TAPES}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_open_spool(){
|
||||||
|
local QSPOOL
|
||||||
|
local OPENSPOOL=0
|
||||||
|
QSPOOL=$(vmcp -b 1048576 q pun \* all 2>/dev/null | grep "OPEN")
|
||||||
|
if [ -n "${QSPOOL}" ]; then
|
||||||
|
let OPENSPOOL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
QSPOOL=$(vmcp -b 1048576 q prt \* all 2>/dev/null | grep "OPEN" | grep -v " CON ")
|
||||||
|
if [ -n "${QSPOOL}" ]; then
|
||||||
|
let OPENSPOOL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${OPENSPOOL} -eq 1 ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_xstore(){
|
||||||
|
local XSTOR
|
||||||
|
XSTOR=$(vmcp -b 1048576 q v xstor 2>/dev/null | grep "XSTORE = none")
|
||||||
|
if [ -z "${XSTOR}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_iucv(){
|
||||||
|
local QIUCV
|
||||||
|
QIUCV=$(vmcp -b 1048576 q iucv 2>/dev/null | grep -vE "^No IUCV paths exist|^Source| *MSG| *MSGALL")
|
||||||
|
if [ -n "${QIUCV}" ]; then
|
||||||
|
return 0
|
||||||
|
else return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage(){
|
||||||
|
echo "Usage: ${0} [ -f ] [ -h ] devno|busid"
|
||||||
|
echo " -q Don't generate any output, just set a return code."
|
||||||
|
echo " -m Suppress the check for local minidisks."
|
||||||
|
echo " Only use this if you know for certain all minidisks for this"
|
||||||
|
echo " guest are NOT local to this instance of z/VM."
|
||||||
|
echo " -h Display this help message."
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCH="$(/bin/uname -m)"
|
||||||
|
if [ "${ARCH}" != "s390x" ] && [ "${ARCH}" != "s390" ]; then
|
||||||
|
echo "This script is only useful on IBM mainframes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HYPERVISOR=$(systemd-detect-virt)
|
||||||
|
if [ "${HYPERVISOR}" != "zvm" ]; then
|
||||||
|
echo "This script is only useful for guests of the z/VM hypervisor."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MDISK_SUPPRESS=0
|
||||||
|
msg="echo"
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt -a --options qhm -n "lgr_check" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
usage
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
for ARG; do
|
||||||
|
case "${ARG}" in
|
||||||
|
-h) usage;
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-m) let MDISK_SUPPRESS=1;
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-q) msg="/bin/true"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--) shift 1
|
||||||
|
;;
|
||||||
|
*) ${msg} "Extraneous input detected: ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
let FAIL=0
|
||||||
|
##let COLS=$(stty -a | sed -n -e '/columns/{s/^.*columns \([0-9]*\);.*$/\1/;p}')
|
||||||
|
|
||||||
|
if [ ! -c /dev/vmcp ]; then
|
||||||
|
${msg} "Cannot find /dev/vmcp to issue z/VM CP commands."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
${msg} "Checking for conditions that might prevent Live Guest Relocation"
|
||||||
|
|
||||||
|
if check_chpidvirt; then
|
||||||
|
${msg} "This guest does not have CHPID Virtualization set for it in the CP directory."
|
||||||
|
${msg} "Live Guest Relocation is absolutely not possible for this guest."
|
||||||
|
# exit 99
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_sysoper ; then
|
||||||
|
${msg} "This guest is currently the z/VM system operator."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
let GUEST_CONN=0
|
||||||
|
if check_disc; then
|
||||||
|
${msg} "The guest is not running disconnected."
|
||||||
|
let GUEST_CONN=1
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_3270; then
|
||||||
|
${msg} -n "The guest has a 3270 console, "
|
||||||
|
if [ ${GUEST_CONN} -eq 0 ]; then
|
||||||
|
${msg} "but it is not currently in use."
|
||||||
|
else ${msg} "and it is currently in use."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if check_graf; then
|
||||||
|
${msg} "The guest has a DIALED 3270 device in current use."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_ascii_console; then
|
||||||
|
${msg} "The guest has the ASCII console attached to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_tdisk; then
|
||||||
|
${msg} "The guest has a temporary disk (T-disk) attached to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_ctc; then
|
||||||
|
${msg} "The guest has a Channel-to-Channel device (CTC) attached to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_dynamic_switch; then
|
||||||
|
${msg} "The guest has a dynamic switching device attached to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_wrkalleg; then
|
||||||
|
${msg} "The guest is currently using virtual working allegiance."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if [ ${MDISK_SUPPRESS} -eq 0 ] && check_maint_mdisks; then
|
||||||
|
${msg} "The guest currently has one or more Minidisks that might be local to this instance of z/VM."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_isolated_vswitch; then
|
||||||
|
${msg} "The guest is currently coupled to an isolated VSWITCH."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_pci_functions; then
|
||||||
|
${msg} "The guest has PCI functions available to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_tape_assign; then
|
||||||
|
${msg} "The guest has potential problems with a tape."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_open_spool; then
|
||||||
|
${msg} "The guest has an open SPOOL file that is not from the virtual console."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_xstore; then
|
||||||
|
${msg} "The guest has Expanded Storage attached to it."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
if check_iucv; then
|
||||||
|
${msg} "The guest has an IUCV connection to a z/VM system service or another z/VM user."
|
||||||
|
let FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${FAIL} == 1 ]; then
|
||||||
|
${msg} "The guest is currently not eligible for Live Guest Relocation."
|
||||||
|
exit 1
|
||||||
|
else ${msg} "As far as can be determined from within the guest, it is currently eligible for Live Guest Relocation."
|
||||||
|
${msg} "This is not a guarantee. Other factors that cannot be checked from within the guest may prevent it from being eligible for LGR."
|
||||||
|
fi
|
||||||
|
|
72
mkdump.8
Normal file
72
mkdump.8
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||||
|
.TH MKDUMP "8" "August 2011" "mkdump 2.0" "System Administration Utilities"
|
||||||
|
.SH NAME
|
||||||
|
mkdump \- Preparing disks for use as S/390 dump device.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B mkdump
|
||||||
|
[\fIOPTIONS\fR] [\fIDEVICE\fR]...
|
||||||
|
.SH DESCRIPTION
|
||||||
|
mkdump 2.0.3
|
||||||
|
.PP
|
||||||
|
Prepare one or more volumes for use as S/390 dump device. Supported devices
|
||||||
|
are ECKD DASD and SCSI over zFCP disks, while multi\-volumes are limited to DASD.
|
||||||
|
.PP
|
||||||
|
Only whole disks can be used, no partitions! If the device is incompatible
|
||||||
|
formatted/partioned, the script will refuse to install the dump record
|
||||||
|
unless the \fB\-\-force\fR switch is given.
|
||||||
|
.PP
|
||||||
|
Disks which are in use or have mounted partitions will not be listed and can't be used.
|
||||||
|
The mentioning of "dumpdevice" after a disk indicates that it is an already usable dump device. Additionally multi\-volume dump devices are indicated by the list of including DASD ids.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
display this help and exit
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR, \fB\-\-version\fR
|
||||||
|
display version information and exit
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR, \fB\-\-debug\fR
|
||||||
|
debug mode, do not run programs which commit changes
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR, \fB\-\-verbose\fR
|
||||||
|
be verbose and show command outputs
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR, \fB\-\-force\fR
|
||||||
|
force overwrite of the disk
|
||||||
|
.TP
|
||||||
|
\fB\-l\fR, \fB\-\-list\-dump\fR
|
||||||
|
display dump disks
|
||||||
|
.TP
|
||||||
|
\fB\-D\fR, \fB\-\-list\-dasd\fR
|
||||||
|
display usable DASD disks (Device, Size, ID, Dump)
|
||||||
|
.TP
|
||||||
|
\fB\-Z\fR, \fB\-\-list\-zfcp\fR
|
||||||
|
display usable SCSI over zFCP disks (Device, Size, ID, WWPN, LUN, Dump)
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
mkdump returns the following exit codes:
|
||||||
|
.RS
|
||||||
|
.IP 0
|
||||||
|
Normal (no errors or warnings detected)
|
||||||
|
.IP 11
|
||||||
|
Invalid or unusable disk (fatal)
|
||||||
|
.IP 12
|
||||||
|
Incompatible formatting/partitioning, can be corrected with --force
|
||||||
|
.IP 13
|
||||||
|
Missing support programs
|
||||||
|
.IP 14
|
||||||
|
Bad command line parameters
|
||||||
|
.IP 15
|
||||||
|
Access problem
|
||||||
|
.IP other
|
||||||
|
Support program failed
|
||||||
|
.SH AUTHOR
|
||||||
|
Written by Tim Hardeck <thardeck@suse.de>.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Report bugs on https://bugzilla.novell.com/
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright \(co 2011 SUSE LINUX Products GmbH
|
||||||
|
License GPLv2 or (at your option) any later version.
|
||||||
|
<http://www.gnu.org/licenses/gpl-2.0.html>
|
||||||
|
.br
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law.
|
666
mkdump.pl.opensuse
Normal file
666
mkdump.pl.opensuse
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
########################################################################
|
||||||
|
#
|
||||||
|
# mkdump.pl - Preparing disks for use as S/390 dump device
|
||||||
|
#
|
||||||
|
# Copyright (c) 2011 Tim Hardeck, SUSE LINUX Products GmbH
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# Based on mkdump.sh (c) 2004 Hannes Reinecke, SuSE AG
|
||||||
|
#
|
||||||
|
# License:
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
# Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Fcntl;
|
||||||
|
use Getopt::Long;
|
||||||
|
|
||||||
|
my $VERSION = "2.0.4";
|
||||||
|
|
||||||
|
my $BLKID = "/usr/sbin/blkid";
|
||||||
|
my $PARTED = "/usr/sbin/parted";
|
||||||
|
my $FDASD = "/usr/sbin/fdasd";
|
||||||
|
my $DASDVIEW = "/usr/sbin/dasdview";
|
||||||
|
my $DASDFMT = "/usr/sbin/dasdfmt";
|
||||||
|
my $ZIPL = "/usr/sbin/zipl";
|
||||||
|
my $UDEVADM = "/usr/sbin/udevadm";
|
||||||
|
my $ZGETDUMP = "/usr/sbin/zgetdump";
|
||||||
|
|
||||||
|
# temporary DASD device configuration file for Zipl
|
||||||
|
my $MDPATH = "/tmp/mvdump.conf.".`mcookie`;
|
||||||
|
chomp($MDPATH);
|
||||||
|
|
||||||
|
my $OPT_DEBUG = 0;
|
||||||
|
my $OPT_FORCE = 0;
|
||||||
|
my $OPT_VERBOSE = 0;
|
||||||
|
|
||||||
|
sub cleanup
|
||||||
|
{
|
||||||
|
# DASD
|
||||||
|
if (-e $MDPATH) {
|
||||||
|
system("rm -f $MDPATH");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub exit_with
|
||||||
|
{
|
||||||
|
my $message = shift();
|
||||||
|
my $exitcode = shift();
|
||||||
|
|
||||||
|
print STDERR "$message Exiting...\n";
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
# fdasd isn't able to create volume label interactively
|
||||||
|
# could be fixed with a reformat
|
||||||
|
if ($exitcode == 65280) {
|
||||||
|
$exitcode = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
# bigger exit codes are not supported
|
||||||
|
if ($exitcode > 255) {
|
||||||
|
$exitcode = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit($exitcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_cmd
|
||||||
|
{
|
||||||
|
my $cmd = shift();
|
||||||
|
|
||||||
|
my $output = "";
|
||||||
|
if (! $OPT_DEBUG) {
|
||||||
|
my ($app) = $cmd =~ /\/(\w+) /;
|
||||||
|
|
||||||
|
# run command
|
||||||
|
$output = `$cmd`;
|
||||||
|
my $exit_code = $?;
|
||||||
|
# wait for udev to finish processing
|
||||||
|
system("$UDEVADM settle");
|
||||||
|
|
||||||
|
# only print output in case of an error or in verbose mode
|
||||||
|
if ($output and ($exit_code != 0 or $OPT_VERBOSE)) {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exit_code != 0) {
|
||||||
|
exit_with("$app failed with exit code $exit_code", $exit_code);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# only print the command in debug mode
|
||||||
|
print("\`$cmd\`\n");
|
||||||
|
}
|
||||||
|
return($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_paths
|
||||||
|
{
|
||||||
|
for my $path ($BLKID, $PARTED, $FDASD, $DASDVIEW, $DASDFMT, $ZIPL, $UDEVADM, $ZGETDUMP) {
|
||||||
|
unless ( -x $path) {
|
||||||
|
exit_with("Command $path is not available.", 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_file
|
||||||
|
{
|
||||||
|
my $path = shift();
|
||||||
|
|
||||||
|
open(my $file, "<", "$path") or exit_with("Unable to access $path: $!.", 15);
|
||||||
|
my @content = <$file>;
|
||||||
|
close($file);
|
||||||
|
|
||||||
|
# no need for arrays in case of single lines
|
||||||
|
if (@content > 1) {
|
||||||
|
return @content;
|
||||||
|
} else {
|
||||||
|
chomp($content[0]);
|
||||||
|
return($content[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_dasd
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
|
||||||
|
if (-r "/sys/block/$device/device/discipline") {
|
||||||
|
return(1);
|
||||||
|
} else {
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub has_free_single_kpartx
|
||||||
|
{
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
return(0) unless ($device =~ /^dm-[0-9]+$/);
|
||||||
|
my $blockpath = "/sys/block/$device";
|
||||||
|
my @holders = glob("$blockpath/holders/*");
|
||||||
|
return(0) unless (@holders == 1);
|
||||||
|
my $dmuuid = read_file("$holders[0]/dm/uuid");
|
||||||
|
return(0) unless ($dmuuid =~ /^part1-mpath-/);
|
||||||
|
my @holderparts = split(/\//, $holders[0]);
|
||||||
|
my $holder = "/dev/" . $holderparts[-1];
|
||||||
|
if(-b $holder and sysopen(my $blockdev, $holder, O_RDWR|O_EXCL)) {
|
||||||
|
close($blockdev);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_zfcp
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
my $blockpath = "/sys/block/$device";
|
||||||
|
my $dmname = undef;
|
||||||
|
|
||||||
|
# if user passed a device name on cmdline that we listed before
|
||||||
|
# convert to a dm-[0-9]+ kernel device name
|
||||||
|
if ($device =~ /^mapper\//) {
|
||||||
|
$device = substr(readlink("/dev/" . $device), 3);
|
||||||
|
$blockpath = "/sys/block/" . $device;
|
||||||
|
}
|
||||||
|
# check if dm-multipath and get one path member
|
||||||
|
if ($device =~ /^dm-[0-9]+$/) {
|
||||||
|
my $dmuuid = read_file("$blockpath/dm/uuid");
|
||||||
|
return(undef) unless $dmuuid =~ /^mpath-/;
|
||||||
|
$dmname = read_file("$blockpath/dm/name");
|
||||||
|
opendir(DIR, "$blockpath/slaves/") or return(undef);
|
||||||
|
while (defined(my $pathmember = readdir(DIR))) {
|
||||||
|
# skip ".", "..", or other non scsi disk entries
|
||||||
|
next unless $pathmember =~ /^sd[a-z]+$/;
|
||||||
|
$device = $pathmember;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
}
|
||||||
|
my $devpath = "/sys/block/$device/device";
|
||||||
|
|
||||||
|
unless (-r "$devpath/hba_id" or -r "$devpath/type") {
|
||||||
|
return(undef);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $devtype = read_file("$devpath/type");
|
||||||
|
|
||||||
|
# SCSI type '0' means disk
|
||||||
|
if ($devtype == 0) {
|
||||||
|
if (defined $dmname) {
|
||||||
|
return("/dev/mapper/$dmname");
|
||||||
|
} else {
|
||||||
|
return("/dev/$device");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return(undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_partition_num
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift, 5);
|
||||||
|
|
||||||
|
my $part_num = grep(/\s+$device\d+/, read_file("/proc/partitions"));
|
||||||
|
|
||||||
|
return($part_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_device
|
||||||
|
{
|
||||||
|
my $device = shift();
|
||||||
|
my $only_dump_disks = shift();
|
||||||
|
my $devpath;
|
||||||
|
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
$devpath = "/sys/block/" . substr(readlink($device), 3);
|
||||||
|
} else {
|
||||||
|
$devpath = "/sys/block/" . substr($device, 5);
|
||||||
|
}
|
||||||
|
my $output = $device;
|
||||||
|
my $dump_device = 0;
|
||||||
|
|
||||||
|
my $size = int(read_file("$devpath/size") / 2048); # 512 Byte blocks
|
||||||
|
# size can't be read this way in case of unformatted devices
|
||||||
|
if ($size != 0) {
|
||||||
|
$output .= "\t${size}MB";
|
||||||
|
} else {
|
||||||
|
$output .= "\tunknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dasd($device)) {
|
||||||
|
my ($busid) = readlink("$devpath/device") =~ /(\w\.\w\.\w{4})/;
|
||||||
|
$output .= "\t$busid";
|
||||||
|
|
||||||
|
# check for dump record and list multi volumes
|
||||||
|
my $zgetdump_output = `$ZGETDUMP -d $device 2>&1`;
|
||||||
|
my @dump_devs = $zgetdump_output =~ /(\w\.\w\.\w{4})/g;
|
||||||
|
if (@dump_devs) {
|
||||||
|
$dump_device = 1;
|
||||||
|
$output .= "\tdumpdevice";
|
||||||
|
# no need to output the dump ids for a single device
|
||||||
|
if (@dump_devs > 1) {
|
||||||
|
for my $id (@dump_devs) {
|
||||||
|
$output .= "|$id";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# check for single volume dump devices
|
||||||
|
if ($zgetdump_output =~ /Single-volume DASD dump tool/) {
|
||||||
|
$dump_device = 1;
|
||||||
|
$output .= "\tdumpdevice";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# get one path member to fill path info for "yast onpanic"
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
my $blockdev = substr(readlink($device), 3);
|
||||||
|
my $blockpath = "/sys/block/" . $blockdev;
|
||||||
|
opendir(DIR, "$blockpath/slaves/") or return(undef);
|
||||||
|
while (defined(my $pathmember = readdir(DIR))) {
|
||||||
|
# skip ".", "..", or other non scsi disk entries
|
||||||
|
next unless $pathmember =~ /^sd[a-z]+$/;
|
||||||
|
$devpath = "/sys/block/" . $pathmember;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
}
|
||||||
|
my $adapter = read_file("$devpath/device/hba_id");
|
||||||
|
my $wwpn = read_file("$devpath/device/wwpn");
|
||||||
|
my $lun = read_file("$devpath/device/fcp_lun");
|
||||||
|
$output .= "\t$adapter\t$wwpn\t$lun";
|
||||||
|
|
||||||
|
# check for dump record
|
||||||
|
my $zgetdump = `$ZGETDUMP -d $device 2>&1`;
|
||||||
|
if ($? == 0) {
|
||||||
|
my ($dsize) = ($zgetdump =~ /Maximum dump size\.:\s+([0-9]+) MB/m);
|
||||||
|
$dsize = $size unless (defined($dsize));
|
||||||
|
$output = "$device\t${dsize}MB\t$adapter\t$wwpn\t$lun\tdumpdevice";
|
||||||
|
$dump_device = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($only_dump_disks) {
|
||||||
|
if ($dump_device) {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub list_free_disks
|
||||||
|
{
|
||||||
|
my $devices_ref = shift();
|
||||||
|
my $type = shift();
|
||||||
|
|
||||||
|
if (@$devices_ref) {
|
||||||
|
for my $device (@$devices_ref) {
|
||||||
|
print_device($device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print STDERR "No free $type devices available!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub list_dump_disks
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
if (@devices) {
|
||||||
|
for my $device (@devices) {
|
||||||
|
print_device($device, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print STDERR "No dump devices available!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub determine_free_disks
|
||||||
|
{
|
||||||
|
my @dasd;
|
||||||
|
my @zfcp;
|
||||||
|
my @devices;
|
||||||
|
|
||||||
|
# gather block devices
|
||||||
|
my $path="/sys/block/";
|
||||||
|
opendir(DIR, $path) or exit_with("Unable to find $path: $!", 15);
|
||||||
|
while (defined(my $file = readdir(DIR))) {
|
||||||
|
# no need to add other devices then dasd* or sd*
|
||||||
|
# or dm-multipath
|
||||||
|
if ($file =~ /^dasd[a-z]+$/ or $file =~ /^sd[a-z]+$/ or
|
||||||
|
$file =~ /^dm-[0-9]+$/) {
|
||||||
|
push(@devices, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
|
||||||
|
for my $entry (@devices) {
|
||||||
|
# only allow disks, no partitions
|
||||||
|
my ($device) = $entry =~ /^([a-z]+)$/;
|
||||||
|
# dm devices other than dm-multipath are filtered by is_zfcp()
|
||||||
|
($device) = $entry =~ /^(dm-[0-9]+)$/ unless ($device);
|
||||||
|
next unless ($device);
|
||||||
|
|
||||||
|
$device = "/dev/$device";
|
||||||
|
|
||||||
|
# determine if the block device could be accessed exclusively
|
||||||
|
if(-b $device and sysopen(my $blockdev, $device, O_RDWR|O_EXCL)) {
|
||||||
|
close($blockdev);
|
||||||
|
if (is_dasd($device)) {
|
||||||
|
push(@dasd, $device);
|
||||||
|
}
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
push(@zfcp, $zfcp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# A dm-multipath device with a single holder
|
||||||
|
# being a kpartx partition number 1 could still
|
||||||
|
# be free or contain a zfcpdump boot record.
|
||||||
|
# Due to the kpartx linear dm mapping, such
|
||||||
|
# dm-multipath device cannot open exclusively.
|
||||||
|
if (has_free_single_kpartx($device)) {
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
push(@zfcp, $zfcp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# wait for udev to process all events triggered by sysopen(,O_EXCL)
|
||||||
|
system("$UDEVADM settle");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(\@dasd, \@zfcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prepare_dasd
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
my $format_disks = "";
|
||||||
|
|
||||||
|
# check formatting
|
||||||
|
for my $device (@devices) {
|
||||||
|
# determine disk layout
|
||||||
|
my ($fmtstr) = `$DASDVIEW -x $device` =~ /(\w\w\w) formatted/;
|
||||||
|
|
||||||
|
SWITCH:
|
||||||
|
for($fmtstr) {
|
||||||
|
if (/NOT/) {
|
||||||
|
print("Unformatted disk, formatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
if (/LDL/) {
|
||||||
|
if ($OPT_FORCE) {
|
||||||
|
print("Linux disk layout, reformatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
} else {
|
||||||
|
print("$device was formatted with the Linux disk layout.\n");
|
||||||
|
print("Unable to use it without reformatting.\n");
|
||||||
|
exit_with("Re-issue the mkdump command with the --force option.", 12);
|
||||||
|
}
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
if (/CDL/) {
|
||||||
|
# allow reformatting with force, since fdasd isn't able to create volume label interactively
|
||||||
|
if ($OPT_FORCE) {
|
||||||
|
print("Compatible disk layout, force reformatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
} else {
|
||||||
|
print("$device: Compatible disk layout, Ok to use.\n");
|
||||||
|
}
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
exit_with("Unknown layout ($fmtstr), cannot use disk.", 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# format devices
|
||||||
|
if ($format_disks) {
|
||||||
|
#up to eight devices in parallel
|
||||||
|
run_cmd("$DASDFMT -P 8 -b 4096 -y -f $format_disks");
|
||||||
|
}
|
||||||
|
|
||||||
|
# check partitioning and partition
|
||||||
|
for my $device (@devices) {
|
||||||
|
my $part_num = get_partition_num($device);
|
||||||
|
if ($part_num == 0 or $OPT_FORCE) {
|
||||||
|
print("Re-partitioning disk $device.\n");
|
||||||
|
run_cmd("$FDASD -a $device");
|
||||||
|
} else {
|
||||||
|
# allow disk with one partition if it don't consist a file system
|
||||||
|
if ($part_num == 1) {
|
||||||
|
my ($fstype) = `$BLKID ${device}1` =~ /TYPE=\"(\w+)\"/;
|
||||||
|
if ($fstype) {
|
||||||
|
exit_with("Device ${device}1 already contains a filesystem of type $fstype.", 12);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exit_with("$part_num partitions detected, cannot use disk $device.", 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setup_dasddump
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
prepare_dasd(@devices);
|
||||||
|
|
||||||
|
# create zipl device configuration file
|
||||||
|
# don't create files in debug mode
|
||||||
|
unless ($OPT_DEBUG) {
|
||||||
|
open(my $file, ">", $MDPATH) or exit_with("Unable to access $MDPATH: $!.", 15);
|
||||||
|
for my $device (@devices) {
|
||||||
|
print{$file}("${device}1\n");
|
||||||
|
}
|
||||||
|
close($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Creating dump record.\n");
|
||||||
|
run_cmd("${ZIPL} -V -n -M $MDPATH");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setup_zfcpdump
|
||||||
|
{
|
||||||
|
my $device = shift();
|
||||||
|
|
||||||
|
# check partitioning
|
||||||
|
my $part_num = get_partition_num($device);
|
||||||
|
if ($part_num == 0 or $OPT_FORCE) {
|
||||||
|
print("Re-partitioning disk $device.\n");
|
||||||
|
run_cmd("$PARTED -s -- $device mklabel gpt mkpart primary 0 -1");
|
||||||
|
} else {
|
||||||
|
if ($part_num > 1) {
|
||||||
|
exit_with("$part_num partitions detected, cannot use disk $device.", 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# install bootloader
|
||||||
|
print("Creating dump record.\n");
|
||||||
|
my $partdev;
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
$partdev = $device . "-part1"; # kpartx partition on multipath
|
||||||
|
} else {
|
||||||
|
$partdev = $device . "1"; # real partition, single path SCSI
|
||||||
|
}
|
||||||
|
run_cmd("${ZIPL} -V -d ${partdev}");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_version
|
||||||
|
{
|
||||||
|
print << "EOF";
|
||||||
|
mkdump $VERSION
|
||||||
|
|
||||||
|
Copyright (c) 2011 SUSE LINUX Products GmbH
|
||||||
|
License GPLv2 or (at your option) any later version.
|
||||||
|
<http://www.gnu.org/licenses/gpl-2.0.html>
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law.
|
||||||
|
|
||||||
|
Written by Tim Hardeck <thardeck\@suse.de>.
|
||||||
|
EOF
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_usage
|
||||||
|
{
|
||||||
|
my $exitcode = shift();
|
||||||
|
|
||||||
|
print << "EOF";
|
||||||
|
Usage: mkdump [OPTIONS] [DEVICE]...
|
||||||
|
mkdump $VERSION
|
||||||
|
|
||||||
|
Prepare one or more volumes for use as S/390 dump device. Supported devices
|
||||||
|
are ECKD DASD and SCSI over zFCP disks, while multi-volumes are limited to DASD.
|
||||||
|
|
||||||
|
Only whole disks can be used, no partitions! If the device is incompatible
|
||||||
|
formatted/partioned, the script will refuse to install the dump record
|
||||||
|
unless the --force switch is given.
|
||||||
|
|
||||||
|
Disks which are in use or have mounted partitions will not be listed and can't be used.
|
||||||
|
The mentioning of "dumpdevice" after a disk indicates that it is an already usable dump device. Additionally multi-volume dump devices are indicated by the list of including DASD ids.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help display this help and exit
|
||||||
|
-V, --version display version information and exit
|
||||||
|
|
||||||
|
-d, --debug debug mode, do not run programs which commit changes
|
||||||
|
-v, --verbose be verbose and show command outputs
|
||||||
|
-f, --force force overwrite of the disk
|
||||||
|
|
||||||
|
-l, --list-dump display dump disks
|
||||||
|
-D, --list-dasd display usable DASD disks (Device, Size, ID, Dump)
|
||||||
|
-Z, --list-zfcp display usable SCSI over zFCP disks (Device, Size, ID, WWPN, LUN, Dump)
|
||||||
|
|
||||||
|
Report bugs on https://bugzilla.novell.com/
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit($exitcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub analyze_cmd_parameters
|
||||||
|
{
|
||||||
|
#verbose, debug and force are global
|
||||||
|
my $opt_help = 0;
|
||||||
|
my $opt_version = 0;
|
||||||
|
my $opt_dump = 0;
|
||||||
|
my $opt_dasd = 0;
|
||||||
|
my $opt_zfcp = 0;
|
||||||
|
|
||||||
|
if (@ARGV == 0) {
|
||||||
|
print_usage(14);
|
||||||
|
}
|
||||||
|
|
||||||
|
Getopt::Long::Configure('bundling');
|
||||||
|
GetOptions(
|
||||||
|
'h|help' => \$opt_help,
|
||||||
|
'V|version' => \$opt_version,
|
||||||
|
'd|debug' => \$OPT_DEBUG,
|
||||||
|
'v|verbose' => \$OPT_VERBOSE,
|
||||||
|
'f|force' => \$OPT_FORCE,
|
||||||
|
'l|list-dump' => \$opt_dump,
|
||||||
|
'D|list-dasd' => \$opt_dasd,
|
||||||
|
'Z|list-zfcp' => \$opt_zfcp,
|
||||||
|
) or print_usage(14);
|
||||||
|
|
||||||
|
if ($opt_help) {
|
||||||
|
print_usage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_version) {
|
||||||
|
print_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
# determine free dasd and zfcp devices
|
||||||
|
my ($dasd_ref, $zfcp_ref) = determine_free_disks();
|
||||||
|
|
||||||
|
if ($opt_dump) {
|
||||||
|
list_dump_disks(@$dasd_ref, @$zfcp_ref);
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_dasd) {
|
||||||
|
list_free_disks(\@$dasd_ref, "dasd");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_zfcp) {
|
||||||
|
list_free_disks(\@$zfcp_ref, "zfcp");
|
||||||
|
}
|
||||||
|
|
||||||
|
# allow listing of both device types at the same time
|
||||||
|
if ($opt_dasd or $opt_zfcp) {
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# check provided devices and be strict
|
||||||
|
my @devices;
|
||||||
|
for my $device (@ARGV) {
|
||||||
|
if (grep(/$device/, @devices)) {
|
||||||
|
exit_with("$device is mentioned more than once.", 14);
|
||||||
|
}
|
||||||
|
# dm devices other than dm-multipath are filtered by is_zfcp()
|
||||||
|
if ( $device =~ /^\/dev\/[a-z]+$/ == 0 and
|
||||||
|
$device !~ /^\/dev\/mapper\// ) {
|
||||||
|
exit_with("The device parameter $device is inaccurate. Only whole disks are allowed.", 14);
|
||||||
|
}
|
||||||
|
if (grep(/$device/, (@$dasd_ref, @$zfcp_ref))) {
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp and @ARGV > 1) {
|
||||||
|
exit_with("Multi-volume dumps aren't supported with zFCP.", 14);
|
||||||
|
}
|
||||||
|
push(@devices, (defined $zfcp) ? $zfcp : $device);
|
||||||
|
} else {
|
||||||
|
if (-b $device) {
|
||||||
|
exit_with("$device is in use or not a DASD/zFCP disk!", 14);
|
||||||
|
} else {
|
||||||
|
exit_with("$device does not exist!", 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@devices == 0) {
|
||||||
|
exit_with("No usable devices where provided.", 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(@devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub main
|
||||||
|
{
|
||||||
|
check_paths();
|
||||||
|
my @devices = analyze_cmd_parameters();
|
||||||
|
|
||||||
|
# only one dump device is possible with zFCP which is enforced in analyze_cmd_parameters
|
||||||
|
my $zfcp = is_zfcp($devices[0]);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
setup_zfcpdump($zfcp);
|
||||||
|
} else {
|
||||||
|
setup_dasddump(@devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Creating the dump device was successful.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
666
mkdump.pl.suse
Normal file
666
mkdump.pl.suse
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
########################################################################
|
||||||
|
#
|
||||||
|
# mkdump.pl - Preparing disks for use as S/390 dump device
|
||||||
|
#
|
||||||
|
# Copyright (c) 2011 Tim Hardeck, SUSE LINUX Products GmbH
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# Based on mkdump.sh (c) 2004 Hannes Reinecke, SuSE AG
|
||||||
|
#
|
||||||
|
# License:
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
# Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Fcntl;
|
||||||
|
use Getopt::Long;
|
||||||
|
|
||||||
|
my $VERSION = "2.0.4";
|
||||||
|
|
||||||
|
my $BLKID = "/sbin/blkid";
|
||||||
|
my $PARTED = "/usr/sbin/parted";
|
||||||
|
my $FDASD = "/sbin/fdasd";
|
||||||
|
my $DASDVIEW = "/sbin/dasdview";
|
||||||
|
my $DASDFMT = "/sbin/dasdfmt";
|
||||||
|
my $ZIPL = "/sbin/zipl";
|
||||||
|
my $UDEVADM = "/sbin/udevadm";
|
||||||
|
my $ZGETDUMP = "/sbin/zgetdump";
|
||||||
|
|
||||||
|
# temporary DASD device configuration file for Zipl
|
||||||
|
my $MDPATH = "/tmp/mvdump.conf.".`mcookie`;
|
||||||
|
chomp($MDPATH);
|
||||||
|
|
||||||
|
my $OPT_DEBUG = 0;
|
||||||
|
my $OPT_FORCE = 0;
|
||||||
|
my $OPT_VERBOSE = 0;
|
||||||
|
|
||||||
|
sub cleanup
|
||||||
|
{
|
||||||
|
# DASD
|
||||||
|
if (-e $MDPATH) {
|
||||||
|
system("rm -f $MDPATH");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub exit_with
|
||||||
|
{
|
||||||
|
my $message = shift();
|
||||||
|
my $exitcode = shift();
|
||||||
|
|
||||||
|
print STDERR "$message Exiting...\n";
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
# fdasd isn't able to create volume label interactively
|
||||||
|
# could be fixed with a reformat
|
||||||
|
if ($exitcode == 65280) {
|
||||||
|
$exitcode = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
# bigger exit codes are not supported
|
||||||
|
if ($exitcode > 255) {
|
||||||
|
$exitcode = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit($exitcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_cmd
|
||||||
|
{
|
||||||
|
my $cmd = shift();
|
||||||
|
|
||||||
|
my $output = "";
|
||||||
|
if (! $OPT_DEBUG) {
|
||||||
|
my ($app) = $cmd =~ /\/(\w+) /;
|
||||||
|
|
||||||
|
# run command
|
||||||
|
$output = `$cmd`;
|
||||||
|
my $exit_code = $?;
|
||||||
|
# wait for udev to finish processing
|
||||||
|
system("$UDEVADM settle");
|
||||||
|
|
||||||
|
# only print output in case of an error or in verbose mode
|
||||||
|
if ($output and ($exit_code != 0 or $OPT_VERBOSE)) {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exit_code != 0) {
|
||||||
|
exit_with("$app failed with exit code $exit_code", $exit_code);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# only print the command in debug mode
|
||||||
|
print("\`$cmd\`\n");
|
||||||
|
}
|
||||||
|
return($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_paths
|
||||||
|
{
|
||||||
|
for my $path ($BLKID, $PARTED, $FDASD, $DASDVIEW, $DASDFMT, $ZIPL, $UDEVADM, $ZGETDUMP) {
|
||||||
|
unless ( -x $path) {
|
||||||
|
exit_with("Command $path is not available.", 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_file
|
||||||
|
{
|
||||||
|
my $path = shift();
|
||||||
|
|
||||||
|
open(my $file, "<", "$path") or exit_with("Unable to access $path: $!.", 15);
|
||||||
|
my @content = <$file>;
|
||||||
|
close($file);
|
||||||
|
|
||||||
|
# no need for arrays in case of single lines
|
||||||
|
if (@content > 1) {
|
||||||
|
return @content;
|
||||||
|
} else {
|
||||||
|
chomp($content[0]);
|
||||||
|
return($content[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_dasd
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
|
||||||
|
if (-r "/sys/block/$device/device/discipline") {
|
||||||
|
return(1);
|
||||||
|
} else {
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub has_free_single_kpartx
|
||||||
|
{
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
return(0) unless ($device =~ /^dm-[0-9]+$/);
|
||||||
|
my $blockpath = "/sys/block/$device";
|
||||||
|
my @holders = glob("$blockpath/holders/*");
|
||||||
|
return(0) unless (@holders == 1);
|
||||||
|
my $dmuuid = read_file("$holders[0]/dm/uuid");
|
||||||
|
return(0) unless ($dmuuid =~ /^part1-mpath-/);
|
||||||
|
my @holderparts = split(/\//, $holders[0]);
|
||||||
|
my $holder = "/dev/" . $holderparts[-1];
|
||||||
|
if(-b $holder and sysopen(my $blockdev, $holder, O_RDWR|O_EXCL)) {
|
||||||
|
close($blockdev);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_zfcp
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift(), 5);
|
||||||
|
my $blockpath = "/sys/block/$device";
|
||||||
|
my $dmname = undef;
|
||||||
|
|
||||||
|
# if user passed a device name on cmdline that we listed before
|
||||||
|
# convert to a dm-[0-9]+ kernel device name
|
||||||
|
if ($device =~ /^mapper\//) {
|
||||||
|
$device = substr(readlink("/dev/" . $device), 3);
|
||||||
|
$blockpath = "/sys/block/" . $device;
|
||||||
|
}
|
||||||
|
# check if dm-multipath and get one path member
|
||||||
|
if ($device =~ /^dm-[0-9]+$/) {
|
||||||
|
my $dmuuid = read_file("$blockpath/dm/uuid");
|
||||||
|
return(undef) unless $dmuuid =~ /^mpath-/;
|
||||||
|
$dmname = read_file("$blockpath/dm/name");
|
||||||
|
opendir(DIR, "$blockpath/slaves/") or return(undef);
|
||||||
|
while (defined(my $pathmember = readdir(DIR))) {
|
||||||
|
# skip ".", "..", or other non scsi disk entries
|
||||||
|
next unless $pathmember =~ /^sd[a-z]+$/;
|
||||||
|
$device = $pathmember;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
}
|
||||||
|
my $devpath = "/sys/block/$device/device";
|
||||||
|
|
||||||
|
unless (-r "$devpath/hba_id" or -r "$devpath/type") {
|
||||||
|
return(undef);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $devtype = read_file("$devpath/type");
|
||||||
|
|
||||||
|
# SCSI type '0' means disk
|
||||||
|
if ($devtype == 0) {
|
||||||
|
if (defined $dmname) {
|
||||||
|
return("/dev/mapper/$dmname");
|
||||||
|
} else {
|
||||||
|
return("/dev/$device");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return(undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_partition_num
|
||||||
|
{
|
||||||
|
# remove leading /dev/
|
||||||
|
my $device = substr(shift, 5);
|
||||||
|
|
||||||
|
my $part_num = grep(/\s+$device\d+/, read_file("/proc/partitions"));
|
||||||
|
|
||||||
|
return($part_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_device
|
||||||
|
{
|
||||||
|
my $device = shift();
|
||||||
|
my $only_dump_disks = shift();
|
||||||
|
my $devpath;
|
||||||
|
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
$devpath = "/sys/block/" . substr(readlink($device), 3);
|
||||||
|
} else {
|
||||||
|
$devpath = "/sys/block/" . substr($device, 5);
|
||||||
|
}
|
||||||
|
my $output = $device;
|
||||||
|
my $dump_device = 0;
|
||||||
|
|
||||||
|
my $size = int(read_file("$devpath/size") / 2048); # 512 Byte blocks
|
||||||
|
# size can't be read this way in case of unformatted devices
|
||||||
|
if ($size != 0) {
|
||||||
|
$output .= "\t${size}MB";
|
||||||
|
} else {
|
||||||
|
$output .= "\tunknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dasd($device)) {
|
||||||
|
my ($busid) = readlink("$devpath/device") =~ /(\w\.\w\.\w{4})/;
|
||||||
|
$output .= "\t$busid";
|
||||||
|
|
||||||
|
# check for dump record and list multi volumes
|
||||||
|
my $zgetdump_output = `$ZGETDUMP -d $device 2>&1`;
|
||||||
|
my @dump_devs = $zgetdump_output =~ /(\w\.\w\.\w{4})/g;
|
||||||
|
if (@dump_devs) {
|
||||||
|
$dump_device = 1;
|
||||||
|
$output .= "\tdumpdevice";
|
||||||
|
# no need to output the dump ids for a single device
|
||||||
|
if (@dump_devs > 1) {
|
||||||
|
for my $id (@dump_devs) {
|
||||||
|
$output .= "|$id";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# check for single volume dump devices
|
||||||
|
if ($zgetdump_output =~ /Single-volume DASD dump tool/) {
|
||||||
|
$dump_device = 1;
|
||||||
|
$output .= "\tdumpdevice";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# get one path member to fill path info for "yast onpanic"
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
my $blockdev = substr(readlink($device), 3);
|
||||||
|
my $blockpath = "/sys/block/" . $blockdev;
|
||||||
|
opendir(DIR, "$blockpath/slaves/") or return(undef);
|
||||||
|
while (defined(my $pathmember = readdir(DIR))) {
|
||||||
|
# skip ".", "..", or other non scsi disk entries
|
||||||
|
next unless $pathmember =~ /^sd[a-z]+$/;
|
||||||
|
$devpath = "/sys/block/" . $pathmember;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
}
|
||||||
|
my $adapter = read_file("$devpath/device/hba_id");
|
||||||
|
my $wwpn = read_file("$devpath/device/wwpn");
|
||||||
|
my $lun = read_file("$devpath/device/fcp_lun");
|
||||||
|
$output .= "\t$adapter\t$wwpn\t$lun";
|
||||||
|
|
||||||
|
# check for dump record
|
||||||
|
my $zgetdump = `$ZGETDUMP -d $device 2>&1`;
|
||||||
|
if ($? == 0) {
|
||||||
|
my ($dsize) = ($zgetdump =~ /Maximum dump size\.:\s+([0-9]+) MB/m);
|
||||||
|
$dsize = $size unless (defined($dsize));
|
||||||
|
$output = "$device\t${dsize}MB\t$adapter\t$wwpn\t$lun\tdumpdevice";
|
||||||
|
$dump_device = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($only_dump_disks) {
|
||||||
|
if ($dump_device) {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("$output\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub list_free_disks
|
||||||
|
{
|
||||||
|
my $devices_ref = shift();
|
||||||
|
my $type = shift();
|
||||||
|
|
||||||
|
if (@$devices_ref) {
|
||||||
|
for my $device (@$devices_ref) {
|
||||||
|
print_device($device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print STDERR "No free $type devices available!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub list_dump_disks
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
if (@devices) {
|
||||||
|
for my $device (@devices) {
|
||||||
|
print_device($device, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print STDERR "No dump devices available!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub determine_free_disks
|
||||||
|
{
|
||||||
|
my @dasd;
|
||||||
|
my @zfcp;
|
||||||
|
my @devices;
|
||||||
|
|
||||||
|
# gather block devices
|
||||||
|
my $path="/sys/block/";
|
||||||
|
opendir(DIR, $path) or exit_with("Unable to find $path: $!", 15);
|
||||||
|
while (defined(my $file = readdir(DIR))) {
|
||||||
|
# no need to add other devices then dasd* or sd*
|
||||||
|
# or dm-multipath
|
||||||
|
if ($file =~ /^dasd[a-z]+$/ or $file =~ /^sd[a-z]+$/ or
|
||||||
|
$file =~ /^dm-[0-9]+$/) {
|
||||||
|
push(@devices, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
|
||||||
|
for my $entry (@devices) {
|
||||||
|
# only allow disks, no partitions
|
||||||
|
my ($device) = $entry =~ /^([a-z]+)$/;
|
||||||
|
# dm devices other than dm-multipath are filtered by is_zfcp()
|
||||||
|
($device) = $entry =~ /^(dm-[0-9]+)$/ unless ($device);
|
||||||
|
next unless ($device);
|
||||||
|
|
||||||
|
$device = "/dev/$device";
|
||||||
|
|
||||||
|
# determine if the block device could be accessed exclusively
|
||||||
|
if(-b $device and sysopen(my $blockdev, $device, O_RDWR|O_EXCL)) {
|
||||||
|
close($blockdev);
|
||||||
|
if (is_dasd($device)) {
|
||||||
|
push(@dasd, $device);
|
||||||
|
}
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
push(@zfcp, $zfcp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# A dm-multipath device with a single holder
|
||||||
|
# being a kpartx partition number 1 could still
|
||||||
|
# be free or contain a zfcpdump boot record.
|
||||||
|
# Due to the kpartx linear dm mapping, such
|
||||||
|
# dm-multipath device cannot open exclusively.
|
||||||
|
if (has_free_single_kpartx($device)) {
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
push(@zfcp, $zfcp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# wait for udev to process all events triggered by sysopen(,O_EXCL)
|
||||||
|
system("$UDEVADM settle");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(\@dasd, \@zfcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prepare_dasd
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
my $format_disks = "";
|
||||||
|
|
||||||
|
# check formatting
|
||||||
|
for my $device (@devices) {
|
||||||
|
# determine disk layout
|
||||||
|
my ($fmtstr) = `$DASDVIEW -x $device` =~ /(\w\w\w) formatted/;
|
||||||
|
|
||||||
|
SWITCH:
|
||||||
|
for($fmtstr) {
|
||||||
|
if (/NOT/) {
|
||||||
|
print("Unformatted disk, formatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
if (/LDL/) {
|
||||||
|
if ($OPT_FORCE) {
|
||||||
|
print("Linux disk layout, reformatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
} else {
|
||||||
|
print("$device was formatted with the Linux disk layout.\n");
|
||||||
|
print("Unable to use it without reformatting.\n");
|
||||||
|
exit_with("Re-issue the mkdump command with the --force option.", 12);
|
||||||
|
}
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
if (/CDL/) {
|
||||||
|
# allow reformatting with force, since fdasd isn't able to create volume label interactively
|
||||||
|
if ($OPT_FORCE) {
|
||||||
|
print("Compatible disk layout, force reformatting $device.\n");
|
||||||
|
$format_disks .= " $device";
|
||||||
|
} else {
|
||||||
|
print("$device: Compatible disk layout, Ok to use.\n");
|
||||||
|
}
|
||||||
|
last SWITCH;
|
||||||
|
}
|
||||||
|
exit_with("Unknown layout ($fmtstr), cannot use disk.", 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# format devices
|
||||||
|
if ($format_disks) {
|
||||||
|
#up to eight devices in parallel
|
||||||
|
run_cmd("$DASDFMT -P 8 -b 4096 -y -f $format_disks");
|
||||||
|
}
|
||||||
|
|
||||||
|
# check partitioning and partition
|
||||||
|
for my $device (@devices) {
|
||||||
|
my $part_num = get_partition_num($device);
|
||||||
|
if ($part_num == 0 or $OPT_FORCE) {
|
||||||
|
print("Re-partitioning disk $device.\n");
|
||||||
|
run_cmd("$FDASD -a $device");
|
||||||
|
} else {
|
||||||
|
# allow disk with one partition if it don't consist a file system
|
||||||
|
if ($part_num == 1) {
|
||||||
|
my ($fstype) = `$BLKID ${device}1` =~ /TYPE=\"(\w+)\"/;
|
||||||
|
if ($fstype) {
|
||||||
|
exit_with("Device ${device}1 already contains a filesystem of type $fstype.", 12);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exit_with("$part_num partitions detected, cannot use disk $device.", 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setup_dasddump
|
||||||
|
{
|
||||||
|
my @devices = @_;
|
||||||
|
|
||||||
|
prepare_dasd(@devices);
|
||||||
|
|
||||||
|
# create zipl device configuration file
|
||||||
|
# don't create files in debug mode
|
||||||
|
unless ($OPT_DEBUG) {
|
||||||
|
open(my $file, ">", $MDPATH) or exit_with("Unable to access $MDPATH: $!.", 15);
|
||||||
|
for my $device (@devices) {
|
||||||
|
print{$file}("${device}1\n");
|
||||||
|
}
|
||||||
|
close($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Creating dump record.\n");
|
||||||
|
run_cmd("${ZIPL} -V -n -M $MDPATH");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setup_zfcpdump
|
||||||
|
{
|
||||||
|
my $device = shift();
|
||||||
|
|
||||||
|
# check partitioning
|
||||||
|
my $part_num = get_partition_num($device);
|
||||||
|
if ($part_num == 0 or $OPT_FORCE) {
|
||||||
|
print("Re-partitioning disk $device.\n");
|
||||||
|
run_cmd("$PARTED -s -- $device mklabel gpt mkpart primary 0 -1");
|
||||||
|
} else {
|
||||||
|
if ($part_num > 1) {
|
||||||
|
exit_with("$part_num partitions detected, cannot use disk $device.", 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# install bootloader
|
||||||
|
print("Creating dump record.\n");
|
||||||
|
my $partdev;
|
||||||
|
if ($device =~ /^\/dev\/mapper\//) {
|
||||||
|
$partdev = $device . "-part1"; # kpartx partition on multipath
|
||||||
|
} else {
|
||||||
|
$partdev = $device . "1"; # real partition, single path SCSI
|
||||||
|
}
|
||||||
|
run_cmd("${ZIPL} -V -d ${partdev}");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_version
|
||||||
|
{
|
||||||
|
print << "EOF";
|
||||||
|
mkdump $VERSION
|
||||||
|
|
||||||
|
Copyright (c) 2011 SUSE LINUX Products GmbH
|
||||||
|
License GPLv2 or (at your option) any later version.
|
||||||
|
<http://www.gnu.org/licenses/gpl-2.0.html>
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law.
|
||||||
|
|
||||||
|
Written by Tim Hardeck <thardeck\@suse.de>.
|
||||||
|
EOF
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_usage
|
||||||
|
{
|
||||||
|
my $exitcode = shift();
|
||||||
|
|
||||||
|
print << "EOF";
|
||||||
|
Usage: mkdump [OPTIONS] [DEVICE]...
|
||||||
|
mkdump $VERSION
|
||||||
|
|
||||||
|
Prepare one or more volumes for use as S/390 dump device. Supported devices
|
||||||
|
are ECKD DASD and SCSI over zFCP disks, while multi-volumes are limited to DASD.
|
||||||
|
|
||||||
|
Only whole disks can be used, no partitions! If the device is incompatible
|
||||||
|
formatted/partioned, the script will refuse to install the dump record
|
||||||
|
unless the --force switch is given.
|
||||||
|
|
||||||
|
Disks which are in use or have mounted partitions will not be listed and can't be used.
|
||||||
|
The mentioning of "dumpdevice" after a disk indicates that it is an already usable dump device. Additionally multi-volume dump devices are indicated by the list of including DASD ids.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help display this help and exit
|
||||||
|
-V, --version display version information and exit
|
||||||
|
|
||||||
|
-d, --debug debug mode, do not run programs which commit changes
|
||||||
|
-v, --verbose be verbose and show command outputs
|
||||||
|
-f, --force force overwrite of the disk
|
||||||
|
|
||||||
|
-l, --list-dump display dump disks
|
||||||
|
-D, --list-dasd display usable DASD disks (Device, Size, ID, Dump)
|
||||||
|
-Z, --list-zfcp display usable SCSI over zFCP disks (Device, Size, ID, WWPN, LUN, Dump)
|
||||||
|
|
||||||
|
Report bugs on https://bugzilla.novell.com/
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit($exitcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub analyze_cmd_parameters
|
||||||
|
{
|
||||||
|
#verbose, debug and force are global
|
||||||
|
my $opt_help = 0;
|
||||||
|
my $opt_version = 0;
|
||||||
|
my $opt_dump = 0;
|
||||||
|
my $opt_dasd = 0;
|
||||||
|
my $opt_zfcp = 0;
|
||||||
|
|
||||||
|
if (@ARGV == 0) {
|
||||||
|
print_usage(14);
|
||||||
|
}
|
||||||
|
|
||||||
|
Getopt::Long::Configure('bundling');
|
||||||
|
GetOptions(
|
||||||
|
'h|help' => \$opt_help,
|
||||||
|
'V|version' => \$opt_version,
|
||||||
|
'd|debug' => \$OPT_DEBUG,
|
||||||
|
'v|verbose' => \$OPT_VERBOSE,
|
||||||
|
'f|force' => \$OPT_FORCE,
|
||||||
|
'l|list-dump' => \$opt_dump,
|
||||||
|
'D|list-dasd' => \$opt_dasd,
|
||||||
|
'Z|list-zfcp' => \$opt_zfcp,
|
||||||
|
) or print_usage(14);
|
||||||
|
|
||||||
|
if ($opt_help) {
|
||||||
|
print_usage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_version) {
|
||||||
|
print_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
# determine free dasd and zfcp devices
|
||||||
|
my ($dasd_ref, $zfcp_ref) = determine_free_disks();
|
||||||
|
|
||||||
|
if ($opt_dump) {
|
||||||
|
list_dump_disks(@$dasd_ref, @$zfcp_ref);
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_dasd) {
|
||||||
|
list_free_disks(\@$dasd_ref, "dasd");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opt_zfcp) {
|
||||||
|
list_free_disks(\@$zfcp_ref, "zfcp");
|
||||||
|
}
|
||||||
|
|
||||||
|
# allow listing of both device types at the same time
|
||||||
|
if ($opt_dasd or $opt_zfcp) {
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# check provided devices and be strict
|
||||||
|
my @devices;
|
||||||
|
for my $device (@ARGV) {
|
||||||
|
if (grep(/$device/, @devices)) {
|
||||||
|
exit_with("$device is mentioned more than once.", 14);
|
||||||
|
}
|
||||||
|
# dm devices other than dm-multipath are filtered by is_zfcp()
|
||||||
|
if ( $device =~ /^\/dev\/[a-z]+$/ == 0 and
|
||||||
|
$device !~ /^\/dev\/mapper\// ) {
|
||||||
|
exit_with("The device parameter $device is inaccurate. Only whole disks are allowed.", 14);
|
||||||
|
}
|
||||||
|
if (grep(/$device/, (@$dasd_ref, @$zfcp_ref))) {
|
||||||
|
my $zfcp = is_zfcp($device);
|
||||||
|
if (defined $zfcp and @ARGV > 1) {
|
||||||
|
exit_with("Multi-volume dumps aren't supported with zFCP.", 14);
|
||||||
|
}
|
||||||
|
push(@devices, (defined $zfcp) ? $zfcp : $device);
|
||||||
|
} else {
|
||||||
|
if (-b $device) {
|
||||||
|
exit_with("$device is in use or not a DASD/zFCP disk!", 14);
|
||||||
|
} else {
|
||||||
|
exit_with("$device does not exist!", 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@devices == 0) {
|
||||||
|
exit_with("No usable devices where provided.", 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(@devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub main
|
||||||
|
{
|
||||||
|
check_paths();
|
||||||
|
my @devices = analyze_cmd_parameters();
|
||||||
|
|
||||||
|
# only one dump device is possible with zFCP which is enforced in analyze_cmd_parameters
|
||||||
|
my $zfcp = is_zfcp($devices[0]);
|
||||||
|
if (defined $zfcp) {
|
||||||
|
setup_zfcpdump($zfcp);
|
||||||
|
} else {
|
||||||
|
setup_dasddump(@devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Creating the dump device was successful.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
10
pkey.conf
Normal file
10
pkey.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2018-2024 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# load pkey module at boot time
|
||||||
|
pkey
|
||||||
|
pkey_cca
|
||||||
|
pkey_ep11
|
||||||
|
pkey_pckmo
|
174
qeth_configure
Normal file
174
qeth_configure
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# qeth_configure
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||||
|
#
|
||||||
|
# Configures a qeth device by calling the IBM-provided chzdev command.
|
||||||
|
# Whereas this script used to validate the parameters provided to it,
|
||||||
|
# we now rely on chzdev to do that instead. The script is intended only
|
||||||
|
# as a "translation layer" to provide backward compatability for older
|
||||||
|
# scripts and tools that invoke it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# qeth_configure [-i] [-l] [-f -t <CARDTYPE> ] [-o "Values"] -n <portno> -p <portname> <read chan> <write chan> <data chan> <online>
|
||||||
|
#
|
||||||
|
# -i Configure IP takeover
|
||||||
|
# -l Configure Layer2 support
|
||||||
|
# -f Override safety checks
|
||||||
|
# -t Valid cardtypes are: qeth, hsi - Deprecated
|
||||||
|
# -o General QETH options, separated by spaces
|
||||||
|
# -n QETH port number to use, 0 or 1. Only needed for real, not virtual
|
||||||
|
# devices.
|
||||||
|
# -p QETH Portname to use - Deprecated. OSAs no longer need a port name.
|
||||||
|
# read/write/data chan = x.y.ssss where
|
||||||
|
# x is always 0 until IBM creates something that
|
||||||
|
# uses that number
|
||||||
|
# y is the logical channel subsystem (lcss)
|
||||||
|
# number. Most often this is 0, but it could
|
||||||
|
# be non-zero
|
||||||
|
# ssss is the four digit subchannel address of
|
||||||
|
# the device, in hexidecimal, with leading
|
||||||
|
# zeros.
|
||||||
|
# online = 0 to take the device offline
|
||||||
|
# 1 to bring the device online
|
||||||
|
#
|
||||||
|
# Return values:
|
||||||
|
# Return codes are determined by the chzdev command.
|
||||||
|
#
|
||||||
|
|
||||||
|
mesg () {
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_mesg () {
|
||||||
|
case "${DEBUG}" in
|
||||||
|
yes) mesg "$@" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
add_cio_channel() {
|
||||||
|
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_cio_channel() {
|
||||||
|
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
echo "Usage: ${0} [options] <read chan> <write chan> <data chan> <online>"
|
||||||
|
echo " -i Configure IP takeover"
|
||||||
|
echo " -l Configure Layer2 support"
|
||||||
|
echo " -f Override safety checks"
|
||||||
|
echo " -t Valid cardtypes are: qeth, hsi - Deprecated."
|
||||||
|
echo " -o General QETH options, separated by spaces"
|
||||||
|
echo " -n QETH port number to use, 0 or 1. Only needed for real, not virtual"
|
||||||
|
echo " devices."
|
||||||
|
echo " -p QETH Portname to use - Deprecated. OSAs no longer need a port name."
|
||||||
|
echo " read/write/data chan = x.y.ssss where"
|
||||||
|
echo " x is always 0 until IBM creates something that"
|
||||||
|
echo " uses that number"
|
||||||
|
echo " y is the logical channel subsystem (lcss)"
|
||||||
|
echo " number. Most often this is 0, but it could"
|
||||||
|
echo " be non-zero"
|
||||||
|
echo " ssss is the four digit subchannel address of"
|
||||||
|
echo " the device, in hexidecimal, with leading"
|
||||||
|
echo " zeros."
|
||||||
|
echo " online = 0 to take the device offline"
|
||||||
|
echo " 1 to bring the device online"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${DEBUG}" != "yes" ]; then
|
||||||
|
DEBUG="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Parse the parameters from the command line
|
||||||
|
#
|
||||||
|
ARGS=$(getopt --options ifln:o:p:t: -n "qeth_configure" -- "$@")
|
||||||
|
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
|
||||||
|
|
||||||
|
eval set -- "${ARGS}"
|
||||||
|
debug_mesg "All the parms passed were ${ARGS}"
|
||||||
|
|
||||||
|
# Set some defaults
|
||||||
|
LAYER_MODE="layer2=0"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "${1}" in
|
||||||
|
-i) debug_mesg "Configure IP takeover"
|
||||||
|
PARM_LIST="${PARM_LIST} ipa_takeover/enable=1"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-f) debug_mesg "This used to mean udev rules will always be generated."
|
||||||
|
debug_mesg "For chzdev, it means safety checks will be overridden."
|
||||||
|
debug_mesg "Kinda sorta the same thing, really."
|
||||||
|
PARM_LIST="${PARM_LIST} -f"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-l) debug_mesg "Configure Layer 2 support"
|
||||||
|
LAYER_MODE="layer2=1"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
-n) debug_mesg "Set QETH port number to ${2}"
|
||||||
|
PARM_LIST="${PARM_LIST} portno=${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-o) debug_mesg "Add the following arbitrary parms: ${2}"
|
||||||
|
PARM_LIST="${PARM_LIST} ${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-p) debug_mesg "QETH Port name is no longer used, don't specify it: ${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-t) debug_mesg "This used to set the card type to ${2}"
|
||||||
|
debug_mesg "Now it gets ignored."
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--) debug_mesg "Found the end of parms indicator: --"
|
||||||
|
shift 1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*) debug_mesg "At the catch-all select entry"
|
||||||
|
debug_mesg "What was selected was ${1}"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
QETH_READ_CHAN=${1}
|
||||||
|
QETH_WRITE_CHAN=${2}
|
||||||
|
QETH_DATA_CHAN=${3}
|
||||||
|
ON_OFF=${4}
|
||||||
|
|
||||||
|
if [ -z "${QETH_READ_CHAN}" ] || [ -z "${QETH_WRITE_CHAN}" ] || [ -z "${QETH_DATA_CHAN}" ] || [ -z "${ON_OFF}" ]; then
|
||||||
|
mesg "You didn't specify all the needed parameters."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${ON_OFF}" == 0 ]; then
|
||||||
|
debug_mesg "chzdev -d qeth --no-root-update ${QETH_READ_CHAN}"
|
||||||
|
chzdev -d qeth --no-root-update ${QETH_READ_CHAN}
|
||||||
|
elif [ "${ON_OFF}" == 1 ]; then
|
||||||
|
debug_mesg "chzdev -e qeth --no-root-update ${LAYER_MODE} ${PARM_LIST} ${QETH_READ_CHAN}"
|
||||||
|
chzdev -e qeth ${LAYER_MODE} --no-root-update ${PARM_LIST} ${QETH_READ_CHAN}
|
||||||
|
else mesg "You must specify a 0 or a 1 for the online/offline attribute."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RC=${?}
|
||||||
|
if [ ${RC} -ne 0 ]; then
|
||||||
|
exit ${RC}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ON_OFF} == 1 ]; then
|
||||||
|
add_cio_channel "${QETH_READ_CHAN},${QETH_WRITE_CHAN},${QETH_DATA_CHAN}"
|
||||||
|
else remove_cio_channel "${QETH_READ_CHAN}"
|
||||||
|
remove_cio_channel "${QETH_WRITE_CHAN}"
|
||||||
|
remove_cio_channel "${QETH_DATA_CHAN}"
|
||||||
|
fi
|
66
qeth_configure.8
Normal file
66
qeth_configure.8
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
.TH qeth_configure "8" "July 2013" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
qeth_configure \- Configures or deconfigures a HiperSocket adapter or an IBM Open Systems Adapter (OSA) in QDIO mode
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B qeth_configure [options] read_channel write_channel data_channel online
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B qeth_configure
|
||||||
|
is intended to make it easy to persistently add and remove HiperSocket Adapters and Open System Adapters (OSAs) that are in QDIO mode. In addition to bringing the adapter online or offline, it will also create or delete the necessary udev rules for the adapter.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP read_channel
|
||||||
|
The device number of the read channel of the adapter. Takes the form x.y.ssss.
|
||||||
|
.IP write_channel
|
||||||
|
The device number of the write channel of the adapter.Takes the form x.y.ssss.
|
||||||
|
.IP data_channel
|
||||||
|
The device number of the data channel of the adapter.Takes the form x.y.ssss.
|
||||||
|
.RS
|
||||||
|
|
||||||
|
where
|
||||||
|
.RS
|
||||||
|
.B x
|
||||||
|
is always 0 until IBM creates something that uses that number.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B y
|
||||||
|
is the logical channel subsystem (lcss) number. Most often this is 0, but it could be non-zero.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.B ssss
|
||||||
|
is the four digit subchannel address of the device, in hexidecimal, with leading zeros. If entered in upper/mixed case, this is automatically converted to lower case.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.IP online
|
||||||
|
Either a literal 1 to bring the adapter online or a literal 0 to take it offline
|
||||||
|
.SH OPTIONAL PARAMETERS
|
||||||
|
.IP -i
|
||||||
|
Configure IP takeover
|
||||||
|
.IP -l
|
||||||
|
Configure Layer 2 support
|
||||||
|
.IP -f
|
||||||
|
Force creation of udev rules, do not check values in /sys. Requires -t to be specfied.
|
||||||
|
.IP "-t CARDTYPE"
|
||||||
|
The type of card being configured. Valid values are: qeth, hsi, or osn.
|
||||||
|
.IP "-o ""Values"""
|
||||||
|
General/arbitrary QETH options, separated by spaces
|
||||||
|
.IP "-n portnumber"
|
||||||
|
QETH port number to use, 0 or 1. Only needed for real, not virtual devices.
|
||||||
|
.IP "-p portname"
|
||||||
|
QETH Portname to use. Only needed if sharing a real OSA with z/OS.
|
||||||
|
.SH FILES
|
||||||
|
Please see the documentation of
|
||||||
|
.B chzdev.
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.IP DEBUG
|
||||||
|
If set to "yes" some minimal debugging information is output during execution.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
Messages and return codes are determined by the
|
||||||
|
.B chzdev
|
||||||
|
command.
|
||||||
|
If environment variable DEBUG is set to "yes," it shows the command line of the invoked
|
||||||
|
.B chzdev,
|
||||||
|
and a message for each command line option is issued on stdout.
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
50
read_values.8
Normal file
50
read_values.8
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.TH read_values "8" "March 2015" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
read_values \- Read information from the /sys and /proc filesystems for SUSE Customer Center (SCC) and the like.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B read_values [-s] [-u] [-c] [-a Attribute] [-L Keyword] [-d debug] [-h]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B read_values
|
||||||
|
is intended to make it easy to read values from the /sys and /proc filesystems. Those values may be at different places depending on the machine type and the kernel version.
|
||||||
|
.SH PARAMETERS
|
||||||
|
.IP -s
|
||||||
|
Outputs the values needed by the SCC (SUSE Customer Center)
|
||||||
|
.IP -u
|
||||||
|
Creates a uuid for this system
|
||||||
|
.IP -c
|
||||||
|
Prints the CPU type of the current system
|
||||||
|
.IP -a Attribute
|
||||||
|
Prints the value of the
|
||||||
|
.B Attribute
|
||||||
|
.IP -L Keyword
|
||||||
|
The
|
||||||
|
.B Keyword
|
||||||
|
may be
|
||||||
|
.B Attribute
|
||||||
|
or
|
||||||
|
.B Recognised.
|
||||||
|
With this option you get a list of all
|
||||||
|
.B Attributes
|
||||||
|
the programm can accept (
|
||||||
|
.B Attribute
|
||||||
|
) or a list of attributes which in turn can be used for the option -L (
|
||||||
|
.B Recognised
|
||||||
|
).
|
||||||
|
.SH FILES
|
||||||
|
.I /sys and /proc
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
The following messages may be issued on stderr:
|
||||||
|
.IP
|
||||||
|
.B Unable to open /proc/sysinfo
|
||||||
|
or
|
||||||
|
.B Unable to open sysinfo.zvm
|
||||||
|
.RS
|
||||||
|
The named file cannot be opened. This means the tool can't do anything useful. Return code 99 is set.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
.B Only one of the options a, c, L, s or u can be specified.
|
||||||
|
.RS
|
||||||
|
Only one of the options a, c, L, s or u can be specified at a time. Return code 1 is set.
|
||||||
|
.RE
|
||||||
|
.SH BUGS
|
||||||
|
Gotta be some, I'm sure. If you find one, please open a bug report.
|
628
read_values.c
Normal file
628
read_values.c
Normal file
@ -0,0 +1,628 @@
|
|||||||
|
/********************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Copyright (C) 2014-2015, 2019-2023 SUSE LLC */
|
||||||
|
/* */
|
||||||
|
/* All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <query_capacity.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data types
|
||||||
|
*/
|
||||||
|
enum datatypes {
|
||||||
|
integer,
|
||||||
|
string,
|
||||||
|
floatingpoint
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WITHOUT_KEY 0
|
||||||
|
#define WITH_KEY 1
|
||||||
|
|
||||||
|
static char *versionstring = "Version 1.0.5 2024-06-20 14:30";
|
||||||
|
|
||||||
|
static char *version = "1.0.5";
|
||||||
|
|
||||||
|
void *configuration_handle = NULL;
|
||||||
|
int layers = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of machine types
|
||||||
|
*/
|
||||||
|
struct machinetype {
|
||||||
|
enum qc_model_families model_families;
|
||||||
|
char *typenumber;
|
||||||
|
char *fullname;
|
||||||
|
} machinetypes[] = {
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2064", "2064 = z900 IBM eServer zSeries 900" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2066", "2066 = z800 IBM eServer zSeries 800" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2084", "2084 = z990 IBM eServer zSeries 990" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2086", "2086 = z890 IBM eServer zSeries 890" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2094", "2094 = z9-EC IBM System z9 Enterprise Class" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2096", "2096 = z9-BC IBM System z9 Business Class" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2097", "2097 = z10-EC IBM System z10 Enterprise Class" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2098", "2098 = z10-BC IBM System z10 Business Class" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2817", "2817 = z196 IBM zEnterprise 196" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2818", "2818 = z114 IBM zEnterprise 114" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2827", "2827 = z12-EC IBM zEnterprise EC12" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2828", "2828 = z12-BC IBM zEnterprise BC12" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2964", "2964 = z13 IBM z13" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "2964", "2964 = IBM LinuxONE Emperor" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "2965", "2965 = z13s IBM z13s (single frame)" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "2965", "2965 = IBM LinuxONE Rockhopper" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "3906", "3906 = z14 IBM z14" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "3906", "3906 = IBM LinuxONE Emperor II" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "3907", "3907 = z14 ZR1 IBM z14 ZR1" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "3907", "3907 = IBM LinuxONE Rockhopper II" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "8561", "8561 = z15 T01 IBM z15 T01" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "8561", "8561 = IBM LinuxONE III LT1" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "8562", "8562 = z15 T02 IBM z15 T02" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "8562", "8562 = IBM LinuxONE III LT2" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "3931", "3931 = z16 A01 IBM z16 A01" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "3931", "3931 = IBM LinuxONE Emperor 4" },
|
||||||
|
{ QC_TYPE_FAMILY_IBMZ, "3932", "3932 = z16 A02 IBM z16 A02" },
|
||||||
|
{ QC_TYPE_FAMILY_LINUXONE, "3932", "3932 = IBM LinuxONE Rockhopper 4" },
|
||||||
|
};
|
||||||
|
|
||||||
|
int debug = 0;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Print the program version */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void print_version()
|
||||||
|
{
|
||||||
|
printf("Version: %s\n", version);
|
||||||
|
}
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Look for one attribute and print it */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void print_attribute(char *user_string, int level, enum qc_attr_id attribute, enum datatypes type, int print_key)
|
||||||
|
{
|
||||||
|
int erg = 0;
|
||||||
|
const char *result_string = NULL;
|
||||||
|
int result_int = 0;
|
||||||
|
float result_float = 0.0;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case integer:
|
||||||
|
erg = qc_get_attribute_int(configuration_handle, attribute, level, &result_int);
|
||||||
|
break;
|
||||||
|
case string:
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, attribute, level, &result_string);
|
||||||
|
break;
|
||||||
|
case floatingpoint:
|
||||||
|
erg = qc_get_attribute_float(configuration_handle, attribute, level, &result_float);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (erg == 1) {
|
||||||
|
if (print_key == WITH_KEY) {
|
||||||
|
printf("%s : ",(user_string == NULL? "NULL": user_string));
|
||||||
|
} /* endif */
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case integer:
|
||||||
|
printf("%d\n",result_int);
|
||||||
|
break;
|
||||||
|
case string:
|
||||||
|
printf("%s\n", result_string);
|
||||||
|
break;
|
||||||
|
case floatingpoint:
|
||||||
|
printf("%f\n",result_float);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} /* endif */
|
||||||
|
else {
|
||||||
|
if ( erg == 0 ) {
|
||||||
|
/* printf("%s : Attribute exists, but is not set. \n", (user_string == NULL? "NULL": user_string)); */
|
||||||
|
} /* endif */
|
||||||
|
else if ( erg < 0) {
|
||||||
|
printf("%s: An error occurred retrieving the attribute. Error: erg = %d, result_string = %s \n", user_string, erg, (result_string == NULL? "NULL": result_string));
|
||||||
|
} /* end else if */
|
||||||
|
/* */
|
||||||
|
/* TODO qc_get_attribute_string returned error */
|
||||||
|
/* */
|
||||||
|
}
|
||||||
|
} /* print_attribute */
|
||||||
|
|
||||||
|
/********************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Open the lib and get the handle */
|
||||||
|
/* */
|
||||||
|
/********************************************************************************/
|
||||||
|
int read_sysinfo()
|
||||||
|
{
|
||||||
|
int return_code;
|
||||||
|
|
||||||
|
configuration_handle = qc_open(&return_code);
|
||||||
|
if (return_code < 0) {
|
||||||
|
printf("Error: Unable to open configuration, return_code =%d\n", return_code);
|
||||||
|
return -1;
|
||||||
|
} /* endif */
|
||||||
|
if (return_code > 0) {
|
||||||
|
printf("Warning: Unable to read configuration completely, return_code =%d\n", return_code);
|
||||||
|
return -2;
|
||||||
|
} /* endif */
|
||||||
|
if (configuration_handle == NULL) {
|
||||||
|
printf("Error: Unable to open configuration, return_code =%d\n", return_code);
|
||||||
|
return -3;
|
||||||
|
} /* endif */
|
||||||
|
layers = qc_get_num_layers(configuration_handle, &return_code);
|
||||||
|
if (layers < 0) {
|
||||||
|
printf("Error: Unable to retrieve number of layers, return_code =%d\n", return_code);
|
||||||
|
return -4;
|
||||||
|
} /* endif */
|
||||||
|
return 0;
|
||||||
|
} /* read_sysinfo */
|
||||||
|
|
||||||
|
/********************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Look at the type of machine we're running on and print out a user */
|
||||||
|
/* friendly string */
|
||||||
|
/* */
|
||||||
|
/********************************************************************************/
|
||||||
|
void print_cputype()
|
||||||
|
{
|
||||||
|
int i, search;
|
||||||
|
int erg;
|
||||||
|
const char *cpu_type = NULL;
|
||||||
|
int family_type = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First find out whether we run on an IBM Z, or a LinuxONE system
|
||||||
|
*/
|
||||||
|
erg = qc_get_attribute_int(configuration_handle, qc_type_family, 0, &family_type);
|
||||||
|
if (erg <= 0 || family_type == -1) {
|
||||||
|
printf("Error reading family type\n");
|
||||||
|
return;
|
||||||
|
} /* endif */
|
||||||
|
/*
|
||||||
|
* Now get the machine ID
|
||||||
|
*/
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, qc_type, 0, &cpu_type);
|
||||||
|
if (erg == 1 && cpu_type != NULL) {
|
||||||
|
for (i = 0, search = 1; (i < sizeof(machinetypes) / sizeof(struct machinetype)) && search ; i++)
|
||||||
|
{
|
||||||
|
if ((family_type == machinetypes[i].model_families) && (strcmp(cpu_type, machinetypes[i].typenumber) == 0)) {
|
||||||
|
printf("%s\n", machinetypes[i].fullname);
|
||||||
|
search = 0;
|
||||||
|
} /* endif */
|
||||||
|
} /* endfor */
|
||||||
|
if (search != 0) {
|
||||||
|
printf("An unknown machine type was reported: %s\n\
|
||||||
|
Please file a bug report with this output:\n" , cpu_type);
|
||||||
|
/* TODO output of /proc/sysinfo */
|
||||||
|
} /* endif */
|
||||||
|
} /* endif */
|
||||||
|
return;
|
||||||
|
} /* print_cputype */
|
||||||
|
|
||||||
|
/********************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Print out the values for SCC */
|
||||||
|
/* */
|
||||||
|
/* To uniquely identify a machine the following information is used: */
|
||||||
|
/* */
|
||||||
|
/* Type */
|
||||||
|
/* Sequence code */
|
||||||
|
/* CPUs total */
|
||||||
|
/* CPUs IFL */
|
||||||
|
/* LPAR Number */
|
||||||
|
/* LPAR Characteristics: */
|
||||||
|
/* LPAR CPUs */
|
||||||
|
/* LPAR IFLs */
|
||||||
|
/* */
|
||||||
|
/* Optional: */
|
||||||
|
/* */
|
||||||
|
/* VM00 Name */
|
||||||
|
/* VM00 Control Programm */
|
||||||
|
/* VM00 CPUs */
|
||||||
|
/* */
|
||||||
|
/********************************************************************************/
|
||||||
|
void print_scc()
|
||||||
|
{
|
||||||
|
print_version();
|
||||||
|
print_attribute("Type", 0, qc_type, string, WITH_KEY);
|
||||||
|
print_attribute("Type Name", 0, qc_type_name, string, WITH_KEY);
|
||||||
|
print_attribute("Sequence Code", 0, qc_sequence_code, string, WITH_KEY);
|
||||||
|
print_attribute("CPUs Total", 0, qc_num_ifl_total, integer, WITH_KEY);
|
||||||
|
print_attribute("CPUs IFL", 0, qc_num_ifl_total, integer, WITH_KEY);
|
||||||
|
print_attribute("LPAR Number", 1, qc_partition_number, integer, WITH_KEY);
|
||||||
|
print_attribute("LPAR Name", 1, qc_layer_name, string, WITH_KEY);
|
||||||
|
print_attribute("LPAR Characteristics", 1, qc_partition_char, string, WITH_KEY);
|
||||||
|
print_attribute("LPAR CPUs Total", 1, qc_num_ifl_total, integer, WITH_KEY);
|
||||||
|
print_attribute("LPAR CPUs IFL", 1, qc_num_ifl_total, integer, WITH_KEY);
|
||||||
|
if (layers > 2) {
|
||||||
|
/*
|
||||||
|
* This means, that eather zKVM or z/Vm is running
|
||||||
|
*/
|
||||||
|
print_attribute("VM00 Name", 3, qc_layer_name, string, WITH_KEY);
|
||||||
|
print_attribute("VM00 Control Program", 2, qc_control_program_id, string, WITH_KEY);
|
||||||
|
print_attribute("VM00 CPUs Total", 3, qc_num_cpu_total, integer, WITH_KEY);
|
||||||
|
print_attribute("VM00 IFLs", 3, qc_num_cpu_total, integer, WITH_KEY);
|
||||||
|
} /* endif */
|
||||||
|
return;
|
||||||
|
} /* print_scc */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Secure boot support models ( check_model () ) */
|
||||||
|
/* Only the following machines support secure boot: */
|
||||||
|
/* z14, z14 ZR1, z15, z16 */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
int check_model (const char *cpu) {
|
||||||
|
|
||||||
|
#define IBM_Models 6 /* Number of IBM models listed below */
|
||||||
|
char *types[IBM_Models] = {
|
||||||
|
"3906",
|
||||||
|
"3907",
|
||||||
|
"8561",
|
||||||
|
"8562",
|
||||||
|
"3931",
|
||||||
|
"3932",
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int models = sizeof(types) / sizeof(types[0]);
|
||||||
|
|
||||||
|
for ( i = 0; i < models; i++) {
|
||||||
|
|
||||||
|
if ( !strcmp(cpu,types[i]) ) {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} /* check_model */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* print out whether secure boot is enabled */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void print_secure_mode()
|
||||||
|
{
|
||||||
|
int erg;
|
||||||
|
int release_major;
|
||||||
|
int release_sub;
|
||||||
|
int release_minor;
|
||||||
|
const char *cpu_type = NULL;
|
||||||
|
int cpu_okay = 0;
|
||||||
|
int Layer = 0;
|
||||||
|
int i = 0;
|
||||||
|
/*
|
||||||
|
* First we have to check whether we have the appropriate kernel Level (>= 5.3)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct utsname uts;
|
||||||
|
|
||||||
|
erg = uname(&uts);
|
||||||
|
if (erg != 0) {
|
||||||
|
perror ("Error executing uname(): ");
|
||||||
|
return;
|
||||||
|
} /* endif */
|
||||||
|
#if 0
|
||||||
|
printf("sysname: %s\n", uts.sysname);
|
||||||
|
printf("nodename: %s\n", uts.nodename);
|
||||||
|
printf("release: %s\n", uts.release);
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* A release number looks like: m.s.mi
|
||||||
|
* where m, s, mi are numbers with one ore more digits
|
||||||
|
* Minimum kernel version is 5.3
|
||||||
|
*/
|
||||||
|
erg = sscanf(uts.release,"%d.%d.%d-%*s", &release_major, &release_sub, &release_minor);
|
||||||
|
if ( release_major < 5 ) {
|
||||||
|
goto return_does_not_exist;
|
||||||
|
}
|
||||||
|
if ( release_sub < 3 ) {
|
||||||
|
goto return_does_not_exist;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
printf("Translated successfully: %d\n", erg);
|
||||||
|
printf("release_major: %d\n", release_major);
|
||||||
|
printf("release_sub: %d\n", release_sub);
|
||||||
|
printf("release_minor: %d\n", release_minor);
|
||||||
|
printf("version: %s\n", uts.version);
|
||||||
|
printf("machine: %s\n", uts.machine);
|
||||||
|
printf("Print_secure called\n");
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Only the following machines support secure boot:
|
||||||
|
* z14, z15, z16
|
||||||
|
* 3906, 3907, 8561, 8562, 3931, 3932
|
||||||
|
*/
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, qc_type, 0, &cpu_type);
|
||||||
|
if (erg == 1 && cpu_type != NULL) {
|
||||||
|
cpu_okay = check_model(cpu_type);
|
||||||
|
if ( cpu_okay == 0 ) {
|
||||||
|
goto return_does_not_exist;
|
||||||
|
} /* endif */
|
||||||
|
} /* endif */
|
||||||
|
|
||||||
|
for ( i = 0; i < layers; i++) {
|
||||||
|
erg = qc_get_attribute_int(configuration_handle, qc_layer_type_num, i, &Layer);
|
||||||
|
if (erg == 1) {
|
||||||
|
print_attribute("Secure mode on ", i, qc_has_secure, integer, WITH_KEY);
|
||||||
|
print_attribute("Secure mode used", i, qc_secure, integer, WITH_KEY);
|
||||||
|
} /* endif */
|
||||||
|
} /* endfor */
|
||||||
|
return;
|
||||||
|
|
||||||
|
return_does_not_exist:
|
||||||
|
/*
|
||||||
|
* Software or hardware does not support secure boot.
|
||||||
|
*/
|
||||||
|
puts("Secure mode on : 0\nSecure mode used : 0");
|
||||||
|
return;
|
||||||
|
} /* print_secure_mode */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* print out the uuid for this machine */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
int print_uuid()
|
||||||
|
{
|
||||||
|
const char *result_string = NULL;
|
||||||
|
int erg;
|
||||||
|
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, qc_sequence_code, 0, &result_string);
|
||||||
|
if (erg != 1)
|
||||||
|
{
|
||||||
|
puts("Error reading the Serial Number");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("%s", result_string);
|
||||||
|
|
||||||
|
result_string = NULL;
|
||||||
|
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, qc_layer_name, 1, &result_string);
|
||||||
|
if (erg != 1)
|
||||||
|
{
|
||||||
|
puts("Error reading the LPAR Name");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("-%s", result_string);
|
||||||
|
|
||||||
|
result_string = NULL;
|
||||||
|
if (layers > 2) {
|
||||||
|
|
||||||
|
erg = qc_get_attribute_string(configuration_handle, qc_layer_name, 3, &result_string);
|
||||||
|
if (erg != 1)
|
||||||
|
{
|
||||||
|
puts("Error Reading the VM Name");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("-%s", result_string);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
return 0;
|
||||||
|
} /* print_uuid */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* print out the list of valid / found symbols */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void list(char * list_attribute_param)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
} /* list */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* print out the requested attribute */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void print_user_attribute(char *key, char *attribute_param, int layer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
} /* print_user_attribute */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Help Function */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
void help()
|
||||||
|
{
|
||||||
|
puts("help:\n\
|
||||||
|
\n\
|
||||||
|
-a <attribute> List the value of the named attribute\n\
|
||||||
|
-c Print the cputype of this machine\n\
|
||||||
|
-d <number> Debug Level\n\
|
||||||
|
-h this help\n\
|
||||||
|
-L <keyword> List the requested list (Attribute, Recognised)\n\
|
||||||
|
-s create Info for SCC\n\
|
||||||
|
-S report whether secure boot is switched on\n\
|
||||||
|
-u create uuid\n\
|
||||||
|
-V print version string\n\
|
||||||
|
");
|
||||||
|
#if 0
|
||||||
|
if (debug != 0) {
|
||||||
|
puts("\n\
|
||||||
|
Valid values for debug:\n\
|
||||||
|
4 - read sysinfo.zvm from current directory instead of /proc/sysinfo\n\
|
||||||
|
8 - printout lines read in from source (see debug == 4)\n\
|
||||||
|
16 - printf found keys in store_value\n\
|
||||||
|
32 - Search expression in show attribute\n\
|
||||||
|
");
|
||||||
|
} /* endif */
|
||||||
|
#endif
|
||||||
|
} /* help */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Main */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
int main(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
int read_sysinfo_opt;
|
||||||
|
int print_attr;
|
||||||
|
int print_cpu;
|
||||||
|
int print_secure;
|
||||||
|
int print_help;
|
||||||
|
int list_attr;
|
||||||
|
int create_scc;
|
||||||
|
int create_uuid;
|
||||||
|
int erg;
|
||||||
|
int return_code;
|
||||||
|
char *print_attribute_param = NULL;
|
||||||
|
char *list_attribute_param = NULL;
|
||||||
|
void *configuration_handle_tmp = NULL;
|
||||||
|
|
||||||
|
read_sysinfo_opt =
|
||||||
|
print_attr =
|
||||||
|
print_cpu =
|
||||||
|
print_secure =
|
||||||
|
print_help =
|
||||||
|
list_attr =
|
||||||
|
create_scc =
|
||||||
|
create_uuid =
|
||||||
|
return_code =
|
||||||
|
erg = 0;
|
||||||
|
if (strcmp(argv[0],"cputype") == 0) {
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
print_cpu++;
|
||||||
|
} /* endif */
|
||||||
|
else {
|
||||||
|
while ((opt = getopt(argc, argv, "a:cd:hL:sSuV")) != -1) {
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'a':
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
print_attr++;
|
||||||
|
print_attribute_param = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
print_cpu++;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
debug = atoi(optarg);
|
||||||
|
if ((debug & 1) == 1) {
|
||||||
|
setenv("QC_DEBUG", "1", 1);
|
||||||
|
} /* endif */
|
||||||
|
if ((debug & 2) == 2) {
|
||||||
|
setenv("QC_AUTODUMP", "1", 1);
|
||||||
|
} /* endif */
|
||||||
|
debug = debug >> 2;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
list_attr++;
|
||||||
|
list_attribute_param = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 's': /* create unique string for scc */
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
create_scc++;
|
||||||
|
break;
|
||||||
|
case 'S': /* print out whether secure boot is enabled */
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
print_secure++;
|
||||||
|
break;
|
||||||
|
case 'u': /* create UUID */
|
||||||
|
read_sysinfo_opt++;
|
||||||
|
create_uuid++;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
printf("%s\n",versionstring);
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
print_help++;
|
||||||
|
break;
|
||||||
|
} /* endswitch */
|
||||||
|
} /* while */
|
||||||
|
} /* endlse */
|
||||||
|
if (print_help != 0) {
|
||||||
|
help();
|
||||||
|
return 0;
|
||||||
|
} /* endif */
|
||||||
|
if (read_sysinfo_opt != 0) {
|
||||||
|
if ((erg = read_sysinfo()) != 0) {
|
||||||
|
return -erg;
|
||||||
|
} /* endif */
|
||||||
|
} /* endif */
|
||||||
|
if ((print_attr + print_cpu + print_secure + list_attr + create_scc + create_uuid) > 1) {
|
||||||
|
fputs("Only one of the options a, c, L, s, S or u can be specified.\n",stderr);
|
||||||
|
return 1;
|
||||||
|
} /* endif */
|
||||||
|
/* still not implemented thatfore set to zero */
|
||||||
|
list_attr = print_attr = 0;
|
||||||
|
if (print_attr != 0) {
|
||||||
|
print_user_attribute(NULL, print_attribute_param, layers);
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
if (print_cpu != 0) {
|
||||||
|
print_cputype();
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
if (print_secure != 0) {
|
||||||
|
print_secure_mode();
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
if (list_attr != 0) {
|
||||||
|
list(list_attribute_param);
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
if (create_scc != 0) {
|
||||||
|
print_scc();
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
if (create_uuid != 0) {
|
||||||
|
if(print_uuid() == 1){
|
||||||
|
goto main_exit_error;
|
||||||
|
}
|
||||||
|
goto main_exit;
|
||||||
|
} /* endif */
|
||||||
|
help();
|
||||||
|
main_exit:
|
||||||
|
if (configuration_handle != NULL) {
|
||||||
|
configuration_handle_tmp = qc_open(&return_code);
|
||||||
|
qc_close(configuration_handle);
|
||||||
|
setenv("QC_DEBUG", "0", 1);
|
||||||
|
setenv("QC_AUTODUMP", "0", 1);
|
||||||
|
qc_close(configuration_handle_tmp);
|
||||||
|
} /* endif */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
main_exit_error:
|
||||||
|
return 1;
|
||||||
|
} /* end main */
|
2
rules.hw_random
Normal file
2
rules.hw_random
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Rules to add hw_random node to maintain SLES11-SP1 backward compatibility
|
||||||
|
KERNEL=="hwrng", SYMLINK+="hw_random"
|
2
rules.xpram
Normal file
2
rules.xpram
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Rules to add xpram* nodes to maintain SLES11-SP1 backward compatibility
|
||||||
|
KERNEL=="sl*[0-9]", SYMLINK+="xpram%n"
|
@ -0,0 +1,64 @@
|
|||||||
|
From dff965465ca9d9c4edaf0f90eadd9a6de335b354 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Date: Fri, 6 Dec 2024 15:28:08 +0100
|
||||||
|
Subject: [PATCH] opticsmon: Fix runaway loop in on_link_change()
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
When on_link_change() gets called with a netdev that would be monitored
|
||||||
|
but hasn't entered zpci_list yet, reloads is 1 after the loops and
|
||||||
|
a reload occurs. Then the netdev is found in the list and reloads
|
||||||
|
becomes -1 which incorrectly triggers more reloads until underflow.
|
||||||
|
Fix this by returning once the device is found. Also just check for
|
||||||
|
reloads being larger than zero.
|
||||||
|
|
||||||
|
Fixes: c34adb9cabee ("opticsmon: Introduce opticsmon tool")
|
||||||
|
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||||
|
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
opticsmon/opticsmon.c | 10 +++++-----
|
||||||
|
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||||
|
index c2f355e2..50dd8d7f 100644
|
||||||
|
--- a/opticsmon/opticsmon.c
|
||||||
|
+++ b/opticsmon/opticsmon.c
|
||||||
|
@@ -280,16 +280,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
|
||||||
|
-reload:
|
||||||
|
+find:
|
||||||
|
util_list_iterate(ctx->zpci_list, zdev) {
|
||||||
|
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||||
|
- reloads--;
|
||||||
|
/* Skip data collection if operational state is
|
||||||
|
* unchanged
|
||||||
|
*/
|
||||||
|
if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||||
|
- continue;
|
||||||
|
+ return;
|
||||||
|
/* Update operation state for VFs even though
|
||||||
|
* they are skipped just for a consistent view
|
||||||
|
*/
|
||||||
|
@@ -297,14 +296,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
/* Only collect optics data for PFs */
|
||||||
|
if (!zpci_is_vf(zdev))
|
||||||
|
dump_adapter_data(ctx, zdev);
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Might be a new device, reload list of devices and retry */
|
||||||
|
- if (reloads) {
|
||||||
|
+ if (reloads > 0) {
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
reloads--;
|
||||||
|
- goto reload;
|
||||||
|
+ goto find;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,969 @@
|
|||||||
|
From d6b702d5791b47f735960ad1f6986e0a32768df6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
Date: Thu, 11 Jul 2024 10:43:37 +0200
|
||||||
|
Subject: [PATCH] zipl/src: add basic support for multiple target base disks
|
||||||
|
|
||||||
|
. Modify disk_get_info() to process multiple sets of target parameters
|
||||||
|
provided by the helper script and store it in the array of "targets"
|
||||||
|
of the structure job_target_data;
|
||||||
|
. Besides the logical device, maintain an array of physical base disks
|
||||||
|
in the disk_info structure;
|
||||||
|
. Use the logical target device only to create bootmap (it is
|
||||||
|
automatically mirrored by the respective linux driver (dm, or md)
|
||||||
|
managing the mirrored target). In contrast, install bootstrap blocks
|
||||||
|
to each physical base disk individually, bypassing that driver;
|
||||||
|
. Report in verbose mode on which base disks the bootstrap
|
||||||
|
installation was performed;
|
||||||
|
. Use the following logic of setting @info->device (which is printed
|
||||||
|
as "Device...:" in verbose mode):
|
||||||
|
. source_auto - the target base disk is set;
|
||||||
|
. source_script - the target (logical) device is set;
|
||||||
|
. source_user - the device specified by user (via --targetbase
|
||||||
|
option), or config file is set.
|
||||||
|
|
||||||
|
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zipl/include/disk.h | 14 +-
|
||||||
|
zipl/include/install.h | 2
|
||||||
|
zipl/include/job.h | 122 ++++++++++++++++++--
|
||||||
|
zipl/include/zipl.h | 1
|
||||||
|
zipl/src/bootmap.c | 23 ++-
|
||||||
|
zipl/src/disk.c | 295 +++++++++++++++++++++++++++++++++++++------------
|
||||||
|
zipl/src/install.c | 89 +++++++++-----
|
||||||
|
zipl/src/job.c | 82 +++++++------
|
||||||
|
8 files changed, 469 insertions(+), 159 deletions(-)
|
||||||
|
|
||||||
|
--- a/zipl/include/disk.h
|
||||||
|
+++ b/zipl/include/disk.h
|
||||||
|
@@ -56,13 +56,14 @@
|
||||||
|
/* targetbase definition */
|
||||||
|
typedef enum {
|
||||||
|
defined_as_device,
|
||||||
|
- defined_as_name
|
||||||
|
+ defined_as_name,
|
||||||
|
+ undefined
|
||||||
|
} definition_t;
|
||||||
|
|
||||||
|
/* Disk information type */
|
||||||
|
struct disk_info {
|
||||||
|
disk_type_t type;
|
||||||
|
- dev_t device;
|
||||||
|
+ dev_t device; /* logical device for bootmap creation */
|
||||||
|
dev_t partition;
|
||||||
|
int devno;
|
||||||
|
int partnum;
|
||||||
|
@@ -72,8 +73,11 @@
|
||||||
|
struct hd_geometry geo;
|
||||||
|
char* name;
|
||||||
|
char* drv_name;
|
||||||
|
- definition_t targetbase;
|
||||||
|
+ definition_t targetbase_def;
|
||||||
|
int is_nvme;
|
||||||
|
+ dev_t basedisks[MAX_TARGETS]; /* array of physical disks for
|
||||||
|
+ * bootstrap blocks recording
|
||||||
|
+ */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct file_range {
|
||||||
|
@@ -113,6 +117,9 @@
|
||||||
|
struct disk_info *info, int align,
|
||||||
|
off_t *offset);
|
||||||
|
void disk_print_devt(dev_t d);
|
||||||
|
+void disk_print_devname(dev_t d);
|
||||||
|
+void prepare_footnote_ptr(int source, char *ptr);
|
||||||
|
+void print_footnote_ref(int source, const char *prefix);
|
||||||
|
void disk_print_info(struct disk_info *info, int source);
|
||||||
|
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
|
||||||
|
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
||||||
|
@@ -122,7 +129,6 @@
|
||||||
|
disk_blockptr_t** blocklist,
|
||||||
|
struct disk_info* pinfo);
|
||||||
|
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
|
||||||
|
-void disk_print_geo(struct disk_info *data);
|
||||||
|
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
|
||||||
|
|
||||||
|
#endif /* not DISK_H */
|
||||||
|
--- a/zipl/include/install.h
|
||||||
|
+++ b/zipl/include/install.h
|
||||||
|
@@ -71,7 +71,7 @@
|
||||||
|
struct program_component *components[NR_PROGRAM_COMPONENTS];
|
||||||
|
int nr_menu_entries;
|
||||||
|
int fd;
|
||||||
|
- char *device;
|
||||||
|
+ char *basetmp[MAX_TARGETS];
|
||||||
|
char *filename;
|
||||||
|
unsigned int tmp_filename_created:1;
|
||||||
|
unsigned int skip_prepare:1;
|
||||||
|
--- a/zipl/include/job.h
|
||||||
|
+++ b/zipl/include/job.h
|
||||||
|
@@ -18,7 +18,6 @@
|
||||||
|
#include "disk.h"
|
||||||
|
#include "zipl.h"
|
||||||
|
|
||||||
|
-
|
||||||
|
enum job_id {
|
||||||
|
job_print_usage = 1,
|
||||||
|
job_print_version = 2,
|
||||||
|
@@ -30,6 +29,21 @@
|
||||||
|
job_mvdump = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Set of parameters per physical disk, which are provided
|
||||||
|
+ * either by user, or by helper script
|
||||||
|
+ */
|
||||||
|
+struct target {
|
||||||
|
+ char *targetbase;
|
||||||
|
+ disk_type_t targettype;
|
||||||
|
+ int targetcylinders;
|
||||||
|
+ int targetheads;
|
||||||
|
+ int targetsectors;
|
||||||
|
+ int targetblocksize;
|
||||||
|
+ blocknum_t targetoffset;
|
||||||
|
+ int check_params;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* target information source */
|
||||||
|
typedef enum {
|
||||||
|
source_unknown = 0,
|
||||||
|
@@ -39,17 +53,21 @@
|
||||||
|
} source_t;
|
||||||
|
|
||||||
|
struct job_target_data {
|
||||||
|
- char* bootmap_dir;
|
||||||
|
- char* targetbase;
|
||||||
|
- disk_type_t targettype;
|
||||||
|
- int targetcylinders;
|
||||||
|
- int targetheads;
|
||||||
|
- int targetsectors;
|
||||||
|
- int targetblocksize;
|
||||||
|
- blocknum_t targetoffset;
|
||||||
|
+ char *bootmap_dir;
|
||||||
|
+ int nr_targets;
|
||||||
|
+ struct target targets[MAX_TARGETS];
|
||||||
|
source_t source;
|
||||||
|
};
|
||||||
|
|
||||||
|
+enum target_params {
|
||||||
|
+ TARGET_BASE,
|
||||||
|
+ TARGET_TYPE,
|
||||||
|
+ TARGET_GEOMETRY,
|
||||||
|
+ TARGET_BLOCKSIZE,
|
||||||
|
+ TARGET_OFFSET,
|
||||||
|
+ LAST_TARGET_PARAM
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
struct job_common_ipl_data {
|
||||||
|
char* image;
|
||||||
|
char* parmline;
|
||||||
|
@@ -142,12 +160,94 @@
|
||||||
|
int is_ldipl_dump;
|
||||||
|
};
|
||||||
|
|
||||||
|
+static inline struct target *target_at(struct job_target_data *data,
|
||||||
|
+ int index)
|
||||||
|
+{
|
||||||
|
+ return index >= MAX_TARGETS ? NULL : &data->targets[index];
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline char *get_targetbase(struct job_target_data *data, int index)
|
||||||
|
+{
|
||||||
|
+ return target_at(data, index)->targetbase;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void set_targetbase(struct job_target_data *data, int index,
|
||||||
|
+ char *value)
|
||||||
|
+{
|
||||||
|
+ target_at(data, index)->targetbase = value;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline disk_type_t get_targettype(struct job_target_data *data,
|
||||||
|
+ int index)
|
||||||
|
+{
|
||||||
|
+ return target_at(data, index)->targettype;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int set_targettype(struct job_target_data *data, int index, char *value);
|
||||||
|
+
|
||||||
|
+static inline char *job_get_targetbase(struct job_data *job)
|
||||||
|
+{
|
||||||
|
+ return get_targetbase(&job->target, 0);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void job_set_targetbase(struct job_data *job, char *value)
|
||||||
|
+{
|
||||||
|
+ set_targetbase(&job->target, 0, value);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline int job_get_nr_targets(struct job_data *job)
|
||||||
|
+{
|
||||||
|
+ return job->target.nr_targets;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void job_set_nr_targets(struct job_data *job, int value)
|
||||||
|
+{
|
||||||
|
+ job->target.nr_targets = value;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline disk_type_t job_get_targettype(struct job_data *job)
|
||||||
|
+{
|
||||||
|
+ return get_targettype(&job->target, 0);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int job_set_targettype(struct job_data *job, char *value);
|
||||||
|
+
|
||||||
|
+#define define_target_param_ops(_TYPE_, _PARAM_) \
|
||||||
|
+static inline _TYPE_ get_target##_PARAM_(struct job_target_data *data, \
|
||||||
|
+ int index) \
|
||||||
|
+{ \
|
||||||
|
+ return target_at(data, index)->target##_PARAM_; \
|
||||||
|
+} \
|
||||||
|
+ \
|
||||||
|
+static inline void set_target##_PARAM_(struct job_target_data *data, \
|
||||||
|
+ int index, _TYPE_ value) \
|
||||||
|
+{ \
|
||||||
|
+ target_at(data, index)->target##_PARAM_ = value; \
|
||||||
|
+} \
|
||||||
|
+ \
|
||||||
|
+static inline _TYPE_ job_get_target##_PARAM_(struct job_data *job) \
|
||||||
|
+{ \
|
||||||
|
+ return get_target##_PARAM_(&job->target, 0); \
|
||||||
|
+} \
|
||||||
|
+ \
|
||||||
|
+static inline void job_set_target##_PARAM_(struct job_data *job, \
|
||||||
|
+ _TYPE_ value) \
|
||||||
|
+{ \
|
||||||
|
+ set_target##_PARAM_(&job->target, 0, value); \
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+define_target_param_ops(int, cylinders)
|
||||||
|
+define_target_param_ops(int, heads)
|
||||||
|
+define_target_param_ops(int, sectors)
|
||||||
|
+define_target_param_ops(int, blocksize)
|
||||||
|
+define_target_param_ops(blocknum_t, offset)
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
- * Return true, if target parameters for the base disk are set
|
||||||
|
+ * Return true, if target parameters are set at least for one target base disk
|
||||||
|
*/
|
||||||
|
static inline int target_parameters_are_set(struct job_target_data *td)
|
||||||
|
{
|
||||||
|
- return td->targetbase != NULL;
|
||||||
|
+ return get_targetbase(td, 0) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int job_get(int argc, char* argv[], struct job_data** data);
|
||||||
|
--- a/zipl/include/zipl.h
|
||||||
|
+++ b/zipl/include/zipl.h
|
||||||
|
@@ -41,6 +41,7 @@
|
||||||
|
#define MENU_DEFAULT_TIMEOUT 0
|
||||||
|
|
||||||
|
#define MAX_DUMP_VOLUMES 32
|
||||||
|
+#define MAX_TARGETS 32
|
||||||
|
|
||||||
|
#define SECURE_BOOT_UNDEFINED -1
|
||||||
|
#define SECURE_BOOT_DISABLED 0
|
||||||
|
--- a/zipl/src/bootmap.c
|
||||||
|
+++ b/zipl/src/bootmap.c
|
||||||
|
@@ -1477,9 +1477,9 @@
|
||||||
|
printf("Target device information\n");
|
||||||
|
disk_print_info(bis->info, job->target.source);
|
||||||
|
}
|
||||||
|
- if (misc_temp_dev(bis->info->device, 1, &bis->device))
|
||||||
|
+ if (misc_temp_dev(bis->info->device, 1, &bis->basetmp[0]))
|
||||||
|
return -1;
|
||||||
|
- if (check_dump_device(job, bis->info, bis->device))
|
||||||
|
+ if (check_dump_device(job, bis->info, bis->basetmp[0]))
|
||||||
|
return -1;
|
||||||
|
printf("Building bootmap directly on partition '%s'%s\n",
|
||||||
|
bis->filename,
|
||||||
|
@@ -1543,6 +1543,8 @@
|
||||||
|
static int prepare_build_program_table_file(struct job_data *job,
|
||||||
|
struct install_set *bis)
|
||||||
|
{
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
if (bis->skip_prepare)
|
||||||
|
/* skip the preparation work */
|
||||||
|
return 0;
|
||||||
|
@@ -1576,8 +1578,12 @@
|
||||||
|
printf("Target device information\n");
|
||||||
|
disk_print_info(bis->info, job->target.source);
|
||||||
|
}
|
||||||
|
- if (misc_temp_dev(bis->info->device, 1, &bis->device))
|
||||||
|
- return -1;
|
||||||
|
+ for (i = 0; i < job_get_nr_targets(job); i++) {
|
||||||
|
+ if (misc_temp_dev(bis->info->basedisks[i],
|
||||||
|
+ 1,
|
||||||
|
+ &bis->basetmp[i]))
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
/* Check configuration number limits */
|
||||||
|
if (job->id == job_menu) {
|
||||||
|
if (check_menu_positions(&job->data.menu, job->name,
|
||||||
|
@@ -1692,9 +1698,9 @@
|
||||||
|
/* Retrieve target device information */
|
||||||
|
if (disk_get_info(job->data.dump.device, &job->target, &info))
|
||||||
|
return -1;
|
||||||
|
- if (misc_temp_dev(info->device, 1, &bis->device))
|
||||||
|
+ if (misc_temp_dev(info->device, 1, &bis->basetmp[0]))
|
||||||
|
return -1;
|
||||||
|
- if (check_dump_device(job, info, bis->device))
|
||||||
|
+ if (check_dump_device(job, info, bis->basetmp[0]))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
assert(!job->target.bootmap_dir);
|
||||||
|
@@ -1844,6 +1850,9 @@
|
||||||
|
if (bis->tmp_filename_created)
|
||||||
|
misc_free_temp_file(bis->filename);
|
||||||
|
free(bis->filename);
|
||||||
|
- misc_free_temp_dev(bis->device);
|
||||||
|
+ for (i = 0; i < MAX_TARGETS; i++) {
|
||||||
|
+ if (bis->basetmp[i])
|
||||||
|
+ misc_free_temp_dev(bis->basetmp[i]);
|
||||||
|
+ }
|
||||||
|
disk_free_info(bis->info);
|
||||||
|
}
|
||||||
|
--- a/zipl/src/disk.c
|
||||||
|
+++ b/zipl/src/disk.c
|
||||||
|
@@ -187,43 +187,134 @@
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * Process a script output represented by FH and consisting
|
||||||
|
+ * of pairs 'key=value' (each such pair is on a separate line).
|
||||||
|
+ * Check its consistency and set the extracted target parameters
|
||||||
|
+ * to the array of "targets" at TD.
|
||||||
|
+ *
|
||||||
|
+ * NOTE: this function defines specifications on valid output of
|
||||||
|
+ * zipl helper scripts. See zipl-support-for-mirrored-devices.txt
|
||||||
|
+ * for details. Before modifying this function, make sure that it
|
||||||
|
+ * won't lead to format change.
|
||||||
|
+ */
|
||||||
|
static int set_target_parameters(FILE *fh, struct job_target_data *td)
|
||||||
|
{
|
||||||
|
- int checkparm = 0;
|
||||||
|
+ int idx[LAST_TARGET_PARAM] = {0};
|
||||||
|
+ struct target *t;
|
||||||
|
char buffer[80];
|
||||||
|
char value[40];
|
||||||
|
+ char *error;
|
||||||
|
+ int i;
|
||||||
|
|
||||||
|
+ /**
|
||||||
|
+ * Process a stream of 'key=value' pairs and distribute
|
||||||
|
+ * them into groups.
|
||||||
|
+ * The i-th occurrence of some "key" in the stream means
|
||||||
|
+ * that the respective pair belongs to the group #i
|
||||||
|
+ */
|
||||||
|
+ error = "Exceeded the maximum number of base disks";
|
||||||
|
while (fgets(buffer, 80, fh)) {
|
||||||
|
if (sscanf(buffer, "targetbase=%s", value) == 1) {
|
||||||
|
- td->targetbase = misc_strdup(value);
|
||||||
|
- checkparm++;
|
||||||
|
+ t = target_at(td, idx[TARGET_BASE]++);
|
||||||
|
+ if (!t)
|
||||||
|
+ goto error;
|
||||||
|
+ t->targetbase = misc_strdup(value);
|
||||||
|
+ goto found;
|
||||||
|
}
|
||||||
|
if (sscanf(buffer, "targettype=%s", value) == 1) {
|
||||||
|
- type_from_target(value, &td->targettype);
|
||||||
|
- checkparm++;
|
||||||
|
+ t = target_at(td, idx[TARGET_TYPE]++);
|
||||||
|
+ if (!t)
|
||||||
|
+ goto error;
|
||||||
|
+ type_from_target(value, &t->targettype);
|
||||||
|
+ goto found;
|
||||||
|
}
|
||||||
|
if (sscanf(buffer, "targetgeometry=%s", value) == 1) {
|
||||||
|
- td->targetcylinders =
|
||||||
|
- atoi(strtok(value, ","));
|
||||||
|
- td->targetheads = atoi(strtok(NULL, ","));
|
||||||
|
- td->targetsectors = atoi(strtok(NULL, ","));
|
||||||
|
- checkparm++;
|
||||||
|
+ t = target_at(td, idx[TARGET_GEOMETRY]++);
|
||||||
|
+ if (!t)
|
||||||
|
+ goto error;
|
||||||
|
+ t->targetcylinders = atoi(strtok(value, ","));
|
||||||
|
+ t->targetheads = atoi(strtok(NULL, ","));
|
||||||
|
+ t->targetsectors = atoi(strtok(NULL, ","));
|
||||||
|
+ goto found;
|
||||||
|
}
|
||||||
|
if (sscanf(buffer, "targetblocksize=%s", value) == 1) {
|
||||||
|
- td->targetblocksize = atoi(value);
|
||||||
|
- checkparm++;
|
||||||
|
+ t = target_at(td, idx[TARGET_BLOCKSIZE]++);
|
||||||
|
+ if (!t)
|
||||||
|
+ goto error;
|
||||||
|
+ t->targetblocksize = atoi(value);
|
||||||
|
+ goto found;
|
||||||
|
}
|
||||||
|
if (sscanf(buffer, "targetoffset=%s", value) == 1) {
|
||||||
|
- td->targetoffset = atol(value);
|
||||||
|
- checkparm++;
|
||||||
|
+ t = target_at(td, idx[TARGET_OFFSET]++);
|
||||||
|
+ if (!t)
|
||||||
|
+ goto error;
|
||||||
|
+ t->targetoffset = atol(value);
|
||||||
|
+ goto found;
|
||||||
|
}
|
||||||
|
+ continue;
|
||||||
|
+found:
|
||||||
|
+ t->check_params++;
|
||||||
|
}
|
||||||
|
- if ((!disk_is_eckd(td->targettype) && checkparm < 4) ||
|
||||||
|
- (disk_is_eckd(td->targettype) && checkparm != 5)) {
|
||||||
|
- error_reason("Target parameters missing from script");
|
||||||
|
- return -1;
|
||||||
|
+ /* Check for consistency */
|
||||||
|
+ error = "Inconsistent script output";
|
||||||
|
+ /*
|
||||||
|
+ * First, calculate total number of groups
|
||||||
|
+ */
|
||||||
|
+ td->nr_targets = 0;
|
||||||
|
+ for (i = 0; i < MAX_TARGETS; i++) {
|
||||||
|
+ t = target_at(td, i);
|
||||||
|
+ if (t->check_params == 0)
|
||||||
|
+ break;
|
||||||
|
+ td->nr_targets++;
|
||||||
|
+ }
|
||||||
|
+ if (!td->nr_targets)
|
||||||
|
+ /* No keywords found in the stream */
|
||||||
|
+ goto error;
|
||||||
|
+ /*
|
||||||
|
+ * Each group has to include targetbase, targettype,
|
||||||
|
+ * targetblocksize and targetoffset.
|
||||||
|
+ */
|
||||||
|
+ if (td->nr_targets != idx[TARGET_BASE] ||
|
||||||
|
+ td->nr_targets != idx[TARGET_TYPE] ||
|
||||||
|
+ td->nr_targets != idx[TARGET_BLOCKSIZE] ||
|
||||||
|
+ td->nr_targets != idx[TARGET_OFFSET])
|
||||||
|
+ goto error;
|
||||||
|
+ /*
|
||||||
|
+ * In addition, any group of "ECKD" type has to include
|
||||||
|
+ * targetgeometry
|
||||||
|
+ */
|
||||||
|
+ for (i = 0; i < td->nr_targets; i++) {
|
||||||
|
+ t = target_at(td, i);
|
||||||
|
+ assert(t->check_params >= 4);
|
||||||
|
+ if (disk_is_eckd(t->targettype) && t->check_params != 5)
|
||||||
|
+ goto error;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
+error:
|
||||||
|
+ error_reason("%s", error);
|
||||||
|
+ return -1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void print_base_disk_params(struct job_target_data *td, int index)
|
||||||
|
+{
|
||||||
|
+ disk_type_t type = get_targettype(td, index);
|
||||||
|
+
|
||||||
|
+ if (!verbose)
|
||||||
|
+ return;
|
||||||
|
+ {
|
||||||
|
+ fprintf(stderr, "Base disk '%s':\n", get_targetbase(td, index));
|
||||||
|
+ fprintf(stderr, " layout........: %s\n", disk_get_type_name(type));
|
||||||
|
+ }
|
||||||
|
+ if (disk_is_eckd(type)) {
|
||||||
|
+ fprintf(stderr, " heads.........: %u\n", get_targetheads(td, index));
|
||||||
|
+ fprintf(stderr, " sectors.......: %u\n", get_targetsectors(td, index));
|
||||||
|
+ fprintf(stderr, " cylinders.....: %u\n", get_targetcylinders(td, index));
|
||||||
|
+ }
|
||||||
|
+ {
|
||||||
|
+ fprintf(stderr, " start.........: %lu\n", get_targetoffset(td, index));
|
||||||
|
+ fprintf(stderr, " blksize.......: %u\n", get_targetblocksize(td, index));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -235,31 +326,57 @@
|
||||||
|
{
|
||||||
|
int majnum, minnum;
|
||||||
|
struct stat stats;
|
||||||
|
-
|
||||||
|
+ int i;
|
||||||
|
+ /*
|
||||||
|
+ * Currently multiple base disks with different parameters
|
||||||
|
+ * are not supported
|
||||||
|
+ */
|
||||||
|
data->devno = -1;
|
||||||
|
- data->phy_block_size = td->targetblocksize;
|
||||||
|
- data->type = td->targettype;
|
||||||
|
- data->partnum = 0;
|
||||||
|
+ data->phy_block_size = get_targetblocksize(td, 0);
|
||||||
|
+ data->type = get_targettype(td, 0);
|
||||||
|
|
||||||
|
- if (sscanf(td->targetbase, "%d:%d", &majnum, &minnum) == 2) {
|
||||||
|
- data->device = makedev(majnum, minnum);
|
||||||
|
- data->targetbase = defined_as_device;
|
||||||
|
- data->partnum = minor(stats.st_rdev) - minnum;
|
||||||
|
- } else {
|
||||||
|
- if (stat(td->targetbase, &stats)) {
|
||||||
|
- error_reason(strerror(errno));
|
||||||
|
- error_text("Could not get information for "
|
||||||
|
- "file '%s'", td->targetbase);
|
||||||
|
+ assert(td->nr_targets != 0);
|
||||||
|
+ for (i = 1; i < td->nr_targets; i++) {
|
||||||
|
+ if (data->type != get_targettype(td, i) ||
|
||||||
|
+ data->phy_block_size != get_targetblocksize(td, i)) {
|
||||||
|
+ print_base_disk_params(td, 0);
|
||||||
|
+ print_base_disk_params(td, i);
|
||||||
|
+ error_reason("Inconsistent base disk geometry in target device");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
- if (!S_ISBLK(stats.st_mode)) {
|
||||||
|
- error_reason("Target base device '%s' is not "
|
||||||
|
- "a block device",
|
||||||
|
- td->targetbase);
|
||||||
|
+ }
|
||||||
|
+ data->partnum = 0;
|
||||||
|
+ data->targetbase_def = undefined;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < td->nr_targets; i++) {
|
||||||
|
+ definition_t defined_as;
|
||||||
|
+
|
||||||
|
+ if (sscanf(get_targetbase(td, i),
|
||||||
|
+ "%d:%d", &majnum, &minnum) == 2) {
|
||||||
|
+ data->basedisks[i] = makedev(majnum, minnum);
|
||||||
|
+ defined_as = defined_as_device;
|
||||||
|
+ } else {
|
||||||
|
+ if (stat(get_targetbase(td, i), &stats)) {
|
||||||
|
+ error_reason(strerror(errno));
|
||||||
|
+ error_text("Could not get information for "
|
||||||
|
+ "file '%s'", get_targetbase(td, i));
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ if (!S_ISBLK(stats.st_mode)) {
|
||||||
|
+ error_reason("Target base device '%s' is not "
|
||||||
|
+ "a block device",
|
||||||
|
+ get_targetbase(td, i));
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ data->basedisks[i] = stats.st_rdev;
|
||||||
|
+ defined_as = defined_as_name;
|
||||||
|
+ }
|
||||||
|
+ if (data->targetbase_def != undefined &&
|
||||||
|
+ data->targetbase_def != defined_as) {
|
||||||
|
+ error_reason("Target base disks are defined by different ways");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
- data->device = stats.st_rdev;
|
||||||
|
- data->targetbase = defined_as_name;
|
||||||
|
+ data->targetbase_def = defined_as;
|
||||||
|
}
|
||||||
|
if (data->type == disk_type_scsi && ioctl(fd, NVME_IOCTL_ID) >= 0)
|
||||||
|
data->is_nvme = 1;
|
||||||
|
@@ -446,11 +563,28 @@
|
||||||
|
static int disk_set_geometry_by_hint(struct job_target_data *td,
|
||||||
|
struct disk_info *data)
|
||||||
|
{
|
||||||
|
- data->geo.heads = td->targetheads;
|
||||||
|
- data->geo.sectors = td->targetsectors;
|
||||||
|
- data->geo.cylinders = td->targetcylinders;
|
||||||
|
- data->geo.start = td->targetoffset;
|
||||||
|
-
|
||||||
|
+ int i;
|
||||||
|
+ /*
|
||||||
|
+ * Currently multiple base disks with different parameters
|
||||||
|
+ * are not supported
|
||||||
|
+ */
|
||||||
|
+ data->geo.heads = get_targetheads(td, 0);
|
||||||
|
+ data->geo.sectors = get_targetsectors(td, 0);
|
||||||
|
+ data->geo.cylinders = get_targetcylinders(td, 0);
|
||||||
|
+ data->geo.start = get_targetoffset(td, 0);
|
||||||
|
+
|
||||||
|
+ assert(td->nr_targets != 0);
|
||||||
|
+ for (i = 1; i < td->nr_targets; i++) {
|
||||||
|
+ if (data->geo.heads != get_targetheads(td, i) ||
|
||||||
|
+ data->geo.sectors != get_targetsectors(td, i) ||
|
||||||
|
+ data->geo.cylinders != get_targetcylinders(td, i) ||
|
||||||
|
+ data->geo.start != get_targetoffset(td, i)) {
|
||||||
|
+ print_base_disk_params(td, 0);
|
||||||
|
+ print_base_disk_params(td, i);
|
||||||
|
+ error_reason("Inconsistent base disk geometry in target device");
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -515,14 +649,16 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- * Prepare INFO required to perform IPL installation on the physical
|
||||||
|
- * disk where the logical DEVICE is located.
|
||||||
|
+ * Prepare INFO required to perform IPL installation on physical disks
|
||||||
|
+ * participating in the logical DEVICE.
|
||||||
|
* Preparation is performed in 2 steps:
|
||||||
|
*
|
||||||
|
- * 1. Find out a physical "base" disk where the logical DEVICE is
|
||||||
|
- * located. Calculate "target" parameters (type, geometry, physical
|
||||||
|
- * block size, data offset, etc);
|
||||||
|
- * 2. Complete INFO by the found base disk and target parameters.
|
||||||
|
+ * 1. Find out a set of physical "base" disks participating in the
|
||||||
|
+ * logical DEVICE. For each found disk calculate "target" parameters
|
||||||
|
+ * (type, geometry, physical block size, data offset, etc) and store
|
||||||
|
+ * it in the array of "targets" of TD;
|
||||||
|
+ * 2. Complete INFO using the found base disks and calculated target
|
||||||
|
+ * parameters.
|
||||||
|
*
|
||||||
|
* TD: optionally contains target parameters specified by user via
|
||||||
|
* config file, or special "target options" of zipl tool.
|
||||||
|
@@ -566,6 +702,7 @@
|
||||||
|
goto error;
|
||||||
|
if (disk_set_info_by_hint(td, data, fd))
|
||||||
|
goto error;
|
||||||
|
+ data->device = stats.st_rdev;
|
||||||
|
break;
|
||||||
|
case source_user:
|
||||||
|
/*
|
||||||
|
@@ -578,6 +715,12 @@
|
||||||
|
goto error;
|
||||||
|
if (disk_set_info_by_hint(td, data, fd))
|
||||||
|
goto error;
|
||||||
|
+ /*
|
||||||
|
+ * multiple base disks are not supported
|
||||||
|
+ * with this source type
|
||||||
|
+ */
|
||||||
|
+ assert(td->nr_targets == 1);
|
||||||
|
+ data->device = data->basedisks[0];
|
||||||
|
break;
|
||||||
|
case source_auto:
|
||||||
|
/* no ready target parameters are available */
|
||||||
|
@@ -585,6 +728,12 @@
|
||||||
|
goto error;
|
||||||
|
if (disk_set_info_auto(data, &stats, fd))
|
||||||
|
goto error;
|
||||||
|
+ /*
|
||||||
|
+ * multiple base disks are not supported
|
||||||
|
+ * with this source type
|
||||||
|
+ */
|
||||||
|
+ data->basedisks[0] = data->device;
|
||||||
|
+ td->nr_targets = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
@@ -940,6 +1089,33 @@
|
||||||
|
printf("%02x:%02x", major(d), minor(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
+void disk_print_devname(dev_t dev)
|
||||||
|
+{
|
||||||
|
+ struct util_proc_part_entry part_entry;
|
||||||
|
+
|
||||||
|
+ if (!util_proc_part_get_entry(dev, &part_entry)) {
|
||||||
|
+ printf("%s", part_entry.name);
|
||||||
|
+ util_proc_part_free_entry(&part_entry);
|
||||||
|
+ } else {
|
||||||
|
+ disk_print_devt(dev);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void prepare_footnote_ptr(int source, char *ptr)
|
||||||
|
+{
|
||||||
|
+ if (source == source_user || source == source_script)
|
||||||
|
+ strcpy(ptr, " *)");
|
||||||
|
+ else
|
||||||
|
+ strcpy(ptr, "");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void print_footnote_ref(int source, const char *prefix)
|
||||||
|
+{
|
||||||
|
+ if (source == source_user)
|
||||||
|
+ printf("%s*) Data provided by user.\n", prefix);
|
||||||
|
+ else if (source == source_script)
|
||||||
|
+ printf("%s*) Data provided by script.\n", prefix);
|
||||||
|
+}
|
||||||
|
|
||||||
|
/* Return a name for a given disk TYPE. */
|
||||||
|
char *
|
||||||
|
@@ -991,12 +1167,11 @@
|
||||||
|
void disk_print_info(struct disk_info *info, int source)
|
||||||
|
{
|
||||||
|
char footnote[4] = "";
|
||||||
|
- if (source == source_user || source == source_script)
|
||||||
|
- strcpy(footnote, " *)");
|
||||||
|
|
||||||
|
+ prepare_footnote_ptr(source, footnote);
|
||||||
|
printf(" Device..........................: ");
|
||||||
|
disk_print_devt(info->device);
|
||||||
|
- if (info->targetbase == defined_as_device)
|
||||||
|
+ if (info->targetbase_def == defined_as_device)
|
||||||
|
printf("%s", footnote);
|
||||||
|
printf("\n");
|
||||||
|
if (info->partnum != 0) {
|
||||||
|
@@ -1007,7 +1182,7 @@
|
||||||
|
if (info->name) {
|
||||||
|
printf(" Device name.....................: %s",
|
||||||
|
info->name);
|
||||||
|
- if (info->targetbase == defined_as_name)
|
||||||
|
+ if (info->targetbase_def == defined_as_name)
|
||||||
|
printf("%s", footnote);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
@@ -1050,21 +1225,7 @@
|
||||||
|
info->phy_block_size, footnote);
|
||||||
|
printf(" Device size in physical blocks..: %ld\n",
|
||||||
|
(long) info->phy_blocks);
|
||||||
|
- if (source == source_user)
|
||||||
|
- printf(" *) Data provided by user.\n");
|
||||||
|
- if (source == source_script)
|
||||||
|
- printf(" *) Data provided by script.\n");
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-/* Print textual representation of geo structure. */
|
||||||
|
-void
|
||||||
|
-disk_print_geo(struct disk_info *data)
|
||||||
|
-{
|
||||||
|
- printf(" geo.heads.........:%u\n", data->geo.heads);
|
||||||
|
- printf(" geo.sectors.......:%u\n", data->geo.sectors);
|
||||||
|
- printf(" geo.cylinders.....:%u\n", data->geo.cylinders);
|
||||||
|
- printf(" geo.start.........:%lu\n", data->geo.start);
|
||||||
|
- printf(" blksize...........:%u\n", data->phy_block_size);
|
||||||
|
+ print_footnote_ref(source, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether a block is a zero block which identifies a hole in a file.
|
||||||
|
--- a/zipl/src/install.c
|
||||||
|
+++ b/zipl/src/install.c
|
||||||
|
@@ -434,11 +434,14 @@
|
||||||
|
{
|
||||||
|
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
|
||||||
|
struct disk_info *info = bis->info;
|
||||||
|
- char *device = bis->device;
|
||||||
|
- int fd, rc;
|
||||||
|
+ char footnote[4];
|
||||||
|
+ int rc;
|
||||||
|
+ int i;
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
return 0;
|
||||||
|
+
|
||||||
|
+ prepare_footnote_ptr(job->target.source, footnote);
|
||||||
|
/* Inform user about what we're up to */
|
||||||
|
printf("Preparing boot device for %s%s: ",
|
||||||
|
disk_get_ipl_type(info->type,
|
||||||
|
@@ -455,40 +458,58 @@
|
||||||
|
disk_print_devt(info->device);
|
||||||
|
printf(".\n");
|
||||||
|
}
|
||||||
|
- /* Open device file */
|
||||||
|
- fd = open(device, O_RDWR);
|
||||||
|
- if (fd == -1) {
|
||||||
|
- error_reason(strerror(errno));
|
||||||
|
- error_text("Could not open temporary device file '%s'",
|
||||||
|
- device);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
- /* Ensure that potential cache inconsistencies between disk and
|
||||||
|
- * partition are resolved by flushing the corresponding buffers. */
|
||||||
|
- if (!dry_run) {
|
||||||
|
- if (ioctl(fd, BLKFLSBUF)) {
|
||||||
|
- fprintf(stderr, "Warning: Could not flush disk "
|
||||||
|
- "caches.\n");
|
||||||
|
+ /* Install independently on each physical target base */
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < job_get_nr_targets(job); i++) {
|
||||||
|
+ int fd;
|
||||||
|
+
|
||||||
|
+ if (verbose) {
|
||||||
|
+ printf("Installing on base disk: ");
|
||||||
|
+ disk_print_devname(info->basedisks[i]);
|
||||||
|
+ printf("%s.\n", footnote);
|
||||||
|
}
|
||||||
|
+ /* Open device file */
|
||||||
|
+ fd = open(bis->basetmp[i], O_RDWR);
|
||||||
|
+ if (fd == -1) {
|
||||||
|
+ error_reason(strerror(errno));
|
||||||
|
+ error_text("Could not open temporary device file '%s'",
|
||||||
|
+ bis->basetmp[i]);
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ /* Ensure that potential cache inconsistencies between disk and
|
||||||
|
+ * partition are resolved by flushing the corresponding buffers.
|
||||||
|
+ */
|
||||||
|
+ if (!dry_run) {
|
||||||
|
+ if (ioctl(fd, BLKFLSBUF)) {
|
||||||
|
+ fprintf(stderr, "Warning: Could not flush disk "
|
||||||
|
+ "caches.\n");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ /*
|
||||||
|
+ * Depending on disk type, install one or two program tables
|
||||||
|
+ * for CCW-type IPL and (or) for List-Directed IPL (see the
|
||||||
|
+ * picture in comments above)
|
||||||
|
+ */
|
||||||
|
+ if (job->id == job_dump_partition) {
|
||||||
|
+ rc = install_bootloader_dump(bis->tables, info,
|
||||||
|
+ scsi_dump_sb_blockptr,
|
||||||
|
+ is_ngdump_enabled(job),
|
||||||
|
+ fd);
|
||||||
|
+ } else {
|
||||||
|
+ rc = install_bootloader_ipl(bis->tables, info,
|
||||||
|
+ fd);
|
||||||
|
+ }
|
||||||
|
+ if (fsync(fd))
|
||||||
|
+ error_text("Could not sync device file '%s'",
|
||||||
|
+ bis->basetmp[i]);
|
||||||
|
+ if (close(fd))
|
||||||
|
+ error_text("Could not close device file '%s'",
|
||||||
|
+ bis->basetmp[i]);
|
||||||
|
+ if (rc)
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
- /*
|
||||||
|
- * Depending on disk type, install one or two program tables
|
||||||
|
- * for CCW-type IPL and (or) for List-Directed IPL (see the
|
||||||
|
- * picture in comments above)
|
||||||
|
- */
|
||||||
|
- if (job->id == job_dump_partition) {
|
||||||
|
- rc = install_bootloader_dump(bis->tables, info,
|
||||||
|
- scsi_dump_sb_blockptr,
|
||||||
|
- is_ngdump_enabled(job),
|
||||||
|
- fd);
|
||||||
|
- } else {
|
||||||
|
- rc = install_bootloader_ipl(bis->tables, info, fd);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (fsync(fd))
|
||||||
|
- error_text("Could not sync device file '%s'", device);
|
||||||
|
- if (close(fd))
|
||||||
|
- error_text("Could not close device file '%s'", device);
|
||||||
|
+ if (verbose)
|
||||||
|
+ print_footnote_ref(job->target.source, "");
|
||||||
|
|
||||||
|
if (!dry_run && rc == 0) {
|
||||||
|
if (info->devno >= 0)
|
||||||
|
--- a/zipl/src/job.c
|
||||||
|
+++ b/zipl/src/job.c
|
||||||
|
@@ -1346,6 +1346,27 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+int set_targettype(struct job_target_data *data, int index, char *value)
|
||||||
|
+{
|
||||||
|
+ return type_from_target(value,
|
||||||
|
+ &target_at(data, index)->targettype);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int job_set_targettype(struct job_data *job, char *value)
|
||||||
|
+{
|
||||||
|
+ return set_targettype(&job->target, 0, value);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int job_set_target(struct job_data *job, char *value)
|
||||||
|
+{
|
||||||
|
+ job_set_targetbase(job, value);
|
||||||
|
+ if (!job_get_targetbase(job))
|
||||||
|
+ return -1;
|
||||||
|
+ job_set_nr_targets(job, 1);
|
||||||
|
+ job->target.source = source_user;
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int
|
||||||
|
get_job_from_section_data(char* data[], struct job_data* job, char* section)
|
||||||
|
{
|
||||||
|
@@ -1362,32 +1383,28 @@
|
||||||
|
return -1;
|
||||||
|
/* Fill in target */
|
||||||
|
if (data[(int) scan_keyword_targetbase] != NULL) {
|
||||||
|
- job->target.targetbase =
|
||||||
|
- misc_strdup(data[(int)
|
||||||
|
- scan_keyword_targetbase]);
|
||||||
|
- if (job->target.targetbase == NULL)
|
||||||
|
+ if (job_set_target(job, misc_strdup(data[(int)
|
||||||
|
+ scan_keyword_targetbase])))
|
||||||
|
return -1;
|
||||||
|
- job->target.source = source_user;
|
||||||
|
}
|
||||||
|
if (data[(int) scan_keyword_targettype] != NULL) {
|
||||||
|
- if (type_from_target(
|
||||||
|
- data[(int) scan_keyword_targettype],
|
||||||
|
- &job->target.targettype))
|
||||||
|
+ if (job_set_targettype(job,
|
||||||
|
+ data[(int) scan_keyword_targettype]))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (data[(int) scan_keyword_targetgeometry] != NULL) {
|
||||||
|
- job->target.targetcylinders =
|
||||||
|
+ job_set_targetcylinders(job,
|
||||||
|
atoi(strtok(data[(int)
|
||||||
|
- scan_keyword_targetgeometry], ","));
|
||||||
|
- job->target.targetheads = atoi(strtok(NULL, ","));
|
||||||
|
- job->target.targetsectors = atoi(strtok(NULL, ","));
|
||||||
|
+ scan_keyword_targetgeometry], ",")));
|
||||||
|
+ job_set_targetheads(job, atoi(strtok(NULL, ",")));
|
||||||
|
+ job_set_targetsectors(job, atoi(strtok(NULL, ",")));
|
||||||
|
}
|
||||||
|
if (data[(int) scan_keyword_targetblocksize] != NULL)
|
||||||
|
- job->target.targetblocksize =
|
||||||
|
- atoi(data[(int) scan_keyword_targetblocksize]);
|
||||||
|
+ job_set_targetblocksize(job,
|
||||||
|
+ atoi(data[(int) scan_keyword_targetblocksize]));
|
||||||
|
if (data[(int) scan_keyword_targetoffset] != NULL)
|
||||||
|
- job->target.targetoffset =
|
||||||
|
- atol(data[(int) scan_keyword_targetoffset]);
|
||||||
|
+ job_set_targetoffset(job,
|
||||||
|
+ atol(data[(int) scan_keyword_targetoffset]));
|
||||||
|
/* Fill in name and address of image file */
|
||||||
|
|
||||||
|
job->data.ipl.common.image = misc_strdup(
|
||||||
|
@@ -1615,37 +1632,32 @@
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case scan_keyword_targetbase:
|
||||||
|
- job->target.targetbase = misc_strdup(
|
||||||
|
- scan[i].content.keyword.value);
|
||||||
|
- if (job->target.targetbase == NULL)
|
||||||
|
+ if (job_set_target(job, misc_strdup(
|
||||||
|
+ scan[i].content.keyword.value)))
|
||||||
|
return -1;
|
||||||
|
- job->target.source = source_user;
|
||||||
|
break;
|
||||||
|
case scan_keyword_targettype:
|
||||||
|
- if (type_from_target(
|
||||||
|
- scan[i].content.keyword.value,
|
||||||
|
- &job->target.targettype))
|
||||||
|
+ if (job_set_targettype(job,
|
||||||
|
+ scan[i].content.keyword.value))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case scan_keyword_targetgeometry:
|
||||||
|
- job->target.targetcylinders =
|
||||||
|
+ job_set_targetcylinders(job,
|
||||||
|
atoi(strtok(
|
||||||
|
scan[i].content.keyword.value,
|
||||||
|
- ","));
|
||||||
|
- job->target.targetheads =
|
||||||
|
- atoi(strtok(NULL, ","));
|
||||||
|
- job->target.targetsectors =
|
||||||
|
- atoi(strtok(NULL, ","));
|
||||||
|
+ ",")));
|
||||||
|
+ job_set_targetheads(job,
|
||||||
|
+ atoi(strtok(NULL, ",")));
|
||||||
|
+ job_set_targetsectors(job,
|
||||||
|
+ atoi(strtok(NULL, ",")));
|
||||||
|
break;
|
||||||
|
case scan_keyword_targetblocksize:
|
||||||
|
- job->target.targetblocksize =
|
||||||
|
- atoi(
|
||||||
|
- scan[i].content.keyword.value);
|
||||||
|
+ job_set_targetblocksize(job, atoi(
|
||||||
|
+ scan[i].content.keyword.value));
|
||||||
|
break;
|
||||||
|
case scan_keyword_targetoffset:
|
||||||
|
- job->target.targetoffset =
|
||||||
|
- atol(
|
||||||
|
- scan[i].content.keyword.value);
|
||||||
|
+ job_set_targetoffset(job, atol(
|
||||||
|
+ scan[i].content.keyword.value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Should not happen */
|
@ -0,0 +1,67 @@
|
|||||||
|
From 2d26a63806d2847f549c06276070a636a61bcb80 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
Date: Wed, 4 Dec 2024 13:37:46 +0100
|
||||||
|
Subject: [PATCH s390-tools] zipl_helper.device-mapper: add missed step in
|
||||||
|
logical device resolution
|
||||||
|
|
||||||
|
This fixes 670bf3e
|
||||||
|
|
||||||
|
Preparing a loop device for IPL by zipl tool, using its partition as
|
||||||
|
zipl target, leads to inconsistent installation setup. The problem is in
|
||||||
|
a missed step in the procedure of logical device resolution performed
|
||||||
|
by the script zipl_helper.device-mapper:
|
||||||
|
|
||||||
|
\# lsblk
|
||||||
|
|
||||||
|
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
|
||||||
|
loop0 7:0 0 5G 0 loop
|
||||||
|
|-loop0p1 253:15 0 128M 0 part
|
||||||
|
`-loop0p2 253:16 0 4.9G 0 part /mnt
|
||||||
|
|
||||||
|
\# ./zipl_helper.device-mapper 253:16
|
||||||
|
|
||||||
|
Expected result:
|
||||||
|
|
||||||
|
targetbase=7:0
|
||||||
|
targettype=SCSI
|
||||||
|
targetblocksize=4096
|
||||||
|
targetoffset=32784
|
||||||
|
|
||||||
|
Actual result:
|
||||||
|
|
||||||
|
targetbase=253:16
|
||||||
|
targettype=SCSI
|
||||||
|
targetblocksize=4096
|
||||||
|
targetoffset=32784
|
||||||
|
|
||||||
|
The fixup adds a missed resolution step.
|
||||||
|
|
||||||
|
Reference-ID: LTC210771
|
||||||
|
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zipl/src/zipl_helper.device-mapper.c | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zipl/src/zipl_helper.device-mapper.c b/zipl/src/zipl_helper.device-mapper.c
|
||||||
|
index aca52be1..918c5aba 100644
|
||||||
|
--- a/zipl/src/zipl_helper.device-mapper.c
|
||||||
|
+++ b/zipl/src/zipl_helper.device-mapper.c
|
||||||
|
@@ -1306,13 +1306,13 @@ static int complete_physical_device(struct physical_device *pd, dev_t *base_dev)
|
||||||
|
*base_dev = base_entry->dev.dev;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
- * In this case base device is the uppermost logical
|
||||||
|
+ * In this case base device is the uppermost
|
||||||
|
* device which provides access to boot sectors
|
||||||
|
*/
|
||||||
|
base_entry = find_base_entry(pd->dmpath, dc->bootsectors);
|
||||||
|
if (!base_entry)
|
||||||
|
return -1;
|
||||||
|
- *base_dev = base_entry->dev.dev;
|
||||||
|
+ *base_dev = first_device_by_target_data(base_entry->target);
|
||||||
|
}
|
||||||
|
/* Check for valid offset of filesystem */
|
||||||
|
if ((pd->offset % (dc->blocksize / SECTOR_SIZE)) != 0) {
|
||||||
|
--
|
||||||
|
2.39.0
|
||||||
|
|
@ -0,0 +1,299 @@
|
|||||||
|
From 8c4b2872b8e24c1a27d8201beb5979c66ac05268 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Thu, 15 Feb 2024 09:08:43 +0100
|
||||||
|
Subject: [PATCH] zkey: Add support for retrieving a list of ultravisor secrets
|
||||||
|
|
||||||
|
Add functions to interface with the ultravisor device (/dev/uv) when
|
||||||
|
running in a secure execution guest to retrieve a list of available
|
||||||
|
secrets.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/Makefile | 4 +-
|
||||||
|
zkey/pvsecrets.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
zkey/pvsecrets.h | 89 ++++++++++++++++++++++++++
|
||||||
|
3 files changed, 253 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 zkey/pvsecrets.c
|
||||||
|
create mode 100644 zkey/pvsecrets.h
|
||||||
|
|
||||||
|
diff --git a/zkey/Makefile b/zkey/Makefile
|
||||||
|
index 501c5ebf..cbecf125 100644
|
||||||
|
--- a/zkey/Makefile
|
||||||
|
+++ b/zkey/Makefile
|
||||||
|
@@ -92,9 +92,11 @@ keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h
|
||||||
|
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
|
||||||
|
ep11.h misc.h utils.h
|
||||||
|
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
|
||||||
|
+pvsecrets.o: pvsecrets.h
|
||||||
|
|
||||||
|
zkey: LDLIBS = -ldl -lcrypto
|
||||||
|
-zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o $(libs)
|
||||||
|
+zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o \
|
||||||
|
+ pvsecrets.o $(libs)
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto
|
||||||
|
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..2874fdf1
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zkey/pvsecrets.c
|
||||||
|
@@ -0,0 +1,161 @@
|
||||||
|
+/*
|
||||||
|
+ * zkey - Generate, re-encipher, and validate secure keys
|
||||||
|
+ *
|
||||||
|
+ * Copyright IBM Corp. 2024
|
||||||
|
+ *
|
||||||
|
+ * s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <err.h>
|
||||||
|
+#include <errno.h>
|
||||||
|
+#include <fcntl.h>
|
||||||
|
+#include <stdbool.h>
|
||||||
|
+#include <string.h>
|
||||||
|
+#include <stdint.h>
|
||||||
|
+#include <sys/ioctl.h>
|
||||||
|
+#include <sys/types.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+
|
||||||
|
+#include "lib/util_base.h"
|
||||||
|
+#include "lib/util_file.h"
|
||||||
|
+#include "lib/util_libc.h"
|
||||||
|
+#include "lib/util_panic.h"
|
||||||
|
+#include "lib/util_path.h"
|
||||||
|
+
|
||||||
|
+#include "pvsecrets.h"
|
||||||
|
+
|
||||||
|
+#define pr_verbose(verbose, fmt...) do { \
|
||||||
|
+ if (verbose) \
|
||||||
|
+ warnx(fmt); \
|
||||||
|
+ } while (0)
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Opens the ultravisor device and returns its file descriptor.
|
||||||
|
+ * This only succeeds when running in a secure execution guest.
|
||||||
|
+ * A failure of this function indicates that it is not running in a secure
|
||||||
|
+ * execution guest.
|
||||||
|
+ *
|
||||||
|
+ * @param verbose if true, verbose messages are printed
|
||||||
|
+ *
|
||||||
|
+ * @returns the file descriptor or -1 to indicate an error
|
||||||
|
+ */
|
||||||
|
+int uv_open_device(bool verbose)
|
||||||
|
+{
|
||||||
|
+ unsigned int pvguest = 0, max_retr_secrets = 0;
|
||||||
|
+ char *path = NULL;
|
||||||
|
+ int uv_fd, err;
|
||||||
|
+
|
||||||
|
+ uv_fd = open(UVDEVICE, O_RDWR);
|
||||||
|
+ if (uv_fd < 0) {
|
||||||
|
+ err = errno;
|
||||||
|
+ warnx("File '%s:' %s\n", UVDEVICE, strerror(errno));
|
||||||
|
+ if (err == EACCES)
|
||||||
|
+ warnx("Only the 'root' user is allowed to perform "
|
||||||
|
+ "this command");
|
||||||
|
+ else
|
||||||
|
+ warnx("Ensure that you are running in a secure "
|
||||||
|
+ "execution guest, and that the 'uvdevice' "
|
||||||
|
+ "kernel module is loaded.");
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ path = util_path_sysfs(SYSFS_UV);
|
||||||
|
+ if (util_file_read_ui(&pvguest, 10, SYSFS_UV_PV_GUEST, path) != 0 ||
|
||||||
|
+ pvguest != 1) {
|
||||||
|
+ warnx("You are not running in a secure execution guest.");
|
||||||
|
+ goto error;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (util_file_read_ui(&max_retr_secrets, 10, SYSFS_UV_MAX_SECRETS,
|
||||||
|
+ path) != 0 ||
|
||||||
|
+ max_retr_secrets == 0) {
|
||||||
|
+ warnx("The ultravisor device is at a too old version, or "
|
||||||
|
+ "the ultravisor does not support retrievable secrets.");
|
||||||
|
+ goto error;
|
||||||
|
+ }
|
||||||
|
+ free(path);
|
||||||
|
+
|
||||||
|
+ pr_verbose(verbose, "Device '%s' has been opened successfully",
|
||||||
|
+ UVDEVICE);
|
||||||
|
+ return uv_fd;
|
||||||
|
+
|
||||||
|
+error:
|
||||||
|
+ free(path);
|
||||||
|
+ close(uv_fd);
|
||||||
|
+
|
||||||
|
+ return -1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Retrieves a list of secrets from the ultravisor. Calls the supplied callback
|
||||||
|
+ * function for each secret found.
|
||||||
|
+ *
|
||||||
|
+ * @param uv_fd the file descriptor of the ultravisor device
|
||||||
|
+ * @param cb the callback function
|
||||||
|
+ * @param cb_private private data to pass to the callback function
|
||||||
|
+ * @param verbose if true, verbose messages are printed
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 on success, a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
|
||||||
|
+ const u8 id[UV_SECRET_ID_LEN],
|
||||||
|
+ void *cb_private),
|
||||||
|
+ void *cb_private, bool verbose)
|
||||||
|
+{
|
||||||
|
+ struct uvio_list_secrets *list;
|
||||||
|
+ struct uvio_ioctl_cb io;
|
||||||
|
+ unsigned int i;
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
|
||||||
|
+ util_assert(cb != NULL, "Internal error: cb is NULL");
|
||||||
|
+
|
||||||
|
+ list = util_zalloc(UVIO_LIST_SECRETS_MAX_LEN);
|
||||||
|
+
|
||||||
|
+ memset(&io, 0, sizeof(io));
|
||||||
|
+ io.argument_addr = list;
|
||||||
|
+ io.argument_len = UVIO_LIST_SECRETS_MAX_LEN;
|
||||||
|
+
|
||||||
|
+ rc = ioctl(uv_fd, UVIO_IOCTL_LIST_SECRETS, &io);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ rc = -errno;
|
||||||
|
+
|
||||||
|
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS: %s",
|
||||||
|
+ strerror(-rc));
|
||||||
|
+
|
||||||
|
+ if (rc == -ENOTTY || rc == -EINVAL)
|
||||||
|
+ warnx("The ultravisor device is at a too old version");
|
||||||
|
+
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (io.uv_rc != UVIO_RC_SUCCESS) {
|
||||||
|
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS' uv_rc: %u",
|
||||||
|
+ io.uv_rc);
|
||||||
|
+ rc = -EIO;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pr_verbose(verbose, "Number of secrets: %u", list->num_secrets_stored);
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < list->num_secrets_stored &&
|
||||||
|
+ i < ARRAY_SIZE(list->secret_entries); i++) {
|
||||||
|
+ if (list->secret_entries[i].secret_type <=
|
||||||
|
+ UV_SECRET_TYPE_AP_ASSOCIATION)
|
||||||
|
+ continue;
|
||||||
|
+
|
||||||
|
+ rc = cb(list->secret_entries[i].secret_idx,
|
||||||
|
+ list->secret_entries[i].secret_type,
|
||||||
|
+ list->secret_entries[i].secret_len,
|
||||||
|
+ list->secret_entries[i].secret_id,
|
||||||
|
+ cb_private);
|
||||||
|
+ if (rc != 0)
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+out:
|
||||||
|
+ free(list);
|
||||||
|
+
|
||||||
|
+ return rc;
|
||||||
|
+}
|
||||||
|
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..2667e859
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zkey/pvsecrets.h
|
||||||
|
@@ -0,0 +1,89 @@
|
||||||
|
+/*
|
||||||
|
+ * zkey - Generate, re-encipher, and validate secure keys
|
||||||
|
+ *
|
||||||
|
+ * This header file defines functions for the PV secrets support as well
|
||||||
|
+ * as the interface to the uv kernel module.
|
||||||
|
+ *
|
||||||
|
+ * Copyright IBM Corp. 2024
|
||||||
|
+ *
|
||||||
|
+ * s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef PVSECRETS_H
|
||||||
|
+#define PVSECRETS_H
|
||||||
|
+
|
||||||
|
+#include "lib/zt_common.h"
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Definitions for the /dev/uv kernel module interface
|
||||||
|
+ */
|
||||||
|
+#define UVDEVICE "/dev/uv"
|
||||||
|
+#define SYSFS_UV "firmware/uv"
|
||||||
|
+#define SYSFS_UV_PV_GUEST "%s/prot_virt_guest"
|
||||||
|
+#define SYSFS_UV_MAX_SECRETS "%s/query/max_retr_secrets"
|
||||||
|
+
|
||||||
|
+struct uvio_ioctl_cb {
|
||||||
|
+ u32 flags;
|
||||||
|
+ u16 uv_rc; /* UV header rc value */
|
||||||
|
+ u16 uv_rrc; /* UV header rrc value */
|
||||||
|
+ void *argument_addr; /* Userspace address of uvio argument */
|
||||||
|
+ u32 argument_len;
|
||||||
|
+ u8 reserved14[0x40 - 0x14]; /* must be zero */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#define UVIO_IOCTL_LIST_SECRETS_NR 3
|
||||||
|
+
|
||||||
|
+#define UVIO_TYPE_UVC 'u'
|
||||||
|
+#define UVIO_IOCTL(nr) _IOWR(UVIO_TYPE_UVC, \
|
||||||
|
+ nr, struct uvio_ioctl_cb)
|
||||||
|
+#define UVIO_IOCTL_LIST_SECRETS UVIO_IOCTL( \
|
||||||
|
+ UVIO_IOCTL_LIST_SECRETS_NR)
|
||||||
|
+
|
||||||
|
+#define UVIO_RC_SUCCESS 0x0001
|
||||||
|
+#define UVIO_RC_MORE_DATA 0x0100
|
||||||
|
+
|
||||||
|
+#define UV_SECRET_TYPE_INVALID 0x00
|
||||||
|
+#define UV_SECRET_TYPE_NULL 0x01
|
||||||
|
+#define UV_SECRET_TYPE_AP_ASSOCIATION 0x02
|
||||||
|
+#define UV_SECRET_TYPE_PLAIN_TEXT 0x03
|
||||||
|
+#define UV_SECRET_TYPE_AES_128 0x04
|
||||||
|
+#define UV_SECRET_TYPE_AES_192 0x05
|
||||||
|
+#define UV_SECRET_TYPE_AES_256 0x06
|
||||||
|
+#define UV_SECRET_TYPE_AES_XTS_128 0x07
|
||||||
|
+#define UV_SECRET_TYPE_AES_XTS_256 0x08
|
||||||
|
+#define UV_SECRET_TYPE_HMAC_SHA_256 0x09
|
||||||
|
+#define UV_SECRET_TYPE_HMAC_SHA_512 0x0a
|
||||||
|
+#define UV_SECRET_TYPE_ECDSA_P256 0x11
|
||||||
|
+#define UV_SECRET_TYPE_ECDSA_P384 0x12
|
||||||
|
+#define UV_SECRET_TYPE_ECDSA_P521 0x13
|
||||||
|
+#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
|
||||||
|
+#define UV_SECRET_TYPE_EDDSA_ED448 0x15
|
||||||
|
+
|
||||||
|
+#define UV_SECRET_ID_LEN 32
|
||||||
|
+
|
||||||
|
+#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
|
||||||
|
+
|
||||||
|
+struct uvio_list_secret_entry {
|
||||||
|
+ u16 secret_idx;
|
||||||
|
+ u16 secret_type;
|
||||||
|
+ u32 secret_len;
|
||||||
|
+ u64 reserved;
|
||||||
|
+ u8 secret_id[UV_SECRET_ID_LEN];
|
||||||
|
+} __packed;
|
||||||
|
+
|
||||||
|
+#define UVIO_MAX_SECRET_ENTRIES ((UVIO_LIST_SECRETS_MAX_LEN - 16) / \
|
||||||
|
+ sizeof(struct uvio_list_secret_entry))
|
||||||
|
+
|
||||||
|
+struct uvio_list_secrets {
|
||||||
|
+ u16 num_secrets_stored;
|
||||||
|
+ u16 num_secrets_total;
|
||||||
|
+ u16 next_secret_idx;
|
||||||
|
+ u16 reserved1;
|
||||||
|
+ u64 reserved2;
|
||||||
|
+ struct uvio_list_secret_entry secret_entries[UVIO_MAX_SECRET_ENTRIES];
|
||||||
|
+} __packed;
|
||||||
|
+
|
||||||
|
+int uv_open_device(bool verbose);
|
||||||
|
+
|
||||||
|
+#endif
|
@ -0,0 +1,129 @@
|
|||||||
|
From cf5560a100b5552e2eeeaac9c60a88ae77233530 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Date: Mon, 9 Dec 2024 15:08:03 +0100
|
||||||
|
Subject: [PATCH] libzpci: opticsmon: Refactor on_link_change() using new
|
||||||
|
zpci_find_by_netdev()
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Finding a PCI device given the name of a netdev seems generally useful
|
||||||
|
so pull this out into a new zpci_find_by_netdev() function in libzpci
|
||||||
|
and use this to simplify on_link_change() removing the need for
|
||||||
|
backwards goto.
|
||||||
|
|
||||||
|
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||||
|
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
include/lib/pci_list.h | 3 +++
|
||||||
|
libzpci/pci_list.c | 31 +++++++++++++++++++++++++++++++
|
||||||
|
opticsmon/opticsmon.c | 27 +++++++++++----------------
|
||||||
|
3 files changed, 45 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/include/lib/pci_list.h b/include/lib/pci_list.h
|
||||||
|
index 829ec244..5b2918bc 100644
|
||||||
|
--- a/include/lib/pci_list.h
|
||||||
|
+++ b/include/lib/pci_list.h
|
||||||
|
@@ -93,4 +93,7 @@ const char *zpci_pft_str(struct zpci_dev *zdev);
|
||||||
|
const char *zpci_operstate_str(operstate_t state);
|
||||||
|
operstate_t zpci_operstate_from_str(const char *oper_str);
|
||||||
|
|
||||||
|
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||||
|
+ struct zpci_netdev **netdev);
|
||||||
|
+
|
||||||
|
#endif /* LIB_ZPCI_PCI_LIST_H */
|
||||||
|
diff --git a/libzpci/pci_list.c b/libzpci/pci_list.c
|
||||||
|
index 10f64e89..e0d56e44 100644
|
||||||
|
--- a/libzpci/pci_list.c
|
||||||
|
+++ b/libzpci/pci_list.c
|
||||||
|
@@ -356,3 +356,34 @@ void zpci_free_dev_list(struct util_list *zpci_list)
|
||||||
|
}
|
||||||
|
util_list_free(zpci_list);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Find a PCI device given the name of a netdev
|
||||||
|
+ *
|
||||||
|
+ * This function allows finding a PCI device when only the name of one
|
||||||
|
+ * of its netdevs is known.
|
||||||
|
+ *
|
||||||
|
+ * @param[in] zpci_list The device list to search
|
||||||
|
+ * @param[in] netdev_name The name of the netdev
|
||||||
|
+ * @param[out] netdev Pointer to store the netdev or NULL if
|
||||||
|
+ * only the PCI device is needed
|
||||||
|
+ *
|
||||||
|
+ * @return The PCI device if one is found NULL otherwise
|
||||||
|
+ */
|
||||||
|
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||||
|
+ struct zpci_netdev **netdev)
|
||||||
|
+{
|
||||||
|
+ struct zpci_dev *zdev = NULL;
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ util_list_iterate(zpci_list, zdev) {
|
||||||
|
+ for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
+ if (!strcmp(zdev->netdevs[i].name, netdev_name)) {
|
||||||
|
+ if (netdev)
|
||||||
|
+ *netdev = &zdev->netdevs[i];
|
||||||
|
+ return zdev;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||||
|
index 50dd8d7f..7ecaa125 100644
|
||||||
|
--- a/opticsmon/opticsmon.c
|
||||||
|
+++ b/opticsmon/opticsmon.c
|
||||||
|
@@ -274,38 +274,33 @@ static int oneshot_mode(struct opticsmon_ctx *ctx)
|
||||||
|
void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
{
|
||||||
|
struct opticsmon_ctx *ctx = arg;
|
||||||
|
- struct zpci_dev *zdev;
|
||||||
|
- int i, reloads = 1;
|
||||||
|
-
|
||||||
|
- if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||||
|
- zpci_list_reload(&ctx->zpci_list);
|
||||||
|
+ struct zpci_netdev *found_netdev;
|
||||||
|
+ struct zpci_dev *zdev = NULL;
|
||||||
|
+ int reloads = 1;
|
||||||
|
|
||||||
|
-find:
|
||||||
|
- util_list_iterate(ctx->zpci_list, zdev) {
|
||||||
|
- for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
- if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||||
|
+ do {
|
||||||
|
+ if (ctx->zpci_list) {
|
||||||
|
+ zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
|
||||||
|
+ if (zdev) {
|
||||||
|
/* Skip data collection if operational state is
|
||||||
|
* unchanged
|
||||||
|
*/
|
||||||
|
- if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||||
|
+ if (found_netdev->operstate == netdev->operstate)
|
||||||
|
return;
|
||||||
|
/* Update operation state for VFs even though
|
||||||
|
* they are skipped just for a consistent view
|
||||||
|
*/
|
||||||
|
- zdev->netdevs[i].operstate = netdev->operstate;
|
||||||
|
+ found_netdev->operstate = netdev->operstate;
|
||||||
|
/* Only collect optics data for PFs */
|
||||||
|
if (!zpci_is_vf(zdev))
|
||||||
|
dump_adapter_data(ctx, zdev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- }
|
||||||
|
- /* Might be a new device, reload list of devices and retry */
|
||||||
|
- if (reloads > 0) {
|
||||||
|
+ /* Could be uninitalized list or a new device, retry after reload */
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
reloads--;
|
||||||
|
- goto find;
|
||||||
|
- }
|
||||||
|
+ } while (reloads > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_EVENTS 8
|
@ -0,0 +1,15 @@
|
|||||||
|
--- a/zipl/src/job.c 2024-09-16 14:20:09.321762661 +0200
|
||||||
|
+++ b/zipl/src/job.c 2024-09-16 14:29:28.601846724 +0200
|
||||||
|
@@ -373,8 +373,11 @@
|
||||||
|
static void
|
||||||
|
free_target_data(struct job_target_data* data)
|
||||||
|
{
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
free(data->bootmap_dir);
|
||||||
|
- free(data->targetbase);
|
||||||
|
+ for (i = 0; i < data->nr_targets; i++)
|
||||||
|
+ free(get_targetbase(data, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
@ -0,0 +1,63 @@
|
|||||||
|
From 592a016a1095fa9813f0bae8256433ba5af4ab9b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
Date: Sat, 7 Dec 2024 12:48:12 +0100
|
||||||
|
Subject: [PATCH s390-tools 2/2] zipl/src: fix imprecise check that file is on
|
||||||
|
specified device
|
||||||
|
|
||||||
|
This fixes c0f02d2
|
||||||
|
|
||||||
|
The check that file is on specified disk is imprecise: In case when
|
||||||
|
target parameters are specified by user, the check compares a logical
|
||||||
|
device with a base disk, which is incorrect.
|
||||||
|
|
||||||
|
The fixup makes the check compare base disks (a specified one with
|
||||||
|
the base disk determined by disk_get_info() procedure).
|
||||||
|
|
||||||
|
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zipl/src/bootmap.c | 9 +++++----
|
||||||
|
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
|
||||||
|
index 7d340156..880b93ce 100644
|
||||||
|
--- a/zipl/src/bootmap.c
|
||||||
|
+++ b/zipl/src/bootmap.c
|
||||||
|
@@ -299,14 +299,15 @@ create_component_header(void* buffer, component_header_type type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
- * Not precise check that the file FILENAME locates on specified physical DISK.
|
||||||
|
+ * Not precise check that the file FILENAME locates on the physical
|
||||||
|
+ * disk specified by WHERE.
|
||||||
|
*
|
||||||
|
* Try to auto-detect parameters of the disk which the file locates on
|
||||||
|
* and compare found device-ID with DISK.
|
||||||
|
* Return 0, if auto-detection succeeded, and it is proven that the
|
||||||
|
* file does NOT locate on DISK. Otherwise, return 1.
|
||||||
|
*/
|
||||||
|
-static int file_is_on_disk(const char *filename, dev_t disk)
|
||||||
|
+static int file_is_on_disk(const char *filename, struct disk_info *where)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Retrieve info of the underlying disk without any user hints
|
||||||
|
@@ -331,7 +332,7 @@ static int file_is_on_disk(const char *filename, dev_t disk)
|
||||||
|
"Warning: Preparing a logical device for boot might fail\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
- if (info->device != disk) {
|
||||||
|
+ if (info->basedisks[0] != where->basedisks[0]) {
|
||||||
|
disk_free_info(info);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -378,7 +379,7 @@ static int add_component_file_range(struct install_set *bis,
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
- if (!file_is_on_disk(filename, bis->info->device)) {
|
||||||
|
+ if (!file_is_on_disk(filename, bis->info)) {
|
||||||
|
error_reason("File is not on target device");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.39.0
|
||||||
|
|
823
s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
Normal file
823
s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
From 5ce79ea667ea946e6591fe898db13becad018667 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Thu, 15 Feb 2024 11:22:04 +0100
|
||||||
|
Subject: [PATCH] zkey: Add the 'pvsecrets list' command
|
||||||
|
|
||||||
|
The 'pvsecrets list' command lists the available protected virtualization
|
||||||
|
secrets. By default, only those pvsecret types are listed, that can be used
|
||||||
|
with zkey. If option '--all/-a' is specified, then all pvsecret types are
|
||||||
|
listed. Nevertheless, pvsecret types not supported by zkey can not be used
|
||||||
|
with zkey.
|
||||||
|
|
||||||
|
This command only works when running in a secure execution guest.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/pvsecrets.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
zkey/pvsecrets.h | 4 +
|
||||||
|
zkey/zkey.1 | 91 ++++++++++-
|
||||||
|
zkey/zkey.c | 135 +++++++++++++++-
|
||||||
|
4 files changed, 619 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
|
||||||
|
index 2874fdf1..7f28feba 100644
|
||||||
|
--- a/zkey/pvsecrets.c
|
||||||
|
+++ b/zkey/pvsecrets.c
|
||||||
|
@@ -7,6 +7,7 @@
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
@@ -17,14 +18,60 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
+#include <openssl/sha.h>
|
||||||
|
+
|
||||||
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_panic.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
+#include "lib/util_rec.h"
|
||||||
|
|
||||||
|
#include "pvsecrets.h"
|
||||||
|
|
||||||
|
+struct pvsecret_type_info {
|
||||||
|
+ u16 type;
|
||||||
|
+ const char *name;
|
||||||
|
+ bool zkey_usage;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct pvsecret_type_info pvsecret_type_info[] = {
|
||||||
|
+ { .type = UV_SECRET_TYPE_NULL, .name = "NULL",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AP_ASSOCIATION, .name = "AP-ASSOCIATION",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_PLAIN_TEXT, .name = "PLAIN-TEXT",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AES_128, .name = "AES-128",
|
||||||
|
+ .zkey_usage = true },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AES_192, .name = "AES-192",
|
||||||
|
+ .zkey_usage = true },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AES_256, .name = "AES-256",
|
||||||
|
+ .zkey_usage = true },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AES_XTS_128, .name = "AES-XTS-128",
|
||||||
|
+ .zkey_usage = true },
|
||||||
|
+ { .type = UV_SECRET_TYPE_AES_XTS_256, .name = "AES-XTS-256",
|
||||||
|
+ .zkey_usage = true },
|
||||||
|
+ { .type = UV_SECRET_TYPE_HMAC_SHA_256, .name = "HMAC-SHA-256",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_HMAC_SHA_512, .name = "HMAC-SHA-512",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_ECDSA_P256, .name = "ECDSA-P256",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_ECDSA_P384, .name = "ECDSA-P384",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_ECDSA_P521, .name = "ECDSA-P521",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_EDDSA_ED25519, .name = "EDDSA-ED25519",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_EDDSA_ED448, .name = "EDDSA-ED448",
|
||||||
|
+ .zkey_usage = false },
|
||||||
|
+ { .type = UV_SECRET_TYPE_INVALID, }
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#define PVSECRETS_REC_ID "Secret ID"
|
||||||
|
+#define PVSECRETS_REC_TYPE "Type"
|
||||||
|
+
|
||||||
|
#define pr_verbose(verbose, fmt...) do { \
|
||||||
|
if (verbose) \
|
||||||
|
warnx(fmt); \
|
||||||
|
@@ -159,3 +206,349 @@ static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Returns true if the secret type is supported by zkey
|
||||||
|
+ *
|
||||||
|
+ * @param type the secret type
|
||||||
|
+ *
|
||||||
|
+ * @returns true if the type is supported, false otherwise
|
||||||
|
+ */
|
||||||
|
+static bool is_pvsecret_type_supported(u16 type)
|
||||||
|
+{
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
||||||
|
+ if (pvsecret_type_info[i].type == type)
|
||||||
|
+ return pvsecret_type_info[i].zkey_usage;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Returns the secret type name for the specified secret type
|
||||||
|
+ *
|
||||||
|
+ * @param type the secret type
|
||||||
|
+ *
|
||||||
|
+ * @returns a constant string containing the type name
|
||||||
|
+ */
|
||||||
|
+static const char *get_pvsecret_type_name(u16 type)
|
||||||
|
+{
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
||||||
|
+ if (pvsecret_type_info[i].type == type)
|
||||||
|
+ return pvsecret_type_info[i].name;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return "[UNKNOWN]";
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Returns the secret type for the specified type name
|
||||||
|
+ *
|
||||||
|
+ * @param name the secret type name
|
||||||
|
+ *
|
||||||
|
+ * @returns the secret type or UV_SECRET_TYPE_INVALID if unknown.
|
||||||
|
+ */
|
||||||
|
+static u16 get_pvsecret_type_by_name(const char *name)
|
||||||
|
+{
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
||||||
|
+ if (strcasecmp(pvsecret_type_info[i].name, name) == 0)
|
||||||
|
+ return pvsecret_type_info[i].type;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return UV_SECRET_TYPE_INVALID;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Parses a 32 byte hex string into a 32 byte binary secret ID
|
||||||
|
+ *
|
||||||
|
+ * @param id_str the hex string to parse
|
||||||
|
+ * @param id the output buffer to store the secret ID
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 for success or a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+static int parse_secret_id_str(const char *id_str,
|
||||||
|
+ unsigned char id[UV_SECRET_ID_LEN])
|
||||||
|
+{
|
||||||
|
+ char hex[3] = { 0 };
|
||||||
|
+ unsigned long val;
|
||||||
|
+ unsigned int i;
|
||||||
|
+ char *endptr;
|
||||||
|
+
|
||||||
|
+ util_assert(id_str != NULL, "Internal error: id_str is NULL");
|
||||||
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
||||||
|
+
|
||||||
|
+ if (strncasecmp(id_str, "0x", 2) == 0)
|
||||||
|
+ id_str += 2;
|
||||||
|
+
|
||||||
|
+ if (strlen(id_str) != UV_SECRET_ID_LEN * 2)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
|
||||||
|
+ hex[0] = id_str[i * 2];
|
||||||
|
+ hex[1] = id_str[i * 2 + 1];
|
||||||
|
+
|
||||||
|
+ errno = 0;
|
||||||
|
+ val = strtoul(hex, &endptr, 16);
|
||||||
|
+ if (errno != 0 || *endptr != '\0' || val > 0xff)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ id[i] = val;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Get the 32 byte binary secret ID from the secret name by calculating the
|
||||||
|
+ * SHA-256 has from the name.
|
||||||
|
+ *
|
||||||
|
+ * @param name the name of the secret
|
||||||
|
+ * @param id the output buffer to store the secret ID
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 for success or a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+static int get_secret_id_from_name(const char *name,
|
||||||
|
+ unsigned char id[UV_SECRET_ID_LEN])
|
||||||
|
+{
|
||||||
|
+ util_assert(name != NULL, "Internal error: id_str is NULL");
|
||||||
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
||||||
|
+ util_assert(UV_SECRET_ID_LEN == SHA256_DIGEST_LENGTH,
|
||||||
|
+ "Internal error: UV_SECRET_ID_LEN != SHA256_DIGEST_LENGTH");
|
||||||
|
+
|
||||||
|
+ if (SHA256((const unsigned char *)name, strlen(name), id) != id)
|
||||||
|
+ return -EIO;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Gets the binary 32 byte secret id from either a hex string or a secret name.
|
||||||
|
+ *
|
||||||
|
+ * @param hex the secret id as hex string. Can be NULL.
|
||||||
|
+ * @param name the secret name. Can be NULL. If the id
|
||||||
|
+ * parameter is non-NULL, then this parameter is
|
||||||
|
+ * ignored.
|
||||||
|
+ * @param id Output: the 32 byte binary secret id.
|
||||||
|
+ * @param id_str Output: the secret id in printable ascii chars
|
||||||
|
+ * form, if name is non-NULL and the name length is
|
||||||
|
+ * less than UV_SECRET_ID_LEN.
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 on success, a negative errno in case of an error.
|
||||||
|
+ * If neither the hex string nor the secret name is specified, 1 is returned,
|
||||||
|
+ * and the id parameter is not modified.
|
||||||
|
+ */
|
||||||
|
+static int get_secret_id_from_hex_or_name(const char *hex, const char *name,
|
||||||
|
+ unsigned char id[UV_SECRET_ID_LEN],
|
||||||
|
+ char id_name[UV_SECRET_ID_LEN])
|
||||||
|
+{
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
||||||
|
+
|
||||||
|
+ if (hex != NULL) {
|
||||||
|
+ rc = parse_secret_id_str(hex, id);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Invalid pvsecret id specified: '%s'", hex);
|
||||||
|
+ return rc;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (name != NULL) {
|
||||||
|
+ rc = get_secret_id_from_name(name, id);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Failed to get the ID from pvsecret name: '%s'",
|
||||||
|
+ name);
|
||||||
|
+ return rc;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (strlen(name) < UV_SECRET_ID_LEN) {
|
||||||
|
+ strncpy(id_name, name, UV_SECRET_ID_LEN);
|
||||||
|
+ id_name[UV_SECRET_ID_LEN - 1] = '\0';
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Checks if the secret id is printable. To be printable, all characters up to
|
||||||
|
+ * the first zero byte must be printable. All bytes after the first zero byte
|
||||||
|
+ * must be all zero. There must be at least one zero byte as the very last byte
|
||||||
|
+ * of the id.
|
||||||
|
+ *
|
||||||
|
+ * @param id the ID of the secret
|
||||||
|
+ * @param name Output: the id in the printable form and enclosed
|
||||||
|
+ * in single quotes if the id is printable. The max
|
||||||
|
+ * length of the name buffer is UV_SECRET_ID_LEN + 2:
|
||||||
|
+ * A starting quote, up to UV_SECRET_ID_LEN-1 chars,
|
||||||
|
+ * an ending quote and a zero termination byte.
|
||||||
|
+ *
|
||||||
|
+ * @returns true if the id is printable, false otherwise.
|
||||||
|
+ */
|
||||||
|
+static bool is_printable_name(const u8 id[UV_SECRET_ID_LEN],
|
||||||
|
+ char name[UV_SECRET_ID_LEN + 2])
|
||||||
|
+{
|
||||||
|
+ bool end_found = false, printable_name = false;
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ name[0] = '\'';
|
||||||
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
|
||||||
|
+ if (!end_found) {
|
||||||
|
+ if (id[i] == '\0') {
|
||||||
|
+ name[1 + i] = '\'';
|
||||||
|
+ end_found = true;
|
||||||
|
+ } else if (isprint(id[i])) {
|
||||||
|
+ name[1 + i] = id[i];
|
||||||
|
+ printable_name = true;
|
||||||
|
+ } else {
|
||||||
|
+ printable_name = false;
|
||||||
|
+ end_found = true;
|
||||||
|
+ }
|
||||||
|
+ } else if (id[i] != '\0') {
|
||||||
|
+ printable_name = false;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (!end_found)
|
||||||
|
+ printable_name = false;
|
||||||
|
+
|
||||||
|
+ return printable_name;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+struct list_secrets_data {
|
||||||
|
+ struct util_rec *rec;
|
||||||
|
+ bool all;
|
||||||
|
+ bool hex;
|
||||||
|
+ u16 type_filter;
|
||||||
|
+ bool id_filter;
|
||||||
|
+ char name[UV_SECRET_ID_LEN];
|
||||||
|
+ unsigned char id[UV_SECRET_ID_LEN];
|
||||||
|
+ unsigned int matched;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Callback used with pvsecrets_list function. Called for each secret.
|
||||||
|
+ *
|
||||||
|
+ * @param idx the index of the secret
|
||||||
|
+ * @param type the type of the secret
|
||||||
|
+ * @param id the ID of the secret
|
||||||
|
+ * @param cb_private callback private data
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 on success, a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+static int pvsecrets_list_cb(u16 UNUSED(idx), u16 type, u32 UNUSED(len),
|
||||||
|
+ const u8 id[UV_SECRET_ID_LEN], void *cb_private)
|
||||||
|
+{
|
||||||
|
+ struct list_secrets_data *list_data = cb_private;
|
||||||
|
+ char name[2 + UV_SECRET_ID_LEN] = { 0 };
|
||||||
|
+ char hex[2 * UV_SECRET_ID_LEN + 1] = { 0 };
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ if (!list_data->all && !is_pvsecret_type_supported(type))
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ if (list_data->type_filter != 0 && type != list_data->type_filter)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ if (list_data->id_filter &&
|
||||||
|
+ memcmp(id, list_data->name, UV_SECRET_ID_LEN) != 0 &&
|
||||||
|
+ memcmp(id, list_data->id, UV_SECRET_ID_LEN) != 0)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++)
|
||||||
|
+ sprintf(&hex[i * 2], "%02x", id[i]);
|
||||||
|
+
|
||||||
|
+ if (!list_data->hex && is_printable_name(id, name))
|
||||||
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, name);
|
||||||
|
+ else
|
||||||
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, hex);
|
||||||
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_TYPE,
|
||||||
|
+ get_pvsecret_type_name(type));
|
||||||
|
+
|
||||||
|
+ if (list_data->matched == 0)
|
||||||
|
+ util_rec_print_hdr(list_data->rec);
|
||||||
|
+
|
||||||
|
+ util_rec_print(list_data->rec);
|
||||||
|
+
|
||||||
|
+ list_data->matched++;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Lists protected virtualization secrets.
|
||||||
|
+ *
|
||||||
|
+ * @param uv_fd the file descriptor of the ultravisor device
|
||||||
|
+ * @param all if true, all secret types are listed
|
||||||
|
+ * @param hex if true, list the secret ID in hex, even if the
|
||||||
|
+ * secret ID would be printable
|
||||||
|
+ * @param type_filter only display secrets of the specified secret type.
|
||||||
|
+ * Can be NULL.
|
||||||
|
+ * @param secret_id the secret id to list. Can be NULL.
|
||||||
|
+ * @param secret_name the secret name to list. Can be NULL. If the id
|
||||||
|
+ * parameter is non-NULL, then this parameter is
|
||||||
|
+ * ignored.
|
||||||
|
+ * @param verbose if true, verbose messages are printed
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 on success, a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
|
||||||
|
+ const char *secret_id, const char *secret_name,
|
||||||
|
+ bool verbose)
|
||||||
|
+{
|
||||||
|
+ struct list_secrets_data list_data = { 0 };
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
|
||||||
|
+
|
||||||
|
+ list_data.all = all;
|
||||||
|
+ list_data.hex = hex;
|
||||||
|
+ list_data.type_filter = UV_SECRET_TYPE_INVALID;
|
||||||
|
+
|
||||||
|
+ if (type_filter != NULL) {
|
||||||
|
+ list_data.type_filter = get_pvsecret_type_by_name(type_filter);
|
||||||
|
+ if (list_data.type_filter == UV_SECRET_TYPE_INVALID) {
|
||||||
|
+ warnx("Invalid pvsecret type specified: %s",
|
||||||
|
+ type_filter);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (secret_id != NULL || secret_name != NULL) {
|
||||||
|
+ rc = get_secret_id_from_hex_or_name(secret_id, secret_name,
|
||||||
|
+ list_data.id,
|
||||||
|
+ list_data.name);
|
||||||
|
+ if (rc < 0)
|
||||||
|
+ return rc;
|
||||||
|
+
|
||||||
|
+ list_data.id_filter = true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ list_data.rec = util_rec_new_wide("-");
|
||||||
|
+ util_rec_def(list_data.rec, PVSECRETS_REC_ID, UTIL_REC_ALIGN_LEFT,
|
||||||
|
+ UV_SECRET_ID_LEN * 2, PVSECRETS_REC_ID);
|
||||||
|
+ util_rec_def(list_data.rec, PVSECRETS_REC_TYPE, UTIL_REC_ALIGN_LEFT,
|
||||||
|
+ 12, PVSECRETS_REC_TYPE);
|
||||||
|
+
|
||||||
|
+ rc = uv_list_secrets(uv_fd, pvsecrets_list_cb, &list_data, verbose);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Failed to list protected virtualization secrets: %s",
|
||||||
|
+ strerror(-rc));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ util_rec_free(list_data.rec);
|
||||||
|
+
|
||||||
|
+ if (list_data.matched == 0)
|
||||||
|
+ rc = -ENOENT;
|
||||||
|
+
|
||||||
|
+ return rc;
|
||||||
|
+}
|
||||||
|
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||||
|
index 2667e859..6acebfdd 100644
|
||||||
|
--- a/zkey/pvsecrets.h
|
||||||
|
+++ b/zkey/pvsecrets.h
|
||||||
|
@@ -86,4 +86,8 @@ struct uvio_list_secrets {
|
||||||
|
|
||||||
|
int uv_open_device(bool verbose);
|
||||||
|
|
||||||
|
+int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
|
||||||
|
+ const char *secret_id, const char *secret_name,
|
||||||
|
+ bool verbose);
|
||||||
|
+
|
||||||
|
#endif
|
||||||
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||||
|
index 8c5a09a7..4386629f 100644
|
||||||
|
--- a/zkey/zkey.1
|
||||||
|
+++ b/zkey/zkey.1
|
||||||
|
@@ -1,8 +1,8 @@
|
||||||
|
-.\" Copyright IBM Corp. 2017, 2020
|
||||||
|
+.\" Copyright IBM Corp. 2017, 2024
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
-.TH ZKEY 1 "July 2020" "s390-tools"
|
||||||
|
+.TH ZKEY 1 "February 2024" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
zkey \- Manage secure AES keys
|
||||||
|
.
|
||||||
|
@@ -1162,6 +1162,54 @@ fails. Use option \fB\-\-no\-volume\-check\fP to omit the volume check, and
|
||||||
|
refresh the keys even if the associated volume(s) do not exist.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
+.SH COMMANDS FOR PROTECTED VIRTUALIZATION
|
||||||
|
+.
|
||||||
|
+Use the \fBpvsecrets\fP command to work with protected virtualization (PV)
|
||||||
|
+secrets. Protected virtualization secrets can be made available to a secure
|
||||||
|
+execution guest and can be used only within that guest.
|
||||||
|
+The \fBpvsecrets\fP command provides subcommands for protected
|
||||||
|
+virtualization specific operations. Use \fBzkey pvsecrets \-\-help\fP to show
|
||||||
|
+the available subcommands. These subcommands only work when running in a
|
||||||
|
+secure execution guest. Only the \fBroot\fP user is allowed to perform these
|
||||||
|
+subcommands.
|
||||||
|
+.
|
||||||
|
+.SS "List available protected virtualization secrets"
|
||||||
|
+.
|
||||||
|
+.B zkey pvsecrets
|
||||||
|
+.BR list | li
|
||||||
|
+.RB [ \-\-all | \-A ]
|
||||||
|
+.RB [ \-\-hex | \-H ]
|
||||||
|
+.RB [ \-\-pvsecret\-type | \-T
|
||||||
|
+.IR pvsecret\-type ]
|
||||||
|
+.RB [ \-\-pvsecret\-id | \-I
|
||||||
|
+.IR pvsecret\-id ]
|
||||||
|
+.RB [ \-\-pvsecret\-name | \-e
|
||||||
|
+.IR pvsecret\-name ]
|
||||||
|
+.RB [ \-\-verbose | \-V ]
|
||||||
|
+.
|
||||||
|
+.PP
|
||||||
|
+Use the
|
||||||
|
+.B pvsecrets list
|
||||||
|
+command to display a list of protected virtualization (PV) secrets. It displays
|
||||||
|
+the pvsecret ID as hex string of 32 bytes or as printable name enclosed in
|
||||||
|
+single quotes (\fB'\fP), if the pvsecret ID consists of only printable
|
||||||
|
+characters. Specify the \fB\-\-hex\fP option to list all pvsecret IDs as hex
|
||||||
|
+string. The
|
||||||
|
+.B pvsecrets list
|
||||||
|
+command also shows the pvsecret type of each secret.
|
||||||
|
+.PP
|
||||||
|
+You can filter the list of pvsecrets by pvsecret ID, pvsecret name and pvsecret
|
||||||
|
+type. Either the \fB\-\-pvsecret\-id\fP option or the
|
||||||
|
+\fB\-\-pvsecret\-name\fP option can be specified. By default the
|
||||||
|
+\fBpvsecrets list\fP command displays only those pvsecrets with types that
|
||||||
|
+are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the
|
||||||
|
+\fB\-\-all\fP option.
|
||||||
|
+.PP
|
||||||
|
+This command is only available when running in a secure execution guest.
|
||||||
|
+Only the \fBroot\fP user is allowed to perform this command.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
.SS "Options for the generate command"
|
||||||
|
@@ -2030,6 +2078,45 @@ repository. This option only has an effect when specified together with option
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
+.SS "Options for the pvsecrets list command"
|
||||||
|
+.TP
|
||||||
|
+.BR \-A ", " \-\-all
|
||||||
|
+List all protected virtualization (PV) secret types, not only those that can be
|
||||||
|
+used with zkey.
|
||||||
|
+.TP
|
||||||
|
+.BR \-H ", " \-\-hex
|
||||||
|
+Show all protected virtualization (PV) secret IDs in hex, even if the ID
|
||||||
|
+contains only printable characters.
|
||||||
|
+.TP
|
||||||
|
+.BR \-T ", " \-\-pvsecret\-type\~\fIpvsecret\-type\fP
|
||||||
|
+Type of the protected virtualization (PV) secret to list. If omitted, all
|
||||||
|
+secret types are listed. Possible values are: \fBPLAIN\-TEXT\fP, \fBAES\-128\fP,
|
||||||
|
+\fBAES\-192\fP, \fBAES\-256\fP, \fBAES\-XTS\-128\fP, \fBAES\-XTS\-256\fP,
|
||||||
|
+\fBHMAC\-SHA\-256\fP, \fBHMAC\-SHA\-512\fP, \fBECDSA\-P256\fP,
|
||||||
|
+\fBECDSA\-P384\fP, \fBECDSA\-P521\fP, \fBEDDSA\-ED25519\fP, and
|
||||||
|
+\fBEDDSA\-ED448\fP.
|
||||||
|
+.TP
|
||||||
|
+.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP
|
||||||
|
+ID of the protected virtualization (PV) secret to list. The pvsecret ID is a 32
|
||||||
|
+byte hex string, optionally prefixed by \fB0x\fP. You can use the YAML file that
|
||||||
|
+was created when using the \fBpvsecret create\fP command for adding the
|
||||||
|
+protected virtualization secret:
|
||||||
|
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP.
|
||||||
|
+You might have to install the \fByq\fP package first.
|
||||||
|
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
|
||||||
|
+can be specified, but not both.
|
||||||
|
+.TP
|
||||||
|
+.BR \-e ", " \-\-pvsecret\-name\~\fIpvsecret\-name\fP
|
||||||
|
+Name of the protected virtualization (PV) secret to list. You can use the YAML
|
||||||
|
+file that was created when using the \fBpvsecret create\fP command for adding
|
||||||
|
+the protected virtualization secret:
|
||||||
|
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
|
||||||
|
+You might have to install the \fByq\fP package first.
|
||||||
|
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
|
||||||
|
+can be specified, but not both.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
.SS "General options"
|
||||||
|
.TP
|
||||||
|
.BR \-V ", " \-\-verbose
|
||||||
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||||
|
index 7c909ff0..adc48d60 100644
|
||||||
|
--- a/zkey/zkey.c
|
||||||
|
+++ b/zkey/zkey.c
|
||||||
|
@@ -1,7 +1,7 @@
|
||||||
|
/*
|
||||||
|
* zkey - Generate, re-encipher, and validate secure keys
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2017, 2020
|
||||||
|
+ * Copyright IBM Corp. 2017, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -34,6 +34,7 @@
|
||||||
|
#include "pkey.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "kms.h"
|
||||||
|
+#include "pvsecrets.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Program configuration
|
||||||
|
@@ -46,7 +47,7 @@ static const struct util_prg prg = {
|
||||||
|
{
|
||||||
|
.owner = "IBM Corp.",
|
||||||
|
.pub_first = 2017,
|
||||||
|
- .pub_last = 2020,
|
||||||
|
+ .pub_last = 2024,
|
||||||
|
},
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
}
|
||||||
|
@@ -93,10 +94,16 @@ static struct zkey_globals {
|
||||||
|
bool open;
|
||||||
|
bool format;
|
||||||
|
bool refresh_properties;
|
||||||
|
+ bool all;
|
||||||
|
+ bool hex;
|
||||||
|
+ char *secret_type;
|
||||||
|
+ char *secret_id;
|
||||||
|
+ char *secret_name;
|
||||||
|
struct ext_lib lib;
|
||||||
|
struct cca_lib cca;
|
||||||
|
struct ep11_lib ep11;
|
||||||
|
int pkey_fd;
|
||||||
|
+ int uv_fd;
|
||||||
|
struct keystore *keystore;
|
||||||
|
struct kms_info kms_info;
|
||||||
|
int first_kms_option;
|
||||||
|
@@ -104,6 +111,7 @@ static struct zkey_globals {
|
||||||
|
size_t num_kms_options;
|
||||||
|
} g = {
|
||||||
|
.pkey_fd = -1,
|
||||||
|
+ .uv_fd = -1,
|
||||||
|
.sector_size = -1,
|
||||||
|
.lib.cca = &g.cca,
|
||||||
|
.lib.ep11 = &g.ep11,
|
||||||
|
@@ -135,6 +143,8 @@ static struct zkey_globals {
|
||||||
|
#define COMMAND_KMS_LIST "list"
|
||||||
|
#define COMMAND_KMS_IMPORT "import"
|
||||||
|
#define COMMAND_KMS_REFRESH "refresh"
|
||||||
|
+#define COMMAND_PVSECRETS "pvsecrets"
|
||||||
|
+#define COMMAND_PVSECRETS_LIST "list"
|
||||||
|
|
||||||
|
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
|
||||||
|
|
||||||
|
@@ -1182,6 +1192,48 @@ static struct util_opt opt_vec[] = {
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
/***********************************************************/
|
||||||
|
+ {
|
||||||
|
+ .flags = UTIL_OPT_FLAG_SECTION,
|
||||||
|
+ .desc = "OPTIONS",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = {"all", 0, NULL, 'A'},
|
||||||
|
+ .desc = "List all protected virtualization (PV) secret types, "
|
||||||
|
+ "not only those that can be used with zkey.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = {"hex", 0, NULL, 'H'},
|
||||||
|
+ .desc = "Show all protected virtualization (PV) secret IDs in "
|
||||||
|
+ "hex, even if the ID contains only printable "
|
||||||
|
+ "characters.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "pvsecret-type", required_argument, NULL, 'T'},
|
||||||
|
+ .argument = "PVSECRET-TYPE",
|
||||||
|
+ .desc = "Type of the protected virtualization (PV) secret to "
|
||||||
|
+ "list. If omitted, all pvsecret types are listed.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "pvsecret-id", required_argument, NULL, 'I'},
|
||||||
|
+ .argument = "PVSECRET-ID",
|
||||||
|
+ .desc = "ID of the protected virtualization (PV) secret to "
|
||||||
|
+ "list. Either '--pvsecret-id/-I' or "
|
||||||
|
+ "'--pvsecret-name/-e' can be specified, but not both.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "pvsecret-name", required_argument, NULL, 'e'},
|
||||||
|
+ .argument = "PVSECRET-NAME",
|
||||||
|
+ .desc = "Name of the protected virtualization (PV) secret to "
|
||||||
|
+ "list. Either '--pvsecret-name/-e' or "
|
||||||
|
+ "'--pvsecret-id/-I' can be specified, but not both.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
+ },
|
||||||
|
+ /***********************************************************/
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
@@ -1249,6 +1301,7 @@ struct zkey_command {
|
||||||
|
int need_cca_library;
|
||||||
|
int need_ep11_library;
|
||||||
|
int need_pkey_device;
|
||||||
|
+ int need_uv_device;
|
||||||
|
char *short_desc;
|
||||||
|
char *long_desc;
|
||||||
|
int has_options;
|
||||||
|
@@ -1285,6 +1338,7 @@ static int command_kms_reencipher(void);
|
||||||
|
static int command_kms_list(void);
|
||||||
|
static int command_kms_import(void);
|
||||||
|
static int command_kms_refresh(void);
|
||||||
|
+static int command_pvsecrets_list(void);
|
||||||
|
|
||||||
|
static struct zkey_command zkey_kms_commands[] = {
|
||||||
|
{
|
||||||
|
@@ -1402,6 +1456,22 @@ static struct zkey_command zkey_kms_commands[] = {
|
||||||
|
{ .command = NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
+static struct zkey_command zkey_pvsecrets_commands[] = {
|
||||||
|
+ {
|
||||||
|
+ .command = COMMAND_PVSECRETS_LIST,
|
||||||
|
+ .abbrev_len = 2,
|
||||||
|
+ .function = command_pvsecrets_list,
|
||||||
|
+ .short_desc = "Lists protected virtualization (PV) secrets",
|
||||||
|
+ .long_desc = "Lists available protected virtualization (PV) "
|
||||||
|
+ "secrets. This command is only available when "
|
||||||
|
+ "running in a secure execution guest. Only the "
|
||||||
|
+ "'root' user is allowed to perform this command",
|
||||||
|
+ .has_options = 1,
|
||||||
|
+ .need_uv_device = 1,
|
||||||
|
+ },
|
||||||
|
+ { .command = NULL }
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
static struct zkey_command zkey_commands[] = {
|
||||||
|
{
|
||||||
|
.command = COMMAND_GENERATE,
|
||||||
|
@@ -1565,6 +1635,21 @@ static struct zkey_command zkey_commands[] = {
|
||||||
|
.has_options = 1,
|
||||||
|
.sub_commands = zkey_kms_commands,
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ .command = COMMAND_PVSECRETS,
|
||||||
|
+ .abbrev_len = 2,
|
||||||
|
+ .short_desc = "Protected virtualization (PV) support",
|
||||||
|
+ .long_desc = "Provides subcommands for working with protected "
|
||||||
|
+ "virtualization (PV) secrets. Protected "
|
||||||
|
+ "virtualization secrets can be made available to "
|
||||||
|
+ "a secure execution guest and can be used only "
|
||||||
|
+ "within that guest. Thus, these subcommands are "
|
||||||
|
+ "only available when running in a secure "
|
||||||
|
+ "execution guest. Only the 'root' user is allowed "
|
||||||
|
+ "to perform these subcommands.",
|
||||||
|
+ .has_options = 1,
|
||||||
|
+ .sub_commands = zkey_pvsecrets_commands,
|
||||||
|
+ },
|
||||||
|
{ .command = NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -2899,6 +2984,28 @@ static int command_kms_refresh(void)
|
||||||
|
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Command handler for 'pvsecrets list'.
|
||||||
|
+ *
|
||||||
|
+ * Lists available protected virtualization secrets
|
||||||
|
+ */
|
||||||
|
+static int command_pvsecrets_list(void)
|
||||||
|
+{
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ if (g.secret_id != NULL && g.secret_name != NULL) {
|
||||||
|
+ warnx("Either '--pvsecret-id/-I' or '--pvsecret-name/-e' can "
|
||||||
|
+ "be specified, but not both");
|
||||||
|
+ util_prg_print_parse_error();
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rc = pvsecrets_list(g.uv_fd, g.all, g.hex, g.secret_type, g.secret_id,
|
||||||
|
+ g.secret_name, g.verbose);
|
||||||
|
+
|
||||||
|
+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Opens the keystore. The keystore directory is either the
|
||||||
|
* default directory or as specified in an environment variable
|
||||||
|
@@ -3234,6 +3341,21 @@ int main(int argc, char *argv[])
|
||||||
|
case OPT_REMOVE_DUMMY_PASSPHRASE:
|
||||||
|
g.remove_passphrase = 1;
|
||||||
|
break;
|
||||||
|
+ case 'A':
|
||||||
|
+ g.all = 1;
|
||||||
|
+ break;
|
||||||
|
+ case 'H':
|
||||||
|
+ g.hex = 1;
|
||||||
|
+ break;
|
||||||
|
+ case 'T':
|
||||||
|
+ g.secret_type = optarg;
|
||||||
|
+ break;
|
||||||
|
+ case 'I':
|
||||||
|
+ g.secret_id = optarg;
|
||||||
|
+ break;
|
||||||
|
+ case 'e':
|
||||||
|
+ g.secret_name = optarg;
|
||||||
|
+ break;
|
||||||
|
case 'h':
|
||||||
|
print_help(command, sub_command);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
@@ -3294,6 +3416,13 @@ int main(int argc, char *argv[])
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ if (cmd->need_uv_device) {
|
||||||
|
+ g.uv_fd = uv_open_device(g.verbose);
|
||||||
|
+ if (g.uv_fd == -1) {
|
||||||
|
+ rc = EXIT_FAILURE;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (g.kms_info.plugin_lib != NULL) {
|
||||||
|
rc = init_kms_plugin(&g.kms_info, g.verbose);
|
||||||
|
@@ -3323,6 +3452,8 @@ int main(int argc, char *argv[])
|
||||||
|
dlclose(g.ep11.lib_ep11);
|
||||||
|
if (g.pkey_fd >= 0)
|
||||||
|
close(g.pkey_fd);
|
||||||
|
+ if (g.uv_fd >= 0)
|
||||||
|
+ close(g.uv_fd);
|
||||||
|
if (g.keystore)
|
||||||
|
keystore_free(g.keystore);
|
||||||
|
if (g.kms_options != NULL)
|
@ -0,0 +1,334 @@
|
|||||||
|
From cf51ac786095f2a1a17d04fea9ee73271438d247 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Wed, 11 Dec 2024 19:25:59 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Add '--(enable|disable)-image-encryption' flags
|
||||||
|
to 'pvimg create'
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
With runtime attestation it might be useful to have non-encrypted Secure
|
||||||
|
Execution images. This patch adds the support for this to the 'pvimg
|
||||||
|
create' and 'genprotimg' commands.
|
||||||
|
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/man/genprotimg.1 | 26 +++++++++++++++++++++-----
|
||||||
|
rust/pvimg/man/pvimg-create.1 | 26 +++++++++++++++++++++-----
|
||||||
|
rust/pvimg/man/pvimg-info.1 | 10 +++++-----
|
||||||
|
rust/pvimg/man/pvimg-test.1 | 10 +++++-----
|
||||||
|
rust/pvimg/man/pvimg.1 | 10 +++++-----
|
||||||
|
rust/pvimg/src/cli.rs | 18 ++++++++++++++++++
|
||||||
|
rust/pvimg/src/cmd/create.rs | 10 ++++++++++
|
||||||
|
7 files changed, 85 insertions(+), 25 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/man/genprotimg.1 b/rust/pvimg/man/genprotimg.1
|
||||||
|
index 46a91aa4..3f4949e9 100644
|
||||||
|
--- a/rust/pvimg/man/genprotimg.1
|
||||||
|
+++ b/rust/pvimg/man/genprotimg.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH genprotimg 1 "2024-12-05" "s390-tools" "Genprotimg Manual"
|
||||||
|
+.TH genprotimg 1 "2024-12-11" "s390-tools" "Genprotimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBgenprotimg\fP - Create an IBM Secure Execution image
|
||||||
|
+\fBgenprotimg\fP \- Create an IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -196,6 +196,22 @@ Disable the support for backup target keys (default).
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
+\-\-enable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Enable encryption of the image components (default). The image components are:
|
||||||
|
+the kernel, ramdisk, and kernel command line.
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
+\-\-disable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Disable encryption of the image components. The image components are: the
|
||||||
|
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||||
|
+contain any confidential content (for example, secrets like non\-public
|
||||||
|
+cryptographic keys).
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
\-v, \-\-verbose
|
||||||
|
.RS 4
|
||||||
|
Provide more detailed output.
|
||||||
|
@@ -222,16 +238,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-create.1 b/rust/pvimg/man/pvimg-create.1
|
||||||
|
index aba197fa..dae1cf18 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-create.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-create.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-create 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-create 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg create\fP - Create an IBM Secure Execution image
|
||||||
|
+\fBpvimg create\fP \- Create an IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -195,6 +195,22 @@ Disable the support for backup target keys (default).
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
+\-\-enable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Enable encryption of the image components (default). The image components are:
|
||||||
|
+the kernel, ramdisk, and kernel command line.
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
+\-\-disable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Disable encryption of the image components. The image components are: the
|
||||||
|
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||||
|
+contain any confidential content (for example, secrets like non\-public
|
||||||
|
+cryptographic keys).
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
\-h, \-\-help
|
||||||
|
.RS 4
|
||||||
|
Print help (see a summary with \fB\-h\fR).
|
||||||
|
@@ -203,16 +219,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-info.1 b/rust/pvimg/man/pvimg-info.1
|
||||||
|
index e88cbe49..d2726c35 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-info.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-info.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-info 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-info 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg info\fP - Print information about the IBM Secure Execution image
|
||||||
|
+\fBpvimg info\fP \- Print information about the IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -51,16 +51,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-test.1 b/rust/pvimg/man/pvimg-test.1
|
||||||
|
index 901c7edb..4fb7d73f 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-test.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-test.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-test 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-test 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg test\fP - Test different aspects of an existing IBM Secure Execution image
|
||||||
|
+\fBpvimg test\fP \- Test different aspects of an existing IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -54,16 +54,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg.1 b/rust/pvimg/man/pvimg.1
|
||||||
|
index 37c8e978..5676b61d 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg\fP - Create and inspect IBM Secure Execution images
|
||||||
|
+\fBpvimg\fP \- Create and inspect IBM Secure Execution images
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -69,16 +69,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs
|
||||||
|
index 2ca4e901..12f0b764 100644
|
||||||
|
--- a/rust/pvimg/src/cli.rs
|
||||||
|
+++ b/rust/pvimg/src/cli.rs
|
||||||
|
@@ -140,6 +140,20 @@ pub struct CreateBootImageLegacyFlags {
|
||||||
|
/// Disable the support for backup target keys (default).
|
||||||
|
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")]
|
||||||
|
pub disable_backup_keys: Option<bool>,
|
||||||
|
+
|
||||||
|
+ /// Enable encryption of the image components (default).
|
||||||
|
+ ///
|
||||||
|
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
|
||||||
|
+ pub enable_image_encryption: Option<bool>,
|
||||||
|
+
|
||||||
|
+ /// Disable encryption of the image components.
|
||||||
|
+ ///
|
||||||
|
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||||
|
+ /// Use only if the components used do not contain any confidential content
|
||||||
|
+ /// (for example, secrets like non-public cryptographic keys).
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_image_encryption", group="header-flags")]
|
||||||
|
+ pub disable_image_encryption: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
@@ -476,6 +490,8 @@ mod test {
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo-hmac", ["--enable-pckmo-hmac"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-backup-keys", ["--enable-backup-keys"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
|
||||||
|
];
|
||||||
|
let invalid_create_args = [
|
||||||
|
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
||||||
|
@@ -501,6 +517,8 @@ mod test {
|
||||||
|
CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]),
|
||||||
|
CliOption::new("disable-pckmo", ["--disable-pckmo"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
||||||
|
+ CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut genprotimg_valid_args = vec![
|
||||||
|
diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs
|
||||||
|
index b696d790..475d3523 100644
|
||||||
|
--- a/rust/pvimg/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvimg/src/cmd/create.rs
|
||||||
|
@@ -80,6 +80,12 @@ fn parse_flags(
|
||||||
|
lf.enable_backup_keys
|
||||||
|
.filter(|x| *x)
|
||||||
|
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))),
|
||||||
|
+ lf.disable_image_encryption
|
||||||
|
+ .filter(|x| *x)
|
||||||
|
+ .and(Some(PcfV1::all_enabled([PcfV1::NoComponentEncryption]))),
|
||||||
|
+ lf.enable_image_encryption
|
||||||
|
+ .filter(|x| *x)
|
||||||
|
+ .and(Some(PcfV1::all_disabled([PcfV1::NoComponentEncryption]))),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
@@ -135,6 +141,10 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
|
||||||
|
read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||||
|
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||||
|
|
||||||
|
+ if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
||||||
|
+ warn!("The components encryption is disabled, make sure that the components do not contain any confidential content.");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
let mut components = components(&opt.component_paths)?;
|
||||||
|
if opt.no_component_check {
|
||||||
|
warn!("The component check is turned off!");
|
340
s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
Normal file
340
s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
From fdf66dc148c09f1d3300cd3378e3f75a83c6214e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Thu, 15 Feb 2024 16:56:04 +0100
|
||||||
|
Subject: [PATCH] zkey: Add PVSECRETS-AES key type
|
||||||
|
|
||||||
|
Add the definitions and utility functions for the PVSECRETS-AES key type.
|
||||||
|
A PVSECRETS-AES key token contains the secret id of a protected
|
||||||
|
virtualization secret. It does not contain the key material, just a
|
||||||
|
reference to the key in the ultravisor.
|
||||||
|
|
||||||
|
When such a key token is used to perform crypto operations later on, the
|
||||||
|
PAES kernel cipher will obtain the protected key belonging to this secret
|
||||||
|
id with the help of the pkey kernel module.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/keystore.c | 4 +-
|
||||||
|
zkey/pkey.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
zkey/pkey.h | 38 ++++++++++++----
|
||||||
|
zkey/pvsecrets.h | 4 +-
|
||||||
|
4 files changed, 143 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||||
|
index 9589d82d..771bc08d 100644
|
||||||
|
--- a/zkey/keystore.c
|
||||||
|
+++ b/zkey/keystore.c
|
||||||
|
@@ -3,7 +3,7 @@
|
||||||
|
*
|
||||||
|
* Keystore handling functions
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2018, 2020
|
||||||
|
+ * Copyright IBM Corp. 2018, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -360,6 +360,8 @@ static int _keystore_valid_key_type(const char *key_type)
|
||||||
|
return 1;
|
||||||
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||||
|
return 1;
|
||||||
|
+ if (strcasecmp(key_type, KEY_TYPE_PVSECRET_AES) == 0)
|
||||||
|
+ return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
diff --git a/zkey/pkey.c b/zkey/pkey.c
|
||||||
|
index 821978bd..53c0a550 100644
|
||||||
|
--- a/zkey/pkey.c
|
||||||
|
+++ b/zkey/pkey.c
|
||||||
|
@@ -1,7 +1,7 @@
|
||||||
|
/*
|
||||||
|
* zkey - Generate, re-encipher, and validate secure keys
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2018
|
||||||
|
+ * Copyright IBM Corp. 2018, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -26,6 +26,7 @@
|
||||||
|
#include "lib/util_panic.h"
|
||||||
|
|
||||||
|
#include "pkey.h"
|
||||||
|
+#include "pvsecrets.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#ifndef AF_ALG
|
||||||
|
@@ -1719,6 +1720,38 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * Check if the specified key is a PVSECRET-AES key token.
|
||||||
|
+ *
|
||||||
|
+ * @param[in] key the secure key token
|
||||||
|
+ * @param[in] key_size the size of the secure key
|
||||||
|
+ *
|
||||||
|
+ * @returns true if the key is a PVSECRET token type
|
||||||
|
+ */
|
||||||
|
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size)
|
||||||
|
+{
|
||||||
|
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||||
|
+
|
||||||
|
+ if (key == NULL || key_size < PVSECRET_KEY_SIZE)
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ if (pvsecret->hdr.type != TOKEN_TYPE_NON_CCA)
|
||||||
|
+ return false;
|
||||||
|
+ if (pvsecret->hdr.version != TOKEN_VERSION_PVSECRET)
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ switch (pvsecret->secret_type) {
|
||||||
|
+ case UV_SECRET_TYPE_AES_128:
|
||||||
|
+ case UV_SECRET_TYPE_AES_192:
|
||||||
|
+ case UV_SECRET_TYPE_AES_256:
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||||
|
+ return true;
|
||||||
|
+ default:
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Check if the specified key is an XTS type key
|
||||||
|
*
|
||||||
|
@@ -1729,6 +1762,8 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
|
||||||
|
*/
|
||||||
|
bool is_xts_key(const u8 *key, size_t key_size)
|
||||||
|
{
|
||||||
|
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||||
|
+
|
||||||
|
if (is_cca_aes_data_key(key, key_size)) {
|
||||||
|
if (key_size == 2 * AESDATA_KEY_SIZE &&
|
||||||
|
is_cca_aes_data_key(key + AESDATA_KEY_SIZE,
|
||||||
|
@@ -1749,11 +1784,41 @@ bool is_xts_key(const u8 *key, size_t key_size)
|
||||||
|
is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE,
|
||||||
|
key_size - EP11_AES_KEY_SIZE))
|
||||||
|
return true;
|
||||||
|
+ } else if (is_pvsecret_aes_key(key, key_size)) {
|
||||||
|
+ switch (pvsecret->secret_type) {
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||||
|
+ return true;
|
||||||
|
+ default:
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * Check if the specified key is a secure key type (i.e. requires a crypto card)
|
||||||
|
+ *
|
||||||
|
+ * @param[in] key the secure key token
|
||||||
|
+ * @param[in] key_size the size of the secure key
|
||||||
|
+ *
|
||||||
|
+ * @returns true if the key is a secure key type
|
||||||
|
+ */
|
||||||
|
+bool is_secure_key(const u8 *key, size_t key_size)
|
||||||
|
+{
|
||||||
|
+ if (is_cca_aes_data_key(key, key_size))
|
||||||
|
+ return true;
|
||||||
|
+ if (is_cca_aes_cipher_key(key, key_size))
|
||||||
|
+ return true;
|
||||||
|
+ if (is_ep11_aes_key(key, key_size))
|
||||||
|
+ return true;
|
||||||
|
+ if (is_ep11_aes_key_with_header(key, key_size))
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Gets the size in bits of the effective key of the specified secure key
|
||||||
|
*
|
||||||
|
@@ -1771,6 +1836,7 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
|
||||||
|
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
||||||
|
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
|
||||||
|
struct ep11kblob_header *hdr = (struct ep11kblob_header *)key;
|
||||||
|
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||||
|
|
||||||
|
util_assert(bitsize != NULL, "Internal error: bitsize is NULL");
|
||||||
|
|
||||||
|
@@ -1805,6 +1871,26 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
|
||||||
|
(key + EP11_AES_KEY_SIZE);
|
||||||
|
*bitsize += hdr->bitlen;
|
||||||
|
}
|
||||||
|
+ } else if (is_pvsecret_aes_key(key, key_size)) {
|
||||||
|
+ switch (pvsecret->secret_type) {
|
||||||
|
+ case UV_SECRET_TYPE_AES_128:
|
||||||
|
+ *bitsize = 128;
|
||||||
|
+ break;
|
||||||
|
+ case UV_SECRET_TYPE_AES_192:
|
||||||
|
+ *bitsize = 192;
|
||||||
|
+ break;
|
||||||
|
+ case UV_SECRET_TYPE_AES_256:
|
||||||
|
+ *bitsize = 256;
|
||||||
|
+ break;
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||||
|
+ *bitsize = 128 * 2;
|
||||||
|
+ break;
|
||||||
|
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||||
|
+ *bitsize = 256 * 2;
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
@@ -1830,9 +1916,31 @@ const char *get_key_type(const u8 *key, size_t key_size)
|
||||||
|
return KEY_TYPE_EP11_AES;
|
||||||
|
if (is_ep11_aes_key_with_header(key, key_size))
|
||||||
|
return KEY_TYPE_EP11_AES;
|
||||||
|
+ if (is_pvsecret_aes_key(key, key_size))
|
||||||
|
+ return KEY_TYPE_PVSECRET_AES;
|
||||||
|
+
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * Returns true if the key type is a secure key type
|
||||||
|
+ *
|
||||||
|
+ * @param[in] key_type the type of the key
|
||||||
|
+ *
|
||||||
|
+ * @returns true if the key type is a secure key type, false otherwise
|
||||||
|
+ */
|
||||||
|
+bool is_secure_key_type(const char *key_type)
|
||||||
|
+{
|
||||||
|
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
||||||
|
+ return true;
|
||||||
|
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
||||||
|
+ return true;
|
||||||
|
+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Returns the minimum card level for a specific key type
|
||||||
|
*
|
||||||
|
diff --git a/zkey/pkey.h b/zkey/pkey.h
|
||||||
|
index 3b57c5f0..ce3bd8bd 100644
|
||||||
|
--- a/zkey/pkey.h
|
||||||
|
+++ b/zkey/pkey.h
|
||||||
|
@@ -4,7 +4,7 @@
|
||||||
|
* This header file defines the interface to the pkey kernel module.
|
||||||
|
* It defines a set of IOCTL commands with its associated structures.
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2017, 2018
|
||||||
|
+ * Copyright IBM Corp. 2017, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -41,6 +41,8 @@ struct tokenheader {
|
||||||
|
#define TOKEN_VERSION_EP11_AES 0x03
|
||||||
|
#define TOKEN_VERSION_EP11_AES_WITH_HEADER 0x06
|
||||||
|
#define TOKEN_VERSION_EP11_ECC_WITH_HEADER 0x07
|
||||||
|
+/* 0x08 is reserved for internal use */
|
||||||
|
+#define TOKEN_VERSION_PVSECRET 0x09
|
||||||
|
|
||||||
|
struct aesdatakeytoken {
|
||||||
|
u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */
|
||||||
|
@@ -116,6 +118,15 @@ struct ep11keytoken {
|
||||||
|
u8 padding[64];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
+#define UV_SECRET_ID_LEN 32
|
||||||
|
+
|
||||||
|
+struct pvsecrettoken {
|
||||||
|
+ struct tokenheader hdr;
|
||||||
|
+ u16 secret_type; /* the secret type as the UV told us */
|
||||||
|
+ u16 secret_len; /* length in bytes of the secret */
|
||||||
|
+ u8 secretid[UV_SECRET_ID_LEN]; /* the secret id for this secret */
|
||||||
|
+} __packed;
|
||||||
|
+
|
||||||
|
#define ZERO_SESSION \
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
|
||||||
|
@@ -124,21 +135,26 @@ struct ep11keytoken {
|
||||||
|
#define EP11_KEY_SIZE sizeof(struct ep11keytoken)
|
||||||
|
#define EP11_AES_KEY_SIZE (sizeof(struct ep11kblob_header) + \
|
||||||
|
sizeof(struct ep11keytoken))
|
||||||
|
+#define PVSECRET_KEY_SIZE sizeof(struct pvsecrettoken)
|
||||||
|
|
||||||
|
/* MAX/MIN from zt_common.h produces warnings for variable length arrays */
|
||||||
|
#define _MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
|
||||||
|
#define MAX_SECURE_KEY_SIZE _MAX( \
|
||||||
|
- _MAX(EP11_KEY_SIZE, \
|
||||||
|
- EP11_AES_KEY_SIZE), \
|
||||||
|
- _MAX(AESDATA_KEY_SIZE, \
|
||||||
|
- AESCIPHER_KEY_SIZE))
|
||||||
|
+ _MAX( \
|
||||||
|
+ _MAX(EP11_KEY_SIZE, \
|
||||||
|
+ EP11_AES_KEY_SIZE), \
|
||||||
|
+ _MAX(AESDATA_KEY_SIZE, \
|
||||||
|
+ AESCIPHER_KEY_SIZE)), \
|
||||||
|
+ PVSECRET_KEY_SIZE)
|
||||||
|
#define MIN_SECURE_KEY_SIZE _MIN( \
|
||||||
|
- _MIN(EP11_KEY_SIZE, \
|
||||||
|
- EP11_AES_KEY_SIZE), \
|
||||||
|
- _MIN(AESDATA_KEY_SIZE, \
|
||||||
|
- AESCIPHER_KEY_SIZE))
|
||||||
|
+ _MIN( \
|
||||||
|
+ _MIN(EP11_KEY_SIZE, \
|
||||||
|
+ EP11_AES_KEY_SIZE), \
|
||||||
|
+ _MIN(AESDATA_KEY_SIZE, \
|
||||||
|
+ AESCIPHER_KEY_SIZE)), \
|
||||||
|
+ PVSECRET_KEY_SIZE)
|
||||||
|
|
||||||
|
struct pkey_seckey {
|
||||||
|
u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */
|
||||||
|
@@ -285,6 +301,7 @@ struct pkey_apqns4keytype {
|
||||||
|
#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA"
|
||||||
|
#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER"
|
||||||
|
#define KEY_TYPE_EP11_AES "EP11-AES"
|
||||||
|
+#define KEY_TYPE_PVSECRET_AES "PVSECRET-AES"
|
||||||
|
|
||||||
|
#define DEFAULT_KEYBITS 256
|
||||||
|
#define PAES_BLOCK_SIZE 16
|
||||||
|
@@ -342,9 +359,12 @@ bool is_cca_aes_cipher_key(const u8 *key, size_t key_size);
|
||||||
|
bool is_ep11_aes_key(const u8 *key, size_t key_size);
|
||||||
|
bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size);
|
||||||
|
bool is_ep11_key_session_bound(const u8 *key, size_t key_size);
|
||||||
|
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size);
|
||||||
|
bool is_xts_key(const u8 *key, size_t key_size);
|
||||||
|
+bool is_secure_key(const u8 *key, size_t key_size);
|
||||||
|
int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize);
|
||||||
|
const char *get_key_type(const u8 *key, size_t key_size);
|
||||||
|
+bool is_secure_key_type(const char *key_type);
|
||||||
|
int get_min_card_level_for_keytype(const char *key_type);
|
||||||
|
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type);
|
||||||
|
enum card_type get_card_type_for_keytype(const char *key_type);
|
||||||
|
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||||
|
index 6acebfdd..ad844035 100644
|
||||||
|
--- a/zkey/pvsecrets.h
|
||||||
|
+++ b/zkey/pvsecrets.h
|
||||||
|
@@ -15,6 +15,8 @@
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
+#include "pkey.h"
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Definitions for the /dev/uv kernel module interface
|
||||||
|
*/
|
||||||
|
@@ -60,8 +62,6 @@ struct uvio_ioctl_cb {
|
||||||
|
#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
|
||||||
|
#define UV_SECRET_TYPE_EDDSA_ED448 0x15
|
||||||
|
|
||||||
|
-#define UV_SECRET_ID_LEN 32
|
||||||
|
-
|
||||||
|
#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
|
||||||
|
|
||||||
|
struct uvio_list_secret_entry {
|
784
s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Normal file
784
s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Normal file
@ -0,0 +1,784 @@
|
|||||||
|
From 95bf7eb285f39a8f827cc013393cc69b1265cd68 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Thu, 15 Feb 2024 15:14:04 +0100
|
||||||
|
Subject: [PATCH] zkey: Add the 'pvsecrets import' command
|
||||||
|
|
||||||
|
The 'pvsecrets import' command imports a protected virtualization secret
|
||||||
|
into the zkey key repository. Like other key import or key generation
|
||||||
|
commands, additional information can be associated with the imported key,
|
||||||
|
such as a textual description, the volume to encrypt with together with
|
||||||
|
the volume type, the sector size, and a dummy passphrase. You can not
|
||||||
|
associate a set of APQNs, since a protected virtualization secret does
|
||||||
|
not need or use a crypto card.
|
||||||
|
|
||||||
|
This command only works when running in a secure execution guest.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/keystore.c | 112 +++++++++++++++++++++++----------
|
||||||
|
zkey/keystore.h | 9 ++-
|
||||||
|
zkey/pvsecrets.c | 126 +++++++++++++++++++++++++++++++++++++
|
||||||
|
zkey/pvsecrets.h | 7 +++
|
||||||
|
zkey/zkey.1 | 134 ++++++++++++++++++++++++++++++++++++++++
|
||||||
|
zkey/zkey.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
6 files changed, 513 insertions(+), 32 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||||
|
index 771bc08df..cde0caf58 100644
|
||||||
|
--- a/zkey/keystore.c
|
||||||
|
+++ b/zkey/keystore.c
|
||||||
|
@@ -2259,9 +2259,11 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- * Imports a secure key from a file and adds it to the key store
|
||||||
|
+ * Imports a secure key from a buffer and adds it to the key store
|
||||||
|
*
|
||||||
|
* @param[in] keystore the key store
|
||||||
|
+ * @param[in] secure_key the buffer containing the key
|
||||||
|
+ * @param[in] secure_key_size the size of the key
|
||||||
|
* @param[in] name the name of the key
|
||||||
|
* @param[in] description textual description of the key (optional, can be NULL)
|
||||||
|
* @param[in] volumes a comma separated list of volumes associated with this
|
||||||
|
@@ -2274,7 +2276,6 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||||
|
* of two and in range 512 - 4096 bytes. 0 means that
|
||||||
|
* the sector size is not specified and the system
|
||||||
|
* default is used.
|
||||||
|
- * @param[in] import_file The name of a secure key containing the key to import
|
||||||
|
* @param[in] volume_type the type of volume
|
||||||
|
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||||
|
* @param[in] passphrase_file the file name of a file containing a passphrase
|
||||||
|
@@ -2283,25 +2284,23 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||||
|
*
|
||||||
|
* @returns 0 for success or a negative errno in case of an error
|
||||||
|
*/
|
||||||
|
-int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
- const char *description, const char *volumes,
|
||||||
|
- const char *apqns, bool noapqncheck, size_t sector_size,
|
||||||
|
- const char *import_file, const char *volume_type,
|
||||||
|
- bool gen_passphrase, const char *passphrase_file,
|
||||||
|
- struct ext_lib *lib)
|
||||||
|
+int keystore_import(struct keystore *keystore, unsigned char *secure_key,
|
||||||
|
+ size_t secure_key_size, const char *name,
|
||||||
|
+ const char *description, const char *volumes,
|
||||||
|
+ const char *apqns, bool noapqncheck, size_t sector_size,
|
||||||
|
+ const char *volume_type, bool gen_passphrase,
|
||||||
|
+ const char *passphrase_file, struct ext_lib *lib)
|
||||||
|
{
|
||||||
|
struct key_filenames file_names = { 0 };
|
||||||
|
struct properties *key_props = NULL;
|
||||||
|
- size_t secure_key_size;
|
||||||
|
const char *key_type;
|
||||||
|
u8 mkvp[MKVP_LENGTH];
|
||||||
|
int selected = 1;
|
||||||
|
- u8 *secure_key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
util_assert(keystore != NULL, "Internal error: keystore is NULL");
|
||||||
|
util_assert(name != NULL, "Internal error: name is NULL");
|
||||||
|
- util_assert(import_file != NULL, "Internal error: import_file is NULL");
|
||||||
|
+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
||||||
|
|
||||||
|
rc = _keystore_get_key_filenames(keystore, name, &file_names);
|
||||||
|
if (rc != 0)
|
||||||
|
@@ -2311,27 +2310,29 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
if (rc != 0)
|
||||||
|
goto out_free_key_filenames;
|
||||||
|
|
||||||
|
- secure_key = read_secure_key(import_file, &secure_key_size,
|
||||||
|
- keystore->verbose);
|
||||||
|
- if (secure_key == NULL) {
|
||||||
|
- rc = -ENOENT;
|
||||||
|
- goto out_free_key_filenames;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
key_type = get_key_type(secure_key, secure_key_size);
|
||||||
|
if (key_type == NULL) {
|
||||||
|
warnx("Key '%s' is not a valid secure key", name);
|
||||||
|
- free(secure_key);
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out_free_key_filenames;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ if (apqns != NULL) {
|
||||||
|
+ warnx("No APQNs can be associated with keys of type %s",
|
||||||
|
+ key_type);
|
||||||
|
+ rc = -EINVAL;
|
||||||
|
+ goto out_free_props;
|
||||||
|
+ }
|
||||||
|
+ goto write_key;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
||||||
|
mkvp, keystore->verbose);
|
||||||
|
if (rc != 0) {
|
||||||
|
warnx("Failed to get the master key verification pattern: %s",
|
||||||
|
strerror(-rc));
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = cross_check_apqns(apqns, mkvp,
|
||||||
|
@@ -2340,17 +2341,17 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
get_card_type_for_keytype(key_type),
|
||||||
|
true, keystore->verbose);
|
||||||
|
if (rc == -EINVAL)
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
|
||||||
|
warnx("Your master key setup is improper");
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
|
||||||
|
if (lib->cca->lib_csulcca == NULL) {
|
||||||
|
rc = load_cca_library(lib->cca, keystore->verbose);
|
||||||
|
if (rc != 0)
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns,
|
||||||
|
@@ -2365,7 +2366,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
warnx("No APQN found that is suitable for "
|
||||||
|
"working with the secure AES key '%s'", name);
|
||||||
|
rc = 0;
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
|
||||||
|
@@ -2375,7 +2376,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
"key: %s", strerror(-rc));
|
||||||
|
if (!selected)
|
||||||
|
print_msg_for_cca_envvars("secure AES key");
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = check_aes_cipher_key(secure_key, secure_key_size);
|
||||||
|
@@ -2386,15 +2387,14 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
if (!prompt_for_yes(keystore->verbose)) {
|
||||||
|
warnx("Operation aborted");
|
||||||
|
rc = -ECANCELED;
|
||||||
|
- goto out_free_key;
|
||||||
|
+ goto out_free_props;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+write_key:
|
||||||
|
rc = write_secure_key(file_names.skey_filename, secure_key,
|
||||||
|
secure_key_size, keystore->verbose);
|
||||||
|
- free(secure_key);
|
||||||
|
- secure_key = NULL;
|
||||||
|
if (rc != 0)
|
||||||
|
goto out_free_props;
|
||||||
|
|
||||||
|
@@ -2414,9 +2414,6 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
"Successfully imported a secure key in '%s' and key info in '%s'",
|
||||||
|
file_names.skey_filename, file_names.info_filename);
|
||||||
|
|
||||||
|
-out_free_key:
|
||||||
|
- if (secure_key != NULL)
|
||||||
|
- free(secure_key);
|
||||||
|
out_free_props:
|
||||||
|
if (key_props != NULL)
|
||||||
|
properties_free(key_props);
|
||||||
|
@@ -2431,6 +2428,59 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * Imports a secure key from a file and adds it to the key store
|
||||||
|
+ *
|
||||||
|
+ * @param[in] keystore the key store
|
||||||
|
+ * @param[in] name the name of the key
|
||||||
|
+ * @param[in] description textual description of the key (optional, can be NULL)
|
||||||
|
+ * @param[in] volumes a comma separated list of volumes associated with this
|
||||||
|
+ * key (optional, can be NULL)
|
||||||
|
+ * @param[in] apqns a comma separated list of APQNs associated with this
|
||||||
|
+ * key (optional, can be NULL)
|
||||||
|
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||||
|
+ * existence and type.
|
||||||
|
+ * @param[in] sector_size the sector size to use with dm-crypt. It must be a
|
||||||
|
+ * power of two and in range 512 - 4096 bytes. 0 means
|
||||||
|
+ * that the sector size is not specified and the system
|
||||||
|
+ * default is used.
|
||||||
|
+ * @param[in] import_file The name of a secure key containing the key to import
|
||||||
|
+ * @param[in] volume_type the type of volume
|
||||||
|
+ * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||||
|
+ * @param[in] passphrase_file the file name of a file containing a passphrase
|
||||||
|
+ * for LUKS2 (optional, can be NULL)
|
||||||
|
+ * @param[in] lib the external library struct
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 for success or a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
+ const char *description, const char *volumes,
|
||||||
|
+ const char *apqns, bool noapqncheck, size_t sector_size,
|
||||||
|
+ const char *import_file, const char *volume_type,
|
||||||
|
+ bool gen_passphrase, const char *passphrase_file,
|
||||||
|
+ struct ext_lib *lib)
|
||||||
|
+{
|
||||||
|
+ size_t secure_key_size;
|
||||||
|
+ u8 *secure_key;
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ util_assert(import_file != NULL, "Internal error: import_file is NULL");
|
||||||
|
+
|
||||||
|
+ secure_key = read_secure_key(import_file, &secure_key_size,
|
||||||
|
+ keystore->verbose);
|
||||||
|
+ if (secure_key == NULL)
|
||||||
|
+ return -ENOENT;
|
||||||
|
+
|
||||||
|
+ rc = keystore_import(keystore, secure_key, secure_key_size, name,
|
||||||
|
+ description, volumes, apqns, noapqncheck,
|
||||||
|
+ sector_size, volume_type, gen_passphrase,
|
||||||
|
+ passphrase_file, lib);
|
||||||
|
+
|
||||||
|
+ if (secure_key != NULL)
|
||||||
|
+ free(secure_key);
|
||||||
|
+
|
||||||
|
+ return rc;
|
||||||
|
+}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes properties of a key in the keystore.
|
||||||
|
diff --git a/zkey/keystore.h b/zkey/keystore.h
|
||||||
|
index 1443b5df4..b4cae9aae 100644
|
||||||
|
--- a/zkey/keystore.h
|
||||||
|
+++ b/zkey/keystore.h
|
||||||
|
@@ -3,7 +3,7 @@
|
||||||
|
*
|
||||||
|
* Keystore handling functions
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2018, 2020
|
||||||
|
+ * Copyright IBM Corp. 2018, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -65,6 +65,13 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||||
|
struct kms_option *kms_options,
|
||||||
|
size_t num_kms_options);
|
||||||
|
|
||||||
|
+int keystore_import(struct keystore *keystore, unsigned char *secure_key,
|
||||||
|
+ size_t secure_key_size, const char *name,
|
||||||
|
+ const char *description, const char *volumes,
|
||||||
|
+ const char *apqns, bool noapqncheck, size_t sector_size,
|
||||||
|
+ const char *volume_type, bool gen_passphrase,
|
||||||
|
+ const char *passphrase_file, struct ext_lib *lib);
|
||||||
|
+
|
||||||
|
int keystore_import_key(struct keystore *keystore, const char *name,
|
||||||
|
const char *description, const char *volumes,
|
||||||
|
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||||
|
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
|
||||||
|
index 7f28febad..a4b3a5a83 100644
|
||||||
|
--- a/zkey/pvsecrets.c
|
||||||
|
+++ b/zkey/pvsecrets.c
|
||||||
|
@@ -552,3 +552,129 @@ int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+struct build_secret_key_blob_data {
|
||||||
|
+ unsigned char id[UV_SECRET_ID_LEN];
|
||||||
|
+ char name[UV_SECRET_ID_LEN];
|
||||||
|
+ struct pvsecrettoken token;
|
||||||
|
+ bool found;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Callback used to generate a pvsecrets key blob for a specific secret ID.
|
||||||
|
+ * Called for each secret.
|
||||||
|
+ *
|
||||||
|
+ * @param idx the index of the secret
|
||||||
|
+ * @param type the type of the secret
|
||||||
|
+ * @param id the ID of the secret
|
||||||
|
+ * @param cb_private callback private data
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 on success, a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+static int pvsecrets_build_key_blob_cb(u16 UNUSED(idx), u16 type, u32 len,
|
||||||
|
+ const u8 id[UV_SECRET_ID_LEN],
|
||||||
|
+ void *cb_private)
|
||||||
|
+{
|
||||||
|
+ struct build_secret_key_blob_data *build_blob_data = cb_private;
|
||||||
|
+
|
||||||
|
+ if (build_blob_data->found)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ if (memcmp(id, build_blob_data->name, UV_SECRET_ID_LEN) != 0 &&
|
||||||
|
+ memcmp(id, build_blob_data->id, UV_SECRET_ID_LEN) != 0)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ memset(&build_blob_data->token, 0, sizeof(build_blob_data->token));
|
||||||
|
+ build_blob_data->token.hdr.type = TOKEN_TYPE_NON_CCA;
|
||||||
|
+ build_blob_data->token.hdr.version = TOKEN_VERSION_PVSECRET;
|
||||||
|
+ build_blob_data->token.secret_type = type;
|
||||||
|
+ build_blob_data->token.secret_len = len;
|
||||||
|
+ memcpy(build_blob_data->token.secretid, id, UV_SECRET_ID_LEN);
|
||||||
|
+
|
||||||
|
+ build_blob_data->found = true;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Imports a protected virtualization secure key from the UV and adds it to the
|
||||||
|
+ * key store
|
||||||
|
+ *
|
||||||
|
+ * @param keystore the key store
|
||||||
|
+ * @param uv_fd the file descriptor of the ultravisor device
|
||||||
|
+ * @param secret_id the secret id as 32 byte hex string. Can be NULL if
|
||||||
|
+ * secret_name is non-NULL.
|
||||||
|
+ * @param secret_name the secret name. Can be NULL if secret_id is non-NULL.
|
||||||
|
+ * @param name the name of the key in the repository
|
||||||
|
+ * @param description textual description of the key (optional, can be NULL)
|
||||||
|
+ * @param volumes a comma separated list of volumes associated with this
|
||||||
|
+ * key (optional, can be NULL)
|
||||||
|
+ * @param volume_type the type of volume
|
||||||
|
+ * @param sector_size the sector size to use with dm-crypt. It must be a
|
||||||
|
+ * power of two and in range 512 - 4096 bytes. 0 means
|
||||||
|
+ * that the sector size is not specified and the system
|
||||||
|
+ * default is used.
|
||||||
|
+ * @param gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||||
|
+ * @param passphrase_file the file name of a file containing a passphrase
|
||||||
|
+ * for LUKS2 (optional, can be NULL)
|
||||||
|
+ * @param verbose if true, verbose messages are printed
|
||||||
|
+ *
|
||||||
|
+ * @returns 0 for success or a negative errno in case of an error
|
||||||
|
+ */
|
||||||
|
+int pvsecrets_import(struct keystore *keystore, int uv_fd,
|
||||||
|
+ const char *secret_id, const char *secret_name,
|
||||||
|
+ const char *name, const char *description,
|
||||||
|
+ const char *volumes, const char *volume_type,
|
||||||
|
+ long sector_size, bool gen_passphrase,
|
||||||
|
+ const char *passphrase_file, bool verbose)
|
||||||
|
+{
|
||||||
|
+ struct build_secret_key_blob_data build_blob_data = { 0 };
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ util_assert(keystore != NULL, "Internal error: keystore is NULL");
|
||||||
|
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
|
||||||
|
+ util_assert(secret_id != NULL || secret_name != NULL,
|
||||||
|
+ "Internal error: secret_id and secrest_name is NULL");
|
||||||
|
+ util_assert(name != NULL, "Internal error: name is NULL");
|
||||||
|
+
|
||||||
|
+ rc = get_secret_id_from_hex_or_name(secret_id, secret_name,
|
||||||
|
+ build_blob_data.id,
|
||||||
|
+ build_blob_data.name);
|
||||||
|
+ if (rc < 0)
|
||||||
|
+ return rc;
|
||||||
|
+ if (rc > 0)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ rc = uv_list_secrets(uv_fd, pvsecrets_build_key_blob_cb,
|
||||||
|
+ &build_blob_data, verbose);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Failed to import the pvsecret with %s '%s': %s",
|
||||||
|
+ secret_id != NULL ? "id" : "name",
|
||||||
|
+ secret_id != NULL ? secret_id : secret_name,
|
||||||
|
+ strerror(-rc));
|
||||||
|
+ return rc;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!build_blob_data.found) {
|
||||||
|
+ warnx("The pvsecret with %s '%s' does not exist",
|
||||||
|
+ secret_id != NULL ? "id" : "name",
|
||||||
|
+ secret_id != NULL ? secret_id : secret_name);
|
||||||
|
+ return -ENOENT;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!is_pvsecret_type_supported(build_blob_data.token.secret_type)) {
|
||||||
|
+ warnx("The type of the pvsecret with %s '%s' is not supported "
|
||||||
|
+ "by zkey: %s", secret_id != NULL ? "id" : "name",
|
||||||
|
+ secret_id != NULL ? secret_id : secret_name,
|
||||||
|
+ get_pvsecret_type_name(
|
||||||
|
+ build_blob_data.token.secret_type));
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token,
|
||||||
|
+ sizeof(build_blob_data.token), name, description,
|
||||||
|
+ volumes, NULL, false, sector_size, volume_type,
|
||||||
|
+ gen_passphrase, passphrase_file, NULL);
|
||||||
|
+
|
||||||
|
+ return rc;
|
||||||
|
+}
|
||||||
|
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||||
|
index ad8440350..9503c5155 100644
|
||||||
|
--- a/zkey/pvsecrets.h
|
||||||
|
+++ b/zkey/pvsecrets.h
|
||||||
|
@@ -16,6 +16,7 @@
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#include "pkey.h"
|
||||||
|
+#include "keystore.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for the /dev/uv kernel module interface
|
||||||
|
@@ -89,5 +90,11 @@ int uv_open_device(bool verbose);
|
||||||
|
int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
|
||||||
|
const char *secret_id, const char *secret_name,
|
||||||
|
bool verbose);
|
||||||
|
+int pvsecrets_import(struct keystore *keystore, int uv_fd,
|
||||||
|
+ const char *secret_id, const char *secret_name,
|
||||||
|
+ const char *name, const char *description,
|
||||||
|
+ const char *volumes, const char *volume_type,
|
||||||
|
+ long sector_size, bool gen_passphrase,
|
||||||
|
+ const char *passphrase_file, bool verbose);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||||
|
index 4386629f9..ba71a839b 100644
|
||||||
|
--- a/zkey/zkey.1
|
||||||
|
+++ b/zkey/zkey.1
|
||||||
|
@@ -1208,6 +1208,64 @@ are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the
|
||||||
|
This command is only available when running in a secure execution guest.
|
||||||
|
Only the \fBroot\fP user is allowed to perform this command.
|
||||||
|
.
|
||||||
|
+.SS "Import protected virtualization secrets into the repository"
|
||||||
|
+.
|
||||||
|
+.B zkey pvsecrets
|
||||||
|
+.BR import | im
|
||||||
|
+.RB [ \-\-pvsecret\-id | \-I
|
||||||
|
+.IR pvsecret\-id ]
|
||||||
|
+.RB [ \-\-pvsecret\-name | \-e
|
||||||
|
+.IR pvsecret\-name ]
|
||||||
|
+.B \-\-name | \-N
|
||||||
|
+.IR key\-name
|
||||||
|
+.RB [ \-\-description | \-d
|
||||||
|
+.IR description ]
|
||||||
|
+.RB [ \-\-volumes | \-l
|
||||||
|
+.IR volume1:dmname1[,volume2:dmname2[,...]] ]
|
||||||
|
+.RB [ \-\-sector\-size | \-S
|
||||||
|
+.IR bytes ]
|
||||||
|
+.RB [ \-\-volume\-type | \-t
|
||||||
|
+.IR type ]
|
||||||
|
+.RB [ \-\-gen\-dummy\-passphrase ]
|
||||||
|
+.RB [ \-\-set\-dummy\-passphrase
|
||||||
|
+.IR passphrase\-file ]
|
||||||
|
+.RB [ \-\-verbose | \-V ]
|
||||||
|
+.
|
||||||
|
+.PP
|
||||||
|
+Use the
|
||||||
|
+.B pvsecrets import
|
||||||
|
+command to import a protected virtualization (PV) secret into the repository.
|
||||||
|
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP
|
||||||
|
+option can be specified. If the pvsecret name is specified, then the
|
||||||
|
+\fB\-\-name\fP option can be omitted. The name of the protected virtualization
|
||||||
|
+secret key object in the repository will then be the same as the pvsecret name.
|
||||||
|
+.PP
|
||||||
|
+You can use the YAML file that was created when using the \fBpvsecret create\fP
|
||||||
|
+command for adding the protected virtualization secret:
|
||||||
|
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP or
|
||||||
|
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
|
||||||
|
+You might have to install the \fByq\fP package first.
|
||||||
|
+.PP
|
||||||
|
+A protected virtualization secret key object does not contain the key material,
|
||||||
|
+but only a reference (i.e. the secret ID) to the key in the ultravisor.
|
||||||
|
+When such a protected virtualization secret key object is used with
|
||||||
|
+\fBdm\-crypt\fP and the \fBPAES\fP kernel cipher, the key material (i.e. a
|
||||||
|
+protected key) is retrieved from the ultravisor and the crypto operation is
|
||||||
|
+performed with it.
|
||||||
|
+.PP
|
||||||
|
+When importing a protected virtualization secret in a key repository,
|
||||||
|
+additional information can be associated with it using the
|
||||||
|
+.B \-\-description
|
||||||
|
+,
|
||||||
|
+.B \-\-volumes
|
||||||
|
+, or the
|
||||||
|
+.B \-\-sector\-size
|
||||||
|
+options. APQNs can not be associated, because protected virtualization secrets
|
||||||
|
+do not require a crypto card.
|
||||||
|
+.PP
|
||||||
|
+This command is only available when running in a secure execution guest.
|
||||||
|
+Only the \fBroot\fP user is allowed to perform this command.
|
||||||
|
+.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
@@ -2117,6 +2175,82 @@ can be specified, but not both.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
+.SS "Options for the pvsecrets import command"
|
||||||
|
+.TP
|
||||||
|
+.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP
|
||||||
|
+ID of the protected virtualization (PV) secret to import. The pvsecret ID is a
|
||||||
|
+32 byte hex string, optionally prefixed by \fB0x\fP. You can use the YAML file
|
||||||
|
+that was created when using the \fBpvsecret create\fP command for adding the
|
||||||
|
+protected virtualization secret:
|
||||||
|
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP.
|
||||||
|
+You might have to install the \fByq\fP package first.
|
||||||
|
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
|
||||||
|
+can be specified, but not both.
|
||||||
|
+.TP
|
||||||
|
+.BR \-e ", " \-\-pvsecret\-name\~\fIpvsecret\-name\fP
|
||||||
|
+Name of the protected virtualization (PV) secret to import. You can use the YAML
|
||||||
|
+file that was created when using the \fBpvsecret create\fP command for adding
|
||||||
|
+the protected virtualization secret:
|
||||||
|
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
|
||||||
|
+You might have to install the \fByq\fP package first.
|
||||||
|
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
|
||||||
|
+can be specified, but not both. If the \fB\-\-pvsecret\-name\fP option is
|
||||||
|
+specified, then the \fB\-\-name\fP option can be omitted. The name of the
|
||||||
|
+protected virtualization secret key object in the repository will then be the
|
||||||
|
+same as the pvsecret name.
|
||||||
|
+.TP
|
||||||
|
+.BR \-N ", " \-\-name\~\fIkey\-name\fP
|
||||||
|
+Specifies the name of the protected virtualization secret key object in the key
|
||||||
|
+repository. If the \fB\-\-pvsecret\-name\fP option is specified, then the
|
||||||
|
+\fB\-\-name\fP option can be omitted. The name of the protected virtualization
|
||||||
|
+secret in the repository will then be the same as the pvsecret name.
|
||||||
|
+.TP
|
||||||
|
+.BR \-d ", " \-\-description\~\fIdescription\fP
|
||||||
|
+Specifies a textual description for the protected virtualization secret in the
|
||||||
|
+key repository.
|
||||||
|
+.TP
|
||||||
|
+.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1[,volume2:dmname2[,...]]\fP
|
||||||
|
+Specifies a comma-separated list of volumes (block devices) which are
|
||||||
|
+associated with the protected virtualization secret in the repository. These
|
||||||
|
+volumes are to be encrypted using \fBdm\-crypt\fP with the protected
|
||||||
|
+virtualization secret. The volume association also contains the device-mapper
|
||||||
|
+name, separated by a colon, used with \fBdm\-crypt\fP. A specific volume can
|
||||||
|
+only be associated with a single key.
|
||||||
|
+.TP
|
||||||
|
+.BR \-S ", " \-\-sector\-size\~\fIbytes\fP
|
||||||
|
+Specifies the sector size in bytes used with \fBdm\-crypt\fP. It must be a power
|
||||||
|
+of two and in the range of 512 to 4096 bytes. If omitted, the system default
|
||||||
|
+sector size is used.
|
||||||
|
+.TP
|
||||||
|
+.BR \-t ", " \-\-volume\-type\~\fItype\fP
|
||||||
|
+Specifies the volume type of the associated volumes used with \fBdm\-crypt\fP.
|
||||||
|
+Possible values are \fBplain\fP and \fBluks2\fP. If omitted, \fBluks2\fP is
|
||||||
|
+used. This option is only available if
|
||||||
|
+.B zkey
|
||||||
|
+has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled,
|
||||||
|
+the default volume type is \fBplain\fP.
|
||||||
|
+.TP
|
||||||
|
+.BR \-\-gen\-dummy\-passphrase
|
||||||
|
+Generate a dummy passphrase randomly and associate it with the protected
|
||||||
|
+virtualization secret used to encrypt LUKS2 volume(s). The LUKS2 passphrase is
|
||||||
|
+of less or no relevance for the security of the volume(s), when a protected
|
||||||
|
+virtualization secret is used to encrypt the volume(s), and can therefore be
|
||||||
|
+stored insecurely inside the key repository. If for a certain usage the
|
||||||
|
+passphrase is of relevance for security, then do not use this option. This
|
||||||
|
+option can only be specified for keys with a volume type of \fBluks2\fP.
|
||||||
|
+.TP
|
||||||
|
+.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP
|
||||||
|
+Set a dummy passphrase that is read from the specified file and associate it
|
||||||
|
+with the protected virtualization secret used to encrypt LUKS2 volume(s).
|
||||||
|
+The LUKS2 passphrase is of less or no relevance for the security of the
|
||||||
|
+volume(s), when an protected virtualization secret is used to encrypt the
|
||||||
|
+volume(s), and can therefore be stored insecurely inside the key repository.
|
||||||
|
+If for a certain usage the passphrase is of relevance for security, then do
|
||||||
|
+not use this option. This option can only be specified for keys with a volume
|
||||||
|
+type of \fBluks2\fP.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
+.
|
||||||
|
.SS "General options"
|
||||||
|
.TP
|
||||||
|
.BR \-V ", " \-\-verbose
|
||||||
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||||
|
index adc48d60b..6e9b32af5 100644
|
||||||
|
--- a/zkey/zkey.c
|
||||||
|
+++ b/zkey/zkey.c
|
||||||
|
@@ -145,6 +145,7 @@ static struct zkey_globals {
|
||||||
|
#define COMMAND_KMS_REFRESH "refresh"
|
||||||
|
#define COMMAND_PVSECRETS "pvsecrets"
|
||||||
|
#define COMMAND_PVSECRETS_LIST "list"
|
||||||
|
+#define COMMAND_PVSECRETS_IMPORT "import"
|
||||||
|
|
||||||
|
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
|
||||||
|
|
||||||
|
@@ -1234,6 +1235,103 @@ static struct util_opt opt_vec[] = {
|
||||||
|
.command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
||||||
|
},
|
||||||
|
/***********************************************************/
|
||||||
|
+ {
|
||||||
|
+ .flags = UTIL_OPT_FLAG_SECTION,
|
||||||
|
+ .desc = "OPTIONS",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "pvsecret-id", required_argument, NULL, 'I'},
|
||||||
|
+ .argument = "PVSECRET-ID",
|
||||||
|
+ .desc = "ID of the protected virtualization (PV) secret to "
|
||||||
|
+ "import. Either '--pvsecret-id/-I' or "
|
||||||
|
+ "'--pvsecret-name/-e' can be specified, but not both.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "pvsecret-name", required_argument, NULL, 'e'},
|
||||||
|
+ .argument = "PVSECRET-NAME",
|
||||||
|
+ .desc = "Name of the protected virtualization (PV) secret to "
|
||||||
|
+ "import. Either '--pvsecret-name/-e' or "
|
||||||
|
+ "'--pvsecret-id/-I' can be specified, but not both. "
|
||||||
|
+ "If the '--pvsecret-name/-e' option is specified, but "
|
||||||
|
+ "the '--name/-N' option is omitted, then the imported "
|
||||||
|
+ "protected virtualisation secret will be named the "
|
||||||
|
+ "same as the pvsecret name.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "name", required_argument, NULL, 'N'},
|
||||||
|
+ .argument = "NAME",
|
||||||
|
+ .desc = "Name of the imported protected virtualisation secret "
|
||||||
|
+ "in the repository. If the '--name/-N' option is "
|
||||||
|
+ "omitted, but the '--pvsecret-name/-e' is specified, "
|
||||||
|
+ "then the imported protected virtualisation secret "
|
||||||
|
+ "will be named the same as the pvsecret name.",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "description", required_argument, NULL, 'd'},
|
||||||
|
+ .argument = "DESCRIPTION",
|
||||||
|
+ .desc = "Textual description of the protected virtualisation "
|
||||||
|
+ "secret in the repository",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "volumes", required_argument, NULL, 'l'},
|
||||||
|
+ .argument = "VOLUME:DMNAME[,...]",
|
||||||
|
+ .desc = "Comma-separated pairs of volume and device-mapper "
|
||||||
|
+ "names that are associated with the protected "
|
||||||
|
+ "virtualisation secret in the repository",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "sector-size", required_argument, NULL, 'S'},
|
||||||
|
+ .argument = "512|4096",
|
||||||
|
+ .desc = "The sector size used with dm-crypt. It must be a power "
|
||||||
|
+ "of two and in range 512 - 4096 bytes. If this option "
|
||||||
|
+ "is omitted, the system default sector size (512) is "
|
||||||
|
+ "used",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+#ifdef HAVE_LUKS2_SUPPORT
|
||||||
|
+ {
|
||||||
|
+ .option = { "volume-type", required_argument, NULL, 't'},
|
||||||
|
+ .argument = "type",
|
||||||
|
+ .desc = "The type of the associated volume(s). Possible values "
|
||||||
|
+ "are 'plain' and 'luks2'. When this option is omitted, "
|
||||||
|
+ "the default is 'luks2'",
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+#endif
|
||||||
|
+ {
|
||||||
|
+ .option = { "gen-dummy-passphrase", 0, NULL,
|
||||||
|
+ OPT_GEN_DUMMY_PASSPHRASE},
|
||||||
|
+ .desc = "Generate a dummy passphrase and associate it with the "
|
||||||
|
+ "protected virtualisation secret used to encrypt LUKS2 "
|
||||||
|
+ "volume(s). The LUKS2 passphrase is of less or no "
|
||||||
|
+ "relevance for the security of the volume(s), when an "
|
||||||
|
+ "protected virtualisation secret is used to encrypt "
|
||||||
|
+ "the volume(s), and can therefore be stored insecurely "
|
||||||
|
+ "inside the secure key repository.",
|
||||||
|
+ .flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .option = { "set-dummy-passphrase", required_argument, NULL,
|
||||||
|
+ OPT_SET_DUMMY_PASSPHRASE},
|
||||||
|
+ .argument = "passphrase-file",
|
||||||
|
+ .desc = "Set a dummy passphrase to be associated with the "
|
||||||
|
+ "protected virtualisation secret used to encrypt LUKS2 "
|
||||||
|
+ "volume(s). The LUKS2 passphrase is of less or no "
|
||||||
|
+ "relevance for the security of the volume(s), when a "
|
||||||
|
+ "protected virtualisation secret is used to encrypt "
|
||||||
|
+ "the volume(s), and can therefore be stored insecurely "
|
||||||
|
+ "inside the secure key repository.",
|
||||||
|
+ .flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ },
|
||||||
|
+ /***********************************************************/
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
OPT_PLACEHOLDER,
|
||||||
|
@@ -1339,6 +1437,7 @@ static int command_kms_list(void);
|
||||||
|
static int command_kms_import(void);
|
||||||
|
static int command_kms_refresh(void);
|
||||||
|
static int command_pvsecrets_list(void);
|
||||||
|
+static int command_pvsecrets_import(void);
|
||||||
|
|
||||||
|
static struct zkey_command zkey_kms_commands[] = {
|
||||||
|
{
|
||||||
|
@@ -1469,6 +1568,19 @@ static struct zkey_command zkey_pvsecrets_commands[] = {
|
||||||
|
.has_options = 1,
|
||||||
|
.need_uv_device = 1,
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ .command = COMMAND_PVSECRETS_IMPORT,
|
||||||
|
+ .abbrev_len = 2,
|
||||||
|
+ .function = command_pvsecrets_import,
|
||||||
|
+ .short_desc = "Imports a protected virtualization (PV) secret",
|
||||||
|
+ .long_desc = "Imports a protected virtualization (PV) secret "
|
||||||
|
+ "into the repository. This command is only "
|
||||||
|
+ "available when running in a secure execution "
|
||||||
|
+ "guest.",
|
||||||
|
+ .has_options = 1,
|
||||||
|
+ .need_keystore = 1,
|
||||||
|
+ .need_uv_device = 1,
|
||||||
|
+ },
|
||||||
|
{ .command = NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -3006,6 +3118,51 @@ static int command_pvsecrets_list(void)
|
||||||
|
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Command handler for 'pvsecrets import'.
|
||||||
|
+ *
|
||||||
|
+ * Import a protected virtualization secret into the repository
|
||||||
|
+ */
|
||||||
|
+static int command_pvsecrets_import(void)
|
||||||
|
+{
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ if (g.secret_id == NULL && g.secret_name == NULL) {
|
||||||
|
+ misc_print_required_parm("--pvsecret-id/-I or "
|
||||||
|
+ "--pvsecret-name/-e");
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
+ if (g.secret_id != NULL && g.secret_name != NULL) {
|
||||||
|
+ warnx("Either '--pvsecret-id/-I' or '--pvsecret-name/-e' can "
|
||||||
|
+ "be specified, but not both");
|
||||||
|
+ util_prg_print_parse_error();
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
+ if (g.secret_name == NULL && g.name == NULL) {
|
||||||
|
+ misc_print_required_parm("--name/-N");
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (g.sector_size < 0)
|
||||||
|
+ g.sector_size = 0;
|
||||||
|
+
|
||||||
|
+ if (g.gen_passphrase && g.passphrase_file != NULL) {
|
||||||
|
+ warnx("Either '--gen-dummy-passphrase' or "
|
||||||
|
+ "'--set-dummy-passphrase' can be specified, but not "
|
||||||
|
+ "both");
|
||||||
|
+ util_prg_print_parse_error();
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rc = pvsecrets_import(g.keystore, g.uv_fd, g.secret_id, g.secret_name,
|
||||||
|
+ g.name != NULL ? g.name : g.secret_name,
|
||||||
|
+ g.description, g.volumes, g.volume_type,
|
||||||
|
+ g.sector_size, g.gen_passphrase,
|
||||||
|
+ g.passphrase_file, g.verbose);
|
||||||
|
+
|
||||||
|
+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Opens the keystore. The keystore directory is either the
|
||||||
|
* default directory or as specified in an environment variable
|
@ -0,0 +1,137 @@
|
|||||||
|
From 5276d408fd10669b3d8e623455778a675e8dc149 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Mon, 19 Feb 2024 10:21:06 +0100
|
||||||
|
Subject: [PATCH] zkey: Reject key generation and APQN association for
|
||||||
|
PVSECRET-AES keys
|
||||||
|
|
||||||
|
Keys of type PVSECRET-AES can not be generated using 'zkey generate'.
|
||||||
|
Furthermore, APQNs can not be associated with keys of type PVSECRET-AES
|
||||||
|
via 'zkey change'. Reject that with a proper error message.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/keystore.c | 32 +++++++++++++++++++++++---------
|
||||||
|
zkey/zkey.1 | 7 +++++++
|
||||||
|
zkey/zkey.c | 5 +++++
|
||||||
|
3 files changed, 35 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||||
|
index cde0caf5..db62e0a6 100644
|
||||||
|
--- a/zkey/keystore.c
|
||||||
|
+++ b/zkey/keystore.c
|
||||||
|
@@ -2009,6 +2009,12 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (!is_secure_key_type(key_type)) {
|
||||||
|
+ warnx("Keys of type %s can not be generated. Use 'zkey "
|
||||||
|
+ "pvsecret import' instead", key_type);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rc = _keystore_get_key_filenames(keystore, name, &file_names);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out_free_key_filenames;
|
||||||
|
@@ -2535,9 +2541,9 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||||
|
const char *null_ptr = NULL;
|
||||||
|
char *upd_volumes = NULL;
|
||||||
|
size_t secure_key_size;
|
||||||
|
+ u8 *secure_key = NULL;
|
||||||
|
u8 mkvp[MKVP_LENGTH];
|
||||||
|
char sect_size[30];
|
||||||
|
- u8 *secure_key;
|
||||||
|
bool kms_bound;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
@@ -2589,13 +2595,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||||
|
- apqns, "APQN",
|
||||||
|
- _keystore_apqn_check,
|
||||||
|
- &apqn_check);
|
||||||
|
- if (rc != 0)
|
||||||
|
- goto out;
|
||||||
|
-
|
||||||
|
secure_key = read_secure_key(file_names.skey_filename,
|
||||||
|
&secure_key_size,
|
||||||
|
keystore->verbose);
|
||||||
|
@@ -2604,11 +2603,24 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ warnx("No APQNs can be associated with keys of type %s",
|
||||||
|
+ get_key_type(secure_key, secure_key_size));
|
||||||
|
+ rc = -EINVAL;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||||
|
+ apqns, "APQN",
|
||||||
|
+ _keystore_apqn_check,
|
||||||
|
+ &apqn_check);
|
||||||
|
+ if (rc != 0)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
rc = get_master_key_verification_pattern(secure_key,
|
||||||
|
secure_key_size,
|
||||||
|
mkvp,
|
||||||
|
keystore->verbose);
|
||||||
|
- free(secure_key);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
@@ -2742,6 +2754,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||||
|
free(upd_volumes);
|
||||||
|
if (upd_volume_type != NULL)
|
||||||
|
free(upd_volume_type);
|
||||||
|
+ if (secure_key != NULL)
|
||||||
|
+ free(secure_key);
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
pr_verbose(keystore, "Failed to change key '%s': %s",
|
||||||
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||||
|
index ba71a839..baaf8478 100644
|
||||||
|
--- a/zkey/zkey.1
|
||||||
|
+++ b/zkey/zkey.1
|
||||||
|
@@ -402,6 +402,9 @@ additional information can be associated with a secure key using the
|
||||||
|
.B \-\-sector\-size
|
||||||
|
options.
|
||||||
|
.PP
|
||||||
|
+Keys of type \fBPVSECRET\-AES\fP do not use a cryptographic adapter, thus APQNs
|
||||||
|
+can not be associated with them.
|
||||||
|
+.PP
|
||||||
|
.B Note:
|
||||||
|
The \fBimport\fP command requires the CCA host library (libcsulcca.so)
|
||||||
|
to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
|
||||||
|
@@ -564,6 +567,10 @@ APQNs that are associated with the key management system plugin.
|
||||||
|
Other associated information is also changed in the key management system when
|
||||||
|
changed using the change command.
|
||||||
|
.PP
|
||||||
|
+For keys of type \fBPVSECRET\-AES\fP you can not change or set the APQN
|
||||||
|
+association. These keys do not use a cryptographic adapter, thus APQNs can not
|
||||||
|
+be associated with them.
|
||||||
|
+.PP
|
||||||
|
.B Note:
|
||||||
|
The secure key itself cannot be changed, only information about the secure
|
||||||
|
key is changed. To rename a secure key, use the \fBrename\fP command.
|
||||||
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||||
|
index 6e9b32af..36bdbcc0 100644
|
||||||
|
--- a/zkey/zkey.c
|
||||||
|
+++ b/zkey/zkey.c
|
||||||
|
@@ -2001,6 +2001,11 @@ static int command_generate(void)
|
||||||
|
return command_generate_repository();
|
||||||
|
if (g.key_type == NULL)
|
||||||
|
g.key_type = KEY_TYPE_CCA_AESDATA;
|
||||||
|
+ if (!is_secure_key_type(g.key_type)) {
|
||||||
|
+ warnx("Keys of type '%s' can not be generated. Use 'zkey "
|
||||||
|
+ "pvsecret import' instead", g.key_type);
|
||||||
|
+ return -EXIT_FAILURE;
|
||||||
|
+ }
|
||||||
|
if (g.pos_arg != NULL) {
|
||||||
|
if (g.volumes != NULL) {
|
||||||
|
warnx("Option '--volumes|-l' is not valid for "
|
@ -0,0 +1,145 @@
|
|||||||
|
From a8eb2bd4e7e74445c953906b33d450c2ace5223f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Mon, 19 Feb 2024 11:26:41 +0100
|
||||||
|
Subject: [PATCH] zkey: Reject re-enciphering of PVSECRET-AES keys
|
||||||
|
|
||||||
|
Keys of type PVSECRET-AES can not be reenciphered using 'zkey reencipher'
|
||||||
|
or 'zkey-cryptsetup reencipher'. Reject that with a proper error message.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/keystore.c | 9 +++++++++
|
||||||
|
zkey/zkey-cryptsetup.1 | 8 ++++++--
|
||||||
|
zkey/zkey-cryptsetup.c | 16 ++++++++++++----
|
||||||
|
zkey/zkey.1 | 4 ++++
|
||||||
|
zkey/zkey.c | 7 +++++++
|
||||||
|
5 files changed, 38 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||||
|
index db62e0a6..4f795a28 100644
|
||||||
|
--- a/zkey/keystore.c
|
||||||
|
+++ b/zkey/keystore.c
|
||||||
|
@@ -3567,6 +3567,15 @@ static int _keystore_process_reencipher(struct keystore *keystore,
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ warnx("Key '%s' is of type %s and can not be re-enciphered, "
|
||||||
|
+ "skipping", name, get_key_type(secure_key,
|
||||||
|
+ secure_key_size));
|
||||||
|
+ info->num_skipped++;
|
||||||
|
+ rc = 0;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
apqns = properties_get(properties, PROP_NAME_APQNS);
|
||||||
|
if (apqns != NULL)
|
||||||
|
apqn_list = str_list_split(apqns);
|
||||||
|
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
|
||||||
|
index c455f845..185edab9 100644
|
||||||
|
--- a/zkey/zkey-cryptsetup.1
|
||||||
|
+++ b/zkey/zkey-cryptsetup.1
|
||||||
|
@@ -1,8 +1,8 @@
|
||||||
|
-.\" Copyright IBM Corp. 2018
|
||||||
|
+.\" Copyright IBM Corp. 2018, 2024
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
-.TH ZKEY\-CRYPTSETUP 1 "May 2018" "s390-tools"
|
||||||
|
+.TH ZKEY\-CRYPTSETUP 1 "February 2024" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
zkey\-cryptsetup \- Manage secure AES volume keys of volumes encrypted with
|
||||||
|
\fBLUKS2\fP and the \fBpaes\fP cipher
|
||||||
|
@@ -115,6 +115,10 @@ command to re-encipher a secure AES volume key of a volume encrypted with
|
||||||
|
re-enciphered when the master key of the cryptographic adapter in CCA or EP11
|
||||||
|
coprocessor mode changes.
|
||||||
|
.PP
|
||||||
|
+Volume keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do
|
||||||
|
+not use a cryptographic adapter, thus they do not need to be re-enciphered when
|
||||||
|
+the master key of a cryptographic adapter changes.
|
||||||
|
+.PP
|
||||||
|
The cryptographic adapter in CCA coprocessor mode has three different registers
|
||||||
|
to store master keys:
|
||||||
|
.RS 2
|
||||||
|
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
|
||||||
|
index 8b55f7d1..2b018a2a 100644
|
||||||
|
--- a/zkey/zkey-cryptsetup.c
|
||||||
|
+++ b/zkey/zkey-cryptsetup.c
|
||||||
|
@@ -2,7 +2,7 @@
|
||||||
|
* zkey-cryptsetup - Re-encipher or validate volume keys of volumes
|
||||||
|
* encrypted with LUKS2 and the paes cipher.
|
||||||
|
*
|
||||||
|
- * Copyright IBM Corp. 2018
|
||||||
|
+ * Copyright IBM Corp. 2018, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
@@ -82,7 +82,7 @@ static const struct util_prg prg = {
|
||||||
|
{
|
||||||
|
.owner = "IBM Corp.",
|
||||||
|
.pub_first = 2018,
|
||||||
|
- .pub_last = 2018,
|
||||||
|
+ .pub_last = 2024,
|
||||||
|
},
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
}
|
||||||
|
@@ -1609,14 +1609,22 @@ static int reencipher_prepare(int token)
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
+ securekeysize = keysize - integrity_keysize;
|
||||||
|
+
|
||||||
|
+ if (!is_secure_key((u8 *)key, securekeysize)) {
|
||||||
|
+ warnx("The volume key of device '%s' is of type %s and can "
|
||||||
|
+ "not be re-enciphered", g.pos_arg,
|
||||||
|
+ get_key_type((u8 *)key, securekeysize));
|
||||||
|
+ rc = -EINVAL;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
reenc_tok.original_keyslot = rc;
|
||||||
|
|
||||||
|
rc = ensure_is_active_keylot(reenc_tok.original_keyslot);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
- securekeysize = keysize - integrity_keysize;
|
||||||
|
-
|
||||||
|
rc = generate_key_verification_pattern((u8 *)key, securekeysize,
|
||||||
|
reenc_tok.verification_pattern,
|
||||||
|
sizeof(reenc_tok.verification_pattern),
|
||||||
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||||
|
index baaf8478..316db5f0 100644
|
||||||
|
--- a/zkey/zkey.1
|
||||||
|
+++ b/zkey/zkey.1
|
||||||
|
@@ -266,6 +266,10 @@ command to re-encipher an existing secure key with a new master key.
|
||||||
|
A secure key must be re-enciphered when the master key of the CCA or EP11
|
||||||
|
cryptographic adapter changes.
|
||||||
|
.PP
|
||||||
|
+Keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do not
|
||||||
|
+use a cryptographic adapter, thus they do not need to be re-enciphered when the
|
||||||
|
+master of a cryptographic adapter changes.
|
||||||
|
+.PP
|
||||||
|
The CCA cryptographic adapter has three different registers to store
|
||||||
|
master keys:
|
||||||
|
.RS 2
|
||||||
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||||
|
index 36bdbcc0..90b46106 100644
|
||||||
|
--- a/zkey/zkey.c
|
||||||
|
+++ b/zkey/zkey.c
|
||||||
|
@@ -2118,6 +2118,13 @@ static int command_reencipher_file(void)
|
||||||
|
if (secure_key == NULL)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ warnx("A key of type %s can not be re-enciphered",
|
||||||
|
+ get_key_type(secure_key, secure_key_size));
|
||||||
|
+ rc = EXIT_FAILURE;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
|
||||||
|
&is_old_mk, NULL, g.verbose);
|
||||||
|
if (rc != 0) {
|
@ -0,0 +1,407 @@
|
|||||||
|
From 833a8e7309ebf0ce70f2ee989ced5f87d6c3550b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Mon, 19 Feb 2024 10:25:54 +0100
|
||||||
|
Subject: [PATCH] zkey: Support validation of key of type PVSECRET-AES
|
||||||
|
|
||||||
|
Keys of type PVSECRET-AES can also be verified via the pkey IOCTL
|
||||||
|
PKEY_VERIFYKEY2, but the card and domain fields must be zero, because such
|
||||||
|
a key does not use a crypto card. Also XTS keys of type PVSRCRET-AES are
|
||||||
|
not represented by 2 concatenated keys but by just one key of type
|
||||||
|
PVSECRET-AES. Thus, special handling is required for XTS keys.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zkey/keystore.c | 50 ++++++++++++++++++++++--------------
|
||||||
|
zkey/pkey.c | 46 +++++++++++++++++++++-------------
|
||||||
|
zkey/zkey-cryptsetup.1 | 4 ++-
|
||||||
|
zkey/zkey-cryptsetup.c | 45 +++++++++++++++++++--------------
|
||||||
|
zkey/zkey.1 | 8 +++---
|
||||||
|
zkey/zkey.c | 57 ++++++++++++++++++++++++------------------
|
||||||
|
6 files changed, 126 insertions(+), 84 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||||
|
index 4f795a28..58f27df2 100644
|
||||||
|
--- a/zkey/keystore.c
|
||||||
|
+++ b/zkey/keystore.c
|
||||||
|
@@ -3055,19 +3055,25 @@ static void _keystore_print_record(struct util_rec *rec,
|
||||||
|
util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No");
|
||||||
|
util_rec_set(rec, REC_KEY_TYPE, key_type);
|
||||||
|
if (validation) {
|
||||||
|
- if (valid)
|
||||||
|
- util_rec_set(rec, REC_MASTERKEY,
|
||||||
|
- "%s master key (MKVP: %s)",
|
||||||
|
- is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
- printable_mkvp(
|
||||||
|
- get_card_type_for_keytype(key_type),
|
||||||
|
- mkvp));
|
||||||
|
- else
|
||||||
|
- util_rec_set(rec, REC_MASTERKEY,
|
||||||
|
- "(unknown, MKVP: %s)",
|
||||||
|
- printable_mkvp(
|
||||||
|
- get_card_type_for_keytype(key_type),
|
||||||
|
- mkvp));
|
||||||
|
+ if (mkvp != NULL) {
|
||||||
|
+ if (valid)
|
||||||
|
+ util_rec_set(rec, REC_MASTERKEY,
|
||||||
|
+ "%s master key (MKVP: %s)",
|
||||||
|
+ is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
+ printable_mkvp(
|
||||||
|
+ get_card_type_for_keytype(
|
||||||
|
+ key_type),
|
||||||
|
+ mkvp));
|
||||||
|
+ else
|
||||||
|
+ util_rec_set(rec, REC_MASTERKEY,
|
||||||
|
+ "(unknown, MKVP: %s)",
|
||||||
|
+ printable_mkvp(
|
||||||
|
+ get_card_type_for_keytype(
|
||||||
|
+ key_type),
|
||||||
|
+ mkvp));
|
||||||
|
+ } else {
|
||||||
|
+ util_rec_set(rec, REC_MASTERKEY, "(none)");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
if (volumes_argz != NULL)
|
||||||
|
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
|
||||||
|
@@ -3294,17 +3300,22 @@ static int _keystore_process_validate(struct keystore *keystore,
|
||||||
|
valid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
||||||
|
- mkvp, keystore->verbose);
|
||||||
|
- if (rc != 0)
|
||||||
|
- goto out;
|
||||||
|
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ rc = get_master_key_verification_pattern(secure_key,
|
||||||
|
+ secure_key_size,
|
||||||
|
+ mkvp,
|
||||||
|
+ keystore->verbose);
|
||||||
|
+ if (rc != 0)
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
_keystore_print_record(info->rec, name, properties, 1,
|
||||||
|
file_names->skey_filename, secure_key_size,
|
||||||
|
is_xts_key(secure_key, secure_key_size),
|
||||||
|
clear_key_bitsize, valid, is_old_mk,
|
||||||
|
_keystore_reencipher_key_exists(file_names),
|
||||||
|
- mkvp,
|
||||||
|
+ is_secure_key(secure_key, secure_key_size) ?
|
||||||
|
+ mkvp : NULL,
|
||||||
|
_keystore_passphrase_file_exists(file_names) ?
|
||||||
|
file_names->pass_filename : NULL);
|
||||||
|
|
||||||
|
@@ -3316,7 +3327,8 @@ static int _keystore_process_validate(struct keystore *keystore,
|
||||||
|
"master key\n", 0);
|
||||||
|
info->num_warnings++;
|
||||||
|
}
|
||||||
|
- if (info->noapqncheck == 0)
|
||||||
|
+ if (info->noapqncheck == 0 &&
|
||||||
|
+ is_secure_key(secure_key, secure_key_size))
|
||||||
|
if (_keystore_display_apqn_status(keystore, properties,
|
||||||
|
mkvp) != 0)
|
||||||
|
info->num_warnings++;
|
||||||
|
diff --git a/zkey/pkey.c b/zkey/pkey.c
|
||||||
|
index 53c0a550..25deb05a 100644
|
||||||
|
--- a/zkey/pkey.c
|
||||||
|
+++ b/zkey/pkey.c
|
||||||
|
@@ -1287,33 +1287,43 @@ int validate_secure_key(int pkey_fd,
|
||||||
|
{
|
||||||
|
struct pkey_verifykey2 verifykey2;
|
||||||
|
struct pkey_apqn *list = NULL;
|
||||||
|
+ bool xts, valid, securekey;
|
||||||
|
u32 i, list_entries = 0;
|
||||||
|
- bool xts, valid;
|
||||||
|
- u32 flags;
|
||||||
|
+ u32 flags = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
||||||
|
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
||||||
|
|
||||||
|
- xts = is_xts_key(secure_key, secure_key_size);
|
||||||
|
+ securekey = is_secure_key(secure_key, secure_key_size);
|
||||||
|
+ xts = securekey ? is_xts_key(secure_key, secure_key_size) : false;
|
||||||
|
|
||||||
|
- flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
||||||
|
- if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
||||||
|
- is_cca_aes_cipher_key(secure_key, secure_key_size))
|
||||||
|
- flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
|
||||||
|
+ if (securekey) {
|
||||||
|
+ flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
||||||
|
+ if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
||||||
|
+ is_cca_aes_cipher_key(secure_key, secure_key_size))
|
||||||
|
+ flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
|
||||||
|
|
||||||
|
- rc = build_apqn_list_for_key(pkey_fd, secure_key,
|
||||||
|
- HALF_KEYSIZE_FOR_XTS(secure_key_size, xts),
|
||||||
|
- flags, apqns, &list, &list_entries,
|
||||||
|
- verbose);
|
||||||
|
- if (rc != 0) {
|
||||||
|
- pr_verbose(verbose, "Failed to build a list of APQNs that can "
|
||||||
|
- "validate this secure key: %s", strerror(-rc));
|
||||||
|
- return rc;
|
||||||
|
+ rc = build_apqn_list_for_key(pkey_fd, secure_key,
|
||||||
|
+ HALF_KEYSIZE_FOR_XTS(
|
||||||
|
+ secure_key_size, xts),
|
||||||
|
+ flags, apqns, &list, &list_entries,
|
||||||
|
+ verbose);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ pr_verbose(verbose, "Failed to build a list of APQNs "
|
||||||
|
+ "that can validate this secure "
|
||||||
|
+ "key: %s", strerror(-rc));
|
||||||
|
+ return rc;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ list = util_malloc(sizeof(struct pkey_apqn));
|
||||||
|
+ list[0].card = 0;
|
||||||
|
+ list[0].domain = 0;
|
||||||
|
+ list_entries = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_old_mk != NULL)
|
||||||
|
- *is_old_mk = true;
|
||||||
|
+ *is_old_mk = securekey ? true : false;
|
||||||
|
if (clear_key_bitsize != NULL)
|
||||||
|
*clear_key_bitsize = 0;
|
||||||
|
|
||||||
|
@@ -1333,7 +1343,7 @@ int validate_secure_key(int pkey_fd,
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (is_xts_key(secure_key, secure_key_size)) {
|
||||||
|
+ if (xts) {
|
||||||
|
rc = validate_secure_xts_key(pkey_fd, &list[i],
|
||||||
|
secure_key,
|
||||||
|
secure_key_size,
|
||||||
|
@@ -1358,7 +1368,7 @@ int validate_secure_key(int pkey_fd,
|
||||||
|
* If at least one of the APQNs have a matching current MK,
|
||||||
|
* then don't report OLD, even if some match the old MK.
|
||||||
|
*/
|
||||||
|
- if (is_old_mk &&
|
||||||
|
+ if (securekey && is_old_mk &&
|
||||||
|
(verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP))
|
||||||
|
*is_old_mk = false;
|
||||||
|
}
|
||||||
|
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
|
||||||
|
index 185edab9..ffd600d4 100644
|
||||||
|
--- a/zkey/zkey-cryptsetup.1
|
||||||
|
+++ b/zkey/zkey-cryptsetup.1
|
||||||
|
@@ -68,7 +68,9 @@ It also displays the attributes of the secure key, such as key size, whether
|
||||||
|
it is a secure key that can be used for the XTS cipher mode, and the master key
|
||||||
|
register (CURRENT or OLD) with which the secure key is enciphered.
|
||||||
|
For further information about master key registers, see the
|
||||||
|
-\fBreencipher\fP command.
|
||||||
|
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
|
||||||
|
+cryptographic adapter, thus no master key information is displayed for such
|
||||||
|
+keys.
|
||||||
|
.PP
|
||||||
|
To open a key slot contained in the LUKS2 header of the volume, a passphrase is
|
||||||
|
required. You are prompted for the passphrase, unless option
|
||||||
|
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
|
||||||
|
index 2b018a2a..65716f3b 100644
|
||||||
|
--- a/zkey/zkey-cryptsetup.c
|
||||||
|
+++ b/zkey/zkey-cryptsetup.c
|
||||||
|
@@ -1346,8 +1346,7 @@ static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) {
|
||||||
|
- if (keysize < 2 * MIN_SECURE_KEY_SIZE ||
|
||||||
|
- (key != NULL && !is_xts_key(key, keysize))) {
|
||||||
|
+ if (key != NULL && !is_xts_key(key, keysize)) {
|
||||||
|
warnx("The volume key size %lu is not valid for the "
|
||||||
|
"cipher mode '%s'", keysize,
|
||||||
|
crypt_get_cipher_mode(g.cd));
|
||||||
|
@@ -1539,8 +1538,9 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
- pr_verbose("Volume key is currently enciphered with %s master key",
|
||||||
|
- is_old ? "OLD" : "CURRENT");
|
||||||
|
+ if (is_secure_key((u8 *)vkey, vkeysize - ikeysize))
|
||||||
|
+ pr_verbose("Volume key is currently enciphered with %s "
|
||||||
|
+ "master key", is_old ? "OLD" : "CURRENT");
|
||||||
|
|
||||||
|
if (key != NULL)
|
||||||
|
*key = vkey;
|
||||||
|
@@ -2023,12 +2023,14 @@ static int command_validate(void)
|
||||||
|
vp_tok_avail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
|
||||||
|
- mkvp, g.verbose);
|
||||||
|
- if (rc != 0) {
|
||||||
|
- warnx("Failed to get the master key verification pattern: %s",
|
||||||
|
- strerror(-rc));
|
||||||
|
- goto out;
|
||||||
|
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||||
|
+ rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
|
||||||
|
+ mkvp, g.verbose);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Failed to get the master key verification "
|
||||||
|
+ "pattern: %s", strerror(-rc));
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
key_type = get_key_type((u8 *)key, seckeysize);
|
||||||
|
@@ -2041,15 +2043,19 @@ static int command_validate(void)
|
||||||
|
printf(" Key type: %s\n", key_type);
|
||||||
|
if (is_valid) {
|
||||||
|
printf(" Clear key size: %lu bits\n", clear_keysize);
|
||||||
|
- printf(" Enciphered with: %s master key (MKVP: "
|
||||||
|
- "%s)\n", is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
- printable_mkvp(get_card_type_for_keytype(key_type),
|
||||||
|
- mkvp));
|
||||||
|
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||||
|
+ printf(" Enciphered with: %s master key (MKVP: "
|
||||||
|
+ "%s)\n", is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
+ printable_mkvp(get_card_type_for_keytype(
|
||||||
|
+ key_type), mkvp));
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
printf(" Clear key size: (unknown)\n");
|
||||||
|
- printf(" Enciphered with: (unknown, MKVP: %s)\n",
|
||||||
|
- printable_mkvp(get_card_type_for_keytype(key_type),
|
||||||
|
- mkvp));
|
||||||
|
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||||
|
+ printf(" Enciphered with: (unknown, MKVP: %s)\n",
|
||||||
|
+ printable_mkvp(get_card_type_for_keytype(
|
||||||
|
+ key_type), mkvp));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
if (vp_tok_avail)
|
||||||
|
print_verification_pattern(vp_tok.verification_pattern);
|
||||||
|
@@ -2065,7 +2071,7 @@ static int command_validate(void)
|
||||||
|
if (!is_valid)
|
||||||
|
printf("\nATTENTION: The secure volume key is not valid.\n");
|
||||||
|
|
||||||
|
- if (is_old_mk)
|
||||||
|
+ if (is_secure_key((u8 *)key, seckeysize) && is_old_mk)
|
||||||
|
util_print_indented("\nWARNING: The secure volume key is "
|
||||||
|
"currently enciphered with the OLD "
|
||||||
|
"master key. To mitigate the danger of "
|
||||||
|
@@ -2195,7 +2201,8 @@ static int command_setkey(void)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (is_old_mk) {
|
||||||
|
+ if (is_secure_key(newkey, newkey_size - integrity_keysize) &&
|
||||||
|
+ is_old_mk) {
|
||||||
|
util_asprintf(&msg, "The secure key in file '%s' is "
|
||||||
|
"enciphered with the master key in the OLD "
|
||||||
|
"master key register. Do you want to set this "
|
||||||
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||||
|
index 316db5f0..b44eadf1 100644
|
||||||
|
--- a/zkey/zkey.1
|
||||||
|
+++ b/zkey/zkey.1
|
||||||
|
@@ -209,7 +209,9 @@ It also displays the attributes of the secure key, such as key sizes, whether
|
||||||
|
it is a secure key that can be used for the XTS cipher mode, the master key
|
||||||
|
register (CURRENT or OLD) with which the secure key is enciphered, and other key
|
||||||
|
attributes. For further information about master key registers, see the
|
||||||
|
-\fBreencipher\fP command.
|
||||||
|
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
|
||||||
|
+cryptographic adapter, thus no master key information is displayed for such
|
||||||
|
+keys.
|
||||||
|
.PP
|
||||||
|
The secure key can either be contained in a file in the file system, or in a
|
||||||
|
secure key repository. To validate a secure key contained in a file, specify
|
||||||
|
@@ -1599,8 +1601,8 @@ This option is only used for secure keys contained in the secure key repository.
|
||||||
|
.TP
|
||||||
|
.BR \-K ", " \-\-key\-type\~\fItype\fP
|
||||||
|
Specifies the key type of the secure key. Possible values are
|
||||||
|
-\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, and \fBEP11\-AES\fP. Only keys with
|
||||||
|
-the specified key type are listed.
|
||||||
|
+\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, \fBEP11\-AES\fP, and
|
||||||
|
+\fBPVSECRET\-AES\fP. Only keys with the specified key type are listed.
|
||||||
|
This option is only used for secure keys contained in the secure key repository.
|
||||||
|
.TP
|
||||||
|
.BR \-L ", " \-\-local\fP
|
||||||
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||||
|
index 90b46106..39a527c4 100644
|
||||||
|
--- a/zkey/zkey.c
|
||||||
|
+++ b/zkey/zkey.c
|
||||||
|
@@ -558,9 +558,10 @@ static struct util_opt opt_vec[] = {
|
||||||
|
.option = { "key-type", required_argument, NULL, 'K'},
|
||||||
|
.argument = "type",
|
||||||
|
.desc = "The type of the key. Possible values are '"
|
||||||
|
- KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' "
|
||||||
|
- "and '"KEY_TYPE_EP11_AES"'. Use this option to list "
|
||||||
|
- "all keys with the specified key type.",
|
||||||
|
+ KEY_TYPE_CCA_AESDATA "', '" KEY_TYPE_CCA_AESCIPHER
|
||||||
|
+ "', '" KEY_TYPE_EP11_AES "', and '"
|
||||||
|
+ KEY_TYPE_PVSECRET_AES "'. Use this option to list all "
|
||||||
|
+ "keys with the specified key type.",
|
||||||
|
.command = COMMAND_LIST,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
@@ -2345,13 +2346,16 @@ static int command_validate_file(void)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
||||||
|
- mkvp, g.verbose);
|
||||||
|
- if (rc != 0) {
|
||||||
|
- warnx("Failed to get the master key verification pattern: %s",
|
||||||
|
- strerror(-rc));
|
||||||
|
- rc = EXIT_FAILURE;
|
||||||
|
- goto out;
|
||||||
|
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ rc = get_master_key_verification_pattern(secure_key,
|
||||||
|
+ secure_key_size,
|
||||||
|
+ mkvp, g.verbose);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ warnx("Failed to get the master key verification "
|
||||||
|
+ "pattern: %s", strerror(-rc));
|
||||||
|
+ rc = EXIT_FAILURE;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
key_type = get_key_type(secure_key, secure_key_size);
|
||||||
|
@@ -2363,25 +2367,30 @@ static int command_validate_file(void)
|
||||||
|
printf(" Clear key size: %lu bits\n", clear_key_size);
|
||||||
|
printf(" XTS type key: %s\n",
|
||||||
|
is_xts_key(secure_key, secure_key_size) ? "Yes" : "No");
|
||||||
|
- printf(" Enciphered with: %s master key (MKVP: %s)\n",
|
||||||
|
- is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
- printable_mkvp(get_card_type_for_keytype(key_type), mkvp));
|
||||||
|
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ printf(" Enciphered with: %s master key (MKVP: %s)\n",
|
||||||
|
+ is_old_mk ? "OLD" : "CURRENT",
|
||||||
|
+ printable_mkvp(get_card_type_for_keytype(key_type),
|
||||||
|
+ mkvp));
|
||||||
|
+ }
|
||||||
|
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
|
||||||
|
vp);
|
||||||
|
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
|
||||||
|
&vp[VERIFICATION_PATTERN_LEN / 2]);
|
||||||
|
|
||||||
|
- rc = cross_check_apqns(NULL, mkvp,
|
||||||
|
- get_min_card_level_for_keytype(key_type),
|
||||||
|
- get_min_fw_version_for_keytype(key_type),
|
||||||
|
- get_card_type_for_keytype(key_type),
|
||||||
|
- true, g.verbose);
|
||||||
|
- if (rc == -EINVAL)
|
||||||
|
- return EXIT_FAILURE;
|
||||||
|
- if (rc != 0 && rc != -ENOTSUP) {
|
||||||
|
- warnx("Your master key setup is improper");
|
||||||
|
- rc = EXIT_FAILURE;
|
||||||
|
- goto out;
|
||||||
|
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||||
|
+ rc = cross_check_apqns(NULL, mkvp,
|
||||||
|
+ get_min_card_level_for_keytype(key_type),
|
||||||
|
+ get_min_fw_version_for_keytype(key_type),
|
||||||
|
+ get_card_type_for_keytype(key_type),
|
||||||
|
+ true, g.verbose);
|
||||||
|
+ if (rc == -EINVAL)
|
||||||
|
+ return EXIT_FAILURE;
|
||||||
|
+ if (rc != 0 && rc != -ENOTSUP) {
|
||||||
|
+ warnx("Your master key setup is improper");
|
||||||
|
+ rc = EXIT_FAILURE;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
@ -0,0 +1,31 @@
|
|||||||
|
From caaf2b2116235d282c2561f0bf6f62b0033c78c4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jakob Naucke <naucke@linux.ibm.com>
|
||||||
|
Date: Wed, 15 Jan 2025 17:36:01 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Fix flag parsing for allowing dump
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Parsing of pvimg flags did not pick up allowing dumping correctly.
|
||||||
|
|
||||||
|
Fixes: f4cf4ae6ebb1 (rust: Add a new tool called 'pvimg')
|
||||||
|
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jakob Naucke <naucke@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/src/cmd/create.rs | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs
|
||||||
|
index 3e2ca655..c9d39745 100644
|
||||||
|
--- a/rust/pvimg/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvimg/src/cmd/create.rs
|
||||||
|
@@ -55,7 +55,7 @@ fn parse_flags(
|
||||||
|
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
|
||||||
|
lf.enable_dump
|
||||||
|
.filter(|x| *x)
|
||||||
|
- .and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
|
||||||
|
+ .and(Some(PcfV1::all_enabled([PcfV1::AllowDumping]))),
|
||||||
|
lf.disable_pckmo
|
||||||
|
.filter(|x| *x)
|
||||||
|
.and(Some(PcfV1::all_disabled([
|
@ -0,0 +1,32 @@
|
|||||||
|
From 7bc12d0202d5819442dd4c32755feb5eb19af70b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Wed, 8 Jan 2025 12:33:05 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Document the change from '--comm-key' to '--cck'
|
||||||
|
in the help message
|
||||||
|
|
||||||
|
This fixes problems when users search for '--comm-key' in the help
|
||||||
|
message.
|
||||||
|
|
||||||
|
Fixes: 5b6d7a467dc3 ("rust/pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias")
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Reviewed-by: Nico Boehr <nrb@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/src/cli.rs | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs
|
||||||
|
index 446e9b74..f5a8c308 100644
|
||||||
|
--- a/rust/pvimg/src/cli.rs
|
||||||
|
+++ b/rust/pvimg/src/cli.rs
|
||||||
|
@@ -330,7 +330,8 @@ pub struct CreateBootImageArgs {
|
||||||
|
|
||||||
|
/// Use the content of FILE as the customer-communication key (CCK).
|
||||||
|
///
|
||||||
|
- /// The file must contain exactly 32 bytes of data.
|
||||||
|
+ /// The file must contain exactly 32 bytes of data. This option used to be
|
||||||
|
+ /// called '--comm-key' in previous versions.
|
||||||
|
#[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
||||||
|
pub cck: Option<PathBuf>,
|
||||||
|
|
BIN
s390-tools-2.31.0.tar.gz
(Stored with Git LFS)
Normal file
BIN
s390-tools-2.31.0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
51
s390-tools-2.34-Fix-Rust-compilation-errors.patch
Normal file
51
s390-tools-2.34-Fix-Rust-compilation-errors.patch
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
From 6a55d0c2e57952600164822dd100e8247b4b010f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Fri, 23 Aug 2024 09:16:26 +0200
|
||||||
|
Subject: [PATCH] rust/pv: Lower most lints to warn
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Lower the lint level to warn for the styling lints.
|
||||||
|
This avoids compile issues during packaging for newer tooling with
|
||||||
|
potential more lint findings.
|
||||||
|
Still deny compiling if a public symbol has no documentation.
|
||||||
|
|
||||||
|
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/173
|
||||||
|
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv/src/lib.rs | 4 ++--
|
||||||
|
rust/pv_core/src/lib.rs | 4 ++--
|
||||||
|
2 files changed, 4 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||||
|
index 9a647617..1084f8e8 100644
|
||||||
|
--- a/rust/pv/src/lib.rs
|
||||||
|
+++ b/rust/pv/src/lib.rs
|
||||||
|
@@ -2,8 +2,8 @@
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023, 2024
|
||||||
|
|
||||||
|
-#![deny(
|
||||||
|
- missing_docs,
|
||||||
|
+#![deny(missing_docs)]
|
||||||
|
+#![warn(
|
||||||
|
missing_debug_implementations,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||||
|
index 1356c1b7..b617b8f9 100644
|
||||||
|
--- a/rust/pv_core/src/lib.rs
|
||||||
|
+++ b/rust/pv_core/src/lib.rs
|
||||||
|
@@ -1,8 +1,8 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023, 2024
|
||||||
|
-#![deny(
|
||||||
|
- missing_docs,
|
||||||
|
+#![deny(missing_docs)]
|
||||||
|
+#![warn(
|
||||||
|
missing_debug_implementations,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
3
s390-tools-2.34.0.tar.gz
Normal file
3
s390-tools-2.34.0.tar.gz
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ea4758c4e460d7f7e040e6aedf68b1be32d63fecb733358b08182f6b9b7440a2
|
||||||
|
size 2114507
|
BIN
s390-tools-2.35.0.tar.gz
(Stored with Git LFS)
Normal file
BIN
s390-tools-2.35.0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
s390-tools-2.36.0.tar.gz
(Stored with Git LFS)
Normal file
BIN
s390-tools-2.36.0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
133
s390-tools-ALP-zdev-live.patch
Normal file
133
s390-tools-ALP-zdev-live.patch
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
zdev/dracut/96zdev-live/module-setup.sh | 32 +++++++++++++++++++++++++
|
||||||
|
zdev/dracut/96zdev-live/parse-zdev-live.sh | 36 +++++++++++++++++++++++++++++
|
||||||
|
zdev/dracut/96zdev-live/write-udev-live.sh | 11 ++++++++
|
||||||
|
zdev/dracut/Makefile | 15 ++++++++++--
|
||||||
|
4 files changed, 92 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zdev/dracut/96zdev-live/module-setup.sh
|
||||||
|
@@ -0,0 +1,32 @@
|
||||||
|
+#!/bin/bash
|
||||||
|
+
|
||||||
|
+# called by dracut
|
||||||
|
+check() {
|
||||||
|
+ arch=${DRACUT_ARCH:-$(uname -m)}
|
||||||
|
+ [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1
|
||||||
|
+
|
||||||
|
+ require_binaries chzdev || return 1
|
||||||
|
+
|
||||||
|
+ [[ $hostonly ]] || return 0
|
||||||
|
+
|
||||||
|
+ # or on request
|
||||||
|
+ return 255
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+# called by dracut
|
||||||
|
+depends() {
|
||||||
|
+ echo bash
|
||||||
|
+ return 0
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+# called by dracut
|
||||||
|
+installkernel() {
|
||||||
|
+ instmods ctcm lcs qeth qeth_l2 qeth_l3 dasd_diag_mod dasd_eckd_mod dasd_fba_mod
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+# called by dracut
|
||||||
|
+install() {
|
||||||
|
+ inst_hook cmdline 41 "$moddir/parse-zdev-live.sh"
|
||||||
|
+ inst_hook cleanup 41 "$moddir/write-udev-live.sh"
|
||||||
|
+ inst_multiple chzdev
|
||||||
|
+}
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zdev/dracut/96zdev-live/parse-zdev-live.sh
|
||||||
|
@@ -0,0 +1,36 @@
|
||||||
|
+#!/bin/bash
|
||||||
|
+#
|
||||||
|
+# 96zdev-live/parse-zdev-live.sh
|
||||||
|
+# Parse the kernel command line for rd.zdev kernel parameters. These
|
||||||
|
+# parameters are evaluated and used to configure z Systems specific devices
|
||||||
|
+# with chzdev(8), especially for use on live/installation type media.
|
||||||
|
+# Note: this is only active on no-hostonly initrds (by default).
|
||||||
|
+#
|
||||||
|
+# Format:
|
||||||
|
+# rd.zdev=TYPE,DEVICE[,SETTINGS]
|
||||||
|
+#
|
||||||
|
+# where
|
||||||
|
+#
|
||||||
|
+# TYPE: all device types supported by chzdev(8), like qeth and dasd
|
||||||
|
+# DEVICE: device specification as supported by chzdev(8) '--enable',
|
||||||
|
+# with the exception of specifying multiple devices, which
|
||||||
|
+# need to be separated by commas. Channel group members
|
||||||
|
+# (or zFCP parameters) in turn are separated by colons.
|
||||||
|
+# SETTINGS: Settings are positional arguments of chzdev in the form
|
||||||
|
+# KEY=VALUE separated by commas.
|
||||||
|
+
|
||||||
|
+zdev_enable="--persistent --enable"
|
||||||
|
+zdev_base_args="--yes --no-root-update --no-settle"
|
||||||
|
+
|
||||||
|
+for zdevs in $(getargs rd.zdev) ; do
|
||||||
|
+ IFS=',' read -r -a zdev <<< "$zdevs"
|
||||||
|
+ if [ -n "$zdev" ] && [ "$zdev" = "no-auto" -o "$zdev" = "auto" ] ; then
|
||||||
|
+ : # ignore, as it's handled by 95zdev
|
||||||
|
+ elif [ -z "$zdev" ] || [ -z "${zdev[1]}" ] ; then
|
||||||
|
+ warn "Unsupported usage of rd.zdev=$zdevs"
|
||||||
|
+ else
|
||||||
|
+ info "+ chzdev $zdev_enable [...] ${zdev[@]}"
|
||||||
|
+ chzdev $zdev_enable $zdev_base_args "${zdev[@]}"
|
||||||
|
+ fi
|
||||||
|
+done
|
||||||
|
+
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zdev/dracut/96zdev-live/write-udev-live.sh
|
||||||
|
@@ -0,0 +1,11 @@
|
||||||
|
+#!/bin/sh
|
||||||
|
+#
|
||||||
|
+# 96zdev-live/write-udev-live.sh
|
||||||
|
+# Copy udeve rules generated by chzdev for device activation starting with 41
|
||||||
|
+# to a *writable* /sysroot -- this is primarily useful for live/installation-
|
||||||
|
+# type media (and by default only active on no-hostonly initrds)
|
||||||
|
+#
|
||||||
|
+
|
||||||
|
+if [ -w /sysroot/etc/udev/rules.d ]; then
|
||||||
|
+ cp -p /etc/udev/rules.d/41-* /sysroot/etc/udev/rules.d
|
||||||
|
+fi
|
||||||
|
--- a/zdev/dracut/Makefile
|
||||||
|
+++ b/zdev/dracut/Makefile
|
||||||
|
@@ -3,17 +3,23 @@
|
||||||
|
|
||||||
|
ZDEVDIR := 95zdev
|
||||||
|
ZDEVKDUMPDIR := 95zdev-kdump
|
||||||
|
+ZDEVLIVEDIR := 96zdev-live
|
||||||
|
|
||||||
|
# HAVE_DRACUT
|
||||||
|
#
|
||||||
|
-# This install time parameter determines whether the zdev dracut module is
|
||||||
|
-# installed (HAVE_DRACUT=1) or not (default). When installed, the module
|
||||||
|
+# This install time parameter determines whether the zdev dracut modules are
|
||||||
|
+# installed (HAVE_DRACUT=1) or not (default). When installed, the 95zdev module
|
||||||
|
# performs the following functions when dracut is run:
|
||||||
|
#
|
||||||
|
# - copy the persistent root device configuration to the initial ram disk
|
||||||
|
# - install a boot-time hook to apply firmware-provided configuration data
|
||||||
|
# to the system
|
||||||
|
#
|
||||||
|
+# The 96zdev-live module performs the following functions when dracut is run:
|
||||||
|
+#
|
||||||
|
+# - install a boot-time hook to apply command-line-provided configuration data
|
||||||
|
+# to a no-hostonly built initial ram disk for use in live/installation media
|
||||||
|
+#
|
||||||
|
ifeq ($(HAVE_DRACUT),1)
|
||||||
|
install:
|
||||||
|
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/
|
||||||
|
@@ -29,4 +35,9 @@
|
||||||
|
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/$(ZDEVKDUMPDIR)
|
||||||
|
$(INSTALL) -m 755 $(ZDEVKDUMPDIR)/module-setup.sh \
|
||||||
|
$(DESTDIR)$(DRACUTMODDIR)/$(ZDEVKDUMPDIR)/
|
||||||
|
+ $(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/$(ZDEVLIVEDIR)
|
||||||
|
+ $(INSTALL) -m 755 $(ZDEVLIVEDIR)/module-setup.sh \
|
||||||
|
+ $(ZDEVLIVEDIR)/parse-zdev-live.sh \
|
||||||
|
+ $(ZDEVLIVEDIR)/write-udev-live.sh \
|
||||||
|
+ $(DESTDIR)$(DRACUTMODDIR)/$(ZDEVLIVEDIR)/
|
||||||
|
endif
|
64
s390-tools-Additional-update-01.patch
Normal file
64
s390-tools-Additional-update-01.patch
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
From dff965465ca9d9c4edaf0f90eadd9a6de335b354 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Date: Fri, 6 Dec 2024 15:28:08 +0100
|
||||||
|
Subject: [PATCH] opticsmon: Fix runaway loop in on_link_change()
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
When on_link_change() gets called with a netdev that would be monitored
|
||||||
|
but hasn't entered zpci_list yet, reloads is 1 after the loops and
|
||||||
|
a reload occurs. Then the netdev is found in the list and reloads
|
||||||
|
becomes -1 which incorrectly triggers more reloads until underflow.
|
||||||
|
Fix this by returning once the device is found. Also just check for
|
||||||
|
reloads being larger than zero.
|
||||||
|
|
||||||
|
Fixes: c34adb9cabee ("opticsmon: Introduce opticsmon tool")
|
||||||
|
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||||
|
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
opticsmon/opticsmon.c | 10 +++++-----
|
||||||
|
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||||
|
index c2f355e2..50dd8d7f 100644
|
||||||
|
--- a/opticsmon/opticsmon.c
|
||||||
|
+++ b/opticsmon/opticsmon.c
|
||||||
|
@@ -280,16 +280,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
|
||||||
|
-reload:
|
||||||
|
+find:
|
||||||
|
util_list_iterate(ctx->zpci_list, zdev) {
|
||||||
|
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||||
|
- reloads--;
|
||||||
|
/* Skip data collection if operational state is
|
||||||
|
* unchanged
|
||||||
|
*/
|
||||||
|
if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||||
|
- continue;
|
||||||
|
+ return;
|
||||||
|
/* Update operation state for VFs even though
|
||||||
|
* they are skipped just for a consistent view
|
||||||
|
*/
|
||||||
|
@@ -297,14 +296,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
/* Only collect optics data for PFs */
|
||||||
|
if (!zpci_is_vf(zdev))
|
||||||
|
dump_adapter_data(ctx, zdev);
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Might be a new device, reload list of devices and retry */
|
||||||
|
- if (reloads) {
|
||||||
|
+ if (reloads > 0) {
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
reloads--;
|
||||||
|
- goto reload;
|
||||||
|
+ goto find;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
129
s390-tools-Additional-update-02.patch
Normal file
129
s390-tools-Additional-update-02.patch
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
From cf5560a100b5552e2eeeaac9c60a88ae77233530 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Date: Mon, 9 Dec 2024 15:08:03 +0100
|
||||||
|
Subject: [PATCH] libzpci: opticsmon: Refactor on_link_change() using new
|
||||||
|
zpci_find_by_netdev()
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Finding a PCI device given the name of a netdev seems generally useful
|
||||||
|
so pull this out into a new zpci_find_by_netdev() function in libzpci
|
||||||
|
and use this to simplify on_link_change() removing the need for
|
||||||
|
backwards goto.
|
||||||
|
|
||||||
|
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||||
|
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
include/lib/pci_list.h | 3 +++
|
||||||
|
libzpci/pci_list.c | 31 +++++++++++++++++++++++++++++++
|
||||||
|
opticsmon/opticsmon.c | 27 +++++++++++----------------
|
||||||
|
3 files changed, 45 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/include/lib/pci_list.h b/include/lib/pci_list.h
|
||||||
|
index 829ec244..5b2918bc 100644
|
||||||
|
--- a/include/lib/pci_list.h
|
||||||
|
+++ b/include/lib/pci_list.h
|
||||||
|
@@ -93,4 +93,7 @@ const char *zpci_pft_str(struct zpci_dev *zdev);
|
||||||
|
const char *zpci_operstate_str(operstate_t state);
|
||||||
|
operstate_t zpci_operstate_from_str(const char *oper_str);
|
||||||
|
|
||||||
|
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||||
|
+ struct zpci_netdev **netdev);
|
||||||
|
+
|
||||||
|
#endif /* LIB_ZPCI_PCI_LIST_H */
|
||||||
|
diff --git a/libzpci/pci_list.c b/libzpci/pci_list.c
|
||||||
|
index 10f64e89..e0d56e44 100644
|
||||||
|
--- a/libzpci/pci_list.c
|
||||||
|
+++ b/libzpci/pci_list.c
|
||||||
|
@@ -356,3 +356,34 @@ void zpci_free_dev_list(struct util_list *zpci_list)
|
||||||
|
}
|
||||||
|
util_list_free(zpci_list);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Find a PCI device given the name of a netdev
|
||||||
|
+ *
|
||||||
|
+ * This function allows finding a PCI device when only the name of one
|
||||||
|
+ * of its netdevs is known.
|
||||||
|
+ *
|
||||||
|
+ * @param[in] zpci_list The device list to search
|
||||||
|
+ * @param[in] netdev_name The name of the netdev
|
||||||
|
+ * @param[out] netdev Pointer to store the netdev or NULL if
|
||||||
|
+ * only the PCI device is needed
|
||||||
|
+ *
|
||||||
|
+ * @return The PCI device if one is found NULL otherwise
|
||||||
|
+ */
|
||||||
|
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||||
|
+ struct zpci_netdev **netdev)
|
||||||
|
+{
|
||||||
|
+ struct zpci_dev *zdev = NULL;
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ util_list_iterate(zpci_list, zdev) {
|
||||||
|
+ for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
+ if (!strcmp(zdev->netdevs[i].name, netdev_name)) {
|
||||||
|
+ if (netdev)
|
||||||
|
+ *netdev = &zdev->netdevs[i];
|
||||||
|
+ return zdev;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||||
|
index 50dd8d7f..7ecaa125 100644
|
||||||
|
--- a/opticsmon/opticsmon.c
|
||||||
|
+++ b/opticsmon/opticsmon.c
|
||||||
|
@@ -274,38 +274,33 @@ static int oneshot_mode(struct opticsmon_ctx *ctx)
|
||||||
|
void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||||
|
{
|
||||||
|
struct opticsmon_ctx *ctx = arg;
|
||||||
|
- struct zpci_dev *zdev;
|
||||||
|
- int i, reloads = 1;
|
||||||
|
-
|
||||||
|
- if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||||
|
- zpci_list_reload(&ctx->zpci_list);
|
||||||
|
+ struct zpci_netdev *found_netdev;
|
||||||
|
+ struct zpci_dev *zdev = NULL;
|
||||||
|
+ int reloads = 1;
|
||||||
|
|
||||||
|
-find:
|
||||||
|
- util_list_iterate(ctx->zpci_list, zdev) {
|
||||||
|
- for (i = 0; i < zdev->num_netdevs; i++) {
|
||||||
|
- if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||||
|
+ do {
|
||||||
|
+ if (ctx->zpci_list) {
|
||||||
|
+ zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
|
||||||
|
+ if (zdev) {
|
||||||
|
/* Skip data collection if operational state is
|
||||||
|
* unchanged
|
||||||
|
*/
|
||||||
|
- if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||||
|
+ if (found_netdev->operstate == netdev->operstate)
|
||||||
|
return;
|
||||||
|
/* Update operation state for VFs even though
|
||||||
|
* they are skipped just for a consistent view
|
||||||
|
*/
|
||||||
|
- zdev->netdevs[i].operstate = netdev->operstate;
|
||||||
|
+ found_netdev->operstate = netdev->operstate;
|
||||||
|
/* Only collect optics data for PFs */
|
||||||
|
if (!zpci_is_vf(zdev))
|
||||||
|
dump_adapter_data(ctx, zdev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- }
|
||||||
|
- /* Might be a new device, reload list of devices and retry */
|
||||||
|
- if (reloads > 0) {
|
||||||
|
+ /* Could be uninitalized list or a new device, retry after reload */
|
||||||
|
zpci_list_reload(&ctx->zpci_list);
|
||||||
|
reloads--;
|
||||||
|
- goto find;
|
||||||
|
- }
|
||||||
|
+ } while (reloads > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_EVENTS 8
|
147
s390-tools-General-update-01.patch
Normal file
147
s390-tools-General-update-01.patch
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
From 1e44ace41de3cbd744b22a8f9835473b091186e0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Thu, 18 Jul 2024 10:55:45 +0200
|
||||||
|
Subject: [PATCH] rust/pvsecret: Refactor writing secret
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Refactor the writing of secret-type dependent output files to ease
|
||||||
|
extensions.
|
||||||
|
|
||||||
|
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv/src/uvsecret/guest_secret.rs | 2 +-
|
||||||
|
rust/pvsecret/src/cmd/create.rs | 89 +++++++++++++++-------------
|
||||||
|
2 files changed, 48 insertions(+), 43 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
index 509691fa..4f1db31c 100644
|
||||||
|
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
@@ -68,7 +68,7 @@ impl GuestSecret {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reference to the confidential data
|
||||||
|
- pub(crate) fn confidential(&self) -> &[u8] {
|
||||||
|
+ pub fn confidential(&self) -> &[u8] {
|
||||||
|
match &self {
|
||||||
|
Self::Null => &[],
|
||||||
|
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
index 808b29e1..9251c38c 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
@@ -62,7 +62,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||||
|
write_out(&opt.output, ser_asrbc, "add-secret request")?;
|
||||||
|
info!("Successfully wrote the request to '{}'", &opt.output);
|
||||||
|
|
||||||
|
- write_secret(&opt.secret, &asrcb, &opt.output)
|
||||||
|
+ write_secret(&opt.secret, asrcb.guest_secret(), &opt.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read+parse the first key from the buffer.
|
||||||
|
@@ -206,54 +206,59 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()>
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Write non confidential data (=name+id) to a yaml stdout
|
||||||
|
+fn write_yaml<P: AsRef<Path>>(
|
||||||
|
+ name: &str,
|
||||||
|
+ guest_secret: &GuestSecret,
|
||||||
|
+ stdout: &bool,
|
||||||
|
+ outp_path: P,
|
||||||
|
+) -> Result<()> {
|
||||||
|
+ debug!("Non-confidential secret information: {guest_secret:x?}");
|
||||||
|
+
|
||||||
|
+ let secret_info = serde_yaml::to_string(guest_secret)?;
|
||||||
|
+ if stdout.to_owned() {
|
||||||
|
+ println!("{secret_info}");
|
||||||
|
+ return Ok(());
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let gen_name: String = name
|
||||||
|
+ .chars()
|
||||||
|
+ .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||||
|
+ .collect();
|
||||||
|
+ let mut yaml_path = outp_path
|
||||||
|
+ .as_ref()
|
||||||
|
+ .parent()
|
||||||
|
+ .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||||
|
+ .to_owned();
|
||||||
|
+ yaml_path.push(gen_name);
|
||||||
|
+ yaml_path.set_extension("yaml");
|
||||||
|
+ write_out(&yaml_path, secret_info, "secret information")?;
|
||||||
|
+ warn!(
|
||||||
|
+ "Successfully wrote secret info to '{}'",
|
||||||
|
+ yaml_path.display().to_string()
|
||||||
|
+ );
|
||||||
|
+ Ok(())
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/// Write the generated secret (if any) to the specified output stream
|
||||||
|
fn write_secret<P: AsRef<Path>>(
|
||||||
|
secret: &AddSecretType,
|
||||||
|
- asrcb: &AddSecretRequest,
|
||||||
|
+ guest_secret: &GuestSecret,
|
||||||
|
outp_path: P,
|
||||||
|
) -> Result<()> {
|
||||||
|
- if let AddSecretType::Association {
|
||||||
|
- name,
|
||||||
|
- stdout,
|
||||||
|
- output_secret: secret_out,
|
||||||
|
- ..
|
||||||
|
- } = secret
|
||||||
|
- {
|
||||||
|
- let gen_name: String = name
|
||||||
|
- .chars()
|
||||||
|
- .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||||
|
- .collect();
|
||||||
|
- let mut gen_path = outp_path
|
||||||
|
- .as_ref()
|
||||||
|
- .parent()
|
||||||
|
- .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||||
|
- .to_owned();
|
||||||
|
- gen_path.push(format!("{gen_name}.yaml"));
|
||||||
|
-
|
||||||
|
- // write non confidential data (=name+id) to a yaml
|
||||||
|
- let secret_info = serde_yaml::to_string(asrcb.guest_secret())?;
|
||||||
|
- if stdout.to_owned() {
|
||||||
|
- println!("{secret_info}");
|
||||||
|
- } else {
|
||||||
|
- write_out(&gen_path, secret_info, "association secret info")?;
|
||||||
|
- debug!(
|
||||||
|
- "Non-confidential secret information: {:x?}",
|
||||||
|
- asrcb.guest_secret()
|
||||||
|
- );
|
||||||
|
- warn!(
|
||||||
|
- "Successfully wrote association info to '{}'",
|
||||||
|
- gen_path.display()
|
||||||
|
- );
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if let Some(path) = secret_out {
|
||||||
|
- if let GuestSecret::Association { secret, .. } = asrcb.guest_secret() {
|
||||||
|
- write_out(path, secret.value(), "Association secret")?
|
||||||
|
- } else {
|
||||||
|
- unreachable!("The secret type has to be `association` at this point (bug)!")
|
||||||
|
+ match secret {
|
||||||
|
+ AddSecretType::Association {
|
||||||
|
+ name,
|
||||||
|
+ stdout,
|
||||||
|
+ output_secret,
|
||||||
|
+ ..
|
||||||
|
+ } => {
|
||||||
|
+ write_yaml(name, guest_secret, stdout, outp_path)?;
|
||||||
|
+ if let Some(path) = output_secret {
|
||||||
|
+ write_out(path, guest_secret.confidential(), "Association secret")?
|
||||||
|
}
|
||||||
|
- info!("Successfully wrote generated association secret to '{path}'");
|
||||||
|
}
|
||||||
|
+ _ => (),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
467
s390-tools-General-update-02.patch
Normal file
467
s390-tools-General-update-02.patch
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
From d1636168b26cc842bc0766235c8a4f2da9663f20 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Tue, 5 Mar 2024 10:46:29 +0100
|
||||||
|
Subject: [PATCH] rust/pv: Support for writing data in PEM format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Use existing OpenSSL functionalities to create PEM files containing
|
||||||
|
arbitrary data.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Acked-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv/src/error.rs | 3 +
|
||||||
|
rust/pv/src/lib.rs | 6 +
|
||||||
|
rust/pv/src/openssl_extensions/bio.rs | 85 +++++++
|
||||||
|
rust/pv/src/openssl_extensions/mod.rs | 2 +
|
||||||
|
.../src/openssl_extensions/stackable_crl.rs | 41 +---
|
||||||
|
rust/pv/src/pem_utils.rs | 222 ++++++++++++++++++
|
||||||
|
6 files changed, 321 insertions(+), 38 deletions(-)
|
||||||
|
create mode 100644 rust/pv/src/openssl_extensions/bio.rs
|
||||||
|
create mode 100644 rust/pv/src/pem_utils.rs
|
||||||
|
|
||||||
|
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||||
|
index af85e93e..3ba808f2 100644
|
||||||
|
--- a/rust/pv/src/error.rs
|
||||||
|
+++ b/rust/pv/src/error.rs
|
||||||
|
@@ -106,6 +106,9 @@ pub enum Error {
|
||||||
|
)]
|
||||||
|
AddDataMissing(&'static str),
|
||||||
|
|
||||||
|
+ #[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||||
|
+ NonAscii,
|
||||||
|
+
|
||||||
|
// errors from other crates
|
||||||
|
#[error(transparent)]
|
||||||
|
PvCore(#[from] pv_core::Error),
|
||||||
|
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||||
|
index 7a33210c..ec31b9a4 100644
|
||||||
|
--- a/rust/pv/src/lib.rs
|
||||||
|
+++ b/rust/pv/src/lib.rs
|
||||||
|
@@ -37,6 +37,7 @@ mod brcb;
|
||||||
|
mod crypto;
|
||||||
|
mod error;
|
||||||
|
mod openssl_extensions;
|
||||||
|
+mod pem_utils;
|
||||||
|
mod req;
|
||||||
|
mod utils;
|
||||||
|
mod uvattest;
|
||||||
|
@@ -71,6 +72,11 @@ pub mod attest {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
+/// Definitions and functions to write objects in PEM format
|
||||||
|
+pub mod pem {
|
||||||
|
+ pub use crate::pem_utils::Pem;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/// Miscellaneous functions and definitions
|
||||||
|
pub mod misc {
|
||||||
|
pub use pv_core::misc::*;
|
||||||
|
diff --git a/rust/pv/src/openssl_extensions/bio.rs b/rust/pv/src/openssl_extensions/bio.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..73528eed
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/rust/pv/src/openssl_extensions/bio.rs
|
||||||
|
@@ -0,0 +1,85 @@
|
||||||
|
+// SPDX-License-Identifier: MIT
|
||||||
|
+//
|
||||||
|
+// Copyright IBM Corp. 2024
|
||||||
|
+
|
||||||
|
+use core::slice;
|
||||||
|
+use openssl::error::ErrorStack;
|
||||||
|
+use openssl_sys::BIO_new_mem_buf;
|
||||||
|
+use std::ffi::c_int;
|
||||||
|
+use std::{marker::PhantomData, ptr};
|
||||||
|
+
|
||||||
|
+pub struct BioMem(*mut openssl_sys::BIO);
|
||||||
|
+
|
||||||
|
+impl Drop for BioMem {
|
||||||
|
+ fn drop(&mut self) {
|
||||||
|
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||||
|
+ unsafe {
|
||||||
|
+ openssl_sys::BIO_free_all(self.0);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl BioMem {
|
||||||
|
+ pub fn new() -> Result<Self, ErrorStack> {
|
||||||
|
+ openssl_sys::init();
|
||||||
|
+
|
||||||
|
+ // SAFETY: Returns a valid pointer or null. null-case is tested right after this.
|
||||||
|
+ let bio = unsafe { openssl_sys::BIO_new(openssl_sys::BIO_s_mem()) };
|
||||||
|
+ match bio.is_null() {
|
||||||
|
+ true => Err(ErrorStack::get()),
|
||||||
|
+ false => Ok(Self(bio)),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||||
|
+ self.0
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Copies the content of this slice into a Vec
|
||||||
|
+ pub fn to_vec(&self) -> Vec<u8> {
|
||||||
|
+ let buf;
|
||||||
|
+ // SAFTEY: BIO provides a continuous memory that can be used to build a slice.
|
||||||
|
+ unsafe {
|
||||||
|
+ let mut ptr = ptr::null_mut();
|
||||||
|
+ let len = openssl_sys::BIO_get_mem_data(self.0, &mut ptr);
|
||||||
|
+ buf = slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
|
||||||
|
+ }
|
||||||
|
+ buf.to_vec()
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub struct BioMemSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||||
|
+impl Drop for BioMemSlice<'_> {
|
||||||
|
+ fn drop(&mut self) {
|
||||||
|
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||||
|
+ unsafe {
|
||||||
|
+ openssl_sys::BIO_free_all(self.0);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl<'a> BioMemSlice<'a> {
|
||||||
|
+ pub fn new(buf: &'a [u8]) -> Result<BioMemSlice<'a>, ErrorStack> {
|
||||||
|
+ openssl_sys::init();
|
||||||
|
+
|
||||||
|
+ // SAFETY: `buf` is a slice (i.e. pointer+size) pointing to a valid memory region.
|
||||||
|
+ // So the resulting bio is valid. Lifetime of the slice is connected by this Rust
|
||||||
|
+ // structure.
|
||||||
|
+ assert!(buf.len() <= c_int::MAX as usize);
|
||||||
|
+ let bio = unsafe {
|
||||||
|
+ {
|
||||||
|
+ let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||||
|
+ match r.is_null() {
|
||||||
|
+ true => Err(ErrorStack::get()),
|
||||||
|
+ false => Ok(r),
|
||||||
|
+ }
|
||||||
|
+ }?
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ Ok(BioMemSlice(bio, PhantomData))
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||||
|
+ self.0
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/rust/pv/src/openssl_extensions/mod.rs b/rust/pv/src/openssl_extensions/mod.rs
|
||||||
|
index fab26638..f6234e5d 100644
|
||||||
|
--- a/rust/pv/src/openssl_extensions/mod.rs
|
||||||
|
+++ b/rust/pv/src/openssl_extensions/mod.rs
|
||||||
|
@@ -6,8 +6,10 @@
|
||||||
|
|
||||||
|
/// Extensions to the rust-openssl crate
|
||||||
|
mod akid;
|
||||||
|
+mod bio;
|
||||||
|
mod crl;
|
||||||
|
mod stackable_crl;
|
||||||
|
|
||||||
|
pub use akid::*;
|
||||||
|
+pub use bio::*;
|
||||||
|
pub use crl::*;
|
||||||
|
diff --git a/rust/pv/src/openssl_extensions/stackable_crl.rs b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||||
|
index aef7cf86..12a9f9de 100644
|
||||||
|
--- a/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||||
|
+++ b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||||
|
@@ -2,16 +2,14 @@
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
-use std::{marker::PhantomData, ptr};
|
||||||
|
-
|
||||||
|
+use crate::openssl_extensions::bio::BioMemSlice;
|
||||||
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
use openssl::{
|
||||||
|
error::ErrorStack,
|
||||||
|
stack::Stackable,
|
||||||
|
x509::{X509Crl, X509CrlRef},
|
||||||
|
};
|
||||||
|
-use openssl_sys::BIO_new_mem_buf;
|
||||||
|
-use std::ffi::c_int;
|
||||||
|
+use std::ptr;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
|
||||||
|
@@ -62,44 +60,11 @@ impl Stackable for StackableX509Crl {
|
||||||
|
type StackType = openssl_sys::stack_st_X509_CRL;
|
||||||
|
}
|
||||||
|
|
||||||
|
-pub struct MemBioSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||||
|
-impl Drop for MemBioSlice<'_> {
|
||||||
|
- fn drop(&mut self) {
|
||||||
|
- unsafe {
|
||||||
|
- openssl_sys::BIO_free_all(self.0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-impl<'a> MemBioSlice<'a> {
|
||||||
|
- pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
|
||||||
|
- openssl_sys::init();
|
||||||
|
-
|
||||||
|
- assert!(buf.len() <= c_int::MAX as usize);
|
||||||
|
- let bio = unsafe {
|
||||||
|
- {
|
||||||
|
- let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||||
|
- if r.is_null() {
|
||||||
|
- Err(ErrorStack::get())
|
||||||
|
- } else {
|
||||||
|
- Ok(r)
|
||||||
|
- }
|
||||||
|
- }?
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- Ok(MemBioSlice(bio, PhantomData))
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||||
|
- self.0
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
impl StackableX509Crl {
|
||||||
|
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
|
||||||
|
unsafe {
|
||||||
|
openssl_sys::init();
|
||||||
|
- let bio = MemBioSlice::new(pem)?;
|
||||||
|
+ let bio = BioMemSlice::new(pem)?;
|
||||||
|
|
||||||
|
let mut crls = vec![];
|
||||||
|
loop {
|
||||||
|
diff --git a/rust/pv/src/pem_utils.rs b/rust/pv/src/pem_utils.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..e6462519
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/rust/pv/src/pem_utils.rs
|
||||||
|
@@ -0,0 +1,222 @@
|
||||||
|
+// SPDX-License-Identifier: MIT
|
||||||
|
+//
|
||||||
|
+// Copyright IBM Corp. 2024
|
||||||
|
+
|
||||||
|
+use crate::Result;
|
||||||
|
+use crate::{openssl_extensions::BioMem, Error};
|
||||||
|
+use openssl::error::ErrorStack;
|
||||||
|
+use pv_core::request::Confidential;
|
||||||
|
+use std::{
|
||||||
|
+ ffi::{c_char, CString},
|
||||||
|
+ fmt::Display,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+mod ffi {
|
||||||
|
+ use openssl_sys::BIO;
|
||||||
|
+ use std::ffi::{c_char, c_int, c_long, c_uchar};
|
||||||
|
+ extern "C" {
|
||||||
|
+ pub fn PEM_write_bio(
|
||||||
|
+ bio: *mut BIO,
|
||||||
|
+ name: *const c_char,
|
||||||
|
+ header: *const c_char,
|
||||||
|
+ data: *const c_uchar,
|
||||||
|
+ len: c_long,
|
||||||
|
+ ) -> c_int;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Thin wrapper around [`CString`] only containing ASCII chars.
|
||||||
|
+#[derive(Debug)]
|
||||||
|
+struct AsciiCString(CString);
|
||||||
|
+
|
||||||
|
+impl AsciiCString {
|
||||||
|
+ /// Convert from string
|
||||||
|
+ ///
|
||||||
|
+ /// # Returns
|
||||||
|
+ /// Error if string is not ASCII or contains null chars
|
||||||
|
+ pub(crate) fn from_str(s: &str) -> Result<Self> {
|
||||||
|
+ match s.is_ascii() {
|
||||||
|
+ true => Ok(Self(CString::new(s).map_err(|_| Error::NonAscii)?)),
|
||||||
|
+ false => Err(Error::NonAscii),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn as_ptr(&self) -> *const c_char {
|
||||||
|
+ self.0.as_ptr()
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Helper struct to construct the PEM format
|
||||||
|
+#[derive(Debug)]
|
||||||
|
+struct InnerPem<'d> {
|
||||||
|
+ name: AsciiCString,
|
||||||
|
+ header: Option<AsciiCString>,
|
||||||
|
+ data: &'d [u8],
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl<'d> InnerPem<'d> {
|
||||||
|
+ fn new(name: &str, header: Option<String>, data: &'d [u8]) -> Result<Self> {
|
||||||
|
+ Ok(Self {
|
||||||
|
+ name: AsciiCString::from_str(name)?,
|
||||||
|
+ header: match header {
|
||||||
|
+ Some(h) => Some(AsciiCString::from_str(&h)?),
|
||||||
|
+ None => None,
|
||||||
|
+ },
|
||||||
|
+ data,
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Generate PEM representation of the data
|
||||||
|
+ fn to_pem(&self) -> Result<Vec<u8>> {
|
||||||
|
+ let bio = BioMem::new()?;
|
||||||
|
+ let hdr_ptr = match self.header {
|
||||||
|
+ // avoid moving variable -> use reference
|
||||||
|
+ Some(ref h) => h.as_ptr(),
|
||||||
|
+ None => std::ptr::null(),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ // SAFETY:
|
||||||
|
+ // All pointers point to valid C strings or memory regions
|
||||||
|
+ let rc = unsafe {
|
||||||
|
+ ffi::PEM_write_bio(
|
||||||
|
+ bio.as_ptr(),
|
||||||
|
+ self.name.as_ptr(),
|
||||||
|
+ hdr_ptr,
|
||||||
|
+ self.data.as_ptr(),
|
||||||
|
+ self.data.len() as std::ffi::c_long,
|
||||||
|
+ )
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ match rc {
|
||||||
|
+ 1 => Err(Error::InternalSsl("Could not write PEM", ErrorStack::get())),
|
||||||
|
+ _ => Ok(bio.to_vec()),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Data in PEM format
|
||||||
|
+///
|
||||||
|
+/// Displays into a printable PEM structure.
|
||||||
|
+/// Must be constructed from another structure in this library.
|
||||||
|
+///
|
||||||
|
+/// ```rust,ignore
|
||||||
|
+/// let pem: Pem = ...;
|
||||||
|
+/// println!("PEM {pem}");
|
||||||
|
+/// ```
|
||||||
|
+/// ```PEM
|
||||||
|
+///-----BEGIN <name>-----
|
||||||
|
+///<header>
|
||||||
|
+///
|
||||||
|
+///<Base64 formatted binary data>
|
||||||
|
+///-----END <name>-----
|
||||||
|
+
|
||||||
|
+#[derive(Debug)]
|
||||||
|
+pub struct Pem {
|
||||||
|
+ pem: Confidential<String>,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[allow(unused)]
|
||||||
|
+impl Pem {
|
||||||
|
+ /// Create a new PEM structure.
|
||||||
|
+ ///
|
||||||
|
+ /// # Errors
|
||||||
|
+ ///
|
||||||
|
+ /// This function will return an error if name or header contain non-ASCII chars, or OpenSSL
|
||||||
|
+ /// could not generate the PEM (very likely due to OOM).
|
||||||
|
+ pub(crate) fn new<D, H>(name: &str, header: H, data: D) -> Result<Self>
|
||||||
|
+ where
|
||||||
|
+ D: AsRef<[u8]>,
|
||||||
|
+ H: Into<Option<String>>,
|
||||||
|
+ {
|
||||||
|
+ let mut header = header.into();
|
||||||
|
+ let header = match header {
|
||||||
|
+ Some(h) if h.ends_with('\n') => Some(h),
|
||||||
|
+ Some(h) if h.is_empty() => None,
|
||||||
|
+ Some(mut h) => {
|
||||||
|
+ h.push('\n');
|
||||||
|
+ Some(h)
|
||||||
|
+ }
|
||||||
|
+ None => None,
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let inner_pem = InnerPem::new(name, header, data.as_ref())?;
|
||||||
|
+
|
||||||
|
+ // Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or OpenSSL Errors
|
||||||
|
+ // Both error should be very unlikely
|
||||||
|
+ // OpenSSL should be able to create PEM if there is enough memory and produce a non-null
|
||||||
|
+ // terminated ASCII-string
|
||||||
|
+ // Unwrap succeeds it's all ASCII
|
||||||
|
+ // Std lib implements all the conversations without a copy
|
||||||
|
+ let pem = CString::new(inner_pem.to_pem()?)
|
||||||
|
+ .map_err(|_| Error::NonAscii)?
|
||||||
|
+ .into_string()
|
||||||
|
+ .unwrap()
|
||||||
|
+ .into();
|
||||||
|
+
|
||||||
|
+ Ok(Self { pem })
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Converts the PEM-data into a byte vector.
|
||||||
|
+ ///
|
||||||
|
+ /// This consumes the `PEM`.
|
||||||
|
+ #[inline]
|
||||||
|
+ #[must_use = "`self` will be dropped if the result is not used"]
|
||||||
|
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||||
|
+ self.pem.into_inner().into_bytes().into()
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Display for Pem {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ self.pem.value().fmt(f)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[cfg(test)]
|
||||||
|
+mod test {
|
||||||
|
+ use super::*;
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn no_data() {
|
||||||
|
+ const EXP: &str =
|
||||||
|
+ "-----BEGIN PEM test-----\ntest hdr value: 17\n\n-----END PEM test-----\n";
|
||||||
|
+ let test_pem = Pem::new("PEM test", "test hdr value: 17".to_string(), []).unwrap();
|
||||||
|
+ let pem_str = test_pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, EXP);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn no_hdr() {
|
||||||
|
+ const EXP: &str =
|
||||||
|
+ "-----BEGIN PEM test-----\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||||
|
+ let test_pem = Pem::new("PEM test", None, "very secret key").unwrap();
|
||||||
|
+ let pem_str = test_pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, EXP);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn some_data() {
|
||||||
|
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||||
|
+ let test_pem = Pem::new(
|
||||||
|
+ "PEM test",
|
||||||
|
+ "test hdr value: 17".to_string(),
|
||||||
|
+ "very secret key",
|
||||||
|
+ )
|
||||||
|
+ .unwrap();
|
||||||
|
+ let pem_str = test_pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, EXP);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn data_linebreak() {
|
||||||
|
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||||
|
+ let test_pem = Pem::new(
|
||||||
|
+ "PEM test",
|
||||||
|
+ "test hdr value: 17\n".to_string(),
|
||||||
|
+ "very secret key",
|
||||||
|
+ )
|
||||||
|
+ .unwrap();
|
||||||
|
+ let pem_str = test_pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, EXP);
|
||||||
|
+ }
|
||||||
|
+}
|
57
s390-tools-General-update-03.patch
Normal file
57
s390-tools-General-update-03.patch
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
From 69eb06f39e5134565babfe96c66a3786c0a571cf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Tue, 20 Feb 2024 14:50:47 +0100
|
||||||
|
Subject: [PATCH] rust/pv_core: Update ffi.rs to linux/uvdevice.h v6.13
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
While at it, add a file global #[allow(dead_code)].
|
||||||
|
The file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||||
|
and there might be things that are not needed here but are defined in that header.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv_core/src/uvdevice/ffi.rs | 11 +++++++++--
|
||||||
|
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/ffi.rs b/rust/pv_core/src/uvdevice/ffi.rs
|
||||||
|
index bbcc5867..3d9998db 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/ffi.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/ffi.rs
|
||||||
|
@@ -2,6 +2,13 @@
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
+// This file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||||
|
+// There might be things that are not needed here but nontheless defined in that header.
|
||||||
|
+// Those two files should be in sync -> there might be unused/dead code.
|
||||||
|
+//
|
||||||
|
+// The `UVIO_IOCTL_*` and `UVIO_SUPP_*` macros
|
||||||
|
+#![allow(dead_code)]
|
||||||
|
+
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use crate::{assert_size, static_assert};
|
||||||
|
@@ -11,9 +18,8 @@ pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
|
||||||
|
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
|
||||||
|
pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000;
|
||||||
|
pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000;
|
||||||
|
-#[allow(unused)]
|
||||||
|
-// here for completeness
|
||||||
|
pub const UVIO_LIST_SECRETS_LEN: usize = 0x1000;
|
||||||
|
+pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000;
|
||||||
|
|
||||||
|
// equal to ascii 'u'
|
||||||
|
pub const UVIO_TYPE_UVC: u8 = 117u8;
|
||||||
|
@@ -23,6 +29,7 @@ pub const UVIO_IOCTL_ATT_NR: u8 = 1;
|
||||||
|
pub const UVIO_IOCTL_ADD_SECRET_NR: u8 = 2;
|
||||||
|
pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3;
|
||||||
|
pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4;
|
||||||
|
+pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5;
|
||||||
|
|
||||||
|
/// Uvdevice IOCTL control block
|
||||||
|
/// Programs can use this struct to communicate with the uvdevice via IOCTLs
|
204
s390-tools-General-update-04.patch
Normal file
204
s390-tools-General-update-04.patch
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
From 01cd81ecf5d1a7e1e504ae1b67692cf63cd4b51d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Tue, 5 Mar 2024 11:56:57 +0100
|
||||||
|
Subject: [PATCH] rust/pv_core: Retrieve Secret UVC
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Create the uvdevice-IOCTL functionality for the new Retrieve Secret UVC.
|
||||||
|
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv_core/src/error.rs | 7 ++
|
||||||
|
rust/pv_core/src/lib.rs | 2 +-
|
||||||
|
rust/pv_core/src/uvdevice/secret.rs | 97 +++++++++++++++++++++++-
|
||||||
|
rust/pv_core/src/uvdevice/secret_list.rs | 15 ++++
|
||||||
|
4 files changed, 118 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/error.rs b/rust/pv_core/src/error.rs
|
||||||
|
index 20fca24d..ba7b7e26 100644
|
||||||
|
--- a/rust/pv_core/src/error.rs
|
||||||
|
+++ b/rust/pv_core/src/error.rs
|
||||||
|
@@ -4,6 +4,8 @@
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
+use crate::uv::SecretId;
|
||||||
|
+
|
||||||
|
/// Result type for this crate
|
||||||
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
@@ -70,6 +72,11 @@ pub enum Error {
|
||||||
|
#[error("The attestation request does not specify a measurement size or measurement data.")]
|
||||||
|
BinArcbNoMeasurement,
|
||||||
|
|
||||||
|
+ #[error(
|
||||||
|
+ "The secret with the ID {id} cannot be retrieved. The requested size is too large ({size})"
|
||||||
|
+ )]
|
||||||
|
+ InvalidRetrievableSecretType { id: SecretId, size: usize },
|
||||||
|
+
|
||||||
|
// errors from other crates
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||||
|
index 349c0b28..5922211f 100644
|
||||||
|
--- a/rust/pv_core/src/lib.rs
|
||||||
|
+++ b/rust/pv_core/src/lib.rs
|
||||||
|
@@ -32,7 +32,7 @@ pub mod misc {
|
||||||
|
/// [`crate::uv::UvCmd`]
|
||||||
|
pub mod uv {
|
||||||
|
pub use crate::uvdevice::attest::AttestationCmd;
|
||||||
|
- pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
|
||||||
|
+ pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||||
|
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||||
|
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||||
|
}
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
index 6c22b6ed..263f17d5 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
@@ -3,8 +3,15 @@
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
use super::ffi;
|
||||||
|
-use crate::{request::MagicValue, uv::UvCmd, uvsecret::AddSecretMagic, Error, Result, PAGESIZE};
|
||||||
|
-use std::io::Read;
|
||||||
|
+use crate::{
|
||||||
|
+ request::{Confidential, MagicValue},
|
||||||
|
+ uv::{SecretEntry, UvCmd},
|
||||||
|
+ uvsecret::AddSecretMagic,
|
||||||
|
+ Error, Result, PAGESIZE,
|
||||||
|
+};
|
||||||
|
+use log::debug;
|
||||||
|
+use std::{io::Read, mem::size_of_val};
|
||||||
|
+use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
/// _List Secrets_ Ultravisor command.
|
||||||
|
///
|
||||||
|
@@ -116,3 +123,89 @@ impl UvCmd for LockCmd {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/// Retrieve a secret value from UV store
|
||||||
|
+#[derive(Debug)]
|
||||||
|
+pub struct RetrieveCmd {
|
||||||
|
+ entry: SecretEntry,
|
||||||
|
+ key: Confidential<Vec<u8>>,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl RetrieveCmd {
|
||||||
|
+ /// Maximum size of a retrieved key (=2 pages)
|
||||||
|
+ pub const MAX_SIZE: usize = ffi::UVIO_RETR_SECRET_MAX_LEN;
|
||||||
|
+
|
||||||
|
+ /// Create a retrieve-secret UVC from a [`SecretEntry`].
|
||||||
|
+ ///
|
||||||
|
+ /// This uses the index of the secret entry for the UVC.
|
||||||
|
+ pub fn from_entry(entry: SecretEntry) -> Result<Self> {
|
||||||
|
+ entry.try_into()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Transform a [`RetrieveCmd`] into a key-vector.
|
||||||
|
+ ///
|
||||||
|
+ /// Only makes sense to call after a successful UVC execution.
|
||||||
|
+ pub fn into_key(self) -> Confidential<Vec<u8>> {
|
||||||
|
+ self.key
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Get the secret entry
|
||||||
|
+ ///
|
||||||
|
+ /// Get the secret entry that is used as metadata to retrieve the secret
|
||||||
|
+ pub fn meta_data(&self) -> &SecretEntry {
|
||||||
|
+ &self.entry
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl TryFrom<SecretEntry> for RetrieveCmd {
|
||||||
|
+ type Error = Error;
|
||||||
|
+
|
||||||
|
+ fn try_from(entry: SecretEntry) -> Result<Self> {
|
||||||
|
+ let len = entry.secret_size() as usize;
|
||||||
|
+
|
||||||
|
+ // Next to impossible if the secret entry is a valid response from UV
|
||||||
|
+ if len > Self::MAX_SIZE {
|
||||||
|
+ return Err(Error::InvalidRetrievableSecretType {
|
||||||
|
+ id: entry.secret_id().to_owned(),
|
||||||
|
+ size: len,
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Ensure that an u16 fits into the buffer.
|
||||||
|
+ let size = std::cmp::max(size_of_val(&entry.index()), len);
|
||||||
|
+ debug!("Create a buf with {} elements", size);
|
||||||
|
+ let mut buf = vec![0; size];
|
||||||
|
+ // The IOCTL expects the secret index in the first two bytes of the buffer. They will be
|
||||||
|
+ // overwritten in the response
|
||||||
|
+ entry.index_be().write_to_prefix(&mut buf).unwrap();
|
||||||
|
+ Ok(Self {
|
||||||
|
+ entry,
|
||||||
|
+ key: buf.into(),
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl UvCmd for RetrieveCmd {
|
||||||
|
+ const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_RETR_SECRET_NR;
|
||||||
|
+
|
||||||
|
+ fn rc_fmt(&self, rc: u16, _: u16) -> Option<&'static str> {
|
||||||
|
+ match rc {
|
||||||
|
+ // should not appear (TM), software creates request from a list item
|
||||||
|
+ 0x0009 => Some("the allocated buffer is to small to store the secret"),
|
||||||
|
+ // should not appear (TM), kernel allocates the memory
|
||||||
|
+ 0x0102 => {
|
||||||
|
+ Some("access exception recognized when accessing retrieved secret storage area")
|
||||||
|
+ }
|
||||||
|
+ // should not appear (TM), software creates request from a list item
|
||||||
|
+ 0x010f => Some("the Secret Store is empty"),
|
||||||
|
+ // should not appear (TM), software creates request from a list item
|
||||||
|
+ 0x0110 => Some("the Secret Store does not contain a secret with the specified index"),
|
||||||
|
+ 0x0111 => Some("the secret is not retrievable"),
|
||||||
|
+ _ => None,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn data(&mut self) -> Option<&mut [u8]> {
|
||||||
|
+ Some(self.key.value_mut())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
index d20928b5..0a8af504 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
@@ -110,6 +110,11 @@ impl SecretEntry {
|
||||||
|
self.index.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /// Returns the index of this [`SecretEntry`] in BE.
|
||||||
|
+ pub(crate) fn index_be(&self) -> &U16<BigEndian> {
|
||||||
|
+ &self.index
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/// Returns the secret type of this [`SecretEntry`].
|
||||||
|
pub fn stype(&self) -> ListableSecretType {
|
||||||
|
self.stype.into()
|
||||||
|
@@ -127,6 +132,16 @@ impl SecretEntry {
|
||||||
|
pub fn id(&self) -> &[u8] {
|
||||||
|
self.id.as_ref()
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /// Get the id as [`SecretId`] reference
|
||||||
|
+ pub(crate) fn secret_id(&self) -> &SecretId {
|
||||||
|
+ &self.id
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Returns the secret size of this [`SecretEntry`].
|
||||||
|
+ pub fn secret_size(&self) -> u32 {
|
||||||
|
+ self.len.get()
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SecretEntry {
|
710
s390-tools-General-update-05.patch
Normal file
710
s390-tools-General-update-05.patch
Normal file
@ -0,0 +1,710 @@
|
|||||||
|
From 4af137f4fad8638169ccf0ddcb6dc4b0fe8fb1c1 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Tue, 5 Mar 2024 12:16:44 +0100
|
||||||
|
Subject: [PATCH] rust/pv_core: Support for listing Retrievable Secrets
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Add support for listing retrievable secrets in the List Secrets UVC.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv_core/src/lib.rs | 2 +
|
||||||
|
rust/pv_core/src/uvdevice.rs | 1 +
|
||||||
|
rust/pv_core/src/uvdevice/retr_secret.rs | 399 +++++++++++++++++++++++
|
||||||
|
rust/pv_core/src/uvdevice/secret_list.rs | 157 +++++++--
|
||||||
|
4 files changed, 536 insertions(+), 23 deletions(-)
|
||||||
|
create mode 100644 rust/pv_core/src/uvdevice/retr_secret.rs
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||||
|
index 5922211f..caebfcea 100644
|
||||||
|
--- a/rust/pv_core/src/lib.rs
|
||||||
|
+++ b/rust/pv_core/src/lib.rs
|
||||||
|
@@ -32,6 +32,8 @@ pub mod misc {
|
||||||
|
/// [`crate::uv::UvCmd`]
|
||||||
|
pub mod uv {
|
||||||
|
pub use crate::uvdevice::attest::AttestationCmd;
|
||||||
|
+ pub use crate::uvdevice::retr_secret::RetrievableSecret;
|
||||||
|
+ pub use crate::uvdevice::retr_secret::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes};
|
||||||
|
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||||
|
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||||
|
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||||
|
index d4176815..e9848243 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice.rs
|
||||||
|
@@ -25,6 +25,7 @@ mod info;
|
||||||
|
mod test;
|
||||||
|
pub(crate) use ffi::uv_ioctl;
|
||||||
|
pub mod attest;
|
||||||
|
+pub mod retr_secret;
|
||||||
|
pub mod secret;
|
||||||
|
pub mod secret_list;
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/retr_secret.rs b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..490152b4
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||||
|
@@ -0,0 +1,399 @@
|
||||||
|
+// SPDX-License-Identifier: MIT
|
||||||
|
+//
|
||||||
|
+// Copyright IBM Corp. 2024
|
||||||
|
+
|
||||||
|
+use crate::uv::{ListableSecretType, RetrieveCmd};
|
||||||
|
+use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
+use std::fmt::Display;
|
||||||
|
+
|
||||||
|
+/// Allowed sizes for AES keys
|
||||||
|
+#[non_exhaustive]
|
||||||
|
+#[derive(PartialEq, Eq, Debug)]
|
||||||
|
+pub enum AesSizes {
|
||||||
|
+ /// 128 bit key
|
||||||
|
+ Bits128,
|
||||||
|
+ /// 192 bit key
|
||||||
|
+ Bits192,
|
||||||
|
+ /// 256 bit key
|
||||||
|
+ Bits256,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl AesSizes {
|
||||||
|
+ /// Construct the key-size from the bit-size.
|
||||||
|
+ ///
|
||||||
|
+ /// Returns [`None`] if the bit-size is not supported.
|
||||||
|
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||||
|
+ match bits {
|
||||||
|
+ 128 => Some(Self::Bits128),
|
||||||
|
+ 192 => Some(Self::Bits192),
|
||||||
|
+ 256 => Some(Self::Bits256),
|
||||||
|
+ _ => None,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Returns the bit-size for the key-type
|
||||||
|
+ const fn bit_size(&self) -> u32 {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Bits128 => 128,
|
||||||
|
+ Self::Bits192 => 192,
|
||||||
|
+ Self::Bits256 => 256,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Display for AesSizes {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ write!(f, "{}", self.bit_size())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Allowed sizes for AES-XTS keys
|
||||||
|
+#[non_exhaustive]
|
||||||
|
+#[derive(PartialEq, Eq, Debug)]
|
||||||
|
+pub enum AesXtsSizes {
|
||||||
|
+ /// Two AES 128 bit keys
|
||||||
|
+ Bits128,
|
||||||
|
+ /// Two AES 256 bit keys
|
||||||
|
+ Bits256,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl AesXtsSizes {
|
||||||
|
+ /// Construct the key-size from the bit-size.
|
||||||
|
+ ///
|
||||||
|
+ /// It's a key containing two keys; bit-size is half the number of bits it has
|
||||||
|
+ /// Returns [`None`] if the bit-size is not supported.
|
||||||
|
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||||
|
+ match bits {
|
||||||
|
+ 128 => Some(Self::Bits128),
|
||||||
|
+ 256 => Some(Self::Bits256),
|
||||||
|
+ _ => None,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Returns the bit-size for the key-type
|
||||||
|
+ ///
|
||||||
|
+ /// It's a key containing two keys: bit-size is half the number of bits it has
|
||||||
|
+ const fn bit_size(&self) -> u32 {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Bits128 => 128,
|
||||||
|
+ Self::Bits256 => 256,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Display for AesXtsSizes {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ write!(f, "{}", self.bit_size())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Allowed sizes for HMAC-SHA keys
|
||||||
|
+#[non_exhaustive]
|
||||||
|
+#[derive(PartialEq, Eq, Debug)]
|
||||||
|
+pub enum HmacShaSizes {
|
||||||
|
+ /// SHA 256 bit
|
||||||
|
+ Sha256,
|
||||||
|
+ /// SHA 512 bit
|
||||||
|
+ Sha512,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl HmacShaSizes {
|
||||||
|
+ /// Construct the key-size from the sha-size.
|
||||||
|
+ ///
|
||||||
|
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||||
|
+ /// The `sha_size` is half of the number of bits in the key
|
||||||
|
+ /// Returns [`None`] if the `sha_size` is not supported.
|
||||||
|
+ pub fn from_sha_size(sha_size: u32) -> Option<Self> {
|
||||||
|
+ match sha_size {
|
||||||
|
+ 256 => Some(Self::Sha256),
|
||||||
|
+ 512 => Some(Self::Sha512),
|
||||||
|
+ _ => None,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Returns the sha-size for the key-type
|
||||||
|
+ ///
|
||||||
|
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||||
|
+ /// The `sha_size` is half of the number of bits in the key
|
||||||
|
+ const fn sha_size(&self) -> u32 {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Sha256 => 256,
|
||||||
|
+ Self::Sha512 => 512,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Display for HmacShaSizes {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ write!(f, "{}", self.sha_size())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Allowed curves for EC private keys
|
||||||
|
+#[non_exhaustive]
|
||||||
|
+#[derive(PartialEq, Eq, Debug)]
|
||||||
|
+pub enum EcCurves {
|
||||||
|
+ /// secp256r1 or prime256v1 curve
|
||||||
|
+ Secp256R1,
|
||||||
|
+ /// secp384p1 curve
|
||||||
|
+ Secp384R1,
|
||||||
|
+ /// secp521r1 curve
|
||||||
|
+ Secp521R1,
|
||||||
|
+ /// ed25519 curve
|
||||||
|
+ Ed25519,
|
||||||
|
+ /// ed448 curve
|
||||||
|
+ Ed448,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl EcCurves {
|
||||||
|
+ const fn exp_size(&self) -> usize {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Secp256R1 => 32,
|
||||||
|
+ Self::Secp384R1 => 48,
|
||||||
|
+ Self::Secp521R1 => 80,
|
||||||
|
+ Self::Ed25519 => 32,
|
||||||
|
+ Self::Ed448 => 64,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Resizes the raw key to the expected size.
|
||||||
|
+ ///
|
||||||
|
+ /// See [`Vec::resize`]
|
||||||
|
+ pub fn resize_raw_key(&self, mut raw: Vec<u8>) -> Vec<u8> {
|
||||||
|
+ raw.resize(self.exp_size(), 0);
|
||||||
|
+ raw
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// The names have to stay constant, otherwise the PEM contains invalid types
|
||||||
|
+impl Display for EcCurves {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Secp256R1 => write!(f, "SECP256R1"),
|
||||||
|
+ Self::Secp384R1 => write!(f, "SECP384R1"),
|
||||||
|
+ Self::Secp521R1 => write!(f, "SECP521R1"),
|
||||||
|
+ Self::Ed25519 => write!(f, "ED25519"),
|
||||||
|
+ Self::Ed448 => write!(f, "ED448"),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Retrievable Secret types
|
||||||
|
+#[non_exhaustive]
|
||||||
|
+#[derive(PartialEq, Eq, Debug)]
|
||||||
|
+pub enum RetrievableSecret {
|
||||||
|
+ /// Plain-text secret
|
||||||
|
+ PlainText,
|
||||||
|
+ /// Protected AES key
|
||||||
|
+ Aes(AesSizes),
|
||||||
|
+ /// Protected AES-XTS key
|
||||||
|
+ AesXts(AesXtsSizes),
|
||||||
|
+ /// Protected HMAC-SHA key
|
||||||
|
+ HmacSha(HmacShaSizes),
|
||||||
|
+ /// Protected EC-private key
|
||||||
|
+ Ec(EcCurves),
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// The names have to stay constant, otherwise the PEM contains invalid/unknown types
|
||||||
|
+impl Display for RetrievableSecret {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ // Alternate representation: Omit sizes/curves
|
||||||
|
+ if f.alternate() {
|
||||||
|
+ match self {
|
||||||
|
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||||
|
+ Self::Aes(_) => write!(f, "AES-KEY"),
|
||||||
|
+ Self::AesXts(_) => write!(f, "AES-XTS-KEY"),
|
||||||
|
+ Self::HmacSha(_) => write!(f, "HMAC-SHA-KEY"),
|
||||||
|
+ Self::Ec(_) => write!(f, "EC-PRIVATE-KEY"),
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ match self {
|
||||||
|
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||||
|
+ Self::Aes(s) => write!(f, "AES-{s}-KEY"),
|
||||||
|
+ Self::AesXts(s) => write!(f, "AES-XTS-{s}-KEY"),
|
||||||
|
+ Self::HmacSha(s) => write!(f, "HMAC-SHA-{s}-KEY"),
|
||||||
|
+ Self::Ec(c) => write!(f, "EC-{c}-PRIVATE-KEY"),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl RetrievableSecret {
|
||||||
|
+ /// Report expected input types
|
||||||
|
+ pub fn expected(&self) -> String {
|
||||||
|
+ match self {
|
||||||
|
+ Self::PlainText => format!("less than {}", RetrieveCmd::MAX_SIZE),
|
||||||
|
+ Self::Aes(_) => "128, 192, or 256".to_string(),
|
||||||
|
+ Self::AesXts(_) => "128 or 256".to_string(),
|
||||||
|
+ Self::HmacSha(_) => "256 or 512".to_string(),
|
||||||
|
+ Self::Ec(_) => "secp256r1, secp384r1, secp521r1, ed25519, or ed448".to_string(),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl From<&RetrievableSecret> for u16 {
|
||||||
|
+ fn from(value: &RetrievableSecret) -> Self {
|
||||||
|
+ match value {
|
||||||
|
+ RetrievableSecret::PlainText => ListableSecretType::PLAINTEXT,
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits128) => ListableSecretType::AES_128_KEY,
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits192) => ListableSecretType::AES_192_KEY,
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits256) => ListableSecretType::AES_256_KEY,
|
||||||
|
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128) => ListableSecretType::AES_128_XTS_KEY,
|
||||||
|
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256) => ListableSecretType::AES_256_XTS_KEY,
|
||||||
|
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256) => {
|
||||||
|
+ ListableSecretType::HMAC_SHA_256_KEY
|
||||||
|
+ }
|
||||||
|
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512) => {
|
||||||
|
+ ListableSecretType::HMAC_SHA_512_KEY
|
||||||
|
+ }
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp256R1) => ListableSecretType::ECDSA_P256_KEY,
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp384R1) => ListableSecretType::ECDSA_P384_KEY,
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp521R1) => ListableSecretType::ECDSA_P521_KEY,
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Ed25519) => ListableSecretType::ECDSA_ED25519_KEY,
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Ed448) => ListableSecretType::ECDSA_ED448_KEY,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// serializes to: <secret type nb> (String name)
|
||||||
|
+impl Serialize for RetrievableSecret {
|
||||||
|
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
+ where
|
||||||
|
+ S: Serializer,
|
||||||
|
+ {
|
||||||
|
+ let id: u16 = self.into();
|
||||||
|
+ serializer.serialize_str(&format!("{id} ({self})"))
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// deserializes from the secret type nb only
|
||||||
|
+impl<'de> Deserialize<'de> for RetrievableSecret {
|
||||||
|
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||||
|
+ where
|
||||||
|
+ D: serde::Deserializer<'de>,
|
||||||
|
+ {
|
||||||
|
+ struct RetrSecretVisitor;
|
||||||
|
+ impl<'de> serde::de::Visitor<'de> for RetrSecretVisitor {
|
||||||
|
+ type Value = RetrievableSecret;
|
||||||
|
+
|
||||||
|
+ fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
+ fmt.write_str(
|
||||||
|
+ "a retrievable secret type: `<number> (String name)` number in [3,10]|[17,21]",
|
||||||
|
+ )
|
||||||
|
+ }
|
||||||
|
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||||
|
+ where
|
||||||
|
+ E: serde::de::Error,
|
||||||
|
+ {
|
||||||
|
+ let (n, _) = s.split_once(' ').ok_or(serde::de::Error::invalid_value(
|
||||||
|
+ serde::de::Unexpected::Str(s),
|
||||||
|
+ &self,
|
||||||
|
+ ))?;
|
||||||
|
+ let id: u16 = n.parse().map_err(|_| {
|
||||||
|
+ serde::de::Error::invalid_value(serde::de::Unexpected::Str(n), &self)
|
||||||
|
+ })?;
|
||||||
|
+ let listable: ListableSecretType = id.into();
|
||||||
|
+ match listable {
|
||||||
|
+ ListableSecretType::Retrievable(r) => Ok(r),
|
||||||
|
+ _ => Err(serde::de::Error::invalid_value(
|
||||||
|
+ serde::de::Unexpected::Unsigned(id.into()),
|
||||||
|
+ &self,
|
||||||
|
+ )),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ de.deserialize_str(RetrSecretVisitor)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[cfg(test)]
|
||||||
|
+mod test {
|
||||||
|
+ use serde_test::{assert_tokens, Token};
|
||||||
|
+
|
||||||
|
+ use super::*;
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_serde_plain() {
|
||||||
|
+ let retr = RetrievableSecret::PlainText;
|
||||||
|
+ assert_tokens(&retr, &[Token::Str("3 (PLAINTEXT)")]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_serde_aes() {
|
||||||
|
+ let retr = RetrievableSecret::Aes(AesSizes::Bits192);
|
||||||
|
+ assert_tokens(&retr, &[Token::Str("5 (AES-192-KEY)")]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_serde_aes_xts() {
|
||||||
|
+ let retr = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||||
|
+ assert_tokens(&retr, &[Token::Str("7 (AES-XTS-128-KEY)")]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_serde_hmac() {
|
||||||
|
+ let retr = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||||
|
+ assert_tokens(&retr, &[Token::Str("9 (HMAC-SHA-256-KEY)")]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_serde_es() {
|
||||||
|
+ let retr = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||||
|
+ assert_tokens(&retr, &[Token::Str("19 (EC-SECP521R1-PRIVATE-KEY)")]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Ensure that the string representation of the retrievable types stay constant, or PEM will have
|
||||||
|
+ // different, incompatible types
|
||||||
|
+ #[test]
|
||||||
|
+ fn stable_type_names() {
|
||||||
|
+ assert_eq!("PLAINTEXT", RetrievableSecret::PlainText.to_string());
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "AES-128-KEY",
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits128).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "AES-192-KEY",
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits192).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "AES-256-KEY",
|
||||||
|
+ RetrievableSecret::Aes(AesSizes::Bits256).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "AES-XTS-128-KEY",
|
||||||
|
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "AES-XTS-256-KEY",
|
||||||
|
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "HMAC-SHA-256-KEY",
|
||||||
|
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "HMAC-SHA-512-KEY",
|
||||||
|
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "EC-SECP256R1-PRIVATE-KEY",
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp256R1).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "EC-SECP384R1-PRIVATE-KEY",
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp384R1).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "EC-SECP521R1-PRIVATE-KEY",
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Secp521R1).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "EC-ED25519-PRIVATE-KEY",
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Ed25519).to_string()
|
||||||
|
+ );
|
||||||
|
+ assert_eq!(
|
||||||
|
+ "EC-ED448-PRIVATE-KEY",
|
||||||
|
+ RetrievableSecret::Ec(EcCurves::Ed448).to_string()
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
index 0a8af504..4e955010 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
@@ -2,9 +2,14 @@
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
|
-use crate::assert_size;
|
||||||
|
-use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result};
|
||||||
|
-use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
+use crate::{
|
||||||
|
+ assert_size,
|
||||||
|
+ misc::to_u16,
|
||||||
|
+ uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||||
|
+ uvdevice::UvCmd,
|
||||||
|
+ Error, Result,
|
||||||
|
+};
|
||||||
|
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||||
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
@@ -18,7 +23,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
|
||||||
|
///
|
||||||
|
/// (de)serializes itself in/from a hex-string
|
||||||
|
#[repr(C)]
|
||||||
|
-#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone)]
|
||||||
|
+#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone, Default)]
|
||||||
|
pub struct SecretId([u8; Self::ID_SIZE]);
|
||||||
|
assert_size!(SecretId, SecretId::ID_SIZE);
|
||||||
|
|
||||||
|
@@ -94,11 +99,11 @@ impl SecretEntry {
|
||||||
|
/// Create a new entry for a [`SecretList`].
|
||||||
|
///
|
||||||
|
/// The content of this entry will very likely not represent the status of the guest in the
|
||||||
|
- /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||||
|
+ /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encouraged.
|
||||||
|
pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
index: index.into(),
|
||||||
|
- stype: stype.into(),
|
||||||
|
+ stype: U16::new(stype.into()),
|
||||||
|
len: secret_len.into(),
|
||||||
|
res_8: 0,
|
||||||
|
id,
|
||||||
|
@@ -117,7 +122,7 @@ impl SecretEntry {
|
||||||
|
|
||||||
|
/// Returns the secret type of this [`SecretEntry`].
|
||||||
|
pub fn stype(&self) -> ListableSecretType {
|
||||||
|
- self.stype.into()
|
||||||
|
+ self.stype.get().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the id of this [`SecretEntry`].
|
||||||
|
@@ -146,7 +151,7 @@ impl SecretEntry {
|
||||||
|
|
||||||
|
impl Display for SecretEntry {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
- let stype: ListableSecretType = self.stype.into();
|
||||||
|
+ let stype: ListableSecretType = self.stype.get().into();
|
||||||
|
writeln!(f, "{} {}:", self.index, stype)?;
|
||||||
|
write!(f, " ")?;
|
||||||
|
for b in self.id.as_ref() {
|
||||||
|
@@ -298,51 +303,115 @@ fn ser_u16<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
pub enum ListableSecretType {
|
||||||
|
/// Association Secret
|
||||||
|
Association,
|
||||||
|
+ /// Retrievable key
|
||||||
|
+ Retrievable(RetrievableSecret),
|
||||||
|
+
|
||||||
|
/// Invalid secret type, that should never appear in a list
|
||||||
|
///
|
||||||
|
/// 0 is reserved
|
||||||
|
- /// 1 is Null secret, with no id and not listable
|
||||||
|
+ /// 1 is Null secret, with no id and not list-able
|
||||||
|
Invalid(u16),
|
||||||
|
/// Unknown secret type
|
||||||
|
Unknown(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListableSecretType {
|
||||||
|
- /// UV type id for an association secret
|
||||||
|
- pub const ASSOCIATION: u16 = 0x0002;
|
||||||
|
- /// UV type id for a null secret
|
||||||
|
- pub const NULL: u16 = 0x0001;
|
||||||
|
const RESERVED_0: u16 = 0x0000;
|
||||||
|
+ /// UV secret-type id for a null secret
|
||||||
|
+ pub const NULL: u16 = 0x0001;
|
||||||
|
+ /// UV secret-type id for an association secret
|
||||||
|
+ pub const ASSOCIATION: u16 = 0x0002;
|
||||||
|
+ /// UV secret-type id for a plain text secret
|
||||||
|
+ pub const PLAINTEXT: u16 = 0x0003;
|
||||||
|
+ /// UV secret-type id for an aes-128-key secret
|
||||||
|
+ pub const AES_128_KEY: u16 = 0x0004;
|
||||||
|
+ /// UV secret-type id for an aes-192-key secret
|
||||||
|
+ pub const AES_192_KEY: u16 = 0x0005;
|
||||||
|
+ /// UV secret-type id for an aes-256-key secret
|
||||||
|
+ pub const AES_256_KEY: u16 = 0x0006;
|
||||||
|
+ /// UV secret-type id for an aes-xts-128-key secret
|
||||||
|
+ pub const AES_128_XTS_KEY: u16 = 0x0007;
|
||||||
|
+ /// UV secret-type id for an aes-xts-256-key secret
|
||||||
|
+ pub const AES_256_XTS_KEY: u16 = 0x0008;
|
||||||
|
+ /// UV secret-type id for an hmac-sha-256-key secret
|
||||||
|
+ pub const HMAC_SHA_256_KEY: u16 = 0x0009;
|
||||||
|
+ /// UV secret-type id for an hmac-sha-512-key secret
|
||||||
|
+ pub const HMAC_SHA_512_KEY: u16 = 0x000a;
|
||||||
|
+ // 0x000b - 0x0010 reserved
|
||||||
|
+ /// UV secret-type id for an ecdsa-p256-private-key secret
|
||||||
|
+ pub const ECDSA_P256_KEY: u16 = 0x0011;
|
||||||
|
+ /// UV secret-type id for an ecdsa-p384-private-key secret
|
||||||
|
+ pub const ECDSA_P384_KEY: u16 = 0x0012;
|
||||||
|
+ /// UV secret-type id for an ecdsa-p521-private-key secret
|
||||||
|
+ pub const ECDSA_P521_KEY: u16 = 0x0013;
|
||||||
|
+ /// UV secret-type id for an ed25519-private-key secret
|
||||||
|
+ pub const ECDSA_ED25519_KEY: u16 = 0x0014;
|
||||||
|
+ /// UV secret-type id for an ed448-private-key secret
|
||||||
|
+ pub const ECDSA_ED448_KEY: u16 = 0x0015;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ListableSecretType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Association => write!(f, "Association"),
|
||||||
|
- Self::Invalid(n) => write!(f, "Invalid({n})"),
|
||||||
|
- Self::Unknown(n) => write!(f, "Unknown({n})"),
|
||||||
|
+ Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"),
|
||||||
|
+ Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"),
|
||||||
|
+ Self::Retrievable(r) => write!(f, "{r}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-impl From<U16<BigEndian>> for ListableSecretType {
|
||||||
|
- fn from(value: U16<BigEndian>) -> Self {
|
||||||
|
- match value.get() {
|
||||||
|
+impl<O: ByteOrder> From<U16<O>> for ListableSecretType {
|
||||||
|
+ fn from(value: U16<O>) -> Self {
|
||||||
|
+ value.get().into()
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl From<u16> for ListableSecretType {
|
||||||
|
+ fn from(value: u16) -> Self {
|
||||||
|
+ match value {
|
||||||
|
Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0),
|
||||||
|
Self::NULL => Self::Invalid(Self::NULL),
|
||||||
|
Self::ASSOCIATION => Self::Association,
|
||||||
|
+ Self::PLAINTEXT => Self::Retrievable(RetrievableSecret::PlainText),
|
||||||
|
+ Self::AES_128_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||||
|
+ Self::AES_192_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits192)),
|
||||||
|
+ Self::AES_256_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits256)),
|
||||||
|
+ Self::AES_128_XTS_KEY => {
|
||||||
|
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits128))
|
||||||
|
+ }
|
||||||
|
+ Self::AES_256_XTS_KEY => {
|
||||||
|
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits256))
|
||||||
|
+ }
|
||||||
|
+ Self::HMAC_SHA_256_KEY => {
|
||||||
|
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha256))
|
||||||
|
+ }
|
||||||
|
+ Self::HMAC_SHA_512_KEY => {
|
||||||
|
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha512))
|
||||||
|
+ }
|
||||||
|
+ Self::ECDSA_P256_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp256R1)),
|
||||||
|
+ Self::ECDSA_P384_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp384R1)),
|
||||||
|
+ Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
|
||||||
|
+ Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
|
||||||
|
+ Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
|
||||||
|
n => Self::Unknown(n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-impl From<ListableSecretType> for U16<BigEndian> {
|
||||||
|
+impl<O: ByteOrder> From<ListableSecretType> for U16<O> {
|
||||||
|
+ fn from(value: ListableSecretType) -> Self {
|
||||||
|
+ Self::new(value.into())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl From<ListableSecretType> for u16 {
|
||||||
|
fn from(value: ListableSecretType) -> Self {
|
||||||
|
match value {
|
||||||
|
ListableSecretType::Association => ListableSecretType::ASSOCIATION,
|
||||||
|
ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n,
|
||||||
|
+ ListableSecretType::Retrievable(r) => (&r).into(),
|
||||||
|
}
|
||||||
|
- .into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -363,8 +432,8 @@ where
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
- if s.len() != SecretId::ID_SIZE * 2 + 2 {
|
||||||
|
- return Err(serde::de::Error::invalid_length(s.len(), &self));
|
||||||
|
+ if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
||||||
|
+ return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
||||||
|
}
|
||||||
|
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
||||||
|
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
||||||
|
@@ -385,7 +454,6 @@ mod test {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use std::io::{BufReader, BufWriter, Cursor};
|
||||||
|
-
|
||||||
|
#[test]
|
||||||
|
fn dump_secret_entry() {
|
||||||
|
const EXP: &[u8] = &[
|
||||||
|
@@ -516,4 +584,47 @@ mod test {
|
||||||
|
)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_list_ser() {
|
||||||
|
+ let list = SecretList {
|
||||||
|
+ total_num_secrets: 0x112,
|
||||||
|
+ secrets: vec![SecretEntry {
|
||||||
|
+ index: 1.into(),
|
||||||
|
+ stype: 2.into(),
|
||||||
|
+ len: 32.into(),
|
||||||
|
+ res_8: 0,
|
||||||
|
+ id: SecretId::from([0; 32]),
|
||||||
|
+ }],
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ assert_ser_tokens(
|
||||||
|
+ &list,
|
||||||
|
+ &[
|
||||||
|
+ Token::Struct {
|
||||||
|
+ name: "SecretList",
|
||||||
|
+ len: 2,
|
||||||
|
+ },
|
||||||
|
+ Token::String("total_num_secrets"),
|
||||||
|
+ Token::U64(0x112),
|
||||||
|
+ Token::String("secrets"),
|
||||||
|
+ Token::Seq { len: Some(1) },
|
||||||
|
+ Token::Struct {
|
||||||
|
+ name: "SecretEntry",
|
||||||
|
+ len: (4),
|
||||||
|
+ },
|
||||||
|
+ Token::String("index"),
|
||||||
|
+ Token::U16(1),
|
||||||
|
+ Token::String("stype"),
|
||||||
|
+ Token::U16(2),
|
||||||
|
+ Token::String("len"),
|
||||||
|
+ Token::U32(32),
|
||||||
|
+ Token::String("id"),
|
||||||
|
+ Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
+ Token::StructEnd,
|
||||||
|
+ Token::SeqEnd,
|
||||||
|
+ Token::StructEnd,
|
||||||
|
+ ],
|
||||||
|
+ )
|
||||||
|
+ }
|
||||||
|
}
|
877
s390-tools-General-update-06.patch
Normal file
877
s390-tools-General-update-06.patch
Normal file
@ -0,0 +1,877 @@
|
|||||||
|
From fd024387d710887bd2016658c44d4762a08c791c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Tue, 5 Mar 2024 12:19:22 +0100
|
||||||
|
Subject: [PATCH] rust/pv: Retrievable secrets support
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Support retrievable secret for Add-Secret requests.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv/src/crypto.rs | 3 +-
|
||||||
|
rust/pv/src/error.rs | 8 +
|
||||||
|
rust/pv/src/lib.rs | 8 +-
|
||||||
|
rust/pv/src/uvsecret.rs | 1 +
|
||||||
|
rust/pv/src/uvsecret/guest_secret.rs | 399 +++++++++++++++++++++++++--
|
||||||
|
rust/pv/src/uvsecret/retr_secret.rs | 234 ++++++++++++++++
|
||||||
|
6 files changed, 631 insertions(+), 22 deletions(-)
|
||||||
|
create mode 100644 rust/pv/src/uvsecret/retr_secret.rs
|
||||||
|
|
||||||
|
diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs
|
||||||
|
index 8f11d2b4..ebc85f72 100644
|
||||||
|
--- a/rust/pv/src/crypto.rs
|
||||||
|
+++ b/rust/pv/src/crypto.rs
|
||||||
|
@@ -29,7 +29,6 @@ pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
|
||||||
|
|
||||||
|
/// SHA-512 digest length (in bytes)
|
||||||
|
pub const SHA_512_HASH_LEN: usize = 64;
|
||||||
|
-
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const SHA_256_HASH_LEN: u32 = 32;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
@@ -60,6 +59,8 @@ impl SymKeyType {
|
||||||
|
pub const AES_256_XTS_KEY_LEN: usize = 64;
|
||||||
|
/// AES256-XTS tweak length (in bytes)
|
||||||
|
pub const AES_256_XTS_TWEAK_LEN: usize = 16;
|
||||||
|
+ /// AES256 GCM Block length
|
||||||
|
+ pub const AES_256_GCM_BLOCK_LEN: usize = 16;
|
||||||
|
|
||||||
|
/// Returns the tag length of the [`SymKeyType`] if it is an AEAD key
|
||||||
|
pub const fn tag_len(&self) -> Option<usize> {
|
||||||
|
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||||
|
index 3ba808f2..601b40f0 100644
|
||||||
|
--- a/rust/pv/src/error.rs
|
||||||
|
+++ b/rust/pv/src/error.rs
|
||||||
|
@@ -109,6 +109,14 @@ pub enum Error {
|
||||||
|
#[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||||
|
NonAscii,
|
||||||
|
|
||||||
|
+ #[error("Incorrect {what} for a {kind}. Is: {value}; expected: {exp}")]
|
||||||
|
+ RetrInvKey {
|
||||||
|
+ what: &'static str,
|
||||||
|
+ kind: String,
|
||||||
|
+ value: String,
|
||||||
|
+ exp: String,
|
||||||
|
+ },
|
||||||
|
+
|
||||||
|
// errors from other crates
|
||||||
|
#[error(transparent)]
|
||||||
|
PvCore(#[from] pv_core::Error),
|
||||||
|
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||||
|
index ec31b9a4..43375669 100644
|
||||||
|
--- a/rust/pv/src/lib.rs
|
||||||
|
+++ b/rust/pv/src/lib.rs
|
||||||
|
@@ -104,7 +104,12 @@ pub mod request {
|
||||||
|
|
||||||
|
/// Reexports some useful OpenSSL symbols
|
||||||
|
pub mod openssl {
|
||||||
|
- pub use openssl::{error::ErrorStack, hash::DigestBytes, pkey, x509};
|
||||||
|
+ pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509};
|
||||||
|
+ // rust-OpenSSL does not define these NIDs
|
||||||
|
+ #[allow(missing_docs)]
|
||||||
|
+ pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
||||||
|
+ #[allow(missing_docs)]
|
||||||
|
+ pub const NID_ED448: Nid = Nid::from_raw(openssl_sys::NID_ED448);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use pv_core::request::*;
|
||||||
|
@@ -118,6 +123,7 @@ pub mod secret {
|
||||||
|
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
|
||||||
|
ext_secret::ExtSecret,
|
||||||
|
guest_secret::GuestSecret,
|
||||||
|
+ retr_secret::{IbmProtectedKey, RetrievedSecret},
|
||||||
|
user_data::verify_asrcb_and_get_user_data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
diff --git a/rust/pv/src/uvsecret.rs b/rust/pv/src/uvsecret.rs
|
||||||
|
index 343e4b05..c3b43bba 100644
|
||||||
|
--- a/rust/pv/src/uvsecret.rs
|
||||||
|
+++ b/rust/pv/src/uvsecret.rs
|
||||||
|
@@ -10,4 +10,5 @@
|
||||||
|
pub mod asrcb;
|
||||||
|
pub mod ext_secret;
|
||||||
|
pub mod guest_secret;
|
||||||
|
+pub mod retr_secret;
|
||||||
|
pub mod user_data;
|
||||||
|
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
index 4f1db31c..3bad6d3c 100644
|
||||||
|
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
@@ -4,20 +4,34 @@
|
||||||
|
|
||||||
|
#[allow(unused_imports)] // used for more convenient docstring
|
||||||
|
use super::asrcb::AddSecretRequest;
|
||||||
|
-use crate::assert_size;
|
||||||
|
use crate::{
|
||||||
|
- crypto::{hash, random_array},
|
||||||
|
- request::Confidential,
|
||||||
|
- Result,
|
||||||
|
+ assert_size,
|
||||||
|
+ crypto::{hash, random_array, SymKeyType},
|
||||||
|
+ request::{
|
||||||
|
+ openssl::{NID_ED25519, NID_ED448},
|
||||||
|
+ Confidential,
|
||||||
|
+ },
|
||||||
|
+ uv::{
|
||||||
|
+ AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
|
||||||
|
+ RetrieveCmd, SecretId,
|
||||||
|
+ },
|
||||||
|
+ Error, Result,
|
||||||
|
};
|
||||||
|
use byteorder::BigEndian;
|
||||||
|
-use openssl::hash::MessageDigest;
|
||||||
|
-use pv_core::uv::{ListableSecretType, SecretId};
|
||||||
|
+use openssl::{
|
||||||
|
+ hash::MessageDigest,
|
||||||
|
+ nid::Nid,
|
||||||
|
+ pkey::{Id, PKey, Private},
|
||||||
|
+};
|
||||||
|
+use pv_core::static_assert;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
-use std::{convert::TryInto, fmt::Display};
|
||||||
|
+use std::fmt::Display;
|
||||||
|
use zerocopy::{AsBytes, U16, U32};
|
||||||
|
|
||||||
|
const ASSOC_SECRET_SIZE: usize = 32;
|
||||||
|
+/// Maximum size of a plain-text secret payload (8190)
|
||||||
|
+pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
|
||||||
|
+static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
|
||||||
|
|
||||||
|
/// A Secret to be added in [`AddSecretRequest`]
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
@@ -36,13 +50,60 @@ pub enum GuestSecret {
|
||||||
|
#[serde(skip)]
|
||||||
|
secret: Confidential<[u8; ASSOC_SECRET_SIZE]>,
|
||||||
|
},
|
||||||
|
+ /// Retrievable key
|
||||||
|
+ ///
|
||||||
|
+ /// Create Retrievables using [`GuestSecret::retrievable`]
|
||||||
|
+ /// Secret size is always valid for the type/kind
|
||||||
|
+ Retrievable {
|
||||||
|
+ /// Retrievable secret type
|
||||||
|
+ kind: RetrievableSecret,
|
||||||
|
+ /// Name of the secret
|
||||||
|
+ name: String,
|
||||||
|
+ /// SHA256 hash of [`GuestSecret::RetrievableKey::name`]
|
||||||
|
+ id: SecretId,
|
||||||
|
+ /// Confidential actual retrievable secret (32 bytes)
|
||||||
|
+ #[serde(skip)]
|
||||||
|
+ secret: Confidential<Vec<u8>>,
|
||||||
|
+ },
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+macro_rules! retr_constructor {
|
||||||
|
+ ($(#[$err:meta])* | $(#[$kind:meta])* => $type: ty, $func: ident) => {
|
||||||
|
+ /// Create a new
|
||||||
|
+ $(#[$kind])*
|
||||||
|
+ /// [`GuestSecret::Retrievable`] secret.
|
||||||
|
+ ///
|
||||||
|
+ /// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||||
|
+ /// * `secret` - the secret value
|
||||||
|
+ ///
|
||||||
|
+ /// # Errors
|
||||||
|
+ ///
|
||||||
|
+ $(#[$err])*
|
||||||
|
+ pub fn $func(name: &str, secret: $type) -> Result<Self> {
|
||||||
|
+ let (kind, secret) = $func(secret)?;
|
||||||
|
+ Ok(Self::Retrievable {
|
||||||
|
+ kind,
|
||||||
|
+ name: name.to_string(),
|
||||||
|
+ id: Self::name_to_id(name)?,
|
||||||
|
+ secret,
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestSecret {
|
||||||
|
+ fn name_to_id(name: &str) -> Result<SecretId> {
|
||||||
|
+ let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||||
|
+ .to_vec()
|
||||||
|
+ .try_into()
|
||||||
|
+ .unwrap();
|
||||||
|
+ Ok(id.into())
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/// Create a new [`GuestSecret::Association`].
|
||||||
|
///
|
||||||
|
/// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||||
|
- /// * `secret` - Value of the secret. Ranom if [`Option::None`]
|
||||||
|
+ /// * `secret` - Value of the secret. Random if [`Option::None`]
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
@@ -51,10 +112,6 @@ impl GuestSecret {
|
||||||
|
where
|
||||||
|
O: Into<Option<[u8; ASSOC_SECRET_SIZE]>>,
|
||||||
|
{
|
||||||
|
- let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||||
|
- .to_vec()
|
||||||
|
- .try_into()
|
||||||
|
- .unwrap();
|
||||||
|
let secret = match secret.into() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => random_array()?,
|
||||||
|
@@ -62,16 +119,28 @@ impl GuestSecret {
|
||||||
|
|
||||||
|
Ok(Self::Association {
|
||||||
|
name: name.to_string(),
|
||||||
|
- id: id.into(),
|
||||||
|
+ id: Self::name_to_id(name)?,
|
||||||
|
secret: secret.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
+ retr_constructor!(#[doc = r"This function will return an error if the secret is larger than 8 pages"]
|
||||||
|
+ | #[doc = r"plaintext"] => Confidential<Vec<u8>>, plaintext);
|
||||||
|
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||||
|
+ | #[doc = r"AES Key"] => Confidential<Vec<u8>>, aes);
|
||||||
|
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||||
|
+ | #[doc = r"AES-XTS Key"] => Confidential<Vec<u8>>, aes_xts);
|
||||||
|
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||||
|
+ | #[doc = r"HMAC-SHA Key"] => Confidential<Vec<u8>>, hmac_sha);
|
||||||
|
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
||||||
|
+ | #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
||||||
|
+
|
||||||
|
/// Reference to the confidential data
|
||||||
|
pub fn confidential(&self) -> &[u8] {
|
||||||
|
match &self {
|
||||||
|
Self::Null => &[],
|
||||||
|
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||||
|
+ Self::Retrievable { secret, .. } => secret.value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -79,7 +148,7 @@ impl GuestSecret {
|
||||||
|
pub(crate) fn auth(&self) -> SecretAuth {
|
||||||
|
match &self {
|
||||||
|
Self::Null => SecretAuth::Null,
|
||||||
|
- // Panic: every non null secret type is listable -> no panic
|
||||||
|
+ // Panic: every non null secret type is list-able -> no panic
|
||||||
|
listable => {
|
||||||
|
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
|
||||||
|
}
|
||||||
|
@@ -92,6 +161,7 @@ impl GuestSecret {
|
||||||
|
// Null is not listable, but the ListableSecretType provides the type constant (1)
|
||||||
|
Self::Null => ListableSecretType::NULL,
|
||||||
|
Self::Association { .. } => ListableSecretType::ASSOCIATION,
|
||||||
|
+ Self::Retrievable { kind, .. } => kind.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -100,6 +170,7 @@ impl GuestSecret {
|
||||||
|
match self {
|
||||||
|
Self::Null => 0,
|
||||||
|
Self::Association { secret, .. } => secret.value().len() as u32,
|
||||||
|
+ Self::Retrievable { secret, .. } => secret.value().len() as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -107,18 +178,157 @@ impl GuestSecret {
|
||||||
|
fn id(&self) -> Option<SecretId> {
|
||||||
|
match self {
|
||||||
|
Self::Null => None,
|
||||||
|
- Self::Association { id, .. } => Some(id.to_owned()),
|
||||||
|
+ Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+type RetrKeyInfo = (RetrievableSecret, Confidential<Vec<u8>>);
|
||||||
|
+
|
||||||
|
+fn extend_to_multiple(mut key: Vec<u8>, multiple: usize) -> Confidential<Vec<u8>> {
|
||||||
|
+ match key.len().checked_rem(multiple) {
|
||||||
|
+ Some(0) | None => key,
|
||||||
|
+ Some(m) => {
|
||||||
|
+ key.resize(key.len() + multiple - m, 0);
|
||||||
|
+ key
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ .into()
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Get a plain-text key
|
||||||
|
+///
|
||||||
|
+/// ```none
|
||||||
|
+/// size U16<BigEndian> | payload (0-8190) bytes
|
||||||
|
+/// ```
|
||||||
|
+fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||||
|
+ let key_len = inp.value().len();
|
||||||
|
+ if key_len > RetrieveCmd::MAX_SIZE {
|
||||||
|
+ return Err(Error::RetrInvKey {
|
||||||
|
+ what: "key size",
|
||||||
|
+ value: key_len.to_string(),
|
||||||
|
+ kind: RetrievableSecret::PlainText.to_string(),
|
||||||
|
+ exp: RetrievableSecret::PlainText.expected(),
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ let mut key = Vec::with_capacity(2 + inp.value().len());
|
||||||
|
+ let key_len: U16<BigEndian> = (key_len as u16).into();
|
||||||
|
+ key.extend_from_slice(key_len.as_bytes());
|
||||||
|
+ key.extend_from_slice(inp.value());
|
||||||
|
+ let key = extend_to_multiple(key, SymKeyType::AES_256_GCM_BLOCK_LEN);
|
||||||
|
+
|
||||||
|
+ Ok((RetrievableSecret::PlainText, key))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Get an AES-key
|
||||||
|
+fn aes(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||||
|
+ let key_len = key.value().len() as u32;
|
||||||
|
+ let bit_size = bitsize(key_len);
|
||||||
|
+ match AesSizes::from_bits(bit_size) {
|
||||||
|
+ Some(size) => Ok((RetrievableSecret::Aes(size), key)),
|
||||||
|
+ None => {
|
||||||
|
+ // Use some AES type to get exp sizes and name
|
||||||
|
+ let kind = RetrievableSecret::Aes(AesSizes::Bits128);
|
||||||
|
+ Err(Error::RetrInvKey {
|
||||||
|
+ what: "key size",
|
||||||
|
+ value: bit_size.to_string(),
|
||||||
|
+ kind: format!("{kind:#}"),
|
||||||
|
+ exp: kind.expected(),
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Get an AES-XTS-key
|
||||||
|
+fn aes_xts(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||||
|
+ let key_len = key.value().len() as u32;
|
||||||
|
+ let bit_size = bitsize(key_len / 2);
|
||||||
|
+ match AesXtsSizes::from_bits(bit_size) {
|
||||||
|
+ Some(size) => Ok((RetrievableSecret::AesXts(size), key)),
|
||||||
|
+ None => {
|
||||||
|
+ // Use some AES-XTS type to get exp sizes and name
|
||||||
|
+ let kind = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||||
|
+ Err(Error::RetrInvKey {
|
||||||
|
+ what: "key size",
|
||||||
|
+ value: bit_size.to_string(),
|
||||||
|
+ kind: format!("{kind:#}"),
|
||||||
|
+ exp: kind.expected(),
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Get an HMAC-SHA-key
|
||||||
|
+fn hmac_sha(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||||
|
+ let key_len = key.value().len() as u32;
|
||||||
|
+ let size = bitsize(key_len / 2);
|
||||||
|
+ match HmacShaSizes::from_sha_size(size) {
|
||||||
|
+ Some(size) => Ok((RetrievableSecret::HmacSha(size), key)),
|
||||||
|
+ None => {
|
||||||
|
+ // Use some HMAC type to get exp sizes and name
|
||||||
|
+ let kind = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||||
|
+ Err(Error::RetrInvKey {
|
||||||
|
+ what: "key size",
|
||||||
|
+ value: size.to_string(),
|
||||||
|
+ kind: format!("{kind:#}"),
|
||||||
|
+ exp: kind.expected(),
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Get an EC-private-key
|
||||||
|
+fn ec(key: PKey<Private>) -> Result<RetrKeyInfo> {
|
||||||
|
+ let (key, nid) = match key.id() {
|
||||||
|
+ Id::EC => {
|
||||||
|
+ let ec_key = key.ec_key()?;
|
||||||
|
+ let key = ec_key.private_key().to_vec();
|
||||||
|
+ let nid = ec_key.group().curve_name().unwrap_or(Nid::UNDEF);
|
||||||
|
+ (key, nid)
|
||||||
|
+ }
|
||||||
|
+ // ED keys are not handled via the EC struct in OpenSSL.
|
||||||
|
+ id @ (Id::ED25519 | Id::ED448) => {
|
||||||
|
+ let key = key.raw_private_key()?;
|
||||||
|
+ let nid = Nid::from_raw(id.as_raw());
|
||||||
|
+ (key, nid)
|
||||||
|
+ }
|
||||||
|
+ _ => (vec![], Nid::UNDEF),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let kind = match nid {
|
||||||
|
+ Nid::X9_62_PRIME256V1 => EcCurves::Secp256R1,
|
||||||
|
+ Nid::SECP384R1 => EcCurves::Secp384R1,
|
||||||
|
+ Nid::SECP521R1 => EcCurves::Secp521R1,
|
||||||
|
+ NID_ED25519 => EcCurves::Ed25519,
|
||||||
|
+ NID_ED448 => EcCurves::Ed448,
|
||||||
|
+ nid => {
|
||||||
|
+ // Use some EC type to get exp sizes and name
|
||||||
|
+ let ec = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||||
|
+ return Err(Error::RetrInvKey {
|
||||||
|
+ what: "curve or format",
|
||||||
|
+ kind: format!("{ec:#}"),
|
||||||
|
+ value: nid.long_name()?.to_string(),
|
||||||
|
+ exp: ec.expected(),
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let key = kind.resize_raw_key(key);
|
||||||
|
+ Ok((RetrievableSecret::Ec(kind), key.into()))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[inline(always)]
|
||||||
|
+const fn bitsize(bytesize: u32) -> u32 {
|
||||||
|
+ bytesize * 8
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
impl Display for GuestSecret {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Null => write!(f, "Meta"),
|
||||||
|
gs => {
|
||||||
|
let kind: U16<BigEndian> = gs.kind().into();
|
||||||
|
- let st: ListableSecretType = kind.into();
|
||||||
|
+ let st: ListableSecretType = kind.get().into();
|
||||||
|
write!(f, "{st}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -153,20 +363,24 @@ assert_size!(ListableSecretHdr, 0x30);
|
||||||
|
|
||||||
|
impl ListableSecretHdr {
|
||||||
|
fn from_guest_secret(gs: &GuestSecret) -> Option<Self> {
|
||||||
|
- let id = gs.id()?;
|
||||||
|
Some(Self {
|
||||||
|
res0: 0,
|
||||||
|
kind: gs.kind().into(),
|
||||||
|
secret_len: gs.secret_len().into(),
|
||||||
|
res8: 0,
|
||||||
|
- id,
|
||||||
|
+ id: gs.id()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
+
|
||||||
|
+ use super::HmacShaSizes as HmacSizes;
|
||||||
|
+ use super::RetrievableSecret::*;
|
||||||
|
use super::*;
|
||||||
|
+ use openssl::ec::{EcGroup, EcKey};
|
||||||
|
+ use pv_core::uv::AesSizes;
|
||||||
|
use serde_test::{assert_tokens, Token};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
@@ -187,8 +401,103 @@ mod test {
|
||||||
|
assert_eq!(secret, exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ macro_rules! retr_test {
|
||||||
|
+ ($name: ident, $func: ident, $size: expr, $exp_kind: expr) => {
|
||||||
|
+ #[test]
|
||||||
|
+ fn $name() {
|
||||||
|
+ let secret_value = vec![0x11; $size];
|
||||||
|
+ let name = "test retr secret".to_string();
|
||||||
|
+ let secret = GuestSecret::$func(&name, secret_value.clone().into()).unwrap();
|
||||||
|
+ let exp_id = [
|
||||||
|
+ 0x61, 0x2c, 0xd6, 0x3e, 0xa8, 0xf2, 0xc1, 0x15, 0xc1, 0xe, 0x15, 0xb8, 0x8a,
|
||||||
|
+ 0x90, 0x16, 0xc1, 0x55, 0xef, 0x9c, 0x7c, 0x2c, 0x8e, 0x56, 0xd0, 0x78, 0x4c,
|
||||||
|
+ 0x8a, 0x1d, 0xc9, 0x3a, 0x80, 0xba,
|
||||||
|
+ ];
|
||||||
|
+ let exp = GuestSecret::Retrievable {
|
||||||
|
+ kind: $exp_kind,
|
||||||
|
+ name,
|
||||||
|
+ id: exp_id.into(),
|
||||||
|
+ secret: secret_value.into(),
|
||||||
|
+ };
|
||||||
|
+ assert_eq!(exp, secret);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ retr_test!(retr_aes_128, aes, 16, Aes(AesSizes::Bits128));
|
||||||
|
+ retr_test!(retr_aes_192, aes, 24, Aes(AesSizes::Bits192));
|
||||||
|
+ retr_test!(retr_aes_256, aes, 32, Aes(AesSizes::Bits256));
|
||||||
|
+ retr_test!(retr_aes_xts_128, aes_xts, 32, AesXts(AesXtsSizes::Bits128));
|
||||||
|
+ retr_test!(retr_aes_xts_256, aes_xts, 64, AesXts(AesXtsSizes::Bits256));
|
||||||
|
+ retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
|
||||||
|
+ retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn plaintext_no_pad() {
|
||||||
|
+ let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
||||||
|
+ let name = "PLAINTEXT_PAD".to_string();
|
||||||
|
+ let secret = GuestSecret::plaintext(&name, key[2..].to_vec().into()).unwrap();
|
||||||
|
+ let exp_id = [
|
||||||
|
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||||
|
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||||
|
+ ];
|
||||||
|
+ let exp = GuestSecret::Retrievable {
|
||||||
|
+ kind: PlainText,
|
||||||
|
+ name,
|
||||||
|
+ id: exp_id.into(),
|
||||||
|
+ secret: key.into(),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ assert_eq!(secret, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
#[test]
|
||||||
|
- fn ap_asc_parse() {
|
||||||
|
+ fn plaintext_pad() {
|
||||||
|
+ let key = vec![0, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0];
|
||||||
|
+ let name = "PLAINTEXT_PAD".to_string();
|
||||||
|
+ let secret = GuestSecret::plaintext(&name, key[2..12].to_vec().into()).unwrap();
|
||||||
|
+ let exp_id = [
|
||||||
|
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||||
|
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||||
|
+ ];
|
||||||
|
+ let exp = GuestSecret::Retrievable {
|
||||||
|
+ kind: PlainText,
|
||||||
|
+ name,
|
||||||
|
+ id: exp_id.into(),
|
||||||
|
+ secret: key.into(),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ assert_eq!(secret, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[track_caller]
|
||||||
|
+ fn test_ec(grp: Nid, exp_kind: EcCurves, exp_len: usize) {
|
||||||
|
+ let key = match grp {
|
||||||
|
+ NID_ED25519 => PKey::generate_ed25519().unwrap(),
|
||||||
|
+ NID_ED448 => PKey::generate_ed448().unwrap(),
|
||||||
|
+ nid => {
|
||||||
|
+ let group = EcGroup::from_curve_name(nid).unwrap();
|
||||||
|
+ let key = EcKey::generate(&group).unwrap();
|
||||||
|
+ PKey::from_ec_key(key).unwrap()
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let (kind, key) = ec(key).unwrap();
|
||||||
|
+
|
||||||
|
+ assert_eq!(kind, Ec(exp_kind));
|
||||||
|
+ assert_eq!(key.value().len(), exp_len);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn retr_ec() {
|
||||||
|
+ test_ec(Nid::X9_62_PRIME256V1, EcCurves::Secp256R1, 32);
|
||||||
|
+ test_ec(Nid::SECP384R1, EcCurves::Secp384R1, 48);
|
||||||
|
+ test_ec(Nid::SECP521R1, EcCurves::Secp521R1, 80);
|
||||||
|
+ test_ec(NID_ED25519, EcCurves::Ed25519, 32);
|
||||||
|
+ test_ec(NID_ED448, EcCurves::Ed448, 64);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn asc_parse() {
|
||||||
|
let id = [
|
||||||
|
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||||
|
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||||
|
@@ -217,6 +526,39 @@ mod test {
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ #[test]
|
||||||
|
+ fn retrievable_parse() {
|
||||||
|
+ let id = [
|
||||||
|
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||||
|
+ 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||||
|
+ 0x89, 0xab, 0xcd, 0xef,
|
||||||
|
+ ];
|
||||||
|
+ let asc = GuestSecret::Retrievable {
|
||||||
|
+ kind: PlainText,
|
||||||
|
+ name: "test123".to_string(),
|
||||||
|
+ id: id.into(),
|
||||||
|
+ secret: vec![].into(),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ assert_tokens(
|
||||||
|
+ &asc,
|
||||||
|
+ &[
|
||||||
|
+ Token::StructVariant {
|
||||||
|
+ name: "GuestSecret",
|
||||||
|
+ variant: "Retrievable",
|
||||||
|
+ len: 3,
|
||||||
|
+ },
|
||||||
|
+ Token::String("kind"),
|
||||||
|
+ Token::String("3 (PLAINTEXT)"),
|
||||||
|
+ Token::String("name"),
|
||||||
|
+ Token::String("test123"),
|
||||||
|
+ Token::String("id"),
|
||||||
|
+ Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
|
||||||
|
+ Token::StructVariantEnd,
|
||||||
|
+ ],
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
#[test]
|
||||||
|
fn guest_secret_bin_null() {
|
||||||
|
let gs = GuestSecret::Null;
|
||||||
|
@@ -228,7 +570,7 @@ mod test {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
- fn guest_secret_bin_ap() {
|
||||||
|
+ fn guest_secret_bin_asoc() {
|
||||||
|
let gs = GuestSecret::Association {
|
||||||
|
name: "test".to_string(),
|
||||||
|
id: [1; 32].into(),
|
||||||
|
@@ -241,4 +583,21 @@ mod test {
|
||||||
|
assert_eq!(exp, gs_bytes_auth.get());
|
||||||
|
assert_eq!(&[2; 32], gs.confidential());
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn guest_secret_bin_retr() {
|
||||||
|
+ let gs = GuestSecret::Retrievable {
|
||||||
|
+ kind: PlainText,
|
||||||
|
+ name: "test".to_string(),
|
||||||
|
+ id: [1; 32].into(),
|
||||||
|
+ secret: vec![2; 32].into(),
|
||||||
|
+ };
|
||||||
|
+ let auth = gs.auth();
|
||||||
|
+ let gs_bytes_auth = auth.get();
|
||||||
|
+ let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
+ exp.extend([1; 32]);
|
||||||
|
+
|
||||||
|
+ assert_eq!(exp, gs_bytes_auth);
|
||||||
|
+ assert_eq!(&[2; 32], gs.confidential());
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
diff --git a/rust/pv/src/uvsecret/retr_secret.rs b/rust/pv/src/uvsecret/retr_secret.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..5fad016f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/rust/pv/src/uvsecret/retr_secret.rs
|
||||||
|
@@ -0,0 +1,234 @@
|
||||||
|
+// SPDX-License-Identifier: MIT
|
||||||
|
+//
|
||||||
|
+// Copyright IBM Corp. 2024
|
||||||
|
+
|
||||||
|
+use crate::{pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
|
||||||
|
+
|
||||||
|
+use byteorder::BigEndian;
|
||||||
|
+use log::warn;
|
||||||
|
+use pv_core::{
|
||||||
|
+ request::Confidential,
|
||||||
|
+ uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
|
||||||
|
+};
|
||||||
|
+use zerocopy::{FromBytes, U16};
|
||||||
|
+
|
||||||
|
+/// An IBM Protected Key
|
||||||
|
+///
|
||||||
|
+/// A protected key, writeable as pem.
|
||||||
|
+///
|
||||||
|
+/// Will convert into PEM as:
|
||||||
|
+/// ```PEM
|
||||||
|
+///-----BEGIN IBM PROTECTED KEY-----
|
||||||
|
+///kind: <name>
|
||||||
|
+///
|
||||||
|
+///<protected key in base64>
|
||||||
|
+///-----END IBM PROTECTED KEY-----
|
||||||
|
+/// ```
|
||||||
|
+#[derive(Debug, PartialEq, Eq)]
|
||||||
|
+pub struct IbmProtectedKey {
|
||||||
|
+ kind: ListableSecretType,
|
||||||
|
+ key: Confidential<Vec<u8>>,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl IbmProtectedKey {
|
||||||
|
+ /// Get the binary representation of the key.
|
||||||
|
+ pub fn data(&self) -> &[u8] {
|
||||||
|
+ self.key.value()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||||
|
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||||
|
+ self.key
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Get the data in PEM format.
|
||||||
|
+ ///
|
||||||
|
+ /// # Errors
|
||||||
|
+ ///
|
||||||
|
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||||
|
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||||
|
+ Pem::new(
|
||||||
|
+ "IBM PROTECTED KEY",
|
||||||
|
+ format!("kind: {}", self.kind),
|
||||||
|
+ self.key.value(),
|
||||||
|
+ )
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn new<K>(kind: ListableSecretType, key: K) -> Self
|
||||||
|
+ where
|
||||||
|
+ K: Into<Confidential<Vec<u8>>>,
|
||||||
|
+ {
|
||||||
|
+ Self {
|
||||||
|
+ kind,
|
||||||
|
+ key: key.into(),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl From<RetrieveCmd> for RetrievedSecret {
|
||||||
|
+ fn from(value: RetrieveCmd) -> Self {
|
||||||
|
+ let kind = value.meta_data().stype();
|
||||||
|
+ let key = value.into_key();
|
||||||
|
+
|
||||||
|
+ match kind {
|
||||||
|
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText) => {
|
||||||
|
+ // Will not run into default, retrieve has a granularity of 16 bytes and 16 bytes is the
|
||||||
|
+ // minimum size
|
||||||
|
+ let len = U16::<BigEndian>::read_from_prefix(key.value())
|
||||||
|
+ .unwrap_or_default()
|
||||||
|
+ .get() as usize;
|
||||||
|
+
|
||||||
|
+ // Test if the plain text secret has a size:
|
||||||
|
+ // 1. len <= 8190
|
||||||
|
+ // 2. first two bytes are max 15 less than buffer-size+2
|
||||||
|
+ // 3. bytes after len + 2 are zero
|
||||||
|
+ match len <= MAX_SIZE_PLAIN_PAYLOAD
|
||||||
|
+ && key.value().len() - (len + 2) < 15
|
||||||
|
+ && key.value()[len + 2..].iter().all(|c| *c == 0)
|
||||||
|
+ {
|
||||||
|
+ false => Self::Plaintext(key),
|
||||||
|
+ true => Self::Plaintext(key.value()[2..len + 2].to_vec().into()),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ kind => {
|
||||||
|
+ match kind {
|
||||||
|
+ ListableSecretType::Retrievable(_) => (),
|
||||||
|
+ _ => warn!("Retrieved an unretrievable Secret! Will continue; interpreting it as a protected key."),
|
||||||
|
+ }
|
||||||
|
+ Self::ProtectedKey(IbmProtectedKey::new(kind, key))
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// A retrieved Secret.
|
||||||
|
+#[derive(Debug, PartialEq, Eq)]
|
||||||
|
+pub enum RetrievedSecret {
|
||||||
|
+ /// A plaintext secret
|
||||||
|
+ Plaintext(Confidential<Vec<u8>>),
|
||||||
|
+ /// An [`IbmProtectedKey`]
|
||||||
|
+ ProtectedKey(IbmProtectedKey),
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl RetrievedSecret {
|
||||||
|
+ /// Create a new IBM PROTECTED KEY object
|
||||||
|
+ pub fn from_cmd(cmd: RetrieveCmd) -> Self {
|
||||||
|
+ cmd.into()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Get the binary representation of the key.
|
||||||
|
+ pub fn data(&self) -> &[u8] {
|
||||||
|
+ match self {
|
||||||
|
+ RetrievedSecret::Plaintext(p) => p.value(),
|
||||||
|
+ RetrievedSecret::ProtectedKey(p) => p.data(),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||||
|
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||||
|
+ match self {
|
||||||
|
+ RetrievedSecret::Plaintext(p) => p,
|
||||||
|
+ RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ /// Get the data in PEM format.
|
||||||
|
+ ///
|
||||||
|
+ /// # Errors
|
||||||
|
+ ///
|
||||||
|
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||||
|
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||||
|
+ match self {
|
||||||
|
+ RetrievedSecret::Plaintext(p) => Pem::new("PLAINTEXT SECRET", None, p.value()),
|
||||||
|
+ RetrievedSecret::ProtectedKey(p) => p.to_pem(),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[cfg(test)]
|
||||||
|
+mod test {
|
||||||
|
+ use super::*;
|
||||||
|
+ use pv_core::uv::*;
|
||||||
|
+
|
||||||
|
+ fn mk_retr(secret: &[u8]) -> RetrievedSecret {
|
||||||
|
+ let entry = SecretEntry::new(
|
||||||
|
+ 0,
|
||||||
|
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText),
|
||||||
|
+ SecretId::default(),
|
||||||
|
+ secret.len() as u32,
|
||||||
|
+ );
|
||||||
|
+ let mut cmd = RetrieveCmd::from_entry(entry).unwrap();
|
||||||
|
+ cmd.data().unwrap().copy_from_slice(secret);
|
||||||
|
+ RetrievedSecret::from_cmd(cmd)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn from_retr_cmd() {
|
||||||
|
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0];
|
||||||
|
+ let prot_key = mk_retr(&secret);
|
||||||
|
+ let exp = RetrievedSecret::Plaintext(secret[2..12].to_vec().into());
|
||||||
|
+ assert_eq!(prot_key, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn from_retr_inv_size() {
|
||||||
|
+ let secret = vec![0x20; 32];
|
||||||
|
+ let prot_key = mk_retr(&secret);
|
||||||
|
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||||
|
+ assert_eq!(prot_key, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn from_retr_inv_no_zero_after_end() {
|
||||||
|
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 1, 0, 0, 0];
|
||||||
|
+ let prot_key = mk_retr(&secret);
|
||||||
|
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||||
|
+ assert_eq!(prot_key, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn from_retr_inv_to_much_padding() {
|
||||||
|
+ let secret = vec![
|
||||||
|
+ 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
+ 0, 0, 0, 0,
|
||||||
|
+ ];
|
||||||
|
+ let prot_key = mk_retr(&secret);
|
||||||
|
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||||
|
+ assert_eq!(prot_key, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn from_retr_0_size() {
|
||||||
|
+ let secret = vec![0x00; 32];
|
||||||
|
+ let prot_key = mk_retr(&secret);
|
||||||
|
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||||
|
+ assert_eq!(prot_key, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn plain_text_pem() {
|
||||||
|
+ let exp = "\
|
||||||
|
+ -----BEGIN PLAINTEXT SECRET-----\n\
|
||||||
|
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||||
|
+ -----END PLAINTEXT SECRET-----\n";
|
||||||
|
+ let prot = RetrievedSecret::Plaintext(vec![17; 48].into());
|
||||||
|
+ let pem = prot.to_pem().unwrap();
|
||||||
|
+ let pem_str = pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn prot_key_pem() {
|
||||||
|
+ let exp = "\
|
||||||
|
+ -----BEGIN IBM PROTECTED KEY-----\n\
|
||||||
|
+ kind: AES-128-KEY\n\n\
|
||||||
|
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||||
|
+ -----END IBM PROTECTED KEY-----\n";
|
||||||
|
+ let prot = IbmProtectedKey::new(
|
||||||
|
+ ListableSecretType::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||||
|
+ vec![17; 48],
|
||||||
|
+ );
|
||||||
|
+ let pem = prot.to_pem().unwrap();
|
||||||
|
+ let pem_str = pem.to_string();
|
||||||
|
+ assert_eq!(pem_str, exp);
|
||||||
|
+ }
|
||||||
|
+}
|
95
s390-tools-General-update-07.patch
Normal file
95
s390-tools-General-update-07.patch
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
From a14f9d4edcc5db0d54e4fbe3ec3d98c7c270bf8e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Fri, 13 Dec 2024 15:04:02 +0100
|
||||||
|
Subject: [PATCH] rust/pvsecret: Improve CLI
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Improve the wording of the help/man text/
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvsecret/src/cli.rs | 26 +++++++++++++-------------
|
||||||
|
1 file changed, 13 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||||
|
index 6deaaebd..c4b9f2b3 100644
|
||||||
|
--- a/rust/pvsecret/src/cli.rs
|
||||||
|
+++ b/rust/pvsecret/src/cli.rs
|
||||||
|
@@ -37,8 +37,8 @@ pub struct CreateSecretOpt {
|
||||||
|
|
||||||
|
/// Specifies the header of the guest image.
|
||||||
|
///
|
||||||
|
- /// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure
|
||||||
|
- /// Execution header. The header must start at a page boundary.
|
||||||
|
+ /// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an
|
||||||
|
+ /// extracted IBM Secure Execution header.
|
||||||
|
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
|
||||||
|
pub hdr: String,
|
||||||
|
|
||||||
|
@@ -150,12 +150,12 @@ pub enum AddSecretType {
|
||||||
|
|
||||||
|
/// Create an association secret.
|
||||||
|
///
|
||||||
|
- /// Use an association secret to connect a trusted I/O device to a guest. The `pvapconfig` tool
|
||||||
|
+ /// Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool
|
||||||
|
/// provides more information about association secrets.
|
||||||
|
Association {
|
||||||
|
- /// String to identify the new secret.
|
||||||
|
+ /// String that identifies the new secret.
|
||||||
|
///
|
||||||
|
- /// The actual secret is set with --input-secret. The name is saved in `NAME.yaml` with
|
||||||
|
+ /// The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with
|
||||||
|
/// white-spaces mapped to `_`.
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
@@ -166,15 +166,15 @@ pub enum AddSecretType {
|
||||||
|
stdout: bool,
|
||||||
|
|
||||||
|
/// Path from which to read the plaintext secret. Uses a random secret if not specified.
|
||||||
|
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||||
|
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||||
|
input_secret: Option<String>,
|
||||||
|
|
||||||
|
- /// Save the generated secret as plaintext in FILE.
|
||||||
|
+ /// Save the generated secret as plaintext in SECRET-FILE.
|
||||||
|
///
|
||||||
|
/// The generated secret can be used to generate add-secret requests for a different guest
|
||||||
|
- /// with the same secret using --input-secret. Destroy the secret when it is not used
|
||||||
|
+ /// with the same secret using '--input-secret'. Destroy the secret when it is not used
|
||||||
|
/// anymore.
|
||||||
|
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||||
|
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||||
|
output_secret: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@@ -243,13 +243,13 @@ pub enum Command {
|
||||||
|
/// Create a new add-secret request.
|
||||||
|
///
|
||||||
|
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a
|
||||||
|
- /// trusted environment, such as your workstation. The `pvattest create` command creates a
|
||||||
|
+ /// trusted environment, such as your workstation. The 'pvattest create' command creates a
|
||||||
|
/// randomly generated key to protect the request. The generated requests can then be added on
|
||||||
|
- /// an IBM Secure Execution guest using `pvsecret add`. The guest can then use the secrets with
|
||||||
|
+ /// an IBM Secure Execution guest using 'pvsecret add'. The guest can then use the secrets with
|
||||||
|
/// the use case depending on the secret type.
|
||||||
|
Create(Box<CreateSecretOpt>),
|
||||||
|
|
||||||
|
- /// Perform an add-secret request (s390x only).
|
||||||
|
+ /// Submit an add-secret request to the Ultravisor (s390x only).
|
||||||
|
///
|
||||||
|
/// Perform an add-secret request using a previously generated add-secret request. Only
|
||||||
|
/// available on s390x.
|
||||||
|
@@ -258,7 +258,7 @@ pub enum Command {
|
||||||
|
/// Lock the secret-store (s390x only).
|
||||||
|
///
|
||||||
|
/// Lock the secret store (s390x only). After this command executed successfully, all
|
||||||
|
- /// add-secret requests will fail. Only available on s390x.
|
||||||
|
+ /// subsequent add-secret requests will fail. Only available on s390x.
|
||||||
|
Lock,
|
||||||
|
|
||||||
|
/// List all ultravisor secrets (s390x only).
|
423
s390-tools-General-update-08.patch
Normal file
423
s390-tools-General-update-08.patch
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
From 93da795520ca2f0a73cfbfc951a9b16437a1b95b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Mon, 19 Feb 2024 15:15:16 +0100
|
||||||
|
Subject: [PATCH] rust/pvsecret: Add support for retrievable secrets
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Support for creating and retrieving retrievable secrets.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvsecret/src/cli.rs | 129 +++++++++++++++++++++++++++++++-
|
||||||
|
rust/pvsecret/src/cmd.rs | 6 +-
|
||||||
|
rust/pvsecret/src/cmd/create.rs | 30 +++++++-
|
||||||
|
rust/pvsecret/src/cmd/list.rs | 12 ++-
|
||||||
|
rust/pvsecret/src/cmd/retr.rs | 62 +++++++++++++++
|
||||||
|
rust/pvsecret/src/main.rs | 1 +
|
||||||
|
6 files changed, 230 insertions(+), 10 deletions(-)
|
||||||
|
create mode 100644 rust/pvsecret/src/cmd/retr.rs
|
||||||
|
|
||||||
|
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||||
|
index c4b9f2b3..4e747682 100644
|
||||||
|
--- a/rust/pvsecret/src/cli.rs
|
||||||
|
+++ b/rust/pvsecret/src/cli.rs
|
||||||
|
@@ -1,7 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
-// Copyright IBM Corp. 2023
|
||||||
|
+// Copyright IBM Corp. 2023, 2024
|
||||||
|
|
||||||
|
+use std::fmt::Display;
|
||||||
|
+
|
||||||
|
+use clap::error::ErrorKind::ValueValidation;
|
||||||
|
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
|
||||||
|
use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT};
|
||||||
|
|
||||||
|
@@ -177,6 +180,72 @@ pub enum AddSecretType {
|
||||||
|
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||||
|
output_secret: Option<String>,
|
||||||
|
},
|
||||||
|
+
|
||||||
|
+ /// Create a retrievable secret.
|
||||||
|
+ ///
|
||||||
|
+ /// A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can
|
||||||
|
+ /// retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext
|
||||||
|
+ /// secret, are retrieved as wrapped/protected key objects and only usable inside the current,
|
||||||
|
+ /// running SE-guest instance.
|
||||||
|
+ #[command(visible_alias = "retr")]
|
||||||
|
+ Retrievable {
|
||||||
|
+ /// String that identifies the new secret.
|
||||||
|
+ ///
|
||||||
|
+ /// The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with
|
||||||
|
+ /// white-spaces mapped to `_`.
|
||||||
|
+ name: String,
|
||||||
|
+
|
||||||
|
+ /// Print the hashed name to stdout.
|
||||||
|
+ ///
|
||||||
|
+ /// The hashed name is not written to `NAME.yaml`
|
||||||
|
+ #[arg(long)]
|
||||||
|
+ stdout: bool,
|
||||||
|
+
|
||||||
|
+ /// Use SECRET-FILE as retrievable secret
|
||||||
|
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath)]
|
||||||
|
+ secret: String,
|
||||||
|
+
|
||||||
|
+ /// Specify the secret type.
|
||||||
|
+ ///
|
||||||
|
+ /// Limitations to the input data apply depending on the secret type.
|
||||||
|
+ #[arg(long = "type", value_name = "TYPE")]
|
||||||
|
+ kind: RetrieveableSecretInpKind,
|
||||||
|
+ },
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||||
|
+pub enum RetrieveableSecretInpKind {
|
||||||
|
+ /// A plaintext secret.
|
||||||
|
+ /// Can be any file up to 8190 bytes long
|
||||||
|
+ Plain,
|
||||||
|
+ /// An AES key.
|
||||||
|
+ /// Must be a plain byte file 128, 192, or 256 bit long.
|
||||||
|
+ Aes,
|
||||||
|
+ /// An AES-XTS key.
|
||||||
|
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||||
|
+ AesXts,
|
||||||
|
+ /// A HMAC-SHA key.
|
||||||
|
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||||
|
+ HmacSha,
|
||||||
|
+ /// An elliptic curve private key.
|
||||||
|
+ /// Must be a PEM or DER file.
|
||||||
|
+ Ec,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Display for RetrieveableSecretInpKind {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ write!(
|
||||||
|
+ f,
|
||||||
|
+ "{}",
|
||||||
|
+ match self {
|
||||||
|
+ Self::Plain => "PLAINTEXT",
|
||||||
|
+ Self::Aes => "AES KEY",
|
||||||
|
+ Self::AesXts => "AES-XTS KEY",
|
||||||
|
+ Self::HmacSha => "HMAC-SHA KEY",
|
||||||
|
+ Self::Ec => "EC PRIVATE KEY",
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// all members s390x only
|
||||||
|
@@ -238,6 +307,56 @@ pub struct VerifyOpt {
|
||||||
|
pub output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
+// all members s390x only
|
||||||
|
+#[derive(Args, Debug)]
|
||||||
|
+pub struct RetrSecretOptions {
|
||||||
|
+ /// Specify the secret ID to be retrieved.
|
||||||
|
+ ///
|
||||||
|
+ /// Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml
|
||||||
|
+ /// created by the create subcommand of this tool. If `hex` is specified, it must be a hex
|
||||||
|
+ /// 32-byte unsigned big endian number string. Leading zeros are required.
|
||||||
|
+ #[cfg(target_arch = "s390x")]
|
||||||
|
+ #[arg(value_name = "ID", value_hint = ValueHint::FilePath)]
|
||||||
|
+ pub input: String,
|
||||||
|
+
|
||||||
|
+ /// Specify the output path to place the secret value
|
||||||
|
+ #[cfg(target_arch = "s390x")]
|
||||||
|
+ #[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)]
|
||||||
|
+ pub output: String,
|
||||||
|
+
|
||||||
|
+ /// Define input type for the Secret ID
|
||||||
|
+ #[cfg(target_arch = "s390x")]
|
||||||
|
+ #[arg(long, value_enum, default_value_t)]
|
||||||
|
+ pub inform: RetrInpFmt,
|
||||||
|
+
|
||||||
|
+ /// Define the output format for the retrieved secret
|
||||||
|
+ #[cfg(target_arch = "s390x")]
|
||||||
|
+ #[arg(long, value_enum, default_value_t)]
|
||||||
|
+ pub outform: RetrOutFmt,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||||
|
+pub enum RetrInpFmt {
|
||||||
|
+ /// Use a yaml file
|
||||||
|
+ #[default]
|
||||||
|
+ Yaml,
|
||||||
|
+ /// Use a hex string.
|
||||||
|
+ Hex,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||||
|
+pub enum RetrOutFmt {
|
||||||
|
+ /// Write the secret as PEM.
|
||||||
|
+ ///
|
||||||
|
+ /// File starts with `-----BEGIN IBM PROTECTED KEY----` and `-----BEGIN
|
||||||
|
+ /// PLAINTEXT SECRET-----` for plaintext secrets it contains one header
|
||||||
|
+ /// line with the type information and the base64 protected key
|
||||||
|
+ #[default]
|
||||||
|
+ Pem,
|
||||||
|
+ /// Write the secret in binary.
|
||||||
|
+ Bin,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
pub enum Command {
|
||||||
|
/// Create a new add-secret request.
|
||||||
|
@@ -274,6 +393,10 @@ pub enum Command {
|
||||||
|
/// provided key. Outputs the arbitrary user-data.
|
||||||
|
Verify(VerifyOpt),
|
||||||
|
|
||||||
|
+ /// Retrieve a secret from the UV secret store (s390x only).
|
||||||
|
+ #[command(visible_alias = "retr")]
|
||||||
|
+ Retrieve(RetrSecretOptions),
|
||||||
|
+
|
||||||
|
/// Print version information and exit.
|
||||||
|
#[command(aliases(["--version"]), hide(true))]
|
||||||
|
Version,
|
||||||
|
@@ -294,13 +417,13 @@ pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
|
||||||
|
}
|
||||||
|
if secret_out == &Some(format!("{name}.yaml")) {
|
||||||
|
return Err(CliOptions::command().error(
|
||||||
|
- clap::error::ErrorKind::ValueValidation,
|
||||||
|
+ ValueValidation,
|
||||||
|
format!("Secret output file and the secret name '{name}.yaml' are the same."),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if format!("{name}.yaml") == opt.output {
|
||||||
|
return Err(CliOptions::command().error(
|
||||||
|
- clap::error::ErrorKind::ValueValidation,
|
||||||
|
+ ValueValidation,
|
||||||
|
format!(
|
||||||
|
"output file and the secret name '{}' are the same.",
|
||||||
|
&opt.output
|
||||||
|
diff --git a/rust/pvsecret/src/cmd.rs b/rust/pvsecret/src/cmd.rs
|
||||||
|
index a826fb31..10d99a5b 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd.rs
|
||||||
|
@@ -16,6 +16,8 @@ mod add;
|
||||||
|
mod list;
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
mod lock;
|
||||||
|
+#[cfg(target_arch = "s390x")]
|
||||||
|
+mod retr;
|
||||||
|
|
||||||
|
// Commands (directly) related to UVCs are only available on s389x
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
@@ -24,12 +26,13 @@ mod uv_cmd {
|
||||||
|
pub use add::add;
|
||||||
|
pub use list::list;
|
||||||
|
pub use lock::lock;
|
||||||
|
+ pub use retr::retr;
|
||||||
|
pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "s390x"))]
|
||||||
|
mod uv_cmd {
|
||||||
|
- use crate::cli::{AddSecretOpt, ListSecretOpt};
|
||||||
|
+ use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
macro_rules! not_supp {
|
||||||
|
($name: ident $( ,$opt: ty )?) => {
|
||||||
|
@@ -40,6 +43,7 @@ mod uv_cmd {
|
||||||
|
}
|
||||||
|
not_supp!(add, AddSecretOpt);
|
||||||
|
not_supp!(list, ListSecretOpt);
|
||||||
|
+ not_supp!(retr, RetrSecretOptions);
|
||||||
|
not_supp!(lock);
|
||||||
|
pub const UV_CMD_FN: &[&str] = &[];
|
||||||
|
}
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
index 9251c38c..73089a12 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
@@ -4,7 +4,6 @@
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
-use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
|
||||||
|
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||||
|
use log::{debug, info, trace, warn};
|
||||||
|
use pv::{
|
||||||
|
@@ -22,6 +21,8 @@ use pv::{
|
||||||
|
use serde_yaml::Value;
|
||||||
|
use utils::get_writer_from_cli_file_arg;
|
||||||
|
|
||||||
|
+use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||||
|
+
|
||||||
|
fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
@@ -32,6 +33,23 @@ where
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
+fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
|
||||||
|
+ let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
|
||||||
|
+
|
||||||
|
+ match kind {
|
||||||
|
+ RetrieveableSecretInpKind::Plain => GuestSecret::plaintext(name, secret_data),
|
||||||
|
+ RetrieveableSecretInpKind::Aes => GuestSecret::aes(name, secret_data),
|
||||||
|
+ RetrieveableSecretInpKind::AesXts => GuestSecret::aes_xts(name, secret_data),
|
||||||
|
+ RetrieveableSecretInpKind::HmacSha => GuestSecret::hmac_sha(name, secret_data),
|
||||||
|
+ RetrieveableSecretInpKind::Ec => GuestSecret::ec(
|
||||||
|
+ name,
|
||||||
|
+ read_private_key(secret_data.value())
|
||||||
|
+ .with_context(|| format!("Cannot read {secret} as {kind} from PEM or DER"))?,
|
||||||
|
+ ),
|
||||||
|
+ }
|
||||||
|
+ .map_err(Error::from)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/// Prepare an add-secret request
|
||||||
|
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||||
|
if pv_guest_bit_set() {
|
||||||
|
@@ -88,6 +106,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||||
|
input_secret: None,
|
||||||
|
..
|
||||||
|
} => GuestSecret::association(name, None)?,
|
||||||
|
+ AddSecretType::Retrievable {
|
||||||
|
+ name, secret, kind, ..
|
||||||
|
+ } => retrievable(name, secret, kind)?,
|
||||||
|
};
|
||||||
|
trace!("AddSecret: {secret:x?}");
|
||||||
|
|
||||||
|
@@ -136,7 +157,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| read_file(p, "User-signing key"))
|
||||||
|
.transpose()?
|
||||||
|
- .map(|buf| read_private_key(&buf))
|
||||||
|
+ .map(|buf| {
|
||||||
|
+ read_private_key(&buf).context("Cannot read {secret} as private key from PEM or DER")
|
||||||
|
+ })
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
if user_data.is_some() || user_key.is_some() {
|
||||||
|
@@ -258,6 +281,9 @@ fn write_secret<P: AsRef<Path>>(
|
||||||
|
write_out(path, guest_secret.confidential(), "Association secret")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ AddSecretType::Retrievable { name, stdout, .. } => {
|
||||||
|
+ write_yaml(name, guest_secret, stdout, outp_path)?
|
||||||
|
+ }
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||||
|
index f7e3a72b..0bd9eca4 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/list.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||||
|
@@ -3,21 +3,25 @@
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||||
|
-use anyhow::{Context, Result};
|
||||||
|
+use anyhow::{Context, Error, Result};
|
||||||
|
use log::warn;
|
||||||
|
use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||||
|
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||||
|
|
||||||
|
/// Do a List Secrets UVC
|
||||||
|
-pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||||
|
- let uv = UvDevice::open()?;
|
||||||
|
+pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||||
|
let mut cmd = ListCmd::default();
|
||||||
|
match uv.send_cmd(&mut cmd)? {
|
||||||
|
UvcSuccess::RC_SUCCESS => (),
|
||||||
|
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||||
|
};
|
||||||
|
+ cmd.try_into().map_err(Error::new)
|
||||||
|
+}
|
||||||
|
|
||||||
|
- let secret_list: SecretList = cmd.try_into()?;
|
||||||
|
+/// Do a List Secrets UVC and output the list in the requested format
|
||||||
|
+pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||||
|
+ let uv = UvDevice::open()?;
|
||||||
|
+ let secret_list = list_uvc(&uv)?;
|
||||||
|
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
|
||||||
|
|
||||||
|
match &opt.format {
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..7f7704cc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/rust/pvsecret/src/cmd/retr.rs
|
||||||
|
@@ -0,0 +1,62 @@
|
||||||
|
+// SPDX-License-Identifier: MIT
|
||||||
|
+//
|
||||||
|
+// Copyright IBM Corp. 2024
|
||||||
|
+
|
||||||
|
+use super::list::list_uvc;
|
||||||
|
+use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions};
|
||||||
|
+use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
+use log::{debug, info};
|
||||||
|
+use pv::{
|
||||||
|
+ misc::open_file,
|
||||||
|
+ misc::write,
|
||||||
|
+ secret::{GuestSecret, RetrievedSecret},
|
||||||
|
+ uv::{RetrieveCmd, SecretId, UvDevice},
|
||||||
|
+};
|
||||||
|
+use utils::get_writer_from_cli_file_arg;
|
||||||
|
+
|
||||||
|
+fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
|
||||||
|
+ let uv = UvDevice::open()?;
|
||||||
|
+ let secrets = list_uvc(&uv)?;
|
||||||
|
+ let secret = secrets
|
||||||
|
+ .into_iter()
|
||||||
|
+ .find(|s| s.id() == id.as_ref())
|
||||||
|
+ .ok_or(anyhow!(
|
||||||
|
+ "The UV secret-store has no secret with the ID {id}"
|
||||||
|
+ ))?;
|
||||||
|
+
|
||||||
|
+ info!("Try to retrieve secret at index: {}", secret.index());
|
||||||
|
+ debug!("Try to retrieve: {secret:?}");
|
||||||
|
+
|
||||||
|
+ let mut uv_cmd = RetrieveCmd::from_entry(secret)?;
|
||||||
|
+ uv.send_cmd(&mut uv_cmd)?;
|
||||||
|
+
|
||||||
|
+ Ok(RetrievedSecret::from_cmd(uv_cmd))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
||||||
|
+ let mut output = get_writer_from_cli_file_arg(&opt.output)?;
|
||||||
|
+ let id = match &opt.inform {
|
||||||
|
+ RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
|
||||||
|
+ GuestSecret::Retrievable { id, .. } => id,
|
||||||
|
+ gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
|
||||||
|
+ },
|
||||||
|
+ RetrInpFmt::Hex => {
|
||||||
|
+ serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let retr_secret =
|
||||||
|
+ retrieve(&id).context("Could not retrieve the secret from the UV secret store.")?;
|
||||||
|
+
|
||||||
|
+ let out_data = match opt.outform {
|
||||||
|
+ RetrOutFmt::Bin => retr_secret.into_bytes(),
|
||||||
|
+ RetrOutFmt::Pem => retr_secret.to_pem()?.into_bytes(),
|
||||||
|
+ };
|
||||||
|
+ write(
|
||||||
|
+ &mut output,
|
||||||
|
+ out_data.value(),
|
||||||
|
+ &opt.output,
|
||||||
|
+ "IBM Protected Key",
|
||||||
|
+ )?;
|
||||||
|
+ Ok(())
|
||||||
|
+}
|
||||||
|
diff --git a/rust/pvsecret/src/main.rs b/rust/pvsecret/src/main.rs
|
||||||
|
index 502a6ea0..883a3ee2 100644
|
||||||
|
--- a/rust/pvsecret/src/main.rs
|
||||||
|
+++ b/rust/pvsecret/src/main.rs
|
||||||
|
@@ -45,6 +45,7 @@ fn main() -> ExitCode {
|
||||||
|
Command::Create(opt) => cmd::create(opt),
|
||||||
|
Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())),
|
||||||
|
Command::Verify(opt) => cmd::verify(opt),
|
||||||
|
+ Command::Retrieve(opt) => cmd::retr(opt),
|
||||||
|
};
|
||||||
|
|
||||||
|
match res {
|
313
s390-tools-General-update-09.patch
Normal file
313
s390-tools-General-update-09.patch
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
From 256289a30aa5d3f6a4d2631dea69d1dc47205150 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Wed, 12 Jun 2024 16:23:31 +0200
|
||||||
|
Subject: [PATCH] rust/pv_core: Refactor secret list
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Improve the secret list implementation. Use structs+{As,From}Bytes
|
||||||
|
instead of arbitrary seeks and reads/writes to parse the secret list.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv_core/src/uvdevice.rs | 10 ++
|
||||||
|
rust/pv_core/src/uvdevice/secret_list.rs | 124 ++++++++++++++---------
|
||||||
|
2 files changed, 86 insertions(+), 48 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||||
|
index e9848243..e701366d 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice.rs
|
||||||
|
@@ -163,6 +163,16 @@ pub enum UvcSuccess {
|
||||||
|
RC_MORE_DATA = UvDevice::RC_MORE_DATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
+impl UvcSuccess {
|
||||||
|
+ /// Returns true if there is more data available
|
||||||
|
+ pub fn more_data(&self) -> bool {
|
||||||
|
+ match self {
|
||||||
|
+ Self::RC_SUCCESS => false,
|
||||||
|
+ Self::RC_MORE_DATA => true,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from
|
||||||
|
/// userspace.
|
||||||
|
///
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
index 4e955010..d7c268c9 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
@@ -4,16 +4,16 @@
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
assert_size,
|
||||||
|
- misc::to_u16,
|
||||||
|
uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||||
|
uvdevice::UvCmd,
|
||||||
|
Error, Result,
|
||||||
|
};
|
||||||
|
-use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||||
|
+use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
io::{Cursor, Read, Seek, Write},
|
||||||
|
+ mem::size_of,
|
||||||
|
slice::Iter,
|
||||||
|
vec::IntoIter,
|
||||||
|
};
|
||||||
|
@@ -31,7 +31,7 @@ impl SecretId {
|
||||||
|
/// Size in bytes of the [`SecretId`]
|
||||||
|
pub const ID_SIZE: usize = 32;
|
||||||
|
|
||||||
|
- /// Create a [`SecretId`] forom a buffer.
|
||||||
|
+ /// Create a [`SecretId`] from a buffer.
|
||||||
|
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
||||||
|
buf.into()
|
||||||
|
}
|
||||||
|
@@ -120,7 +120,7 @@ impl SecretEntry {
|
||||||
|
&self.index
|
||||||
|
}
|
||||||
|
|
||||||
|
- /// Returns the secret type of this [`SecretEntry`].
|
||||||
|
+ /// Returns the secret type of this [`SecretEntry`]
|
||||||
|
pub fn stype(&self) -> ListableSecretType {
|
||||||
|
self.stype.get().into()
|
||||||
|
}
|
||||||
|
@@ -161,12 +161,45 @@ impl Display for SecretEntry {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+#[repr(C)]
|
||||||
|
+#[derive(Debug, FromBytes, AsBytes, FromZeroes, Clone, PartialEq, Eq, Default, Serialize)]
|
||||||
|
+struct SecretListHdr {
|
||||||
|
+ #[serde(skip)]
|
||||||
|
+ num_secrets_stored: U16<BigEndian>,
|
||||||
|
+ #[serde(serialize_with = "ser_u16")]
|
||||||
|
+ total_num_secrets: U16<BigEndian>,
|
||||||
|
+ #[serde(skip)]
|
||||||
|
+ next_secret_idx: U16<BigEndian>,
|
||||||
|
+ #[serde(skip)]
|
||||||
|
+ reserved_06: u16,
|
||||||
|
+ #[serde(skip)]
|
||||||
|
+ reserved_08: u64,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl SecretListHdr {
|
||||||
|
+ fn new(num_secrets_stored: u16, total_num_secrets: u16, next_secret_idx: u16) -> Self {
|
||||||
|
+ Self {
|
||||||
|
+ num_secrets_stored: num_secrets_stored.into(),
|
||||||
|
+ total_num_secrets: total_num_secrets.into(),
|
||||||
|
+ next_secret_idx: next_secret_idx.into(),
|
||||||
|
+ reserved_06: 0,
|
||||||
|
+ reserved_08: 0,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+assert_size!(SecretListHdr, 16);
|
||||||
|
+
|
||||||
|
/// List of secrets used to parse the [`crate::uv::ListCmd`] result.
|
||||||
|
///
|
||||||
|
-/// The list should not hold more than 0xffffffff elements
|
||||||
|
-#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||||
|
+/// The list should ONLY be created from an UV-Call result using either:
|
||||||
|
+/// - [`TryInto::try_into`] from [`ListCmd`]
|
||||||
|
+/// - [`SecretList::decode`]
|
||||||
|
+/// Any other ways can create invalid lists that do not represent the UV secret store.
|
||||||
|
+/// The list must not hold more than [`u32::MAX`] elements
|
||||||
|
+#[derive(Debug, PartialEq, Eq, Serialize, Default)]
|
||||||
|
pub struct SecretList {
|
||||||
|
- total_num_secrets: usize,
|
||||||
|
+ #[serde(flatten)]
|
||||||
|
+ hdr: SecretListHdr,
|
||||||
|
secrets: Vec<SecretEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -202,10 +235,14 @@ impl SecretList {
|
||||||
|
/// The content of this list will very likely not represent the status of the guest in the
|
||||||
|
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||||
|
pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self {
|
||||||
|
- Self {
|
||||||
|
- total_num_secrets: total_num_secrets as usize,
|
||||||
|
+ Self::new_with_hdr(
|
||||||
|
+ SecretListHdr::new(total_num_secrets, total_num_secrets, 0),
|
||||||
|
secrets,
|
||||||
|
- }
|
||||||
|
+ )
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn new_with_hdr(hdr: SecretListHdr, secrets: Vec<SecretEntry>) -> Self {
|
||||||
|
+ Self { hdr, secrets }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the slice.
|
||||||
|
@@ -229,19 +266,12 @@ impl SecretList {
|
||||||
|
///
|
||||||
|
/// This number may be not equal to the provided number of [`SecretEntry`]
|
||||||
|
pub fn total_num_secrets(&self) -> usize {
|
||||||
|
- self.total_num_secrets
|
||||||
|
+ self.hdr.total_num_secrets.get() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes the list in the same binary format the UV would do
|
||||||
|
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||||
|
- let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?;
|
||||||
|
- w.write_u16::<BigEndian>(num_s)?;
|
||||||
|
- w.write_u16::<BigEndian>(
|
||||||
|
- self.total_num_secrets
|
||||||
|
- .try_into()
|
||||||
|
- .map_err(|_| Error::ManySecrets)?,
|
||||||
|
- )?;
|
||||||
|
- w.write_all(&[0u8; 12])?;
|
||||||
|
+ w.write_all(self.hdr.as_bytes())?;
|
||||||
|
for secret in &self.secrets {
|
||||||
|
w.write_all(secret.as_bytes())?;
|
||||||
|
}
|
||||||
|
@@ -250,19 +280,20 @@ impl SecretList {
|
||||||
|
|
||||||
|
/// Decodes the list from the binary format of the UV into this internal representation
|
||||||
|
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
|
||||||
|
- let num_s = r.read_u16::<BigEndian>()?;
|
||||||
|
- let total_num_secrets = r.read_u16::<BigEndian>()? as usize;
|
||||||
|
- let mut v: Vec<SecretEntry> = Vec::with_capacity(num_s as usize);
|
||||||
|
- r.seek(std::io::SeekFrom::Current(12))?; // skip reserved bytes
|
||||||
|
+ let mut buf = [0u8; size_of::<SecretListHdr>()];
|
||||||
|
+ r.read_exact(&mut buf)?;
|
||||||
|
+ let hdr = SecretListHdr::ref_from(&buf).unwrap();
|
||||||
|
+
|
||||||
|
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
|
||||||
|
- for _ in 0..num_s {
|
||||||
|
+ let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
|
||||||
|
+ for _ in 0..hdr.num_secrets_stored.get() {
|
||||||
|
r.read_exact(&mut buf)?;
|
||||||
|
// cannot fail. buffer has the same size as the secret entry
|
||||||
|
let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
|
||||||
|
v.push(secr);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
- total_num_secrets,
|
||||||
|
+ hdr: hdr.clone(),
|
||||||
|
secrets: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -278,7 +309,7 @@ impl TryFrom<ListCmd> for SecretList {
|
||||||
|
|
||||||
|
impl Display for SecretList {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
- writeln!(f, "Total number of secrets: {}", self.total_num_secrets)?;
|
||||||
|
+ writeln!(f, "Total number of secrets: {}", self.total_num_secrets())?;
|
||||||
|
if !self.secrets.is_empty() {
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
@@ -481,8 +512,8 @@ mod test {
|
||||||
|
let buf = [
|
||||||
|
0x00u8, 0x01, // num secr stored
|
||||||
|
0x01, 0x12, // total num secrets
|
||||||
|
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
- 0x00, // reserved
|
||||||
|
+ 0x01, 0x01, // next valid idx
|
||||||
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||||
|
// secret
|
||||||
|
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||||
|
0x00, 0x00, 0x00, 0x20, // len
|
||||||
|
@@ -493,16 +524,16 @@ mod test {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
- let exp = SecretList {
|
||||||
|
- total_num_secrets: 0x112,
|
||||||
|
- secrets: vec![SecretEntry {
|
||||||
|
+ let exp = SecretList::new_with_hdr(
|
||||||
|
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||||
|
+ vec![SecretEntry {
|
||||||
|
index: 1.into(),
|
||||||
|
stype: 2.into(),
|
||||||
|
len: 32.into(),
|
||||||
|
res_8: 0,
|
||||||
|
id: SecretId::from([0; 32]),
|
||||||
|
}],
|
||||||
|
- };
|
||||||
|
+ );
|
||||||
|
|
||||||
|
let mut br = BufReader::new(Cursor::new(buf));
|
||||||
|
let sl = SecretList::decode(&mut br).unwrap();
|
||||||
|
@@ -514,8 +545,8 @@ mod test {
|
||||||
|
const EXP: &[u8] = &[
|
||||||
|
0x00, 0x01, // num secr stored
|
||||||
|
0x01, 0x12, // total num secrets
|
||||||
|
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
- 0x00, // reserved
|
||||||
|
+ 0x01, 0x01, // next valid idx
|
||||||
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||||
|
// secret
|
||||||
|
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||||
|
0x00, 0x00, 0x00, 0x20, // len
|
||||||
|
@@ -526,16 +557,16 @@ mod test {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
- let sl = SecretList {
|
||||||
|
- total_num_secrets: 0x112,
|
||||||
|
- secrets: vec![SecretEntry {
|
||||||
|
+ let sl = SecretList::new_with_hdr(
|
||||||
|
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||||
|
+ vec![SecretEntry {
|
||||||
|
index: 1.into(),
|
||||||
|
stype: 2.into(),
|
||||||
|
len: 32.into(),
|
||||||
|
res_8: 0,
|
||||||
|
id: SecretId::from([0; 32]),
|
||||||
|
}],
|
||||||
|
- };
|
||||||
|
+ );
|
||||||
|
|
||||||
|
let mut buf = [0u8; 0x40];
|
||||||
|
{
|
||||||
|
@@ -587,26 +618,23 @@ mod test {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn secret_list_ser() {
|
||||||
|
- let list = SecretList {
|
||||||
|
- total_num_secrets: 0x112,
|
||||||
|
- secrets: vec![SecretEntry {
|
||||||
|
+ let list = SecretList::new_with_hdr(
|
||||||
|
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||||
|
+ vec![SecretEntry {
|
||||||
|
index: 1.into(),
|
||||||
|
stype: 2.into(),
|
||||||
|
len: 32.into(),
|
||||||
|
res_8: 0,
|
||||||
|
id: SecretId::from([0; 32]),
|
||||||
|
}],
|
||||||
|
- };
|
||||||
|
+ );
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&list,
|
||||||
|
&[
|
||||||
|
- Token::Struct {
|
||||||
|
- name: "SecretList",
|
||||||
|
- len: 2,
|
||||||
|
- },
|
||||||
|
+ Token::Map { len: None },
|
||||||
|
Token::String("total_num_secrets"),
|
||||||
|
- Token::U64(0x112),
|
||||||
|
+ Token::U16(0x112),
|
||||||
|
Token::String("secrets"),
|
||||||
|
Token::Seq { len: Some(1) },
|
||||||
|
Token::Struct {
|
||||||
|
@@ -623,7 +651,7 @@ mod test {
|
||||||
|
Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
Token::StructEnd,
|
||||||
|
Token::SeqEnd,
|
||||||
|
- Token::StructEnd,
|
||||||
|
+ Token::MapEnd,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
111
s390-tools-General-update-10.patch
Normal file
111
s390-tools-General-update-10.patch
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
From 93216d916c479ee1292aa1d598ac9c0e7f585bd8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Wed, 12 Jun 2024 16:35:15 +0200
|
||||||
|
Subject: [PATCH] rust/pv*: Support longer secret lists
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Make use of the enhanced list secrets UAPI for the uvdevice in the latest kernel
|
||||||
|
version. This allows fetching secret lists with more than 85 entries via
|
||||||
|
reserving more userspace memory in the IOCTL argument.
|
||||||
|
|
||||||
|
While at it, move the errno readout next to the ioctl-syscall.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv_core/src/uvdevice.rs | 6 ++++--
|
||||||
|
rust/pv_core/src/uvdevice/secret.rs | 11 +++++++++++
|
||||||
|
rust/pvsecret/src/cmd/list.rs | 28 +++++++++++++++++++++-------
|
||||||
|
3 files changed, 36 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||||
|
index e701366d..689748a1 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice.rs
|
||||||
|
@@ -59,11 +59,13 @@ fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> {
|
||||||
|
rc = ioctl(raw_fd, cmd, cb.as_ptr_mut());
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // NOTE io::Error handles all errnos ioctl uses
|
||||||
|
+ let errno = std::io::Error::last_os_error();
|
||||||
|
+
|
||||||
|
debug!("ioctl resulted with {cb:?}");
|
||||||
|
match rc {
|
||||||
|
0 => Ok(()),
|
||||||
|
- // NOTE io::Error handles all errnos ioctl uses
|
||||||
|
- _ => Err(std::io::Error::last_os_error().into()),
|
||||||
|
+ _ => Err(errno.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
index 263f17d5..cb5b7233 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||||
|
@@ -24,6 +24,17 @@ impl ListCmd {
|
||||||
|
Self(vec![0; size])
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /// Create a new list secrets command with `pages` capacity.
|
||||||
|
+ ///
|
||||||
|
+ /// * `pages` - number pf pages to allocate for this IOCTL
|
||||||
|
+ ///
|
||||||
|
+ /// # Panic
|
||||||
|
+ /// This function will trigger a panic if the allocation size is larger than [`usize::MAX`].
|
||||||
|
+ /// Very likely an OOM situation occurs way before this!
|
||||||
|
+ pub fn with_pages(pages: usize) -> Self {
|
||||||
|
+ Self::with_size(pages * PAGESIZE)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/// Create a new list secrets command with a one page capacity
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::with_size(PAGESIZE)
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||||
|
index 0bd9eca4..56294cac 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/list.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||||
|
@@ -2,19 +2,33 @@
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
+use std::io::ErrorKind;
|
||||||
|
+
|
||||||
|
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||||
|
use anyhow::{Context, Error, Result};
|
||||||
|
-use log::warn;
|
||||||
|
-use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||||
|
+use log::{info, warn};
|
||||||
|
+use pv::uv::{ListCmd, SecretList, UvDevice};
|
||||||
|
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||||
|
|
||||||
|
+const SECRET_LIST_BUF_SIZE: usize = 4;
|
||||||
|
+
|
||||||
|
/// Do a List Secrets UVC
|
||||||
|
pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||||
|
- let mut cmd = ListCmd::default();
|
||||||
|
- match uv.send_cmd(&mut cmd)? {
|
||||||
|
- UvcSuccess::RC_SUCCESS => (),
|
||||||
|
- UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||||
|
- };
|
||||||
|
+ let mut cmd = ListCmd::with_pages(SECRET_LIST_BUF_SIZE);
|
||||||
|
+ let more_data = match uv.send_cmd(&mut cmd) {
|
||||||
|
+ Ok(v) => Ok(v),
|
||||||
|
+ Err(pv::PvCoreError::Io(e)) if e.kind() == ErrorKind::InvalidInput => {
|
||||||
|
+ info!("Uvdevice does not suport longer list. Fallback to one page list.");
|
||||||
|
+ cmd = ListCmd::default();
|
||||||
|
+ uv.send_cmd(&mut cmd)
|
||||||
|
+ }
|
||||||
|
+ Err(e) => Err(e),
|
||||||
|
+ }?
|
||||||
|
+ .more_data();
|
||||||
|
+ if more_data {
|
||||||
|
+ warn!("The secret list contains more data but the uvdevice cannot show all.");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
cmd.try_into().map_err(Error::new)
|
||||||
|
}
|
||||||
|
|
387
s390-tools-General-update-11.patch
Normal file
387
s390-tools-General-update-11.patch
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
From ff04f76257791593c8f92374f295a0c478e3b0f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Date: Mon, 5 Aug 2024 09:34:47 +0200
|
||||||
|
Subject: [PATCH] rust/pv*: Allow the use of non-hashes secret IDs
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Secret IDs identify a secret in the store. Tooling (pvsecret) calculates
|
||||||
|
them by hashing a user-defined string. With this patch it is now
|
||||||
|
possible to skip the hash step and directly use the input string as the
|
||||||
|
ID. Up to the first 31 bytes of the input ASCII-string are used. The last byte
|
||||||
|
is the NUL char. During list pvsecret tries to interpret the secret
|
||||||
|
as ASCII string and if possible displays the ASCII characters alongside
|
||||||
|
the hex number.
|
||||||
|
|
||||||
|
Also, use the Upper/Lower Hex formatters for the hexstring formatting of
|
||||||
|
SecretId. Display will, additionally show the ASCII representation if
|
||||||
|
applicable.
|
||||||
|
|
||||||
|
While at it, use Self wherever possible.
|
||||||
|
|
||||||
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||||
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||||
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pv/src/uvsecret/guest_secret.rs | 16 ++-
|
||||||
|
rust/pv_core/src/uvdevice/secret_list.rs | 163 ++++++++++++++++++++---
|
||||||
|
rust/pvsecret/src/cli.rs | 8 ++
|
||||||
|
rust/pvsecret/src/cmd/create.rs | 4 +-
|
||||||
|
rust/pvsecret/src/cmd/retr.rs | 14 +-
|
||||||
|
5 files changed, 184 insertions(+), 21 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
index 3bad6d3c..87b58bb8 100644
|
||||||
|
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||||
|
@@ -92,7 +92,8 @@ macro_rules! retr_constructor {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestSecret {
|
||||||
|
- fn name_to_id(name: &str) -> Result<SecretId> {
|
||||||
|
+ /// Hashes the name with sha256
|
||||||
|
+ pub fn name_to_id(name: &str) -> Result<SecretId> {
|
||||||
|
let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||||
|
.to_vec()
|
||||||
|
.try_into()
|
||||||
|
@@ -135,6 +136,19 @@ impl GuestSecret {
|
||||||
|
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
||||||
|
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
||||||
|
|
||||||
|
+ /// Use the name as ID, do not hash it
|
||||||
|
+ pub fn no_hash_name(&mut self) {
|
||||||
|
+ match self {
|
||||||
|
+ Self::Null => (),
|
||||||
|
+ Self::Association {
|
||||||
|
+ name, ref mut id, ..
|
||||||
|
+ }
|
||||||
|
+ | Self::Retrievable {
|
||||||
|
+ name, ref mut id, ..
|
||||||
|
+ } => id.clone_from(&SecretId::from_string(name)),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/// Reference to the confidential data
|
||||||
|
pub fn confidential(&self) -> &[u8] {
|
||||||
|
match &self {
|
||||||
|
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
index d7c268c9..7c7e63b5 100644
|
||||||
|
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||||
|
@@ -11,7 +11,9 @@ use crate::{
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
use std::{
|
||||||
|
- fmt::Display,
|
||||||
|
+ cmp::min,
|
||||||
|
+ ffi::CStr,
|
||||||
|
+ fmt::{Debug, Display, LowerHex, UpperHex},
|
||||||
|
io::{Cursor, Read, Seek, Write},
|
||||||
|
mem::size_of,
|
||||||
|
slice::Iter,
|
||||||
|
@@ -35,6 +37,33 @@ impl SecretId {
|
||||||
|
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
||||||
|
buf.into()
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /// Create a Id from a string
|
||||||
|
+ ///
|
||||||
|
+ /// Uses the first 31 bytes from `name` as id
|
||||||
|
+ /// Does not hash anything. Byte 32 is the NUL char
|
||||||
|
+ pub fn from_string(name: &str) -> Self {
|
||||||
|
+ let len = min(name.len(), Self::ID_SIZE - 1);
|
||||||
|
+ let mut res = Self::default();
|
||||||
|
+ res.0[0..len].copy_from_slice(&name.as_bytes()[0..len]);
|
||||||
|
+ res
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Tries to represent the Id as printable-ASCII string
|
||||||
|
+ pub fn as_ascii(&self) -> Option<&str> {
|
||||||
|
+ if let Ok(t) = CStr::from_bytes_until_nul(&self.0) {
|
||||||
|
+ if let Ok(t) = t.to_str() {
|
||||||
|
+ if !t.is_empty()
|
||||||
|
+ && t.chars()
|
||||||
|
+ .all(|c| c.is_ascii_whitespace() | c.is_ascii_graphic())
|
||||||
|
+ && self.0[t.len()..].iter().all(|b| *b == 0)
|
||||||
|
+ {
|
||||||
|
+ return Some(t);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ None
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SecretId {
|
||||||
|
@@ -42,8 +71,8 @@ impl Serialize for SecretId {
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
- // calls Display at one point
|
||||||
|
- ser.serialize_str(&self.to_string())
|
||||||
|
+ // calls LowerHex at one point
|
||||||
|
+ ser.serialize_str(&format!("{self:#x}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -56,12 +85,36 @@ impl<'de> Deserialize<'de> for SecretId {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+impl UpperHex for SecretId {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ if f.alternate() {
|
||||||
|
+ write!(f, "0x")?;
|
||||||
|
+ }
|
||||||
|
+ for b in self.0 {
|
||||||
|
+ write!(f, "{b:02X}")?;
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl LowerHex for SecretId {
|
||||||
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
+ if f.alternate() {
|
||||||
|
+ write!(f, "0x")?;
|
||||||
|
+ }
|
||||||
|
+ for b in self.0 {
|
||||||
|
+ write!(f, "{b:02x}")?;
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
impl Display for SecretId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
- let mut s = String::with_capacity(32 * 2 + 2);
|
||||||
|
- s.push_str("0x");
|
||||||
|
- let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}"));
|
||||||
|
- write!(f, "{s}")
|
||||||
|
+ if let Some(s) = self.as_ascii() {
|
||||||
|
+ write!(f, "{s} | ")?;
|
||||||
|
+ }
|
||||||
|
+ write!(f, "{self:#x}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -79,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
|
||||||
|
|
||||||
|
/// A secret in a [`SecretList`]
|
||||||
|
#[repr(C)]
|
||||||
|
-#[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
||||||
|
+#[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
||||||
|
pub struct SecretEntry {
|
||||||
|
#[serde(serialize_with = "ser_u16")]
|
||||||
|
index: U16<BigEndian>,
|
||||||
|
@@ -153,11 +206,7 @@ impl Display for SecretEntry {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let stype: ListableSecretType = self.stype.get().into();
|
||||||
|
writeln!(f, "{} {}:", self.index, stype)?;
|
||||||
|
- write!(f, " ")?;
|
||||||
|
- for b in self.id.as_ref() {
|
||||||
|
- write!(f, "{b:02x}")?;
|
||||||
|
- }
|
||||||
|
- Ok(())
|
||||||
|
+ write!(f, " {}", self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -269,6 +318,11 @@ impl SecretList {
|
||||||
|
self.hdr.total_num_secrets.get() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /// Find the first [`SecretEntry`] that has the provided [`SecretId`]
|
||||||
|
+ pub fn find(&self, id: &SecretId) -> Option<SecretEntry> {
|
||||||
|
+ self.iter().find(|e| e.id() == id.as_ref()).cloned()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/// Encodes the list in the same binary format the UV would do
|
||||||
|
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||||
|
w.write_all(self.hdr.as_bytes())?;
|
||||||
|
@@ -456,7 +510,7 @@ where
|
||||||
|
type Value = [u8; SecretId::ID_SIZE];
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
- formatter.write_str("a `32 bytes long hexstring` prepended with 0x")
|
||||||
|
+ formatter.write_str("a `32 bytes (=64 character) long hexstring` prepended with 0x")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||||
|
@@ -464,7 +518,10 @@ where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
||||||
|
- return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
||||||
|
+ return Err(serde::de::Error::invalid_length(
|
||||||
|
+ s.len().saturating_sub("0x".len()),
|
||||||
|
+ &self,
|
||||||
|
+ ));
|
||||||
|
}
|
||||||
|
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
||||||
|
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
||||||
|
@@ -655,4 +712,80 @@ mod test {
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_display() {
|
||||||
|
+ let text = "Fancy secret ID";
|
||||||
|
+ let id = SecretId::from_string(text);
|
||||||
|
+
|
||||||
|
+ let exp =
|
||||||
|
+ "Fancy secret ID | 0x46616e6379207365637265742049440000000000000000000000000000000000";
|
||||||
|
+ assert_eq!(id.to_string(), exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_long_name() {
|
||||||
|
+ let text = "the most fanciest secret ID you ever seen in the time the universe exists";
|
||||||
|
+ let id = SecretId::from_string(text);
|
||||||
|
+ let exp =
|
||||||
|
+ "the most fanciest secret ID you | 0x746865206d6f73742066616e63696573742073656372657420494420796f7500";
|
||||||
|
+ assert_eq!(id.to_string(), exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_no_ascii_name() {
|
||||||
|
+ let text = [0; 32];
|
||||||
|
+ let id = SecretId::from(text);
|
||||||
|
+
|
||||||
|
+ let exp = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
+ assert_eq!(id.to_string(), exp);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_no_ascii_name2() {
|
||||||
|
+ let text = [
|
||||||
|
+ 0x25, 0x55, 3, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
||||||
|
+ 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
+ ];
|
||||||
|
+ let id = SecretId::from(text);
|
||||||
|
+ assert_eq!(id.as_ascii(), None);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_no_ascii_name3() {
|
||||||
|
+ let text = [
|
||||||
|
+ 0x25, 0x55, 0, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
||||||
|
+ 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
+ ];
|
||||||
|
+ let id = SecretId::from(text);
|
||||||
|
+ assert_eq!(id.as_ascii(), None);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn secret_id_hex() {
|
||||||
|
+ let id_str = "Nice Test 123";
|
||||||
|
+ let id = SecretId::from_string(id_str);
|
||||||
|
+
|
||||||
|
+ let s = format!("{id:#x}");
|
||||||
|
+ assert_eq!(
|
||||||
|
+ s,
|
||||||
|
+ "0x4e69636520546573742031323300000000000000000000000000000000000000"
|
||||||
|
+ );
|
||||||
|
+ let s = format!("{id:x}");
|
||||||
|
+ assert_eq!(
|
||||||
|
+ s,
|
||||||
|
+ "4e69636520546573742031323300000000000000000000000000000000000000"
|
||||||
|
+ );
|
||||||
|
+ let s = format!("{id:#X}");
|
||||||
|
+ assert_eq!(
|
||||||
|
+ s,
|
||||||
|
+ "0x4E69636520546573742031323300000000000000000000000000000000000000"
|
||||||
|
+ );
|
||||||
|
+
|
||||||
|
+ let s = format!("{id:X}");
|
||||||
|
+ assert_eq!(
|
||||||
|
+ s,
|
||||||
|
+ "4E69636520546573742031323300000000000000000000000000000000000000"
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||||
|
index 4e747682..d858fc29 100644
|
||||||
|
--- a/rust/pvsecret/src/cli.rs
|
||||||
|
+++ b/rust/pvsecret/src/cli.rs
|
||||||
|
@@ -141,6 +141,12 @@ pub struct CreateSecretOpt {
|
||||||
|
/// by default.
|
||||||
|
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||||
|
pub user_sign_key: Option<String>,
|
||||||
|
+
|
||||||
|
+ /// Do not hash the name, use it directly as secret ID.
|
||||||
|
+ ///
|
||||||
|
+ /// Ignored for meta-secrets.
|
||||||
|
+ #[arg(long)]
|
||||||
|
+ pub use_name: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
@@ -342,6 +348,8 @@ pub enum RetrInpFmt {
|
||||||
|
Yaml,
|
||||||
|
/// Use a hex string.
|
||||||
|
Hex,
|
||||||
|
+ /// Use a name-string. Will hash it if no secret with the name found.
|
||||||
|
+ Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
index 73089a12..fab37e67 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||||
|
@@ -94,7 +94,7 @@ fn read_private_key(buf: &[u8]) -> Result<PKey<Private>> {
|
||||||
|
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||||
|
debug!("Build add-secret request");
|
||||||
|
|
||||||
|
- let secret = match &opt.secret {
|
||||||
|
+ let mut secret = match &opt.secret {
|
||||||
|
AddSecretType::Meta => GuestSecret::Null,
|
||||||
|
AddSecretType::Association {
|
||||||
|
name,
|
||||||
|
@@ -112,6 +112,8 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||||
|
};
|
||||||
|
trace!("AddSecret: {secret:x?}");
|
||||||
|
|
||||||
|
+ opt.use_name.then(|| secret.no_hash_name());
|
||||||
|
+
|
||||||
|
let mut flags = match &opt.pcf {
|
||||||
|
Some(v) => (&try_parse_u64(v, "pcf")?).into(),
|
||||||
|
None => AddSecretFlags::default(),
|
||||||
|
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
||||||
|
index 7f7704cc..ad3e91c3 100644
|
||||||
|
--- a/rust/pvsecret/src/cmd/retr.rs
|
||||||
|
+++ b/rust/pvsecret/src/cmd/retr.rs
|
||||||
|
@@ -17,12 +17,17 @@ use utils::get_writer_from_cli_file_arg;
|
||||||
|
fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
|
||||||
|
let uv = UvDevice::open()?;
|
||||||
|
let secrets = list_uvc(&uv)?;
|
||||||
|
- let secret = secrets
|
||||||
|
- .into_iter()
|
||||||
|
- .find(|s| s.id() == id.as_ref())
|
||||||
|
+ let secret = match secrets.find(id) {
|
||||||
|
+ Some(s) => s,
|
||||||
|
+ // hash it + try again if it is ASCII-representable
|
||||||
|
+ None => match id.as_ascii() {
|
||||||
|
+ Some(s) => secrets.find(&GuestSecret::name_to_id(s)?),
|
||||||
|
+ None => None,
|
||||||
|
+ }
|
||||||
|
.ok_or(anyhow!(
|
||||||
|
"The UV secret-store has no secret with the ID {id}"
|
||||||
|
- ))?;
|
||||||
|
+ ))?,
|
||||||
|
+ };
|
||||||
|
|
||||||
|
info!("Try to retrieve secret at index: {}", secret.index());
|
||||||
|
debug!("Try to retrieve: {secret:?}");
|
||||||
|
@@ -43,6 +48,7 @@ pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
||||||
|
RetrInpFmt::Hex => {
|
||||||
|
serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
|
||||||
|
}
|
||||||
|
+ RetrInpFmt::Name => SecretId::from_string(&opt.input),
|
||||||
|
};
|
||||||
|
|
||||||
|
let retr_secret =
|
1207
s390-tools-General-update-12.patch
Normal file
1207
s390-tools-General-update-12.patch
Normal file
File diff suppressed because it is too large
Load Diff
334
s390-tools-Support-unencrypted-SE-images-01.patch
Normal file
334
s390-tools-Support-unencrypted-SE-images-01.patch
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
From cf51ac786095f2a1a17d04fea9ee73271438d247 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Wed, 11 Dec 2024 19:25:59 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Add '--(enable|disable)-image-encryption' flags
|
||||||
|
to 'pvimg create'
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
With runtime attestation it might be useful to have non-encrypted Secure
|
||||||
|
Execution images. This patch adds the support for this to the 'pvimg
|
||||||
|
create' and 'genprotimg' commands.
|
||||||
|
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/man/genprotimg.1 | 26 +++++++++++++++++++++-----
|
||||||
|
rust/pvimg/man/pvimg-create.1 | 26 +++++++++++++++++++++-----
|
||||||
|
rust/pvimg/man/pvimg-info.1 | 10 +++++-----
|
||||||
|
rust/pvimg/man/pvimg-test.1 | 10 +++++-----
|
||||||
|
rust/pvimg/man/pvimg.1 | 10 +++++-----
|
||||||
|
rust/pvimg/src/cli.rs | 18 ++++++++++++++++++
|
||||||
|
rust/pvimg/src/cmd/create.rs | 10 ++++++++++
|
||||||
|
7 files changed, 85 insertions(+), 25 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/man/genprotimg.1 b/rust/pvimg/man/genprotimg.1
|
||||||
|
index 46a91aa4..3f4949e9 100644
|
||||||
|
--- a/rust/pvimg/man/genprotimg.1
|
||||||
|
+++ b/rust/pvimg/man/genprotimg.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH genprotimg 1 "2024-12-05" "s390-tools" "Genprotimg Manual"
|
||||||
|
+.TH genprotimg 1 "2024-12-11" "s390-tools" "Genprotimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBgenprotimg\fP - Create an IBM Secure Execution image
|
||||||
|
+\fBgenprotimg\fP \- Create an IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -196,6 +196,22 @@ Disable the support for backup target keys (default).
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
+\-\-enable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Enable encryption of the image components (default). The image components are:
|
||||||
|
+the kernel, ramdisk, and kernel command line.
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
+\-\-disable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Disable encryption of the image components. The image components are: the
|
||||||
|
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||||
|
+contain any confidential content (for example, secrets like non\-public
|
||||||
|
+cryptographic keys).
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
\-v, \-\-verbose
|
||||||
|
.RS 4
|
||||||
|
Provide more detailed output.
|
||||||
|
@@ -222,16 +238,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-create.1 b/rust/pvimg/man/pvimg-create.1
|
||||||
|
index aba197fa..dae1cf18 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-create.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-create.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-create 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-create 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg create\fP - Create an IBM Secure Execution image
|
||||||
|
+\fBpvimg create\fP \- Create an IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -195,6 +195,22 @@ Disable the support for backup target keys (default).
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
+\-\-enable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Enable encryption of the image components (default). The image components are:
|
||||||
|
+the kernel, ramdisk, and kernel command line.
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
+\-\-disable\-image\-encryption
|
||||||
|
+.RS 4
|
||||||
|
+Disable encryption of the image components. The image components are: the
|
||||||
|
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||||
|
+contain any confidential content (for example, secrets like non\-public
|
||||||
|
+cryptographic keys).
|
||||||
|
+.RE
|
||||||
|
+.RE
|
||||||
|
+.PP
|
||||||
|
\-h, \-\-help
|
||||||
|
.RS 4
|
||||||
|
Print help (see a summary with \fB\-h\fR).
|
||||||
|
@@ -203,16 +219,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-info.1 b/rust/pvimg/man/pvimg-info.1
|
||||||
|
index e88cbe49..d2726c35 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-info.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-info.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-info 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-info 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg info\fP - Print information about the IBM Secure Execution image
|
||||||
|
+\fBpvimg info\fP \- Print information about the IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -51,16 +51,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg-test.1 b/rust/pvimg/man/pvimg-test.1
|
||||||
|
index 901c7edb..4fb7d73f 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg-test.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg-test.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg-test 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg-test 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg test\fP - Test different aspects of an existing IBM Secure Execution image
|
||||||
|
+\fBpvimg test\fP \- Test different aspects of an existing IBM Secure Execution image
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -54,16 +54,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/man/pvimg.1 b/rust/pvimg/man/pvimg.1
|
||||||
|
index 37c8e978..5676b61d 100644
|
||||||
|
--- a/rust/pvimg/man/pvimg.1
|
||||||
|
+++ b/rust/pvimg/man/pvimg.1
|
||||||
|
@@ -3,11 +3,11 @@
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
|
||||||
|
-.TH pvimg 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||||
|
+.TH pvimg 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
-\fBpvimg\fP - Create and inspect IBM Secure Execution images
|
||||||
|
+\fBpvimg\fP \- Create and inspect IBM Secure Execution images
|
||||||
|
\fB
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
@@ -69,16 +69,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP 8
|
||||||
|
-.B 0 - Program finished successfully
|
||||||
|
+.B 0 \- Program finished successfully
|
||||||
|
The command was executed successfully.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 1 - Generic error
|
||||||
|
+.B 1 \- Generic error
|
||||||
|
Something went wrong during the operation. Refer to the error
|
||||||
|
message.
|
||||||
|
.RE
|
||||||
|
.TP 8
|
||||||
|
-.B 2 - Usage error
|
||||||
|
+.B 2 \- Usage error
|
||||||
|
The command was used incorrectly, for example: unsupported command
|
||||||
|
line flag, or wrong number of arguments.
|
||||||
|
.RE
|
||||||
|
diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs
|
||||||
|
index 2ca4e901..12f0b764 100644
|
||||||
|
--- a/rust/pvimg/src/cli.rs
|
||||||
|
+++ b/rust/pvimg/src/cli.rs
|
||||||
|
@@ -140,6 +140,20 @@ pub struct CreateBootImageLegacyFlags {
|
||||||
|
/// Disable the support for backup target keys (default).
|
||||||
|
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")]
|
||||||
|
pub disable_backup_keys: Option<bool>,
|
||||||
|
+
|
||||||
|
+ /// Enable encryption of the image components (default).
|
||||||
|
+ ///
|
||||||
|
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
|
||||||
|
+ pub enable_image_encryption: Option<bool>,
|
||||||
|
+
|
||||||
|
+ /// Disable encryption of the image components.
|
||||||
|
+ ///
|
||||||
|
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||||
|
+ /// Use only if the components used do not contain any confidential content
|
||||||
|
+ /// (for example, secrets like non-public cryptographic keys).
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_image_encryption", group="header-flags")]
|
||||||
|
+ pub disable_image_encryption: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
@@ -476,6 +490,8 @@ mod test {
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo-hmac", ["--enable-pckmo-hmac"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-backup-keys", ["--enable-backup-keys"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
|
||||||
|
];
|
||||||
|
let invalid_create_args = [
|
||||||
|
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
||||||
|
@@ -501,6 +517,8 @@ mod test {
|
||||||
|
CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]),
|
||||||
|
CliOption::new("disable-pckmo", ["--disable-pckmo"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
||||||
|
+ CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut genprotimg_valid_args = vec![
|
||||||
|
diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs
|
||||||
|
index b696d790..475d3523 100644
|
||||||
|
--- a/rust/pvimg/src/cmd/create.rs
|
||||||
|
+++ b/rust/pvimg/src/cmd/create.rs
|
||||||
|
@@ -80,6 +80,12 @@ fn parse_flags(
|
||||||
|
lf.enable_backup_keys
|
||||||
|
.filter(|x| *x)
|
||||||
|
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))),
|
||||||
|
+ lf.disable_image_encryption
|
||||||
|
+ .filter(|x| *x)
|
||||||
|
+ .and(Some(PcfV1::all_enabled([PcfV1::NoComponentEncryption]))),
|
||||||
|
+ lf.enable_image_encryption
|
||||||
|
+ .filter(|x| *x)
|
||||||
|
+ .and(Some(PcfV1::all_disabled([PcfV1::NoComponentEncryption]))),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
@@ -135,6 +141,10 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
|
||||||
|
read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||||
|
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||||
|
|
||||||
|
+ if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
||||||
|
+ warn!("The components encryption is disabled, make sure that the components do not contain any confidential content.");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
let mut components = components(&opt.component_paths)?;
|
||||||
|
if opt.no_component_check {
|
||||||
|
warn!("The component check is turned off!");
|
167
s390-tools-pvimg-additional-01.patch
Normal file
167
s390-tools-pvimg-additional-01.patch
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
From 5b6d7a467dc342c9c25a0af72b2d5546798cdc94 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Thu, 12 Dec 2024 20:19:56 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Add '--cck <FILE>' command line option and make
|
||||||
|
'--comm-key' an alias
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Add '--cck <FILE>' as an command line option and make '--comm-key' an
|
||||||
|
alias of it. This makes the command line more similar to the other
|
||||||
|
Secure Execution related PV-tools (e.g. pvattest and pvsecret).
|
||||||
|
|
||||||
|
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/man/genprotimg.1 | 11 +++++------
|
||||||
|
rust/pvimg/man/pvimg-create.1 | 11 +++++------
|
||||||
|
rust/pvimg/src/cli.rs | 14 ++++++++------
|
||||||
|
rust/pvimg/src/cmd/create.rs | 3 +--
|
||||||
|
4 files changed, 19 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/man/genprotimg.1
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||||
|
@@ -123,7 +123,7 @@ Overwrite an existing Secure Execution b
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
-\-\-comm\-key <FILE>
|
||||||
|
+\-\-cck, \-\-comm\-key <FILE>
|
||||||
|
.RS 4
|
||||||
|
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||||
|
contain exactly 32 bytes of data.
|
||||||
|
@@ -133,7 +133,7 @@ contain exactly 32 bytes of data.
|
||||||
|
\-\-enable\-dump
|
||||||
|
.RS 4
|
||||||
|
Enable Secure Execution guest dump support. This option requires the
|
||||||
|
-\fB\-\-comm\-key\fR option.
|
||||||
|
+\fB\-\-cck\fR option.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
@@ -146,8 +146,7 @@ Disable Secure Execution guest dump supp
|
||||||
|
\-\-enable\-cck\-extension\-secret
|
||||||
|
.RS 4
|
||||||
|
Add\-secret requests must provide an extension secret that matches the
|
||||||
|
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||||
|
-option.
|
||||||
|
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
@@ -268,7 +267,7 @@ Generate an IBM Secure Execution image:
|
||||||
|
|
||||||
|
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||||
|
.PP
|
||||||
|
-.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||||
|
+.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||||
|
.SH NOTES
|
||||||
|
.IP "1." 4
|
||||||
|
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-create.1
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||||
|
@@ -122,7 +122,7 @@ Overwrite an existing Secure Execution b
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
-\-\-comm\-key <FILE>
|
||||||
|
+\-\-cck, \-\-comm\-key <FILE>
|
||||||
|
.RS 4
|
||||||
|
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||||
|
contain exactly 32 bytes of data.
|
||||||
|
@@ -132,7 +132,7 @@ contain exactly 32 bytes of data.
|
||||||
|
\-\-enable\-dump
|
||||||
|
.RS 4
|
||||||
|
Enable Secure Execution guest dump support. This option requires the
|
||||||
|
-\fB\-\-comm\-key\fR option.
|
||||||
|
+\fB\-\-cck\fR option.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
@@ -145,8 +145,7 @@ Disable Secure Execution guest dump supp
|
||||||
|
\-\-enable\-cck\-extension\-secret
|
||||||
|
.RS 4
|
||||||
|
Add\-secret requests must provide an extension secret that matches the
|
||||||
|
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||||
|
-option.
|
||||||
|
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
@@ -249,7 +248,7 @@ Generate an IBM Secure Execution image:
|
||||||
|
|
||||||
|
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||||
|
.PP
|
||||||
|
-.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||||
|
+.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||||
|
.SH NOTES
|
||||||
|
.IP "1." 4
|
||||||
|
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||||
|
@@ -96,8 +96,8 @@ pub struct ComponentPaths {
|
||||||
|
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
|
||||||
|
pub struct CreateBootImageLegacyFlags {
|
||||||
|
/// Enable Secure Execution guest dump support. This option requires the
|
||||||
|
- /// '--comm-key' option.
|
||||||
|
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||||
|
+ /// '--cck' option.
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||||
|
pub enable_dump: Option<bool>,
|
||||||
|
|
||||||
|
/// Disable Secure Execution guest dump support (default).
|
||||||
|
@@ -105,9 +105,9 @@ pub struct CreateBootImageLegacyFlags {
|
||||||
|
pub disable_dump: Option<bool>,
|
||||||
|
|
||||||
|
/// Add-secret requests must provide an extension secret that matches the
|
||||||
|
- /// CCK-derived extension secret. This option requires the '--comm-key'
|
||||||
|
+ /// CCK-derived extension secret. This option requires the '--cck'
|
||||||
|
/// option.
|
||||||
|
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||||
|
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||||
|
pub enable_cck_extension_secret: Option<bool>,
|
||||||
|
|
||||||
|
/// Add-secret requests don't have to provide the CCK-derived extension
|
||||||
|
@@ -328,8 +328,8 @@ pub struct CreateBootImageArgs {
|
||||||
|
/// Use the content of FILE as the customer-communication key (CCK).
|
||||||
|
///
|
||||||
|
/// The file must contain exactly 32 bytes of data.
|
||||||
|
- #[arg(long, value_name = "FILE")]
|
||||||
|
- pub comm_key: Option<PathBuf>,
|
||||||
|
+ #[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
||||||
|
+ pub cck: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub legacy_flags: CreateBootImageLegacyFlags,
|
||||||
|
@@ -482,6 +482,8 @@ mod test {
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||||
|
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||||
|
+ CliOption::new("comm-key", ["--cck", "/dev/null"])])),
|
||||||
|
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||||
|
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
|
||||||
|
CliOption::new("x-scf", ["--x-scf", "0x0"])])),
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/create.rs
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||||
|
@@ -137,8 +137,7 @@ pub fn create(opt: &CreateBootImageArgs)
|
||||||
|
let verified_host_keys = opt
|
||||||
|
.certificate_args
|
||||||
|
.get_verified_hkds("Secure Execution image")?;
|
||||||
|
- let user_provided_keys =
|
||||||
|
- read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||||
|
+ let user_provided_keys = read_user_provided_keys(opt.cck.as_deref(), &opt.experimental_args)?;
|
||||||
|
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||||
|
|
||||||
|
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
58
s390-tools-pvimg-info-command-01.patch
Normal file
58
s390-tools-pvimg-info-command-01.patch
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
From 560b276f7e9938475af921c8ebd4cd05910dbf31 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Fri, 6 Dec 2024 20:45:36 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Fix possible 'range start index out of range for
|
||||||
|
slice' error
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Fix possible 'range start index 16 out of range for slice of length 0'
|
||||||
|
error by adding a check of the slice data length.
|
||||||
|
|
||||||
|
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 23 +++++++++++++++++++++++
|
||||||
|
1 file changed, 23 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
index f7ae1bc9..ac3a2e6e 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
@@ -259,6 +259,10 @@ impl SeHdr {
|
||||||
|
return Err(Error::InvalidSeHdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if sehs <= common_size {
|
||||||
|
+ return Err(Error::InvalidSeHdr);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
data.resize(sehs, 0);
|
||||||
|
reader.read_exact(&mut data[common_size..])?;
|
||||||
|
Self::try_from_data(&data)
|
||||||
|
@@ -366,3 +370,22 @@ impl AeadCipherTrait for SeHdrPlain {
|
||||||
|
self.data.aead_tag_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+#[cfg(test)]
|
||||||
|
+mod tests {
|
||||||
|
+ use std::io::Cursor;
|
||||||
|
+
|
||||||
|
+ use super::SeHdr;
|
||||||
|
+ use crate::error::Error;
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn test_sehdr_try_from_io() {
|
||||||
|
+ // Invalid SeHdr as `sehs` is set to 0
|
||||||
|
+ assert!(matches!(
|
||||||
|
+ SeHdr::try_from_io(Cursor::new([
|
||||||
|
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 8
|
||||||
|
+ ])),
|
||||||
|
+ Err(Error::InvalidSeHdr)
|
||||||
|
+ ));
|
||||||
|
+ }
|
||||||
|
+}
|
51
s390-tools-pvimg-info-command-02.patch
Normal file
51
s390-tools-pvimg-info-command-02.patch
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
From 3f6572e901ddcc654021c4302cb2a99999acb87a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Wed, 18 Dec 2024 13:41:13 +0100
|
||||||
|
Subject: [PATCH] rust/utils: mkdtemp: fix memory leak
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Fix memory leak of @template_raw. The documentation of CString::into_raw
|
||||||
|
reads:
|
||||||
|
|
||||||
|
"Consumes the CString and transfers ownership of the string to a C
|
||||||
|
caller.
|
||||||
|
...
|
||||||
|
Failure to call CString::from_raw will lead to a memory leak." [1]
|
||||||
|
|
||||||
|
Let's fix the memory leak by always calling `CString::from_raw` and
|
||||||
|
therefore reclaim the ownership.
|
||||||
|
|
||||||
|
[1] https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw
|
||||||
|
|
||||||
|
Fixes: e56acf4f14b0 ("pv_core: add `TemporaryDirectory`")
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/utils/src/tmpfile.rs | 7 ++++---
|
||||||
|
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/utils/src/tmpfile.rs b/rust/utils/src/tmpfile.rs
|
||||||
|
index 07acdba8..883d5586 100644
|
||||||
|
--- a/rust/utils/src/tmpfile.rs
|
||||||
|
+++ b/rust/utils/src/tmpfile.rs
|
||||||
|
@@ -16,13 +16,14 @@ fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
|
||||||
|
// SAFETY: template_raw is a valid CString because it was generated by
|
||||||
|
// the `CString::new`.
|
||||||
|
let ret = libc::mkdtemp(template_raw);
|
||||||
|
+ // SAFETY: `template_raw` is still a valid CString because it was
|
||||||
|
+ // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||||
|
+ let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||||
|
|
||||||
|
if ret.is_null() {
|
||||||
|
+ drop(path_cstr);
|
||||||
|
Err(std::io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
- // SAFETY: `template_raw` is still a valid CString because it was
|
||||||
|
- // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||||
|
- let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||||
|
let path = OsStr::from_bytes(path_cstr.as_bytes());
|
||||||
|
let path = std::path::PathBuf::from(path);
|
||||||
|
|
334
s390-tools-pvimg-info-command-03.patch
Normal file
334
s390-tools-pvimg-info-command-03.patch
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
From 944581eaefe4c6887790f2b8ed39c9ee76146c55 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Tue, 17 Dec 2024 11:58:01 +0100
|
||||||
|
Subject: [PATCH] rust/pvimg: Add upper estimates for the Secure Execution
|
||||||
|
header
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
A Secure Execution header V1 can be at maximum two pages large, optional
|
||||||
|
items are not supported, and the size of the encrypted part cannot be
|
||||||
|
larger than the total size of the Secure Execution header add this as
|
||||||
|
Deku assertions and additional conditions to the code. In addition, add
|
||||||
|
a check for the number of key slots.
|
||||||
|
|
||||||
|
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/src/pv_utils/error.rs | 3 +
|
||||||
|
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 50 +++++++++++++---
|
||||||
|
rust/pvimg/src/pv_utils/se_hdr/builder.rs | 10 +++-
|
||||||
|
rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs | 71 ++++++++++++++++++++---
|
||||||
|
rust/pvimg/src/pv_utils/uvdata.rs | 18 ++++--
|
||||||
|
5 files changed, 130 insertions(+), 22 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/error.rs b/rust/pvimg/src/pv_utils/error.rs
|
||||||
|
index 2a176276..a12c4a22 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/error.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/error.rs
|
||||||
|
@@ -30,6 +30,9 @@ pub enum Error {
|
||||||
|
#[error("Invalid Secure Execution header")]
|
||||||
|
InvalidSeHdr,
|
||||||
|
|
||||||
|
+ #[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")]
|
||||||
|
+ InvalidSeHdrTooLarge { given: usize, maximum: usize },
|
||||||
|
+
|
||||||
|
#[error("Invalid component metadata.")]
|
||||||
|
InvalidComponentMetadata,
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
index ac3a2e6e..b8dadba1 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||||
|
@@ -171,8 +171,8 @@ impl AeadCipherTrait for SeHdr {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AeadDataTrait for SeHdr {
|
||||||
|
- fn aad(&self) -> Vec<u8> {
|
||||||
|
- [serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat()
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||||
|
+ Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data(&self) -> Vec<u8> {
|
||||||
|
@@ -265,7 +265,7 @@ impl SeHdr {
|
||||||
|
|
||||||
|
data.resize(sehs, 0);
|
||||||
|
reader.read_exact(&mut data[common_size..])?;
|
||||||
|
- Self::try_from_data(&data)
|
||||||
|
+ Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -342,13 +342,13 @@ impl UvDataPlainTrait for SeHdrPlain {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AeadPlainDataTrait for SeHdrPlain {
|
||||||
|
- fn aad(&self) -> Vec<u8> {
|
||||||
|
- let data_aad = self.data.aad();
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||||
|
+ let data_aad = self.data.aad()?;
|
||||||
|
|
||||||
|
- [serialize_to_bytes(&self.common).unwrap(), data_aad].concat()
|
||||||
|
+ Ok([serialize_to_bytes(&self.common)?, data_aad].concat())
|
||||||
|
}
|
||||||
|
|
||||||
|
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||||
|
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||||
|
self.data.data()
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -387,5 +387,41 @@ mod tests {
|
||||||
|
])),
|
||||||
|
Err(Error::InvalidSeHdr)
|
||||||
|
));
|
||||||
|
+
|
||||||
|
+ // Invalid SeHdr as the `sehs` is too large.
|
||||||
|
+ assert!(matches!(
|
||||||
|
+ SeHdr::try_from_io(Cursor::new([
|
||||||
|
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 1, 255, 65, 65, 65, 65, 67, 0,
|
||||||
|
+ 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241,
|
||||||
|
+ 241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65,
|
||||||
|
+ 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112,
|
||||||
|
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120,
|
||||||
|
+ 0, 112, 112, 0, 1, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
+ 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91,
|
||||||
|
+ 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||||
|
+ 91, 91, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0,
|
||||||
|
+ 0, 0, 48, 53, 53, 53, 53, 53, 53, 53, 91, 91, 91, 241, 241, 46, 49, 49, 0, 49, 49,
|
||||||
|
+ 0, 0, 112, 112, 112, 91, 0, 0, 0, 0, 9, 0, 49, 50, 22, 241, 241, 241, 241, 241,
|
||||||
|
+ 241, 241, 241, 241, 241, 241, 91, 91, 91, 91, 91, 255, 251, 0, 0, 91, 91, 91, 91,
|
||||||
|
+ 91, 91, 91, 91, 91, 91, 91, 0, 0, 91, 0, 0, 10, 91, 91, 91, 65, 65, 65, 65
|
||||||
|
+ ])),
|
||||||
|
+ Err(Error::InvalidSeHdr)
|
||||||
|
+ ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/se_hdr/builder.rs b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||||
|
index ba6de898..93bcc7af 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||||
|
@@ -230,8 +230,14 @@ mod tests {
|
||||||
|
|
||||||
|
let decrypted = bin.decrypt(&prot_key).expect("BUG");
|
||||||
|
assert_eq!(bin.common, decrypted.common);
|
||||||
|
- assert_eq!(bin.aad(), decrypted.aad());
|
||||||
|
- assert_ne!(&bin.data(), decrypted.data().value());
|
||||||
|
+ assert_eq!(
|
||||||
|
+ bin.aad().expect("should not fail"),
|
||||||
|
+ decrypted.aad().expect("should not fail")
|
||||||
|
+ );
|
||||||
|
+ assert_ne!(
|
||||||
|
+ &bin.data(),
|
||||||
|
+ decrypted.data().expect("should not fail").value()
|
||||||
|
+ );
|
||||||
|
let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||||
|
index a7f2f609..b179d50d 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||||
|
@@ -19,6 +19,7 @@ use serde::{Serialize, Serializer};
|
||||||
|
use super::keys::phkh_v1;
|
||||||
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
+ misc::PAGESIZE,
|
||||||
|
pv_utils::{
|
||||||
|
error::Result,
|
||||||
|
se_hdr::{
|
||||||
|
@@ -51,11 +52,14 @@ struct HdrSizesV1 {
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||||
|
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||||
|
struct SeHdrAadV1 {
|
||||||
|
+ #[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
|
||||||
|
sehs: u32,
|
||||||
|
#[serde(serialize_with = "ser_hex")]
|
||||||
|
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
|
||||||
|
res1: u32,
|
||||||
|
+ #[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
|
||||||
|
nks: u64,
|
||||||
|
+ #[deku(assert = "*sea <= (*sehs).into()")]
|
||||||
|
sea: u64,
|
||||||
|
nep: u64,
|
||||||
|
#[serde(serialize_with = "ser_lower_hex")]
|
||||||
|
@@ -118,6 +122,7 @@ pub struct SeHdrConfV1 {
|
||||||
|
psw: PSW,
|
||||||
|
#[serde(serialize_with = "ser_lower_hex")]
|
||||||
|
scf: u64,
|
||||||
|
+ #[deku(assert_eq = "0")]
|
||||||
|
noi: u32,
|
||||||
|
res2: u32,
|
||||||
|
#[deku(count = "noi")]
|
||||||
|
@@ -200,6 +205,7 @@ where
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeHdrDataV1 {
|
||||||
|
+ const MAX_SIZE: usize = 2 * PAGESIZE;
|
||||||
|
const PCF_DEFAULT: u64 = 0x0;
|
||||||
|
const SCF_DEFAULT: u64 = 0x0;
|
||||||
|
|
||||||
|
@@ -241,7 +247,14 @@ impl SeHdrDataV1 {
|
||||||
|
tag: SeHdrTagV1::default(),
|
||||||
|
};
|
||||||
|
let hdr_size = ret.size()?;
|
||||||
|
- ret.aad.sehs = hdr_size.phs.try_into()?;
|
||||||
|
+ let phs = hdr_size.phs.try_into()?;
|
||||||
|
+ if phs > Self::MAX_SIZE {
|
||||||
|
+ return Err(Error::InvalidSeHdrTooLarge {
|
||||||
|
+ given: phs,
|
||||||
|
+ maximum: Self::MAX_SIZE,
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ ret.aad.sehs = phs.try_into()?;
|
||||||
|
ret.aad.sea = hdr_size.sea;
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
@@ -494,8 +507,8 @@ impl KeyExchangeTrait for SeHdrBinV1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AeadDataTrait for SeHdrBinV1 {
|
||||||
|
- fn aad(&self) -> Vec<u8> {
|
||||||
|
- serialize_to_bytes(&self.aad).unwrap()
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||||
|
+ serialize_to_bytes(&self.aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data(&self) -> Vec<u8> {
|
||||||
|
@@ -508,12 +521,12 @@ impl AeadDataTrait for SeHdrBinV1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AeadPlainDataTrait for SeHdrDataV1 {
|
||||||
|
- fn aad(&self) -> Vec<u8> {
|
||||||
|
- serialize_to_bytes(&self.aad).unwrap()
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||||
|
+ serialize_to_bytes(&self.aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||||
|
- serialize_to_bytes(self.data.value()).unwrap().into()
|
||||||
|
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||||
|
+ Ok(serialize_to_bytes(self.data.value())?.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag(&self) -> Vec<u8> {
|
||||||
|
@@ -610,4 +623,48 @@ mod tests {
|
||||||
|
assert_eq!(psw, hdr_data_v1.data.value().psw);
|
||||||
|
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value());
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ #[test]
|
||||||
|
+ fn max_size_sehdr_test() {
|
||||||
|
+ const MAX_HOST_KEYS: usize = 95;
|
||||||
|
+
|
||||||
|
+ let (_, host_key) = get_test_key_and_cert();
|
||||||
|
+ let pub_key = host_key.public_key().unwrap();
|
||||||
|
+ let host_keys_max: Vec<_> = (0..MAX_HOST_KEYS).map(|_| pub_key.clone()).collect();
|
||||||
|
+ let too_many_host_keys: Vec<_> = (0..MAX_HOST_KEYS + 1).map(|_| pub_key.clone()).collect();
|
||||||
|
+ let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||||
|
+ let meta = ComponentMetadataV1 {
|
||||||
|
+ ald: [0x1; SHA_512_HASH_LEN],
|
||||||
|
+ pld: [0x2; SHA_512_HASH_LEN],
|
||||||
|
+ tld: [0x3; SHA_512_HASH_LEN],
|
||||||
|
+ nep: 3,
|
||||||
|
+ key: xts_key,
|
||||||
|
+ };
|
||||||
|
+ let psw = PSW {
|
||||||
|
+ addr: 1234,
|
||||||
|
+ mask: 5678,
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||||
|
+ .expect("should not fail");
|
||||||
|
+ builder
|
||||||
|
+ .add_hostkeys(&host_keys_max)
|
||||||
|
+ .expect("should not fail")
|
||||||
|
+ .with_components(meta.clone())
|
||||||
|
+ .expect("should not fail");
|
||||||
|
+ let bin = builder.build().expect("should not fail");
|
||||||
|
+ assert_eq!(bin.common.version, SeHdrVersion::V1);
|
||||||
|
+ let hdr_v1: SeHdrBinV1 = bin.data.try_into().expect("should not fail");
|
||||||
|
+ assert_eq!(hdr_v1.aad.sehs, 8160);
|
||||||
|
+
|
||||||
|
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||||
|
+ .expect("should not fail");
|
||||||
|
+
|
||||||
|
+ builder
|
||||||
|
+ .add_hostkeys(&too_many_host_keys)
|
||||||
|
+ .expect("should not fail")
|
||||||
|
+ .with_components(meta)
|
||||||
|
+ .expect("should not fail");
|
||||||
|
+ assert!(matches!(builder.build(), Err(Error::InvalidSeHdr)));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
diff --git a/rust/pvimg/src/pv_utils/uvdata.rs b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||||
|
index b0ec355a..c6ed9567 100644
|
||||||
|
--- a/rust/pvimg/src/pv_utils/uvdata.rs
|
||||||
|
+++ b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||||
|
@@ -34,7 +34,7 @@ pub trait AeadCipherTrait {
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub trait AeadDataTrait {
|
||||||
|
/// Returns the authenticated associated data.
|
||||||
|
- fn aad(&self) -> Vec<u8>;
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Returns the encrypted data.
|
||||||
|
fn data(&self) -> Vec<u8>;
|
||||||
|
@@ -47,10 +47,10 @@ pub trait AeadDataTrait {
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub trait AeadPlainDataTrait {
|
||||||
|
/// Returns the authenticated associated data.
|
||||||
|
- fn aad(&self) -> Vec<u8>;
|
||||||
|
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Returns the unencrypted data.
|
||||||
|
- fn data(&self) -> Confidential<Vec<u8>>;
|
||||||
|
+ fn data(&self) -> Result<Confidential<Vec<u8>>>;
|
||||||
|
|
||||||
|
/// Returns the tag data.
|
||||||
|
fn tag(&self) -> Vec<u8>;
|
||||||
|
@@ -124,8 +124,14 @@ pub trait UvDataPlainTrait:
|
||||||
|
expected: self.aead_key_type().to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
- let aad = self.aad();
|
||||||
|
- let unecrypted_data = self.data();
|
||||||
|
+ let aad = self.aad().map_err(|err| match err {
|
||||||
|
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||||
|
+ err => err,
|
||||||
|
+ })?;
|
||||||
|
+ let unecrypted_data = self.data().map_err(|err| match err {
|
||||||
|
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||||
|
+ err => err,
|
||||||
|
+ })?;
|
||||||
|
let iv = self.iv();
|
||||||
|
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
|
||||||
|
Self::C::try_from_data(&result.into_buf())
|
||||||
|
@@ -169,7 +175,7 @@ pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clon
|
||||||
|
}
|
||||||
|
|
||||||
|
let tag_size = self.aead_tag_size();
|
||||||
|
- let aad = self.aad();
|
||||||
|
+ let aad = self.aad()?;
|
||||||
|
let unecrypted_data = self.data();
|
||||||
|
let iv = self.iv();
|
||||||
|
let tag = self.tag();
|
101
s390-tools-pvimg-info-command-04.patch
Normal file
101
s390-tools-pvimg-info-command-04.patch
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
From 6e48c5ebaa26c6bd2a1bc33ccf36ed8bd6946358 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Date: Tue, 17 Dec 2024 18:13:31 +0100
|
||||||
|
Subject: [PATCH] pvimg: info: Rename '--key' into '--hdr-key' and use '--key'
|
||||||
|
as an alias
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Rename '--key' into '--hdr-key' and use '--key' as an (non-visible)
|
||||||
|
alias for '--hdr-key' in order to keep the command line backwards
|
||||||
|
compatible. The chances of someone using '--key' are very low, as this
|
||||||
|
version has not yet been released by any OS distribution.
|
||||||
|
|
||||||
|
This change makes the command line options for the different subcommands
|
||||||
|
more consistent and therefore easier to use.
|
||||||
|
|
||||||
|
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||||
|
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||||
|
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||||
|
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||||
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||||
|
---
|
||||||
|
rust/pvimg/man/pvimg-info.1 | 2 +-
|
||||||
|
rust/pvimg/src/cli.rs | 22 +++++++++++++++++++---
|
||||||
|
rust/pvimg/src/cmd/info.rs | 2 +-
|
||||||
|
3 files changed, 21 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-info.1
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||||
|
@@ -37,7 +37,7 @@ Possible values:
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
-\-\-key <FILE>
|
||||||
|
+\-\-hdr\-key <FILE>
|
||||||
|
.RS 4
|
||||||
|
Use the key in FILE to decrypt the Secure Execution header.
|
||||||
|
.RE
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||||
|
@@ -192,8 +192,8 @@ pub struct InfoArgs {
|
||||||
|
pub format: OutputFormat,
|
||||||
|
|
||||||
|
/// Use the key in FILE to decrypt the Secure Execution header.
|
||||||
|
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||||
|
- pub key: Option<PathBuf>,
|
||||||
|
+ #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
|
||||||
|
+ pub hdr_key: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
@@ -710,6 +710,22 @@ mod test {
|
||||||
|
CliOption::new("image", ["/dev/null"]),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
+ flat_map_collect(insert(
|
||||||
|
+ args.clone(),
|
||||||
|
+ vec![
|
||||||
|
+ CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]),
|
||||||
|
+ CliOption::new("format", ["--format=json"]),
|
||||||
|
+ CliOption::new("image", ["/dev/null"]),
|
||||||
|
+ ],
|
||||||
|
+ )),
|
||||||
|
+ flat_map_collect(insert(
|
||||||
|
+ args.clone(),
|
||||||
|
+ vec![
|
||||||
|
+ CliOption::new("hdr-key", ["--key", "/dev/null"]),
|
||||||
|
+ CliOption::new("format", ["--format=json"]),
|
||||||
|
+ CliOption::new("image", ["/dev/null"]),
|
||||||
|
+ ],
|
||||||
|
+ )),
|
||||||
|
// separation between keyword and positional args works
|
||||||
|
flat_map_collect(insert(
|
||||||
|
args.clone(),
|
||||||
|
@@ -750,7 +766,7 @@ mod test {
|
||||||
|
|
||||||
|
// Test for invalid combinations
|
||||||
|
// Input is missing
|
||||||
|
- let mut pvimg_invalid_args = vec![vec!["pvimg", "test"]];
|
||||||
|
+ let mut pvimg_invalid_args = vec![vec!["pvimg", "info"]];
|
||||||
|
|
||||||
|
for create_args in &valid_test_args {
|
||||||
|
pvimg_valid_args.push(
|
||||||
|
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/info.rs
|
||||||
|
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||||
|
@@ -27,7 +27,7 @@ pub fn info(opt: &InfoArgs) -> Result<Ow
|
||||||
|
|
||||||
|
SeHdr::seek_sehdr(&mut input, None)?;
|
||||||
|
let hdr = SeHdr::try_from_io(input)?;
|
||||||
|
- if let Some(key_path) = &opt.key {
|
||||||
|
+ if let Some(key_path) = &opt.hdr_key {
|
||||||
|
let key =
|
||||||
|
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
|
||||||
|
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;
|
6
s390-tools-rpmlintrc
Normal file
6
s390-tools-rpmlintrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
addFilter("statically-linked-binary /usr/lib/s390-tools/.*")
|
||||||
|
addFilter("statically-linked-binary /usr/bin/read_values")
|
||||||
|
addFilter("systemd-service-without-service_.* *@.service")
|
||||||
|
addFilter("position-independent-executable-suggested ")
|
||||||
|
addFilter("non-etc-or-var-file-marked-as-conffile /boot/zipl/active_devices.txt")
|
||||||
|
addFilter("zero-length /boot/zipl/active_devices.txt")
|
30
s390-tools-sles12-create-filesystem-links.patch
Normal file
30
s390-tools-sles12-create-filesystem-links.patch
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
etc/udev/rules.d/59-dasd.rules | 10 +++++++++-
|
||||||
|
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/etc/udev/rules.d/59-dasd.rules
|
||||||
|
+++ b/etc/udev/rules.d/59-dasd.rules
|
||||||
|
@@ -15,7 +15,7 @@
|
||||||
|
|
||||||
|
LABEL="dasd_block_end"
|
||||||
|
|
||||||
|
-ACTION!="change|add", GOTO="dasd_symlinks_end"
|
||||||
|
+ACTION!="change|add", GOTO="dasd_partition_end"
|
||||||
|
|
||||||
|
# for partitions import parent information
|
||||||
|
KERNEL=="dasd*[0-9]", IMPORT{parent}=="ID_*"
|
||||||
|
@@ -24,6 +24,14 @@
|
||||||
|
KERNEL=="dasd*[0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}-part%n"
|
||||||
|
KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}-part%n"
|
||||||
|
|
||||||
|
+LABEL="dasd_partition_end"
|
||||||
|
+
|
||||||
|
+ENV{ID_SERIAL}!="?*", GOTO="dasd_symlinks_end"
|
||||||
|
+# by-label/by-uuid (filesystem properties)
|
||||||
|
+IMPORT{builtin}="blkid"
|
||||||
|
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID}"
|
||||||
|
+ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_SAFE}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_SAFE}"
|
||||||
|
+
|
||||||
|
LABEL="dasd_symlinks_end"
|
||||||
|
|
||||||
|
# on device add set request queue scheduler to none
|
@ -0,0 +1,33 @@
|
|||||||
|
From d0c2ffc90b9ee0e7b741d1c4b644cdf79f1d922b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Wed, 20 May 2015 11:57:11 +0200
|
||||||
|
Subject: [PATCH] fdasd: skip partition check and BLKRRPART ioctl for emulated
|
||||||
|
devices
|
||||||
|
|
||||||
|
If 'fdasd -f' is called we cannot rely on the partition detection
|
||||||
|
via a simple check of the minor number, so the check should be
|
||||||
|
suppressed.
|
||||||
|
Similarly, not every emulated device supports the BLKRRPART ioctl,
|
||||||
|
so we should be suppressing the error message for these devices, too.
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.de>
|
||||||
|
---
|
||||||
|
fdasd/fdasd.c | 4 +++-
|
||||||
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/fdasd/fdasd.c
|
||||||
|
+++ b/fdasd/fdasd.c
|
||||||
|
@@ -1231,10 +1231,12 @@
|
||||||
|
*/
|
||||||
|
static void fdasd_reread_partition_table(fdasd_anchor_t *anc)
|
||||||
|
{
|
||||||
|
+ int rc = 0 ;
|
||||||
|
if (!anc->silent)
|
||||||
|
printf("rereading partition table...\n");
|
||||||
|
|
||||||
|
- if (dasd_reread_partition_table(options.device, 5) != 0) {
|
||||||
|
+ rc = dasd_reread_partition_table(options.device, 1);
|
||||||
|
+ if (rc == EINVAL && !anc->force_virtual) {
|
||||||
|
fdasd_error(anc, unable_to_ioctl, "Error while rereading "
|
||||||
|
"partition table.\nPlease reboot!");
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
From f7a0f391f2c4e8acc96b21ab5de54a178aa60088 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 22 Nov 2013 15:39:38 +0100
|
||||||
|
Subject: [PATCH] 59-dasd.rules: generate by-id links on 'change' and 'add'
|
||||||
|
|
||||||
|
The by-id rules need to be triggered on both, 'change' and 'add',
|
||||||
|
to work correctly during restarting udev.
|
||||||
|
|
||||||
|
References: bnc#808042
|
||||||
|
|
||||||
|
Signed-off-by: Robert Milasan <rmilasan@suse.de>
|
||||||
|
---
|
||||||
|
etc/udev/rules.d/59-dasd.rules | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/etc/udev/rules.d/59-dasd.rules b/etc/udev/rules.d/59-dasd.rules
|
||||||
|
index 2b1435c..a08cb7c 100644
|
||||||
|
--- a/etc/udev/rules.d/59-dasd.rules
|
||||||
|
+++ b/etc/udev/rules.d/59-dasd.rules
|
||||||
|
@@ -6,9 +6,9 @@
|
||||||
|
SUBSYSTEM!="block", GOTO="dasd_symlinks_end"
|
||||||
|
KERNEL!="dasd*", GOTO="dasd_symlinks_end"
|
||||||
|
|
||||||
|
-ACTION!="change", GOTO="dasd_block_end"
|
||||||
|
+ACTION!="change|add", GOTO="dasd_block_end"
|
||||||
|
# by-id (hardware serial number)
|
||||||
|
-KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/sbin/dasdinfo -a -e -b $kernel"
|
||||||
|
+KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/usr/sbin/dasdinfo -a -e -b $kernel"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}"
|
||||||
|
--
|
||||||
|
1.8.1.4
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
From f7a0f391f2c4e8acc96b21ab5de54a178aa60088 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 22 Nov 2013 15:39:38 +0100
|
||||||
|
Subject: [PATCH] 59-dasd.rules: generate by-id links on 'change' and 'add'
|
||||||
|
|
||||||
|
The by-id rules need to be triggered on both, 'change' and 'add',
|
||||||
|
to work correctly during restarting udev.
|
||||||
|
|
||||||
|
References: bnc#808042
|
||||||
|
|
||||||
|
Signed-off-by: Robert Milasan <rmilasan@suse.de>
|
||||||
|
---
|
||||||
|
etc/udev/rules.d/59-dasd.rules | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/etc/udev/rules.d/59-dasd.rules
|
||||||
|
+++ b/etc/udev/rules.d/59-dasd.rules
|
||||||
|
@@ -6,7 +6,7 @@
|
||||||
|
SUBSYSTEM!="block", GOTO="dasd_symlinks_end"
|
||||||
|
KERNEL!="dasd*", GOTO="dasd_symlinks_end"
|
||||||
|
|
||||||
|
-ACTION!="change", GOTO="dasd_block_end"
|
||||||
|
+ACTION!="change|add", GOTO="dasd_block_end"
|
||||||
|
# by-id (hardware serial number)
|
||||||
|
KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/sbin/dasdinfo -a -e -b $kernel"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
|
20
s390-tools-sles12-zipl_boot_msg.patch
Normal file
20
s390-tools-sles12-zipl_boot_msg.patch
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
zipl/boot/menu.c | 7 +++++--
|
||||||
|
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/zipl/boot/menu.c
|
||||||
|
+++ b/zipl/boot/menu.c
|
||||||
|
@@ -168,8 +168,11 @@
|
||||||
|
/* print config list */
|
||||||
|
menu_list();
|
||||||
|
|
||||||
|
- if (is_zvm())
|
||||||
|
- printf("Note: VM users please use '#cp vi vmsg <input>'\n");
|
||||||
|
+ if (is_zvm()) {
|
||||||
|
+ printf(" \n");
|
||||||
|
+ printf("Note: VM users please use '#cp vi vmsg <input> <kernel-parameters>'\n");
|
||||||
|
+ printf(" \n");
|
||||||
|
+ }
|
||||||
|
|
||||||
|
value = menu_read();
|
||||||
|
|
148
s390-tools-sles15-sysconfig-compatible-dumpconf.patch
Normal file
148
s390-tools-sles15-sysconfig-compatible-dumpconf.patch
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
---
|
||||||
|
etc/sysconfig/dumpconf | 133 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
1 file changed, 133 insertions(+)
|
||||||
|
|
||||||
|
--- a/etc/sysconfig/dumpconf
|
||||||
|
+++ b/etc/sysconfig/dumpconf
|
||||||
|
@@ -1,3 +1,4 @@
|
||||||
|
+###########################################################################################
|
||||||
|
#
|
||||||
|
# s390 dump config
|
||||||
|
#
|
||||||
|
@@ -78,3 +79,135 @@
|
||||||
|
# dumpconf becomes active immediately during system startup.
|
||||||
|
#
|
||||||
|
# ON_PANIC=reipl
|
||||||
|
+
|
||||||
|
+############################ Begin Definitions ###########################################
|
||||||
|
+## Path: System/Dumpconf
|
||||||
|
+## Description: Configures the actions which should be performed after a kernel panic
|
||||||
|
+## Type: list(stop,dump,vmcmd,reipl,dump_reipl)
|
||||||
|
+## Default: "stop"
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the action that should be taken if a kernel panic happens.
|
||||||
|
+#
|
||||||
|
+ON_PANIC="stop"
|
||||||
|
+
|
||||||
|
+## Type: integer(0:300)
|
||||||
|
+## Default: 5
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Using reipl or dump_reipl actions with ON_PANIC can lead to the system
|
||||||
|
+# looping with alternating IPLs and crashes. Use DELAY_MINUTES to prevent
|
||||||
|
+# such a loop. DELAY_MINUTES delays activating the specified panic action
|
||||||
|
+# for a newly started system. When the specified time has elapsed, dumpconf
|
||||||
|
+# activates the specified panic action. This action is taken should the
|
||||||
|
+# system subsequently crash. If the system crashes before the time has
|
||||||
|
+# elapsed the previously defined action is taken. If no previous action has
|
||||||
|
+# been defined the default action (STOP) is performed.
|
||||||
|
+#
|
||||||
|
+DELAY_MINUTES="5"
|
||||||
|
+
|
||||||
|
+## Type: list(ccw,fcp,nvme)
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the type, ccw for DASD, fcp for zFCP, or nvme for NVMe Disk.
|
||||||
|
+#
|
||||||
|
+DUMP_TYPE=""
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the device id for a DASD or SCSI over zFCP dump device.
|
||||||
|
+#
|
||||||
|
+# For example (DASD and SCSI over zFCP have the same structure): DEVICE=0.0.4711
|
||||||
|
+#
|
||||||
|
+DEVICE=""
|
||||||
|
+
|
||||||
|
+# Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the WWPN for a zFCP dump device.
|
||||||
|
+#
|
||||||
|
+# For example: WWPN=0x5005076303004711
|
||||||
|
+#
|
||||||
|
+WWPN=""
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the LUN for a zFCP dump device.
|
||||||
|
+#
|
||||||
|
+# For example: LUN=0x4711000000000000
|
||||||
|
+#
|
||||||
|
+LUN=""
|
||||||
|
+
|
||||||
|
+## Type: integer(0:30)
|
||||||
|
+## Default: "0"
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the Boot program selector for a zFCP dump device.
|
||||||
|
+#
|
||||||
|
+# A decimal value between 0 and 30 specifying the program to be loaded from
|
||||||
|
+# the FCP-I/O device.
|
||||||
|
+#
|
||||||
|
+BOOTPROG="0"
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: "0"
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the Boot record logical block address for a zFCP dump device.
|
||||||
|
+#
|
||||||
|
+# The hexadecimal digits designating the logical-block address of the boot record of the FCP-I/O device.
|
||||||
|
+# It must be a value from 0-FFFFFFFF FFFFFFFF. For values longer than 8 hex characters at least one separator
|
||||||
|
+# blank is required after the 8th character.
|
||||||
|
+#
|
||||||
|
+BR_LBA="0"
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the Function ID for NVMe dump device.
|
||||||
|
+#
|
||||||
|
+# The hexadecimal digits designating the Function ID for the NMVe disk.
|
||||||
|
+#
|
||||||
|
+# For example: FID=0x00000300
|
||||||
|
+#
|
||||||
|
+FID=""
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# Define the Namespace ID for the NVMe dump device
|
||||||
|
+#
|
||||||
|
+# The hexadecimal digits designating the Namespace ID for the NMVe disk.
|
||||||
|
+#
|
||||||
|
+# For example: NSID=0x00000001
|
||||||
|
+#
|
||||||
|
+NSID=""
|
||||||
|
+
|
||||||
|
+## Type: string
|
||||||
|
+## Default: ""
|
||||||
|
+## ServiceRestart: dumpconf
|
||||||
|
+#
|
||||||
|
+# VMCMD_<X>
|
||||||
|
+# Specifies a CP command, <X> is a number from one to eight. You can
|
||||||
|
+# specify up to eight CP commands that are executed in case of a kernel
|
||||||
|
+# panic. Note that VM commands, device adresses, and VM guest names
|
||||||
|
+# must be uppercase.
|
||||||
|
+#
|
||||||
|
+VMCMD_1=""
|
||||||
|
+VMCMD_2=""
|
||||||
|
+VMCMD_3=""
|
||||||
|
+VMCMD_4=""
|
||||||
|
+VMCMD_5=""
|
||||||
|
+VMCMD_6=""
|
||||||
|
+VMCMD_7=""
|
||||||
|
+VMCMD_8=""
|
||||||
|
+
|
||||||
|
+############################### End Definitions ##############################################
|
||||||
|
\ No newline at end of file
|
@ -0,0 +1,50 @@
|
|||||||
|
Subject: zdev: Add support for handling I/O configuration data
|
||||||
|
From: Peter Oberparleiter <oberpar@linux.ibm.com>
|
||||||
|
|
||||||
|
Summary: zdev: Add support for handling I/O configuration data
|
||||||
|
Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode
|
||||||
|
can access a firmware-generated I/O configuration data file that
|
||||||
|
contains s390-specific information about available I/O devices
|
||||||
|
such as qeth device numbers and parameters, and FCP device IDs.
|
||||||
|
|
||||||
|
This data file is intended to remove the need for users to
|
||||||
|
manually enter the corresponding device data during installation.
|
||||||
|
|
||||||
|
Linux kernels with the corresponding support make the I/O
|
||||||
|
configuration data available at the following location:
|
||||||
|
|
||||||
|
/sys/firmware/sclp_sd/config/data
|
||||||
|
|
||||||
|
This patch set adds support for handling this data file using the
|
||||||
|
chzdev and lszdev tools:
|
||||||
|
|
||||||
|
- I/O configuration data can be applied using chzdev's --import
|
||||||
|
option
|
||||||
|
- Initial RAM-Disk scripts automatically apply the
|
||||||
|
I/O configuration data to the system configuration
|
||||||
|
- lszdev can be used to display the applied auto-configuration
|
||||||
|
data
|
||||||
|
- chzdev can be used to manually override the
|
||||||
|
auto-configuration data
|
||||||
|
|
||||||
|
Upstream-ID: -
|
||||||
|
Problem-ID: LS1604
|
||||||
|
|
||||||
|
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
|
||||||
|
---
|
||||||
|
zdev/src/zdev-root-update.dracut | 6 ------
|
||||||
|
1 file changed, 6 deletions(-)
|
||||||
|
|
||||||
|
--- a/zdev/src/zdev-root-update.dracut
|
||||||
|
+++ b/zdev/src/zdev-root-update.dracut
|
||||||
|
@@ -20,10 +20,4 @@
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
-echo "Installing IPL record"
|
||||||
|
-zipl --noninteractive || {
|
||||||
|
- echo "${TOOLNAME}: Error: Could not install IPL record" >&2
|
||||||
|
- exit 1
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
exit 0
|
465
s390-tools-sles15sp3-Allow-multiple-device-arguments.patch
Normal file
465
s390-tools-sles15sp3-Allow-multiple-device-arguments.patch
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
From d6582bbaf0f3986a42f562046dc0caa9de89c75e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 6 Oct 2017 08:58:17 +0200
|
||||||
|
Subject: [PATCH] dasdfmt: Allow multiple device arguments
|
||||||
|
|
||||||
|
Allow the user to specify several devices as arguments to dasdfmt.
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.com>
|
||||||
|
---
|
||||||
|
dasdfmt/dasdfmt.8 | 6 -
|
||||||
|
dasdfmt/dasdfmt.c | 197 +++++++++++++++++++++++++++++++-----------------------
|
||||||
|
2 files changed, 119 insertions(+), 84 deletions(-)
|
||||||
|
|
||||||
|
--- a/dasdfmt/dasdfmt.8
|
||||||
|
+++ b/dasdfmt/dasdfmt.8
|
||||||
|
@@ -11,14 +11,14 @@
|
||||||
|
.br
|
||||||
|
[\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR]
|
||||||
|
.br
|
||||||
|
- [\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] \fIdevice\fR
|
||||||
|
+ [\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] \fIdevice\fR [\fIdevice\fR]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
-\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it
|
||||||
|
+\fBdasdfmt\fR formats one or several DASD (ECKD) disk drive(s) to prepare them
|
||||||
|
for usage with Linux for S/390.
|
||||||
|
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
|
||||||
|
Any device node created by udev for kernel 2.6 can be used
|
||||||
|
-(e.g. '/dev/dasd/0.0.b100/disc').
|
||||||
|
+(e.g. '/dev/dasd/0.0.b100/disc'). It is possible to specify up to 512 devices.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in
|
||||||
|
--- a/dasdfmt/dasdfmt.c
|
||||||
|
+++ b/dasdfmt/dasdfmt.c
|
||||||
|
@@ -25,6 +25,8 @@
|
||||||
|
|
||||||
|
#include "dasdfmt.h"
|
||||||
|
|
||||||
|
+#define MAX_DEVICES 512
|
||||||
|
+#define MAX_LENGTH 256
|
||||||
|
#define BUSIDSIZE 8
|
||||||
|
#define SEC_PER_DAY (60 * 60 * 24)
|
||||||
|
#define SEC_PER_HOUR (60 * 60)
|
||||||
|
@@ -57,7 +59,9 @@
|
||||||
|
static struct dasdfmt_globals {
|
||||||
|
dasd_information2_t dasd_info;
|
||||||
|
char *dev_path; /* device path entered by user */
|
||||||
|
+ char dev_path_array[MAX_DEVICES][MAX_LENGTH]; /* Array of device paths entered by user */
|
||||||
|
char *dev_node; /* reliable device node determined by dasdfmt */
|
||||||
|
+ char dev_node_array[MAX_DEVICES][MAX_LENGTH]; /* Array of reliable device nodes determined by dasdfmt */
|
||||||
|
int verbosity;
|
||||||
|
int testmode;
|
||||||
|
int withoutprompt;
|
||||||
|
@@ -484,15 +488,15 @@
|
||||||
|
program_interrupt_in_progress = 1;
|
||||||
|
|
||||||
|
if (disk_disabled) {
|
||||||
|
- printf("Re-accessing the device...\n");
|
||||||
|
+ printf("Re-accessing %s...\n", g.dev_path);
|
||||||
|
disk_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
- printf("Rereading the partition table...\n");
|
||||||
|
+ printf("Rereading the partition table for %s...\n", g.dev_path);
|
||||||
|
rc = dasd_reread_partition_table(g.dev_node, 5);
|
||||||
|
if (rc) {
|
||||||
|
ERRMSG("%s: (signal handler) Re-reading partition table "
|
||||||
|
- "failed. (%s)\n", prog_name, strerror(rc));
|
||||||
|
+ "for %s failed. (%s)\n", prog_name, g.dev_path, strerror(rc));
|
||||||
|
} else {
|
||||||
|
printf("Exiting...\n");
|
||||||
|
}
|
||||||
|
@@ -512,9 +516,6 @@
|
||||||
|
unsigned int maj, min;
|
||||||
|
struct stat dev_stat;
|
||||||
|
|
||||||
|
- if (optind + 1 < argc)
|
||||||
|
- error("More than one device specified!");
|
||||||
|
-
|
||||||
|
if (optind >= argc)
|
||||||
|
error("No device specified!");
|
||||||
|
|
||||||
|
@@ -610,10 +611,10 @@
|
||||||
|
error("the ioctl call to retrieve read/write status information failed: %s",
|
||||||
|
strerror(err));
|
||||||
|
if (ro)
|
||||||
|
- error("Disk is read only!");
|
||||||
|
+ error("Disk %s is read only!", g.dev_path);
|
||||||
|
if (!g.force) {
|
||||||
|
if (g.dasd_info.open_count > 1)
|
||||||
|
- error("Disk in use!");
|
||||||
|
+ error("Disk %s is in use!", g.dev_path);
|
||||||
|
}
|
||||||
|
if (strncmp(g.dasd_info.type, "ECKD", 4) != 0) {
|
||||||
|
warnx("Unsupported disk type");
|
||||||
|
@@ -700,7 +701,7 @@
|
||||||
|
struct dasd_eckd_characteristics *characteristics;
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Retrieving disk geometry...\n");
|
||||||
|
+ printf("Retrieving disk geometry for %s...\n", g.dev_path);
|
||||||
|
|
||||||
|
characteristics = (struct dasd_eckd_characteristics *)
|
||||||
|
&g.dasd_info.characteristics;
|
||||||
|
@@ -728,13 +729,13 @@
|
||||||
|
"Cylinders above this limit will not be"
|
||||||
|
" accessible as a linux partition!\n"
|
||||||
|
"Type \"yes\" to continue, no will leave"
|
||||||
|
- " the disk untouched: ", LV_COMPAT_CYL);
|
||||||
|
+ " the %s disk untouched: ", LV_COMPAT_CYL, g.dev_path);
|
||||||
|
if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL)
|
||||||
|
return;
|
||||||
|
if (strcasecmp(inp_buffer, "yes") &&
|
||||||
|
strcasecmp(inp_buffer, "yes\n")) {
|
||||||
|
- printf("Omitting ioctl call (disk will "
|
||||||
|
- "NOT be formatted).\n");
|
||||||
|
+ printf("Omitting ioctl call (disk %s will "
|
||||||
|
+ "NOT be formatted).\n", g.dev_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -872,7 +873,7 @@
|
||||||
|
check_params->start_unit = 0;
|
||||||
|
check_params->stop_unit = (cylinders * heads) - 1;
|
||||||
|
|
||||||
|
- printf("Checking format of the entire disk...\n");
|
||||||
|
+ printf("Checking format of the entire %s disk...\n", g.dev_path);
|
||||||
|
|
||||||
|
if (g.testmode) {
|
||||||
|
printf("Test mode active, omitting ioctl.\n");
|
||||||
|
@@ -896,7 +897,7 @@
|
||||||
|
if (process_tracks(cylinders, heads, check_params))
|
||||||
|
error("Use --mode=full to perform a clean format.");
|
||||||
|
|
||||||
|
- printf("Done. Disk is fine.\n");
|
||||||
|
+ printf("Done. Disk %s is fine.\n", g.dev_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -946,8 +947,8 @@
|
||||||
|
|
||||||
|
printf("Device Type: %s Provisioned\n",
|
||||||
|
g.ese ? "Thinly" : "Fully");
|
||||||
|
- printf("\nI am going to format the device ");
|
||||||
|
- printf("%s in the following way:\n", g.dev_path);
|
||||||
|
+ printf("\nI am going to format %s ", g.dev_path);
|
||||||
|
+ printf("in the following way:\n");
|
||||||
|
printf(" Device number of device : 0x%x\n", g.dasd_info.devno);
|
||||||
|
printf(" Labelling device : %s\n",
|
||||||
|
(g.writenolabel) ? "no" : "yes");
|
||||||
|
@@ -1012,7 +1013,7 @@
|
||||||
|
int ipl1_record_len, ipl2_record_len;
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Retrieving dasd information... ");
|
||||||
|
+ printf("Retrieving dasd information for %s... ", g.dev_path);
|
||||||
|
|
||||||
|
get_blocksize(&blksize);
|
||||||
|
|
||||||
|
@@ -1030,7 +1031,7 @@
|
||||||
|
|
||||||
|
/* write empty bootstrap (initial IPL records) */
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Writing empty bootstrap...\n");
|
||||||
|
+ printf("Writing empty bootstrap to %s...\n", g.dev_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: ldl labels do not contain the key field
|
||||||
|
@@ -1089,7 +1090,7 @@
|
||||||
|
label_position = g.dasd_info.label_block * blksize;
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Writing label...\n");
|
||||||
|
+ printf("Writing label to %s...\n", g.dev_path);
|
||||||
|
|
||||||
|
rc = lseek(fd, label_position, SEEK_SET);
|
||||||
|
if (rc != label_position) {
|
||||||
|
@@ -1120,7 +1121,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Writing VTOC... ");
|
||||||
|
+ printf("Writing VTOC to %s... ", g.dev_path);
|
||||||
|
|
||||||
|
label_position = (VTOC_START_CC * heads + VTOC_START_HH) *
|
||||||
|
geo.sectors * blksize;
|
||||||
|
@@ -1242,7 +1243,7 @@
|
||||||
|
if (!g.ese || g.no_discard)
|
||||||
|
return;
|
||||||
|
|
||||||
|
- printf("Releasing space for the entire device...\n");
|
||||||
|
+ printf("Releasing space for the entire %s device...\n", g.dev_path);
|
||||||
|
err = dasd_release_space(g.dev_node, &r);
|
||||||
|
if (err)
|
||||||
|
error("Could not release space: %s", strerror(err));
|
||||||
|
@@ -1261,20 +1262,21 @@
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!(g.withoutprompt && g.verbosity < 1))
|
||||||
|
- printf("Formatting the device. This may take a while "
|
||||||
|
- "(get yourself a coffee).\n");
|
||||||
|
+ printf("Formatting the %s device. This may take a while "
|
||||||
|
+ "(get yourself a coffee).\n", g.dev_path);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Detaching the device...\n");
|
||||||
|
+ printf("Detaching the %s device...\n", g.dev_path);
|
||||||
|
|
||||||
|
disk_disable(g.dev_node);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Invalidate first track...\n");
|
||||||
|
+ printf("Invalidate first track on %s...\n", g.dev_path);
|
||||||
|
|
||||||
|
err = dasd_format_disk(filedes, &temp);
|
||||||
|
if (err != 0)
|
||||||
|
- error("(invalidate first track) IOCTL BIODASDFMT failed: %s", strerror(err));
|
||||||
|
+ error("(invalidate first track) IOCTL BIODASDFMT failed for %s: %s",
|
||||||
|
+ g.dev_path, strerror(err));
|
||||||
|
|
||||||
|
/* except track 0 from standard formatting procss */
|
||||||
|
p->start_unit = 1;
|
||||||
|
@@ -1282,19 +1284,19 @@
|
||||||
|
process_tracks(cylinders, heads, p);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("formatting tracks complete...\n");
|
||||||
|
+ printf("formatting tracks for %s complete...\n", g.dev_path);
|
||||||
|
|
||||||
|
temp.intensity = p->intensity;
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Revalidate first track...\n");
|
||||||
|
+ printf("Revalidate first track on %s...\n", g.dev_path);
|
||||||
|
|
||||||
|
err = dasd_format_disk(filedes, &temp);
|
||||||
|
if (err != 0)
|
||||||
|
error("(re-validate first track) IOCTL BIODASDFMT failed: %s", strerror(err));
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Re-accessing the device...\n");
|
||||||
|
+ printf("Re-accessing the %s device...\n", g.dev_path);
|
||||||
|
|
||||||
|
disk_enable();
|
||||||
|
}
|
||||||
|
@@ -1306,18 +1308,18 @@
|
||||||
|
format_data_t *p)
|
||||||
|
{
|
||||||
|
if (!(g.withoutprompt && g.verbosity < 1))
|
||||||
|
- printf("Formatting the device. This may take a while "
|
||||||
|
- "(get yourself a coffee).\n");
|
||||||
|
+ printf("Formatting the %s device. This may take a while "
|
||||||
|
+ "(get yourself a coffee).\n", g.dev_path);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Detaching the device...\n");
|
||||||
|
+ printf("Detaching the %s device...\n", g.dev_path);
|
||||||
|
|
||||||
|
disk_disable(g.dev_node);
|
||||||
|
|
||||||
|
process_tracks(cylinders, heads, p);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
- printf("Formatting tracks complete...\n");
|
||||||
|
+ printf("formatting tracks for %s complete...\n", g.dev_path);
|
||||||
|
|
||||||
|
if (g.verbosity > 0)
|
||||||
|
printf("Re-accessing the device...\n");
|
||||||
|
@@ -1426,16 +1428,16 @@
|
||||||
|
if (!g.withoutprompt) {
|
||||||
|
printf("\n");
|
||||||
|
if (mode != EXPAND)
|
||||||
|
- printf("--->> ATTENTION! <<---\nAll data of "
|
||||||
|
- "that device will be lost.\n");
|
||||||
|
+ printf("--->> ATTENTION! <<---\nAll data on "
|
||||||
|
+ "the %s device will be lost.\n", g.dev_path);
|
||||||
|
printf("Type \"yes\" to continue, no will leave the "
|
||||||
|
"disk untouched: ");
|
||||||
|
if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL)
|
||||||
|
return;
|
||||||
|
if (strcasecmp(inp_buffer, "yes") &&
|
||||||
|
strcasecmp(inp_buffer, "yes\n")) {
|
||||||
|
- printf("Omitting ioctl call (disk will "
|
||||||
|
- "NOT be formatted).\n");
|
||||||
|
+ printf("Omitting ioctl call (disk %s will "
|
||||||
|
+ "NOT be formatted).\n", g.dev_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1453,12 +1455,12 @@
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
- printf("Finished formatting the device.\n");
|
||||||
|
+ printf("Finished formatting the %s device.\n", g.dev_path);
|
||||||
|
|
||||||
|
if (!(g.writenolabel || mode == EXPAND))
|
||||||
|
dasdfmt_write_labels(vlabel, cylinders, heads);
|
||||||
|
|
||||||
|
- printf("Rereading the partition table... ");
|
||||||
|
+ printf("Rereading the partition table for %s... ", g.dev_path);
|
||||||
|
err = dasd_reread_partition_table(g.dev_node, 5);
|
||||||
|
if (err != 0) {
|
||||||
|
ERRMSG("%s: error during rereading the partition "
|
||||||
|
@@ -1472,7 +1474,7 @@
|
||||||
|
static void eval_format_mode(void)
|
||||||
|
{
|
||||||
|
if (!g.force && g.mode_specified && g.ese && mode == EXPAND) {
|
||||||
|
- warnx("WARNING: The specified device is thin-provisioned");
|
||||||
|
+ warnx("WARNING: The specified device, %s, is thin-provisioned", g.dev_path);
|
||||||
|
warnx("Format mode 'expand' is not feasible.");
|
||||||
|
error("Use --mode=full or --mode=quick to perform a clean format");
|
||||||
|
}
|
||||||
|
@@ -1495,20 +1497,70 @@
|
||||||
|
prog_name = p + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int main(int argc, char *argv[])
|
||||||
|
+void process_dasd(volume_label_t *orig_vlabel, format_data_t format_params)
|
||||||
|
{
|
||||||
|
volume_label_t vlabel;
|
||||||
|
char old_volser[7];
|
||||||
|
-
|
||||||
|
char str[ERR_LENGTH];
|
||||||
|
+ unsigned int cylinders, heads; int rc;
|
||||||
|
+
|
||||||
|
+ rc = dasd_get_info(g.dev_node, &g.dasd_info);
|
||||||
|
+ if (rc != 0)
|
||||||
|
+ error("the ioctl call to retrieve device information failed: %s", strerror(rc));
|
||||||
|
+
|
||||||
|
+ g.ese = dasd_sys_ese(g.dev_node);
|
||||||
|
+ eval_format_mode();
|
||||||
|
+
|
||||||
|
+ /* Not sure this next line is needed in the new version of the code. */
|
||||||
|
+ memcpy(&vlabel, orig_vlabel, sizeof(vlabel));
|
||||||
|
+
|
||||||
|
+ /* Either let the user specify the blksize or get it from the kernel */
|
||||||
|
+ if (!g.blksize_specified) {
|
||||||
|
+ if (!(mode == FULL ||
|
||||||
|
+ g.dasd_info.format == DASD_FORMAT_NONE) || g.check)
|
||||||
|
+ get_blocksize(&format_params.blksize);
|
||||||
|
+ else
|
||||||
|
+ format_params = ask_user_for_blksize(format_params);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (g.keep_volser) {
|
||||||
|
+ if (g.labelspec)
|
||||||
|
+ error("The -k and -l options are mutually exclusive");
|
||||||
|
+ if (!(format_params.intensity & DASD_FMT_INT_COMPAT))
|
||||||
|
+ error("WARNING: VOLSER cannot be kept when using the ldl format!");
|
||||||
|
+
|
||||||
|
+ if (dasdfmt_get_volser(old_volser) == 0)
|
||||||
|
+ vtoc_volume_label_set_volser(&vlabel, old_volser);
|
||||||
|
+ else
|
||||||
|
+ error("VOLSER not found on device %s", g.dev_path);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ check_disk();
|
||||||
|
+
|
||||||
|
+ if (check_param(str, ERR_LENGTH, &format_params) < 0)
|
||||||
|
+ error("%s", str);
|
||||||
|
+
|
||||||
|
+ set_geo(&cylinders, &heads);
|
||||||
|
+ set_label(&vlabel, &format_params, cylinders);
|
||||||
|
+
|
||||||
|
+ if (g.check)
|
||||||
|
+ check_disk_format(cylinders, heads, &format_params);
|
||||||
|
+ else
|
||||||
|
+ do_format_dasd(&vlabel, &format_params, cylinders, heads);
|
||||||
|
+
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int main(int argc, char *argv[])
|
||||||
|
+{
|
||||||
|
+ volume_label_t vlabel;
|
||||||
|
+
|
||||||
|
char buf[7];
|
||||||
|
|
||||||
|
char *blksize_param_str = NULL;
|
||||||
|
char *reqsize_param_str = NULL;
|
||||||
|
char *hashstep_str = NULL;
|
||||||
|
|
||||||
|
- int rc;
|
||||||
|
- unsigned int cylinders, heads;
|
||||||
|
+ int rc, numdev = 0, i;
|
||||||
|
|
||||||
|
/* Establish a handler for interrupt signals. */
|
||||||
|
signal(SIGTERM, program_interrupt_signal);
|
||||||
|
@@ -1644,6 +1696,9 @@
|
||||||
|
break; /* exit loop if finished */
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Reset the value of rc since we're going to use it again later. */
|
||||||
|
+ rc = 0;
|
||||||
|
+
|
||||||
|
CHECK_SPEC_MAX_ONCE(g.blksize_specified, "blocksize");
|
||||||
|
CHECK_SPEC_MAX_ONCE(g.labelspec, "label");
|
||||||
|
CHECK_SPEC_MAX_ONCE(g.writenolabel, "omit-label-writing flag");
|
||||||
|
@@ -1662,48 +1717,28 @@
|
||||||
|
if (g.print_hashmarks)
|
||||||
|
PARSE_PARAM_INTO(g.hashstep, hashstep_str, 10, "hashstep");
|
||||||
|
|
||||||
|
- get_device_name(optind, argc, argv);
|
||||||
|
-
|
||||||
|
- rc = dasd_get_info(g.dev_node, &g.dasd_info);
|
||||||
|
- if (rc != 0)
|
||||||
|
- error("the ioctl call to retrieve device information failed: %s", strerror(rc));
|
||||||
|
-
|
||||||
|
- g.ese = dasd_sys_ese(g.dev_node);
|
||||||
|
- eval_format_mode();
|
||||||
|
+ while (optind < argc) {
|
||||||
|
+ get_device_name(optind, argc, argv);
|
||||||
|
+ strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));
|
||||||
|
+ strncpy(g.dev_node_array[numdev], g.dev_node, strlen(g.dev_node));
|
||||||
|
|
||||||
|
- /* Either let the user specify the blksize or get it from the kernel */
|
||||||
|
- if (!g.blksize_specified) {
|
||||||
|
- if (!(mode == FULL ||
|
||||||
|
- g.dasd_info.format == DASD_FORMAT_NONE) || g.check)
|
||||||
|
- get_blocksize(&format_params.blksize);
|
||||||
|
- else
|
||||||
|
- format_params = ask_user_for_blksize(format_params);
|
||||||
|
+ optind++;
|
||||||
|
+ numdev++;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (g.keep_volser) {
|
||||||
|
- if (g.labelspec)
|
||||||
|
- error("The -k and -l options are mutually exclusive");
|
||||||
|
- if (!(format_params.intensity & DASD_FMT_INT_COMPAT))
|
||||||
|
- error("WARNING: VOLSER cannot be kept when using the ldl format!");
|
||||||
|
-
|
||||||
|
- if (dasdfmt_get_volser(old_volser) == 0)
|
||||||
|
- vtoc_volume_label_set_volser(&vlabel, old_volser);
|
||||||
|
- else
|
||||||
|
- error("VOLSER not found on device %s", g.dev_path);
|
||||||
|
- }
|
||||||
|
+ if (!numdev)
|
||||||
|
+ error("%s: No device specified!\n",
|
||||||
|
+ prog_name);
|
||||||
|
|
||||||
|
- check_disk();
|
||||||
|
+ if (numdev > 1 && g.labelspec)
|
||||||
|
+ error("Specifying a volser to be written doesn't make sense when formatting multiple DASD volumes.");
|
||||||
|
|
||||||
|
- if (check_param(str, ERR_LENGTH, &format_params) < 0)
|
||||||
|
- error("%s", str);
|
||||||
|
-
|
||||||
|
- set_geo(&cylinders, &heads);
|
||||||
|
- set_label(&vlabel, &format_params, cylinders);
|
||||||
|
-
|
||||||
|
- if (g.check)
|
||||||
|
- check_disk_format(cylinders, heads, &format_params);
|
||||||
|
- else
|
||||||
|
- do_format_dasd(&vlabel, &format_params, cylinders, heads);
|
||||||
|
+ for (i = 0; i < numdev; i++)
|
||||||
|
+ {
|
||||||
|
+ strncpy(g.dev_path, g.dev_path_array[i], strlen(g.dev_path_array[i])+1);
|
||||||
|
+ strncpy(g.dev_node, g.dev_node_array[i], strlen(g.dev_node_array[i])+1);
|
||||||
|
+ process_dasd(&vlabel, format_params);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
free(g.dev_path);
|
||||||
|
free(g.dev_node);
|
176
s390-tools-sles15sp3-Format-devices-in-parallel.patch
Normal file
176
s390-tools-sles15sp3-Format-devices-in-parallel.patch
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
From a61154fd93122f5a0f2b74f21c3ac29eb437f150 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 6 Oct 2017 09:39:36 +0200
|
||||||
|
Subject: [PATCH] dasdfmt: Format devices in parallel
|
||||||
|
|
||||||
|
Allow dasdfmt to run in parallel when several devices are specified.
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.com>
|
||||||
|
---
|
||||||
|
dasdfmt/dasdfmt.8 | 16 +++++++++++++-
|
||||||
|
dasdfmt/dasdfmt.c | 58 ++++++++++++++++++++++++++++++++++++++++++------------
|
||||||
|
2 files changed, 60 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
--- a/dasdfmt/dasdfmt.8
|
||||||
|
+++ b/dasdfmt/dasdfmt.8
|
||||||
|
@@ -7,7 +7,7 @@
|
||||||
|
dasdfmt \- formatting of DASD (ECKD) disk drives.
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
-\fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-P] [\-m \fIstep\fR]
|
||||||
|
+\fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-Q] [\-P] [\-m \fIstep\fR]
|
||||||
|
.br
|
||||||
|
[\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR]
|
||||||
|
.br
|
||||||
|
@@ -95,7 +95,7 @@
|
||||||
|
running in background or redirecting the output to a file.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
-\fB\-P\fR or \fB\-\-percentage\fR
|
||||||
|
+\fB\-Q\fR or \fB\-\-percentage\fR
|
||||||
|
Print one line for each formatted cylinder showing the number of the
|
||||||
|
cylinder and percentage of formatting process.
|
||||||
|
Intended to be used by higher level interfaces.
|
||||||
|
@@ -164,6 +164,18 @@
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-l\fR \fIvolser\fR or \fB\-\-label\fR=\fIvolser\fR
|
||||||
|
+\fB-P\fR \fInumdisks\fR or \fB--max_parallel\fR=\fInumdisks\fR
|
||||||
|
+Specify the number of disks to be formatted in parallel.
|
||||||
|
+\fInumdisks\fR specifies the number of formatting processed,
|
||||||
|
+independent on the overall number of disks to be formatted.
|
||||||
|
+The maximum value for \fInumdisks\fR is 512. Default is 1.
|
||||||
|
+.br
|
||||||
|
+Using this option can decrease overall processing time when formatting
|
||||||
|
+several disks. Please note that the I/O throughput will dramatically
|
||||||
|
+increase when using this option. Use with care.
|
||||||
|
+.br
|
||||||
|
+
|
||||||
|
+.TP
|
||||||
|
Specify the volume serial number or volume identifier to be written
|
||||||
|
to disk after formatting. If no label is specified, a sensible default
|
||||||
|
is used. \fIvolser\fR is interpreted as ASCII string and is automatically
|
||||||
|
--- a/dasdfmt/dasdfmt.c
|
||||||
|
+++ b/dasdfmt/dasdfmt.c
|
||||||
|
@@ -13,6 +13,7 @@
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
+#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include "lib/dasd_base.h"
|
||||||
|
#include "lib/dasd_sys.h"
|
||||||
|
@@ -81,6 +82,7 @@
|
||||||
|
int mode_specified;
|
||||||
|
int ese;
|
||||||
|
int no_discard;
|
||||||
|
+ int procnum;
|
||||||
|
} g = {
|
||||||
|
.dasd_info = { 0 },
|
||||||
|
};
|
||||||
|
@@ -105,6 +107,11 @@
|
||||||
|
.desc = "Perform complete format check on device",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ .option = { "max_parallel", required_argument, NULL, 'P' },
|
||||||
|
+ .desc = "Format devices in parallel",
|
||||||
|
+ .flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
+ },
|
||||||
|
UTIL_OPT_SECTION("FORMAT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "blocksize", required_argument, NULL, 'b' },
|
||||||
|
@@ -162,7 +169,7 @@
|
||||||
|
.desc = "Show a progressbar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
- .option = { "percentage", no_argument, NULL, 'P' },
|
||||||
|
+ .option = { "percentage", no_argument, NULL, 'Q' },
|
||||||
|
.desc = "Show progress in percent",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("MISC"),
|
||||||
|
@@ -311,7 +318,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g.print_hashmarks && (cyl / g.hashstep - hashcount) != 0) {
|
||||||
|
- printf("#");
|
||||||
|
+ printf("%d|", g.procnum);
|
||||||
|
fflush(stdout);
|
||||||
|
hashcount++;
|
||||||
|
}
|
||||||
|
@@ -1560,7 +1567,11 @@
|
||||||
|
char *reqsize_param_str = NULL;
|
||||||
|
char *hashstep_str = NULL;
|
||||||
|
|
||||||
|
- int rc, numdev = 0, i;
|
||||||
|
+ int rc, numdev = 0, numproc = 0, status;
|
||||||
|
+ int max_parallel =1 ;
|
||||||
|
+ int running = 0;
|
||||||
|
+ int chpid;
|
||||||
|
+ int tmp;
|
||||||
|
|
||||||
|
/* Establish a handler for interrupt signals. */
|
||||||
|
signal(SIGTERM, program_interrupt_signal);
|
||||||
|
@@ -1623,7 +1634,7 @@
|
||||||
|
g.print_hashmarks = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
- case 'P':
|
||||||
|
+ case 'Q':
|
||||||
|
if (!(g.print_hashmarks || g.print_progressbar))
|
||||||
|
g.print_percentage = 1;
|
||||||
|
break;
|
||||||
|
@@ -1682,6 +1693,9 @@
|
||||||
|
case OPT_NODISCARD:
|
||||||
|
g.no_discard = 1;
|
||||||
|
break;
|
||||||
|
+ case 'P':
|
||||||
|
+ max_parallel = atoi(optarg);
|
||||||
|
+ break;
|
||||||
|
case OPT_CHECK:
|
||||||
|
g.check = 1;
|
||||||
|
break;
|
||||||
|
@@ -1733,15 +1747,35 @@
|
||||||
|
if (numdev > 1 && g.labelspec)
|
||||||
|
error("Specifying a volser to be written doesn't make sense when formatting multiple DASD volumes.");
|
||||||
|
|
||||||
|
- for (i = 0; i < numdev; i++)
|
||||||
|
- {
|
||||||
|
- strncpy(g.dev_path, g.dev_path_array[i], strlen(g.dev_path_array[i])+1);
|
||||||
|
- strncpy(g.dev_node, g.dev_node_array[i], strlen(g.dev_node_array[i])+1);
|
||||||
|
- process_dasd(&vlabel, format_params);
|
||||||
|
+ for (numproc = 0; numproc < numdev; numproc++) {
|
||||||
|
+ chpid = fork();
|
||||||
|
+ if (chpid == -1 )
|
||||||
|
+ ERRMSG_EXIT(EXIT_FAILURE,
|
||||||
|
+ "%s: Unable to create child process: %s\n",
|
||||||
|
+ prog_name, strerror(errno));
|
||||||
|
+ if (!chpid) {
|
||||||
|
+ g.procnum = numproc;
|
||||||
|
+ strncpy(g.dev_path, g.dev_path_array[numproc], strlen(g.dev_path_array[numproc])+1);
|
||||||
|
+ strncpy(g.dev_node, g.dev_node_array[numproc], strlen(g.dev_node_array[numproc])+1);
|
||||||
|
+ process_dasd(&vlabel, format_params);
|
||||||
|
+
|
||||||
|
+ free(g.dev_path);
|
||||||
|
+ free(g.dev_node);
|
||||||
|
+ exit(0);
|
||||||
|
+ } else {
|
||||||
|
+ running++;
|
||||||
|
+ if (running >= max_parallel) {
|
||||||
|
+ if (wait(&tmp) > 0 && WEXITSTATUS(tmp))
|
||||||
|
+ rc = WEXITSTATUS(tmp);
|
||||||
|
+ running--;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
- free(g.dev_path);
|
||||||
|
- free(g.dev_node);
|
||||||
|
+ /* wait until all formatting children have finished */
|
||||||
|
+ while(wait(&status) > 0)
|
||||||
|
+ if (WEXITSTATUS(status))
|
||||||
|
+ rc = WEXITSTATUS(status);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
+ return rc;
|
||||||
|
}
|
196
s390-tools-sles15sp3-Implement-Y-yast_mode.patch
Normal file
196
s390-tools-sles15sp3-Implement-Y-yast_mode.patch
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
From eabcb26fa4a91d410a6f75a9915a9ebb9f702c6b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 6 Oct 2017 09:55:40 +0200
|
||||||
|
Subject: [PATCH] dasdfmt: Implement '-Y/--yast_mode'
|
||||||
|
|
||||||
|
Implement an option '-Y' to suppress most output.
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.com>
|
||||||
|
---
|
||||||
|
dasdfmt/dasdfmt.8 | 7 ++++-
|
||||||
|
dasdfmt/dasdfmt.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++------
|
||||||
|
2 files changed, 72 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
--- a/dasdfmt/dasdfmt.8
|
||||||
|
+++ b/dasdfmt/dasdfmt.8
|
||||||
|
@@ -7,7 +7,7 @@
|
||||||
|
dasdfmt \- formatting of DASD (ECKD) disk drives.
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
-\fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-Q] [\-P] [\-m \fIstep\fR]
|
||||||
|
+\fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-Q] [\-P] [\-Y] [\-m \fIstep\fR]
|
||||||
|
.br
|
||||||
|
[\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR]
|
||||||
|
.br
|
||||||
|
@@ -112,6 +112,11 @@
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
+\fB-Y\fR or \fB--yast_mode\fR
|
||||||
|
+YaST mode; suppress most output.
|
||||||
|
+.br
|
||||||
|
+
|
||||||
|
+.TP
|
||||||
|
\fB\-M\fR \fImode\fR or \fB\-\-mode\fR=\fImode\fR
|
||||||
|
Specify the \fImode\fR to be used to format the device. Valid modes are:
|
||||||
|
.RS
|
||||||
|
--- a/dasdfmt/dasdfmt.c
|
||||||
|
+++ b/dasdfmt/dasdfmt.c
|
||||||
|
@@ -83,6 +83,7 @@
|
||||||
|
int ese;
|
||||||
|
int no_discard;
|
||||||
|
int procnum;
|
||||||
|
+ int yast_mode;
|
||||||
|
} g = {
|
||||||
|
.dasd_info = { 0 },
|
||||||
|
};
|
||||||
|
@@ -172,6 +173,10 @@
|
||||||
|
.option = { "percentage", no_argument, NULL, 'Q' },
|
||||||
|
.desc = "Show progress in percent",
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ .option = { "yast_mode", no_argument, NULL, 'Y' },
|
||||||
|
+ .desc = "YaST mode",
|
||||||
|
+ },
|
||||||
|
UTIL_OPT_SECTION("MISC"),
|
||||||
|
{
|
||||||
|
.option = { "check_host_count", no_argument, NULL, 'C' },
|
||||||
|
@@ -318,7 +323,9 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g.print_hashmarks && (cyl / g.hashstep - hashcount) != 0) {
|
||||||
|
- printf("%d|", g.procnum);
|
||||||
|
+ if (g.yast_mode)
|
||||||
|
+ printf("%d|", g.procnum);
|
||||||
|
+ else printf("#");
|
||||||
|
fflush(stdout);
|
||||||
|
hashcount++;
|
||||||
|
}
|
||||||
|
@@ -392,7 +399,7 @@
|
||||||
|
unsigned int kl = 0;
|
||||||
|
int blksize = cdata->expect.blksize;
|
||||||
|
|
||||||
|
- if (g.print_progressbar || g.print_hashmarks)
|
||||||
|
+ if ((g.print_progressbar || g.print_hashmarks) && !g.yast_mode)
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -780,8 +787,9 @@
|
||||||
|
g.hashstep = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
- printf("Printing hashmark every %d cylinders.\n",
|
||||||
|
- g.hashstep);
|
||||||
|
+ if (!g.yast_mode)
|
||||||
|
+ printf("Printing hashmark every %d cylinders.\n",
|
||||||
|
+ g.hashstep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1462,17 +1470,19 @@
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
- printf("Finished formatting the %s device.\n", g.dev_path);
|
||||||
|
+ if (!g.yast_mode)
|
||||||
|
+ printf("Finished formatting the %s device.\n", g.dev_path);
|
||||||
|
|
||||||
|
if (!(g.writenolabel || mode == EXPAND))
|
||||||
|
dasdfmt_write_labels(vlabel, cylinders, heads);
|
||||||
|
|
||||||
|
- printf("Rereading the partition table for %s... ", g.dev_path);
|
||||||
|
+ if (!g.yast_mode)
|
||||||
|
+ printf("Rereading the partition table for %s... ", g.dev_path);
|
||||||
|
err = dasd_reread_partition_table(g.dev_node, 5);
|
||||||
|
if (err != 0) {
|
||||||
|
ERRMSG("%s: error during rereading the partition "
|
||||||
|
"table: %s.\n", prog_name, strerror(err));
|
||||||
|
- } else {
|
||||||
|
+ } else if (!g.yast_mode) {
|
||||||
|
printf("ok\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1548,6 +1558,7 @@
|
||||||
|
error("%s", str);
|
||||||
|
|
||||||
|
set_geo(&cylinders, &heads);
|
||||||
|
+
|
||||||
|
set_label(&vlabel, &format_params, cylinders);
|
||||||
|
|
||||||
|
if (g.check)
|
||||||
|
@@ -1557,6 +1568,29 @@
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void yast_print_cylinfo(const char *dev_filename)
|
||||||
|
+{
|
||||||
|
+ unsigned int cylinders = -1u;
|
||||||
|
+ int fd;
|
||||||
|
+ dasd_information2_t dasd_info;
|
||||||
|
+ struct dasd_eckd_characteristics *characteristics;
|
||||||
|
+
|
||||||
|
+ fd = open(dev_filename, O_RDONLY);
|
||||||
|
+ if ((fd != -1) && ( ! ioctl(fd, BIODASDINFO2, &dasd_info))) {
|
||||||
|
+
|
||||||
|
+ characteristics = (struct dasd_eckd_characteristics *) &dasd_info.characteristics;
|
||||||
|
+ if (characteristics->no_cyl == LV_COMPAT_CYL && characteristics->long_no_cyl)
|
||||||
|
+ cylinders = characteristics->long_no_cyl;
|
||||||
|
+ else
|
||||||
|
+ cylinders = characteristics->no_cyl;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (fd != -1)
|
||||||
|
+ close(fd);
|
||||||
|
+ printf("%u\n", cylinders);
|
||||||
|
+ fflush(stdout);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
volume_label_t vlabel;
|
||||||
|
@@ -1693,6 +1727,10 @@
|
||||||
|
case OPT_NODISCARD:
|
||||||
|
g.no_discard = 1;
|
||||||
|
break;
|
||||||
|
+ case 'Y':
|
||||||
|
+ /* YaST mode */
|
||||||
|
+ g.yast_mode = 1;
|
||||||
|
+ break;
|
||||||
|
case 'P':
|
||||||
|
max_parallel = atoi(optarg);
|
||||||
|
break;
|
||||||
|
@@ -1728,6 +1766,21 @@
|
||||||
|
reqsize = DEFAULT_REQUESTSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/* If -Y (YaST mode) was specified by the caller, then we need to suppress
|
||||||
|
+ * most of all the other output that might be generated. But, we _do_ want
|
||||||
|
+ * hashmarks printed so that YaST can track what's going on. If it wasn't
|
||||||
|
+ * specified on the command line, set it to a default of 10 cylinders.
|
||||||
|
+ */
|
||||||
|
+ if (g.yast_mode) {
|
||||||
|
+ g.verbosity = 0;
|
||||||
|
+ g.print_progressbar = 0;
|
||||||
|
+ g.print_percentage = 0;
|
||||||
|
+ if (! g.print_hashmarks) {
|
||||||
|
+ g.print_hashmarks = 1;
|
||||||
|
+ hashstep_str = "10";
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (g.print_hashmarks)
|
||||||
|
PARSE_PARAM_INTO(g.hashstep, hashstep_str, 10, "hashstep");
|
||||||
|
|
||||||
|
@@ -1747,6 +1800,12 @@
|
||||||
|
if (numdev > 1 && g.labelspec)
|
||||||
|
error("Specifying a volser to be written doesn't make sense when formatting multiple DASD volumes.");
|
||||||
|
|
||||||
|
+ if (g.yast_mode) {
|
||||||
|
+ for (numproc = 0; numproc < numdev; numproc++)
|
||||||
|
+ yast_print_cylinfo(g.dev_path_array[numproc]);
|
||||||
|
+
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
for (numproc = 0; numproc < numdev; numproc++) {
|
||||||
|
chpid = fork();
|
||||||
|
if (chpid == -1 )
|
@ -0,0 +1,62 @@
|
|||||||
|
From 8f05578d90df49dce6e13ee850fdc8bab84916ba Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Fri, 6 Oct 2017 12:23:32 +0200
|
||||||
|
Subject: [PATCH] dasdfmt: Implement '-f' for backwards compability
|
||||||
|
|
||||||
|
YaST is calling dasdfmt with '-f device', which used to be the old
|
||||||
|
calling convention. So to not keel over when used with an older
|
||||||
|
version of YaST we should accept this option, too.
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.com>
|
||||||
|
---
|
||||||
|
dasdfmt/dasdfmt.8 | 5 ++++-
|
||||||
|
dasdfmt/dasdfmt.c | 10 ++++++++++
|
||||||
|
2 files changed, 14 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/dasdfmt/dasdfmt.8
|
||||||
|
+++ b/dasdfmt/dasdfmt.8
|
||||||
|
@@ -11,7 +11,7 @@
|
||||||
|
.br
|
||||||
|
[\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR]
|
||||||
|
.br
|
||||||
|
- [\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] \fIdevice\fR [\fIdevice\fR]
|
||||||
|
+ [\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] [-f \fIdevice\fR] [\fIdevice\fR]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\fBdasdfmt\fR formats one or several DASD (ECKD) disk drive(s) to prepare them
|
||||||
|
@@ -39,6 +39,9 @@
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR
|
||||||
|
Increases verbosity.
|
||||||
|
+.TP
|
||||||
|
+\fB-f\fR \fIdevice\fR or \fB--device\fR=\fIdevice\fR
|
||||||
|
+Specify device to format. For backwards compability only.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-y\fR
|
||||||
|
--- a/dasdfmt/dasdfmt.c
|
||||||
|
+++ b/dasdfmt/dasdfmt.c
|
||||||
|
@@ -113,6 +113,10 @@
|
||||||
|
.desc = "Format devices in parallel",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ .option = { "device", required_argument, NULL, 'f' },
|
||||||
|
+ .desc = "Specify device to format",
|
||||||
|
+ },
|
||||||
|
UTIL_OPT_SECTION("FORMAT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "blocksize", required_argument, NULL, 'b' },
|
||||||
|
@@ -1649,6 +1653,12 @@
|
||||||
|
}
|
||||||
|
g.layout_specified = 1;
|
||||||
|
break;
|
||||||
|
+ case 'f':
|
||||||
|
+ get_device_name(optind-1, argc, argv);
|
||||||
|
+ strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));
|
||||||
|
+ strncpy(g.dev_node_array[numdev], g.dev_node, strlen(g.dev_node));
|
||||||
|
+ numdev++;
|
||||||
|
+ break;
|
||||||
|
case 'y':
|
||||||
|
g.withoutprompt = 1;
|
||||||
|
break;
|
@ -0,0 +1,56 @@
|
|||||||
|
From 943e577440d74ad7f8787af2590c8ab4579a459b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Hannes Reinecke <hare@suse.de>
|
||||||
|
Date: Thu, 5 Nov 2015 10:57:38 +0100
|
||||||
|
Subject: [PATCH] dasdfmt: retry BIODASDINFO if device is busy
|
||||||
|
|
||||||
|
Modern udev have the wonderful 'feature' to sending a 'change'
|
||||||
|
event whenever a device opened with O_RDWR is closed again.
|
||||||
|
The reasoning is that the said program _might_ have changed
|
||||||
|
the partition table and hence we _might_ have missed a partition
|
||||||
|
update.
|
||||||
|
But in doing so it not only generated tons of pointless events
|
||||||
|
but also confused the hell out of other programs.
|
||||||
|
Idiots.
|
||||||
|
|
||||||
|
References: bsc#937340
|
||||||
|
|
||||||
|
Signed-off-by: Hannes Reinecke <hare@suse.de>
|
||||||
|
---
|
||||||
|
dasdfmt/dasdfmt.c | 19 ++++++++++++++++++-
|
||||||
|
1 file changed, 18 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/dasdfmt/dasdfmt.c
|
||||||
|
+++ b/dasdfmt/dasdfmt.c
|
||||||
|
@@ -621,7 +621,7 @@
|
||||||
|
*/
|
||||||
|
static void check_disk(void)
|
||||||
|
{
|
||||||
|
- int err;
|
||||||
|
+ int err, index = 0 ;
|
||||||
|
bool ro;
|
||||||
|
|
||||||
|
err = dasd_is_ro(g.dev_node, &ro);
|
||||||
|
@@ -631,6 +631,23 @@
|
||||||
|
if (ro)
|
||||||
|
error("Disk %s is read only!", g.dev_path);
|
||||||
|
if (!g.force) {
|
||||||
|
+ /*
|
||||||
|
+ * udev strikes again.
|
||||||
|
+ * Modern udev will issue a 'change' event whenever
|
||||||
|
+ * a device opened with O_RDWR is closed again.
|
||||||
|
+ * On the grounds that program _might_ have changed
|
||||||
|
+ * the partition table.
|
||||||
|
+ * And confusing the hell out ouf anyone else.
|
||||||
|
+ * Bah.
|
||||||
|
+ */
|
||||||
|
+ for ( index = 0 ; index < 6 ; index++ ) {
|
||||||
|
+ if (g.dasd_info.open_count > 1) {
|
||||||
|
+ dasd_get_info(g.dev_node, &g.dasd_info);
|
||||||
|
+ sleep(1);
|
||||||
|
+ }
|
||||||
|
+ else break;
|
||||||
|
+
|
||||||
|
+ }
|
||||||
|
if (g.dasd_info.open_count > 1)
|
||||||
|
error("Disk %s is in use!", g.dev_path);
|
||||||
|
}
|
@ -0,0 +1,286 @@
|
|||||||
|
Index: s390-tools-service/rust/pv/src/verify.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-service.orig/rust/pv/src/verify.rs
|
||||||
|
+++ s390-tools-service/rust/pv/src/verify.rs
|
||||||
|
@@ -3,10 +3,11 @@
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
use core::slice;
|
||||||
|
-use log::debug;
|
||||||
|
+use log::{debug, trace};
|
||||||
|
+use openssl::error::ErrorStack;
|
||||||
|
use openssl::stack::Stack;
|
||||||
|
use openssl::x509::store::X509Store;
|
||||||
|
-use openssl::x509::{CrlStatus, X509Ref, X509StoreContext, X509};
|
||||||
|
+use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509};
|
||||||
|
use openssl_extensions::crl::StackableX509Crl;
|
||||||
|
use openssl_extensions::crl::X509StoreContextExtension;
|
||||||
|
|
||||||
|
@@ -82,8 +83,8 @@ impl HkdVerifier for CertVerifier {
|
||||||
|
if verified_crls.is_empty() {
|
||||||
|
bail_hkd_verify!(NoCrl);
|
||||||
|
}
|
||||||
|
- for crl in &verified_crls {
|
||||||
|
- match crl.get_by_cert(&hkd.to_owned()) {
|
||||||
|
+ for crl in verified_crls {
|
||||||
|
+ match crl.get_by_serial(hkd.serial_number()) {
|
||||||
|
CrlStatus::NotRevoked => (),
|
||||||
|
_ => bail_hkd_verify!(HdkRevoked),
|
||||||
|
}
|
||||||
|
@@ -94,21 +95,54 @@ impl HkdVerifier for CertVerifier {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CertVerifier {
|
||||||
|
+ fn quirk_crls(
|
||||||
|
+ ctx: &mut X509StoreContextRef,
|
||||||
|
+ subject: &X509NameRef,
|
||||||
|
+ ) -> Result<Stack<StackableX509Crl>, ErrorStack> {
|
||||||
|
+ match ctx.crls(subject) {
|
||||||
|
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||||
|
+ _ => (),
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Armonk/Poughkeepsie fixup
|
||||||
|
+ trace!("quirk_crls: Try Locality");
|
||||||
|
+ if let Some(locality_subject) = helper::armonk_locality_fixup(subject) {
|
||||||
|
+ match ctx.crls(&locality_subject) {
|
||||||
|
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||||
|
+ _ => (),
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // reorder
|
||||||
|
+ trace!("quirk_crls: Try Locality+Reorder");
|
||||||
|
+ if let Ok(locality_ordered_subject) = helper::reorder_x509_names(&locality_subject) {
|
||||||
|
+ match ctx.crls(&locality_ordered_subject) {
|
||||||
|
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||||
|
+ _ => (),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // reorder unchanged loaciliy subject
|
||||||
|
+ trace!("quirk_crls: Try Reorder");
|
||||||
|
+ if let Ok(ordered_subject) = helper::reorder_x509_names(subject) {
|
||||||
|
+ match ctx.crls(&ordered_subject) {
|
||||||
|
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||||
|
+ _ => (),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ // nothing found, return empty stack
|
||||||
|
+ Stack::new()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
///Download the CLRs that a HKD refers to.
|
||||||
|
pub fn hkd_crls(&self, hkd: &X509Ref) -> Result<Stack<StackableX509Crl>> {
|
||||||
|
let mut ctx = X509StoreContext::new()?;
|
||||||
|
// Unfortunately we cannot use a dedicated function here and have to use a closure (E0434)
|
||||||
|
// Otherwise, we cannot refer to self
|
||||||
|
+ // Search for local CRLs
|
||||||
|
let mut crls = ctx.init_opt(&self.store, None, None, |ctx| {
|
||||||
|
let subject = self.ibm_z_sign_key.subject_name();
|
||||||
|
- match ctx.crls(subject) {
|
||||||
|
- Ok(crls) => Ok(crls),
|
||||||
|
- _ => {
|
||||||
|
- // reorder the name and try again
|
||||||
|
- let broken_subj = helper::reorder_x509_names(subject)?;
|
||||||
|
- ctx.crls(&broken_subj).or_else(helper::stack_err_hlp)
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ Self::quirk_crls(ctx, subject)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !self.offline {
|
||||||
|
Index: s390-tools-service/rust/pv/src/verify/helper.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-service.orig/rust/pv/src/verify/helper.rs
|
||||||
|
+++ s390-tools-service/rust/pv/src/verify/helper.rs
|
||||||
|
@@ -13,7 +13,7 @@ use openssl::{
|
||||||
|
error::ErrorStack,
|
||||||
|
nid::Nid,
|
||||||
|
ssl::SslFiletype,
|
||||||
|
- stack::{Stack, Stackable},
|
||||||
|
+ stack::Stack,
|
||||||
|
x509::{
|
||||||
|
store::{File, X509Lookup, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef},
|
||||||
|
verify::{X509VerifyFlags, X509VerifyParam},
|
||||||
|
@@ -25,6 +25,7 @@ use openssl_extensions::{
|
||||||
|
akid::{AkidCheckResult, AkidExtension},
|
||||||
|
crl::X509StoreExtension,
|
||||||
|
};
|
||||||
|
+use std::str::from_utf8;
|
||||||
|
use std::{cmp::Ordering, ffi::c_int, time::Duration, usize};
|
||||||
|
|
||||||
|
/// Minimum security level for the keys/certificates used to establish a chain of
|
||||||
|
@@ -39,7 +40,6 @@ const SECURITY_CHAIN_MAX_LEN: c_int = 2;
|
||||||
|
/// verifies that the HKD
|
||||||
|
/// * has enough security bits
|
||||||
|
/// * is inside its validity period
|
||||||
|
-/// * issuer name is the subject name of the [`sign_key`]
|
||||||
|
/// * the Authority Key ID matches the Signing Key ID of the [`sign_key`]
|
||||||
|
pub fn verify_hkd_options(hkd: &X509Ref, sign_key: &X509Ref) -> Result<()> {
|
||||||
|
let hk_pkey = hkd.public_key()?;
|
||||||
|
@@ -53,9 +53,6 @@ pub fn verify_hkd_options(hkd: &X509Ref,
|
||||||
|
// verify that the hkd is still valid
|
||||||
|
check_validity_period(hkd.not_before(), hkd.not_after())?;
|
||||||
|
|
||||||
|
- // check if hkd.issuer_name == issuer.subject
|
||||||
|
- check_x509_name_equal(sign_key.subject_name(), hkd.issuer_name())?;
|
||||||
|
-
|
||||||
|
// verify that the AKID of the hkd matches the SKID of the issuer
|
||||||
|
if let Some(akid) = hkd.akid() {
|
||||||
|
if akid.check(sign_key) != AkidCheckResult::OK {
|
||||||
|
@@ -75,9 +72,6 @@ pub fn verify_crl(crl: &X509CrlRef, issu
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- check_x509_name_equal(crl.issuer_name(), issuer.subject_name()).ok()?;
|
||||||
|
-
|
||||||
|
match crl.verify(issuer.public_key().ok()?.as_ref()).ok()? {
|
||||||
|
true => Some(()),
|
||||||
|
false => None,
|
||||||
|
@@ -207,7 +201,8 @@ pub fn download_crls_into_store(store: &
|
||||||
|
//Asn1StringRef::as_slice aka ASN1_STRING_get0_data gives a string without \0 delimiter
|
||||||
|
const IBM_Z_COMMON_NAME: &[u8; 43usize] = b"International Business Machines Corporation";
|
||||||
|
const IBM_Z_COUNTRY_NAME: &[u8; 2usize] = b"US";
|
||||||
|
-const IBM_Z_LOCALITY_NAME: &[u8; 12usize] = b"Poughkeepsie";
|
||||||
|
+const IBM_Z_LOCALITY_NAME_POUGHKEEPSIE: &[u8; 12usize] = b"Poughkeepsie";
|
||||||
|
+const IBM_Z_LOCALITY_NAME_ARMONK: &[u8; 6usize] = b"Armonk";
|
||||||
|
const IBM_Z_ORGANIZATIONAL_UNIT_NAME_SUFFIX: &str = "Key Signing Service";
|
||||||
|
const IBM_Z_ORGANIZATION_NAME: &[u8; 43usize] = b"International Business Machines Corporation";
|
||||||
|
const IBM_Z_STATE: &[u8; 8usize] = b"New York";
|
||||||
|
@@ -226,7 +221,8 @@ fn is_ibm_signing_cert(cert: &X509) -> b
|
||||||
|
if subj.entries().count() != IMB_Z_ENTRY_COUNT
|
||||||
|
|| !name_data_eq(subj, Nid::COUNTRYNAME, IBM_Z_COUNTRY_NAME)
|
||||||
|
|| !name_data_eq(subj, Nid::STATEORPROVINCENAME, IBM_Z_STATE)
|
||||||
|
- || !name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME)
|
||||||
|
+ || !(name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_POUGHKEEPSIE)
|
||||||
|
+ || name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK))
|
||||||
|
|| !name_data_eq(subj, Nid::ORGANIZATIONNAME, IBM_Z_ORGANIZATION_NAME)
|
||||||
|
|| !name_data_eq(subj, Nid::COMMONNAME, IBM_Z_COMMON_NAME)
|
||||||
|
{
|
||||||
|
@@ -367,24 +363,6 @@ fn check_validity_period(not_before: &As
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-fn check_x509_name_equal(lhs: &X509NameRef, rhs: &X509NameRef) -> Result<()> {
|
||||||
|
- if lhs.entries().count() != rhs.entries().count() {
|
||||||
|
- bail_hkd_verify!(IssuerMismatch);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- for l in lhs.entries() {
|
||||||
|
- // search for the matching value in the rhs names
|
||||||
|
- // found none? -> names are not equal
|
||||||
|
- if !rhs
|
||||||
|
- .entries()
|
||||||
|
- .any(|r| l.data().as_slice() == r.data().as_slice())
|
||||||
|
- {
|
||||||
|
- bail_hkd_verify!(IssuerMismatch);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- Ok(())
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
const NIDS_CORRECT_ORDER: [Nid; 6] = [
|
||||||
|
Nid::COUNTRYNAME,
|
||||||
|
Nid::ORGANIZATIONNAME,
|
||||||
|
@@ -407,13 +385,28 @@ pub fn reorder_x509_names(subject: &X509
|
||||||
|
Ok(correct_subj.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
-pub fn stack_err_hlp<T: Stackable>(
|
||||||
|
- e: ErrorStack,
|
||||||
|
-) -> std::result::Result<Stack<T>, openssl::error::ErrorStack> {
|
||||||
|
- match e.errors().len() {
|
||||||
|
- 0 => Stack::<T>::new(),
|
||||||
|
- _ => Err(e),
|
||||||
|
+/**
|
||||||
|
+* Workaround for potential locality mismatches between CRLs and Certs
|
||||||
|
+* # Return
|
||||||
|
+* fixed subject or none if locality was not Armonk or any OpenSSL error
|
||||||
|
+*/
|
||||||
|
+pub fn armonk_locality_fixup(subject: &X509NameRef) -> Option<X509Name> {
|
||||||
|
+ if !name_data_eq(subject, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK) {
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let mut ret = X509Name::builder().ok()?;
|
||||||
|
+ for entry in subject.entries() {
|
||||||
|
+ match entry.object().nid() {
|
||||||
|
+ nid @ Nid::LOCALITYNAME => ret
|
||||||
|
+ .append_entry_by_nid(nid, from_utf8(IBM_Z_LOCALITY_NAME_POUGHKEEPSIE).ok()?)
|
||||||
|
+ .ok()?,
|
||||||
|
+ _ => {
|
||||||
|
+ ret.append_entry(entry).ok()?;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+ Some(ret.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
@@ -451,20 +444,6 @@ mod test {
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
- #[test]
|
||||||
|
- fn x509_name_equal() {
|
||||||
|
- let sign_crt = load_gen_cert("ibm.crt");
|
||||||
|
- let hkd = load_gen_cert("host.crt");
|
||||||
|
- let other = load_gen_cert("inter_ca.crt");
|
||||||
|
-
|
||||||
|
- assert!(super::check_x509_name_equal(sign_crt.subject_name(), hkd.issuer_name()).is_ok(),);
|
||||||
|
-
|
||||||
|
- assert!(matches!(
|
||||||
|
- super::check_x509_name_equal(other.subject_name(), hkd.subject_name()),
|
||||||
|
- Err(Error::HkdVerify(IssuerMismatch))
|
||||||
|
- ));
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
#[test]
|
||||||
|
fn is_ibm_z_sign_key() {
|
||||||
|
let ibm_crt = load_gen_cert("ibm.crt");
|
||||||
|
Index: s390-tools-service/rust/pv/src/verify/test.rs
|
||||||
|
===================================================================
|
||||||
|
--- s390-tools-service.orig/rust/pv/src/verify/test.rs
|
||||||
|
+++ s390-tools-service/rust/pv/src/verify/test.rs
|
||||||
|
@@ -84,7 +84,6 @@ fn verify_online() {
|
||||||
|
let inter_crt = get_cert_asset_path_string("inter_ca.crt");
|
||||||
|
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||||
|
let hkd_revoked = load_gen_cert("host_rev.crt");
|
||||||
|
- let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
|
||||||
|
let hkd_exp = load_gen_cert("host_crt_expired.crt");
|
||||||
|
let hkd = load_gen_cert("host.crt");
|
||||||
|
|
||||||
|
@@ -112,11 +111,6 @@ fn verify_online() {
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
- verifier.verify(&hkd_inv),
|
||||||
|
- Err(Error::HkdVerify(IssuerMismatch))
|
||||||
|
- ));
|
||||||
|
-
|
||||||
|
- assert!(matches!(
|
||||||
|
verifier.verify(&hkd_exp),
|
||||||
|
Err(Error::HkdVerify(AfterValidity))
|
||||||
|
));
|
||||||
|
@@ -130,7 +124,6 @@ fn verify_offline() {
|
||||||
|
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||||
|
let ibm_crl = get_cert_asset_path_string("ibm.crl");
|
||||||
|
let hkd_revoked = load_gen_cert("host_rev.crt");
|
||||||
|
- let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
|
||||||
|
let hkd_exp = load_gen_cert("host_crt_expired.crt");
|
||||||
|
let hkd = load_gen_cert("host.crt");
|
||||||
|
|
||||||
|
@@ -149,11 +142,6 @@ fn verify_offline() {
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
- verifier.verify(&hkd_inv),
|
||||||
|
- Err(Error::HkdVerify(IssuerMismatch))
|
||||||
|
- ));
|
||||||
|
-
|
||||||
|
- assert!(matches!(
|
||||||
|
verifier.verify(&hkd_exp),
|
||||||
|
Err(Error::HkdVerify(AfterValidity))
|
||||||
|
));
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user