commit 7533564948717809808443a35d8e50ea9f9ab325 Author: CentOS Sources Date: Tue Sep 8 04:38:26 2020 -0400 import s390utils-2.6.0-28.el8_2.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9776127 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +SOURCES/cmsfs-1.1.8c.tar.gz +SOURCES/s390-tools-2.6.0.tar.gz +SOURCES/src_vipa-2.1.0.tar.gz diff --git a/.s390utils.metadata b/.s390utils.metadata new file mode 100644 index 0000000..cba2a76 --- /dev/null +++ b/.s390utils.metadata @@ -0,0 +1,3 @@ +9c9a4e89bddb2b4e6e09ef6fc7c2e6f2ad6316de SOURCES/cmsfs-1.1.8c.tar.gz +46a09493030c3c80987b7710e34a33462e4b90f4 SOURCES/s390-tools-2.6.0.tar.gz +8ed8592a0a9370ce8422df9231ccb17f6cf49bed SOURCES/src_vipa-2.1.0.tar.gz diff --git a/SOURCES/00-zipl-prepare.install b/SOURCES/00-zipl-prepare.install new file mode 100755 index 0000000..315b2f9 --- /dev/null +++ b/SOURCES/00-zipl-prepare.install @@ -0,0 +1,9 @@ +#!/bin/bash + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +# Remove it, since for zipl the images are always installed in /boot +rm -rf "${BOOT_DIR_ABS%/*}" diff --git a/SOURCES/20-zipl-kernel.install b/SOURCES/20-zipl-kernel.install new file mode 100755 index 0000000..99bfcb9 --- /dev/null +++ b/SOURCES/20-zipl-kernel.install @@ -0,0 +1,179 @@ +#!/bin/bash + +if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then + exit 0 +fi + +[[ -f /etc/sysconfig/kernel ]] && . /etc/sysconfig/kernel + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +KERNEL_DIR="${KERNEL_IMAGE%/*}" + +MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID + +BLS_DIR="/boot/loader/entries" +ZIPLCFG="/etc/zipl.conf" +CMDLINE_LINUX_DEBUG=" systemd.log_level=debug systemd.log_target=kmsg" +LINUX_DEBUG_VERSION_POSTFIX="_with_debugging" +LINUX_DEBUG_TITLE_POSTFIX=" with debugging" + +mkbls() { + local kernelver=$1 && shift + local datetime=$1 && shift + local kernelopts=$1 && shift + + local debugname="" + local flavor="" + + if [[ "$kernelver" == *\+* ]] ; then + local flavor=-"${kernelver##*+}" + if [[ "${flavor}" == "-debug" ]]; then + local debugname=" with debugging" + local debugid="-debug" + fi + fi + + cat </dev/null && \ + restorecon -R "/boot/${i##*/}-${KERNEL_VERSION}" + done + # hmac is .vmlinuz-.hmac so needs a special treatment + i="$KERNEL_DIR/.${KERNEL_IMAGE##*/}.hmac" + if [[ -e "$i" ]]; then + cp -a "$i" "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + command -v restorecon &>/dev/null && \ + restorecon "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + fi + fi + + if [[ ! -f /sbin/new-kernel-pkg || -d "${BLS_DIR}" ]]; then + declare -a BOOT_OPTIONS + if [[ -f /etc/kernel/cmdline ]]; then + read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline + fi + + if ! [[ ${BOOT_OPTIONS[*]} ]]; then + read -r -d '' -a line < /proc/cmdline + for i in "${line[@]}"; do + [[ "${i#initrd=*}" != "$i" || "${i#BOOT_IMAGE=*}" != "$i" ]] && continue + BOOT_OPTIONS+=("$i") + done + fi + + if ! [[ ${BOOT_OPTIONS[*]} ]]; then + echo "Could not determine the kernel command line parameters." >&2 + echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2 + exit 1 + fi + + [[ -d "$BLS_DIR" ]] || mkdir -m 0700 -p "$BLS_DIR" + BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf" + if [[ -f "${KERNEL_DIR}/bls.conf" ]]; then + cp -aT "${KERNEL_DIR}/bls.conf" "${BLS_TARGET}" || exit $? + sed -i -e "s,^linux.*,linux /boot/vmlinuz-${KERNEL_VERSION},g" "${BLS_TARGET}" + sed -i -e "s,^initrd.*,initrd /boot/initramfs-${KERNEL_VERSION}.img,g" "${BLS_TARGET}" + sed -i -e "s#^options.*#options ${BOOT_OPTIONS[*]}#g" "${BLS_TARGET}" + else + mkbls "${KERNEL_VERSION}" \ + "$(date -u +%Y%m%d%H%M%S -d "$(stat -c '%y' "${KERNEL_DIR}")")" \ + "${BOOT_OPTIONS[*]}" >"${BLS_TARGET}" + fi + + if [[ "$KERNEL_VERSION" == *\+* ]] && [ "x$DEFAULTDEBUG" != "xyes" ]; then + UPDATEDEFAULT="no" + fi + + if [[ "x$UPDATEDEFAULT" = "xyes" ]]; then + TITLE="$(grep '^title[ \t]' "${BLS_TARGET}" | sed -e 's/^title[ \t]*//')" + NEWDEFAULT="${TITLE}" + fi + + if [ "x${MAKEDEBUG}" = "xyes" ]; then + BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + cp -aT "${BLS_TARGET}" "${BLS_DEBUG}" + TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')" + VERSION="$(grep '^version[ \t]' "${BLS_DEBUG}" | sed -e 's/^version[ \t]*//')" + BLSID="$(grep '^id[ \t]' "${BLS_DEBUG}" | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + sed -i -e "s/^title.*/title ${TITLE}${LINUX_DEBUG_TITLE_POSTFIX}/" "${BLS_DEBUG}" + sed -i -e "s/^version.*/version ${VERSION}${LINUX_DEBUG_VERSION_POSTFIX}/" "${BLS_DEBUG}" + sed -i -e "s/^id.*/${BLSID}/" "${BLS_DEBUG}" + sed -i -e "s#^options.*#options ${BOOT_OPTIONS[*]}${CMDLINE_LINUX_DEBUG}#" "${BLS_DEBUG}" + if [ -n "$NEWDEFAULT" -a "x$DEFAULTDEBUG" = "xyes" ]; then + TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')" + NEWDEFAULT="${TITLE}" + fi + fi + + if [ -n "$NEWDEFAULT" ]; then + sed -i -e "s,^default=.*,default=${NEWDEFAULT}," "${ZIPLCFG}" + fi + + exit 0 + fi + + /sbin/new-kernel-pkg --package "kernel${flavor}" --install "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --mkinitrd --dracut --depmod --update "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --rpmposttrans "$KERNEL_VERSION" || exit $? + # If grubby is used there's no need to run other installation plugins + exit 77 + ;; + remove) + if [[ ! -f /sbin/new-kernel-pkg || -d "${BLS_DIR}" ]]; then + ARCH="$(uname -m)" + BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf" + BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + + TITLE="$(grep '^title[ \t]' "${BLS_TARGET}" | sed -e 's/^title[ \t]*//')" + sed -i -e "/^default=${TITLE}/d" "${ZIPLCFG}" + + if [[ -f "${BLS_DEBUG}" ]]; then + TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')" + sed -i -e "/^default=${TITLE}/d" "${ZIPLCFG}" + fi + + rm -f "${BLS_TARGET}" "${BLS_DEBUG}" + + for i in vmlinuz System.map config zImage.stub dtb; do + rm -rf "/boot/${i}-${KERNEL_VERSION}" + done + # hmac is .vmlinuz-.hmac so needs a special treatment + rm -f "/boot/.vmlinuz-${KERNEL_VERSION}.hmac" + + exit 0 + fi + + /sbin/new-kernel-pkg --package "kernel${flavor+-$flavor}" --rminitrd --rmmoddep --remove "$KERNEL_VERSION" || exit $? + # If grubby is used there's no need to run other installation plugins + exit 77 + ;; + *) + ;; +esac diff --git a/SOURCES/52-zipl-rescue.install b/SOURCES/52-zipl-rescue.install new file mode 100755 index 0000000..dbf0c1b --- /dev/null +++ b/SOURCES/52-zipl-rescue.install @@ -0,0 +1,48 @@ +#!/bin/bash + +[[ -f /etc/os-release ]] && . /etc/os-release +[[ -f /etc/sysconfig/kernel ]] && . /etc/sysconfig/kernel + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID + +BLS_DIR="/boot/loader/entries" + +[[ "$KERNEL_VERSION" == *\+* ]] && flavor=-"${KERNEL_VERSION##*+}" +case "$COMMAND" in + add) + if [[ ! -f /sbin/new-kernel-pkg || -d "${BLS_DIR}" ]]; then + declare -a BOOT_OPTIONS + if [[ -f /etc/kernel/cmdline ]]; then + read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline + fi + + if ! [[ ${BOOT_OPTIONS[*]} ]]; then + read -r -d '' -a line < /proc/cmdline + for i in "${line[@]}"; do + [[ "${i#initrd=*}" != "$i" ]] && continue + BOOT_OPTIONS+=("$i") + done + fi + + if ! [[ ${BOOT_OPTIONS[*]} ]]; then + echo "Could not determine the kernel command line parameters." >&2 + echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2 + exit 1 + fi + + BLS_RESCUE="${BLS_DIR}/${MACHINE_ID}-0-rescue.conf" + if [[ -f "${BLS_RESCUE}" ]] && grep -q '^options.*$kernelopts' "${BLS_RESCUE}"; then + sed -i -e "s,^linux.*,linux /boot/vmlinuz-0-rescue-${MACHINE_ID},g" "${BLS_RESCUE}" + sed -i -e "s,^initrd.*,initrd /boot/initramfs-0-rescue-${MACHINE_ID}.img,g" "${BLS_RESCUE}" + sed -i -e "s#^options.*#options ${BOOT_OPTIONS[*]}#g" "${BLS_RESCUE}" + fi + fi + ;; + *) + ;; +esac diff --git a/SOURCES/91-zipl.install b/SOURCES/91-zipl.install new file mode 100755 index 0000000..6205638 --- /dev/null +++ b/SOURCES/91-zipl.install @@ -0,0 +1,15 @@ +#!/bin/bash + +if [[ ! -f /etc/zipl.conf ]]; then + exit 0 +fi + +COMMAND="$1" + +case "$COMMAND" in + add|remove) + zipl > /dev/null + ;; + *) + ;; +esac diff --git a/SOURCES/ccw.udev b/SOURCES/ccw.udev new file mode 100644 index 0000000..a12ad05 --- /dev/null +++ b/SOURCES/ccw.udev @@ -0,0 +1,13 @@ +ACTION!="add|change", GOTO="ccw_end" +SUBSYSTEM!="ccw", GOTO="ccw_end" +ATTRS{cutype}=="1731/01", RUN+="ccw_init" +ATTRS{cutype}=="1731/02", RUN+="ccw_init" +ATTRS{cutype}=="1731/05", RUN+="ccw_init" +ATTRS{cutype}=="1731/06", RUN+="ccw_init" +ATTRS{cutype}=="3088/01", RUN+="ccw_init" +ATTRS{cutype}=="3088/08", RUN+="ccw_init" +ATTRS{cutype}=="3088/60", RUN+="ccw_init" +ATTRS{cutype}=="3088/61", RUN+="ccw_init" +ATTRS{cutype}=="3088/1e", RUN+="ccw_init" +ATTRS{cutype}=="3088/1f", RUN+="ccw_init" +LABEL="ccw_end" diff --git a/SOURCES/ccw_init b/SOURCES/ccw_init new file mode 100644 index 0000000..23e8ed9 --- /dev/null +++ b/SOURCES/ccw_init @@ -0,0 +1,185 @@ +#! /bin/sh + +[ -z "$DEVPATH" ] && exit 0 +[ "$SUBSYSTEM" != "ccw" ] && exit 0 + +[ -e /etc/ccw.conf ] && MODE="dracut" || MODE="normal" +OLD_IFS="$IFS" + +get_config_line_by_subchannel() +{ + local CHANNEL + CHANNEL="$1" + while read line; do + IFS="," + set $line + IFS="$OLD_IFS" + for i in $@; do + if [ "$CHANNEL" = "$i" ]; then + echo $line + return 0 + fi + done + done < /etc/ccw.conf + return 1 +} + +# borrowed from network-scrips, initscripts along with the get_config_by_subchannel +[ -z "$__sed_discard_ignored_files" ] && __sed_discard_ignored_files='/\(~\|\.bak\|\.old\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' + +get_config_by_subchannel () +{ + LANG=C grep -E -i -l \ + "^[[:space:]]*SUBCHANNELS=['\"]?([0-9]\.[0-9]\.[a-f0-9]+,){0,2}${1}(,[0-9]\.[0-9]\.[a-f0-9]+){0,2}['\"]?([[:space:]]+#|[[:space:]]*$)" \ + /etc/sysconfig/network-scripts/ifcfg-* \ + | LC_ALL=C sed -e "$__sed_discard_ignored_files" +} + +CHANNEL=${DEVPATH##*/} + +if [ $MODE = "dracut" ]; then + CONFIG_LINE=$(get_config_line_by_subchannel $CHANNEL) + + [ $? -ne 0 -o -z "$CONFIG_LINE" ] && break + + IFS="," + set $CONFIG_LINE + IFS="$OLD_IFS" + NETTYPE=$1 + shift + SUBCHANNELS="$1" + OPTIONS="" + shift + while [ $# -gt 0 ]; do + case $1 in + *=*) OPTIONS="$OPTIONS $1";; + [0-9]*) SUBCHANNELS="$SUBCHANNELS,$1";; + esac + shift + done +elif [ $MODE = "normal" ]; then + NOLOCALE="yes" + + CONFIG_FILE=$(get_config_by_subchannel $CHANNEL) + + if [ -n "$CONFIG_FILE" ]; then + . $CONFIG_FILE + else + exit 1 + fi +else + echo "Unknown mode=$MODE" + exit 1 +fi + + +# now we have extracted these variables from the config files: +# SUBCHANNELS +# OPTIONS + +# put LAYER2 option into its own variable +set $OPTIONS +OPTIONS="" +while [ $# -gt 0 ]; do + case $1 in + layer2=*) LAYER2=${1##layer2=};; + *=*) OPTIONS="$OPTIONS $1";; + esac + shift +done + +# translate variables from the interface config files to OPTIONS +if [ -n "$PORTNAME" ]; then + if [ "$NETTYPE" = "lcs" ]; then + OPTIONS="$OPTIONS portno=$PORTNAME" + else + OPTIONS="$OPTIONS portname=$PORTNAME" + fi +fi +if [ "$NETTYPE" = "ctc" -a -n "$CTCPROT" ]; then + OPTIONS="$OPTIONS protocol=$CTCPROT" +fi + +# SUBCHANNELS is only set on mainframe ccwgroup devices +[ -z "$SUBCHANNELS" -o -z "$NETTYPE" ] && exit 0 +if [ "$NETTYPE" = "ctc" ]; then + DIR="/sys/bus/ccwgroup/drivers/ctcm" +else + DIR="/sys/bus/ccwgroup/drivers/$NETTYPE" +fi + +i=0 +while [ $i -lt 20 ]; do + [ -e $DIR ] && break + sleep 0.1 + i=$(($i+1)) +done + +# driver missing or not loaded +[ ! -e $DIR ] && exit 0 + +IFS="," +set $SUBCHANNELS +IFS="$OLD_IFS" +CHANNEL1=$1 +CHANNEL2=$2 +SYSDIR="$DIR/$CHANNEL1" + +[ -e $SYSDIR ] && exit 0 + +# check if the interface is already online +if [ -e $SYSDIR/online ]; then + read on <$SYSDIR/online + [ "$on" = "1" ] && exit 0 +fi + +DRIVER=$(readlink $DEVPATH/driver) +DRIVER=${DRIVER##*/} +if [ "$DRIVER" = "lcs" -a "$NETTYPE" = "ctc" ]; then + echo "$CHANNEL" > /sys/bus/ccw/drivers/lcs/unbind + echo "$CHANNEL" > /sys/bus/ccw/drivers/ctcm/bind + echo "$CHANNEL2" > /sys/bus/ccw/drivers/lcs/unbind + echo "$CHANNEL2" > /sys/bus/ccw/drivers/ctcm/bind +fi +if [ "$DRIVER" = "ctcm" -a "$NETTYPE" = "lcs" ]; then + echo "$CHANNEL" > /sys/bus/ccw/drivers/ctcm/unbind + echo "$CHANNEL" > /sys/bus/ccw/drivers/lcs/bind + echo "$CHANNEL2" > /sys/bus/ccw/drivers/ctcm/unbind + echo "$CHANNEL2" > /sys/bus/ccw/drivers/lcs/bind +fi + +if [ ! -e $SYSDIR ]; then + echo "$SUBCHANNELS" > $DIR/group + i=0 + while [ $i -lt 20 ]; do + [ -e $SYSDIR ] && break + sleep 0.1 + i=$(($i+1)) + done + + [ ! -e $SYSDIR ] && exit 1 +fi + +# check if the interface is already online +if [ -e $SYSDIR/online ]; then + read on <$SYSDIR/online + [ "$on" = "1" ] && exit 0 +fi + +# first set layer2, other options may depend on it +[ -n "$LAYER2" ] && echo $LAYER2 > $SYSDIR/layer2 + +if [ -n "$OPTIONS" ]; then + for i in $OPTIONS; do + OPT=${i%%=*} + VAL=${i##*=} + if [ -e "$SYSDIR/$OPT" ]; then + echo "$VAL" > "$SYSDIR/$OPT" || \ + echo "Could not set value \"$VAL\" for OPTION \"$OPT\" with SUBCHANNELS \"$SUBCHANNELS\"" + else + echo "OPTION \"$OPT\" does not exist for SUBCHANNELS \"$SUBCHANNELS\"" + fi + done +fi + +[ -e $SYSDIR/online ] && echo 1 > $SYSDIR/online diff --git a/SOURCES/cmsfs-1.1.8-kernel26.patch b/SOURCES/cmsfs-1.1.8-kernel26.patch new file mode 100644 index 0000000..c045827 --- /dev/null +++ b/SOURCES/cmsfs-1.1.8-kernel26.patch @@ -0,0 +1,12 @@ +diff -urN cmsfs-1.1.8/cmsfssed.sh cmsfs-1.1.8_/cmsfssed.sh +--- cmsfs-1.1.8/cmsfssed.sh 2003-02-28 17:52:59.000000000 -0500 ++++ cmsfs-1.1.8_/cmsfssed.sh 2004-05-28 16:36:22.000000000 -0400 +@@ -85,7 +85,7 @@ + DRIVER_SOURCE="cmsfs22x.c" + MODULES_DIRECTORY="/lib/modules/`uname -r`/fs" + ;; +- 2.4*|2.5*) ++ 2.4*|2.5*|2.6*|3.*|4.*) + LINUX_RELEASE="2.4" + # ln -s cmsfs24x.c cmsfsvfs.c + INCLUDES="-I/lib/modules/`uname -r`/build/include" diff --git a/SOURCES/cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch b/SOURCES/cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch new file mode 100644 index 0000000..275d371 --- /dev/null +++ b/SOURCES/cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch @@ -0,0 +1,31 @@ +From 25442f958a12b428b7d063b927ac48965dcd8164 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 28 Jan 2011 16:11:19 +0100 +Subject: [PATCH] use detected filesystem block size on FBA devices + +If a FBA device is not properly formated, then the CMS file system can +have a different block size. The cmsfs tools were able to detect the file +system block size, but in fact they still used default 512 instead. And +using the default was causing crashes. Now the detected value is used. + +https://bugzilla.redhat.com/show_bug.cgi?id=651012 +--- + cmsfsany.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/cmsfsany.c b/cmsfsany.c +index 55bcfdc..18efffb 100644 +--- a/cmsfsany.c ++++ b/cmsfsany.c +@@ -102,7 +102,7 @@ int cmsfs_find_label(struct CMSSUPER *vol,struct CMSFSADT *adt) + cmsfs_error(cmsfs_ermsg); + } + vol->flags = CMSFSFBA; +- vol->blksz = 512; ++ vol->blksz = blksz; + return vol->blksz; + } } + +-- +1.7.3.5 + diff --git a/SOURCES/cmsfs-1.1.8-warnings.patch b/SOURCES/cmsfs-1.1.8-warnings.patch new file mode 100644 index 0000000..cb1501d --- /dev/null +++ b/SOURCES/cmsfs-1.1.8-warnings.patch @@ -0,0 +1,11 @@ +--- cmsfs-1.1.8/cmsfsvol.c.warnings 2003-07-18 01:38:57.000000000 +0200 ++++ cmsfs-1.1.8/cmsfsvol.c 2005-09-06 16:57:15.000000000 +0200 +@@ -52,7 +52,7 @@ + + /* print a header; looks like CMS */ + (void) printf("LABEL VDEV M STAT CYL TYPE \ +-BLKSZ FILES BLKS USED-(%) BLKS LEFT BLK TOTAL\n"); ++BLKSZ FILES BLKS USED-(%%) BLKS LEFT BLK TOTAL\n"); + + for ( ; i < argc ; i++) + { diff --git a/SOURCES/dasd.udev b/SOURCES/dasd.udev new file mode 100644 index 0000000..5364935 --- /dev/null +++ b/SOURCES/dasd.udev @@ -0,0 +1,16 @@ +ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="dasd-eckd", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="dasd-fba", RUN+="/sbin/dasdconf.sh" + +# This list should be autogenerated with "modinfo dasd_{eckd,fba}_mod" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t1750m*dt3380dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t1750m*dt3390dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t2107m*dt3380dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t2107m*dt3390dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t9343m*dt9345dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t2105m*dt3380dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t3990m*dt3380dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t3880m*dt3390dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t2105m*dt3390dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t3990m*dt3390dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t3880m*dt3370dm*", RUN+="/sbin/dasdconf.sh" +ACTION=="add", SUBSYSTEM=="ccw", ATTR{modalias}=="ccw:t6310m*dt9336dm*", RUN+="/sbin/dasdconf.sh" diff --git a/SOURCES/dasdconf.sh b/SOURCES/dasdconf.sh new file mode 100644 index 0000000..2be5276 --- /dev/null +++ b/SOURCES/dasdconf.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +# config file syntax: +# deviceno sysfs_opts... +# +# Examples: +# 0.0.0203 readonly=1 failfast=1 +# 0.0.0204 +# 0.0.0205 erplog=1 + +[ -z "$DEVPATH" ] && exit 0 +[ "$ACTION" != "add" ] && exit 0 + +CHANNEL=${DEVPATH##*/} + +CONFIG=/etc/dasd.conf +PATH=/sbin:/bin +export PATH + +warn() { + [ -e /dev/kmsg ] && echo "<4>dasdconf.sh Warning: $@" > /dev/kmsg + echo "dasdconf.sh Warning: $@" >&2 +} + +if [ -f "$CONFIG" ]; then + if [ ! -d /sys/bus/ccw/drivers/dasd-eckd ] && [ ! -d /sys/bus/ccw/drivers/dasd-fba ]; then + #warn "No dasd-eckd or dasd-eckd loaded" + exit 0 + fi + sed 'y/ABCDEF/abcdef/' < $CONFIG | while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + set $line + + # if we are in single add mode, only add the new CHANNEL + [ "$SUBSYSTEM" = "ccw" ] && [ "$1" != "$CHANNEL" ] && continue + + DEVICE=$1 + SYSFSPATH= + + if [ -r "/sys/bus/ccw/drivers/dasd-eckd/$DEVICE" ]; then + SYSFSPATH="/sys/bus/ccw/drivers/dasd-eckd/$DEVICE" + elif [ -r "/sys/bus/ccw/drivers/dasd-fba/$DEVICE" ]; then + SYSFSPATH="/sys/bus/ccw/drivers/dasd-fba/$DEVICE" + else + # if we are in single add mode, this is a failure! + [ "$SUBSYSTEM" = "ccw" ] && warn "Could not find $DEVICE in sysfs" + continue + fi + + # skip already onlined devices + if [ "$(cat $SYSFSPATH/online)" = "1" ]; then + if [ "$SUBSYSTEM" = "ccw" ]; then + # if we are in single add mode, we should not touch the device + warn "$DEVICE is already online, not configuring" + exit 0 + fi + continue + fi + + shift + while [ -n "$1" ]; do + ( + attribute="$1" + IFS="=" + set $attribute + + if [ "$1" = "use_diag" ]; then + # this module better only returns after + # all sysfs entries have the "use_diag" file + modprobe dasd_diag_mod + fi + + if [ -r "$SYSFSPATH/$1" ]; then + echo $2 > $SYSFSPATH/$1 || warn "Could not set $1=$2 for $DEVICE" + else + warn "$1 does not exist for $DEVICE" + fi + ) + shift + done + + # Now, put the device online + echo 1 > $SYSFSPATH/online || echo "Could not activate $DEVICE" + + # if we are in single add mode, we are done + [ "$SUBSYSTEM" = "ccw" ] && exit 0 + ;; + esac + done +fi +exit 0 diff --git a/SOURCES/device_cio_free b/SOURCES/device_cio_free new file mode 100644 index 0000000..cd2b21f --- /dev/null +++ b/SOURCES/device_cio_free @@ -0,0 +1,313 @@ +#!/bin/sh +# +# Copyright 2009, 2010 Red Hat, Inc. +# License: GPLv2 +# Author: Dan Horák +# +# unblock devices listed in various config files and wait until they are ready +# +# it uses dasd and zfcp config file +# config file syntax: +# deviceno options +# or +# deviceno WWPN FCPLUN +# +# also processes the system ccw config file and network interface configurations +# +# requires: echo, sleep, modprobe, grep, printf, sed. +# +# it is used in +# anaconda +# dracut generated initramfs +# normal system startup driven by upstart +# + +DASDCONFIG=/etc/dasd.conf +ZFCPCONFIG=/etc/zfcp.conf +ZNETCONFIG=/etc/ccw.conf +BLACKLIST=/proc/cio_ignore +CIO_SETTLE=/proc/cio_settle +VERBOSE= +PATH=/bin:/sbin +DEVICE= # list of devices given on command line +ALL_DEVICES= # list of all unblocked devices +WAITING_TIMEOUT=60 # maximum time to wait for all devices to appear +WAITING_TOTAL=0 # actual time spent waiting for devices + +usage() +{ + echo "Usage: $CMD [-h|--help] [-V|--verbose] [-d|--device ]" + echo " -h|--help print this message" + echo " -V|--verbose be verbose" + echo " -d|--device unblock and wait for specified device" + exit 1 +} + +# accepts single device, comma-separated lists and dash separated ranges and their combinations +# the comma separated list is split so we minimize the effect of unsuccessful freeing +free_device() +{ + local DEV DEV_LIST + + [ -z "$1" ] && return + + DEV_LIST=$(echo "$1" | sed 'y/ABCDEF/abcdef/' | sed 's/,/ /g') + + for DEV in $DEV_LIST; do + [ $VERBOSE ] && echo "Freeing device(s) $DEV" + if ! echo "free $DEV" > $BLACKLIST 2> /dev/null ; then + echo "Error: can't free device(s) $DEV" + else + if [ -z $ALL_DEVICES ]; then + ALL_DEVICES="$DEV" + else + ALL_DEVICES="$ALL_DEVICES,$DEV" + fi + fi + done +} + +# wait until a device appears on the ccw bus +wait_on_single_device() +{ + local DEVICE_ONLINE DEV + + [ -z "$1" ] && return + + DEV="$1" + DEVICE_ONLINE="/sys/bus/ccw/devices/$DEV/online" + + [ $VERBOSE ] && echo "Waiting on device $DEV" + [ -f "$DEVICE_ONLINE" ] && return + + for t in 1 2 3 4 5 + do + if [ $WAITING_TOTAL -ge $WAITING_TIMEOUT ]; then + [ $VERBOSE ] && echo "Waiting timeout of $WAITING_TIMEOUT seconds reached" + break + fi + WAITING_TOTAL=$(($WAITING_TOTAL + $t)) + [ $VERBOSE ] && echo "Waiting additional $t second(s) and $WAITING_TOTAL second(s) in total" + sleep $t + [ -f "$DEVICE_ONLINE" ] && return + done + echo "Error: device $DEV still not ready" +} + +# wait until recently unblocked devices are ready +# at this point we know the content of ALL_DEVICES is syntacticly correct +wait_on_devices() +{ + if [ -w $CIO_SETTLE ]; then + [ $VERBOSE ] && echo "Waiting until all pending CIO requests are processed" + echo 1 > $CIO_SETTLE + return + fi + + OLD_IFS=$IFS + IFS="," + set $ALL_DEVICES + for DEV in $* + do + IFS="." + + # get the lower bound for range or get the single device + LOWER=${DEV%%-*} + set $LOWER + if [ $# -eq 1 ]; then + L0=0 + L1=0 + L2=$(printf "%d" "0x$1") + else + L0=$(printf "%d" "0x$1") + L1=$(printf "%d" "0x$2") + L2=$(printf "%d" "0x$3") + fi + + # get the upper bound for range or get the single device + UPPER=${DEV##*-} + set $UPPER + if [ $# -eq 1 ]; then + U0=0 + U1=0 + U2=$(printf "%d" "0x$1") + else + U0=$(printf "%d" "0x$1") + U1=$(printf "%d" "0x$2") + U2=$(printf "%d" "0x$3") + fi + + IFS=$OLD_IFS + + # iterate thru all devices + i=$L0 + while [ $i -le $U0 ]; do + [ $i -eq $L0 ] && LJ=$L1 || LJ=0 + [ $i -eq $U0 ] && UJ=$U1 || UJ=3 + + j=$LJ + while [ $j -le $UJ ]; do + [ $i -eq $L0 -a $j -eq $L1 ] && LK=$L2 || LK=0 + [ $i -eq $U0 -a $j -eq $U1 ] && UK=$U2 || UK=65535 + + k=$LK + while [ $k -le $UK ]; do + wait_on_single_device "$(printf %x.%x.%04x $i $j $k)" + k=$(($k + 1)) + done + j=$(($j + 1)) + done + i=$(($i + 1)) + done + done +} + +process_config_file() +{ + local CONFIG + + [ -z "$1" ] && return + + CONFIG="$1" + if [ -f "$CONFIG" ]; then + while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + set $line + free_device $1 + ;; + esac + done < "$CONFIG" + fi +} + +# check how we were called +CMD=${0##*/} +DIR=${0%/*} +ARGS=$@ +case $CMD in + "dasd_cio_free") + MODE_DASD="yes" + ;; + "zfcp_cio_free") + MODE_ZFCP="yes" + ;; + "znet_cio_free") + MODE_ZNET="yes" + ;; + "device_cio_free") + MODE_DASD="yes" + MODE_ZFCP="yes" + MODE_ZNET="yes" + ;; + *) + echo "Error: unknown alias '$CMD'." + echo "Supported aliases are dasd_cio_free, zfcp_cio_free and znet_cio_free." + exit 1 + ;; +esac + +# process command line options +while [ $# -gt 0 ]; do + case $1 in + -V|--verbose) + VERBOSE=yes + ;; + -h|--help) + usage + ;; + -d|--device) + shift + if [ "$1" ]; then + if [ "$DEVICE" ]; then + DEVICE="$DEVICE,$1" + else + DEVICE=$1 + fi + else + echo "Error: no device given" + usage + fi + ;; + *) + echo "Error: unknown option $1" + usage + ;; + esac + shift +done + +if [ ! -f $BLACKLIST ]; then + echo "Error: $BLACKLIST kernel interface doesn't exist" + exit 2 +fi + +if [ "$DEVICE" ]; then + [ $VERBOSE ] && echo "Freeing specific devices" + free_device $DEVICE + wait_on_devices + udevadm settle + exit 0 +fi + +if [ $VERBOSE ]; then + echo -n "Freeing devices:" + [ $MODE_DASD ] && echo -n " dasd" + [ $MODE_ZFCP ] && echo -n " zfcp" + [ $MODE_ZNET ] && echo -n " znet" + echo +fi + +[ $MODE_DASD ] && process_config_file $DASDCONFIG +[ $MODE_ZFCP ] && process_config_file $ZFCPCONFIG + +if [ $MODE_DASD ]; then + # process the device list defined as option for the dasd module + DEVICES=$(modprobe --showconfig | LANG=C grep "options[[:space:]]\+dasd_mod" | \ + sed -e 's/.*[[:space:]]dasd=\([^[:space:]]*\).*/\1/' -e 's/([^)]*)//g' \ + -e 's/nopav\|nofcx\|autodetect\|probeonly//g' -e 's/,,/,/g' -e 's/^,//' -e 's/,$//') + + for DEVRANGE in $(echo $DEVICES | sed 's/,/ /g'); do + free_device $DEVRANGE + done +fi + +if [ $MODE_ZNET ]; then + # process the config file + if [ -f "$ZNETCONFIG" ]; then + while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + # grep 2 or 3 channels from each ",," line + DEVICES=$(echo $line | LANG=C grep -E -i -o "([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)") + free_device $DEVICES + ;; + esac + done < "$ZNETCONFIG" + fi + # process channels from network interface configurations + if [ -z "$__sed_discard_ignored_files" ]; then + if [ -f /etc/init.d/functions ]; then + . /etc/init.d/functions + else + # default value copied from initscripts 9.03.10 + __sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' + fi + fi + for line in $(LANG=C grep -E -i -h \ + "^[[:space:]]*SUBCHANNELS=['\"]?([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)['\"]?([[:space:]]+#|[[:space:]]*$)" \ + $( (ls /etc/sysconfig/network-scripts/ifcfg-* 2> /dev/null || echo "__no_config_file") | \ + LC_ALL=C sed -e "$__sed_discard_ignored_files") 2> /dev/null) + do + eval "$line" + free_device $SUBCHANNELS + done +fi + +[ -z "$ALL_DEVICES" ] && exit 0 + +wait_on_devices diff --git a/SOURCES/device_cio_free.service b/SOURCES/device_cio_free.service new file mode 100644 index 0000000..f9564e1 --- /dev/null +++ b/SOURCES/device_cio_free.service @@ -0,0 +1,13 @@ +[Unit] +Description=Free all devices on startup +DefaultDependencies=no +Before=sysinit.target systemd-udev-trigger.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/sbin/device_cio_free +StandardOutput=syslog + +[Install] +WantedBy=sysinit.target diff --git a/SOURCES/normalize_dasd_arg b/SOURCES/normalize_dasd_arg new file mode 100644 index 0000000..3e7afa6 --- /dev/null +++ b/SOURCES/normalize_dasd_arg @@ -0,0 +1,120 @@ +#!/bin/sh +# +# Copyright 2012 Red Hat, Inc. +# License: GPLv2 +# Author: Jesse Keating +# +# Normalize DASD data into valid dasd.conf format +# +# Standard input should be the DASD argument +# Standard otuput is the properly formatted content +# +# it is used in +# dracut generated initramfs +# +# Much of this code was salvaged from linuxrc.s390 from Anaconda: +# +# License GPLv2+ +# +# Copyright (C) 2000-2004 by +# Bernhard Rosenkraenzer +# Oliver Paukstadt +# Karsten Hopp +# Florian La Roche +# Nils Philippsen +# Helge Deller +# David Sainty +# Copyright (C) IBM Corp. 2008,2009 +# Author: Steffen Maier + + +function canonicalize_devno() +{ + case ${#1} in + 3) echo "0.0.0${1}" ;; + 4) echo "0.0.${1}" ;; + *) echo "${1}" ;; + esac + return 0 +} + +read DASD +# See if we've gotten a format like ,feature or ,, +[[ "$DASD" =~ (\,*=[[:digit:]]) ]] +case $? in + # case of 0 is features, just turn the comma into a space + 0) echo $DASD |sed 's/,/ /g';; + *) # We've got no features, do things normally + for dasditem in $(echo $DASD |sed 's/,/ /g') + do + unset range features lo hi attrs devno lodevno hidevno devbusid sys + case $dasditem in + autodetect|probeonly|nopav|nofcx|"") continue ;; # these don't gen a config + *) + IFS='(' + read range features <<< "$dasditem" + unset IFS + lo=${range%%-*} + [[ "$lo" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]] + case $? in + 0) # string matched the pattern + lo=$(canonicalize_devno $lo) ;; + 1) # string did not match the pattern + echo $"Incorrect format for lower bound of DASD range $range: $lo" 1>&2 + exit 1 + ;; + 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; + *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; + esac + if [ "${range//*-*/}" = "" ]; then + hi=${range##*-} + [[ "$hi" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]] + case $? in + 0) # string matched the pattern + hi=$(canonicalize_devno $hi) + if [ "${lo%.*}" != "${hi%.*}" ]; then + echo $"Prefixes of DASD range $range do not match: ${lo%.*} != ${hi%.*}" 1>&2 + exit 1 + fi + ;; + 1) # string did not match the pattern + echo $"Incorrect format for upper bound of DASD range $range: $hi" 1>&2 + exit 1 + ;; + 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; + *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; + esac + fi + if [ "${features//*)/}" != "" ]; then + echo $"Missing closing parenthesis at features of DASD range $range: ($features" 1>&2 + exit 1 + fi + if [ -n "$features" ]; then + attrs="" + features="${features%)}" + for feature in $(echo $features |sed 's/:/\n/g'); do + case $feature in + ro) attrs=$attrs" readonly" ;; + diag) attrs=$attrs" use_diag" ;; + erplog|failfast) attrs=$attrs" "$feature ;; + *) echo $"Unknown DASD feature for device range $range: $feature" 1>&2 + exit 1 + ;; + esac + done + fi + [ -z "$hi" ] && hi=$lo + lodevno=$((0x${lo##*.})) + hidevno=$((0x${hi##*.})) + for ((devno=$lodevno; $devno <= $hidevno; ++devno)); do + devbusid=$(printf "%s.%04x" ${lo%.*} $devno) + echo -n "$devbusid" + for attr in $attrs; do + echo -n " $attr=1" + done + echo + done + esac + done + ;; +esac diff --git a/SOURCES/s390-tools-rhel.patch b/SOURCES/s390-tools-rhel.patch new file mode 100644 index 0000000..f263c53 --- /dev/null +++ b/SOURCES/s390-tools-rhel.patch @@ -0,0 +1,22895 @@ +From ab2402c95f031ad97544a86e4418cb765313eee8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 31 Aug 2018 10:07:35 +0200 +Subject: [PATCH 01/44] drop LOADLIBES variable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove depreciated LOADLIBES variable from the Makefile rules, LDLIBS serves +the same purpose these days. + +Signed-off-by: Dan Horák +--- + common.mak | 2 +- + cpuplugd/Makefile | 2 +- + ipl_tools/Makefile | 2 +- + vmconvert/Makefile | 2 +- + vmur/Makefile | 2 +- + ziomon/Makefile | 10 +++++----- + 6 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/common.mak b/common.mak +index 571fb30..6f0990e 100644 +--- a/common.mak ++++ b/common.mak +@@ -239,7 +239,7 @@ endif + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@ + + %: %.o +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + %.a: + $(AR) rcs $@ $^ +diff --git a/cpuplugd/Makefile b/cpuplugd/Makefile +index a9a49ab..916638d 100644 +--- a/cpuplugd/Makefile ++++ b/cpuplugd/Makefile +@@ -7,7 +7,7 @@ LDLIBS += -lm + OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o + + cpuplugd: $(OBJECTS) +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + clean: + rm -f cpuplugd $(OBJECTS) +diff --git a/ipl_tools/Makefile b/ipl_tools/Makefile +index 128ec3e..506d5cd 100644 +--- a/ipl_tools/Makefile ++++ b/ipl_tools/Makefile +@@ -6,7 +6,7 @@ objects = main.o ccw.o fcp.o system.o shutdown.o \ + cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o + + chreipl: $(objects) +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + lsreipl: + ln -sf chreipl lsreipl +diff --git a/vmconvert/Makefile b/vmconvert/Makefile +index 4d0216c..380eb19 100644 +--- a/vmconvert/Makefile ++++ b/vmconvert/Makefile +@@ -9,7 +9,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a + objects = vmconvert.o + + vmconvert: $(objects) $(libs) +- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(DESTDIR)$(MANDIR)/man1 +diff --git a/vmur/Makefile b/vmur/Makefile +index 1a6bddc..2c1c2d5 100644 +--- a/vmur/Makefile ++++ b/vmur/Makefile +@@ -11,7 +11,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a \ + objects = vmur.o + + vmur: $(objects) $(libs) +- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRSBINDIR) $(DESTDIR)$(MANDIR)/man8 +diff --git a/ziomon/Makefile b/ziomon/Makefile +index 778401b..61c2399 100644 +--- a/ziomon/Makefile ++++ b/ziomon/Makefile +@@ -12,33 +12,33 @@ ziomon_mgr_main.o: ziomon_mgr.c + ziomon_mgr: LDLIBS += -lm + ziomon_mgr: ziomon_dacc.o ziomon_util.o ziomon_mgr_main.o ziomon_tools.o \ + ziomon_zfcpdd.o ziomon_msg_tools.o +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + ziomon_util_main.o: ziomon_util.c ziomon_util.h + $(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@ + ziomon_util: LDLIBS += -lm + ziomon_util: ziomon_util_main.o ziomon_tools.o +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + ziomon_zfcpdd_main.o: ziomon_zfcpdd.c ziomon_zfcpdd.h + $(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@ + ziomon_zfcpdd: LDLIBS += -lm -lrt -lpthread + ziomon_zfcpdd: ziomon_zfcpdd_main.o ziomon_tools.o +- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + ziorep_traffic: ziorep_traffic.o ziorep_framer.o ziorep_frameset.o \ + ziorep_printers.o ziomon_dacc.o ziomon_util.o \ + ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \ + ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \ + ziorep_filters.o +- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + ziorep_utilization: ziorep_utilization.o ziorep_framer.o ziorep_frameset.o \ + ziorep_printers.o ziomon_dacc.o ziomon_util.o \ + ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \ + ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \ + ziorep_filters.o +- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ++ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + install: all + $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ +-- +2.21.3 + + +From ed7cf76fd149a9fed3ce9f728e072bccc44997cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 31 Aug 2018 10:13:38 +0200 +Subject: [PATCH 02/44] zkey: Drop redundant include +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Dan Horák +--- + zkey/Makefile | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/zkey/Makefile b/zkey/Makefile +index 68f35cf..725cb3b 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -22,7 +22,6 @@ else + INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2 + endif + +-CPPFLAGS += -I../include + LIBS = $(rootdir)/libutil/libutil.a + + detect-libcryptsetup.h: +-- +2.21.3 + + +From 3a354d9d8e83a36edb9ce68fb85b3cc4afd19991 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 31 Aug 2018 10:17:07 +0200 +Subject: [PATCH 03/44] zkey: Be consistent when refering to libutil.a +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Dan Horák +--- + zkey/Makefile | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/zkey/Makefile b/zkey/Makefile +index 725cb3b..7e2047a 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -22,7 +22,7 @@ else + INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2 + endif + +-LIBS = $(rootdir)/libutil/libutil.a ++libs = $(rootdir)/libutil/libutil.a + + detect-libcryptsetup.h: + echo "#include " > detect-libcryptsetup.h +@@ -69,10 +69,10 @@ keystore.o: keystore.c keystore.h properties.h + zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h + + zkey: LDLIBS = -ldl -lcrypto +-zkey: zkey.o pkey.o properties.o keystore.o $(LIBS) ++zkey: zkey.o pkey.o properties.o keystore.o $(libs) + + zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c +-zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(LIBS) ++zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) + + install-common: + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +-- +2.21.3 + + +From 913721b06be3b4662593c3f1a344256c46ce841a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 31 Aug 2018 04:29:39 -0400 +Subject: [PATCH 04/44] zkey: Be explicit about linking the tools +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +I've met cases when the make's default rule for linking was used instead omitting +the ALL_LDFLAGS variable. The linking rule from common.mak is defined for linking +*.o files only, here we have libutil.a too. + +Signed-off-by: Dan Horák +--- + zkey/Makefile | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/zkey/Makefile b/zkey/Makefile +index 7e2047a..901ddd4 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -70,9 +70,11 @@ zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h + + zkey: LDLIBS = -ldl -lcrypto + zkey: zkey.o pkey.o properties.o keystore.o $(libs) ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c + zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) ++ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + install-common: + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +-- +2.21.3 + + +From 6a3da6ec34a0947bd14cd0a36ab08b607236a414 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Wed, 17 Oct 2018 13:52:48 +0200 +Subject: [PATCH 05/44] zkey: Makefile: Avoid relink of modules during 'make + install' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Because targets check-dep-zkey and check-dep-zkey-cryptsetup +do not produce any file, any targets that have a pre-req on those +targets are rebuilt during 'make install'. + +Also correct .PHONY targets. + +Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/46 +Signed-off-by: Ingo Franzki +Signed-off-by: Jan Höppner +--- + zkey/Makefile | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/zkey/Makefile b/zkey/Makefile +index 901ddd4..bc7bc33 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -37,6 +37,7 @@ check-dep-zkey: + "openssl/evp.h", \ + "openssl-devel", \ + "HAVE_OPENSSL=0") ++ touch check-dep-zkey + + check-dep-zkey-cryptsetup: detect-libcryptsetup.h + $(call check_dep, \ +@@ -50,6 +51,7 @@ check-dep-zkey-cryptsetup: detect-libcryptsetup.h + "json-c/json.h", \ + "json-c-devel", \ + "HAVE_JSONC=0") ++ touch check-dep-zkey-cryptsetup + + zkey-skip: + echo " SKIP zkey due to HAVE_OPENSSL=0" +@@ -93,6 +95,9 @@ install-zkey-cryptsetup: + install: all install-common $(INSTALL_TARGETS) + + clean: +- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h ++ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \ ++ check-dep-zkey check-dep-zkey-cryptsetup + +-.PHONY: all install clean ++.PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \ ++ zkey-cryptsetup-skip-jsonc install-common install-zkey \ ++ install-zkey-cryptsetup +-- +2.21.3 + + +From e7b59a9f7cc5d049ef68331855a881beb85a1347 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Thu, 25 Oct 2018 12:57:29 +0200 +Subject: [PATCH 06/44] zkey: Makefile: Don't rebuild .o.d files on 'make + install' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .o.d make targets in common.mak do not expect that +header files are generated by a make target. When a new header +file is generated, the .o.d targets will be rebuilt on the +next make invocation, because that new header file is then +detected, and is then treated as a new dependency of all .o.d +targets. + +Signed-off-by: Ingo Franzki +Signed-off-by: Jan Höppner +--- + zkey/Makefile | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/zkey/Makefile b/zkey/Makefile +index bc7bc33..a44b14b 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -24,12 +24,12 @@ endif + + libs = $(rootdir)/libutil/libutil.a + +-detect-libcryptsetup.h: +- echo "#include " > detect-libcryptsetup.h +- echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.h +- echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.h +- echo "#endif" >> detect-libcryptsetup.h +- echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.h ++detect-libcryptsetup.dep: ++ echo "#include " > detect-libcryptsetup.dep ++ echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.dep ++ echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.dep ++ echo "#endif" >> detect-libcryptsetup.dep ++ echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.dep + + check-dep-zkey: + $(call check_dep, \ +@@ -39,10 +39,10 @@ check-dep-zkey: + "HAVE_OPENSSL=0") + touch check-dep-zkey + +-check-dep-zkey-cryptsetup: detect-libcryptsetup.h ++check-dep-zkey-cryptsetup: detect-libcryptsetup.dep + $(call check_dep, \ + "zkey-cryptsetup", \ +- "detect-libcryptsetup.h", \ ++ "detect-libcryptsetup.dep", \ + "cryptsetup-devel version 2.0.3", \ + "HAVE_CRYPTSETUP2=0", \ + "-I.") +@@ -95,7 +95,7 @@ install-zkey-cryptsetup: + install: all install-common $(INSTALL_TARGETS) + + clean: +- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \ ++ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.dep \ + check-dep-zkey check-dep-zkey-cryptsetup + + .PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \ +-- +2.21.3 + + +From dc92c4ce7963dcf5018c3ef3672b6d217f29842f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:26:40 +0100 +Subject: [PATCH 07/44] zpcictl: Add tool to manage PCI devices (#1525409) + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +--- + .gitignore | 1 + + Makefile | 2 +- + zpcictl/Makefile | 18 +++ + zpcictl/zpcictl.8 | 80 ++++++++++ + zpcictl/zpcictl.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++ + zpcictl/zpcictl.h | 60 ++++++++ + 6 files changed, 539 insertions(+), 1 deletion(-) + create mode 100644 zpcictl/Makefile + create mode 100644 zpcictl/zpcictl.8 + create mode 100644 zpcictl/zpcictl.c + create mode 100644 zpcictl/zpcictl.h + +diff --git a/.gitignore b/.gitignore +index 41feaf6..042233f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -89,3 +89,4 @@ zipl/src/chreipl_helper.device-mapper + zipl/src/zipl + zipl/src/zipl_helper.device-mapper + zkey/zkey ++zpcictl/zpcictl +diff --git a/Makefile b/Makefile +index adc92b6..bb2b900 100644 +--- a/Makefile ++++ b/Makefile +@@ -8,7 +8,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ + tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ + vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ + ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ +- systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc ++ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl + SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) + + all: $(TOOL_DIRS) +diff --git a/zpcictl/Makefile b/zpcictl/Makefile +new file mode 100644 +index 0000000..2b315c5 +--- /dev/null ++++ b/zpcictl/Makefile +@@ -0,0 +1,18 @@ ++include ../common.mak ++ ++all: zpcictl ++ ++libs = $(rootdir)/libutil/libutil.a ++ ++zpcictl: zpcictl.o $(libs) ++ ++install: all ++ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zpcictl $(DESTDIR)$(BINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 zpcictl.8 \ ++ $(DESTDIR)$(MANDIR)/man8 ++ ++clean: ++ rm -f *.o *~ zpcictl core ++ ++.PHONY: all install clean +diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 +new file mode 100644 +index 0000000..4f8fcd8 +--- /dev/null ++++ b/zpcictl/zpcictl.8 +@@ -0,0 +1,80 @@ ++.\" Copyright 2017 IBM Corp. ++.\" s390-tools is free software; you can redistribute it and/or modify ++.\" it under the terms of the MIT license. See LICENSE for details. ++.\" ++.\" Macro for inserting an option description prologue. ++.\" .OD [] [args] ++.de OD ++. ds args " ++. if !'\\$3'' .as args \fI\\$3\fP ++. if !'\\$4'' .as args \\$4 ++. if !'\\$5'' .as args \fI\\$5\fP ++. if !'\\$6'' .as args \\$6 ++. if !'\\$7'' .as args \fI\\$7\fP ++. PD 0 ++. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4 ++. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4 ++. PD ++.. ++. ++.TH zpcictl 8 "Oct 2018" s390-tools zpcictl ++. ++.SH NAME ++zpcictl - Manage PCI devices on z Systems ++. ++. ++.SH SYNOPSIS ++.B "zpcictl" ++.I "OPTIONS" ++.I "DEVICE" ++. ++. ++.SH DESCRIPTION ++.B zpcictl ++is a tool for managing PCI devices on the IBM z Systems platform. It is ++especially used for reporting errorneous PCI devices to the service element. ++ ++.B Note: ++For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent ++with any error handling action. The smartmontools are required to be installed ++for this to work. ++.PP ++. ++. ++.SH DEVICE ++.B DEVICE ++can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node ++of an NVMe device (e.g. /dev/nvme0). ++. ++. ++.SH OPTIONS ++.SS Error Handling ++.OD reset "" "DEVICE" ++Reset ++.I DEVICE ++and initiate a re-initialisation of the adapter. ++.PP ++. ++.OD deconfigure "" "DEVICE" ++De-configure ++.I DEVICE ++and prepare for any repair action. This action will move the ++PCI device from a configured to a reserved state. ++.PP ++. ++.OD report-error "" "DEVICE" ++Report any device error for ++.IR DEVICE . ++The ++.I DEVICE ++is marked as erroneous and no further action is initiated on it. ++.PP ++. ++.SS Misc ++.OD help "h" "" ++Print usage information, then exit. ++.PP ++. ++.OD version "v" "" ++Print version information, then exit. ++.PP +diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c +new file mode 100644 +index 0000000..5f63b17 +--- /dev/null ++++ b/zpcictl/zpcictl.c +@@ -0,0 +1,379 @@ ++/* ++ * zpcictl - Manage PCI devices on z Systems ++ * ++ * Copyright IBM Corp. 2018 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include "lib/util_base.h" ++#include "lib/util_libc.h" ++#include "lib/util_opt.h" ++#include "lib/util_path.h" ++#include "lib/util_prg.h" ++#include "lib/util_proc.h" ++#include "lib/util_rec.h" ++#include "lib/util_scandir.h" ++ ++#include "zpcictl.h" ++ ++#define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" ++ ++static const struct util_prg prg = { ++ .desc = "Use zpcictl to manage PCI devices on s390\n" ++ "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", ++ .args = "DEVICE", ++ .copyright_vec = { ++ { ++ .owner = "IBM Corp.", ++ .pub_first = 2018, ++ .pub_last = 2018, ++ }, ++ UTIL_PRG_COPYRIGHT_END ++ } ++}; ++ ++/* Defines for options with no short command */ ++#define OPT_RESET 128 ++#define OPT_DECONF 129 ++#define OPT_REPORT_ERR 130 ++ ++static struct util_opt opt_vec[] = { ++ UTIL_OPT_SECTION("ERROR HANDLING"), ++ { ++ .option = { "reset", no_argument, NULL, OPT_RESET }, ++ .desc = "Reset device", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, ++ .desc = "De-configure device and prepare for any repair action", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, ++ .desc = "Report device error to service element (SE)", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ UTIL_OPT_SECTION("MISC"), ++ UTIL_OPT_HELP, ++ UTIL_OPT_VERSION, ++ UTIL_OPT_END ++}; ++ ++static int is_char_dev(const char *dev) ++{ ++ struct stat s; ++ ++ if (stat(dev, &s)) ++ return 0; ++ ++ return S_ISCHR(s.st_mode); ++} ++ ++static int is_blk_dev(const char *dev) ++{ ++ struct stat s; ++ ++ if (stat(dev, &s)) ++ return 0; ++ ++ return S_ISBLK(s.st_mode); ++} ++ ++static void fopen_err(char *path) ++{ ++ warnx("Could not open file %s: %s", path, strerror(errno)); ++ free(path); ++ exit(EXIT_FAILURE); ++} ++ ++#define READ_CHUNK_SIZE 512 ++ ++static char *collect_smart_data(struct zpci_device *pdev) ++{ ++ char *buffer = NULL; ++ size_t count = 0; ++ char *cmd; ++ FILE *fd; ++ ++ util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); ++ fd = popen(cmd, "r"); ++ if (!fd) ++ goto out; ++ ++ while (!feof(fd)) { ++ buffer = realloc(buffer, count + READ_CHUNK_SIZE); ++ if (!buffer) { ++ warnx("Could not collect S.M.A.R.T. data"); ++ goto out; ++ } ++ count += fread(&buffer[count], 1, READ_CHUNK_SIZE, fd); ++ if (ferror(fd)) { ++ free(buffer); ++ buffer = NULL; ++ goto out; ++ } ++ } ++ ++ buffer = realloc(buffer, count); ++ if (!buffer && count > 0) ++ warnx("Could not collect S.M.A.R.T. data"); ++ if (buffer) ++ buffer[count] = '\0'; ++ ++out: ++ pclose(fd); ++ free(cmd); ++ ++ return buffer; ++} ++ ++static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) ++{ ++ unsigned int val; ++ char *path; ++ FILE *fp; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/%s", pdev->slot, attr); ++ fp = fopen(path, "r"); ++ if (!fp) ++ fopen_err(path); ++ fscanf(fp, "%x", &val); ++ fclose(fp); ++ free(path); ++ ++ return val; ++} ++ ++static void sysfs_write_data(struct zpci_report_error *report, char *slot) ++{ ++ char *path; ++ int fd, rc; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); ++ fd = open(path, O_WRONLY); ++ if (!fd) ++ fopen_err(path); ++ rc = write(fd, report, sizeof(*report)); ++ if (rc == -1) ++ warnx("Could not write to file: %s: %s", path, strerror(errno)); ++ if (close(fd)) ++ warnx("Could not close file: %s: %s", path, strerror(errno)); ++ free(path); ++} ++ ++static void sysfs_get_slot_addr(const char *dev, char *slot) ++{ ++ unsigned int major, minor; ++ struct stat dev_stat; ++ char addr[13]; ++ char *path; ++ FILE *fp; ++ ++ if (stat(dev, &dev_stat) != 0) { ++ errx(EXIT_FAILURE, "Could not get stat information for %s: %s", ++ dev, strerror(errno)); ++ } ++ major = major(dev_stat.st_rdev); ++ minor = minor(dev_stat.st_rdev); ++ ++ path = util_path_sysfs("dev/char/%u:%u/address", major, minor); ++ fp = fopen(path, "r"); ++ if (!fp) ++ fopen_err(path); ++ fscanf(fp, "%s", addr); ++ fclose(fp); ++ free(path); ++ ++ strcpy(slot, addr); ++} ++ ++static void get_device_node(struct zpci_device *pdev) ++{ ++ struct dirent **de_vec; ++ char *path, *dev; ++ char slot[13]; ++ int count, i; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/nvme", pdev->slot); ++ count = util_scandir(&de_vec, alphasort, path, "nvme*"); ++ if (count == -1) { ++ warnx("Could not read directory %s: %s", path, strerror(errno)); ++ free(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ for (i = 0; i < count; i++) { ++ util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); ++ sysfs_get_slot_addr(dev, slot); ++ if (strcmp(slot, pdev->slot) == 0) { ++ pdev->device = dev; ++ break; ++ } ++ } ++ ++ util_scandir_free(de_vec, count); ++ free(path); ++} ++ ++static int device_exists(char *dev) ++{ ++ char *path; ++ int rc = 0; ++ ++ path = util_path_sysfs("bus/pci/devices/%s", dev); ++ if (util_path_exists(path) || util_path_exists(dev)) ++ rc = 1; ++ free(path); ++ ++ return rc; ++} ++ ++static void get_device_info(struct zpci_device *pdev, char *dev) ++{ ++ if (!device_exists(dev)) ++ errx(EXIT_FAILURE, "Device %s not found", dev); ++ if (is_blk_dev(dev)) ++ errx(EXIT_FAILURE, "Unsupported device type %s", dev); ++ if (is_char_dev(dev)) { ++ sysfs_get_slot_addr(dev, pdev->slot); ++ pdev->device = dev; ++ } else { ++ strcpy(pdev->slot, dev); ++ } ++ ++ pdev->class = sysfs_read_value(pdev, "class"); ++ pdev->fid = sysfs_read_value(pdev, "function_id"); ++ pdev->pchid = sysfs_read_value(pdev, "pchid"); ++ ++ /* In case a slot address was specified, we still need to figure out ++ * the device node for NVMe devices. Otherwise we won't be able to ++ * collect S.M.A.R.T. data at a later point. ++ */ ++ if (!pdev->device && pdev->class == PCI_CLASS_NVME) ++ get_device_node(pdev); ++} ++ ++/* ++ * Issue an SCLP Adapter Error Notification event with a specific action ++ * qualifier. ++ * ++ * Collect additional information when possible (e.g. S.M.A.R.T. data for NVMe ++ * devices). ++ */ ++static void sclp_issue_action(struct zpci_device *pdev, int action) ++{ ++ struct zpci_report_error report = { ++ .header = { 0 }, ++ .data = { 0 } ++ }; ++ char *sdata = NULL; ++ ++ report.header.version = 1; ++ report.header.action = action; ++ report.header.length = sizeof(report.data); ++ report.data.timestamp = (__u64)time(NULL); ++ report.data.err_log_id = 0x4713; ++ ++ if (pdev->class == PCI_CLASS_NVME) ++ sdata = collect_smart_data(pdev); ++ if (sdata) { ++ strncpy(report.data.log_data, sdata, sizeof(report.data.log_data)); ++ free(sdata); ++ } ++ sysfs_write_data(&report, pdev->slot); ++} ++ ++/* ++ * Reset the PCI device and initiate a re-initialization. ++ */ ++static void sclp_reset_device(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET); ++} ++ ++/* ++ * De-Configure/repair PCI device. Moves the device from configured ++ * to reserved state. ++ */ ++static void sclp_deconfigure(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_DECONF); ++} ++ ++/* ++ * Report an error to the SE. ++ */ ++static void sclp_report_error(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_REPORT_ERR); ++} ++ ++static void parse_cmdline(int argc, char *argv[], struct options *opts) ++{ ++ int cmd; ++ ++ util_prg_init(&prg); ++ util_opt_init(opt_vec, NULL); ++ ++ do { ++ cmd = util_opt_getopt_long(argc, argv); ++ ++ switch (cmd) { ++ case OPT_RESET: ++ opts->reset = 1; ++ break; ++ case OPT_DECONF: ++ opts->deconfigure = 1; ++ break; ++ case OPT_REPORT_ERR: ++ opts->report = 1; ++ break; ++ case 'h': ++ util_prg_print_help(); ++ util_opt_print_help(); ++ exit(EXIT_SUCCESS); ++ case 'v': ++ util_prg_print_version(); ++ exit(EXIT_SUCCESS); ++ case -1: ++ /* End of options string */ ++ if (argc == 1) { ++ errx(EXIT_FAILURE, ++ "Use '%s --help' for more information", ++ argv[0]); ++ } ++ break; ++ } ++ } while (cmd != -1); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct zpci_device pdev = { 0 }; ++ struct options opts = { 0 }; ++ ++ parse_cmdline(argc, argv, &opts); ++ ++ if (optind >= argc) ++ errx(EXIT_FAILURE, "No device specified"); ++ ++ get_device_info(&pdev, argv[optind]); ++ ++ if (opts.reset) ++ sclp_reset_device(&pdev); ++ else if (opts.deconfigure) ++ sclp_deconfigure(&pdev); ++ else if (opts.report) ++ sclp_report_error(&pdev); ++ ++ return 0; ++} +diff --git a/zpcictl/zpcictl.h b/zpcictl/zpcictl.h +new file mode 100644 +index 0000000..5187e7c +--- /dev/null ++++ b/zpcictl/zpcictl.h +@@ -0,0 +1,60 @@ ++/* ++ * zpcictl - Manage PCI devices on z Systems ++ * ++ * Copyright IBM Corp. 2018 ++ * ++ * 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 ZPCICTL_H ++#define ZPCICTL_H ++ ++#include ++#include "lib/zt_common.h" ++ ++#define SCLP_ERRNOTIFY_AQ_RESET 0 ++#define SCLP_ERRNOTIFY_AQ_DECONF 1 ++#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 ++ ++#define PCI_CLASS_UNCLASSIFIED 0x000000U ++#define PCI_CLASS_NVME 0x010802U ++#define PCI_CLASS_NETWORK 0x020000U ++ ++struct options { ++ unsigned int reset; ++ unsigned int deconfigure; ++ unsigned int report; ++}; ++ ++struct zpci_device { ++ u16 fid; ++ u16 pchid; ++ u32 class; ++ char slot[13]; ++ char *device; ++}; ++ ++struct zpci_report_error_header { ++ __u8 version; /* Interface version byte */ ++ __u8 action; /* Action qualifier byte ++ * 0: Adapter Reset Request ++ * 1: Deconfigure and repair action requested ++ * 2: Informational Report ++ */ ++ __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ ++ __u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */ ++}; ++ ++struct zpci_report_error_data { ++ __u64 timestamp; ++ __u64 err_log_id; ++ char log_data[4054]; /* We cannot exceed a total of 4074 bytes (header + data) */ ++}; ++ ++struct zpci_report_error { ++ struct zpci_report_error_header header; ++ struct zpci_report_error_data data; ++} __packed; ++ ++#endif /* ZPCICTL_H */ +-- +2.21.3 + + +From c17b203ee85a63c812654e937f0c5cb3da6229e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:35:09 +0100 +Subject: [PATCH 08/44] zpcictl: Read device link to obtain device address + (#1639220) + +Description: zpcictl: Read device link to obtain device address +Symptom: Issuing zpcictl --report-error /dev/nvme0 leads to: + zpcictl: Could not open file /sys/dev/char/249:0/address: + No such file or directory +Problem: The sysfs attribute 'address' in /sys/dev/char/x:x/ is not + present on all kernel versions. +Solution: Read the device link using readlink() to obtain the device + address instead. +Reproduction: For example, run zpcictl --report-error /dev/nvme0 before + kernel 4.8. +--- + zpcictl/zpcictl.8 | 13 +++++---- + zpcictl/zpcictl.c | 72 +++++++++++++++++++++++++++++++---------------- + 2 files changed, 55 insertions(+), 30 deletions(-) + +diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 +index 4f8fcd8..41fab9a 100644 +--- a/zpcictl/zpcictl.8 ++++ b/zpcictl/zpcictl.8 +@@ -1,4 +1,4 @@ +-.\" Copyright 2017 IBM Corp. ++.\" Copyright IBM Corp. 2018 + .\" s390-tools is free software; you can redistribute it and/or modify + .\" it under the terms of the MIT license. See LICENSE for details. + .\" +@@ -30,9 +30,10 @@ zpcictl - Manage PCI devices on z Systems + . + . + .SH DESCRIPTION ++With + .B zpcictl +-is a tool for managing PCI devices on the IBM z Systems platform. It is +-especially used for reporting errorneous PCI devices to the service element. ++, you can manage PCI devices on the IBM z Systems platform. It is especially ++used for reporting erroneous PCI devices to the service element. + + .B Note: + For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent +@@ -44,7 +45,9 @@ for this to work. + .SH DEVICE + .B DEVICE + can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node +-of an NVMe device (e.g. /dev/nvme0). ++of an NVMe device (e.g. ++.I /dev/nvme0 ++). + . + . + .SH OPTIONS +@@ -52,7 +55,7 @@ of an NVMe device (e.g. /dev/nvme0). + .OD reset "" "DEVICE" + Reset + .I DEVICE +-and initiate a re-initialisation of the adapter. ++and initiate a re-initialization of the PCI device. + .PP + . + .OD deconfigure "" "DEVICE" +diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c +index 5f63b17..a9e38fb 100644 +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -104,6 +104,9 @@ static char *collect_smart_data(struct zpci_device *pdev) + char *cmd; + FILE *fd; + ++ if (!pdev->device) ++ return NULL; ++ + util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); + fd = popen(cmd, "r"); + if (!fd) +@@ -155,45 +158,60 @@ static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) + + static void sysfs_write_data(struct zpci_report_error *report, char *slot) + { ++ size_t r_size; + char *path; +- int fd, rc; ++ FILE *fp; ++ ++ r_size = sizeof(*report); + + path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); +- fd = open(path, O_WRONLY); +- if (!fd) ++ fp = fopen(path, "w"); ++ if (!fp) + fopen_err(path); +- rc = write(fd, report, sizeof(*report)); +- if (rc == -1) ++ if (fwrite(report, 1, r_size, fp) != r_size) + warnx("Could not write to file: %s: %s", path, strerror(errno)); +- if (close(fd)) ++ if (fclose(fp)) + warnx("Could not close file: %s: %s", path, strerror(errno)); + free(path); + } + +-static void sysfs_get_slot_addr(const char *dev, char *slot) ++/* lstat() doesn't work for sysfs files, so we have to work with a fixed size */ ++#define READLINK_SIZE 256 ++ ++static int sysfs_get_slot_addr(const char *dev, char *slot) + { ++ char device[READLINK_SIZE], *result; + unsigned int major, minor; + struct stat dev_stat; +- char addr[13]; ++ ssize_t len; + char *path; +- FILE *fp; + + if (stat(dev, &dev_stat) != 0) { +- errx(EXIT_FAILURE, "Could not get stat information for %s: %s", +- dev, strerror(errno)); ++ warnx("Could not get stat information for %s: %s", ++ dev, strerror(errno)); ++ return 0; + } + major = major(dev_stat.st_rdev); + minor = minor(dev_stat.st_rdev); + +- path = util_path_sysfs("dev/char/%u:%u/address", major, minor); +- fp = fopen(path, "r"); +- if (!fp) +- fopen_err(path); +- fscanf(fp, "%s", addr); +- fclose(fp); ++ path = util_path_sysfs("dev/char/%u:%u/device", major, minor); ++ len = readlink(path, device, READLINK_SIZE - 1); + free(path); ++ if (len != -1) { ++ device[len] = '\0'; ++ } else { ++ warnx("Could not read device link for %s", dev); ++ return 0; ++ } ++ ++ result = strrchr(device, '/'); ++ if (result) ++ result++; ++ else ++ result = device; ++ strcpy(slot, result); + +- strcpy(slot, addr); ++ return 1; + } + + static void get_device_node(struct zpci_device *pdev) +@@ -208,12 +226,13 @@ static void get_device_node(struct zpci_device *pdev) + if (count == -1) { + warnx("Could not read directory %s: %s", path, strerror(errno)); + free(path); +- exit(EXIT_FAILURE); ++ return; + } + + for (i = 0; i < count; i++) { + util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); +- sysfs_get_slot_addr(dev, slot); ++ if (!sysfs_get_slot_addr(dev, slot)) ++ continue; + if (strcmp(slot, pdev->slot) == 0) { + pdev->device = dev; + break; +@@ -240,11 +259,13 @@ static int device_exists(char *dev) + static void get_device_info(struct zpci_device *pdev, char *dev) + { + if (!device_exists(dev)) +- errx(EXIT_FAILURE, "Device %s not found", dev); ++ errx(EXIT_FAILURE, "Could not find device %s", dev); + if (is_blk_dev(dev)) + errx(EXIT_FAILURE, "Unsupported device type %s", dev); + if (is_char_dev(dev)) { +- sysfs_get_slot_addr(dev, pdev->slot); ++ if (!sysfs_get_slot_addr(dev, pdev->slot)) ++ errx(EXIT_FAILURE, ++ "Could not determine slot address for %s", dev); + pdev->device = dev; + } else { + strcpy(pdev->slot, dev); +@@ -254,9 +275,10 @@ static void get_device_info(struct zpci_device *pdev, char *dev) + pdev->fid = sysfs_read_value(pdev, "function_id"); + pdev->pchid = sysfs_read_value(pdev, "pchid"); + +- /* In case a slot address was specified, we still need to figure out +- * the device node for NVMe devices. Otherwise we won't be able to +- * collect S.M.A.R.T. data at a later point. ++ /* ++ * In case a slot address was specified, the device node for NVMe ++ * devices is still needed. Otherwise it won't be possible to collect ++ * S.M.A.R.T. data at a later point. + */ + if (!pdev->device && pdev->class == PCI_CLASS_NVME) + get_device_node(pdev); +-- +2.21.3 + + +From 8d8e0a970dbde0ca4486192aed806d837f080370 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:36:36 +0100 +Subject: [PATCH 09/44] zpcictl: Change wording of man-page and help output + (#1643451) + +Description: zpcictl: Change wording of man-page and help output +Symptom: - +Problem: Wording not clear enough and platform description is wrong. +Solution: Change wording and use the correct platform description. +Reproduction: - +Upstream-ID: aaaebb2030c80151ecac528f22cb9a52752b868c +--- + zpcictl/zpcictl.8 | 38 +++++++++++++++----------------------- + zpcictl/zpcictl.c | 15 ++++++++------- + 2 files changed, 23 insertions(+), 30 deletions(-) + +diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 +index 41fab9a..38aa344 100644 +--- a/zpcictl/zpcictl.8 ++++ b/zpcictl/zpcictl.8 +@@ -20,7 +20,7 @@ + .TH zpcictl 8 "Oct 2018" s390-tools zpcictl + . + .SH NAME +-zpcictl - Manage PCI devices on z Systems ++zpcictl - Manage PCI devices on IBM Z + . + . + .SH SYNOPSIS +@@ -30,50 +30,42 @@ zpcictl - Manage PCI devices on z Systems + . + . + .SH DESCRIPTION +-With ++Use + .B zpcictl +-, you can manage PCI devices on the IBM z Systems platform. It is especially +-used for reporting erroneous PCI devices to the service element. ++to manage PCI devices on the IBM Z platform. In particular, ++use this command to report defective PCI devices to the service element. + + .B Note: + For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent +-with any error handling action. The smartmontools are required to be installed +-for this to work. ++with any error handling action. For this extendend data collection, the ++smartmontools must be installed. + .PP + . + . + .SH DEVICE +-.B DEVICE +-can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node +-of an NVMe device (e.g. ++A PCI slot address (e.g. 0000:00:00.0) or the main device node of an NVMe ++device (e.g. + .I /dev/nvme0 + ). + . + . + .SH OPTIONS +-.SS Error Handling ++.SS Error Handling Options + .OD reset "" "DEVICE" +-Reset +-.I DEVICE +-and initiate a re-initialization of the PCI device. ++Reset and re-initialize the PCI device. + .PP + . + .OD deconfigure "" "DEVICE" +-De-configure +-.I DEVICE +-and prepare for any repair action. This action will move the +-PCI device from a configured to a reserved state. ++Deconfigure the PCI device and prepare for any repair action. This action ++changes the status of the PCI device from configured to reserved. + .PP + . + .OD report-error "" "DEVICE" +-Report any device error for +-.IR DEVICE . +-The +-.I DEVICE +-is marked as erroneous and no further action is initiated on it. ++Report any device error for the PCI device. ++The device is marked as defective but no further action is taken. + .PP + . +-.SS Misc ++.SS General Options + .OD help "h" "" + Print usage information, then exit. + .PP +diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c +index a9e38fb..7cfa0d3 100644 +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -27,8 +27,9 @@ + #define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" + + static const struct util_prg prg = { +- .desc = "Use zpcictl to manage PCI devices on s390\n" +- "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", ++ .desc = "Use zpcictl to manage PCI devices on IBM Z\n" ++ "DEVICE is the slot ID or node of the device " ++ "(e.g. 0000:00:00.0 or /dev/nvme0)", + .args = "DEVICE", + .copyright_vec = { + { +@@ -46,23 +47,23 @@ static const struct util_prg prg = { + #define OPT_REPORT_ERR 130 + + static struct util_opt opt_vec[] = { +- UTIL_OPT_SECTION("ERROR HANDLING"), ++ UTIL_OPT_SECTION("ERROR HANDLING OPTIONS"), + { + .option = { "reset", no_argument, NULL, OPT_RESET }, +- .desc = "Reset device", ++ .desc = "Reset the device", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + { + .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, +- .desc = "De-configure device and prepare for any repair action", ++ .desc = "Deconfigure the device to prepare for any repair action", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + { + .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, +- .desc = "Report device error to service element (SE)", ++ .desc = "Report a device error to the service element (SE)", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, +- UTIL_OPT_SECTION("MISC"), ++ UTIL_OPT_SECTION("GENERAL OPTIONS"), + UTIL_OPT_HELP, + UTIL_OPT_VERSION, + UTIL_OPT_END +-- +2.21.3 + + +From 916022e17d4cb189c0d83028ed09d3f4600046dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:37:34 +0100 +Subject: [PATCH 10/44] zdev: qeth BridgePort and VNICC attribute conflict + (#1643452) + +Description: zdev: qeth BridgePort and VNICC attribute conflict +Symptom: chzdev cannot set VNICC attributes due to a conflict with + BridgePort attributes. +Problem: Existing conflict checking always assumes a BridgePort and a + VNICC attribute are active. +Solution: Introduce a function that determines if BridgePort or VNICC + attributes are active and use only active attributes for conflict + checking. +Reproduction: Set VNICC attribute with chzdev w/o active BridgePort attributes. +Upstream-ID: ffe91d1b3082730905caee75b6ec8b05e3cf46a3 +--- + zdev/src/qeth.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/zdev/src/qeth.c b/zdev/src/qeth.c +index 46bc23d..6191ad1 100644 +--- a/zdev/src/qeth.c ++++ b/zdev/src/qeth.c +@@ -1171,6 +1171,37 @@ static exit_code_t check_ineffective_settings(struct setting_list *list, + return rc; + } + ++/* Check if a possibly conflicting setting is active in the configuration */ ++static bool conflict_setting_active(struct setting *s) ++{ ++ enum qeth_attr_group_type t; ++ ++ t = get_attr_group_type(s); ++ if (t != group_bridge && t != group_vnicc) { ++ /* Check BridgePort and VNICC attributes only */ ++ return false; ++ } ++ if (s->specified) { ++ /* Specified on the command line: We are strict here and do not ++ * allow to specify VNICC and BridgePort attributes in the same ++ * command to avoid issues when attributes are enabled/disabled ++ * in the wrong order. Example: disable VNICC and enable ++ * BridgePort in the same command would result in an error ++ * because BridgePort attributes are set first. ++ */ ++ return true; ++ } ++ if (attrib_match_default(s->attrib, s->value)) { ++ /* Not active if set to default value */ ++ return false; ++ } ++ if (s->actual_value && strncmp(s->actual_value, "n/a", 3) == 0) { ++ /* Not active if in n/a state (conflicting attribute set) */ ++ return false; ++ } ++ return true; ++} ++ + /* Check if there are conflicting attribute settings */ + static exit_code_t check_conflicting_settings(struct setting_list *list) + { +@@ -1182,6 +1213,8 @@ static exit_code_t check_conflicting_settings(struct setting_list *list) + util_list_iterate(&list->list, s) { + if (s->removed) + continue; ++ if (!conflict_setting_active(s)) ++ continue; + t = get_attr_group_type(s); + if (t == group_bridge && (!bridge || !bridge->specified)) + bridge = s; +-- +2.21.3 + + +From e2916bdb493e9565d74b0334fa45f17c6f0f730c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:38:30 +0100 +Subject: [PATCH 11/44] qethqoat: add OSA-Express7S support (#1644384) + +Description: qethqoat: add OSA-Express7S support +Symptom: qethqoat fails to report HW generation and link speed for + OSA-Express7S network card. +Problem: Missing identifiers for new values in the QUERY OAT data. +Solution: Add identifiers for card generation and link speed. +Reproduction: Run qethqoat on an OSA-Express7S, check the output for + 'OSA Generation' and 'Port speed/mode'. +Upstream-ID: 20145b6d06debd47944bff0a471d17e5eba07010 +--- + qethqoat/qethqoat.c | 6 ++++++ + qethqoat/qethqoat.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/qethqoat/qethqoat.c b/qethqoat/qethqoat.c +index 6a30d1d..7110c28 100644 +--- a/qethqoat/qethqoat.c ++++ b/qethqoat/qethqoat.c +@@ -208,6 +208,9 @@ static void print_physical(struct qeth_qoat_physical *phdr) + case OAT_OSA_GEN_OSAE6S: + osagen = "OSA-Express6S"; + break; ++ case OAT_OSA_GEN_OSAE7S: ++ osagen = "OSA-Express7S"; ++ break; + default: + sprintf(tmp, "unknown (0x%x)", phdr->osa_gen); + osagen = tmp; +@@ -239,6 +242,9 @@ static void print_physical(struct qeth_qoat_physical *phdr) + case OAT_PORT_SPEED_10gbs_full: + speed = "10 Gb/s / full duplex"; + break; ++ case OAT_PORT_SPEED_25gbs_full: ++ speed = "25 Gb/s / full duplex"; ++ break; + case OAT_PORT_SPEED_UNKNOWN: + speed = "unknown / unknown"; + break; +diff --git a/qethqoat/qethqoat.h b/qethqoat/qethqoat.h +index dd7e992..e692937 100644 +--- a/qethqoat/qethqoat.h ++++ b/qethqoat/qethqoat.h +@@ -58,6 +58,7 @@ struct qeth_qoat_physical { + #define OAT_OSA_GEN_OSAE4S 0x02 + #define OAT_OSA_GEN_OSAE5S 0x03 + #define OAT_OSA_GEN_OSAE6S 0x04 ++#define OAT_OSA_GEN_OSAE7S 0x05 + __u8 osa_gen; + #define OAT_PORT_SPEED_UNKNOWN 0x00 + #define OAT_PORT_SPEED_10mbs_half 0x01 +@@ -68,6 +69,7 @@ struct qeth_qoat_physical { + #define OAT_PORT_SPEED_1000mbs_full 0x06 + #define OAT_PORT_SPEED_NA 0x07 + #define OAT_PORT_SPEED_10gbs_full 0x08 ++#define OAT_PORT_SPEED_25gbs_full 0x0A + __u8 port_speed; + #define OAT_PORT_MEDIA_COPPER 0x01 + #define OAT_PORT_MEDIA_MULTI_MODE 0x02 +-- +2.21.3 + + +From ba7eb5cd9d858f2ff8bd29e0b610337896587b94 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:39:35 +0100 +Subject: [PATCH 12/44] zcryptctl: add zcryptctl to manage multiple zcrypt + nodes (#1646354) + +Summary: zcryptctl: add zcryptctl to manage multiple zcrypt nodes +Description: There is a new zcrypt kernel feature which provides + multiple customizable device nodes for the zcrypt + device driver. Here is the userspace part of this + which adds a new application zcryptctl for user + friendly management of this feature. +Upstream-ID: f05f7d656b13c3904f0c55e86ebe9e9b19fcd222 +--- + zconf/zcrypt/Makefile | 7 +- + zconf/zcrypt/zcryptctl.8 | 147 ++++++ + zconf/zcrypt/zcryptctl.c | 1030 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1182 insertions(+), 2 deletions(-) + create mode 100644 zconf/zcrypt/zcryptctl.8 + create mode 100644 zconf/zcrypt/zcryptctl.c + +diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile +index 698e148..d075f34 100644 +--- a/zconf/zcrypt/Makefile ++++ b/zconf/zcrypt/Makefile +@@ -1,21 +1,24 @@ + include ../../common.mak + +-all: chzcrypt lszcrypt ++all: chzcrypt lszcrypt zcryptctl + + libs = $(rootdir)/libutil/libutil.a + + chzcrypt: chzcrypt.o misc.o $(libs) + lszcrypt: lszcrypt.o misc.o $(libs) ++zcryptctl: zcryptctl.o misc.o $(libs) + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8 ++ $(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8 + + clean: +- rm -f *.o chzcrypt lszcrypt ++ rm -f *.o chzcrypt lszcrypt zcryptctl + + .PHONY: all install clean +diff --git a/zconf/zcrypt/zcryptctl.8 b/zconf/zcrypt/zcryptctl.8 +new file mode 100644 +index 0000000..5d098da +--- /dev/null ++++ b/zconf/zcrypt/zcryptctl.8 +@@ -0,0 +1,147 @@ ++.\" zcryptctl.8 ++.\" ++.\" Copyright 2018 IBM Corp. ++.\" s390-tools is free software; you can redistribute it and/or modify ++.\" it under the terms of the MIT license. See LICENSE for details. ++.\" ++.\" use ++.\" groff -man -Tutf8 zcryptctl.8 ++.\" or ++.\" nroff -man zcryptctl.8 ++.\" to process this source ++.\" ++.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools" ++.SH NAME ++zcryptctl \- display information and administrate zcrypt multiple device nodes ++.SH SYNOPSIS ++.TP 8 ++.B zcryptctl list ++.TP ++.B zcryptctl create ++.R [ ++.I node-name ++.R ] ++.TP ++.B zcryptctl destroy ++.I node-name ++.TP ++.B zcryptctl addap ++.R | ++.B delap ++.I node-name adapter-nr ++.TP ++.B zcryptctl adddom ++.R | ++.B deldom ++.I node-name domain-nr ++.TP ++.B zcryptctl addioctl ++.R | ++.B delioctl ++.I node-name ioctl-term ++.TP ++.B zcryptctl config ++.I config-file ++.TP ++.B zcryptctl listconfig ++.SH DESCRIPTION ++The ++.B zcryptctl ++command displays information and maintains the multi device node ++extension for the zcrypt device driver. ++.P ++With the multi device node extension you can create and configure ++additional zcrypt device nodes which can be used as alternate device ++nodes to access the crypto hardware provided by the zcrypt device ++driver. Each zcrypt device node can be restricted in terms of crypto ++cards, domains, and available ioctls. Such a device node can be used ++as a base for container solutions like Docker to control and restrict ++the access to crypto resources. ++.SH COMMANDS ++.TP 8 ++.B zcryptctl list ++Show all the additional device nodes that are currently active. ++.TP ++.B zcryptctl create ++.R [ ++.I node-name ++.R ] ++Create a new zcrypt device node. The \fInode-name\fP might be given ++and needs to be unique and not in use. If there is no node name ++provided, the zcrypt device driver will create a new one with pattern ++zcrypt_\fIx\fP, with \fIx\fP being the next free number. Up to 256 ++additional device nodes can be created. The newly created additional ++device node appears in /dev and has read and write permissions enabled ++only for root. By default all adapters, domains and ioctls are ++initially disabled on this new device node. ++.TP ++.B zcryptctl destroy ++.I node-name ++Destroy an additional zcrypt device node. The device node is only ++marked for disposal and destroyed when it is no longer used. ++.TP ++.B zcryptctl addap ++.R | ++.B delap ++.I node-name adapter-nr ++Update the filter for the specified zcrypt device node and add or ++delete a crypto adapter to be accessible via this node. The symbol ++\fBALL\fP can be used to enable or disable all adapters. ++.TP ++.B zcryptctl adddom ++.R | ++.B deldom ++.I node-name domain-nr ++Update the filter for the specified zcrypt device node and add or ++delete a domain to be accessible through this node. The symbol ++\fBALL\fP can be used to enable or disable all domains. ++.TP ++.B zcryptctl addioctl ++.R | ++.B delioctl ++.I node-name ioctl-term ++Update the filter for the specified zcrypt device node and add or ++delete an ioctl. The ioctl might be specified as symbolic string (one ++of \fBICARSAMODEXPO\fP, \fBICARSACRT\fP, \fBZSECSENDCPRB\fP, ++\fBZSENDEP11CPRB\fP, \fBZCRYPT_DEVICE_STATUS\fP, ++\fBZCRYPT_STATUS_MASK\fP, \fBZCRYPT_QDEPTH_MASK\fP, ++\fBZCRYPT_PERDEV_REQCNT\fP) or numeric value in the range 0-255 and ++the symbol \fBALL\fP can be used to include all ioctls. ++.TP ++.B zcryptctl config ++.I config-file ++Process a config file. The given configuration file is read line by ++line and the settings are applied. Syntax is simple: ++.RS ++.IP "node=" ++.IP "aps=" ++.IP "doms=" ++.IP "ioctls=" ++.LP ++Empty lines are ignored and the '#' marks the rest of the ++line as comment. ++.LP ++The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP, ++\fBdoms=\fP and \fBioctls=\fP lines customize the previously created ++node. The symbol \fBALL\fP is also recognized for aps, doms, and ++ioctls. ++.LP ++Each action must fit into one line, spreading over multiple lines is ++not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP ++and \fBioctls=\fP lines to customize the very same node. ++.LP ++Processing stops when a line cannot be parsed or the current action ++fails. In this case the exit status is non zero but the successful ++actions until the failure occurs are not rolled back. ++.RE ++.TP ++.B zcryptctl listconfig ++List the current configuration in a form suitable for input to the ++\fBzcryptctl config\fP command. ++.LP ++.SH EXIT STATUS ++On successful completion of the command the exit status is 0. A non ++zero return code (and some kind of failure message) is emitted if the ++processing could not complete successful. ++.SH SEE ALSO ++\fBlszcrypt\fR(8) +diff --git a/zconf/zcrypt/zcryptctl.c b/zconf/zcrypt/zcryptctl.c +new file mode 100644 +index 0000000..8326a08 +--- /dev/null ++++ b/zconf/zcrypt/zcryptctl.c +@@ -0,0 +1,1030 @@ ++/* ++ * zcryptctl - Maintain zcrypt multi device nodes. ++ * ++ * by Harald Freudenberger ++ * Copyright IBM Corp. 2018 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/util_base.h" ++#include "lib/util_file.h" ++#include "lib/util_opt.h" ++#include "lib/util_panic.h" ++#include "lib/util_path.h" ++#include "lib/util_prg.h" ++#include "lib/util_proc.h" ++#include "lib/util_rec.h" ++#include "lib/util_scandir.h" ++#include "lib/zt_common.h" ++ ++#define MAX_ZDEV_IOCTLS 256 ++#define ZCRYPT_NAME "zcrypt" ++#define MAX_ZDEV_CARDIDS_EXT 256 ++#define MAX_ZDEV_DOMAINS_EXT 256 ++#define ZCRYPTDEVICE "/dev/z90crypt" ++#define _UNUSED_ __attribute__((unused)) ++ ++/* ++ * Currently known commands ++ */ ++#define CMD_LIST 0x0001 ++#define CMD_CREATE 0x0002 ++#define CMD_DESTROY 0x0003 ++#define CMD_ADD_AP 0x0004 ++#define CMD_DEL_AP 0x0005 ++#define CMD_ADD_DOM 0x0006 ++#define CMD_DEL_DOM 0x0007 ++#define CMD_ADD_IOCTL 0x0008 ++#define CMD_DEL_IOCTL 0x0009 ++#define CMD_CONFIG 0x000A ++#define CMD_LISTCONFIG 0x000B ++ ++/* ++ * Program configuration ++ */ ++static const struct util_prg prg = { ++ .args = "", ++ .command_args = "COMMAND [COMMAND-PARAMS]", ++ .desc = "Display and administrate zcrypt multiple device nodes.", ++ .copyright_vec = { ++ { ++ .owner = "IBM Corp.", ++ .pub_first = 2018, ++ .pub_last = 2018, ++ }, ++ UTIL_PRG_COPYRIGHT_END ++ } ++}; ++ ++static struct util_opt opt_vec[] = { ++ UTIL_OPT_HELP, ++ UTIL_OPT_VERSION, ++ UTIL_OPT_END ++}; ++ ++/* ++ * List of currently known and supported ioctls ++ */ ++static struct zcryptctl_ioctls_s { ++ int nr; ++ const char *name; ++} zcryptctl_ioctls[] = { ++ { ++ .name = "ICARSAMODEXPO", ++ .nr = 0x05, ++ }, ++ { ++ .name = "ICARSACRT", ++ .nr = 0x06, ++ }, ++ { ++ .name = "ZSECSENDCPRB", ++ .nr = 0x81, ++ }, ++ { ++ .name = "ZSENDEP11CPRB", ++ .nr = 0x04, ++ }, ++ { ++ .name = "ZCRYPT_DEVICE_STATUS", ++ .nr = 0x5f, ++ }, ++ { ++ .name = "ZCRYPT_STATUS_MASK", ++ .nr = 0x58, ++ }, ++ { ++ .name = "ZCRYPT_QDEPTH_MASK", ++ .nr = 0x59, ++ }, ++ { ++ .name = "ZCRYPT_PERDEV_REQCNT", ++ .nr = 0x5a, ++ }, ++ { ++ .name = NULL, ++ .nr = 0, ++ }, ++}; ++ ++static int ioctlstr2value(const char *str) ++{ ++ int i; ++ ++ for (i = 0; zcryptctl_ioctls[i].name; i++) ++ if (strcasecmp(str, zcryptctl_ioctls[i].name) == 0) ++ return zcryptctl_ioctls[i].nr; ++ ++ return -1; ++} ++ ++static const char *value2ioctlstr(int value) ++{ ++ int i; ++ ++ for (i = 0; zcryptctl_ioctls[i].name; i++) ++ if (value == zcryptctl_ioctls[i].nr) ++ return zcryptctl_ioctls[i].name; ++ ++ return NULL; ++} ++ ++static int check_nodename(const char *nodename) ++{ ++ struct stat sb; ++ const char *node; ++ char pathname[PATH_MAX]; ++ ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ snprintf(pathname, sizeof(pathname), "/dev/%s", node); ++ pathname[sizeof(pathname) - 1] = '\0'; ++ if (stat(pathname, &sb) != 0) ++ return -1; ++ if (!S_ISCHR(sb.st_mode)) ++ return -2; ++ ++ return 0; ++} ++ ++static int check_zcrypt_class_dir(void) ++{ ++ int rc = 0; ++ char *afile; ++ ++ afile = util_path_sysfs("class/%s", ZCRYPT_NAME); ++ if (!util_path_is_dir(afile)) ++ rc = -1; ++ ++ free(afile); ++ return rc; ++} ++ ++static int fetch_major_minor(const char *nodename, int *major, int *minor) ++{ ++ FILE *f; ++ int rc = 0; ++ char *afile; ++ const char *node; ++ ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ afile = util_path_sysfs("class/%s/%s/dev", ZCRYPT_NAME, node); ++ f = fopen(afile, "r"); ++ if (!f) { ++ rc = -1; ++ goto out; ++ } ++ if (fscanf(f, "%i:%i", major, minor) != 2) { ++ fclose(f); ++ rc = -2; ++ goto out; ++ } ++ fclose(f); ++ ++out: ++ free(afile); ++ return rc; ++} ++ ++static int write_dn_attr(const char *nodename, const char *attr, ++ const char *value) ++{ ++ FILE *f; ++ int rc = 0; ++ char *afile; ++ const char *node; ++ ++ if (nodename) { ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ afile = util_path_sysfs("class/%s/%s/%s", ++ ZCRYPT_NAME, node, attr); ++ } else ++ afile = util_path_sysfs("class/%s/%s", ZCRYPT_NAME, attr); ++ f = fopen(afile, "w"); ++ if (!f) { ++ rc = -1; ++ goto out; ++ } ++ if (fprintf(f, "%s\n", value) < 0) { ++ fclose(f); ++ rc = -2; ++ goto out; ++ } ++ fflush(f); ++ if (ferror(f)) { ++ fclose(f); ++ rc = -2; ++ goto out; ++ } ++ ++ fclose(f); ++ ++out: ++ free(afile); ++ return rc; ++} ++ ++static int read_dn_attr(const char *nodename, const char *attr, ++ char *value, int valuelen) ++{ ++ int rc; ++ FILE *f; ++ char *afile; ++ const char *node; ++ ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ afile = util_path_sysfs("class/%s/%s/%s", ZCRYPT_NAME, node, attr); ++ f = fopen(afile, "r"); ++ if (!f) { ++ rc = -1; ++ goto out; ++ } ++ value = fgets(value, valuelen, f); ++ fclose(f); ++ rc = value ? 0 : -2; ++ ++out: ++ free(afile); ++ return rc; ++} ++ ++static int test_bit(int n, const char *hexbytestr) ++{ ++ char c; ++ int v, i = 0; ++ ++ if (strncmp(hexbytestr, "0x", 2) == 0) ++ i += 2; ++ c = hexbytestr[i + n / 4]; ++ if (c >= '0' && c <= '9') ++ v = c - '0'; ++ else if (c >= 'a' && c <= 'f') ++ v = 10 + c - 'a'; ++ else if (c >= 'A' && c <= 'F') ++ v = 10 + c - 'A'; ++ else ++ errx(EXIT_FAILURE, ++ "Could not parse hex digit '%c'", c); ++ ++ return v & (1 << (3 - (n % 4))); ++} ++ ++static int cmd_list(int cmd, ++ const char *node _UNUSED_, ++ const char *arg _UNUSED_) ++{ ++ DIR *dir; ++ char *dirname; ++ const char *p; ++ struct dirent *de; ++ int i, n, major, minor, count = 0; ++ char buf[80], tab = (cmd == CMD_LISTCONFIG ? ' ' : '\t'); ++ ++ dirname = util_path_sysfs("class/%s", ZCRYPT_NAME); ++ dir = opendir(dirname); ++ if (!dir) ++ errx(EXIT_FAILURE, ++ "Could not read directory '%s' errno=%d (%s)", ++ dirname, errno, strerror(errno)); ++ while ((de = readdir(dir)) != NULL) { ++ if (de->d_name[0] == '.' || de->d_type == DT_REG) ++ continue; ++ if (fetch_major_minor(de->d_name, &major, &minor) != 0) ++ errx(EXIT_FAILURE, ++ "Could not fetch major/minor from sysfs for zcrypt node '%s'", ++ de->d_name); ++ if (cmd == CMD_LISTCONFIG) { ++ printf("node = %s\n", de->d_name); ++ printf(" aps ="); ++ } else { ++ printf("zcrypt node name:\t%s\n", de->d_name); ++ printf(" device node:\t/dev/%s\n", de->d_name); ++ printf(" major:minor:\t%d:%d\n", major, minor); ++ printf(" adapter:"); ++ } ++ if (read_dn_attr(de->d_name, "apmask", buf, sizeof(buf)) != 0) ++ errx(EXIT_FAILURE, ++ "Could not fetch apmask attribute from sysfs for zcrypt node '%s'", ++ de->d_name); ++ for (i = n = 0; i < MAX_ZDEV_CARDIDS_EXT; i++) ++ if (test_bit(i, buf)) ++ printf("%c%d", n++ == 0 ? tab : ',', i); ++ putchar('\n'); ++ if (cmd == CMD_LISTCONFIG) ++ printf(" doms ="); ++ else ++ printf(" domains:"); ++ if (read_dn_attr(de->d_name, "aqmask", buf, sizeof(buf)) != 0) ++ errx(EXIT_FAILURE, ++ "Could not fetch aqmask attribute from sysfs for zcrypt node '%s'", ++ de->d_name); ++ for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++) ++ if (test_bit(i, buf)) ++ printf("%c%d", n++ == 0 ? tab : ',', i); ++ putchar('\n'); ++ if (cmd == CMD_LISTCONFIG) ++ printf(" ioctls ="); ++ else ++ printf(" ioctls:"); ++ if (read_dn_attr(de->d_name, "ioctlmask", ++ buf, sizeof(buf)) != 0) ++ errx(EXIT_FAILURE, ++ "Could not fetch ioctlmask attribute from sysfs for zcrypt node '%s'", ++ de->d_name); ++ for (i = n = 0; i < MAX_ZDEV_IOCTLS; i++) { ++ if (test_bit(i, buf)) { ++ p = value2ioctlstr(i); ++ if (p) ++ printf("%c%s", ++ n++ == 0 ? tab : ',', p); ++ else ++ printf("%c%d", ++ n++ == 0 ? tab : ',', i); ++ } ++ } ++ putchar('\n'); ++ count++; ++ } ++ closedir(dir); ++ ++ if (count == 0) ++ printf("No additional zcrypt device nodes defined\n"); ++ ++ return 0; ++} ++ ++static int cmd_create(int cmd _UNUSED_, ++ const char *nodename, ++ const char *arg _UNUSED_) ++{ ++ int rc; ++ const char *node; ++ char buf[PATH_MAX]; ++ ++ if (nodename) { ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ strncpy(buf, node, sizeof(buf) - 1); ++ } else ++ strncpy(buf, "\n", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = 0; ++ ++ rc = write_dn_attr(NULL, "create", buf); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not write into sysfs entry to create zdev node"); ++ ++ printf("Device node created\n"); ++ ++ return 0; ++} ++ ++static int cmd_destroy(int cmd _UNUSED_, ++ const char *nodename, ++ const char *arg _UNUSED_) ++{ ++ int rc; ++ struct stat sb; ++ const char *node; ++ char pathname[PATH_MAX]; ++ ++ node = strrchr(nodename, '/'); ++ node = node ? node + 1 : nodename; ++ snprintf(pathname, sizeof(pathname), "/dev/%s", node); ++ pathname[sizeof(pathname) - 1] = '\0'; ++ rc = stat(pathname, &sb); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not check status for '%s'", pathname); ++ if (!S_ISCHR(sb.st_mode)) ++ errx(EXIT_FAILURE, ++ "File '%s' is not a character device node", pathname); ++ ++ rc = write_dn_attr(NULL, "destroy", node); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not write into sysfs entry to destroy zdev node '%s'", ++ node); ++ ++ printf("Device node '%s' marked for destruction\n", node); ++ ++ return 0; ++} ++ ++static void add_del_ap(int cmd, const char *node, int ap) ++{ ++ int rc; ++ char buf[PATH_MAX]; ++ ++ if (cmd == CMD_ADD_AP) ++ sprintf(buf, "+%d", ap); ++ else ++ sprintf(buf, "-%d", ap); ++ rc = write_dn_attr(node, "apmask", buf); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not write into sysfs entry to %s adapter %d for zdev node '%s'", ++ cmd == CMD_ADD_AP ? "add" : "remove", ap, node); ++} ++ ++static int cmd_add_del_ap(int cmd, const char *node, const char *arg) ++{ ++ int ap, all = 0; ++ ++ if (strcasecmp(arg, "ALL") == 0) { ++ all = 1; ++ } else { ++ if (sscanf(arg, "%i", &ap) != 1) ++ errx(EXIT_FAILURE, ++ "Invalid adapter argument '%s'", arg); ++ if (ap < 0 || ap >= MAX_ZDEV_CARDIDS_EXT) ++ errx(EXIT_FAILURE, ++ "Adapter argument '%s' out of range [0..%d]", ++ arg, MAX_ZDEV_CARDIDS_EXT - 1); ++ } ++ ++ if (!all) { ++ add_del_ap(cmd, node, ap); ++ printf("Adapter %d %s\n", ap, ++ (cmd == CMD_ADD_AP ? "added" : "removed")); ++ } else { ++ for (ap = 0; ap < MAX_ZDEV_CARDIDS_EXT; ap++) ++ add_del_ap(cmd, node, ap); ++ printf("All adapters %s\n", ++ (cmd == CMD_ADD_AP ? "added" : "removed")); ++ } ++ ++ return 0; ++} ++ ++static void add_del_dom(int cmd, const char *node, int dom) ++{ ++ int rc; ++ char buf[PATH_MAX]; ++ ++ if (cmd == CMD_ADD_DOM) ++ sprintf(buf, "+%d", dom); ++ else ++ sprintf(buf, "-%d", dom); ++ rc = write_dn_attr(node, "aqmask", buf); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not write into sysfs entry to %s domain %d for zdev node '%s'", ++ cmd == CMD_ADD_DOM ? "add" : "remove", dom, node); ++} ++ ++static int cmd_add_del_dom(int cmd, const char *node, const char *arg) ++{ ++ int dom, all = 0; ++ ++ if (strcasecmp(arg, "ALL") == 0) { ++ all = 1; ++ } else { ++ if (sscanf(arg, "%i", &dom) != 1) ++ errx(EXIT_FAILURE, ++ "Invalid domain argument '%s'", arg); ++ if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT) ++ errx(EXIT_FAILURE, ++ "Domain argument '%s' out of range [0..%d]", ++ arg, MAX_ZDEV_DOMAINS_EXT - 1); ++ } ++ ++ if (!all) { ++ add_del_dom(cmd, node, dom); ++ printf("Domain %d %s\n", dom, ++ (cmd == CMD_ADD_DOM ? "added" : "removed")); ++ } else { ++ for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++) ++ add_del_dom(cmd, node, dom); ++ printf("All domains %s\n", ++ (cmd == CMD_ADD_DOM ? "added" : "removed")); ++ } ++ ++ return 0; ++} ++ ++static void add_del_ioctl(int cmd, const char *node, int ioctlnr) ++{ ++ int rc; ++ char buf[PATH_MAX]; ++ ++ if (cmd == CMD_ADD_IOCTL) ++ sprintf(buf, "+%d", ioctlnr); ++ else ++ sprintf(buf, "-%d", ioctlnr); ++ rc = write_dn_attr(node, "ioctlmask", buf); ++ if (rc != 0) ++ errx(EXIT_FAILURE, ++ "Could not write into sysfs entry to %s ioctl %d for zdev node '%s'", ++ cmd == CMD_ADD_IOCTL ? "add" : "remove", ioctlnr, node); ++} ++ ++static int cmd_add_del_ioctl(int cmd, const char *node, const char *arg) ++{ ++ int ioctlnr, all = 0; ++ ++ if (strcasecmp(arg, "ALL") == 0) { ++ all = 1; ++ } else { ++ ioctlnr = ioctlstr2value(arg); ++ if (ioctlnr < 0) ++ if (sscanf(arg, "%i", &ioctlnr) != 1) ++ errx(EXIT_FAILURE, ++ "Invalid ioctl argument '%s'", arg); ++ if (ioctlnr < 0 || ioctlnr >= MAX_ZDEV_IOCTLS) ++ errx(EXIT_FAILURE, ++ "Ioctl argument '%s' out of range [0..%d]", ++ arg, MAX_ZDEV_IOCTLS - 1); ++ } ++ ++ if (!all) { ++ add_del_ioctl(cmd, node, ioctlnr); ++ printf("Ioctl %s %s\n", arg, ++ (cmd == CMD_ADD_IOCTL ? "added" : "removed")); ++ } else { ++ for (ioctlnr = 0; ioctlnr < MAX_ZDEV_IOCTLS; ioctlnr++) ++ add_del_ioctl(cmd, node, ioctlnr); ++ printf("All Ioctls %s\n", ++ (cmd == CMD_ADD_IOCTL ? "added" : "removed")); ++ } ++ ++ return 0; ++} ++ ++static int _match_keyword(char **p, const char *keyword) ++{ ++ int n = strlen(keyword); ++ ++ if (strncmp(*p, keyword, n) == 0) { ++ *p += n; ++ return n; ++ } ++ ++ return 0; ++} ++ ++static int _match_character(char **p, char c) ++{ ++ char *q = *p; ++ ++ while (isblank(*q)) ++ q++; ++ if (*q != c) ++ return 0; ++ q++; ++ while (isblank(*q)) ++ q++; ++ *p = q; ++ ++ return 1; ++} ++ ++static int _match_string(char **p, char *buf) ++{ ++ int n = 0; ++ char *q = *p; ++ ++ while (isblank(*q)) ++ q++; ++ while (*q && *q != ',' && !isspace(*q)) { ++ buf[n++] = *q; ++ q++; ++ } ++ while (isblank(*q)) ++ q++; ++ ++ if (n > 0) { ++ buf[n] = '\0'; ++ *p = q; ++ } ++ ++ return n; ++} ++ ++static int cmd_config(int cmd _UNUSED_, ++ const char *nodename _UNUSED_, ++ const char *arg) ++{ ++ ssize_t n; ++ size_t linesize = 0; ++ int nr = 0, havenode = 0; ++ FILE *f = fopen(arg, "r"); ++ char *p, *line = NULL, node[128], buf[128]; ++ ++ if (!f) ++ errx(EXIT_FAILURE, ++ "Could not open file '%s'", arg); ++ ++ while ((n = getline(&line, &linesize, f)) != -1) { ++ nr++; ++ p = line; ++ while (isspace(*p)) ++ p++; ++ if (*p == '\0' || *p == '#') ++ continue; ++ if (_match_keyword(&p, "node")) { ++ if (!_match_character(&p, '=')) ++ errx(EXIT_FAILURE, ++ "Missing '=' at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ if (!_match_string(&p, node)) ++ errx(EXIT_FAILURE, ++ "Missing node name at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ cmd_create(CMD_CREATE, node, NULL); ++ havenode = 1; ++ } else if (_match_keyword(&p, "aps")) { ++ if (!havenode) ++ errx(EXIT_FAILURE, ++ "Missing node=... before processing any aps=... statements in line %d '%s'", ++ nr, line); ++ if (!_match_character(&p, '=')) ++ errx(EXIT_FAILURE, ++ "Missing '=' at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ while (1) { ++ while (isspace(*p)) ++ p++; ++ if (*p == '\0' || *p == '#') ++ break; ++ if (!_match_string(&p, buf)) ++ errx(EXIT_FAILURE, ++ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ cmd_add_del_ap(CMD_ADD_AP, node, buf); ++ while (isblank(*p) || *p == ',') ++ p++; ++ } ++ } else if (_match_keyword(&p, "doms")) { ++ if (!havenode) ++ errx(EXIT_FAILURE, ++ "Missing node=... before processing any doms=... statements in line %d '%s'", ++ nr, line); ++ if (!_match_character(&p, '=')) ++ errx(EXIT_FAILURE, ++ "Missing '=' at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ while (1) { ++ while (isspace(*p)) ++ p++; ++ if (*p == '\0' || *p == '#') ++ break; ++ if (!_match_string(&p, buf)) ++ errx(EXIT_FAILURE, ++ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ cmd_add_del_dom(CMD_ADD_DOM, node, buf); ++ while (isblank(*p) || *p == ',') ++ p++; ++ } ++ } else if (_match_keyword(&p, "ioctls")) { ++ if (!havenode) ++ errx(EXIT_FAILURE, ++ "Missing node=... before processing any ioctls=... statements in line %d '%s'", ++ nr, line); ++ if (!_match_character(&p, '=')) ++ errx(EXIT_FAILURE, ++ "Missing '=' at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ while (1) { ++ while (isspace(*p)) ++ p++; ++ if (*p == '\0' || *p == '#') ++ break; ++ if (!_match_string(&p, buf)) ++ errx(EXIT_FAILURE, ++ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ cmd_add_del_ioctl(CMD_ADD_IOCTL, node, buf); ++ while (isblank(*p) || *p == ',') ++ p++; ++ } ++ } else ++ errx(EXIT_FAILURE, ++ "Unknown keyword '%-8.8s...' in line %d '%s'", ++ p, nr, line); ++ } ++ ++ free(line); ++ fclose(f); ++ ++ return 0; ++} ++ ++static struct zcryptctl_cmds_s { ++ int cmd; ++ const char *usage; ++ const char *command; ++ const char *description; ++ int (*function)(int cmd, const char *node, const char *arg); ++} zcryptctl_cmds[] = { ++ { ++ .cmd = CMD_LIST, ++ .command = "list", ++ .function = cmd_list, ++ .usage = "zcryptctl list", ++ .description = ++ "List all currently known additional zcrypt device nodes.", ++ }, ++ { ++ .cmd = CMD_CREATE, ++ .command = "create", ++ .function = cmd_create, ++ .usage = "zcryptctl create [nodename]", ++ .description = ++ "Create a new zcrypt device node.\n" ++ "The node-name might be given and needs to be unique and not\n" ++ "in use. If there is no node name provided, the zcrypt device\n" ++ "driver will create a new one with pattern zcrypt_\n" ++ "with being the next free number. By default all\n" ++ "adapters, domains and ioctls are initially disabled on this\n" ++ "new device node." ++ }, ++ { ++ .cmd = CMD_DESTROY, ++ .command = "destroy", ++ .function = cmd_destroy, ++ .usage = "zcryptctl destroy ", ++ .description = ++ "Destroy an additional zcrypt device node.\n" ++ "Mark the given zcrypt device node as disposable. The removal\n" ++ "will take place when it is no longer used.", ++ }, ++ { ++ .cmd = CMD_ADD_AP, ++ .command = "addap", ++ .function = cmd_add_del_ap, ++ .usage = "zcryptctl addap ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "add an crypto adapter to be accessible via this node. The\n" ++ "adapter argument may be a number in the range 0-255 or the\n" ++ "symbol ALL.", ++ }, ++ { ++ .cmd = CMD_DEL_AP, ++ .command = "delap", ++ .function = cmd_add_del_ap, ++ .usage = "zcryptctl delap ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "remove a crypto adapter from the allowed adapters list. The\n" ++ "adapter argument may be a number in the range 0-255 or the\n" ++ "symbol ALL.", ++ }, ++ { ++ .cmd = CMD_ADD_DOM, ++ .command = "adddom", ++ .function = cmd_add_del_dom, ++ .usage = "zcryptctl adddom ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "add a crypto domain to be accessible via this node. The\n" ++ "domain argument may be a number in the range 0-255 or the\n" ++ "symbol ALL.", ++ }, ++ { ++ .cmd = CMD_DEL_DOM, ++ .command = "deldom", ++ .function = cmd_add_del_dom, ++ .usage = "zcryptctl deldom ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "remove a crypto domain from the allowed domains list. The\n" ++ "domain argument may be a number in the range 0-255 or the\n" ++ "symbol ALL.", ++ }, ++ { ++ .cmd = CMD_ADD_IOCTL, ++ .command = "addioctl", ++ .function = cmd_add_del_ioctl, ++ .usage = "zcryptctl addioctl ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "add an ioctl number to be accessible via this node. The\n" ++ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n" ++ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n" ++ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n" ++ "or a number in the range 0-255 or the symbol ALL.", ++ }, ++ { ++ .cmd = CMD_DEL_IOCTL, ++ .command = "delioctl", ++ .function = cmd_add_del_ioctl, ++ .usage = "zcryptctl delioctl ", ++ .description = ++ "Update the filter for the specified zcrypt device node and\n" ++ "remove an ioctl number from the allowed ioctls list. The\n" ++ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n" ++ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n" ++ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n" ++ "or a number in the range 0-255 or the symbol ALL.", ++ }, ++ { ++ .cmd = CMD_CONFIG, ++ .command = "config", ++ .function = cmd_config, ++ .usage = "zcryptctl config ", ++ .description = ++ "Process a config file. The given config file is read line by\n" ++ "line and the settings are applied. Syntax is simple:\n" ++ " node=\n" ++ " aps=\n" ++ " doms=\n" ++ " ioctls=\n" ++ "Empty lines are ignored and the '#' marks the rest of the\n" ++ "line as comment.\n" ++ "The node= line creates a new zcrypt device node, the\n" ++ "aps=, doms= and ioctls= lines customize the previously\n" ++ "created node. The symbol ALL is also recognized for aps,\n" ++ "doms, and ioctls.\n" ++ "Each action must fit into one line, spreading over multiple\n" ++ "lines is not supported. But you can use more than one\n" ++ "aps=, doms= and ioctls= lines to customize the very same\n" ++ "node.\n" ++ "Processing stops when a line cannot be parsed or the\n" ++ "current action fails. When the config file has been\n" ++ "processed successful, the zcryptctl return code is 0. A non\n" ++ "zero return code (and some kind of failure message) is\n" ++ "emitted on partial completion.", ++ }, ++ { ++ .cmd = CMD_LISTCONFIG, ++ .command = "listconfig", ++ .function = cmd_list, ++ .usage = "zcryptctl listconfig", ++ .description = ++ "List all currently known additional zcrypt device nodes\n" ++ "in a format suitable for the 'config' command.", ++ }, ++ { ++ .command = NULL, ++ .cmd = 0, ++ } ++}; ++ ++static int get_command_index(const char *cmdstr) ++{ ++ int i; ++ ++ for (i = 0; zcryptctl_cmds[i].command; i++) ++ if (!strcmp(zcryptctl_cmds[i].command, cmdstr)) ++ return i; ++ ++ return -1; ++} ++ ++static void commands_print_help(void) ++{ ++ int i; ++ ++ for (i = 0; zcryptctl_cmds[i].command; i++) ++ if (zcryptctl_cmds[i].usage) ++ printf(" %s\n", zcryptctl_cmds[i].usage); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int c, cmdindex = -1; ++ int rc = EXIT_SUCCESS; ++ ++ util_prg_init(&prg); ++ util_opt_init(opt_vec, NULL); ++ ++ for (c = 1; c < argc; c++) { ++ cmdindex = get_command_index(argv[c]); ++ if (cmdindex >= 0) ++ break; ++ } ++ ++ while (1) { ++ c = util_opt_getopt_long(argc, argv); ++ if (c == -1) ++ break; ++ switch (c) { ++ case 'h': ++ if (cmdindex < 0) { ++ util_prg_print_help(); ++ commands_print_help(); ++ util_opt_print_help(); ++ } else { ++ printf("Usage: %s\n", ++ zcryptctl_cmds[cmdindex].usage); ++ printf("%s\n", ++ zcryptctl_cmds[cmdindex].description); ++ } ++ return EXIT_SUCCESS; ++ case 'v': ++ util_prg_print_version(); ++ return EXIT_SUCCESS; ++ default: ++ util_opt_print_parse_error(c, argv); ++ return EXIT_FAILURE; ++ } ++ } ++ ++ if (cmdindex < 0) ++ errx(EXIT_FAILURE, "Missing or invalid command argument"); ++ ++ if (check_zcrypt_class_dir() != 0) ++ errx(EXIT_FAILURE, ++ "Directory class/%s is missing in sysfs.\n" ++ "Multiple zcrypt node support is not available", ++ ZCRYPT_NAME); ++ ++ c = zcryptctl_cmds[cmdindex].cmd; ++ switch (c) { ++ case CMD_LIST: ++ case CMD_LISTCONFIG: ++ rc = zcryptctl_cmds[cmdindex].function(c, NULL, NULL); ++ break; ++ case CMD_CREATE: ++ rc = zcryptctl_cmds[cmdindex].function(c, ++ optind + 1 < argc ? ++ argv[optind + 1] : NULL, ++ NULL); ++ break; ++ case CMD_DESTROY: ++ if (optind + 1 >= argc) ++ errx(EXIT_FAILURE, "Missing node name argument"); ++ if (check_nodename(argv[optind + 1]) != 0) ++ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", ++ argv[optind + 1]); ++ rc = zcryptctl_cmds[cmdindex].function(c, ++ argv[optind + 1], NULL); ++ break; ++ case CMD_ADD_AP: ++ case CMD_DEL_AP: ++ if (optind + 1 >= argc) ++ errx(EXIT_FAILURE, "Missing node name argument"); ++ if (optind + 2 >= argc) ++ errx(EXIT_FAILURE, "Missing adapter argument"); ++ if (check_nodename(argv[optind + 1]) != 0) ++ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", ++ argv[optind + 1]); ++ rc = zcryptctl_cmds[cmdindex].function(c, ++ argv[optind + 1], ++ argv[optind + 2]); ++ break; ++ case CMD_ADD_DOM: ++ case CMD_DEL_DOM: ++ if (optind + 1 >= argc) ++ errx(EXIT_FAILURE, "Missing node name argument"); ++ if (optind + 2 >= argc) ++ errx(EXIT_FAILURE, "Missing domain argument"); ++ if (check_nodename(argv[optind + 1]) != 0) ++ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", ++ argv[optind + 1]); ++ rc = zcryptctl_cmds[cmdindex].function(c, ++ argv[optind + 1], ++ argv[optind + 2]); ++ break; ++ case CMD_ADD_IOCTL: ++ case CMD_DEL_IOCTL: ++ if (optind + 1 >= argc) ++ errx(EXIT_FAILURE, "Missing node name argument"); ++ if (optind + 2 >= argc) ++ errx(EXIT_FAILURE, "Missing ioctl argument"); ++ if (check_nodename(argv[optind + 1]) != 0) ++ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", ++ argv[optind + 1]); ++ rc = zcryptctl_cmds[cmdindex].function(c, ++ argv[optind + 1], ++ argv[optind + 2]); ++ break; ++ case CMD_CONFIG: ++ if (optind + 1 >= argc) ++ errx(EXIT_FAILURE, "Missing filename argument"); ++ rc = zcryptctl_cmds[cmdindex].function(c, NULL, ++ argv[optind + 1]); ++ break; ++ default: ++ errx(EXIT_FAILURE, "Unknown command %d", c); ++ } ++ ++ return rc; ++} +-- +2.21.3 + + +From d5aab30ab6dd79a47d821b39e9f803d4afe8ed7b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 19 Nov 2018 11:40:35 +0100 +Subject: [PATCH 13/44] lszcrypt: support for alternate zcrypt device drivers + (#1646355) + +Summary: lszcrypt: support for alternate zcrypt device drivers +Description: With kernel 4.19 there comes an extension to the + existing AP bus which supports alternate zcrypt + drivers. For details about this see kernel patch + "s390/zcrypt: AP bus support for alternate + driver(s)". So now lszcrypt displays the driver name + in verbose mode. As some of the information + displayed by lszcrypt was based on sysfs attributes, + which are only available when the default zcrypt + driver is bound to the device, this also needed some + rework. If a sysfs attribute is not available + because of an alternate driver binding (or no + driver) a question mark is printed into the field. +Upstream-ID: 0a0b4c382693cded5652404e8fa2c0e483aa33df +--- + zconf/zcrypt/lszcrypt.8 | 4 +- + zconf/zcrypt/lszcrypt.c | 163 +++++++++++++++++++++++++++------------- + 2 files changed, 112 insertions(+), 55 deletions(-) + +diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8 +index 7196806..826e109 100644 +--- a/zconf/zcrypt/lszcrypt.8 ++++ b/zconf/zcrypt/lszcrypt.8 +@@ -54,8 +54,8 @@ status. + .B -V, --verbose + The verbose level for cryptographic device information. + With this verbose level additional information like hardware card type, +-hardware queue depth, pending request queue count, outstanding +-request queue count, and installed function facilities are displayed. ++hardware queue depth, pending requests count, installed function ++facilities and driver binding is displayed. + .TP 8 + .B + Specifies a cryptographic device to display. A cryptographic device can be +diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c +index eb3cd6e..580407f 100644 +--- a/zconf/zcrypt/lszcrypt.c ++++ b/zconf/zcrypt/lszcrypt.c +@@ -1,7 +1,7 @@ + /** + * lszcrypt - Display zcrypt devices and configuration settings + * +- * Copyright IBM Corp. 2008, 2017 ++ * Copyright IBM Corp. 2008, 2018 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. +@@ -56,6 +56,25 @@ struct lszcrypt_l *lszcrypt_l = &l; + #define MASK_CLASS_STATELESS 0x00400000 + #define CLASS_STATELESS "restricted function set" + ++/* ++ * facility bits ++ */ ++#define MAX_FAC_BITS 9 ++static struct fac_bits_s { ++ int mask; ++ char c; ++} fac_bits[MAX_FAC_BITS] = { ++ { 0x80000000, 'S' }, ++ { 0x40000000, 'M' }, ++ { 0x20000000, 'C' }, ++ { 0x10000000, 'D' }, ++ { 0x08000000, 'A' }, ++ { 0x04000000, 'X' }, ++ { 0x02000000, 'N' }, ++ { 0x00800000, 'F' }, ++ { 0x00400000, 'R' }, ++}; ++ + /* + * Program configuration + */ +@@ -66,7 +85,7 @@ const struct util_prg prg = { + { + .owner = "IBM Corp.", + .pub_first = 2008, +- .pub_last = 2017, ++ .pub_last = 2018, + }, + UTIL_PRG_COPYRIGHT_END + } +@@ -255,7 +274,8 @@ static void show_capability(const char *id_str) + /* Skip devices, which are not supported by zcrypt layer */ + if (!util_path_is_readable("%s/type", dev) || + !util_path_is_readable("%s/online", dev)) { +- printf("Detailed capability information for %s (hardware type %ld) is not available.\n", card, hwtype); ++ printf("Detailed capability information for %s (hardware type %ld) is not available.\n", ++ card, hwtype); + return; + } + cbuf[0] = '\0'; +@@ -299,11 +319,13 @@ static void show_capability(const char *id_str) + } else if (func_val & MASK_EP11) { + printf("%s", CAP_EP11); + } else { +- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype); ++ printf("Detailed capability information for %s (hardware type %ld) is not available.", ++ card, hwtype); + } + break; + default: +- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype); ++ printf("Detailed capability information for %s (hardware type %ld) is not available.", ++ card, hwtype); + break; + } + printf("\n"); +@@ -315,17 +337,22 @@ static void show_capability(const char *id_str) + static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, + const char *sub_dev) + { +- unsigned long facility; + char buf[256]; ++ unsigned long facility; + +- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev); +- util_rec_set(rec, "type", buf); ++ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev)) ++ util_rec_set(rec, "type", "-"); ++ else ++ util_rec_set(rec, "type", buf); + +- util_file_read_line(buf, sizeof(buf), "%s/%s/online", grp_dev, sub_dev); +- if (strcmp(buf, "0") == 0) +- util_rec_set(rec, "online", "offline"); ++ if (util_file_read_line(buf, sizeof(buf), "%s/%s/online", ++ grp_dev, sub_dev)) ++ util_rec_set(rec, "online", "-"); + else +- util_rec_set(rec, "online", "online"); ++ if (strcmp(buf, "0") == 0) ++ util_rec_set(rec, "online", "offline"); ++ else ++ util_rec_set(rec, "online", "online"); + + util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); + if (facility & MASK_COPRO) +@@ -339,7 +366,7 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, + + util_file_read_line(buf, sizeof(buf), "%s/%s/request_count", + grp_dev, sub_dev); +- util_rec_set(rec, "request_count", buf); ++ util_rec_set(rec, "requests", buf); + } + + /* +@@ -348,20 +375,19 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, + static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev, + const char *sub_dev) + { ++ int i; + unsigned long facility; +- char buf[256]; +- long depth; ++ char buf[256], afile[PATH_MAX]; ++ long depth, pending1, pending2; + + if (l.verbose == 0) + return; + +- util_file_read_line(buf, sizeof(buf), "%s/%s/pendingq_count", +- grp_dev, sub_dev); +- util_rec_set(rec, "pendingq_count", buf); +- +- util_file_read_line(buf, sizeof(buf), "%s/%s/requestq_count", +- grp_dev, sub_dev); +- util_rec_set(rec, "requestq_count", buf); ++ util_file_read_l(&pending1, 10, "%s/%s/pendingq_count", ++ grp_dev, sub_dev); ++ util_file_read_l(&pending2, 10, "%s/%s/requestq_count", ++ grp_dev, sub_dev); ++ util_rec_set(rec, "pending", "%ld", pending1 + pending2); + + util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev); + util_rec_set(rec, "hwtype", buf); +@@ -370,7 +396,18 @@ static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev, + util_rec_set(rec, "depth", "%02d", depth + 1); + + util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); +- util_rec_set(rec, "facility", "0x%08x", facility); ++ for (i = 0; i < MAX_FAC_BITS; i++) ++ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-'; ++ buf[i] = '\0'; ++ util_rec_set(rec, "facility", buf); ++ ++ snprintf(afile, sizeof(afile), "%s/%s/driver", grp_dev, sub_dev); ++ afile[sizeof(afile) - 1] = '\0'; ++ memset(buf, 0, sizeof(buf)); ++ if (readlink(afile, buf, sizeof(buf)) > 0) ++ util_rec_set(rec, "driver", strrchr(buf, '/') + 1); ++ else ++ util_rec_set(rec, "driver", "-no-driver-"); + } + + /* +@@ -382,9 +419,13 @@ static void show_subdevice(struct util_rec *rec, const char *grp_dev, + if (!util_path_is_dir("%s/%s", grp_dev, sub_dev)) + errx(EXIT_FAILURE, "Error - cryptographic device %s/%s does not exist.", grp_dev, sub_dev); + +- /* Skip devices, which are not supported by zcrypt layer */ +- if (!util_path_is_readable("%s/type", grp_dev) || +- !util_path_is_readable("%s/%s/online", grp_dev, sub_dev)) ++ /* ++ * If not verbose mode, skip devices which are not supported ++ * by the zcrypt layer. ++ */ ++ if (l.verbose == 0 && ++ (!util_path_is_readable("%s/type", grp_dev) || ++ !util_path_is_readable("%s/%s/online", grp_dev, sub_dev))) + return; + + util_rec_set(rec, "card", sub_dev); +@@ -414,11 +455,13 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev) + */ + static void read_rec_default(struct util_rec *rec, const char *grp_dev) + { +- unsigned long facility; + char buf[256]; ++ unsigned long facility; + +- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev); +- util_rec_set(rec, "type", buf); ++ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev)) ++ util_rec_set(rec, "type", "-"); ++ else ++ util_rec_set(rec, "type", buf); + + util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); + if (facility & MASK_COPRO) +@@ -430,14 +473,16 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev) + else + util_rec_set(rec, "mode", "Unknown"); + +- util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev); +- if (strcmp(buf, "0") == 0) +- util_rec_set(rec, "online", "offline"); ++ if (util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev)) ++ util_rec_set(rec, "online", "-"); + else +- util_rec_set(rec, "online", "online"); ++ if (strcmp(buf, "0") == 0) ++ util_rec_set(rec, "online", "offline"); ++ else ++ util_rec_set(rec, "online", "online"); + + util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev); +- util_rec_set(rec, "request_count", buf); ++ util_rec_set(rec, "requests", buf); + } + + /* +@@ -445,18 +490,17 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev) + */ + static void read_rec_verbose(struct util_rec *rec, const char *grp_dev) + { ++ int i; + unsigned long facility; +- char buf[256]; +- long depth; ++ char buf[256], afile[PATH_MAX]; ++ long depth, pending1, pending2; + + if (l.verbose == 0) + return; + +- util_file_read_line(buf, sizeof(buf), "%s/pendingq_count", grp_dev); +- util_rec_set(rec, "pendingq_count", buf); +- +- util_file_read_line(buf, sizeof(buf), "%s/requestq_count", grp_dev); +- util_rec_set(rec, "requestq_count", buf); ++ util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev); ++ util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev); ++ util_rec_set(rec, "pending", "%ld", pending1 + pending2); + + util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev); + util_rec_set(rec, "hwtype", buf); +@@ -465,7 +509,18 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev) + util_rec_set(rec, "depth", "%02d", depth + 1); + + util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); +- util_rec_set(rec, "facility", "0x%08x", facility); ++ for (i = 0; i < MAX_FAC_BITS; i++) ++ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-'; ++ buf[i] = '\0'; ++ util_rec_set(rec, "facility", buf); ++ ++ snprintf(afile, sizeof(afile), "%s/driver", grp_dev); ++ afile[sizeof(afile) - 1] = '\0'; ++ memset(buf, 0, sizeof(buf)); ++ if (readlink(afile, buf, sizeof(buf)) > 0) ++ util_rec_set(rec, "driver", strrchr(buf, '/') + 1); ++ else ++ util_rec_set(rec, "driver", "-no-driver-"); + } + + /* +@@ -481,9 +536,14 @@ static void show_device(struct util_rec *rec, const char *device) + grp_dev = util_path_sysfs("devices/ap/%s", device); + if (!util_path_is_dir(grp_dev)) + errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device); +- /* Skip devices, which are not supported by zcrypt layer */ +- if (!util_path_is_readable("%s/type", grp_dev) || +- !util_path_is_readable("%s/online", grp_dev)) { ++ ++ /* ++ * If not verbose mode, skip devices which are not supported ++ * by the zcrypt layer. ++ */ ++ if (l.verbose == 0 && ++ (!util_path_is_readable("%s/type", grp_dev) || ++ !util_path_is_readable("%s/online", grp_dev))) { + goto out_free; + } + util_rec_set(rec, "card", card); +@@ -506,8 +566,7 @@ static void define_rec_default(struct util_rec *rec) + util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE"); + util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE"); + util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 7, "STATUS"); +- util_rec_def(rec, "request_count", UTIL_REC_ALIGN_RIGHT, 11, +- "REQUEST_CNT"); ++ util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS"); + } + + /* +@@ -517,13 +576,11 @@ static void define_rec_verbose(struct util_rec *rec) + { + if (l.verbose == 0) + return; +- util_rec_def(rec, "pendingq_count", UTIL_REC_ALIGN_RIGHT, 12, +- "PENDINGQ_CNT"); +- util_rec_def(rec, "requestq_count", UTIL_REC_ALIGN_RIGHT, 12, +- "REQUESTQ_CNT"); +- util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 7, "HW_TYPE"); +- util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 7, "Q_DEPTH"); ++ util_rec_def(rec, "pending", UTIL_REC_ALIGN_RIGHT, 8, "PENDING"); ++ util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 6, "HWTYPE"); ++ util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 6, "QDEPTH"); + util_rec_def(rec, "facility", UTIL_REC_ALIGN_LEFT, 10, "FUNCTIONS"); ++ util_rec_def(rec, "driver", UTIL_REC_ALIGN_LEFT, 11, "DRIVER"); + } + + /* +-- +2.21.3 + + +From 47e15616b0c81f85918794a0af2073aac6455826 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Tue, 11 Dec 2018 09:46:40 +0100 +Subject: [PATCH 14/44] zkey: Fails to run commands generated by 'zkey + cryptsetup' (#1650628) + +Description: zkey: Fails to run commands generated by 'zkey cryptsetup' +Symptom: Fails to run commands generated by 'zkey cryptsetup'. +Problem: When using 'zkey cryptsetup' with --run option the + execution of the generated commands may fail, when + the executable to be run is located in '/sbin'. +Solution: Include /sbin into PATH when executing commands. +Reproduction: Use 'zkey cryptsetup' with option --run on a distribution + where 'cryptsetup' is located in '/sbin'. +Upstream-ID: 9100327092c470c2e5b5819087c8094822a1c751 +--- + zkey/keystore.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zkey/keystore.c b/zkey/keystore.c +index 11f555c..a4ad634 100644 +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -3235,7 +3235,7 @@ static int _keystore_execute_cmd(const char *cmd, + { + int rc; + +- rc = setenv("PATH", "/bin:/usr/bin:/usr/sbin", 1); ++ rc = setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1); + if (rc < 0) + return rc; + +-- +2.21.3 + + +From 3328fadb3147a24bae10495259fa7fd00b973eec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Tue, 11 Dec 2018 10:14:05 +0100 +Subject: [PATCH 15/44] zkey: Enhance error message about missing CCA library + (#1655134) + +Description: zkey: Enhance error message about missing CCA library. +Symptom: "zkey-cryptsetup reencipher" fails with missing + library and confusing error message. +Problem: The "zkey reencipher" command as well as the "zkey-cryptsetup + reencipher" command requires the IBM CCA Host Libraries and + Tools package to be installed. This is a closed source + library that is not distributed by the distributions, but + must be downloaded separately from an IBM web page. +Solution: Enhance the error message to point to the web page where + the package can be downloaded. +Reproduction: Run the "zkey-cryptsetup reencipher" or "zkey reencipher" + command without having installed the IBM CCA Host Libraries + and Tools package. +--- + zkey/pkey.c | 13 +++++++++---- + zkey/zkey-cryptsetup.1 | 3 ++- + zkey/zkey.1 | 3 ++- + 3 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/zkey/pkey.c b/zkey/pkey.c +index fe43d02..15e606a 100644 +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -48,6 +48,7 @@ + * Definitions for the CCA library + */ + #define CCA_LIBRARY_NAME "libcsulcca.so" ++#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" + + #define DEFAULT_KEYBITS 256 + +@@ -71,16 +72,20 @@ int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose) + /* Load the CCA library */ + *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); + if (*lib_csulcca == NULL) { +- warnx("%s\nEnsure that the IBM CCA Host Libraries and " +- "Tools are installed properly", dlerror()); ++ pr_verbose(verbose, "%s", dlerror()); ++ warnx("The command requires the IBM CCA Host Libraries and " ++ "Tools.\nFor the supported environments and downloads, " ++ "see:\n%s", CCA_WEB_PAGE); + return -ELIBACC; + } + + /* Get the Key Token Change function */ + *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC"); + if (*dll_CSNBKTC == NULL) { +- warnx("%s\nEnsure that the IBM CCA Host Libraries and " +- "Tools are installed properly", dlerror()); ++ pr_verbose(verbose, "%s", dlerror()); ++ warnx("The command requires the IBM CCA Host Libraries and " ++ "Tools.\nFor the supported environments and downloads, " ++ "see:\n%s", CCA_WEB_PAGE); + dlclose(*lib_csulcca); + *lib_csulcca = NULL; + return -ELIBACC; +diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 +index bf92e21..988ef76 100644 +--- a/zkey/zkey-cryptsetup.1 ++++ b/zkey/zkey-cryptsetup.1 +@@ -182,7 +182,8 @@ behave in the same way as with \fBcryptsetup\fP. + .PP + .B Note: + The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) +-to be installed. ++to be installed. For the supported environments and downloads, see: ++\fIhttp://www.ibm.com/security/cryptocards\fP + . + . + . +diff --git a/zkey/zkey.1 b/zkey/zkey.1 +index c9f7906..0837b27 100644 +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -282,7 +282,8 @@ a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case. + .PP + .B Note: + The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) +-to be installed. ++to be installed. For the supported environments and downloads, see: ++\fIhttp://www.ibm.com/security/cryptocards\fP + . + .SS "Import existing AES secure keys into the secure key repository" + . +-- +2.21.3 + + +From ba7d612c055c887d5cbb596a369d32f00c47711d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Tue, 29 Jan 2019 13:06:21 +0100 +Subject: [PATCH 16/44] zfcpdump: add install script for zfcpdump kernel + (#1600480) + +Summary: zfcpdump: add install script for zfcpdump kernel +Description: zipl expects to find the zfcpdump kernel at a specific location + (usually /usr/lib/s390-tools/zfcpdump/zfcpdump-image). During + kernel installation however, the kernels are installed to /boot. + In order for zipl to find the image add a script that manages a + link to the latest installed zfcpdump kernel at said location. +--- + .gitignore | 3 +- + common.mak | 7 +- + zfcpdump/10-zfcpdump.install.in | 114 ++++++++++++++++++++++++++++++++ + zfcpdump/Makefile | 22 ++++-- + zipl/include/zipl.h | 7 -- + zipl/src/Makefile | 7 +- + zipl/src/job.c | 10 +-- + 7 files changed, 143 insertions(+), 27 deletions(-) + create mode 100644 zfcpdump/10-zfcpdump.install.in + +diff --git a/.gitignore b/.gitignore +index 042233f..6cbec8c 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -76,7 +76,8 @@ zdsfs/zdsfs + zdump/zgetdump + zfcpdump/cpioinit + zfcpdump/zfcpdump_part +-zfcpdump/zfcpdump_part.rd ++zfcpdump/zfcpdump-initrd ++zfcpdump/10-zfcpdump.install + ziomon/ziomon_mgr + ziomon/ziomon_util + ziomon/ziomon_zfcpdd +diff --git a/common.mak b/common.mak +index 6f0990e..6029687 100644 +--- a/common.mak ++++ b/common.mak +@@ -182,9 +182,10 @@ GROUP = $(shell id -gn) + export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP + + # Special defines for zfcpdump +-ZFCPDUMP_PART_IMAGE = zfcpdump_part.image +-ZFCPDUMP_PART_RD = zfcpdump_part.rd +-export ZFCPDUMP_DIR ZFCPDUMP_PART_IMAGE ZFCPDUMP_PART_RD ++ZFCPDUMP_IMAGE = zfcpdump-image ++ZFCPDUMP_INITRD = zfcpdump-initrd ++ZFCPDUMP_FLAVOR = zfcpdump ++export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_INITRD ZFCPDUMP_FLAVOR + + CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) + HOSTCFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) +diff --git a/zfcpdump/10-zfcpdump.install.in b/zfcpdump/10-zfcpdump.install.in +new file mode 100644 +index 0000000..37fde3b +--- /dev/null ++++ b/zfcpdump/10-zfcpdump.install.in +@@ -0,0 +1,114 @@ ++#!/bin/bash ++# ++# 10-zfcpdump.install - Installation script to handle zfcpdump kernels ++# ++# Copyright IBM Corp. 2018 ++# ++# s390-tools is free software; you can redistribute it and/or modify ++# it under the terms of the MIT license. See LICENSE for details. ++# ++# ++# This script supports two modes: ++# ++# 1) Installing the images to /boot// ++# subdirectories, i.e. BOOT_DIR_ABS, as recommended by the BLS. ++# In this case file names are taken over from the original files. ++# ++# 2) Installing the images directly to /boot. In this case the files are ++# renamed to -. ++# ++# The existence of BOOT_DIR_ABS is taken as trigger to switch between both ++# modes. ++# ++# The KERNEL_VERSION is assumed to contain '@flavor@' to identify the image ++# as a zfcpdump kernel. ++ ++COMMAND="$1" ++KERNEL_VERSION="$2" ++BOOT_DIR_ABS="$3" ++KERNEL_IMAGE="$4" ++ ++# Location zipl looks for the zfcpdump kernel ++ZFCPDUMP_IMAGE='@zfcpdump_image@' ++ ++# Only handle zfcpdump kernels ++echo "$KERNEL_VERSION" | grep -q '@flavor@' || exit 0 ++ ++case "$COMMAND" in ++ add) ++ KERNEL_DIR="$(dirname $KERNEL_IMAGE)" ++ KERNEL_NAME="$(basename $KERNEL_IMAGE)" ++ ++ for f in \ ++ "$KERNEL_IMAGE" \ ++ "$KERNEL_DIR"/System.map \ ++ "$KERNEL_DIR"/config \ ++ "$KERNEL_DIR"/zImage.stub ++ do ++ test -e "$f" || continue ++ test -d "$BOOT_DIR_ABS" \ ++ && DEST="$BOOT_DIR_ABS/$(basename $f)" \ ++ || DEST="/boot/$(basename $f)-$KERNEL_VERSION" ++ ++ cp -aT "$f" "$DEST" ++ test $(command -v restorecon) && restorecon -R "$DEST" ++ done ++ ++ # hmac file need special treatment ++ f="$KERNEL_DIR/.$KERNEL_NAME.hmac" ++ if [ -e "$f" ]; then ++ test -d "$BOOT_DIR_ABS" \ ++ && DEST="$BOOT_DIR_ABS/$(basename $f)" \ ++ || DEST="/boot/.$KERNEL_NAME-$KERNEL_VERSION.hmac" ++ ++ cp -aT "$f" "$DEST" ++ test $(command -v restorecon) && restorecon -R "$DEST" ++ fi ++ ++ # Set link so zipl finds the kernel ++ test -d "$BOOT_DIR_ABS" \ ++ && TARGET="$BOOT_DIR_ABS/$KERNEL_NAME" \ ++ || TARGET="/boot/$KERNEL_NAME-$KERNEL_VERSION" ++ ln -sf "$TARGET" "$ZFCPDUMP_IMAGE" ++ ;; ++ ++ remove) ++ # On removal ++ # $KERNEL_IMAGE is empty -> $KERNEL_NAME is empty -> rebuild it ++ KERNEL_NAME="$(basename $(readlink $ZFCPDUMP_IMAGE))" ++ if [ -d "$BOOT_DIR_ABS" ]; then ++ INSTALL_DIR="$(dirname $BOOT_DIR_ABS)" ++ else ++ INSTALL_DIR="/boot/" ++ KERNEL_NAME="$(echo $KERNEL_NAME \ ++ | sed -e "s#\(.*\)-$KERNEL_VERSION#\1#")" ++ fi ++ ++ for f in $(find "$INSTALL_DIR" -name "*$KERNEL_VERSION*"); do ++ rm -rf "$f" ++ done ++ ++ # Update link to latest remaining zfcpdump kernel. ++ if [ $(readlink "$ZFCPDUMP_IMAGE" | grep "$KERNEL_VERSION") ] ++ then ++ NEXT_IMAGE=$( \ ++ find "$INSTALL_DIR" -type f \ ++ | grep '@flavor@' \ ++ | grep "$KERNEL_NAME" \ ++ | grep -v "hmac" \ ++ | sort -V \ ++ | tail -n1 ) ++ ++ test $NEXT_IMAGE \ ++ && ln -sf "$NEXT_IMAGE" "$ZFCPDUMP_IMAGE" \ ++ || rm -f "$ZFCPDUMP_IMAGE" ++ fi ++ ;; ++ *) ++ ;; ++esac ++ ++# Prevent execution of all other scripts. ++# The zfcpdump kernel is stripped down to the bare minimum needed for ++# dumping. It is not supposed to be used for any other purpose. ++exit 77 +diff --git a/zfcpdump/Makefile b/zfcpdump/Makefile +index 5fcb073..cb2fd41 100644 +--- a/zfcpdump/Makefile ++++ b/zfcpdump/Makefile +@@ -1,6 +1,7 @@ + include ../common.mak + + CPIOINIT = $(call echocmd," CPIOINI ",/$@)./cpioinit ++INSTALL_SCRIPTS = 10-zfcpdump.install + + ifeq (${HAVE_LIBC_STATIC},0) + +@@ -20,7 +21,7 @@ check_dep: + "HAVE_LIBC_STATIC=0", \ + "-static") + +-all: check_dep $(ZFCPDUMP_PART_RD) ++all: check_dep $(ZFCPDUMP_INITRD) scripts + + cpioinit: cpioinit.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $^ +@@ -29,17 +30,26 @@ zfcpdump_part: zfcpdump.o zfcpdump_part.o + $(LINK) $(ALL_LDFLAGS) $^ -static -o $@ + $(STRIP) -s $@ + +-$(ZFCPDUMP_PART_RD): cpioinit zfcpdump_part ++$(ZFCPDUMP_INITRD): cpioinit zfcpdump_part + $(CPIOINIT) zfcpdump_part > $@.tmp + $(GZIP) -f $@.tmp +- $(MV) $@.tmp.gz $(ZFCPDUMP_PART_RD) ++ $(MV) $@.tmp.gz $(ZFCPDUMP_INITRD) ++ ++scripts: $(INSTALL_SCRIPTS) ++ chmod +x $(INSTALL_SCRIPTS) + + install: all +- $(INSTALL) -m 611 $(ZFCPDUMP_PART_RD) $(DESTDIR)$(ZFCPDUMP_DIR) ++ $(INSTALL) -m 611 $(ZFCPDUMP_INITRD) $(DESTDIR)$(ZFCPDUMP_DIR) + ++%: %.in ++ zfcpdump_image=$(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE); \ ++ $(SED) -e "s#@zfcpdump_image@#$$zfcpdump_image#g" \ ++ -e "s#@flavor@#$(ZFCPDUMP_FLAVOR)#g" \ ++ < $< > $@ + endif + + clean: +- rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_PART_RD) ++ rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_INITRD) \ ++ $(INSTALL_SCRIPTS) + +-.PHONY: all clean install check_dep ++.PHONY: all clean install check_dep scripts +diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h +index 2aed54f..770801e 100644 +--- a/zipl/include/zipl.h ++++ b/zipl/include/zipl.h +@@ -48,13 +48,6 @@ + #define MENU_DEFAULT_PROMPT 0 + #define MENU_DEFAULT_TIMEOUT 0 + +-#define FSDUMP_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_IMAGE) +-#define FSDUMP_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_RD) +-#define FSDUMP_PART_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" \ +- STRINGIFY(ZFCPDUMP_PART_IMAGE) +-#define FSDUMP_PART_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" \ +- STRINGIFY(ZFCPDUMP_PART_RD) +- + #define MAX_DUMP_VOLUMES 32 + + /* Internal component load address type */ +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index 1634c0d..be14fce 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -2,11 +2,8 @@ + include ../../common.mak + + ALL_CPPFLAGS += -I../include -I../boot \ +- -DZFCPDUMP_DIR=$(ZFCPDUMP_DIR) \ +- -DZFCPDUMP_FS_IMAGE=$(ZFCPDUMP_FS_IMAGE) \ +- -DZFCPDUMP_FS_RD=$(ZFCPDUMP_FS_RD) \ +- -DZFCPDUMP_PART_IMAGE=$(ZFCPDUMP_PART_IMAGE) \ +- -DZFCPDUMP_PART_RD=$(ZFCPDUMP_PART_RD) \ ++ -DZFCPDUMP_IMAGE="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE))" \ ++ -DZFCPDUMP_INITRD="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_INITRD))" \ + -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) + ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) + +diff --git a/zipl/src/job.c b/zipl/src/job.c +index e6d2981..22d2549 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -874,22 +874,22 @@ check_job_dump_images(struct job_dump_data* dump, char* name) + { + int rc; + /* Add data needed to convert fs dump job to IPL job */ +- rc = misc_check_readable_file(FSDUMP_PART_IMAGE); ++ rc = misc_check_readable_file(ZFCPDUMP_IMAGE); + if (rc) { + error_text("Need external file '%s' for partition dump", +- FSDUMP_PART_IMAGE); ++ ZFCPDUMP_IMAGE); + return rc; + } +- dump->image = misc_strdup(FSDUMP_PART_IMAGE); ++ dump->image = misc_strdup(ZFCPDUMP_IMAGE); + if (dump->image == NULL) + return -1; + dump->image_addr = DEFAULT_IMAGE_ADDRESS; + + /* Ramdisk is no longer required with new initramfs dump system */ +- if (misc_check_readable_file(FSDUMP_PART_RAMDISK)) ++ if (misc_check_readable_file(ZFCPDUMP_INITRD)) + dump->ramdisk = NULL; + else { +- dump->ramdisk = misc_strdup(FSDUMP_PART_RAMDISK); ++ dump->ramdisk = misc_strdup(ZFCPDUMP_INITRD); + if (dump->ramdisk == NULL) + return -1; + dump->ramdisk_addr = UNSPECIFIED_ADDRESS; +-- +2.21.3 + + +From eef1d70b8bd0ec96aa90f5dad9795196c85382ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 9 Jan 2019 13:58:10 +0100 +Subject: [PATCH 17/44] pkey: Support autoloading kernel pkey module (#1664632) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The module is loaded automatically based on CPU features, but it's still +too late in some use cases. Thus allow distros to use explicit loading. + +See also: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=63c19be095d0f8eb8992674991e44b4228bd4179 + +Closes: https://github.com/ibm-s390-tools/s390-tools/pull/51 +Signed-off-by: Dan Horák +Reviewed-by: Ingo Franzki +Signed-off-by: Jan Höppner +(cherry picked from commit dffd41943e5c01be2f343da7726edabf9d2ec05e) +--- + etc/modules-load.d/s390-pkey.conf | 2 ++ + 1 file changed, 2 insertions(+) + create mode 100644 etc/modules-load.d/s390-pkey.conf + +diff --git a/etc/modules-load.d/s390-pkey.conf b/etc/modules-load.d/s390-pkey.conf +new file mode 100644 +index 0000000..972a099 +--- /dev/null ++++ b/etc/modules-load.d/s390-pkey.conf +@@ -0,0 +1,2 @@ ++# Load protected key support module on s390 early at boot ++pkey +-- +2.21.3 + + +From 7a3ba6fb39f015da0ee10e1dc0e14ae9fcf6347f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Tue, 21 May 2019 13:39:08 +0200 +Subject: [PATCH 18/44] zipl: Secure Boot support for SCSI IPL (#1659401) + +Description: The Secure Boot firmware feature ensures that only signed + and verified code is executed during IPL. + This feature adds support for secure boot to the zipl + tool and internal loader. + +Upstream-ID: e764f460c457ab2a6000acb5f2eb7169866ce192 +Upstream-ID: 6825645a21a3221629e7d5aa9d5cfbf6e3bc4188 +Upstream-ID: 0c1a63ce5ec6059374d9f8dd55bffbb42ef9bd45 +Upstream-ID: 7c7e10ed8fb048efc4e0cd91b0f6fa704fba128e +Upstream-ID: 58a7462f65d85726168303bd58e7472bd4c82731 +Upstream-ID: 75d4317f208da1e25afd520fe43a1786333388ad +Upstream-ID: dc2e4395506956a99819484d8938e877c4c2cd88 +Upstream-ID: 7e7a77675d2b88b3d516d60aa64f2b1264b25117 +Upstream-ID: 331b54d573c38c7eed6617eef69cf296c2e86d6d +Upstream-ID: 6ea645b3453264a0f1a60955c4476dab54035f88 +--- + zipl/boot/Makefile | 26 ++- + zipl/boot/error.h | 3 + + zipl/boot/libc.c | 20 ++ + zipl/boot/libc.h | 1 + + zipl/boot/s390.h | 5 + + zipl/boot/stage2.c | 9 +- + zipl/boot/stage2.h | 3 +- + zipl/boot/stage2.lds | 5 +- + zipl/boot/stage3.c | 114 ++++++++--- + zipl/boot/stage3.h | 142 ++++++++++++++ + zipl/boot/stage3.lds | 38 ++-- + zipl/include/boot.h | 11 +- + zipl/include/bootmap.h | 31 +++ + zipl/include/job.h | 1 + + zipl/include/misc.h | 1 + + zipl/include/scan.h | 3 +- + zipl/include/zipl.h | 15 +- + zipl/man/zipl.8 | 15 ++ + zipl/man/zipl.conf.5 | 27 +++ + zipl/src/Makefile | 7 +- + zipl/src/boot.c | 33 ++-- + zipl/src/bootmap.c | 417 +++++++++++++++++++++++++++++++++-------- + zipl/src/job.c | 44 ++++- + zipl/src/misc.c | 1 - + zipl/src/scan.c | 34 ++-- + 25 files changed, 837 insertions(+), 169 deletions(-) + +diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile +index 52b3a23..da7e95f 100644 +--- a/zipl/boot/Makefile ++++ b/zipl/boot/Makefile +@@ -13,10 +13,9 @@ FILES = fba0.bin fba1b.bin fba2.bin \ + eckd0_ldl.bin eckd0_cdl.bin \ + eckd1.bin eckd1b.bin eckd2.bin \ + tape0.bin \ +- eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin \ +- stage3.bin ++ eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin + +-all: data.o data.h tape0.bin ++all: data.o data.h tape0.bin stage3.bin + + # Prevent make from using some default rules... + %: %.S +@@ -44,7 +43,7 @@ eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \ + fba2.exec: head.o stage2.o cio.o fba2.o libc.o menu.o sclp.o \ + kdump2.o kdump.o entry.o + stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ +- kdump.o entry.o ++ kdump.o entry.o stage3.lds + + %.exec: %.o + @STAGE=$$( \ +@@ -77,6 +76,22 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ + --only-section=.fixup \ + $< $@ + ++stage3.bin: stage3.exec ++ $(OBJCOPY) -O binary \ ++ --only-section=.stage2.head \ ++ --only-section=.text.dummy \ ++ --only-section=.text.start \ ++ --only-section=.text \ ++ --only-section=.ex_table \ ++ --only-section=.data \ ++ --only-section=.rodata.str1.2 \ ++ --only-section=.rodata \ ++ --only-section=.stage2dump.tail \ ++ --only-section=.eckd2dump_mv.tail \ ++ --only-section=.fixup \ ++ --pad-to=0xf000 \ ++ $< $@ ++ + data.o: $(FILES) + $(LD) $(NO_PIE_LDFLAGS) -r -b binary -o data.o $(FILES) + +@@ -86,6 +101,7 @@ data.h: data.o + echo "extern char $$SYMBOL;" >>data.h; done + + clean: +- rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy ++ rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \ ++ stage3.bin + + .PHONY: all clean +diff --git a/zipl/boot/error.h b/zipl/boot/error.h +index 93fd748..715973e 100644 +--- a/zipl/boot/error.h ++++ b/zipl/boot/error.h +@@ -31,6 +31,9 @@ + /* Internal error */ + #define EINTERNAL 0x00004511 + ++/* Secure IPL error */ ++#define ESECUREBOOT 0x00004512 ++ + /* kdump: No operating system information was found */ + #define EOS_INFO_MISSING 0x00004520 + +diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c +index f86f62a..5944c4e 100644 +--- a/zipl/boot/libc.c ++++ b/zipl/boot/libc.c +@@ -58,6 +58,26 @@ void *memcpy(void *dest, const void *src, unsigned long n) + return dest; + } + ++/* ++ * Move @n bytes of memory from @src to @dest. The memory regions may overlap. ++ */ ++void *memmove(void *dest, const void *src, unsigned long n) ++{ ++ const char *s = src; ++ char *d = dest; ++ ++ if (s < d) { ++ d += n; ++ s += n; ++ while (n--) ++ *--d = *--s; ++ } else { ++ while (n--) ++ *d++ = *s++; ++ } ++ return dest; ++} ++ + /* + * Copy string + */ +diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h +index b05a116..44097fa 100644 +--- a/zipl/boot/libc.h ++++ b/zipl/boot/libc.h +@@ -49,6 +49,7 @@ typedef unsigned char uint8_t; + void printf(const char *, ...); + void sprintf(char *, const char *, ...); + void *memcpy(void *, const void *, unsigned long); ++void *memmove(void *, const void *, unsigned long); + void *memset(void *, int c, unsigned long); + char *strcat(char *, const char *); + int strncmp(const char *, const char *, unsigned long); +diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h +index 279d16b..6e5a7ae 100644 +--- a/zipl/boot/s390.h ++++ b/zipl/boot/s390.h +@@ -35,6 +35,11 @@ struct psw_t { + uint64_t addr; + } __aligned(8); + ++struct psw32_t { ++ uint32_t mask; ++ uint32_t addr; ++} __aligned(8); ++ + void load_wait_psw(uint64_t, struct psw_t *); + + struct _lowcore { +diff --git a/zipl/boot/stage2.c b/zipl/boot/stage2.c +index 02d644e..99e1bd1 100644 +--- a/zipl/boot/stage2.c ++++ b/zipl/boot/stage2.c +@@ -115,8 +115,13 @@ void start(void) + /* skip header */ + entry = (struct component_entry *) + (load_address + sizeof(struct component_header)); +- +- while (entry->type == COMPONENT_LOAD) { ++ while (entry->type == COMPONENT_LOAD || ++ entry->type == COMPONENT_SIGNATURE) { ++ if (entry->type == COMPONENT_SIGNATURE) { ++ /* Skip unhandled signature components */ ++ entry++; ++ continue; ++ } + load_address = (void *)(unsigned long) + entry->address.load_address[1]; + load_blocklist(entry, subchannel_id, load_address); +diff --git a/zipl/boot/stage2.h b/zipl/boot/stage2.h +index b79c798..8fd5bd3 100644 +--- a/zipl/boot/stage2.h ++++ b/zipl/boot/stage2.h +@@ -61,7 +61,8 @@ struct component_entry { + + typedef enum { + COMPONENT_EXECUTE = 0x01, +- COMPONENT_LOAD = 0x02 ++ COMPONENT_LOAD = 0x02, ++ COMPONENT_SIGNATURE = 0x03 + } component_type; + + struct stage2_descr { +diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds +index befe8e2..41efa79 100644 +--- a/zipl/boot/stage2.lds ++++ b/zipl/boot/stage2.lds +@@ -9,7 +9,8 @@ + * 0x2000-0x4fff Sections (load): head, text, data, rodata, rodata.str + * 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes) + * 0x5200-0x5fff Sections: bss +- * 0x6000-0x9fff Memory allocation (heap) ++ * 0x6000-0x8fff Memory allocation (heap) ++ * 0x9000-0x9fff Memory to load stage3 parameter to + * 0xa000-0xdfff Memory to load stage3 to + * 0xe000-0xffff Stack + * +@@ -52,7 +53,7 @@ SECTIONS + + . = 0x6000; + __heap_start = .; +- . = 0xa000; ++ . = 0x9000; + __heap_stop = .; + + . = 0xf000; +diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c +index eb02627..f7a9597 100644 +--- a/zipl/boot/stage3.c ++++ b/zipl/boot/stage3.c +@@ -12,35 +12,15 @@ + #include "libc.h" + #include "s390.h" + #include "stage3.h" ++#include "error.h" + +-/* +- * 48 Byte dummy space for external symbols +- * _parm_addr; address of parmline +- * _initrd_addr; address of initrd +- * _initrd_len; length of initrd +- * _load_psw; load psw of kernel +- * _extra_parm; use extra parm line mechanism? +- * stage3_flags; flags (e.g. STAGE3_FLAG_KDUMP) +- * +- * needed to blow up the binary and leave room +- */ +-__attribute__ ((section(".text.dummy"))) void _dummy(void) +-{ +- asm volatile( +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ".long 0x00000000\n" +- ); +-} ++#define for_each_rb_entry(entry, rb) \ ++ for (entry = rb->entries; \ ++ (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \ ++ entry++) ++ ++static const char *msg_sipl_inval = "Secure boot failure: invalid load address"; ++static const char *msg_sipl_unverified = "Secure boot failure: unverified load address"; + + static unsigned char ebc_037[256] = { + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ +@@ -221,6 +201,64 @@ start_kernel(void) + : [psw] "a" (psw) ); + } + ++unsigned int ++is_verified_address(unsigned long image_addr) ++{ ++ struct ipl_rb_component_entry *comp; ++ struct ipl_rb_components *comps; ++ struct ipl_pl_hdr *pl_hdr; ++ struct ipl_rl_hdr *rl_hdr; ++ struct ipl_rb_hdr *rb_hdr; ++ unsigned long tmp; ++ void *rl_end; ++ ++ /* ++ * There is an IPL report, to find it load the pointer to the ++ * IPL parameter information block from lowcore and skip past ++ * the IPL parameter list, then align the address to a double ++ * word boundary. ++ */ ++ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; ++ pl_hdr = (struct ipl_pl_hdr *) tmp; ++ tmp = (tmp + pl_hdr->len + 7) & -8UL; ++ rl_hdr = (struct ipl_rl_hdr *) tmp; ++ /* Walk through the IPL report blocks in the IPL Report list */ ++ comps = NULL; ++ rl_end = (void *) rl_hdr + rl_hdr->len; ++ rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr); ++ while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end && ++ (void *) rb_hdr + rb_hdr->len <= rl_end) { ++ switch (rb_hdr->rbt) { ++ case IPL_RBT_COMPONENTS: ++ comps = (struct ipl_rb_components *) rb_hdr; ++ break; ++ default: ++ break; ++ } ++ ++ rb_hdr = (void *) rb_hdr + rb_hdr->len; ++ } ++ for_each_rb_entry(comp, comps) { ++ if (image_addr == comp->addr && ++ comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED && ++ comp->flags & IPL_RB_COMPONENT_FLAG_VERIFIED) ++ return 1; ++ } ++ return 0; ++} ++ ++unsigned int ++secure_boot_enabled() ++{ ++ struct ipl_pl_hdr *pl_hdr; ++ unsigned long tmp; ++ ++ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; ++ pl_hdr = (struct ipl_pl_hdr *) tmp; ++ ++ return pl_hdr->flags & IPL_FLAG_SECURE; ++} ++ + void start(void) + { + unsigned int subchannel_id; +@@ -228,6 +266,26 @@ void start(void) + unsigned char *command_line = (unsigned char *)COMMAND_LINE; + unsigned int begin = 0, end = 0, length = 0; + ++ /* ++ * IPL process is secure we have to use default IPL values and ++ * check if the psw jump address is within at the start of a ++ * verified component. If it is not IPL is aborted. ++ */ ++ if (secure_boot_enabled()) { ++ if (_image_addr != DEFAULT_IMAGE_ADDR || ++ _load_psw != DEFAULT_PSW_LOAD) ++ panic(ESECUREBOOT, "%s", msg_sipl_inval); ++ ++ if (!is_verified_address(_load_psw & PSW_ADDR_MASK)) ++ panic(ESECUREBOOT, "%s", msg_sipl_unverified); ++ } ++ /* ++ * cut the kernel header ++ */ ++ memmove((void *)_image_addr, ++ (void *)_image_addr + KERNEL_HEADER_SIZE, ++ _image_len - KERNEL_HEADER_SIZE); ++ + /* store subchannel ID into low core and into new kernel space */ + subchannel_id = S390_lowcore.subchannel_id; + *(unsigned int *)__LC_IPLDEV = subchannel_id; +diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h +index cdc0d77..91477b1 100644 +--- a/zipl/boot/stage3.h ++++ b/zipl/boot/stage3.h +@@ -27,14 +27,156 @@ + #define STAGE3_FLAG_SCSI 0x0001000000000000ULL + #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL + ++#define IPL_FLAG_SECURE 0x40 ++ ++#define DEFAULT_IMAGE_ADDR 0x10000 ++#define DEFAULT_PSW_LOAD 0x0008000080010000L ++#define PSW_ADDR_MASK 0x000000007FFFFFFFL ++#define KERNEL_HEADER_SIZE 65536 ++ + #define UNSPECIFIED_ADDRESS -1ULL + ++ ++/* IPL Parameter List header */ ++struct ipl_pl_hdr { ++ uint32_t len; ++ uint8_t flags; ++ uint8_t reserved1[2]; ++ uint8_t version; ++} __packed; ++ ++/* IPL Parameter Block header */ ++struct ipl_pb_hdr { ++ uint32_t len; ++ uint8_t pbt; ++} __packed; ++ ++/* IPL Parameter Block 0 with common fields */ ++struct ipl_pb0_common { ++ uint32_t len; ++ uint8_t pbt; ++ uint8_t flags; ++ uint8_t reserved1[2]; ++ uint8_t loadparm[8]; ++ uint8_t reserved2[84]; ++} __packed; ++ ++/* IPL Parameter Block 0 for FCP */ ++struct ipl_pb0_fcp { ++ uint32_t len; ++ uint8_t pbt; ++ uint8_t reserved1[3]; ++ uint8_t loadparm[8]; ++ uint8_t reserved2[304]; ++ uint8_t opt; ++ uint8_t reserved3[3]; ++ uint8_t cssid; ++ uint8_t reserved4[1]; ++ uint8_t devno; ++ uint8_t reserved5[4]; ++ uint64_t wwpn; ++ uint64_t lun; ++ uint32_t bootprog; ++ uint8_t reserved6[12]; ++ uint64_t br_lba; ++ uint32_t scp_data_len; ++ uint8_t reserved7[260]; ++ uint8_t scp_data[]; ++} __packed; ++ ++/* IPL Parameter Block 0 for CCW */ ++struct ipl_pb0_ccw { ++ uint32_t len; ++ uint8_t pbt; ++ uint8_t flags; ++ uint8_t reserved1[2]; ++ uint8_t loadparm[8]; ++ uint8_t reserved2[84]; ++ uint16_t reserved3 : 13; ++ uint8_t ssid : 3; ++ uint16_t devno; ++ uint8_t vm_flags; ++ uint8_t reserved4[3]; ++ uint32_t vm_parm_len; ++ uint8_t nss_name[8]; ++ uint8_t vm_parm[64]; ++ uint8_t reserved5[8]; ++} __packed; ++ ++struct ipl_parameter_block { ++ struct ipl_pl_hdr hdr; ++ union { ++ struct ipl_pb_hdr pb0_hdr; ++ struct ipl_pb0_common common; ++ struct ipl_pb0_fcp fcp; ++ struct ipl_pb0_ccw ccw; ++ char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)]; ++ }; ++} __packed __aligned(PAGE_SIZE); ++ ++/* IPL Report List header */ ++struct ipl_rl_hdr { ++ uint32_t len; ++ uint8_t flags; ++ uint8_t reserved1[2]; ++ uint8_t version; ++ uint8_t reserved2[8]; ++} __packed; ++ ++/* IPL Report Block header */ ++struct ipl_rb_hdr { ++ uint32_t len; ++ uint8_t rbt; ++ uint8_t reserved1[11]; ++} __packed; ++ ++/* IPL Report Block types */ ++enum ipl_rbt { ++ IPL_RBT_CERTIFICATES = 1, ++ IPL_RBT_COMPONENTS = 2, ++}; ++ ++/* IPL Report Block for the certificate list */ ++struct ipl_rb_certificate_entry { ++ uint64_t addr; ++ uint64_t len; ++} __packed; ++ ++struct ipl_rb_certificates { ++ uint32_t len; ++ uint8_t rbt; ++ uint8_t reserved1[11]; ++ struct ipl_rb_certificate_entry entries[]; ++} __packed; ++ ++/* IPL Report Block for the component list */ ++struct ipl_rb_component_entry { ++ uint64_t addr; ++ uint64_t len; ++ uint8_t flags; ++ uint8_t reserved1[5]; ++ uint16_t certificate_index; ++ uint8_t reserved2[8]; ++}; ++ ++#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80 ++#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40 ++ ++struct ipl_rb_components { ++ uint32_t len; ++ uint8_t rbt; ++ uint8_t reserved1[11]; ++ struct ipl_rb_component_entry entries[]; ++} __packed; ++ + extern unsigned long long _parm_addr; /* address of parmline */ + extern unsigned long long _initrd_addr; /* address of initrd */ + extern unsigned long long _initrd_len; /* length of initrd */ + extern unsigned long long _load_psw; /* load psw of kernel */ + extern unsigned long long _extra_parm; /* use extra parm line mechanism? */ + extern unsigned long long stage3_flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */ ++extern unsigned long long _image_len; /* length of kernel */ ++extern unsigned long long _image_addr; /* target address of kernel */ + extern void kdump_stage3(); + + #endif /* STAGE3_H */ +diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds +index 638fb25..c67bc32 100644 +--- a/zipl/boot/stage3.lds ++++ b/zipl/boot/stage3.lds +@@ -1,33 +1,47 @@ + /* + * Memory layout for stage 3 ++ * ========================= ++ * ++ * General memory layout ++ * --------------------- ++ * ++ * 0x0000-0x1fff Lowcore ++ * 0x2000-0x5fff Memory allocation (heap) ++ * 0x6000-0x8fff free ++ * 0x9000-0x9fff Stage3 parameter ++ * 0xa000-0xdfff Stage3 code + data ++ * 0xe000-0xffff Stack + */ + + SECTIONS + { + . = 0x0; + +- . = 0x7000; ++ . = 0x2000; + __heap_start = .; +- . = 0xa000; ++ . = 0x6000; + __heap_stop = .; + +- . = 0xa000; ++ ++ /* stage 3 parameter */ ++ . = 0x9000; + _parm_addr = .; +- . = 0xa008; ++ . = 0x9008; + _initrd_addr = .; +- . = 0xa010; ++ . = 0x9010; + _initrd_len = .; +- . = 0xa018; ++ . = 0x9018; + _load_psw = .; +- . = 0xa020; ++ . = 0x9020; + _extra_parm = .; +- . = 0xa028; ++ . = 0x9028; + stage3_flags =.; ++ . = 0x9030; ++ _image_len = .; ++ . = 0x9038; ++ _image_addr = .; + + . = 0xa000; +- .text.dummy : { *(.text.dummy) } +- +- . = 0xa050; + .text.start : { *(.text.start) } + .text : { *(.text) } + __ex_table_start = .; +@@ -35,11 +49,9 @@ SECTIONS + __ex_table_stop = .; + .eh_frame : { *(.eh_frame) } + +- . = 0xc000; + __bss_start = .; + .bss : { *(.bss) } + __bss_stop = .; + .rodata : {*(.rodata) } + .data : { *(.data) } +- + } +diff --git a/zipl/include/boot.h b/zipl/include/boot.h +index f21ab59..6bd2a9b 100644 +--- a/zipl/include/boot.h ++++ b/zipl/include/boot.h +@@ -248,6 +248,9 @@ struct boot_stage3_params { + uint64_t load_psw; + uint64_t extra_parm; + uint16_t flags; ++ uint16_t reserved[3]; ++ uint64_t image_len; ++ uint64_t image_addr; + } __attribute__ ((packed)); + + #define STAGE3_FLAG_SCSI 0x0001 +@@ -305,10 +308,10 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, + disk_blockptr_t *stage2_list, + blocknum_t stage2_count); + int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job); +-size_t get_stage3_size(); +-int boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, +- address_t initrd_addr, size_t initrd_len, +- address_t image_addr, int extra_parm, uint16_t flags); ++int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, ++ address_t initrd_addr, size_t initrd_len, ++ address_t load_addr, int extra_parm, uint16_t flags, ++ size_t image_len); + int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr, + address_t initrd_addr, address_t image_addr); + int boot_get_tape_dump(void** data, size_t* size, uint64_t mem); +diff --git a/zipl/include/bootmap.h b/zipl/include/bootmap.h +index c03c041..4a0bd04 100644 +--- a/zipl/include/bootmap.h ++++ b/zipl/include/bootmap.h +@@ -16,6 +16,37 @@ + #include "job.h" + #include "zipl.h" + ++#define SIGNATURE_MAGIC "~Module signature appended~\n" ++#define PKCS7_FORMAT 0x01 ++ ++struct signature_header { ++ uint8_t format; ++ uint8_t reserved[3]; ++ uint32_t length; ++} __attribute((packed)); ++ ++typedef union { ++ uint64_t load_address; ++ uint64_t load_psw; ++ struct signature_header sig_head; ++} component_data; ++ ++/* ++ * The file_signature structure and the PKEY_ID definition ++ * are based on linux/scripts/sign-file.c ++ */ ++struct file_signature { ++ u8 algorithm; ++ u8 hash; ++ u8 id_type; ++ u8 signer_len; ++ u8 key_id_len; ++ u8 __pad[3]; ++ u32 sig_len; ++ char magic[28]; ++}; ++ ++#define PKEY_ID_PKCS7 0x02 + + int bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + disk_blockptr_t *scsi_dump_sb_blockptr, +diff --git a/zipl/include/job.h b/zipl/include/job.h +index 4c9253f..fce811c 100644 +--- a/zipl/include/job.h ++++ b/zipl/include/job.h +@@ -121,6 +121,7 @@ struct job_data { + int add_files; + int dry_run; + int command_line; ++ int is_secure; + }; + + +diff --git a/zipl/include/misc.h b/zipl/include/misc.h +index 37a8a87..5a349a7 100644 +--- a/zipl/include/misc.h ++++ b/zipl/include/misc.h +@@ -51,6 +51,7 @@ int misc_check_readable_file(const char* filename); + int misc_check_writable_device(const char* devno, int blockdev, int chardev); + void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to); + void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to); ++unsigned int misc_check_secure_boot(void); + + #define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +diff --git a/zipl/include/scan.h b/zipl/include/scan.h +index 0fe415c..4dcee0b 100644 +--- a/zipl/include/scan.h ++++ b/zipl/include/scan.h +@@ -16,7 +16,7 @@ + + + #define SCAN_SECTION_NUM 9 +-#define SCAN_KEYWORD_NUM 21 ++#define SCAN_KEYWORD_NUM 22 + #define SCAN_KEYWORD_ONLY_NUM 1 + #define SCAN_AUTOMENU_NAME "zipl-automatic-menu" + +@@ -51,6 +51,7 @@ enum scan_keyword_id { + scan_keyword_targetoffset = 18, + scan_keyword_defaultauto = 19, + scan_keyword_kdump = 20, ++ scan_keyword_secure = 21, + }; + + enum scan_section_type { +diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h +index 770801e..6f2d115 100644 +--- a/zipl/include/zipl.h ++++ b/zipl/include/zipl.h +@@ -20,10 +20,11 @@ + #define DISK_LAYOUT_ID 0x00000001 + + #define ZIPL_STAGE2_LOAD_ADDRESS 0x2000 +-#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa050LL ++#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa000LL + #define DEFAULT_IMAGE_ADDRESS 0x10000LL + #define KDUMP_IMAGE_ADDRESS 0x10010LL + #define DEFAULT_STAGE3_ADDRESS 0xa000LL ++#define DEFAULT_STAGE3_PARAMS_ADDRESS 0x9000LL + #define MINIMUM_ADDRESS 0x10000LL + #define ADDRESS_LIMIT 0x80000000LL + #define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */ +@@ -31,11 +32,15 @@ + #define MAXIMUM_PARMLINE_SIZE 0x380 + #define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000 + ++#define STAGE3_HEAP_SIZE 0x4000 ++#define STAGE3_HEAP_ADDRESS 0x2000 ++#define STAGE3_STACK_SIZE 0x1000 ++#define STAGE3_STACK_ADDRESS 0xF000 ++ + #define PSW_ADDRESS_MASK 0x000000007fffffffLL + #define PSW_LOAD 0x0008000080000000LL + #define PSW_DISABLED_WAIT 0x000a000000000000LL + +-#define KERNEL_HEADER_SIZE 65536 + #define BOOTMAP_FILENAME "bootmap" + #define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX" + +@@ -44,12 +49,18 @@ + #define ZIPL_CONF_VAR "ZIPLCONF" + #define ZIPL_DEFAULT_CONF "/etc/zipl.conf" + #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" ++#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" ++#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure" + + #define MENU_DEFAULT_PROMPT 0 + #define MENU_DEFAULT_TIMEOUT 0 + + #define MAX_DUMP_VOLUMES 32 + ++#define SECURE_BOOT_DISABLED 0 ++#define SECURE_BOOT_ENABLED 1 ++#define SECURE_BOOT_AUTO 2 ++ + /* Internal component load address type */ + typedef uint64_t address_t; + +diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 +index 9891975..2f873f3 100644 +--- a/zipl/man/zipl.8 ++++ b/zipl/man/zipl.8 +@@ -352,6 +352,21 @@ whether they contain a dump signature or not. + This option can only be used together with + .BR \-\-mvdump . + ++.TP ++.BR "\-S " " or " "\-\-secure " ++Control the zIPL secure boot support. ++ can take one of three values: ++ ++ auto (default) ++ Write signatures if available and supported by the system. ++ 1 ++ Signatures are written independent of support indicated by the local ++ system. Also missing signatures for stage 3 and kernel IPL files ++ will result in an error. ++ 0 ++ No signatures will be written. ++ ++ + .SH EXAMPLE + 1. Scenario: prepare disk for booting a Linux kernel image using the + following parameters: +diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5 +index d4877d8..71d1252 100644 +--- a/zipl/man/zipl.conf.5 ++++ b/zipl/man/zipl.conf.5 +@@ -82,6 +82,8 @@ below). + .br + defaultmenu = menu1 + .br ++secure = auto ++.br + + [linux] + .br +@@ -517,6 +519,31 @@ An optional hexadecimal address may be provided to load the kernel to a + non-default memory location. + .PP + ++.B secure ++= ++.IR auto / 1 / 0 ++(configuration only) ++.IP ++.B Configuration section: ++.br ++Control the zIPL secure boot support. ++Set this option to one of the following: ++.IP " - " 12 ++.BR auto: ++Write signatures if available and supported by the system. ++.IP " - " 12 ++.BR 1: ++Signatures are written independent of support indicated by the local system. ++Also missing signatures for stage 3 and kernel IPL files will result in an error. ++.IP " - " 12 ++.BR 0: ++No signatures will be written. ++ ++The default value for ++.B 'secure' ++is auto. ++.PP ++ + .B segment + = + .IR segment\-file , address +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index be14fce..bed970c 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -15,8 +15,9 @@ objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ + + zipl_helpers = $(basename $(wildcard zipl_helper.*.c)) + chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers)) ++zipl_stage3 = ../boot/stage3.bin + +-all: zipl $(chreipl_helpers) ++all: zipl $(chreipl_helpers) $(zipl_stage3) + + zipl: $(objects) $(libs) + +@@ -33,6 +34,7 @@ install: all + $(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \ + $(DESTDIR)$(TOOLS_LIBDIR) + $(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR) ++ $(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR) + + clean: + rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl +@@ -46,3 +48,6 @@ clean: + + ../boot/data.o: + make -C ../boot data.o ++ ++../boot/stage3.bin: ++ make -C ../boot stage3.bin +diff --git a/zipl/src/boot.c b/zipl/src/boot.c +index 279d246..b13bcb7 100644 +--- a/zipl/src/boot.c ++++ b/zipl/src/boot.c +@@ -14,6 +14,8 @@ + #include + #include + #include ++#include ++#include + + #include "../boot/data.h" + #include "boot.h" +@@ -68,20 +70,17 @@ boot_check_data(void) + return 0; + } + +-/* Export stage 3 size for partition dump with dump kernel */ +-size_t +-get_stage3_size() +-{ +- return DATA_SIZE(stage3); +-} +- +-/* Create a stage 3 loader in memory. ++/* ++ * Create a stage 3 parameter block in memory. + * Upon success, return 0 and set BUFFER to point to the data buffer and set +- * BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */ ++ * BYTECOUNT to contain the parameter block size in bytes. ++ * Return non-zero otherwise. ++ */ + int +-boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, +- address_t initrd_addr, size_t initrd_len, address_t image_addr, +- int extra_parm, uint16_t flags) ++boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, ++ address_t initrd_addr, size_t initrd_len, ++ address_t image_addr, int extra_parm, uint16_t flags, ++ size_t image_len) + { + struct boot_stage3_params params; + void* data; +@@ -92,9 +91,10 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, + return -1; + } + /* Get memory */ +- data = misc_malloc(DATA_SIZE(stage3)); ++ data = misc_malloc(sizeof(params)); + if (data == NULL) + return -1; ++ memset(data, 0, sizeof(params)); + /* Prepare params section */ + params.parm_addr = (uint64_t) parm_addr; + params.initrd_addr = (uint64_t) initrd_addr; +@@ -102,11 +102,12 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, + params.load_psw = (uint64_t)(image_addr | PSW_LOAD); + params.extra_parm = (uint64_t) extra_parm; + params.flags = flags; ++ params.image_len = (uint64_t) image_len; ++ params.image_addr = (uint64_t) image_addr; + /* Initialize buffer */ +- memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3)); +- memcpy(data, ¶ms, sizeof(struct boot_stage3_params)); ++ memcpy(data, ¶ms, sizeof(params)); + *buffer = data; +- *bytecount = DATA_SIZE(stage3); ++ *bytecount = sizeof(params); + return 0; + } + +diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c +index 1b71b03..456c2ef 100644 +--- a/zipl/src/bootmap.c ++++ b/zipl/src/bootmap.c +@@ -19,6 +19,7 @@ + #include + + #include "lib/util_part.h" ++#include "lib/util_path.h" + + #include "boot.h" + #include "bootmap.h" +@@ -117,6 +118,22 @@ check_menu_positions(struct job_menu_data* menu, char* name, + return 0; + } + ++static bool ++check_secure_boot_support(void) ++{ ++ unsigned int val; ++ FILE *fp; ++ ++ fp = fopen(ZIPL_SIPL_PATH, "r"); ++ if (!fp) ++ return false; ++ ++ fscanf(fp, "%d", &val); ++ fclose(fp); ++ ++ return val ? true : false; ++} ++ + + /* Write COUNT elements of the blocklist specified by LIST as a linked list + * of segment table blocks to the file identified by file descriptor FD. Upon +@@ -199,24 +216,21 @@ add_program_table(int fd, disk_blockptr_t* table, int entries, + return rc; + } + +- + struct component_entry { + uint8_t data[23]; + uint8_t type; +- union { +- uint64_t load_address; +- uint64_t load_psw; +- } address; ++ component_data compdat; + } __attribute((packed)); + + typedef enum { + component_execute = 0x01, +- component_load = 0x02 ++ component_load = 0x02, ++ component_signature = 0x03 + } component_type; + + static void + create_component_entry(void* buffer, disk_blockptr_t* pointer, +- component_type type, uint64_t address, ++ component_type type, component_data data, + struct disk_info* info) + { + struct component_entry* entry; +@@ -228,10 +242,15 @@ create_component_entry(void* buffer, disk_blockptr_t* pointer, + case component_load: + bootmap_store_blockptr(&entry->data, pointer, + info); +- entry->address.load_address = address; ++ entry->compdat.load_address = data.load_address; + break; + case component_execute: +- entry->address.load_psw = address; ++ entry->compdat.load_psw = data.load_psw; ++ break; ++ case component_signature: ++ bootmap_store_blockptr(&entry->data, pointer, ++ info); ++ entry->compdat.sig_head = data.sig_head; + break; + } + } +@@ -267,7 +286,7 @@ struct component_loc { + + static int + add_component_file(int fd, const char* filename, address_t load_address, +- off_t offset, void* component, int add_files, ++ size_t trailer, void *component, int add_files, + struct disk_info* info, struct job_target_data* target, + struct component_loc *location) + { +@@ -279,8 +298,6 @@ add_component_file(int fd, const char* filename, address_t load_address, + size_t size; + blocknum_t count; + int rc; +- int from; +- unsigned int to; + + if (add_files) { + /* Read file to buffer */ +@@ -289,17 +306,10 @@ add_component_file(int fd, const char* filename, address_t load_address, + error_text("Could not read file '%s'", filename); + return rc; + } +- /* Ensure minimum size */ +- if (size <= (size_t) offset) { +- error_reason("File '%s' is too small (has to be " +- "greater than %ld bytes)", filename, +- (long) offset); +- free(buffer); +- return -1; +- } ++ size -= trailer; + /* Write buffer */ +- count = disk_write_block_buffer(fd, 0, buffer + offset, +- size - offset, &list, info); ++ count = disk_write_block_buffer(fd, 0, buffer, ++ size, &list, info); + free(buffer); + if (count == 0) { + error_text("Could not write to bootmap file"); +@@ -321,20 +331,7 @@ add_component_file(int fd, const char* filename, address_t load_address, + disk_free_info(file_info); + if (count == 0) + return -1; +- if (count * info->phy_block_size <= (size_t) offset) { +- error_reason("File '%s' is too small (has to be " +- "greater than %ld bytes)", filename, +- (long) offset); +- free(list); +- return -1; +- } +- if (offset > 0) { +- /* Shorten list by offset */ +- from = offset / info->phy_block_size; +- count -= from; +- for (to=0; to < count; to++, from++) +- list[to] = list[from]; +- } ++ count -= DIV_ROUND_UP(trailer, info->phy_block_size); + } + /* Fill in component location */ + loc.addr = load_address; +@@ -346,7 +343,7 @@ add_component_file(int fd, const char* filename, address_t load_address, + free(list); + if (rc == 0) { + create_component_entry(component, &segment, component_load, +- load_address, info); ++ (component_data) load_address, info); + /* Return location if requested */ + if (location != NULL) + *location = loc; +@@ -354,11 +351,10 @@ add_component_file(int fd, const char* filename, address_t load_address, + return rc; + } + +- + static int +-add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, ++add_component_buffer(int fd, void* buffer, size_t size, component_data data, + void* component, struct disk_info* info, +- struct component_loc *location) ++ struct component_loc *location, int type) + { + struct component_loc loc; + disk_blockptr_t segment; +@@ -372,17 +368,21 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, + error_text("Could not write to bootmap file"); + return -1; + } +- /* Fill in component location */ +- loc.addr = load_address; +- loc.size = count * info->phy_block_size; ++ if (type == component_load) { ++ /* Fill in component location */ ++ loc.addr = data.load_address; ++ loc.size = count * info->phy_block_size; ++ } else { ++ loc.addr = 0; ++ loc.size = 0; ++ } + /* Try to compact list */ + count = disk_compact_blocklist(list, count, info); + /* Write segment table */ + rc = add_segment_table(fd, list, count, &segment, info); + free(list); + if (rc == 0) { +- create_component_entry(component, &segment, component_load, +- load_address, info); ++ create_component_entry(component, &segment, type, data, info); + /* Return location if requested */ + if (location != NULL) + *location = loc; +@@ -391,6 +391,30 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, + } + + ++static int ++add_dummy_buffer(int fd, size_t size, address_t addr, void *component, ++ struct disk_info *info, struct component_loc *comp_loc) ++{ ++ char *buffer; ++ int rc; ++ ++ buffer = misc_malloc(size); ++ if (buffer == NULL) ++ return -1; ++ ++ memset(buffer, 0, size); ++ rc = add_component_buffer(fd, buffer, size, ++ (component_data) (uint64_t) addr, ++ component, info, comp_loc, component_load); ++ if (rc) { ++ free(buffer); ++ return rc; ++ } ++ free(buffer); ++ return 0; ++} ++ ++ + static void + print_components(const char *name[], struct component_loc *loc, int num) + { +@@ -409,23 +433,86 @@ print_components(const char *name[], struct component_loc *loc, int num) + } + } + ++static int ++extract_signature(char *filename, void **ret_signature, ++ struct signature_header *sig_head) ++{ ++ struct file_signature *file_sig; ++ size_t signature_size = 0; ++ void *signature; ++ char *buffer; ++ size_t size; ++ ++ if (misc_read_file(filename, &buffer, &size, 0)) ++ return 0; ++ ++ file_sig = (void *) buffer + size - sizeof(*file_sig); ++ if (memcmp(file_sig->magic, SIGNATURE_MAGIC, sizeof(file_sig->magic)) ++ != 0) ++ goto out; ++ ++ signature = misc_malloc(file_sig->sig_len); ++ if (signature == NULL) ++ goto out; ++ signature_size = file_sig->sig_len; ++ ++ memcpy(signature, buffer + size - signature_size - sizeof(*file_sig), ++ signature_size); ++ ++ *ret_signature = signature; ++ sig_head->length = signature_size; ++ ++ switch (file_sig->id_type) { ++ case PKEY_ID_PKCS7: ++ sig_head->format = PKCS7_FORMAT; ++ break; ++ default: ++ error_text("Unsupported signature type %02x", ++ file_sig->id_type); ++ signature_size = 0; ++ goto out; ++ } ++ /* return size of signature and corresponding header */ ++ signature_size += sizeof(*file_sig); ++out: ++ free(buffer); ++ return signature_size; ++} ++ ++static void ++check_remaining_filesize(size_t filesize, size_t signature_size, ++ struct disk_info *info, char *filename) ++{ ++ if ((filesize - signature_size) % info->phy_block_size) { ++ fprintf(stderr, ++ "Warning: Size of signed file %s is not a multiple of the disk block size\n", ++ filename); ++ } ++} + + static int + add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + int verbose, int add_files, component_header_type type, +- struct disk_info* info, struct job_target_data* target) ++ struct disk_info* info, struct job_target_data* target, ++ int is_secure) + { ++ struct component_loc comp_loc[10]; ++ struct signature_header sig_head; ++ size_t ramdisk_size, image_size; ++ bool secure_boot_supported; ++ size_t stage3_params_size; ++ const char *comp_name[10]; ++ size_t signature_size; ++ int offset, flags = 0; ++ void *stage3_params; + struct stat stats; +- void* table; +- void* stage3; +- size_t stage3_size; +- const char *comp_name[4] = {"kernel image", "parmline", +- "initial ramdisk", "internal loader"}; +- struct component_loc comp_loc[4]; ++ void *signature; ++ int comp_nr = 0; ++ void *table; + int rc; +- int offset, flags = 0; + + memset(comp_loc, 0, sizeof(comp_loc)); ++ memset(&sig_head, 0, sizeof(sig_head)); + table = misc_malloc(info->phy_block_size); + if (table == NULL) + return -1; +@@ -456,55 +543,189 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + return -1; + } + } +- if (info->type == disk_type_scsi) ++ ramdisk_size = stats.st_size; ++ if (info->type == disk_type_scsi) { + flags |= STAGE3_FLAG_SCSI; ++ /* ++ * Add dummy components for stage 3 heap and stack to block the ++ * associated memory areas against firmware use. ++ */ ++ rc = add_dummy_buffer(fd, STAGE3_HEAP_SIZE, STAGE3_HEAP_ADDRESS, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr]); ++ if (rc) { ++ error_text("Could not add stage3 HEAP dummy"); ++ free(table); ++ return rc; ++ } ++ comp_name[comp_nr] = "heap area"; ++ offset += sizeof(struct component_entry); ++ comp_nr++; ++ rc = add_dummy_buffer(fd, STAGE3_STACK_SIZE, ++ STAGE3_STACK_ADDRESS, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr]); ++ if (rc) { ++ error_text("Could not add stage3 STACK dummy"); ++ free(table); ++ return rc; ++ } ++ comp_name[comp_nr] = "stack area"; ++ offset += sizeof(struct component_entry); ++ comp_nr++; ++ } + if (ipl->is_kdump) + flags |= STAGE3_FLAG_KDUMP; + ++ /* Get kernel file size */ ++ if (stat(ipl->image, &stats)) { ++ error_reason(strerror(errno)); ++ error_text("Could not get information for file '%s'", ++ ipl->image); ++ free(table); ++ return -1; ++ } ++ image_size = stats.st_size; ++ secure_boot_supported = check_secure_boot_support(); ++ signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature, ++ &sig_head); ++ if (signature_size && ++ (is_secure == SECURE_BOOT_ENABLED || ++ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { ++ if (verbose) ++ printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH); ++ ++ rc = add_component_buffer(fd, signature, sig_head.length, ++ (component_data)sig_head, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr], ++ component_signature); ++ if (rc) { ++ error_text("Could not add stage3 signature"); ++ free(table); ++ return rc; ++ } ++ comp_name[comp_nr] = "loader signature"; ++ offset += sizeof(struct component_entry); ++ comp_nr++; ++ free(signature); ++ } else if (is_secure == SECURE_BOOT_ENABLED) { ++ /* ++ * If secure boot is forced and we have failed to extract a ++ * signature for the stage 3 loader zipl will abort with an ++ * error message ++ */ ++ error_text("Could not install Secure Boot IPL records"); ++ error_reason("Missing signature in internal loader file %s", ++ ZIPL_STAGE3_PATH); ++ free(table); ++ return -1; ++ } ++ + /* Add stage 3 loader to bootmap */ +- rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr, +- ipl->ramdisk_addr, (size_t) stats.st_size, +- ipl->is_kdump ? ipl->image_addr + 0x10 : +- ipl->image_addr, +- (info->type == disk_type_scsi) ? 0 : 1, +- flags); ++ rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS, ++ signature_size, VOID_ADD(table, offset), 1, ++ info, target, &comp_loc[comp_nr]); + if (rc) { ++ error_text("Could not add internal loader file '%s'", ++ ZIPL_STAGE3_PATH); + free(table); + return rc; + } +- rc = add_component_buffer(fd, stage3, stage3_size, +- DEFAULT_STAGE3_ADDRESS, +- VOID_ADD(table, offset), info, &comp_loc[3]); +- free(stage3); ++ offset += sizeof(struct component_entry); ++ comp_name[comp_nr] = "internal loader"; ++ comp_nr++; ++ ++ /* Add stage 3 parameter to bootmap */ ++ rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size, ++ ipl->parm_addr, ipl->ramdisk_addr, ++ ramdisk_size, ++ ipl->is_kdump ? ipl->image_addr + 0x10 : ++ ipl->image_addr, ++ (info->type == disk_type_scsi) ? 0 : 1, ++ flags, image_size); + if (rc) { +- error_text("Could not add stage 3 boot loader"); ++ free(table); ++ return rc; ++ } ++ rc = add_component_buffer(fd, stage3_params, stage3_params_size, ++ (component_data) (uint64_t) ++ DEFAULT_STAGE3_PARAMS_ADDRESS, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr], component_load); ++ free(stage3_params); ++ if (rc) { ++ error_text("Could not add parameters"); + free(table); + return -1; + } + offset += sizeof(struct component_entry); ++ comp_name[comp_nr] = "parameters"; ++ comp_nr++; ++ + /* Add kernel image */ + if (verbose) { + printf(" kernel image......: %s\n", ipl->image); + } ++ signature_size = extract_signature(ipl->image, &signature, &sig_head); ++ if (signature_size && ++ (is_secure == SECURE_BOOT_ENABLED || ++ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { ++ if (verbose) ++ printf(" signature for.....: %s\n", ipl->image); ++ ++ rc = add_component_buffer(fd, signature, sig_head.length, ++ (component_data)sig_head, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr], ++ component_signature); ++ if (rc) { ++ error_text("Could not add image signature"); ++ free(table); ++ return rc; ++ } ++ comp_name[comp_nr] = "image signature"; ++ offset += sizeof(struct component_entry); ++ comp_nr++; ++ free(signature); ++ check_remaining_filesize(image_size, signature_size, info, ++ ipl->image); ++ } else if (is_secure == SECURE_BOOT_ENABLED) { ++ /* ++ * If secure boot is forced and we have failed to extract a ++ * signature for the kernel image zipl will abort with an ++ * error message ++ */ ++ error_text("Could not install Secure Boot IPL records"); ++ error_reason("Missing signature in image file %s", ++ ipl->image); ++ free(table); ++ return -1; ++ } ++ + rc = add_component_file(fd, ipl->image, ipl->image_addr, +- KERNEL_HEADER_SIZE, VOID_ADD(table, offset), +- add_files, info, target, &comp_loc[0]); ++ signature_size, VOID_ADD(table, offset), ++ add_files, info, target, &comp_loc[comp_nr]); + if (rc) { + error_text("Could not add image file '%s'", ipl->image); + free(table); + return rc; + } + offset += sizeof(struct component_entry); ++ comp_name[comp_nr] = "kernel image"; ++ comp_nr++; ++ ++ /* Add kernel parmline */ + if (ipl->parmline != NULL) { +- /* Add kernel parmline */ + if (verbose) { + printf(" kernel parmline...: '%s'\n", ipl->parmline); + } + rc = add_component_buffer(fd, ipl->parmline, + strlen(ipl->parmline) + 1, +- ipl->parm_addr, ++ (component_data) ipl->parm_addr, + VOID_ADD(table, offset), +- info, &comp_loc[1]); ++ info, &comp_loc[comp_nr], ++ component_load); + if (rc) { + error_text("Could not add parmline '%s'", + ipl->parmline); +@@ -512,14 +733,45 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + return -1; + } + offset += sizeof(struct component_entry); ++ comp_name[comp_nr] = "parmline"; ++ comp_nr++; + } + + /* finally add ramdisk */ + if (ipl->ramdisk != NULL) { ++ signature_size = extract_signature(ipl->ramdisk, &signature, ++ &sig_head); ++ if (signature_size && ++ (is_secure == SECURE_BOOT_ENABLED || ++ (is_secure == SECURE_BOOT_AUTO && ++ secure_boot_supported))) { ++ if (verbose) { ++ printf(" signature for.....: %s\n", ++ ipl->ramdisk); ++ } ++ rc = add_component_buffer(fd, signature, ++ sig_head.length, ++ (component_data)sig_head, ++ VOID_ADD(table, offset), info, ++ &comp_loc[comp_nr], ++ component_signature); ++ if (rc) { ++ error_text("Could not add ramdisk signature"); ++ free(table); ++ return rc; ++ } ++ comp_name[comp_nr] = "ramdisk signature"; ++ offset += sizeof(struct component_entry); ++ comp_nr++; ++ free(signature); ++ check_remaining_filesize(ramdisk_size, signature_size, ++ info, ipl->ramdisk); ++ } + rc = add_component_file(fd, ipl->ramdisk, +- ipl->ramdisk_addr, 0, ++ ipl->ramdisk_addr, signature_size, + VOID_ADD(table, offset), +- add_files, info, target, &comp_loc[2]); ++ add_files, info, target, ++ &comp_loc[comp_nr]); + if (rc) { + error_text("Could not add ramdisk '%s'", + ipl->ramdisk); +@@ -527,13 +779,16 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + return -1; + } + offset += sizeof(struct component_entry); ++ comp_name[comp_nr] = "initial ramdisk"; ++ comp_nr++; + } + if (verbose) +- print_components(comp_name, comp_loc, 4); ++ print_components(comp_name, comp_loc, comp_nr); + /* Terminate component table */ + create_component_entry(VOID_ADD(table, offset), NULL, + component_execute, +- ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD, ++ (component_data) (uint64_t) ++ (ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD), + info); + /* Write component table */ + rc = disk_write_block_aligned(fd, table, info->phy_block_size, +@@ -584,7 +839,8 @@ if (rc) { + print_components(comp_name, comp_loc, 1); + /* Terminate component table */ + create_component_entry(VOID_ADD(table, offset), NULL, +- component_execute, PSW_DISABLED_WAIT, info); ++ component_execute, (component_data) (uint64_t) ++ PSW_DISABLED_WAIT, info); + /* Write component table */ + rc = disk_write_block_aligned(fd, table, info->phy_block_size, + program, info); +@@ -674,7 +930,7 @@ add_dump_program(int fd, struct job_dump_data* dump, + return rc; + ipl.parm_addr = dump->parm_addr; + return add_ipl_program(fd, &ipl, program, verbose, 1, +- type, info, target); ++ type, info, target, SECURE_BOOT_DISABLED); + } + + +@@ -711,7 +967,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_ipl_program(fd, &job->data.ipl, &table[0], + verbose || job->command_line, + job->add_files, component_header, +- info, &job->target); ++ info, &job->target, job->is_secure); + break; + case job_segment: + if (job->command_line) +@@ -762,7 +1018,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header, +- info, &job->target); ++ info, &job->target, ++ job->is_secure); + break; + case job_print_usage: + case job_print_version: +@@ -913,7 +1170,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, + ulong size; + ulong unused_size; + +- size = DIV_ROUND_UP(get_stage3_size(), info->phy_block_size); ++ /* Use approximated stage 3 size as starting point */ ++ size = MINIMUM_ADDRESS; ++ + /* Ramdisk */ + if (job->data.dump.ramdisk != NULL) { + if (stat(job->data.dump.ramdisk, &st)) +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 22d2549..2d8de8f 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -52,11 +52,12 @@ static struct option options[] = { + { "dry-run", no_argument, NULL, '0'}, + { "force", no_argument, NULL, 'f'}, + { "kdump", required_argument, NULL, 'k'}, ++ { "secure", required_argument, NULL, 'S'}, + { NULL, 0, NULL, 0 } + }; + + /* Command line option abbreviations */ +-static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:"; ++static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:"; + + struct command_line { + char* data[SCAN_KEYWORD_NUM]; +@@ -215,6 +216,11 @@ get_command_line(int argc, char* argv[], struct command_line* line) + } else + cmdline.menu = optarg; + break; ++ case 'S': ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_secure, ++ optarg); ++ break; + case 'h': + cmdline.help = 1; + break; +@@ -511,7 +517,7 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp, + /* Fill in component data */ + num = 0; + rc = set_cl_element(&cl[num++], "kernel image", ipl->image, +- &ipl->image_addr, 0, 0x10000, ++ &ipl->image_addr, 0, 0, + MAXIMUM_PHYSICAL_BLOCKSIZE); + if (rc) + goto error; +@@ -1263,6 +1269,26 @@ type_from_target(char *target, disk_type_t *type) + } + } + ++static int ++set_secure_ipl(char *keyword, struct job_data *job) ++{ ++ if (strcmp(keyword, "auto") == 0) { ++ job->is_secure = SECURE_BOOT_AUTO; ++ } else if (strcmp(keyword, "0") == 0) { ++ job->is_secure = SECURE_BOOT_DISABLED; ++ } else if (strcmp(keyword, "1") == 0) { ++ if (job->target.targettype != disk_type_scsi) { ++ error_reason("Secure boot forced for non-SCSI disk type"); ++ return -1; ++ } ++ job->is_secure = SECURE_BOOT_ENABLED; ++ } else { ++ error_reason("Invalid secure boot setting '%s'", ++ keyword); ++ return -1; ++ } ++ return 0; ++} + + static int + get_job_from_section_data(char* data[], struct job_data* job, char* section) +@@ -1345,6 +1371,13 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + return -1; + } + } ++ /* Fill in secure boot */ ++ if (data[(int) scan_keyword_secure] != NULL) { ++ rc = set_secure_ipl(data[(int) scan_keyword_secure], ++ job); ++ if (rc) ++ return rc; ++ } + break; + case section_ipl_tape: + /* Tape IPL job */ +@@ -1502,6 +1535,13 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) + job->data.menu.timeout = + atol(scan[i].content.keyword.value); + break; ++ case scan_keyword_secure: ++ rc = set_secure_ipl( ++ scan[i].content.keyword.value, ++ job); ++ if (rc) ++ return rc; ++ break; + case scan_keyword_target: + job->target.bootmap_dir = misc_strdup( + scan[i].content.keyword.value); +diff --git a/zipl/src/misc.c b/zipl/src/misc.c +index fd16810..057c9a0 100644 +--- a/zipl/src/misc.c ++++ b/zipl/src/misc.c +@@ -22,7 +22,6 @@ + #include "error.h" + #include "misc.h" + +- + /* Allocate SIZE bytes of memory. Upon success, return pointer to memory. + * Return NULL otherwise. */ + void * +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index fe72e9a..e57361e 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -45,45 +45,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { + * ult to tofs e mete file isk ent et pt out ultm dump + * rs enu + * +- * targ targ targ targ targ defa kdum +- * etba etty etge etbl etof ulta p ++ * targ targ targ targ targ defa kdum secu ++ * etba etty etge etbl etof ulta p re + * se pe omet ocks fset uto + * ry ize + */ + /* default auto */ + {opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, +- opt, opt, opt, opt, opt, opt, inv}, ++ opt, opt, opt, opt, opt, opt, inv, opt}, + /* default menu */ + {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, opt}, + /* default section */ + {req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, opt}, + /* ipl */ + {inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv, +- opt, opt, opt, opt, opt, inv, opt}, ++ opt, opt, opt, opt, opt, inv, opt, opt}, + /* segment load */ + {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, inv}, + /* part dump */ + {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, inv}, + /* fs dump */ + {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, inv}, + /* ipl tape */ + {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv, +- inv, inv, inv, inv, inv, inv, inv}, ++ inv, inv, inv, inv, inv, inv, inv, inv}, + /* multi volume dump */ + {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, +- inv, inv, inv, inv, inv, inv, inv} ++ inv, inv, inv, inv, inv, inv, inv, inv} + }; + + /* Determines which keyword may be present in a menu section */ + enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = { + /* menu section */ + opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, +- opt, opt, opt, opt, opt, inv, inv ++ opt, opt, opt, opt, opt, inv, inv, opt + }; + + /* Mapping of keyword IDs to strings */ +@@ -111,6 +111,7 @@ static const struct { + { "timeout", scan_keyword_timeout}, + { "tape", scan_keyword_tape}, + { "kdump", scan_keyword_kdump}, ++ { "secure", scan_keyword_secure}, + }; + + /* List of keywords that are used without an assignment */ +@@ -1863,6 +1864,7 @@ scan_build_automenu(struct scan_token* scan) + /* defaultmenu */ 1 + + /* menu heading */ 1 + + /* keyword default,prompt,timeout */ 3 + ++ /* keyword secure */ 1 + + /* target keywords*/ num_targets + + /* missing target definitions */ num_sections * num_targets + + /* number assigment */ num_sections; +@@ -1978,6 +1980,14 @@ scan_build_automenu(struct scan_token* scan) + db_keyword[i])) + goto err; + } ++ /* secure= */ ++ i = (int) scan_keyword_secure; ++ if (db_keyword[i]) { ++ if (scan_append_keyword_assignment(new_scan, &i_new, ++ scan_keyword_secure, ++ db_keyword[i])) ++ goto err; ++ } + /* target= */ + /* targetbase= */ + /* targetgeometry= */ +-- +2.21.3 + + +From 100c89a273fb4bcc1deb93aaf77ac9264d9ac3ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 2 May 2019 15:46:39 +0200 +Subject: [PATCH 19/44] zipl: update stage3 objcopy command for gcc9 (#1659401) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The objcopy command for stage3.bin needs to take the new rodata section into account. +See also PR #60. + +Signed-off-by: Dan Horák +--- + zipl/boot/Makefile | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile +index da7e95f..a049797 100644 +--- a/zipl/boot/Makefile ++++ b/zipl/boot/Makefile +@@ -85,6 +85,7 @@ stage3.bin: stage3.exec + --only-section=.ex_table \ + --only-section=.data \ + --only-section=.rodata.str1.2 \ ++ --only-section=.rodata.cst8 \ + --only-section=.rodata \ + --only-section=.stage2dump.tail \ + --only-section=.eckd2dump_mv.tail \ +-- +2.21.3 + + +From 78f0479055e025ba849766aa2d60c22a7bcdbfba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Tue, 21 May 2019 13:49:09 +0200 +Subject: [PATCH 20/44] s390-tools: Add zcryptstats tool (#1658756) + +Description: The zcryptstats tool displays usage statistics of + IBM Crypto Express adapters. It obtains cryptographic + performance measurement data periodically and displays + the data for each cryptographic device for each + interval. A cryptographic device can be either a card + device or queue device (APQN). + +Upstream-ID: 5f2ddad6a83e04f6a64ea39619148c80e56c0814 +Upstream-ID: b165500b6987f3fcbbb6daee6d84d66af7fbd41f +Upstream-ID: 58189b878697fef3bf405fdd864bf40f2149ccc0 +Upstream-ID: 7b4a05e5a36fd36ba1866047f6b4814f127ec846 +Upstream-ID: 8be43a1f16923da037d5f5bffca73c433ca1231b +Upstream-ID: 43fcb694bfd2e4515ce131ea3aba2f81e8437757 +--- + README.md | 1 + + include/lib/util_rec.h | 3 + + libutil/util_rec.c | 89 +- + libutil/util_rec_example.c | 7 + + zconf/zcrypt/Makefile | 7 +- + zconf/zcrypt/zcryptstats.8 | 247 ++++ + zconf/zcrypt/zcryptstats.c | 2418 ++++++++++++++++++++++++++++++++++++ + 7 files changed, 2768 insertions(+), 4 deletions(-) + create mode 100644 zconf/zcrypt/zcryptstats.8 + create mode 100644 zconf/zcrypt/zcryptstats.c + +diff --git a/README.md b/README.md +index 08c27d7..af1024e 100644 +--- a/README.md ++++ b/README.md +@@ -129,6 +129,7 @@ Package contents + or show encryption state of attached LUNs. + - lszcrypt: Show Information about zcrypt devices and configuration. + - chzcrypt: Modify the zcrypt configuration. ++ - zcryptstats: Display usage statistics of IBM Crypto Express adapters. + - znetconf: List and configure network devices for s390 network adapters. + - cio_ignore: Query and modify the contents of the CIO device driver + blacklist. +diff --git a/include/lib/util_rec.h b/include/lib/util_rec.h +index a44461a..7a0c2f6 100644 +--- a/include/lib/util_rec.h ++++ b/include/lib/util_rec.h +@@ -68,5 +68,8 @@ const char *util_rec_get(struct util_rec *rec, const char *key); + + void util_rec_print_hdr(struct util_rec *rec); + void util_rec_print(struct util_rec *rec); ++void util_rec_print_separator(struct util_rec *rec); ++ ++void util_rec_set_indent(struct util_rec *rec, int indent); + + #endif /** LIB_UTIL_REC_H @} */ +diff --git a/libutil/util_rec.c b/libutil/util_rec.c +index 621bebc..c216cf9 100644 +--- a/libutil/util_rec.c ++++ b/libutil/util_rec.c +@@ -64,6 +64,7 @@ struct rec_fmt { + int argz_sep; + } csv_p; + } d; ++ int indent; + }; + + /* +@@ -122,9 +123,50 @@ struct util_rec *util_rec_new_wide(const char *hdr_sep) + rec->fmt.type = REC_FMT_WIDE; + rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep); + rec->fmt.d.wide_p.argz_sep = ','; ++ rec->fmt.indent = 0; + return rec; + } + ++/* ++ * Print the indentation characters ++ */ ++static inline void rec_print_indention(int indent) ++{ ++ if (indent <= 0) ++ return; ++ ++ printf("%*s", indent, ""); ++} ++ ++/* ++ * Print record separator in "wide" output format ++ */ ++static void rec_print_wide_separator(struct util_rec *rec) ++{ ++ const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep; ++ int size = 0, field_count = 0; ++ struct util_rec_fld *fld; ++ char *buf; ++ ++ if (!hdr_sep) ++ return; ++ ++ util_list_iterate(rec->list, fld) { ++ if (fld->hdr) { ++ size += fld->width; ++ field_count++; ++ } ++ } ++ ++ size += field_count - 1; ++ buf = util_malloc(size + 1); ++ memset(buf, (int)hdr_sep[0], size); ++ buf[size] = 0; ++ rec_print_indention(rec->fmt.indent); ++ printf("%s\n", buf); ++ free(buf); ++} ++ + /* + * Print record header in "wide" output format + */ +@@ -135,6 +177,7 @@ static void rec_print_wide_hdr(struct util_rec *rec) + struct util_rec_fld *fld; + char *buf; + ++ rec_print_indention(rec->fmt.indent); + util_list_iterate(rec->list, fld) { + if (col_nr) + printf(" "); +@@ -156,6 +199,7 @@ static void rec_print_wide_hdr(struct util_rec *rec) + buf = util_malloc(size + 1); + memset(buf, (int)hdr_sep[0], size); + buf[size] = 0; ++ rec_print_indention(rec->fmt.indent); + printf("%s\n", buf); + free(buf); + } +@@ -172,6 +216,7 @@ void rec_print_wide(struct util_rec *rec) + int fld_count = 0; + char *entry; + ++ rec_print_indention(rec->fmt.indent); + util_list_iterate(rec->list, fld) { + if (!fld->hdr) + continue; +@@ -225,6 +270,7 @@ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep, + rec->fmt.d.long_p.key_size = key_size; + rec->fmt.d.long_p.val_size = val_size; + rec->fmt.d.long_p.argz_sep = ' '; ++ rec->fmt.indent = 0; + return rec; + } + +@@ -241,6 +287,7 @@ static void rec_print_long_hdr(struct util_rec *rec) + fld = rec_get_fld(rec, p->key); + util_assert(fld != NULL, "Record not found\n"); + util_assert(fld->hdr != NULL, "Header for field not found\n"); ++ rec_print_indention(rec->fmt.indent); + if (p->col_sep) { + printf("%-*s %s %-*s\n", p->key_size, fld->hdr, + p->col_sep, fld->width, fld->val); +@@ -255,6 +302,7 @@ static void rec_print_long_hdr(struct util_rec *rec) + buf = util_malloc(len + 1); + memset(buf, p->hdr_sep[0], len); + buf[len] = 0; ++ rec_print_indention(rec->fmt.indent); + printf("%s\n", buf); + free(buf); + } +@@ -277,19 +325,24 @@ static void rec_print_long(struct util_rec *rec) + continue; + if (!fld->val) + continue; ++ rec_print_indention(rec->fmt.indent); + item = argz_next(fld->val, fld->len, item); + if (p->col_sep) { + printf(" %-*s %s %s\n", + p->key_size - 8, fld->hdr, p->col_sep, item); +- while ((item = argz_next(fld->val, fld->len, item))) ++ while ((item = argz_next(fld->val, fld->len, item))) { ++ rec_print_indention(rec->fmt.indent); + printf(" %-*s %c %s\n", + p->key_size - 8, "", p->argz_sep, item); ++ } + } else { + printf(" %-*s %s\n", + p->key_size - 8, fld->hdr, fld->val); +- while ((item = argz_next(fld->val, fld->len, item))) ++ while ((item = argz_next(fld->val, fld->len, item))) { ++ rec_print_indention(rec->fmt.indent); + printf(" %-*s %s\n", + p->key_size - 8, "", item); ++ } + } + } + printf("\n"); +@@ -320,6 +373,7 @@ struct util_rec *util_rec_new_csv(const char *col_sep) + rec->fmt.type = REC_FMT_CSV; + rec->fmt.d.csv_p.col_sep = util_strdup(col_sep); + rec->fmt.d.csv_p.argz_sep = ' '; ++ rec->fmt.indent = 0; + return rec; + } + +@@ -332,6 +386,7 @@ void rec_print_csv_hdr(struct util_rec *rec) + struct util_rec_fld *fld; + int fld_count = 0; + ++ rec_print_indention(rec->fmt.indent); + util_list_iterate(rec->list, fld) { + if (fld_count) + printf("%c", *col_sep); +@@ -354,6 +409,7 @@ void rec_print_csv(struct util_rec *rec) + int fld_count = 0; + char *item = NULL; + ++ rec_print_indention(rec->fmt.indent); + util_list_iterate(rec->list, fld) { + item = argz_next(fld->val, fld->len, item); + if (fld_count) +@@ -470,6 +526,24 @@ void util_rec_print_hdr(struct util_rec *rec) + } + } + ++/** ++ * Print record separator according to output format ++ * ++ * @param[in] rec Record pointer ++ */ ++void util_rec_print_separator(struct util_rec *rec) ++{ ++ switch (rec->fmt.type) { ++ case REC_FMT_WIDE: ++ rec_print_wide_separator(rec); ++ break; ++ case REC_FMT_LONG: ++ break; ++ case REC_FMT_CSV: ++ break; ++ } ++} ++ + /** + * Set a field value to an argz vector + * +@@ -537,3 +611,14 @@ const char *util_rec_get(struct util_rec *rec, const char *key) + + return (fld != NULL) ? fld->val : NULL; + } ++ ++/** ++ * Sets the indentation of the record ++ * ++ * @param[in] rec Record pointer ++ * @param[in] indent Number of characters to indent ++ */ ++void util_rec_set_indent(struct util_rec *rec, int indent) ++{ ++ rec->fmt.indent = indent; ++} +diff --git a/libutil/util_rec_example.c b/libutil/util_rec_example.c +index bc38bb4..801f909 100644 +--- a/libutil/util_rec_example.c ++++ b/libutil/util_rec_example.c +@@ -42,6 +42,8 @@ static void print_records(const char *format, struct util_rec *rec) + /* Print the record */ + util_rec_print(rec); + } ++ /* Print a separator line (is a nop for long and csv format) */ ++ util_rec_print_separator(rec); + printf("\n"); + } + +@@ -72,6 +74,11 @@ int main(void) + print_records("Wide format", rec); + util_rec_free(rec); + ++ rec = util_rec_new_wide("-"); ++ util_rec_set_indent(rec, 4); ++ print_records("Wide format with indentation", rec); ++ util_rec_free(rec); ++ + rec = util_rec_new_long("-", ":", "number", 30, 20); + print_records("Long format", rec); + util_rec_free(rec); +diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile +index d075f34..f679775 100644 +--- a/zconf/zcrypt/Makefile ++++ b/zconf/zcrypt/Makefile +@@ -1,24 +1,27 @@ + include ../../common.mak + +-all: chzcrypt lszcrypt zcryptctl ++all: chzcrypt lszcrypt zcryptctl zcryptstats + + libs = $(rootdir)/libutil/libutil.a + + chzcrypt: chzcrypt.o misc.o $(libs) + lszcrypt: lszcrypt.o misc.o $(libs) + zcryptctl: zcryptctl.o misc.o $(libs) ++zcryptstats: zcryptstats.o $(libs) + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptstats $(DESTDIR)$(BINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8 ++ $(INSTALL) -m 644 -c zcryptstats.8 $(DESTDIR)$(MANDIR)/man8 + + clean: +- rm -f *.o chzcrypt lszcrypt zcryptctl ++ rm -f *.o chzcrypt lszcrypt zcryptctl zcryptstats + + .PHONY: all install clean +diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8 +new file mode 100644 +index 0000000..8c31c69 +--- /dev/null ++++ b/zconf/zcrypt/zcryptstats.8 +@@ -0,0 +1,247 @@ ++.\" Copyright IBM Corp. 2019 ++.\" 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 ZCRYPTSTATS 1 "January 2019" "s390-tools" ++.SH NAME ++zcryptstats \- Display usage statistics of IBM Crypto Express adapters ++. ++. ++.SH SYNOPSIS ++.B zcryptstats ++.RI [ OPTIONS ] ++.RI [ DEVICE_ID ++[...] ] ++. ++.PP ++.B zcryptstats ++.BR \-\-help | \-h ++.br ++.B zcryptstats ++.BR \-\-version | \-v ++. ++. ++. ++.SH DESCRIPTION ++. ++Use \fBzcryptstats\fP to display usage statistics of IBM Crypto Express ++adapters. ++.PP ++\fBzcryptstats\fP obtains cryptographic performance measurement data ++periodically and displays the data for each cryptographic device for each ++interval. ++A cryptographic device can be either a card device or a queue device (APQN). ++\fBzcryptstats\fP runs forever unless you limit the number of intervals with ++the \fB\-\-count\fP option. The default interval time is 10 seconds. ++Use the \fB\-\-interval\fP option to specify a different interval time. ++.PP ++By default, all available cryptographic devices are monitored. ++You can optionally specify the device IDs of the devices to be monitored. ++The card device representation and the queue device are both in hexadecimal ++notation. ++.PP ++Use the \fB\-\-no-apqn\fP option to omit the performance measurement data of ++the queues. If the system does not support obtaining cryptographic ++performance measurement data on the queue devices, only the card devices ++are monitored. ++.PP ++For each device, a set of counters is displayed. The amount and meaning of the ++counters are dependent on the device type and mode, see the COUNTERS section. ++For each counter and interval, the following values are displayed: ++.RS 2 ++.IP "\(bu" 2 ++Number of measured operations. ++.IP "\(bu" 2 ++Rate of the measured operation in operations per second. ++.IP "\(bu" 2 ++Utilization of the device in percent. ++.IP "\(bu" 2 ++Average duration of the operations. ++.RE ++.PP ++The sum of all operations is displayed in a separate \fBtotals\fP line. ++Use the \fB\-\-only-totals\fP option to omit the individual counters and ++display the totals only. Use the \fB\-\-no\-totals\fP option to omit the ++totals. ++.PP ++ ++.B Note: ++The utilization value of a counter can exceed 100%. This value is caused by ++the parallel execution of cryptographic operations. ++.PP ++Cryptographic performance measurement data might not be available when Linux ++is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an ++error message and exits. ++. ++. ++. ++.SH OPTIONS ++. ++.TP ++.BR DEVICE_ID ++Specifies a cryptographic device for which statistics are displayed. ++A device ID can either be a card device ID ++(\fI\fP) or a queue device (APQN) ID (\fI.\fP). ++To filter all devices by domain, provide \fI.\fP. ++If no IDs are given, statistics are displayed for all available devices. ++. ++.TP ++.BR \-i ", " \-\-interval\~\fIINTERVAL\fP ++Specifies the interval time in seconds. If this option is omitted, then the ++default interval time of 10 seconds is used. ++. ++.TP ++.BR \-c ", " \-\-count\~\fICOUNT\fP ++Specifies the number of reports that are generated at \fIINTERVAL\fP seconds ++apart. If this option is omitted, the \fBzcryptstats\fP command generates ++reports continuously, until it is stopped with control-C. ++. ++.TP ++.BR \-o ", " \-\-output\~\fIJSON\fP|\fITABLE\fP|\fICSV\fP ++Displays the statistics in the specified format. If this option is omitted, a ++comprehensive report is displayed. Supported output formats are: ++.RS 8 ++.IP "\(bu" 2 ++\fBJSON:\fP Displays the statistics in Javascript Object Notation (JSON) format. ++JSON output field order is undefined, and new fields might be added in the ++future. ++.IP "\(bu" 2 ++\fBTABLE:\fP Displays the statistics in a human readable simple table format. ++The individual counters are omitted, and only the totals are displayed. ++This output format implies option \fB\-\-only-totals\fP. ++.IP "\(bu" 2 ++\fBCSV:\fP Displays the statistics in comma-separated values format. The values ++are separated with a semicolon. The individual counters are omitted, and only ++the totals are displayed. This output format implies option ++\fB\-\-only-totals\fP. ++.RE ++. ++.TP ++.BR \-t ", " \-\-no\-totals ++Excludes the totals of all counters of a card device or queue device ++(APQN). This option cannot be specified together with option ++\fB\-\-only\-totals\fP or option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP. ++. ++.TP ++.BR \-T ", " \-\-only\-totals ++Displays only the totals of all counters of a card device or a queue device ++(APQN), but not the individual counters. This option is implied with ++option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP. ++. ++.TP ++.BR \-a ", " \-\-no\-apqn ++Displays only the counters of the card device, but omits the counters of the ++queue device (APQN). If the system does not support obtaining cryptographic ++performance measurement data on the queue devices, this option is implied. ++. ++.TP ++.BR \-M ", " \-\-map\-type\~\fIMAPPING\fP ++Maps unknown cryptographic device types and modes to known types and modes. ++This option should only be used when new, so far unknown cryptographic devices ++are found. You can then map them to known devices and modes, provided that the ++new cryptographic devices report the same counters as the known cryptographic ++device to which it is mapped. ++The mapping specification consists of a comma-separated list of ++\fIFROM\-TYPE\fP:\fIFROM\-MODE\fP=\fITO\-TYPE\fP:\fITO\-MODE\fP specifications. ++The type and mode values must be specified in decimal notation. ++. ++.TP ++.BR \-A ", " \-\-all ++Displays all cards devices and queue devices (APQNs), not only those that are ++available to the Linux instance. Using this option additional cryptographic ++devices that are available in the CEC, but not available to the Linux system ++are also monitored. ++This option cannot be specified together with option \fB\-\-only-online\fP. ++. ++.TP ++.BR \-O ", " \-\-only\-online ++Displays only online cards devices and queue devices (APQNs). This option ++cannot be specified together with option \fB\-\-all\fP. ++. ++.TP ++.BR \-V ", " \-\-verbose ++Displays additional information messages during processing. ++.TP ++.BR \-h ", " \-\-help ++Displays help text and exits. ++.TP ++.BR \-v ", " \-\-version ++Displays version information and exits. ++. ++. ++. ++.SH COUNTERS ++. ++.PP ++.B IBM Crypto Express adapter in accelerator mode: ++.RS 4 ++.TP ++.B All ++All operations on the adapter ++.TP ++.B RSA Key-gen ++RSA-key-generation operations (also included in \fBAll\fP). ++.RE ++.PP ++.B IBM Crypto Express adapter in CCA co-processor mode: ++.RS 4 ++.TP ++.B RSA 1024 ME ++1024-bit ME-format RSA operations. ++.TP ++.B RSA 2048 ME ++2048-bit ME-format RSA operations. ++.TP ++.B RSA 1024 CRT ++1024-bit CRT-format RSA operations. ++.TP ++.B RSA 2048 CRT ++2048-bit CRT-format RSA operations. ++.TP ++.B RSA 4096 ME ++4096-bit ME-format RSA operations. ++.TP ++.B RSA 4096 CTR ++4096-bit CRT-format RSA operations. ++.RE ++.PP ++.B IBM Crypto Express adapter in EP11 co-processor mode: ++.RS 4 ++.TP ++.B Asym. Slow ++Slow asymmetric-key functions. ++.TP ++.B Asym. Fast ++Fast asymmetric-key functions. ++.TP ++.B Symm. Partial ++Symmetric-key functions that return partial or incremental results. ++.TP ++.B Symm. Complete ++Symmetric-key functions that return a complete or final result. ++.TP ++.B Asym. Key-gen ++asymmetric-key generation function. ++.RE ++.PP ++. ++. ++. ++.SH EXAMPLES ++.TP ++.B zcryptstats 02 ++Display statistics for all cryptographic devices with card ID \fB02\fP. ++.TP ++.B zcryptstats 02.0005 --interval 5 ++Display statistics for cryptographic devices with card ID \fB02\fP and domain ++ID \fB0005\fP in a 5 second interval. ++.TP ++.B zcryptstats .0005 --count 10 ++Display statistics for cryptographic devices with domain ID \fB0005\fP with the ++default interval time of 10 seconds, for 10 intervals. ++.TP ++.B zcryptstats 02 --output JSON ++Display statistics for all cryptographic devices with card ID \fB02\fP in ++\fBJSON\fP output format. ++.TP ++ +diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c +new file mode 100644 +index 0000000..136d5ba +--- /dev/null ++++ b/zconf/zcrypt/zcryptstats.c +@@ -0,0 +1,2418 @@ ++/* ++ * zcryptstats - Show usage statistics of IBM Crypto Express adapters ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/util_base.h" ++#include "lib/util_file.h" ++#include "lib/util_libc.h" ++#include "lib/util_opt.h" ++#include "lib/util_path.h" ++#include "lib/util_prg.h" ++#include "lib/util_rec.h" ++#include "lib/util_scandir.h" ++#include "lib/zt_common.h" ++ ++#ifndef offsetof ++ #define offsetof(type, member) ((size_t) &((type *)0)->member) ++#endif ++#ifndef offsetofend ++ #define offsetofend(type, member) \ ++ (offsetof(type, member) + sizeof(((type *)0)->member)) ++#endif ++ ++#define SYSFS_DEVICES_AP_PATH "devices/ap" ++#define SYSFS_DEVICES_CARD "devices/ap/card%02x" ++#define SYSFS_DEVICES_APQN "devices/ap/card%02x/%02x.%04x" ++#define SYSFS_DEVICES_CARD_ONLINE "devices/ap/card%02x/online" ++#define SYSFS_DEVICES_APQN_ONLINE "devices/ap/card%02x/%02x.%04x/online" ++#define CHSC_DEVICE "/dev/chsc" ++#define NUM_CARDS_OLD 64 ++#define NUM_CARDS 256 ++#define NUM_DOMAINS 256 ++ ++#define MASK_WORD_BITS (sizeof(uint32_t) * 8) ++#define MASK_WORD_NO(n) ((n) / MASK_WORD_BITS) ++#define MASK_BIT_IN_WORD(n) ((n) % MASK_WORD_BITS) ++#define MASK_BIT(n) (0x80000000 >> MASK_BIT_IN_WORD(n)) ++ ++struct chsc_apdn { ++ uint8_t ap_index; ++ uint8_t domain_index; ++} __packed; ++ ++struct chsc_scdmd_request { ++ struct chsc_header header; ++ struct chsc_apdn first_drid; ++ struct chsc_apdn last_drid; ++ uint32_t s:1; ++ uint32_t reserved1:31; ++ uint32_t reserved2; ++ uint32_t apsm[8]; ++ uint32_t dsm[8]; ++} __packed; ++ ++struct chsc_scdmd_response { ++ struct chsc_header header; ++ uint32_t reserved1; ++ uint16_t p:1; ++ uint16_t reserved2:15; ++ struct chsc_apdn crid; ++ uint32_t reserved3; ++} __packed; ++ ++struct chsc_scdmd_area { ++ struct chsc_scdmd_request request; ++ struct chsc_scdmd_response response; ++ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scdmd_request) - ++ sizeof(struct chsc_scdmd_response)]; ++} __packed; ++ ++struct chsc_scmd_request { ++ struct chsc_header header; ++ uint8_t reserved1; ++ uint8_t zeros1:6; ++ uint8_t one:1; ++ uint8_t zero:1; ++ uint8_t fcs; ++ uint8_t lcs; ++ uint32_t reserved2; ++ uint32_t reserved3; ++} __packed; ++ ++struct chsc_scmd_response { ++ struct chsc_header header; ++ uint32_t reserved1; ++ uint32_t p:1; ++ uint32_t reserved2:31; ++ uint32_t reserved3; ++} __packed; ++ ++struct chsc_scmd_area { ++ struct chsc_scmd_request request; ++ struct chsc_scmd_response response; ++ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scmd_request) - ++ sizeof(struct chsc_scmd_response)]; ++} __packed; ++ ++struct chsc_cmb_header { ++ uint8_t reserved1; ++ uint8_t ct; /* AP_DEVICE_TYPE_xxx values */ ++ uint8_t format; ++ uint8_t ax; ++ float s; ++ uint32_t v; ++ uint8_t dx; ++ uint8_t mt; ++ uint16_t l4; ++} __packed; ++ ++struct chsc_cmb_entry { ++ u64 t; ++ u64 c; ++} __packed; ++ ++struct chsc_cmb_area { ++ struct chsc_cmb_header header; ++ struct chsc_cmb_entry entries[32]; ++} __packed; ++ ++#define CRYPTO_TYPE_PCICC 3 ++#define CRYPTO_TYPE_PCICA 4 ++#define CRYPTO_TYPE_PCIXCC 5 ++#define CRYPTO_TYPE_CEX2A 6 ++#define CRYPTO_TYPE_CEX2C 7 ++#define CRYPTO_TYPE_CEX3A 8 ++#define CRYPTO_TYPE_CEX3C 9 ++#define CRYPTO_TYPE_CEX4S 10 ++#define CRYPTO_TYPE_CEX5S 11 ++#define CRYPTO_TYPE_CEX6S 12 ++ ++#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S ++ ++struct crypto_counter { ++ const char *name; ++ bool is_totals; ++}; ++ ++struct crypto_mode { ++ const char *name; ++ char indicatior_char; ++ unsigned int num_counters; ++ const struct crypto_counter *counters; ++}; ++ ++struct crypto_type { ++ const char *name; ++ unsigned int num_modes; ++ const struct crypto_mode *modes; ++}; ++ ++#define NUM_COPROC_COUNTERS 2 ++const struct crypto_counter counter_coproc[NUM_COPROC_COUNTERS] = { ++ { .name = "All", .is_totals = true }, ++ { .name = "RSA Key-gen" }, ++}; ++ ++#define NUM_ACCEL_COUNTERS 6 ++const struct crypto_counter counter_accel[NUM_ACCEL_COUNTERS] = { ++ { .name = "RSA 1024 ME" }, ++ { .name = "RSA 2048 ME" }, ++ { .name = "RSA 1024 CRT" }, ++ { .name = "RSA 2048 CRT" }, ++ { .name = "RSA 4096 ME" }, ++ { .name = "RSA 4096 CTR" }, ++}; ++ ++#define NUM_EP11_COUNTERS 5 ++const struct crypto_counter counter_ep11[NUM_EP11_COUNTERS] = { ++ { .name = "Asym. Slow" }, ++ { .name = "Asym. Fast" }, ++ { .name = "Symm. Partial" }, ++ { .name = "Symm. Complete" }, ++ { .name = "Asym. Key-gen" }, ++}; ++ ++#define NUM_PCICA_COUNTERS 20 ++const struct crypto_counter counter_pcica[NUM_PCICA_COUNTERS] = { ++ { .name = "RSA 1024 ME (E0)" }, ++ { .name = "RSA 2048 ME (E0)" }, ++ { .name = "RSA 1024 CRT (E0)" }, ++ { .name = "RSA 2048 CRT (E0)" }, ++ { .name = "RSA 1024 ME (E1)" }, ++ { .name = "RSA 2048 ME (E1)" }, ++ { .name = "RSA 1024 CRT (E1)" }, ++ { .name = "RSA 2048 CRT (E1)" }, ++ { .name = "RSA 1024 ME (E2)" }, ++ { .name = "RSA 2048 ME (E2)" }, ++ { .name = "RSA 1024 CRT (E2)" }, ++ { .name = "RSA 2048 CRT (E2)" }, ++ { .name = "RSA 1024 ME (E3)" }, ++ { .name = "RSA 2048 ME (E3)" }, ++ { .name = "RSA 1024 CRT (E3)" }, ++ { .name = "RSA 2048 CRT (E3)" }, ++ { .name = "RSA 1024 ME (E4)" }, ++ { .name = "RSA 2048 ME (E4)" }, ++ { .name = "RSA 1024 CRT (E4)" }, ++ { .name = "RSA 2048 CRT (E4)" }, ++}; ++ ++#define NUM_COPROC_MODES 1 ++const struct crypto_mode mode_coproc[1] = { ++ { .num_counters = NUM_COPROC_COUNTERS, ++ .counters = counter_coproc}, ++}; ++ ++#define NUM_ACCEL_MODES 1 ++const struct crypto_mode mode_accel[1] = { ++ { .num_counters = NUM_ACCEL_COUNTERS, ++ .counters = counter_accel }, ++}; ++ ++#define NUM_PCICA_MODES 1 ++const struct crypto_mode mode_pcica[1] = { ++ { .num_counters = NUM_PCICA_COUNTERS, ++ .counters = counter_pcica }, ++}; ++ ++#define NUM_CEX456_MODES 11 ++const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { .name = "Accelerator", .indicatior_char = 'A', ++ .num_counters = NUM_ACCEL_COUNTERS, ++ .counters = counter_accel }, ++ { .name = "CCA co-processor", .indicatior_char = 'C', ++ .num_counters = NUM_COPROC_COUNTERS, ++ .counters = counter_coproc}, ++ { .name = "EP11 co-processor", .indicatior_char = 'P', ++ .num_counters = NUM_EP11_COUNTERS, ++ .counters = counter_ep11 }, ++}; ++ ++#define NUM_CRYPTO_TYPES 13 ++const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { ++ { 0 }, ++ { 0 }, ++ { 0 }, ++ { .name = "PCICC", .num_modes = NUM_COPROC_MODES, ++ .modes = mode_coproc }, ++ { .name = "PCICA", .num_modes = NUM_PCICA_MODES, ++ .modes = mode_pcica }, ++ { .name = "PCIXCC", .num_modes = NUM_COPROC_MODES, ++ .modes = mode_coproc}, ++ { .name = "CEX2A", .num_modes = NUM_ACCEL_MODES, ++ .modes = mode_accel }, ++ { .name = "CEX2C", .num_modes = NUM_COPROC_MODES, ++ .modes = mode_coproc }, ++ { .name = "CEX3A", .num_modes = NUM_ACCEL_MODES, ++ .modes = mode_accel }, ++ { .name = "CEX3C", .num_modes = NUM_COPROC_MODES, ++ .modes = mode_coproc }, ++ { .name = "CEX4", .num_modes = NUM_CEX456_MODES, ++ .modes = mode_cex456 }, ++ { .name = "CEX5", .num_modes = NUM_CEX456_MODES, ++ .modes = mode_cex456 }, ++ { .name = "CEX6", .num_modes = NUM_CEX456_MODES, ++ .modes = mode_cex456 }, ++}; ++ ++ ++struct type_mapping { ++ uint8_t from_type; ++ uint8_t from_mode; ++ uint8_t to_type; ++ uint8_t to_mode; ++ struct type_mapping *next; ++}; ++ ++struct device_selection { ++ int card; ++ int domain; /* -1 if not specified */ ++ struct device_selection *next; ++}; ++ ++struct interval_data { ++ bool current_valid; ++ bool previous_valid; ++ struct chsc_cmb_area current; ++ struct chsc_cmb_area previous; ++}; ++ ++struct card_data { ++ struct interval_data data; ++ struct interval_data *domains[NUM_DOMAINS]; ++}; ++ ++struct interval_values { ++ u64 count; ++ double rate; ++ double utilization; ++ double duration; ++}; ++ ++struct print_func { ++ int (*print_initialize)(void); ++ int (*print_terminate)(void); ++ int (*print_header)(void); ++ int (*print_footer)(void); ++ int (*print_interval_header)(unsigned long interval_count, ++ const char *timestamp); ++ int (*print_interval_footer)(void); ++ int (*print_device_header)(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *type, const char *timestamp); ++ int (*print_device_footer)(void); ++ int (*print_counter_data)(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *type, const char *timestamp, ++ const char *name, ++ struct interval_values *vals); ++ int (*print_counter_separator)(void); ++}; ++ ++#define pr_call(func) g.print_funcs->func == NULL ? 0 : g.print_funcs->func ++ ++static int default_print_initialize(void); ++static int default_print_terminate(void); ++static int default_print_header(void); ++static int default_print_footer(void); ++static int default_print_interval_header(unsigned long interval_count, ++ const char *timestamp); ++static int default_print_device_header(bool is_apqn, uint8_t card, ++ uint8_t domain, const char *type, ++ const char *timestamp); ++static int default_print_device_footer(void); ++static int default_print_counter_data(bool is_apqn, uint8_t card, ++ uint8_t domain, const char *type, ++ const char *timestamp, const char *name, ++ struct interval_values *vals); ++static int default_print_counter_separator(void); ++ ++static const struct print_func default_print = { ++ .print_initialize = default_print_initialize, ++ .print_terminate = default_print_terminate, ++ .print_header = default_print_header, ++ .print_footer = default_print_footer, ++ .print_interval_header = default_print_interval_header, ++ .print_device_header = default_print_device_header, ++ .print_device_footer = default_print_device_footer, ++ .print_counter_data = default_print_counter_data, ++ .print_counter_separator = default_print_counter_separator, ++}; ++ ++static int json_print_initialize(void); ++static int json_print_header(void); ++static int json_print_footer(void); ++static int json_print_interval_header(unsigned long interval_count, ++ const char *timestamp); ++static int json_print_interval_footer(void); ++static int json_print_device_header(bool is_apqn, uint8_t card, ++ uint8_t domain, const char *type, ++ const char *timestamp); ++static int json_print_device_footer(void); ++static int json_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *type, const char *timestamp, ++ const char *name, ++ struct interval_values *vals); ++ ++static const struct print_func json_print = { ++ .print_initialize = json_print_initialize, ++ .print_header = json_print_header, ++ .print_footer = json_print_footer, ++ .print_interval_header = json_print_interval_header, ++ .print_interval_footer = json_print_interval_footer, ++ .print_device_header = json_print_device_header, ++ .print_device_footer = json_print_device_footer, ++ .print_counter_data = json_print_counter_data, ++}; ++ ++ ++static int table_print_initialize(void); ++static int table_print_terminate(void); ++static int table_print_header(void); ++static int table_print_interval_footer(void); ++static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *type, const char *timestamp, ++ const char *name, ++ struct interval_values *vals); ++ ++static const struct print_func table_print = { ++ .print_initialize = table_print_initialize, ++ .print_terminate = table_print_terminate, ++ .print_header = table_print_header, ++ .print_interval_footer = table_print_interval_footer, ++ .print_counter_data = table_print_counter_data, ++}; ++ ++static int csv_print_initialize(void); ++static int csv_print_terminate(void); ++static int csv_print_header(void); ++static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *type, const char *timestamp, ++ const char *name, ++ struct interval_values *vals); ++ ++static const struct print_func csv_print = { ++ .print_initialize = csv_print_initialize, ++ .print_terminate = csv_print_terminate, ++ .print_header = csv_print_header, ++ .print_counter_data = csv_print_counter_data, ++}; ++ ++/* ++ * Program configuration ++ */ ++const struct util_prg prg = { ++ .desc = "Display usage statistics of IBM Crypto Express adapters", ++ .args = "[DEVICE_IDS]", ++ .copyright_vec = { ++ { ++ .owner = "IBM Corp.", ++ .pub_first = 2019, ++ .pub_last = 2019, ++ }, ++ UTIL_PRG_COPYRIGHT_END ++ } ++}; ++ ++/* ++ * Global variables for program options ++ */ ++static struct zcryptstats_globals { ++ long interval; ++ unsigned long count; ++ bool no_totals; ++ bool only_totals; ++ bool no_apqn; ++ char *map_type; ++ char **device_ids; ++ bool all; ++ bool only_online; ++ bool verbose; ++ int chsc_fd; ++ uint8_t max_card_used; ++ uint32_t card_mask[8]; ++ uint8_t min_card; ++ uint8_t max_card; ++ uint32_t domain_mask[8]; ++ uint8_t min_domain; ++ uint8_t max_domain; ++ struct device_selection *dev_selection; ++ struct type_mapping *type_mapping; ++ struct card_data *cards[NUM_CARDS]; ++ const struct print_func *print_funcs; ++ struct util_rec *device_rec; ++ struct util_rec *counter_rec; ++ bool first_device; ++ bool first_counter; ++} g = { ++ .interval = 10, ++ .chsc_fd = -1, ++ .print_funcs = &default_print, ++}; ++ ++ ++static volatile bool quit; ++ ++/* ++ * Configuration of command line options ++ */ ++static struct util_opt opt_vec[] = { ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ }, ++ { ++ .option = {"interval", required_argument, NULL, 'i'}, ++ .argument = "INTERVAL", ++ .desc = "Specifies the interval time in seconds. If omitted, a " ++ "default interval of 10 seconds is used", ++ }, ++ { ++ .option = {"count", required_argument, NULL, 'c'}, ++ .argument = "COUNT", ++ .desc = "Specifies the number of reports that are generated " ++ "at INTERVAL seconds apart. If omitted, reports are " ++ "generated continuously, until stopped with control-C", ++ }, ++ { ++ .option = {"output", required_argument, NULL, 'o'}, ++ .argument = "JSON|TABLE|CSV", ++ .desc = "Displays the statistics in the specified format. If " ++ "this option is omitted, a comprehensive report is " ++ "displayed. Supported output formats are: JSON, TABLE, " ++ "CSV. With TABLE and CSV the display of the individual " ++ "counters are omitted, and only the totals are " ++ "displayed. CSV and TABLE output formats imply option " ++ "--only-totals", ++ }, ++ { ++ .option = {"no-totals", 0, NULL, 't'}, ++ .desc = "Excludes the totals of all counters of a card " ++ "device or queue device (APQN). It can not be " ++ "specified together with option --only-totals or " ++ "option --output TABLE|CSV", ++ }, ++ { ++ .option = {"only-totals", 0, NULL, 'T'}, ++ .desc = "Displays only the totals of all counters of a card " ++ "device or a queue device (APQN), but not the " ++ "individual counters. This option is implied with " ++ "option --output TABLE|CSV", ++ }, ++ { ++ .option = {"no-apqn", 0, NULL, 'a'}, ++ .desc = "Displays only the counters of the card device, but " ++ "omits the counters of the queue device (APQN). If the " ++ "system does not support obtaining cryptographic " ++ "performance measurement data on the queue devices, " ++ "then this option is implied", ++ }, ++ { ++ .option = {"map-type", required_argument, NULL, 'M'}, ++ .argument = "MAPPING", ++ .desc = "Maps unknown cryptographic device types and modes to " ++ "known types and modes. This option should only be " ++ "used when new, so far unknown cryptographic devices " ++ "are found. You can then map them to known devices and " ++ "modes, provided that the new cryptographic devices " ++ "report the same counters as the known cryptographic " ++ "device to which it is mapped. The mapping " ++ "specification consists of a comma-separated list of " ++ "FROM-TYPE:FROM-MODE=TO-TYPE:TO-MODE specifications. " ++ "The type and mode values must be specified in decimal " ++ "notation", ++ }, ++ { ++ .option = {"all", 0, NULL, 'A'}, ++ .desc = "Displays all cards devices and queue devices (APQNs), " ++ "not only those that are available to the Linux " ++ "system. Using this option additional cryptographic " ++ "devices that are available in the CEC, but not " ++ "available to the Linux system are also monitored. " ++ "This option can not be specified together with option " ++ "--only-online", ++ }, ++ { ++ .option = {"only-online", 0, NULL, 'O'}, ++ .desc = "Displays only online cards devices and queue devices " ++ "(APQNs). This option can not be specified together " ++ "with option --all" ++ }, ++ { ++ .option = {"verbose", 0, NULL, 'V'}, ++ .desc = "Prints additional information messages during " ++ "processing", ++ }, ++ UTIL_OPT_HELP, ++ UTIL_OPT_VERSION, ++ UTIL_OPT_END ++}; ++ ++#define pr_verbose(fmt...) do { \ ++ if (g.verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++/* ++ * Describe adapter ids ++ */ ++static void print_adapter_id_help(void) ++{ ++ printf("\n"); ++ printf("DEVICE_IDS\n"); ++ util_print_indented(" List of cryptographic device IDs separated by " ++ "blanks for which statistics are displayed. " ++ "DEVICE_ID can either be a card device ID " ++ "('') or a queue device ID (." ++ "'). To filter all devices by domain, " ++ "provide '.'. If no IDs are given, " ++ "statistics are displayed for all available " ++ "devices.", 2); ++ printf("\n"); ++ printf("EXAMPLE:\n"); ++ util_print_indented(" Display statistics for all cryptographic " ++ "devices with card ID '02.", 2); ++ printf(" # zcryptstats 02\n"); ++ printf("\n"); ++ util_print_indented(" Display statistics for cryptographic devices " ++ "with card ID '02' and domain ID '0005'.", 2); ++ printf(" # zcryptstats 02.0005\n"); ++ printf("\n"); ++} ++ ++static struct type_mapping *find_type_mapping(uint8_t from_type, ++ uint8_t from_mode) ++{ ++ struct type_mapping *map = g.type_mapping; ++ ++ while (map != NULL) { ++ if (map->from_type == from_type && map->from_mode == from_mode) ++ return map; ++ map = map->next; ++ } ++ return NULL; ++} ++ ++/* ++ * Get the name of the card for a crypto type and mode. ++ * Note: This function might return the address of a static string variable. ++ * It is only valid until this function is called again. ++ */ ++static const char *get_card_name(uint8_t type, uint8_t mode) ++{ ++ const struct crypto_type *ct; ++ const struct crypto_mode *m; ++ static char temp_name[250]; ++ struct type_mapping *map; ++ ++ map = find_type_mapping(type, mode); ++ if (map != NULL) { ++ type = map->to_type; ++ mode = map->to_mode; ++ } else if (type >= NUM_CRYPTO_TYPES) { ++ type = CRYPTO_TYPE_TOLERATION; ++ } ++ ++ if (type >= NUM_CRYPTO_TYPES) ++ return "UNKNOWN ADAPTER TYPE"; ++ ++ ct = &crypto_types[type]; ++ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) ++ return "UNKNOWN ADAPTER TYPE"; ++ ++ if (mode >= ct->num_modes) ++ return ct->name; ++ ++ m = &ct->modes[mode]; ++ snprintf(temp_name, sizeof(temp_name) - 1, "%s%c (%s)", ct->name, ++ m->indicatior_char, m->name != NULL ? m->name : ""); ++ return temp_name; ++} ++ ++/* ++ * Get the name of a counter for a crypto type, mode and index. ++ * Note: This function might return the address of a static string variable. ++ * It is only valid until this function is called again. ++ */ ++static const char *get_counter_name(uint8_t type, uint8_t mode, uint8_t index) ++{ ++ const struct crypto_type *ct; ++ const struct crypto_mode *m; ++ static char temp_name[250]; ++ struct type_mapping *map; ++ ++ map = find_type_mapping(type, mode); ++ if (map != NULL) { ++ type = map->to_type; ++ mode = map->to_mode; ++ } else if (type >= NUM_CRYPTO_TYPES) { ++ type = CRYPTO_TYPE_TOLERATION; ++ } ++ ++ if (type >= NUM_CRYPTO_TYPES) ++ goto generic; ++ ++ ct = &crypto_types[type]; ++ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) ++ goto generic; ++ ++ if (mode >= ct->num_modes) ++ goto generic; ++ ++ m = &ct->modes[mode]; ++ if (m->counters == NULL || m->num_counters == 0) ++ goto generic; ++ ++ if (index >= m->num_counters) ++ goto generic; ++ ++ return m->counters[index].name; ++ ++generic: ++ snprintf(temp_name, sizeof(temp_name) - 1, "COUNTER %u", index); ++ return temp_name; ++} ++ ++/* ++ * Returns true if a counter for a crypto type, mode and index represents the ++ * total number of operations. ++ */ ++static bool is_counter_totals(uint8_t type, uint8_t mode, uint8_t index) ++{ ++ const struct crypto_type *ct; ++ const struct crypto_mode *m; ++ struct type_mapping *map; ++ ++ map = find_type_mapping(type, mode); ++ if (map != NULL) { ++ type = map->to_type; ++ mode = map->to_mode; ++ } else if (type >= NUM_CRYPTO_TYPES) { ++ type = CRYPTO_TYPE_TOLERATION; ++ } ++ ++ if (type >= NUM_CRYPTO_TYPES) ++ return false; ++ ++ ct = &crypto_types[type]; ++ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) ++ return false; ++ ++ if (mode >= ct->num_modes) ++ return false; ++ ++ m = &ct->modes[mode]; ++ if (m->counters == NULL || m->num_counters == 0) ++ return false; ++ ++ if (index >= m->num_counters) ++ return false; ++ ++ return m->counters[index].is_totals; ++} ++ ++/* ++ * Returns true if the card is available ++ */ ++static bool is_card_available(uint8_t card) ++{ ++ char *path; ++ bool ret; ++ ++ path = util_path_sysfs(SYSFS_DEVICES_CARD, card); ++ ret = util_path_is_dir(path); ++ free(path); ++ ++ return ret; ++} ++ ++/* ++ * Returns true if the APQN is available ++ */ ++static bool is_apqn_available(uint8_t card, uint8_t domain) ++{ ++ char *path; ++ bool ret; ++ ++ path = util_path_sysfs(SYSFS_DEVICES_APQN, card, card, domain); ++ ret = util_path_is_dir(path); ++ free(path); ++ ++ return ret; ++} ++ ++/* ++ * Returns true if the card is online ++ */ ++static bool is_card_online(uint8_t card) ++{ ++ unsigned long online; ++ char *path; ++ int rc; ++ ++ path = util_path_sysfs(SYSFS_DEVICES_CARD_ONLINE, card); ++ rc = util_file_read_ul(&online, 10, path); ++ free(path); ++ ++ return rc == 0 && online != 0; ++} ++ ++/* ++ * Returns true if the APQN is online ++ */ ++static bool is_apqn_online(uint8_t card, uint8_t domain) ++{ ++ unsigned long online; ++ char *path; ++ int rc; ++ ++ path = util_path_sysfs(SYSFS_DEVICES_APQN_ONLINE, card, card, domain); ++ rc = util_file_read_ul(&online, 10, path); ++ free(path); ++ ++ if (rc != 0) ++ return false; ++ ++ return online != 0; ++} ++ ++/* ++ * Updates the APQNs data with data for the current interval. ++ */ ++static void update_apqn_data(uint8_t card, uint8_t domain, ++ struct chsc_cmb_area *cmb, size_t cmb_len) ++{ ++ struct card_data *cd = g.cards[card]; ++ struct interval_data *dd; ++ ++ if (cd == NULL) { ++ cd = util_malloc(sizeof(struct card_data)); ++ memset(cd, 0, sizeof(struct card_data)); ++ g.cards[card] = cd; ++ ++ pr_verbose("Card %02x added", card); ++ } ++ ++ dd = cd->domains[domain]; ++ if (dd == NULL) { ++ dd = util_malloc(sizeof(struct interval_data)); ++ memset(dd, 0, sizeof(struct interval_data)); ++ cd->domains[domain] = dd; ++ ++ pr_verbose("APQN %02x.%04x added", card, domain); ++ } else { ++ if (!dd->current_valid) { ++ dd->previous = dd->current; ++ dd->previous_valid = true; ++ } ++ } ++ ++ memset(&dd->current, 0, sizeof(struct chsc_cmb_area)); ++ memcpy(&dd->current, cmb, cmb_len); ++ dd->current_valid = true; ++} ++ ++/* ++ * Updates the card's data with data for the current interval. ++ */ ++static void update_card_data(uint8_t card, struct chsc_cmb_area *cmb, ++ size_t cmb_len) ++{ ++ struct card_data *cd = g.cards[card]; ++ ++ if (cd == NULL) { ++ cd = util_malloc(sizeof(struct card_data)); ++ memset(cd, 0, sizeof(struct card_data)); ++ g.cards[card] = cd; ++ ++ pr_verbose("Card %02x added", card); ++ } else { ++ if (!cd->data.current_valid) { ++ cd->data.previous = cd->data.current; ++ cd->data.previous_valid = true; ++ } ++ }; ++ ++ memset(&cd->data.current, 0, sizeof(struct chsc_cmb_area)); ++ memcpy(&cd->data.current, cmb, cmb_len); ++ cd->data.current_valid = true; ++} ++ ++/* ++ * Frees the interval data of a card ++ */ ++static void free_card_data(struct card_data *cd) ++{ ++ struct interval_data *dd; ++ int domain; ++ ++ if (cd == NULL) ++ return; ++ ++ for (domain = 0; domain < NUM_DOMAINS; domain++) { ++ dd = cd->domains[domain]; ++ if (dd == NULL) ++ continue; ++ ++ free(dd); ++ cd->domains[domain] = NULL; ++ } ++ ++ free(cd); ++} ++ ++/* ++ * Frees the interval data ++ */ ++static void free_interval_data(void) ++{ ++ struct card_data *cd; ++ int card; ++ ++ for (card = 0; card < NUM_CARDS; card++) { ++ cd = g.cards[card]; ++ if (cd == NULL) ++ continue; ++ ++ free_card_data(cd); ++ g.cards[card] = NULL; ++ } ++} ++ ++/* ++ * Returns the highest card index used by the system. ++ * If there is a card with an index > 0x3f, then the returned number is 0xff, ++ * else 0x3f is returned. ++ */ ++static int get_max_card_index(uint8_t *max_index) ++{ ++ struct dirent **dev_vec = NULL; ++ int i, count, card, rc = 0; ++ char *path; ++ ++ path = util_path_sysfs(SYSFS_DEVICES_AP_PATH); ++ if (!util_path_is_dir(path)) { ++ warnx("Crypto device driver is not available"); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ count = util_scandir(&dev_vec, NULL, path, "card[0-9a-fA-F]+"); ++ if (count < 1) { ++ warnx("No crypto card devices found"); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ *max_index = NUM_CARDS_OLD - 1; ++ for (i = 0; i < count; i++) { ++ if (sscanf(dev_vec[i]->d_name, "card%x", &card) != 1) ++ continue; ++ if (card >= NUM_CARDS_OLD) ++ *max_index = NUM_CARDS - 1; ++ } ++ ++ pr_verbose("Max card index used: %u", *max_index); ++ ++out: ++ free(path); ++ if (dev_vec != NULL) ++ free(dev_vec); ++ return rc; ++} ++ ++/* ++ * Returns the size of the CMB. The size is either contained in l4 field ++ * of the cmb, or a fix length dependent on the crypto type, if l4 is zero. ++ */ ++static size_t get_cmb_length(struct chsc_cmb_area *cmb) ++{ ++ size_t len = cmb->header.l4; ++ ++ if (len != 0) ++ return len; ++ ++ switch (cmb->header.ct) { ++ case CRYPTO_TYPE_PCICC: ++ case CRYPTO_TYPE_PCIXCC: ++ case CRYPTO_TYPE_CEX2C: ++ case CRYPTO_TYPE_CEX3C: ++ return 64; ++ case CRYPTO_TYPE_PCICA: ++ return 336; ++ case CRYPTO_TYPE_CEX2A: ++ case CRYPTO_TYPE_CEX3A: ++ return 80; ++ default: ++ warnx("Zero length value in CMB"); ++ return 0; ++ } ++} ++ ++/* ++ * Return true if the device is in the device selection list and mask ++ */ ++static bool filter_device(uint8_t card, uint8_t domain, bool is_apqn) ++{ ++ struct device_selection *dev; ++ bool found; ++ ++ /* Check for selection mask */ ++ if ((g.card_mask[MASK_WORD_NO(card)] & ++ MASK_BIT(card)) == 0) { ++ pr_verbose("Skipping card %02x (mask)", card); ++ return false; ++ } ++ if (is_apqn) { ++ if ((g.domain_mask[MASK_WORD_NO(domain)] & ++ MASK_BIT(domain)) == 0) { ++ pr_verbose("Skipping APQN %02x.%04x (mask)", card, ++ domain); ++ return false; ++ } ++ } ++ ++ /* Check for device selection list */ ++ if (g.dev_selection != NULL) { ++ dev = g.dev_selection; ++ found = false; ++ while (dev != NULL) { ++ if (is_apqn == false) { ++ /* Its a card */ ++ if (card == (dev->card >= 0 ? dev->card : ++ card)) { ++ found = true; ++ break; ++ } ++ } else { ++ /* Its an APQN */ ++ if (card == (dev->card >= 0 ? dev->card : ++ card) && ++ domain == (dev->domain >= 0 ? dev->domain : ++ domain)) { ++ found = true; ++ break; ++ } ++ } ++ ++ dev = dev->next; ++ } ++ ++ if (!found) { ++ if (is_apqn) ++ pr_verbose("Skipping APQN %02x.%04x " ++ "(selection)", card, domain); ++ else ++ pr_verbose("Skipping card %02x (selection)", ++ card); ++ return false; ++ } ++ } ++ ++ if (g.all) ++ return true; ++ ++ /* Check if card/APQN is available in the system (SYSFS) */ ++ if (!is_card_available(card)) { ++ pr_verbose("Skipping card %02x (not available)", card); ++ return false; ++ } ++ if (is_apqn && !is_apqn_available(card, domain)) { ++ pr_verbose("Skipping APQN %02x.%04x (not available)", ++ card, domain); ++ return false; ++ } ++ ++ if (g.only_online) { ++ /* Check if card/APQN is online */ ++ if (!is_card_online(card)) { ++ pr_verbose("Skipping card %02x (not online)", card); ++ return false; ++ } ++ if (is_apqn && !is_apqn_online(card, domain)) { ++ pr_verbose("Skipping APQN %02x.%04x (not online)", ++ card, domain); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * Process a crypto measurement block. ++ * Passes back the actual length of the CMB processed and its card number. ++ * Returns -ENODEV when the CMB is skipped. ++ */ ++static int process_cmb(struct chsc_cmb_area *cmb, size_t size, size_t *length, ++ uint8_t *card) ++{ ++ size_t len; ++ ++ len = get_cmb_length(cmb); ++ if (len == 0) ++ return -EINVAL; ++ ++ if (len > size) { ++ warnx("Length value in CMB exceeds size of CMB"); ++ return -EINVAL; ++ } ++ ++ if (card != NULL) ++ *card = cmb->header.ax; ++ if (length != NULL) ++ *length = len; ++ ++ if (filter_device(cmb->header.ax, cmb->header.dx, ++ cmb->header.format == 1) == false) ++ return -ENODEV; ++ ++ if (cmb->header.format == 1) ++ update_apqn_data(cmb->header.ax, cmb->header.dx, cmb, len); ++ else ++ update_card_data(cmb->header.ax, cmb, len); ++ ++ return 0; ++} ++ ++/* ++ * Translate the CHSC response code to an error (0 or negative errno) ++ */ ++static int chsc_error_from_response(int response) ++{ ++ if (response != 0x0001) ++ pr_verbose("CHSC Response code: %04x", response); ++ ++ switch (response) { ++ case 0x0001: ++ return 0; ++ case 0x0002: ++ return -EOPNOTSUPP; ++ case 0x0003: ++ case 0x0006: ++ case 0x0007: ++ case 0x0008: ++ case 0x000a: ++ case 0x0103: ++ case 0x0104: ++ return -EINVAL; ++ case 0x0004: ++ return -EOPNOTSUPP; ++ case 0x000b: ++ return -EBUSY; ++ case 0x0102: ++ return -ENOMEM; ++ case 0x0105: ++ return -EACCES; ++ case 0x0100: ++ case 0x0107: ++ return -ENODEV; ++ default: ++ return -EIO; ++ } ++} ++ ++/* ++ * Process the APQN measurement data and extract the CMBs ++ */ ++static int process_apqn_measurement_data(struct chsc_scdmd_area *scdmd_area) ++{ ++ size_t size = scdmd_area->response.header.length - ++ sizeof(struct chsc_scdmd_response); ++ size_t len, ofs = 0; ++ int rc; ++ ++ while (ofs < size) { ++ rc = process_cmb((struct chsc_cmb_area *) ++ &scdmd_area->response_data[ofs], ++ size - ofs, &len, NULL); ++ if (rc != 0 && rc != -ENODEV) ++ return rc; ++ ofs += len; ++ ++ } ++ ++ return 0; ++} ++ ++/* ++ * Get Crypto Measurement data on the APQN level ++ */ ++static int get_apqn_measurement_data(uint8_t card) ++{ ++ struct chsc_scdmd_area scdmd_area; ++ int rc; ++ ++ memset(&scdmd_area, 0, sizeof(scdmd_area)); ++ do { ++ scdmd_area.request.header.code = 0x102d; ++ scdmd_area.request.header.length = ++ sizeof(struct chsc_scdmd_request); ++ scdmd_area.request.first_drid.ap_index = card; ++ scdmd_area.request.first_drid.domain_index = g.min_domain; ++ scdmd_area.request.last_drid.ap_index = card; ++ scdmd_area.request.last_drid.domain_index = g.max_domain; ++ scdmd_area.request.s = 1; ++ scdmd_area.request.apsm[MASK_WORD_NO(card)] |= MASK_BIT(card); ++ memcpy(scdmd_area.request.dsm, g.domain_mask, ++ sizeof(scdmd_area.request.dsm)); ++ ++ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scdmd_area); ++ if (rc != 0) { ++ rc = -errno; ++ warnx("Failed to get APQN measurement data for card " ++ "%02x: %s", card, strerror(errno)); ++ break; ++ } ++ ++ rc = chsc_error_from_response(scdmd_area.response.header.code); ++ if (rc != 0) { ++ if (rc != -EOPNOTSUPP && rc != -ENODEV) { ++ warnx("Failed to get APQN crypto measurement " ++ "data for card %02x: %s", card, ++ strerror(-rc)); ++ } else { ++ pr_verbose("Failed to get APQN crypto " ++ "measurement data for card %02x: %s", ++ card, strerror(-rc)); ++ /* ++ * ignore return code other than -EOPNOTSUPP ++ * and -ENODEV ++ */ ++ rc = 0; ++ } ++ break; ++ } ++ ++ rc = process_apqn_measurement_data(&scdmd_area); ++ if (rc != 0) ++ break; ++ ++ if (scdmd_area.response.p) ++ scdmd_area.request.first_drid = ++ scdmd_area.response.crid; ++ } while (scdmd_area.response.p); ++ ++ return rc; ++} ++ ++/* ++ * Process the card measurement data and extract the CMBs ++ */ ++static int process_card_measurement_data(struct chsc_scmd_area *scmd_area, ++ uint8_t *last_card) ++{ ++ size_t size = scmd_area->response.header.length - ++ sizeof(struct chsc_scmd_response); ++ size_t len, ofs = 0; ++ int rc; ++ ++ while (ofs < size) { ++ rc = process_cmb((struct chsc_cmb_area *) ++ &scmd_area->response_data[ofs], ++ size - ofs, &len, last_card); ++ if (rc != 0 && rc != -ENODEV) ++ return rc; ++ ofs += len; ++ ++ if (rc == -ENODEV) ++ continue; ++ ++ if (!g.no_apqn) { ++ rc = get_apqn_measurement_data(*last_card); ++ if (rc != 0) ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Get Crypto Measurement data on the card level ++ */ ++static int get_card_measurement_data(void) ++{ ++ struct chsc_scmd_area scmd_area; ++ uint8_t last_card = 0; ++ int rc; ++ ++ memset(&scmd_area, 0, sizeof(scmd_area)); ++ do { ++ scmd_area.request.header.code = 0x102e; ++ scmd_area.request.header.length = ++ sizeof(struct chsc_scmd_request); ++ scmd_area.request.one = 1; ++ scmd_area.request.fcs = g.min_card; ++ scmd_area.request.lcs = g.max_card; ++ ++ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scmd_area); ++ if (rc != 0) { ++ rc = -errno; ++ warnx("Failed to get card measurement data: %s", ++ strerror(errno)); ++ break; ++ } ++ ++ rc = chsc_error_from_response(scmd_area.response.header.code); ++ if (rc != 0) { ++ warnx("Failed to get card crypto measurement data: %s", ++ strerror(-rc)); ++ break; ++ } ++ ++ rc = process_card_measurement_data(&scmd_area, &last_card); ++ if (rc != 0) ++ break; ++ ++ if (scmd_area.response.p) ++ scmd_area.request.fcs = last_card + 1; ++ } while (scmd_area.response.p && last_card < g.max_card); ++ ++ return rc; ++} ++ ++/* ++ * Signal handler for SIGALRM ++ */ ++static void alarm_handler(int UNUSED(sig)) ++{ ++ if (!quit) ++ alarm(g.interval); ++} ++ ++/* ++ * Signal handler for SIGINT and SIGTERM ++ */ ++static void int_handler(int UNUSED(sig)) ++{ ++ quit = true; ++ raise(SIGALRM); ++} ++ ++/* ++ * Calculates the time difference between tv1 and tv2 in seconds ++ */ ++static float time_diff(struct timeval *tv1, struct timeval *tv2) ++{ ++ struct timeval tv_diff; ++ ++ tv_diff.tv_sec = tv2->tv_sec - tv1->tv_sec; ++ tv_diff.tv_usec = tv2->tv_usec - tv1->tv_usec; ++ if (tv_diff.tv_usec < 0) { ++ tv_diff.tv_sec--; ++ tv_diff.tv_usec += 1000000; ++ } ++ ++ return (float)tv_diff.tv_sec + (float)(0.000001 * tv_diff.tv_usec); ++} ++ ++/* ++ * Initialize the default print format ++ */ ++static int default_print_initialize(void) ++{ ++ g.device_rec = util_rec_new_wide("-"); ++ util_rec_def(g.device_rec, "device", UTIL_REC_ALIGN_LEFT, 7, ++ "DEVICE"); ++ util_rec_def(g.device_rec, "kind", UTIL_REC_ALIGN_LEFT, 5, ""); ++ util_rec_def(g.device_rec, "type", UTIL_REC_ALIGN_LEFT, 33, ++ "TYPE"); ++ util_rec_def(g.device_rec, "time", UTIL_REC_ALIGN_LEFT, 20, ++ "TIMESTAMP"); ++ ++ g.counter_rec = util_rec_new_wide("-"); ++ util_rec_def(g.counter_rec, "name", UTIL_REC_ALIGN_LEFT, 18, ++ "COUNTER"); ++ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, ++ "OPS"); ++ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, ++ "RATE"); ++ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, ++ 12, "UTILIZATION"); ++ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, ++ 15, "AVG.DURATION"); ++ ++ return 0; ++} ++ ++/* ++ * Terminate the default print format ++ */ ++static int default_print_terminate(void) ++{ ++ util_rec_free(g.counter_rec); ++ util_rec_free(g.device_rec); ++ ++ return 0; ++} ++ ++/* ++ * Print the header lines for the default print format ++ */ ++static int default_print_header(void) ++{ ++ char timestamp[64]; ++ struct utsname un; ++ struct tm *tm; ++ time_t t; ++ ++ time(&t); ++ tm = localtime(&t); ++ strftime(timestamp, sizeof(timestamp), "%x", tm); ++ ++ if (uname(&un) != 0) ++ return -errno; ++ ++ printf("%s %s (%s) \t%s\t%s\n\n", un.sysname, un.release, ++ un.nodename, timestamp, un.machine); ++ ++ return 0; ++} ++ ++/* ++ * Print the footer lines for the default print format ++ */ ++static int default_print_footer(void) ++{ ++ printf("\n"); ++ return 0; ++} ++ ++/* ++ * Print the interval header lines for the default print format ++ */ ++static int default_print_interval_header(unsigned long interval_count, ++ const char *timestamp) ++{ ++ ++ printf("*****************************************************" ++ "***************\n"); ++ printf("TIME: %s\t\tINTERVAL: %lu\n\n", timestamp, interval_count); ++ return 0; ++} ++ ++/* ++ * Prints the separator lines in front of a device for the default print format ++ */ ++static int default_print_device_header(bool is_apqn, uint8_t card, ++ uint8_t domain, const char *type, ++ const char *timestamp) ++{ ++ if (is_apqn) ++ util_rec_set(g.device_rec, "device", "%02x.%04x", card, ++ domain); ++ else ++ util_rec_set(g.device_rec, "device", "%02x", card); ++ util_rec_set(g.device_rec, "kind", "%s", ++ is_apqn ? "APQN" : "CARD"); ++ util_rec_set(g.device_rec, "type", "%s", type); ++ util_rec_set(g.device_rec, "time", "%s", timestamp); ++ ++ util_rec_print_hdr(g.device_rec); ++ util_rec_print(g.device_rec); ++ printf("\n"); ++ ++ util_rec_set_indent(g.counter_rec, is_apqn ? 8 : 4); ++ util_rec_print_hdr(g.counter_rec); ++ ++ return 0; ++} ++ ++/* ++ * Prints the separator lines after a device for the default print format ++ */ ++static int default_print_device_footer(void) ++{ ++ ++ printf("\n"); ++ return 0; ++} ++ ++ ++/** ++ * Prints the counter data for the default print format ++ */ ++static int default_print_counter_data(bool UNUSED(is_apqn), ++ uint8_t UNUSED(card), ++ uint8_t UNUSED(domain), ++ const char *UNUSED(type), ++ const char *UNUSED(timestamp), ++ const char *name, ++ struct interval_values *vals) ++{ ++ util_rec_set(g.counter_rec, "name", "%s", name); ++ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); ++ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); ++ util_rec_set(g.counter_rec, "utilization", "%.2f %%", ++ vals->utilization * 100); ++ if (vals->duration >= 1) ++ util_rec_set(g.counter_rec, "duration", "%.3f sec ", ++ vals->duration); ++ else if (vals->duration >= 0.001) ++ util_rec_set(g.counter_rec, "duration", "%.3f msec", ++ vals->duration * 1000); ++ else ++ util_rec_set(g.counter_rec, "duration", "%.3f usec", ++ vals->duration * 1000000); ++ util_rec_print(g.counter_rec); ++ ++ return 0; ++} ++ ++/* ++ * Prints a separator between the counter lines and the totals line for the ++ * default print format ++ */ ++static int default_print_counter_separator(void) ++{ ++ util_rec_print_separator(g.counter_rec); ++ return 0; ++} ++ ++/* ++ * Initialize the JSON print format ++ */ ++static int json_print_initialize(void) ++{ ++ /* Use a decimal point to make JSON code compliant with RFC7159 */ ++ setlocale(LC_NUMERIC, "C"); ++ return 0; ++} ++ ++/* ++ * Print the header lines for the JSON print format ++ */ ++static int json_print_header(void) ++{ ++ char timestamp[64]; ++ struct utsname un; ++ struct tm *tm; ++ time_t t; ++ ++ time(&t); ++ tm = localtime(&t); ++ strftime(timestamp, sizeof(timestamp), "%x", tm); ++ ++ if (uname(&un) != 0) ++ return -errno; ++ ++ printf("{\"zcryptstats\": {\n"); ++ printf("\t\"host\": {\n"); ++ printf("\t\t\"nodename\": \"%s\",\n", un.nodename); ++ printf("\t\t\"sysname\": \"%s\",\n", un.sysname); ++ printf("\t\t\"release\": \"%s\",\n", un.release); ++ printf("\t\t\"machine\": \"%s\",\n", un.machine); ++ printf("\t\t\"date\": \"%s\",\n", timestamp); ++ printf("\t\t\"statistics\": [\n"); ++ ++ return 0; ++} ++ ++/* ++ * Print the footer lines for the JSON print format ++ */ ++static int json_print_footer(void) ++{ ++ printf("\n\t\t]\n"); ++ printf("\t}\n"); ++ printf("}}\n"); ++ ++ return 0; ++} ++ ++/* ++ * Print the interval header lines for the JSON print format ++ */ ++static int json_print_interval_header(unsigned long interval_count, ++ const char *timestamp) ++{ ++ if (interval_count > 1) ++ printf(",\n"); ++ printf("\t\t\t{\n"); ++ printf("\t\t\t\t\"interval\": %lu, \"timestamp\": \"%s\"," ++ " \"devices\": [\n", interval_count, timestamp); ++ ++ return 0; ++} ++ ++/* ++ * Print the interval footer lines for the JSON print format ++ */ ++static int json_print_interval_footer(void) ++{ ++ if (!g.first_device) ++ printf("\n"); ++ printf("\t\t\t\t]\n"); ++ printf("\t\t\t}"); ++ ++ return 0; ++} ++ ++/* ++ * Prints the separator lines in front of a device for the JSON print format ++ */ ++static int json_print_device_header(bool is_apqn, uint8_t card, ++ uint8_t domain, const char *type, ++ const char *UNUSED(timestamp)) ++{ ++ if (!g.first_device) ++ printf(",\n"); ++ printf("\t\t\t\t\t{"); ++ if (is_apqn) ++ printf("\"device\": \"%02x.%04x\"", card, ++ domain); ++ else ++ printf("\"device\": \"%02x\"", card); ++ printf(", \"type\": \"%s\",\n", type); ++ printf("\t\t\t\t\t \"counters\": [\n"); ++ ++ return 0; ++} ++ ++/* ++ * Prints the separator lines after a device for the JSON print format ++ */ ++static int json_print_device_footer(void) ++{ ++ if (!g.first_counter) ++ printf("\n"); ++ printf("\t\t\t\t\t ]}"); ++ ++ return 0; ++} ++ ++ ++/** ++ * Prints the counter data for the JSON print format ++ */ ++static int json_print_counter_data(bool UNUSED(is_apqn), ++ uint8_t UNUSED(card), ++ uint8_t UNUSED(domain), ++ const char *UNUSED(type), ++ const char *UNUSED(timestamp), ++ const char *name, ++ struct interval_values *vals) ++{ ++ if (!g.first_counter) ++ printf(",\n"); ++ printf("\t\t\t\t\t\t{\"counter\": \"%s\", \"ops\": %llu, " ++ "\"rate\": %.2f, \"utilization\": %.2f, \"duration\": %.9f}", ++ name, vals->count, vals->rate, vals->utilization * 100, ++ vals->duration); ++ ++ return 0; ++} ++ ++static int table_print_initialize(void) ++{ ++ g.counter_rec = util_rec_new_wide("-"); ++ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20, ++ "TIMESTAMP"); ++ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7, ++ "DEVICE"); ++ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, ++ "OPS"); ++ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, ++ "RATE"); ++ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, ++ 12, "UTILIZATION"); ++ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, ++ 15, "AVG.DURATION"); ++ return 0; ++} ++ ++static int table_print_terminate(void) ++{ ++ util_rec_free(g.counter_rec); ++ return 0; ++} ++ ++static int table_print_header(void) ++{ ++ int rc; ++ ++ rc = default_print_header(); ++ if (rc != 0) ++ return rc; ++ ++ util_rec_print_hdr(g.counter_rec); ++ return 0; ++} ++ ++static int table_print_interval_footer(void) ++{ ++ util_rec_print_separator(g.counter_rec); ++ return 0; ++} ++ ++static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *UNUSED(type), ++ const char *timestamp, ++ const char *UNUSED(name), ++ struct interval_values *vals) ++{ ++ if (is_apqn) ++ util_rec_set(g.counter_rec, "device", "%02x.%04x", card, ++ domain); ++ else ++ util_rec_set(g.counter_rec, "device", "%02x", card); ++ util_rec_set(g.counter_rec, "time", "%s", timestamp); ++ ++ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); ++ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); ++ util_rec_set(g.counter_rec, "utilization", "%.2f %%", ++ vals->utilization * 100); ++ if (vals->duration >= 1) ++ util_rec_set(g.counter_rec, "duration", "%.3f sec ", ++ vals->duration); ++ else if (vals->duration >= 0.001) ++ util_rec_set(g.counter_rec, "duration", "%.3f msec", ++ vals->duration * 1000); ++ else ++ util_rec_set(g.counter_rec, "duration", "%.3f usec", ++ vals->duration * 1000000); ++ ++ util_rec_print(g.counter_rec); ++ ++ return 0; ++} ++ ++static int csv_print_initialize(void) ++{ ++ /* Use a decimal point to not conflict with the colon separator char */ ++ setlocale(LC_NUMERIC, "C"); ++ ++ g.counter_rec = util_rec_new_csv(","); ++ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20, ++ "TIMESTAMP"); ++ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7, ++ "DEVICE"); ++ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, ++ "OPS"); ++ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, ++ "RATE"); ++ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, ++ 12, "UTILIZATION"); ++ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, ++ 15, "AVG.DURATION"); ++ return 0; ++} ++ ++static int csv_print_terminate(void) ++{ ++ util_rec_free(g.counter_rec); ++ return 0; ++} ++ ++static int csv_print_header(void) ++{ ++ util_rec_print_hdr(g.counter_rec); ++ return 0; ++} ++ ++ ++static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, ++ const char *UNUSED(type), ++ const char *timestamp, ++ const char *UNUSED(name), ++ struct interval_values *vals) ++{ ++ if (is_apqn) ++ util_rec_set(g.counter_rec, "device", "%02x.%04x", card, ++ domain); ++ else ++ util_rec_set(g.counter_rec, "device", "%02x", card); ++ util_rec_set(g.counter_rec, "time", "%s", timestamp); ++ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); ++ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); ++ util_rec_set(g.counter_rec, "utilization", "%.2f %%", ++ vals->utilization * 100); ++ util_rec_set(g.counter_rec, "duration", "%.9f", vals->duration); ++ ++ util_rec_print(g.counter_rec); ++ ++ return 0; ++} ++ ++/* ++ * Calculates number of ops, utilization, duration and rate of an ++ * interval from the timer values, scale and interval time. ++ */ ++static void calc_interval_values(struct chsc_cmb_entry *current, ++ struct chsc_cmb_entry *previous, ++ float scale, ++ float interval_time, ++ struct interval_values *result) ++{ ++ u64 tdiff; ++ ++ tdiff = current->t - previous->t; ++ result->count = current->c - previous->c; ++ result->utilization = (double)(tdiff) * scale / interval_time; ++ if (result->count > 0) ++ result->duration = (double)(tdiff) * scale / result->count; ++ else ++ result->duration = 0; ++ result->rate = (double)result->count / interval_time; ++} ++ ++/* ++ * Print the measurement data of an interval ++ */ ++static int print_interval_data(struct interval_data *data, ++ const char *timestamp, float interval_time) ++{ ++ struct chsc_cmb_entry total_current; ++ struct chsc_cmb_entry total_previous; ++ struct interval_values vals; ++ const char *type, *counter; ++ uint32_t mask = 0x80000000; ++ bool totals_found = false; ++ size_t len; ++ int i, rc; ++ ++ len = get_cmb_length(&data->current); ++ type = get_card_name(data->current.header.ct, data->current.header.mt); ++ ++ rc = pr_call(print_device_header)(data->current.header.format == 1, ++ data->current.header.ax, ++ data->current.header.dx, type, ++ timestamp); ++ if (rc != 0) ++ return rc; ++ ++ memset(&total_current, 0, sizeof(total_current)); ++ memset(&total_previous, 0, sizeof(total_previous)); ++ ++ g.first_counter = true; ++ ++ for (i = 0; i < 32 && ++ offsetofend(struct chsc_cmb_area, entries[i]) <= len; i++) { ++ if (data->current.header.v & mask) { ++ if (is_counter_totals(data->current.header.ct, ++ data->current.header.mt, i)) { ++ total_current.t = data->current.entries[i].t; ++ total_current.c = data->current.entries[i].c; ++ total_previous.t = data->previous.entries[i].t; ++ total_previous.c = data->previous.entries[i].c; ++ totals_found = true; ++ } else if (!totals_found) { ++ total_current.t += data->current.entries[i].t; ++ total_current.c += data->current.entries[i].c; ++ total_previous.t += data->previous.entries[i].t; ++ total_previous.c += data->previous.entries[i].c; ++ } ++ ++ if (g.only_totals) ++ continue; ++ ++ calc_interval_values(&data->current.entries[i], ++ &data->previous.entries[i], ++ data->current.header.s, ++ interval_time, ++ &vals); ++ ++ counter = get_counter_name(data->current.header.ct, ++ data->current.header.mt, i); ++ ++ rc = pr_call(print_counter_data)( ++ data->current.header.format == 1, ++ data->current.header.ax, ++ data->current.header.dx, type, ++ timestamp, counter, &vals); ++ if (rc != 0) ++ break; ++ ++ g.first_counter = false; ++ } ++ mask >>= 1; ++ } ++ ++ if (!g.no_totals) { ++ rc = pr_call(print_counter_separator)(); ++ if (rc != 0) ++ return rc; ++ ++ calc_interval_values(&total_current, &total_previous, ++ data->current.header.s, interval_time, ++ &vals); ++ ++ rc = pr_call(print_counter_data)( ++ data->current.header.format == 1, ++ data->current.header.ax, ++ data->current.header.dx, type, ++ timestamp, "Total", &vals); ++ if (rc != 0) ++ return rc; ++ } ++ ++ rc = pr_call(print_device_footer)(); ++ if (rc != 0) ++ return rc; ++ ++ return 0; ++} ++ ++/* ++ * Print the measured data ++ */ ++static int print_measurement_data(unsigned long interval_count, ++ float interval_time, const char *timestamp) ++{ ++ bool header_printed = false; ++ struct interval_data *dd; ++ struct card_data *cd; ++ int card, domain, rc; ++ ++ g.first_device = true; ++ for (card = 0; card < NUM_CARDS; card++) { ++ cd = g.cards[card]; ++ if (cd == NULL) ++ continue; ++ ++ if (!cd->data.current_valid) { ++ /* Not update in last interval -> free it */ ++ free_card_data(cd); ++ g.cards[card] = NULL; ++ ++ pr_verbose("Card %02x removed", card); ++ continue; ++ } ++ ++ if (cd->data.previous_valid) { ++ if (memcmp(&cd->data.current.header, ++ &cd->data.previous.header, ++ sizeof(struct chsc_cmb_header)) != 0) { ++ free_card_data(cd); ++ g.cards[card] = NULL; ++ ++ pr_verbose("CMB header mismatch, card %02x " ++ "removed", card); ++ continue; ++ } ++ ++ if (!header_printed) { ++ rc = pr_call(print_interval_header)( ++ interval_count, timestamp); ++ if (rc != 0) ++ return rc; ++ header_printed = true; ++ } ++ ++ rc = print_interval_data(&cd->data, timestamp, ++ interval_time); ++ if (rc != 0) ++ return rc; ++ ++ g.first_device = false; ++ } ++ ++ cd->data.current_valid = false; ++ ++ for (domain = 0; domain < NUM_DOMAINS; domain++) { ++ dd = cd->domains[domain]; ++ if (dd == NULL) ++ continue; ++ ++ if (!dd->current_valid) { ++ /* Not update in last interval -> free it */ ++ free(dd); ++ cd->domains[domain] = NULL; ++ ++ pr_verbose("APQN %02x.%04x removed", card, ++ domain); ++ } ++ ++ if (dd->previous_valid) { ++ if (memcmp(&dd->current.header, ++ &dd->previous.header, ++ sizeof(struct chsc_cmb_header))) { ++ free(dd); ++ cd->domains[domain] = NULL; ++ ++ pr_verbose("CMB header mismatch, APQN " ++ "%02x.%04x removed", card, ++ domain); ++ continue; ++ } ++ ++ rc = print_interval_data(dd, timestamp, ++ interval_time); ++ if (rc != 0) ++ return rc; ++ } ++ ++ dd->current_valid = false; ++ } ++ } ++ ++ if (header_printed) { ++ rc = pr_call(print_interval_footer)(); ++ if (rc != 0) ++ return rc; ++ } else if (interval_count > 0) { ++ pr_verbose("No data was reported in this interval"); ++ warnx("Failed to get card crypto measurement data: %s", ++ strerror(ENODEV)); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Perform the measurement in intervals ++ */ ++static int perform_measurement(void) ++{ ++ struct timeval tv_current, tv_previous; ++ struct sigaction alrm_act, int_act; ++ unsigned long interval_count = 0; ++ float interval_time; ++ char timestamp[64]; ++ struct tm *tm; ++ int rc; ++ ++ /* Set a handler for SIGINT/SIGTERM */ ++ memset(&int_act, 0, sizeof(int_act)); ++ int_act.sa_handler = int_handler; ++ sigaction(SIGINT, &int_act, NULL); ++ sigaction(SIGTERM, &int_act, NULL); ++ ++ /* Set a handler for SIGALRM */ ++ memset(&alrm_act, 0, sizeof(alrm_act)); ++ alrm_act.sa_handler = alarm_handler; ++ sigaction(SIGALRM, &alrm_act, NULL); ++ ++ rc = pr_call(print_initialize)(); ++ if (rc != 0) ++ return rc; ++ ++ rc = pr_call(print_header)(); ++ if (rc != 0) ++ return 0; ++ ++ alarm(g.interval); ++ ++ memset(&tv_current, 0, sizeof(tv_current)); ++ while (!quit) { ++ pr_verbose("Interval %lu", interval_count); ++ ++ tv_previous = tv_current; ++ rc = gettimeofday(&tv_current, NULL); ++ if (rc != 0) ++ break; ++ ++ tm = localtime(&tv_current.tv_sec); ++ if (tm == NULL) ++ break; ++ strftime(timestamp, sizeof(timestamp), "%x %X", tm); ++ interval_time = time_diff(&tv_previous, &tv_current); ++ ++ rc = get_card_measurement_data(); ++ if (rc != 0) ++ break; ++ ++ rc = print_measurement_data(interval_count, interval_time, ++ timestamp); ++ if (rc != 0) ++ break; ++ ++ if (g.count > 0 && interval_count >= g.count) { ++ pr_verbose("Interval limit reached"); ++ break; ++ } ++ interval_count++; ++ ++ if (quit) ++ break; ++ ++ pause(); ++ } ++ ++ if (quit) ++ pr_verbose("Measurement stopped by user"); ++ ++ alarm(0); ++ memset(&alrm_act, 0, sizeof(alrm_act)); ++ alrm_act.sa_handler = SIG_DFL; ++ sigaction(SIGALRM, &alrm_act, NULL); ++ ++ rc = pr_call(print_footer)(); ++ if (rc != 0) ++ return 0; ++ ++ rc = pr_call(print_terminate)(); ++ if (rc != 0) ++ return rc; ++ ++ return 0; ++} ++ ++/* ++ * Parse the type mapping specification: ++ * TYPE:MODE=TYPE:MODE[,TYPE:MODE=TYPE:MODE[,...]] ++ */ ++static int parse_type_mapping(char *mapping) ++{ ++ unsigned int from_type, to_type, from_mode, to_mode; ++ struct type_mapping *map; ++ char *tok; ++ ++ tok = strtok(mapping, ","); ++ while (tok != NULL) { ++ if (sscanf(tok, "%u:%u=%u:%u", &from_type, &from_mode, ++ &to_type, &to_mode) != 4) { ++ warnx("Invalid type mapping: %s", tok); ++ return -EINVAL; ++ } ++ ++ pr_verbose("from_type: %u from_mode: %u to_type: %u " ++ "to_mode: %u", from_type, from_mode, to_type, ++ to_mode); ++ ++ if (from_type < NUM_CRYPTO_TYPES && ++ crypto_types[from_type].name != NULL && ++ from_mode < crypto_types[from_type].num_modes && ++ crypto_types[from_type].modes[from_mode].counters != NULL) { ++ warnx("Cannot map a known type/mode to another " ++ "type/mode: %s", tok); ++ return -EINVAL; ++ } ++ ++ if (to_type >= NUM_CRYPTO_TYPES || ++ crypto_types[to_type].name == NULL || ++ to_mode >= crypto_types[to_type].num_modes || ++ crypto_types[to_type].modes[to_mode].counters == NULL) { ++ warnx("Cannot map a type/mode to an unknown " ++ "type/mode: %s", tok); ++ return -EINVAL; ++ } ++ ++ map = util_malloc(sizeof(struct type_mapping)); ++ map->from_type = from_type; ++ map->from_mode = from_mode; ++ map->to_type = to_type; ++ map->to_mode = to_mode; ++ ++ map->next = g.type_mapping; ++ g.type_mapping = map; ++ ++ tok = strtok(NULL, ","); ++ } ++ ++ return 0; ++} ++ ++static void free_type_mapping(void) ++{ ++ struct type_mapping *map = g.type_mapping; ++ struct type_mapping *next; ++ ++ while (map != NULL) { ++ next = map->next; ++ free(map); ++ map = next; ++ } ++} ++ ++static int add_device_selection(const char *device_id) ++{ ++ int card = -1, domain = -1; ++ struct device_selection *dev; ++ ++ pr_verbose("device_id: '%s'", device_id); ++ ++ /* check for 'card[.domain]' specification */ ++ if (sscanf(device_id, "%x.%x", &card, &domain) >= 1) { ++ pr_verbose("card: %d domain: %d", card, domain); ++ if (card < 0 || card > g.max_card_used) { ++ warnx("Invalid card specified: %s", device_id); ++ return -EINVAL; ++ } ++ g.card_mask[MASK_WORD_NO(card)] |= MASK_BIT(card); ++ g.min_card = MIN(g.min_card, card); ++ g.max_card = MAX(g.max_card, card); ++ ++ if (domain >= 0) { ++ if (domain > NUM_DOMAINS) { ++ warnx("Invalid domain specified: %s", ++ device_id); ++ return -EINVAL; ++ } ++ g.domain_mask[MASK_WORD_NO(domain)] |= ++ MASK_BIT(domain); ++ g.min_domain = MIN(g.max_domain, domain); ++ g.max_domain = MAX(g.max_domain, domain); ++ } else { ++ memset(g.domain_mask, 0xff, sizeof(g.domain_mask)); ++ g.min_domain = 0; ++ g.max_domain = NUM_DOMAINS - 1; ++ } ++ ++ dev = util_malloc(sizeof(struct device_selection)); ++ dev->card = card; ++ dev->domain = domain; ++ dev->next = g.dev_selection; ++ g.dev_selection = dev; ++ ++ return 0; ++ } ++ /* check for '.domain' specification */ ++ if (device_id[0] == '.' && ++ sscanf(device_id + 1, "%x", &domain) == 1) { ++ pr_verbose("domain: %d", domain); ++ if (domain < 0 || domain > NUM_DOMAINS) { ++ warnx("Invalid domain specified: %s", device_id); ++ return -EINVAL; ++ } ++ ++ g.domain_mask[MASK_WORD_NO(domain)] |= MASK_BIT(domain); ++ g.min_domain = MIN(g.max_domain, domain); ++ g.max_domain = MAX(g.max_domain, domain); ++ memset(g.card_mask, 0xff, sizeof(g.card_mask)); ++ g.min_card = 0; ++ g.max_card = g.max_card_used; ++ ++ dev = util_malloc(sizeof(struct device_selection)); ++ dev->card = -1; ++ dev->domain = domain; ++ dev->next = g.dev_selection; ++ g.dev_selection = dev; ++ ++ return 0; ++ } ++ ++ warnx("Invalid device ID specified: %s", device_id); ++ return -EINVAL; ++} ++ ++/* ++ * Frees the device selection list ++ */ ++static void free_device_selection(void) ++{ ++ struct device_selection *dev = g.dev_selection; ++ struct device_selection *next; ++ ++ while (dev != NULL) { ++ next = dev->next; ++ free(dev); ++ ++ dev = next; ++ } ++} ++ ++/* ++ * Build the AP and domain selection mask from the specified device ID's. ++ */ ++static int parse_device_selection(void) ++{ ++ int i, rc; ++ ++ g.min_card = NUM_CARDS - 1; ++ g.max_card = 0; ++ g.min_domain = NUM_DOMAINS - 1; ++ g.max_domain = 0; ++ ++ for (i = 0; g.device_ids[i] != NULL; i++) { ++ rc = add_device_selection(g.device_ids[i]); ++ if (rc != 0) ++ return rc; ++ } ++ ++ if (i == 0) { ++ /* No device-IDs specified */ ++ memset(g.card_mask, 0xff, sizeof(g.card_mask)); ++ g.min_card = 0; ++ g.max_card = g.max_card_used; ++ memset(g.domain_mask, 0xff, sizeof(g.domain_mask)); ++ g.min_domain = 0; ++ g.max_domain = NUM_DOMAINS - 1; ++ } ++ ++ pr_verbose("Min card: %u, max card: %u", g.min_card, g.max_card); ++ pr_verbose("Min domain: %u, max domain: %u", g.min_domain, ++ g.max_domain); ++ ++ return 0; ++} ++ ++/* ++ * Entry point ++ */ ++int main(int argc, char *argv[]) ++{ ++ char *endp; ++ int c, rc; ++ ++ util_prg_init(&prg); ++ util_opt_init(opt_vec, NULL); ++ ++ while (1) { ++ c = util_opt_getopt_long(argc, argv); ++ if (c == -1) ++ break; ++ switch (c) { ++ case 'i': ++ g.interval = strtoll(optarg, &endp, 0); ++ if (*optarg == '\0' || *endp != '\0' || ++ g.interval <= 0 || ++ (g.interval == LLONG_MAX && errno == ERANGE)) { ++ warnx("Invalid value for '--interval'|" ++ "'-i': '%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 'c': ++ g.count = strtoull(optarg, &endp, 0); ++ if (*optarg == '\0' || *endp != '\0' || g.count == 0 || ++ (g.count == LLONG_MAX && errno == ERANGE)) { ++ warnx("Invalid value for '--count'|" ++ "'-c': '%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 'o': ++ if (strcasecmp(optarg, "JSON") == 0) { ++ g.print_funcs = &json_print; ++ } else if (strcasecmp(optarg, "TABLE") == 0) { ++ g.only_totals = true; ++ g.print_funcs = &table_print; ++ } else if (strcasecmp(optarg, "CSV") == 0) { ++ g.only_totals = true; ++ g.print_funcs = &csv_print; ++ } else { ++ warnx("Invalid value for '--output'|" ++ "'-o': '%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 't': ++ g.no_totals = true; ++ break; ++ case 'T': ++ g.only_totals = true; ++ break; ++ case 'a': ++ g.no_apqn = true; ++ break; ++ case 'M': ++ g.map_type = optarg; ++ break; ++ case 'A': ++ g.all = true; ++ break; ++ case 'O': ++ g.only_online = true; ++ break; ++ case 'V': ++ g.verbose = true; ++ break; ++ case 'h': ++ util_prg_print_help(); ++ util_opt_print_help(); ++ print_adapter_id_help(); ++ return EXIT_SUCCESS; ++ case 'v': ++ util_prg_print_version(); ++ return EXIT_SUCCESS; ++ default: ++ util_opt_print_parse_error(c, argv); ++ return EXIT_FAILURE; ++ } ++ } ++ ++ /* remaining positional args are device IDs */ ++ g.device_ids = &argv[optind]; ++ ++ if (g.only_totals && g.no_totals) { ++ warnx("Either --no-totals or --only-totals can be specified, " ++ "but not both"); ++ return EXIT_FAILURE; ++ } ++ ++ if (g.only_online && g.all) { ++ warnx("Either --only-online or --all can be specified, " ++ "but not both"); ++ return EXIT_FAILURE; ++ } ++ ++ pr_verbose("Interval: %ld Count: %ld", g.interval, g.count); ++ ++ rc = get_max_card_index(&g.max_card_used); ++ if (rc != 0) { ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = parse_device_selection(); ++ if (rc != 0) { ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ if (g.map_type != NULL) { ++ rc = parse_type_mapping(g.map_type); ++ if (rc != 0) { ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ } ++ ++ g.chsc_fd = open(CHSC_DEVICE, O_RDWR); ++ if (g.chsc_fd < 0) { ++ warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno)); ++ return EXIT_FAILURE; ++ } ++ pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE); ++ ++ /* Don't buffer data if redirected to a pipe */ ++ setbuf(stdout, NULL); ++ ++ rc = perform_measurement(); ++ if (rc != 0) { ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++out: ++ if (g.chsc_fd >= 0) ++ close(g.chsc_fd); ++ free_device_selection(); ++ free_type_mapping(); ++ free_interval_data(); ++ ++ return rc; ++} ++ +-- +2.21.3 + + +From c0670b62ccd7a9f1f1b3a8a49114c2e3c673ecd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20H=C3=B6ppner?= +Date: Mon, 1 Apr 2019 09:53:06 +0200 +Subject: [PATCH 21/44] zpcictl: Check for regular directory (#1695001) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In case a regular directory was specified, rather than a device node, +the check if the device exists will pass. The following code paths then +assume a slot id was specified. This in turn may lead to a buffer +overflow when the device data is copied to to the zpci_device struct. + +Check if the specified path is a regular directory and prevent a +possible later buffer overflow and copying wrong data respectively. + +Signed-off-by: Jan Höppner +(cherry picked from commit c7a255fcd9433bba6dd516f6b28a139bbc5351d3) +--- + zpcictl/zpcictl.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c +index 7cfa0d3..f2a55ad 100644 +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -249,8 +249,12 @@ static int device_exists(char *dev) + char *path; + int rc = 0; + ++ /* In case a device node is specified, this will be sufficiant */ ++ if (util_path_exists(dev) && !util_path_is_dir(dev)) ++ return 1; ++ + path = util_path_sysfs("bus/pci/devices/%s", dev); +- if (util_path_exists(path) || util_path_exists(dev)) ++ if (util_path_exists(path)) + rc = 1; + free(path); + +-- +2.21.3 + + +From 9f0fe2e3d5bcc079577700d778e3f1a58fced510 Mon Sep 17 00:00:00 2001 +From: Thomas Richter +Date: Wed, 10 Jul 2019 13:01:09 +0200 +Subject: [PATCH 22/44] cpumf: Add support for CPU-Measurement Facility + counters SVN 6 (#1683276) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for CPU-Measurement facility counter second version +number 6. This adds some more counters to the crypto counter set. +Extended counter set is the same as for z14. + +Signed-off-by: Thomas Richter +Signed-off-by: Jan Höppner +(cherry picked from commit f110a77b68d46f10cab8847cf73fe92686ae7d11) +--- + cpumf/Makefile | 2 +- + cpumf/bin/cpumf_helper.in | 7 +++- + ...svn-generic.ctr => cpum-cf-csvn-12345.ctr} | 0 + cpumf/data/cpum-cf-csvn-6.ctr | 33 +++++++++++++++++++ + cpumf/data/cpum-cf-hw-counter.map | 6 +++- + 5 files changed, 45 insertions(+), 3 deletions(-) + rename cpumf/data/{cpum-cf-csvn-generic.ctr => cpum-cf-csvn-12345.ctr} (100%) + create mode 100644 cpumf/data/cpum-cf-csvn-6.ctr + +diff --git a/cpumf/Makefile b/cpumf/Makefile +index 3fc4c0f..2e11810 100644 +--- a/cpumf/Makefile ++++ b/cpumf/Makefile +@@ -6,7 +6,7 @@ include ../common.mak + CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf + DATA_FILES = cpum-cf-hw-counter.map \ + cpum-cf-cfvn-1.ctr cpum-cf-cfvn-3.ctr \ +- cpum-cf-csvn-generic.ctr \ ++ cpum-cf-csvn-12345.ctr cpum-cf-csvn-6.ctr \ + cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \ + cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \ + cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr +diff --git a/cpumf/bin/cpumf_helper.in b/cpumf/bin/cpumf_helper.in +index 4757725..e1da017 100755 +--- a/cpumf/bin/cpumf_helper.in ++++ b/cpumf/bin/cpumf_helper.in +@@ -266,7 +266,12 @@ sub cpumf_load_ctrdef($;$) + # List of "generic" counter sets + my @def = (); + push @def, "cfvn-" . $version->{cfvn}; +- push @def, "csvn-generic"; ++ if ($version->{csvn} >= 1 && $version->{csvn} <= 6) { ++ push @def, "csvn-12345"; ++ } ++ if ($version->{csvn} == 6) { ++ push @def, "csvn-6"; ++ } + + my $h = {}; + # Load counter set definition +diff --git a/cpumf/data/cpum-cf-csvn-generic.ctr b/cpumf/data/cpum-cf-csvn-12345.ctr +similarity index 100% +rename from cpumf/data/cpum-cf-csvn-generic.ctr +rename to cpumf/data/cpum-cf-csvn-12345.ctr +diff --git a/cpumf/data/cpum-cf-csvn-6.ctr b/cpumf/data/cpum-cf-csvn-6.ctr +new file mode 100644 +index 0000000..4ea3b44 +--- /dev/null ++++ b/cpumf/data/cpum-cf-csvn-6.ctr +@@ -0,0 +1,33 @@ ++Counter: 80 Name:ECC_FUNCTION_COUNT ++Short-Description:ECC Function Count ++Description: ++This counter counts the ++total number of the elliptic-curve cryptography (ECC) ++functions issued by the CPU. ++. ++Counter: 81 Name:ECC_CYCLES_COUNT ++Short-Description:ECC Cycles Count ++Description: ++This counter counts the total ++number of CPU cycles when the ECC coprocessor is ++busy performing the elliptic-curve cryptography ++(ECC) functions issued by the CPU. ++. ++Counter: 82 Name:ECC_BLOCKED_FUNCTION_COUNT ++Short-Description:Ecc Blocked Function Count ++Description: ++This counter ++counts the total number of the elliptic-curve ++cryptography (ECC) functions that are issued by the CPU ++and are blocked because the ECC coprocessor is ++busy performing a function issued by another CPU. ++. ++Counter: 83 Name:ECC_BLOCKED_CYCLES_COUNT ++Short-Description:ECC Blocked Cycles Count ++Description: ++This counter counts ++the total number of CPU cycles blocked for the elliptic-curve ++cryptography (ECC) functions issued by the ++CPU because the ECC coprocessor is busy perform- ++ing a function issued by another CPU. ++. +diff --git a/cpumf/data/cpum-cf-hw-counter.map b/cpumf/data/cpum-cf-hw-counter.map +index a8a2846..5729b95 100644 +--- a/cpumf/data/cpum-cf-hw-counter.map ++++ b/cpumf/data/cpum-cf-hw-counter.map +@@ -14,7 +14,8 @@ + 'cfvn-3' => 'cpum-cf-cfvn-3.ctr', + + # CSVN +- 'csvn-generic' => 'cpum-cf-csvn-generic.ctr', ++ 'csvn-12345' => 'cpum-cf-csvn-12345.ctr', ++ 'csvn-6' => 'cpum-cf-csvn-6.ctr', + + # Extended counters + 2097 => 'cpum-cf-extended-z10.ctr', +@@ -27,4 +28,7 @@ + 2965 => 'cpum-cf-extended-z13.ctr', + 3906 => 'cpum-cf-extended-z14.ctr', + 3907 => 'cpum-cf-extended-z14.ctr', ++ # Identical with z14 ++ 8561 => 'cpum-cf-extended-z14.ctr', ++ 8562 => 'cpum-cf-extended-z14.ctr', + }; +-- +2.21.3 + + +From f58ee5580cd6e357dce488d9cd3d999e7ad61f16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 19 Jul 2019 09:41:08 +0200 +Subject: [PATCH 23/44] ziomon: fix utilization recording with multi-digit scsi + hosts (#1731203) + +Description: ziomon: fix utilization recording with multi-digit scsi hosts +Symptom: During running of ziomon script, user receives ziomon + error: "ziomon: Number of LUNs does not match number of + devices: 2 devices and 1 LUNs". Also user receives + ziomon_util error: "ziomon_util: Path does not exist: + /sys/class/scsi_host/host0/utilization - correct kernel + version?". After collection of records, user receives + error during using of ziorep_utilization or + ziorep_traffic: "ziorep_traffic: Could not retrieve + initial data - data files corrupted or broken, or the + .agg file is missing." +Problem: s390-tools-1.9.0 introduced a new way of recognizing of + multipath device paths with using of sed command + invocation in ziomon script. With this new way of + recognizing, if there are paths, related to multipath + device, with SCSI host ID longer, than one digit, + ziomon incorrectly parses the multipath -l command + output. It erroneously cuts off all but the least + significant digit of the SCSI host ID (H) of paths in + H:B:T:L format (Host:Bus:Target:Lun). This leads to + passing of hosts (-a) and paths (-l) with wrong + SCSI host ID to ziomon_util. In turn ziomon_util cannot + recognize hosts with non-existing SCSI host ID and + issues an error. Also, wrong sed command invocation + could lead to receiving of duplicate LUNs by ziomon + after parsing of multipath -l command output. Then + ziomon excludes duplicates from WRP_LUNS, which leads + to mismatch between number of LUNs and number of + detected block devices and issues ziomon script error, + without starting ziomon_util and without writing to + specified log file. +Solution: The regular expression to match a path in H:B:T:L + format started with a greedy ".*", which erroneously + consumed parts of the SCSI host ID (H). The solution + is to replace the greedy ".*" by "[^0-9]*", so that sed + command does not consume parts of the SCSI host ID any + more. +Reproduction: Create such multipath device, that its related path + contains LUN with SCSI host ID longer, than one digit. + Example for wrong SCSI host ID: + $ multipath -l + mpathc (36005076307ffc5e300000000000083f5) dm-2 IBM ... + size=20G features='1 queue_if_no_path' hwhandler='0'... + `-+- policy='service-time 0' prio=0 status=active + |- 10:0:0:1089814659 sdb 8:16 active undef running + `- 11:0:0:1089814659 sdf 8:80 active undef running + Example for duplicate SCSI host ID: + $ multipath -l + mpathd (36005076307ffc5e300000000000083f4) dm-3 IBM ... + size=20G features='1 queue_if_no_path' hwhandler='0'... + `-+- policy='service-time 0' prio=0 status=active + |- 10:0:0:1089749123 sda 8:0 active undef running + `- 0:0:0:1089749123 sdd 8:48 active undef running + Use ziomon tool with one of the described multipath + device as input. +Upstream-ID: f2dee9f542439cf07e00df4296b05a47b81e2469 +--- + ziomon/ziomon | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ziomon/ziomon b/ziomon/ziomon +index 87c4bc4..26fa269 100755 +--- a/ziomon/ziomon ++++ b/ziomon/ziomon +@@ -514,7 +514,7 @@ function check_for_multipath_devices() { + (( i+=2 )); + while [[ `echo "${mp_arr[$i]:0:1}" | grep -ve "[0-9a-zA-Z]"` ]] && [ $i -lt ${#mp_arr[@]} ]; do + if [ `echo ${mp_arr[$i]} | grep -e "[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}" | wc -l` -ne 0 ]; then +- line="`echo ${mp_arr[$i]} | sed 's/.*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`"; ++ line="`echo ${mp_arr[$i]} | sed 's/[^0-9]*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`"; + checked_devs[${#checked_devs[@]}]=`echo $line | awk '{print "/dev/"$2}'`; + ddebug " adding ${checked_devs[${#checked_devs[@]}-1]}"; + WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}"; +-- +2.21.3 + + +From ef93298c3f0a8d318c0568f3bec10b76279bb15b Mon Sep 17 00:00:00 2001 +From: Peter Oberparleiter +Date: Tue, 12 Mar 2019 14:05:17 +0100 +Subject: [PATCH 24/44] zdev: Do not export inacceptable attribute values + (#1731960) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For some device driver SysFS attributes, values read may not be +acceptable input values for that attribute. + +An example would be the group of qeth VNICC attributes that return "n/a" +when VNICC setup is not supported, but only accept "0" and "1" as valid +values that can be written to it. + +This leads to errors such as the following when data for such attributes +is imported: + + # chzdev f500 --import test.conf + Importing configuration data from test.conf + QETH device 0.0.f500:0.0.f501:0.0.f502 configure failed + Error: Invalid value for qeth attribute: vnicc/flooding=n/a (*) + Acceptable values: + - Integers in the range 0 - 1 + Use 'chzdev qeth --help-attribute vnicc/flooding' for more information + Note: You can use --force to override safety checks (*) + +To fix this, change chzdev's --export function to skip any attribute +value that is not acceptable for that attribute. + +Fixes: e831269e7433 ("zdev: Add support for VNIC Characteristics") +Signed-off-by: Peter Oberparleiter +Signed-off-by: Jan Höppner +(cherry picked from commit 4ff6519961965deb278093c83ea0f46fb7c7c205) +--- + zdev/src/export.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/zdev/src/export.c b/zdev/src/export.c +index f1acb8a..a66db0b 100644 +--- a/zdev/src/export.c ++++ b/zdev/src/export.c +@@ -78,6 +78,10 @@ static bool is_exportable(struct setting *s, config_t config) + /* Skip values that cannot be determined. */ + return false; + } ++ if (!attrib_check_value(a, s->value)) { ++ /* Skip values that are not acceptable input values. */ ++ return false; ++ } + if (!attrib_match_default(s->attrib, s->value)) { + /* All non-default values should be exported. */ + return true; +-- +2.21.3 + + +From de7f4178f2178268ed65292bf5e1f42894832d9a Mon Sep 17 00:00:00 2001 +From: Stefan Haberland +Date: Wed, 17 Jul 2019 17:26:40 +0200 +Subject: [PATCH 25/44] zipl: do not overwrite BOOT_IMAGE entry (#1728677) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The zipl internal loader adds a variable BOOT_IMAGE= to the commandline +so that it is visible in the operating system which menu entry has been +chosen. + +This entry was overwritten by the stage3 parameter page. + +Fix by re-arranging the internal memory layout and putting the command +line extra param, which contains the BOOT_IMAGE entry, at 0xe000. +This location is available because less than one page is used for the +stack. + +Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/67 +Signed-off-by: Stefan Haberland +Signed-off-by: Jan Höppner +(cherry picked from commit 8bf5f8d0e293b294c9b7aaebea80e92634c7564d) +--- + zipl/boot/Makefile | 4 ++-- + zipl/boot/menu.h | 2 +- + zipl/boot/stage2.lds | 3 ++- + zipl/boot/stage3.h | 2 +- + 4 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile +index a049797..0faf3ec 100644 +--- a/zipl/boot/Makefile ++++ b/zipl/boot/Makefile +@@ -6,7 +6,7 @@ CFLAGS_BOOT = $(NO_PIE_CFLAGS) -Os -g -I../include -D__ASSEMBLY__ \ + -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ + -fno-delete-null-pointer-checks \ + -fexec-charset=IBM1047 -m64 -mpacked-stack \ +- -mstack-size=8192 -mstack-guard=128 -msoft-float \ ++ -mstack-size=4096 -mstack-guard=128 -msoft-float \ + -W -Wall -Wformat-security + + FILES = fba0.bin fba1b.bin fba2.bin \ +@@ -90,7 +90,7 @@ stage3.bin: stage3.exec + --only-section=.stage2dump.tail \ + --only-section=.eckd2dump_mv.tail \ + --only-section=.fixup \ +- --pad-to=0xf000 \ ++ --pad-to=0xe000 \ + $< $@ + + data.o: $(FILES) +diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h +index 83338a8..1b103a8 100644 +--- a/zipl/boot/menu.h ++++ b/zipl/boot/menu.h +@@ -15,7 +15,7 @@ + #include "stage2.h" + + /* address of extra command line */ +-#define COMMAND_LINE_EXTRA (0xA000-0x400) ++#define COMMAND_LINE_EXTRA 0xE000 + + /* max command line length */ + #define COMMAND_LINE_SIZE 896 +diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds +index 41efa79..34ed014 100644 +--- a/zipl/boot/stage2.lds ++++ b/zipl/boot/stage2.lds +@@ -12,7 +12,8 @@ + * 0x6000-0x8fff Memory allocation (heap) + * 0x9000-0x9fff Memory to load stage3 parameter to + * 0xa000-0xdfff Memory to load stage3 to +- * 0xe000-0xffff Stack ++ * 0xe000-0xe3ff command line extra ++ * 0xe400-0xffff Stack + * + * Special memory locations + * ------------------------ +diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h +index 91477b1..3a02001 100644 +--- a/zipl/boot/stage3.h ++++ b/zipl/boot/stage3.h +@@ -22,7 +22,7 @@ + #define OLDMEM_SIZE 0x10420 + #define COMMAND_LINE 0x10480 + #define COMMAND_LINE_SIZE 896 +-#define COMMAND_LINE_EXTRA (0xA000-0x400) ++#define COMMAND_LINE_EXTRA 0xE000 + + #define STAGE3_FLAG_SCSI 0x0001000000000000ULL + #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL +-- +2.21.3 + + +From d25f635ebdb0f2faa8ca018859c3223c3dac4d0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20H=C3=B6ppner?= +Date: Wed, 31 Jul 2019 15:25:00 +0200 +Subject: [PATCH 26/44] fdasd: Fix exit status in error cases (#1734816) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In error cases, fdasd returns -1 which results in the return value 255. +This is due to the fact that only the low-order 8 bits are used for the +status value. See 2.13 Status Information [1] in the POSIX standard and +the exit() POSIX man page [2] for more details. + +Instead of returning -1, use the EXIT_FAILURE constant to indicate +unsuccessful termination properly. + +[1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html +[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exit.html + +Signed-off-by: Jan Höppner +(cherry picked from commit 80443ab629a234e2af55dd9bcceb6fe150c6c79e) +--- + fdasd/fdasd.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c +index 085d2be..5de7955 100644 +--- a/fdasd/fdasd.c ++++ b/fdasd/fdasd.c +@@ -397,7 +397,7 @@ static void fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str) + fputc('\n', stderr); + fputs(err_str, stderr); + +- fdasd_exit(anc, -1); ++ fdasd_exit(anc, EXIT_FAILURE); + } + + /* +@@ -3040,5 +3040,5 @@ int main(int argc, char *argv[]) + } + } + +- return -1; ++ return EXIT_FAILURE; + } +-- +2.21.3 + + +From 89ed5688c2b351f487e75e3035948aadcab73a8f Mon Sep 17 00:00:00 2001 +From: Stefan Haberland +Date: Wed, 7 Aug 2019 16:59:20 +0200 +Subject: [PATCH 27/44] zipl: fix zfcp dump image location (#1730707) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The zfcp dumper fails with the following error: + +MLOPDM003I: Machine loader finished, moving data to final storage + location. + + uncompression error +--- System halted + +HCPGIR450W CP entered; disabled wait +PSW 00020001 80000000 00000000 DEADBEEF + +The zipl command shows overlapping components when installing the dumper +to a zfcp disk: + +zipl -d /dev/sda1 +Building bootmap directly on partition '/dev/sda1' +Adding dump section + kernel image......: /lib/s390-tools/zfcpdump/zfcpdump-image + kernel parmline...: 'root=/dev/ram0 dump_mem=1 possible_cpus=1 + cgroup_disable=memory ' + component address: + heap area.......: 0x00002000-0x00005fff + stack area......: 0x0000f000-0x0000ffff + internal loader.: 0x0000a000-0x0000dfff + parameters......: 0x00009000-0x000091ff + kernel image....: 0x00010000-0x005761ff + ^^^^^^ + parmline........: 0x00567000-0x005671ff + ^^^^^^ +Preparing boot device: sda. +Done. + +With the secure IPL patchset the offset of the kernel image has been +removed for the normal IPL case but it has not been removed for the dump +image which leads to the overlap of 0x10000. + +Fix by removing the offset for the dump case. + +Signed-off-by: Stefan Haberland +Reviewd-by: Mikhail Zaslonko +Tested-by: Mikhail Zaslonko +Signed-off-by: Jan Höppner +(cherry picked from commit a9f4ae2db3fa513941244956e323cb6ce214ca5f) +--- + zipl/src/job.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 2d8de8f..e68d1b7 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -559,7 +559,7 @@ get_dump_components(struct job_dump_data *dump, + /* Fill in component data */ + num = 0; + rc = set_cl_element(&cl[num++], "kernel image", dump->image, +- &dump->image_addr, 0, 0x10000, ++ &dump->image_addr, 0, 0x0, + MAXIMUM_PHYSICAL_BLOCKSIZE); + if (rc) + goto error; +-- +2.21.3 + + +From 9e5da1e34622725dd9fb7a681f99552bcdabe414 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 12:23:26 +0100 +Subject: [PATCH 28/44] dasdfmt/lsdasd: Add Thin provisioning base support + (#1651733) + +Summary: dasdfmt/lsdasd: Add Thin provisioning base support +Description: Make dasdfmt and lsdasd aware of thinly provisioned (Extent Space + Efficient (ESE)) DASD volumes. + + If an ESE volume is recognised a QUICK format is performed, + formatting only the first two tracks. The mode can always be + overwritten by --mode. + Previously allocated space is always released before formatting, + if not specified otherwise. The option --no-discard (-D) is + provided to omit the space release. + + The ESE information is added to the discipline output of lsdasd + and the extended output additionally lists information about + logical capacity, allocated space, and the extent size. +--- + Makefile | 2 +- + common.mak | 4 - + dasdfmt/Makefile | 1 - + dasdfmt/dasdfmt.8 | 6 + + dasdfmt/dasdfmt.c | 60 +++++++- + dasdfmt/dasdfmt.h | 48 +----- + dasdinfo/Makefile | 3 +- + dasdview/Makefile | 1 - + dasdview/dasdview.c | 4 +- + dasdview/dasdview.h | 3 +- + fdasd/Makefile | 1 - + fdasd/fdasd.c | 2 +- + include/lib/dasd_base.h | 36 +++++ + include/lib/dasd_sys.h | 3 +- + include/lib/u2s.h | 21 --- + include/lib/util_sys.h | 17 ++ + libdasd/dasd_ioctl.c | 20 +++ + libdasd/dasd_sys.c | 114 ++++++++++++-- + libu2s/Makefile | 14 -- + libu2s/misc.c | 27 ---- + libu2s/misc.h | 15 -- + libu2s/u2s.c | 334 ---------------------------------------- + libutil/Makefile | 3 +- + libutil/util_sys.c | 78 ++++++++++ + libzds/libzds.c | 4 +- + tunedasd/src/Makefile | 1 - + zconf/lsdasd | 30 +++- + zdsfs/Makefile | 5 +- + zipl/src/Makefile | 3 +- + zipl/src/install.c | 4 +- + 30 files changed, 354 insertions(+), 510 deletions(-) + delete mode 100644 include/lib/u2s.h + create mode 100644 include/lib/util_sys.h + delete mode 100644 libu2s/Makefile + delete mode 100644 libu2s/misc.c + delete mode 100644 libu2s/misc.h + delete mode 100644 libu2s/u2s.c + create mode 100644 libutil/util_sys.c + +diff --git a/Makefile b/Makefile +index bb2b900..8a07a4d 100644 +--- a/Makefile ++++ b/Makefile +@@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar + # Include common definitions + include common.mak + +-LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw libvmcp ++LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp + TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ + tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ + vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ +diff --git a/common.mak b/common.mak +index 6029687..6f6acbb 100644 +--- a/common.mak ++++ b/common.mak +@@ -331,10 +331,6 @@ $(rootdir)/libzds/libzds.a: $(rootdir)/libzds + $(MAKE) -C $(rootdir)/libzds/ libzds.a + .PHONY: $(rootdir)/libzds + +-$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s +- $(MAKE) -C $(rootdir)/libu2s/ libu2s.a +-.PHONY: $(rootdir)/libu2s +- + $(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump + $(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a + .PHONY: $(rootdir)/libvmdump +diff --git a/dasdfmt/Makefile b/dasdfmt/Makefile +index 03877b3..98d9d3e 100644 +--- a/dasdfmt/Makefile ++++ b/dasdfmt/Makefile +@@ -4,7 +4,6 @@ all: dasdfmt + + libs = $(rootdir)/libdasd/libdasd.a \ + $(rootdir)/libvtoc/libvtoc.a \ +- $(rootdir)/libu2s/libu2s.a \ + $(rootdir)/libutil/libutil.a + + dasdfmt: dasdfmt.o $(libs) +diff --git a/dasdfmt/dasdfmt.8 b/dasdfmt/dasdfmt.8 +index 99da9ed..c3e98fa 100644 +--- a/dasdfmt/dasdfmt.8 ++++ b/dasdfmt/dasdfmt.8 +@@ -122,6 +122,8 @@ Format the first two tracks and write label and partition information. Only use + this option if you are sure that the target DASD already contains a regular + format with the specified blocksize. A blocksize can optionally be specified + using \fB-b\fR (\fB--blocksize\fR). ++.br ++For thin-provisioned DASD ESE volumes this is the default mode. + .IP expand + Format all unformatted tracks at the end of the target DASD. This mode assumes + that tracks at the beginning of the DASD volume have already been correctly +@@ -136,6 +138,10 @@ using \fB-b\fR (\fB--blocksize\fR). + Perform a complete format check on a DASD volume. A blocksize can be specified + with \fB-b\fR (\fB--blocksize\fR). + ++.TP ++\fB--no-discard\fR ++Omit a full space release when formatting a thin-provisioned DASD ESE volume. ++ + .TP + \fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR + Number of cylinders to be processed in one formatting step. +diff --git a/dasdfmt/dasdfmt.c b/dasdfmt/dasdfmt.c +index a34d79b..c78080c 100644 +--- a/dasdfmt/dasdfmt.c ++++ b/dasdfmt/dasdfmt.c +@@ -53,6 +53,7 @@ static const struct util_prg prg = { + /* Defines for options with no short command */ + #define OPT_CHECK 128 + #define OPT_NOZERO 129 ++#define OPT_NODISCARD 130 + + static struct util_opt opt_vec[] = { + UTIL_OPT_SECTION("FORMAT ACTIONS"), +@@ -105,6 +106,11 @@ static struct util_opt opt_vec[] = { + .desc = "Prevent storage server from modifying record 0", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, ++ { ++ .option = { "no-discard", no_argument, NULL, OPT_NODISCARD }, ++ .desc = "Do not discard space before formatting", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + { + .option = { NULL, no_argument, NULL, 'y' }, + .desc = "Start formatting without further user-confirmation", +@@ -921,6 +927,8 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname, + printf("Drive Geometry: %d Cylinders * %d Heads = %d Tracks\n", + cylinders, heads, (cylinders * heads)); + ++ printf("Device Type: %s Provisioned\n", ++ info->ese ? "Thinly" : "Fully"); + printf("\nI am going to format the device "); + printf("%s in the following way:\n", devname); + printf(" Device number of device : 0x%x\n", info->dasd_info.devno); +@@ -939,7 +947,10 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname, + (p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no"); + printf(" Blocksize : %d\n", p->blksize); + printf(" Mode : %s\n", mode_str[mode]); +- ++ if (info->ese) { ++ printf(" Full Space Release : %s\n", ++ (info->no_discard || mode == FULL) ? "no" : "yes"); ++ } + if (info->testmode) + printf("Test mode active, omitting ioctl.\n"); + } +@@ -1234,6 +1245,26 @@ static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders, + process_tracks(info, cylinders, heads, format_params); + } + ++static void dasdfmt_release_space(dasdfmt_info_t *info) ++{ ++ format_data_t r = { ++ .start_unit = 0, ++ .stop_unit = 0, ++ .intensity = DASD_FMT_INT_ESE_FULL, ++ }; ++ int err = 0; ++ ++ if (!info->ese || info->no_discard) ++ return; ++ ++ printf("Releasing space for the entire device...\n"); ++ err = dasd_release_space(dev_filename, &r); ++ if (err) { ++ ERRMSG_EXIT(EXIT_FAILURE, "%s: Could not release space (%s)\n", ++ prog_name, strerror(err)); ++ } ++} ++ + static void dasdfmt_prepare_and_format(dasdfmt_info_t *info, + unsigned int cylinders, + unsigned int heads, format_data_t *p) +@@ -1329,6 +1360,8 @@ static void dasdfmt_quick_format(dasdfmt_info_t *info, unsigned int cylinders, + + if (info->force) { + printf("Skipping format check due to --force.\n"); ++ } else if (info->ese) { ++ printf("Skipping format check due to thin-provisioned device.\n"); + } else { + check_blocksize(info, p->blksize); + +@@ -1394,7 +1427,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, + if ((info->verbosity > 0) || !info->withoutprompt || info->testmode) + dasdfmt_print_info(info, devname, vlabel, cylinders, heads, p); + +- count = u2s_get_host_access_count(devname); ++ count = dasd_get_host_access_count(devname); + if (info->force_host) { + if (count > 1) { + ERRMSG_EXIT(EXIT_FAILURE, +@@ -1438,6 +1471,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, + dasdfmt_prepare_and_format(info, cylinders, heads, p); + break; + case QUICK: ++ dasdfmt_release_space(info); + dasdfmt_quick_format(info, cylinders, heads, p); + break; + case EXPAND: +@@ -1461,6 +1495,19 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, + } + } + ++static void eval_format_mode(dasdfmt_info_t *info) ++{ ++ if (!info->force && info->mode_specified && info->ese && mode == EXPAND) { ++ ERRMSG_EXIT(EXIT_FAILURE, ++ "WARNING: The specified device is thin-provisioned\n" ++ "Format mode 'expand' is not feasible.\n" ++ "Use --mode=full or --mode=quick to perform a clean format\n"); ++ } ++ ++ if (!info->mode_specified) ++ mode = info->ese ? QUICK : FULL; ++} ++ + int main(int argc, char *argv[]) + { + dasdfmt_info_t info = { +@@ -1496,8 +1543,6 @@ int main(int argc, char *argv[]) + format_params.blksize = DEFAULT_BLOCKSIZE; + format_params.intensity = DASD_FMT_INT_COMPAT; + +- mode = FULL; +- + /*************** parse parameters **********************/ + + while (1) { +@@ -1601,6 +1646,10 @@ int main(int argc, char *argv[]) + "invalid. Consult the man page for " + "more information.\n", + prog_name, optarg); ++ info.mode_specified = 1; ++ break; ++ case OPT_NODISCARD: ++ info.no_discard = 1; + break; + case OPT_CHECK: + info.check = 1; +@@ -1645,6 +1694,9 @@ int main(int argc, char *argv[]) + "device information failed (%s).\n", + prog_name, strerror(rc)); + ++ info.ese = dasd_sys_ese(dev_filename); ++ eval_format_mode(&info); ++ + /* Either let the user specify the blksize or get it from the kernel */ + if (!info.blksize_specified) { + if (!(mode == FULL || +diff --git a/dasdfmt/dasdfmt.h b/dasdfmt/dasdfmt.h +index 5470c90..2f53a0d 100644 +--- a/dasdfmt/dasdfmt.h ++++ b/dasdfmt/dasdfmt.h +@@ -27,10 +27,6 @@ + #include + #include + +-/**************************************************************************** +- * SECTION: Definition needed for DASD-API (see dasd.h) * +- ****************************************************************************/ +- + /* + * Represents possible format modes that can be specified when formatting + * a DASD. +@@ -45,57 +41,16 @@ static const char mode_str[3][10] = { + "Full", "Quick", "Expand" + }; + +-/* +- * values to be used for format_data_t.intensity +- * 0/8: normal format +- * 1/9: also write record zero +- * 3/11: also write home address +- * 4/12: invalidate track +- */ +-#define DASD_FMT_INT_FMT_R0 1 /* write record zero */ +-#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */ +-#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ +-#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ +-#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ +- +-/* +- * values to be used in format_check_t for indicating +- * possible format errors +- */ +-#define DASD_FMT_ERR_TOO_FEW_RECORDS 1 +-#define DASD_FMT_ERR_TOO_MANY_RECORDS 2 +-#define DASD_FMT_ERR_BLKSIZE 3 +-#define DASD_FMT_ERR_RECORD_ID 4 +-#define DASD_FMT_ERR_KEY_LENGTH 5 +- +-/* +- * values to be used for dasd_information2_t.format +- * 0x00: NOT formatted +- * 0x01: Linux disc layout +- * 0x02: Common disc layout +- */ +-#define DASD_FORMAT_NONE 0 +-#define DASD_FORMAT_LDL 1 +-#define DASD_FORMAT_CDL 2 +- +-/**************************************************************************** +- * SECTION: DASDFMT internal types * +- ****************************************************************************/ +- + #define DASD_PARTN_BITS 2 + #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + + #define EXIT_MISUSE 1 + #define EXIT_BUSY 2 +-#define LABEL_LENGTH 14 +-#define VLABEL_CHARS 84 +-#define LINE_LENGTH 80 + #define ERR_LENGTH 90 + + #define DEFAULT_BLOCKSIZE 4096 + /* requestsize - number of cylinders in one format step */ + #define DEFAULT_REQUESTSIZE 10 +-#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1) + + #define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} + #define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} +@@ -137,6 +92,9 @@ typedef struct dasdfmt_info { + int force_host; + int layout_specified; + int check; ++ int mode_specified; ++ int ese; ++ int no_discard; + } dasdfmt_info_t; + + +diff --git a/dasdinfo/Makefile b/dasdinfo/Makefile +index ea45bf1..ed81c3f 100644 +--- a/dasdinfo/Makefile ++++ b/dasdinfo/Makefile +@@ -1,7 +1,6 @@ + include ../common.mak + +-libs = $(rootdir)/libu2s/libu2s.a \ +- $(rootdir)/libutil/libutil.a \ ++libs = $(rootdir)/libutil/libutil.a \ + $(rootdir)/libdasd/libdasd.a + + all: dasdinfo +diff --git a/dasdview/Makefile b/dasdview/Makefile +index b7bdba3..59fda17 100644 +--- a/dasdview/Makefile ++++ b/dasdview/Makefile +@@ -7,7 +7,6 @@ all: dasdview + libs = $(rootdir)/libdasd/libdasd.a \ + $(rootdir)/libzds/libzds.a \ + $(rootdir)/libvtoc/libvtoc.a \ +- $(rootdir)/libu2s/libu2s.a \ + $(rootdir)/libutil/libutil.a + + dasdview: dasdview.o $(libs) +diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c +index d773b02..3cf5629 100644 +--- a/dasdview/dasdview.c ++++ b/dasdview/dasdview.c +@@ -28,10 +28,10 @@ + #include "lib/dasd_base.h" + #include "lib/dasd_sys.h" + #include "lib/libzds.h" +-#include "lib/u2s.h" + #include "lib/util_base.h" + #include "lib/util_opt.h" + #include "lib/util_prg.h" ++#include "lib/util_sys.h" + #include "lib/vtoc.h" + #include "lib/zt_common.h" + +@@ -183,7 +183,7 @@ dasdview_get_info(dasdview_info_t *info) + else + info->hw_cylinders = characteristics->no_cyl; + +- if (u2s_getbusid(info->device, info->busid) == -1) ++ if (util_sys_get_dev_addr(info->device, info->busid) != 0) + info->busid_valid = 0; + else + info->busid_valid = 1; +diff --git a/dasdview/dasdview.h b/dasdview/dasdview.h +index 50dd3fe..35f2c54 100644 +--- a/dasdview/dasdview.h ++++ b/dasdview/dasdview.h +@@ -11,7 +11,6 @@ + #define DASDVIEW_H + + #include +-#include "lib/u2s.h" + + /******************************************************************************** + * SECTION: Definitions needed for DASD-API (see dasd.h) +@@ -109,7 +108,7 @@ typedef struct dasdview_info + int f8c; + int f9c; + +- char busid[U2S_BUS_ID_SIZE]; ++ char busid[DASD_BUS_ID_SIZE]; + int busid_valid; + int raw_track_access; + struct zdsroot *zdsroot; +diff --git a/fdasd/Makefile b/fdasd/Makefile +index 360d81f..facd89e 100644 +--- a/fdasd/Makefile ++++ b/fdasd/Makefile +@@ -3,7 +3,6 @@ include ../common.mak + libs = $(rootdir)/libvtoc/libvtoc.a \ + $(rootdir)/libzds/libzds.a \ + $(rootdir)/libdasd/libdasd.a \ +- $(rootdir)/libu2s/libu2s.a \ + $(rootdir)/libutil/libutil.a + + all: fdasd +diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c +index 5de7955..569cc78 100644 +--- a/fdasd/fdasd.c ++++ b/fdasd/fdasd.c +@@ -935,7 +935,7 @@ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name) + fdasd_error(anc, device_verification_failed, err_str); + } + +- count = u2s_get_host_access_count(name); ++ count = dasd_get_host_access_count(name); + if (anc->force_host) { + if (count > 1) { + snprintf(err_str, ERROR_STRING_SIZE, +diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h +index cf7be2e..abd17f5 100644 +--- a/include/lib/dasd_base.h ++++ b/include/lib/dasd_base.h +@@ -20,6 +20,9 @@ + #include + #include + ++/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */ ++#define DASD_BUS_ID_SIZE 9 ++ + typedef struct dasd_information2_t { + unsigned int devno; /* S/390 devno */ + unsigned int real_devno; /* for aliases */ +@@ -51,6 +54,16 @@ typedef struct dasd_information2_t { + unsigned int reserved7; /* reserved for further use ,... */ + } dasd_information2_t; + ++/* ++ * values to be used for dasd_information2_t.format ++ * 0x00: NOT formatted ++ * 0x01: Linux disc layout ++ * 0x02: Common disc layout ++ */ ++#define DASD_FORMAT_NONE 0 ++#define DASD_FORMAT_LDL 1 ++#define DASD_FORMAT_CDL 2 ++ + struct dasd_eckd_characteristics { + unsigned short cu_type; + struct { +@@ -136,6 +149,16 @@ typedef struct format_data_t { + unsigned int intensity; + } format_data_t; + ++/* ++ * values to be used for format_data_t.intensity ++ */ ++#define DASD_FMT_INT_FMT_R0 1 /* write record zero */ ++#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */ ++#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ ++#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ ++#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ ++#define DASD_FMT_INT_ESE_FULL 32 /* release space for entire volume */ ++ + /* + * struct format_check_t + * represents all data necessary to evaluate the format of +@@ -154,6 +177,16 @@ typedef struct format_check_t { + unsigned int key_length; /* Key length of first record in error */ + } format_check_t; + ++/* ++ * values to be used in format_check_t for indicating ++ * possible format errors ++ */ ++#define DASD_FMT_ERR_TOO_FEW_RECORDS 1 ++#define DASD_FMT_ERR_TOO_MANY_RECORDS 2 ++#define DASD_FMT_ERR_BLKSIZE 3 ++#define DASD_FMT_ERR_RECORD_ID 4 ++#define DASD_FMT_ERR_KEY_LENGTH 5 ++ + #ifndef __linux__ + /* definition from hdreg.h */ + struct hd_geometry { +@@ -174,6 +207,8 @@ struct hd_geometry { + #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t) + /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ + #define BIODASDFMT _IOW(DASD_IOCTL_LETTER, 1, format_data_t) ++/* Release Allocated Space */ ++#define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) + /* Check device format according to format_data_t */ + #define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t) + +@@ -197,6 +232,7 @@ int dasd_check_format(const char *device, format_check_t *p); + int dasd_format_disk(int fd, format_data_t *p); + int dasd_disk_disable(const char *device, int *fd); + int dasd_disk_enable(int fd); ++int dasd_release_space(const char *device, format_data_t *r); + int dasd_get_blocksize(const char *device, unsigned int *blksize); + int dasd_get_blocksize_in_bytes(const char *device, unsigned long long *blksize); + int dasd_get_geo(const char *device, struct hd_geometry *geo); +diff --git a/include/lib/dasd_sys.h b/include/lib/dasd_sys.h +index 5438172..75b186e 100644 +--- a/include/lib/dasd_sys.h ++++ b/include/lib/dasd_sys.h +@@ -13,9 +13,10 @@ + #define LIB_DASD_SYS_H + + #include +-#include "u2s.h" + + int dasd_sys_raw_track_access(char *); ++int dasd_sys_ese(char *); + int dasd_reset_chpid(char *, char *); ++int dasd_get_host_access_count(char *device); + + #endif /* LIB_DASD_SYS_H */ +diff --git a/include/lib/u2s.h b/include/lib/u2s.h +deleted file mode 100644 +index fe5d673..0000000 +--- a/include/lib/u2s.h ++++ /dev/null +@@ -1,21 +0,0 @@ +-/* +- * +- * Copyright IBM Corp. 2004, 2017 +- * +- * s390-tools is free software; you can redistribute it and/or modify +- * it under the terms of the MIT license. See LICENSE for details. +- * +- * History of changes (starts July 2004) +- * 2004-07-02 initial +- */ +- +-#ifndef LIB_U2S_H +-#define LIB_U2S_H +- +-#define U2S_BUS_ID_SIZE 32 +- +-int u2s_getbusid(char *, char *); +-int u2s_read_attribute(char *, char *, char *, size_t); +-int u2s_get_host_access_count(char *); +- +-#endif /* LIB_U2S_H */ +diff --git a/include/lib/util_sys.h b/include/lib/util_sys.h +new file mode 100644 +index 0000000..e0f078b +--- /dev/null ++++ b/include/lib/util_sys.h +@@ -0,0 +1,17 @@ ++/* ++ * @defgroup util_sys_h util_sys: SysFS interface ++ * @{ ++ * @brief Work with SysFS ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 LIB_UTIL_SYS_H ++#define LIB_UTIL_SYS_H ++ ++int util_sys_get_dev_addr(const char *dev, char *addr); ++ ++#endif /** LIB_UTIL_SYS_H @} */ +diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c +index bbfb384..ef1605e 100644 +--- a/libdasd/dasd_ioctl.c ++++ b/libdasd/dasd_ioctl.c +@@ -228,6 +228,26 @@ int dasd_check_format(const char *device, format_check_t *p) + return 0; + } + ++/* ++ * Release Allocated Space ++ * ++ * @param[in] fd device node's file descriptor ++ * @param[in] r format options ++ * ++ * @retval 0 in case of success ++ * @retval errno in case of failure ++ */ ++int dasd_release_space(const char *device, format_data_t *r) ++{ ++ int fd; ++ ++ fd = dasd_open_device(device, O_RDONLY); ++ RUN_IOCTL(fd, BIODASDRAS, r); ++ dasd_close_device(fd); ++ ++ return 0; ++} ++ + /* + * Reread partition table + * +diff --git a/libdasd/dasd_sys.c b/libdasd/dasd_sys.c +index f2fd826..94c2d12 100644 +--- a/libdasd/dasd_sys.c ++++ b/libdasd/dasd_sys.c +@@ -12,7 +12,11 @@ + #include + #include + ++#include "lib/dasd_base.h" + #include "lib/dasd_sys.h" ++#include "lib/util_file.h" ++#include "lib/util_path.h" ++#include "lib/util_sys.h" + + /** + * Get raw-track access mode status +@@ -31,39 +35,80 @@ + */ + int dasd_sys_raw_track_access(char *devnode) + { +- char busid[9]; +- char path[47]; ++ char busid[DASD_BUS_ID_SIZE]; ++ char *path; + FILE *fp; + int rc; + +- if (u2s_getbusid(devnode, busid)) ++ if (util_sys_get_dev_addr(devnode, busid) != 0) + return 0; + +- sprintf(path, "/sys/bus/ccw/devices/%s/raw_track_access", busid); +- ++ path = util_path_sysfs("bus/ccw/devices/%s/raw_track_access", busid); + fp = fopen(path, "r"); +- if (!fp) ++ if (!fp) { ++ free(path); + return 0; ++ } + + rc = fgetc(fp) - '0'; + fclose(fp); ++ free(path); + + return (rc == 1) ? 1 : 0; + } + ++/** ++ * Is volume extent space efficient a.k.a. thin-provisioned ++ * ++ * The "devnode" parameter can be any valid relative or absolute path to ++ * a DASD device node, for example: ++ * ++ * - /dev/dasda ++ * - /dev/disk/by-path/ccw-0.0.bf20 ++ * ++ * @param[in] devnode Device node of interest ++ * ++ * @retval 1 Volume is extent space efficient ++ * @retval 0 Volume is not extent space efficient or ++ * cannot be determined ++ */ ++int dasd_sys_ese(char *devnode) ++{ ++ char busid[DASD_BUS_ID_SIZE]; ++ char *path; ++ FILE *fp; ++ int rc; ++ ++ if (util_sys_get_dev_addr(devnode, busid) != 0) ++ return 0; ++ ++ path = util_path_sysfs("bus/ccw/devices/%s/ese", busid); ++ fp = fopen(path, "r"); ++ if (!fp) { ++ free(path); ++ return 0; ++ } ++ ++ rc = fgetc(fp) - '0'; ++ fclose(fp); ++ ++ return (rc == 1) ? 1 : 0; ++} + + int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) + { + unsigned int val; +- char path[40]; + int count, i; ++ char *path; + FILE *fp; + +- sprintf(path, "/sys/bus/ccw/devices/%s/../chpids", busid); ++ path = util_path_sysfs("bus/ccw/devices/%s/../chpids", busid); + *mask = 0; + fp = fopen(path, "r"); +- if (!fp) ++ if (!fp) { ++ free(path); + return ENODEV; ++ } + + for (i = 0; i < 8; i++) { + count = fscanf(fp, " %x", &val); +@@ -75,6 +120,7 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) + *mask = 0x80 >> i; + } + fclose(fp); ++ free(path); + + return 0; + } +@@ -102,22 +148,25 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) + int dasd_reset_chpid(char *devnode, char *chpid_char) + { + unsigned int chpid; +- char path[41]; +- char busid[9]; ++ char busid[DASD_BUS_ID_SIZE]; + int mask, rc; + char *endptr; ++ char *path; + FILE *fp; + +- if (u2s_getbusid(devnode, busid)) ++ if (util_sys_get_dev_addr(devnode, busid) != 0) + return ENODEV; + + if (!chpid_char) { +- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); ++ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); + fp = fopen(path, "w"); +- if (!fp) ++ if (!fp) { ++ free(path); + return ENODEV; ++ } + fprintf(fp, "%s", "all\n"); + fclose(fp); ++ free(path); + return 0; + } + +@@ -132,12 +181,45 @@ int dasd_reset_chpid(char *devnode, char *chpid_char) + if (!mask) + return ENOENT; + +- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); ++ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); + fp = fopen(path, "w"); +- if (!fp) ++ if (!fp) { ++ free(path); + return ENODEV; ++ } + fprintf(fp, "%02x", mask); + fclose(fp); ++ free(path); + + return 0; + } ++ ++/** ++ * Read amount of host with access to \p device ++ * ++ * The \p device can be any valid relative or absolute path to a DASD device ++ * node, for example: ++ * ++ * - /dev/dasda ++ * - /dev/disk/by-path/ccw-0.0.bf20 ++ * ++ * @param[in] device Device node of interest ++ * ++ * @retval n Number of hosts with access to \p device ++ * @retval 0 Value could not be determined ++ */ ++int dasd_get_host_access_count(char *device) ++{ ++ char busid[9]; ++ char *path; ++ long value; ++ ++ if (!util_sys_get_dev_addr(device, busid)) ++ return 0; ++ ++ path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid); ++ util_file_read_l(&value, 10, path); ++ free(path); ++ ++ return value; ++} +diff --git a/libu2s/Makefile b/libu2s/Makefile +deleted file mode 100644 +index 523282b..0000000 +--- a/libu2s/Makefile ++++ /dev/null +@@ -1,14 +0,0 @@ +-include ../common.mak +- +-lib = libu2s.a +- +-all: $(lib) +- +-objects = u2s.o misc.o +- +-$(lib): $(objects) +- +-install: all +- +-clean: +- rm -f *.o $(lib) +diff --git a/libu2s/misc.c b/libu2s/misc.c +deleted file mode 100644 +index d29784c..0000000 +--- a/libu2s/misc.c ++++ /dev/null +@@ -1,27 +0,0 @@ +-/* +- * Misc - Local helper functions +- * +- * Copyright IBM Corp. 2016, 2017 +- * +- * 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 +-#include "lib/util_base.h" +- +-/* +- * Helper function that copies a string safely +- */ +-size_t misc_strlcpy(char *dest, const char *src, size_t size) +-{ +- size_t str_len = strlen(src); +- size_t len; +- +- if (size) { +- len = MIN(size - 1, str_len); +- memcpy(dest, src, len); +- dest[len] = '\0'; +- } +- return str_len; +-} +diff --git a/libu2s/misc.h b/libu2s/misc.h +deleted file mode 100644 +index 53b7356..0000000 +--- a/libu2s/misc.h ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * Misc - Local helper functions +- * +- * Copyright IBM Corp. 2016, 2017 +- * +- * 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 MISC_H +-#define MISC_H +- +-size_t misc_strlcpy(char *, const char *, size_t); +- +-#endif /* MISC_H */ +diff --git a/libu2s/u2s.c b/libu2s/u2s.c +deleted file mode 100644 +index 9ffafa7..0000000 +--- a/libu2s/u2s.c ++++ /dev/null +@@ -1,334 +0,0 @@ +-/* +- * +- * Copyright IBM Corp. 2004, 2017 +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "lib/u2s.h" +-#include "misc.h" +- +-#define DEV_BUFFER_LENGTH 20 +-#define PATH_BUFFER_LENGTH 256 +-#define BUSIDSIZE 9 +- +-#define BLOCKPATH "/sys/block/" +-#define DEVICE_LINK "device" +-#define DEV_ATTRIBUTE "dev" +- +- +-/* +- * Helper function that expects a file name and returns 1 if this +- * is a directory or 0 otherwise. +- */ +-static int isdir(char *name) { +- +- struct stat statbuf; +- +- if (stat(name, &statbuf) < 0) +- return 0; +- return S_ISDIR(statbuf.st_mode); +-} +- +-/* +- * Helper function that expects a directory name in sysfs of the form +- * /sys/block// or /sys/block///. +- * It will try to read the file "dev" in this directory and compare +- * it's contents with the given dev string of the form :. +- * Trailing white space (newline) is ignored. +- * The buffer name is expected to be long enough to hold the additional "dev". +- * Returns 1 if the directory matches dev, 0 otherwise. +- */ +-static int check_directory(char *name, char *dev) { +- +- char buffer[DEV_BUFFER_LENGTH]; +- char *end; +- int fd; +- ssize_t count; +- int dev_attr_len, dev_parm_len; +- unsigned int namelen; +- +- namelen = strlen(name); +- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEV_ATTRIBUTE)) +- return 0; +- end = name + namelen; +- strcpy(end, DEV_ATTRIBUTE); +- fd = open(name, O_RDONLY); +- *end = 0; +- if (fd < 0) +- return 0; +- count = read(fd, buffer, DEV_BUFFER_LENGTH); +- close(fd); +- if (count < 0) +- return 0; +- dev_attr_len = strspn(buffer, "1234567890:"); +- dev_parm_len = strlen(dev); +- if (dev_attr_len != dev_parm_len ) +- return 0; +- return (strncmp(dev, buffer, dev_parm_len) == 0); +-} +- +-/* +- * Helper function that expects a directory name in sysfs of the form +- * /sys/block//. It will try to read a link "device" +- * in this directory and extract the busid, which is the last part +- * of that link. The buffer name is expected to be long enough +- * to hold the additional "device". +- * name: block device path in sysfs. +- * busid: buffer in which the busid string will be returned +- * returns 0 for successful operation and -1 in case of an error. +- */ +-static int extract_busid(char *name, char *busid) { +- +- int count; +- unsigned int namelen; +- char linkbuffer[PATH_BUFFER_LENGTH]; +- char *start, *end; +- size_t len; +- +- namelen = strlen(name); +- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEVICE_LINK)) +- return 0; +- end = name + namelen; +- strcpy(end, DEVICE_LINK); +- count = readlink(name, linkbuffer, PATH_BUFFER_LENGTH - 1); +- if (count < 0) +- return -1; +- linkbuffer[count] = 0; +- start = strrchr(linkbuffer, '/'); +- if (!start) +- return -1; +- start++; +- len = misc_strlcpy(busid, start, BUSIDSIZE); +- if (len >= BUSIDSIZE) +- return -1; +- +- return 0; +-}; +- +-/* +- * Helper function that makes some basic checks on a directory entry. +- * The function checks if there is still enough space left in the buffer +- * for the new string, excludes '.' and '..', and verifies that the entry +- * is actually a directory. +- * buffer: the beginning of the name buffer +- * oldend: the current end of the string in the name buffer +- * dir: the dirent in question +- * returns: a pointer to the new end of the string in buffer or NULL if +- * one of the checks failed +- */ +- +-static char *append_if_directory(char *buffer, char *oldend, struct dirent *dir) { +- +- char *newend; +- int oldlength, dirlength; +- +- if (strcmp(dir->d_name, ".") == 0 || +- strcmp(dir->d_name, "..") == 0) +- return NULL; +- oldlength = strlen(buffer); +- dirlength = strlen(dir->d_name); +- if (PATH_BUFFER_LENGTH < oldlength + dirlength + 2) +- return NULL; +- strcpy(oldend, dir->d_name); +- if (!isdir(buffer)) { +- *oldend = 0; +- return NULL; +- } +- newend = oldend + dirlength; +- strcpy(newend, "/"); +- newend++; +- +- return newend; +-} +- +-/* +- * helper function that searches for a specific block device and returns +- * it's busid +- * dev: : of the device +- * busid: buffer in which the busid string will be returned +- * returns 0 for successful operation and -1 in case of an error. +- */ +-static int find_busid_in_sysfs(char *dev, char *busid) { +- +- DIR *blockdir, *diskdir; +- struct dirent *blockde, *diskde; +- int found = 0; +- char namebuffer[PATH_BUFFER_LENGTH]; +- char *blockend, *diskend = NULL, *partend; +- +- /* everything, including the other helper functions, works on the +- * same buffer area 'namebuffer'. The pointers blockend, diskend +- * and partend point to the end of the various names. +- * Example: +- * "/sys/block/dasda/dasda1/" +- * ^ blockend +- * ^ diskend +- * ^ partend +- */ +- +- strcpy(namebuffer,BLOCKPATH); +- blockdir = opendir(namebuffer); +- if (!blockdir) +- return -1; +- blockend = namebuffer + strlen(namebuffer); +- /* check each entry in /sys/block */ +- while ((blockde = readdir(blockdir))) { +- diskend = append_if_directory(namebuffer, blockend, blockde); +- if (!diskend) +- continue; +- found = check_directory(namebuffer, dev); +- if (found) +- break; +- diskdir = opendir(namebuffer); +- if (!diskdir) +- continue; +- /* check each entry in /sys/block/ */ +- while ((diskde = readdir(diskdir))) { +- partend = append_if_directory( +- namebuffer, diskend, diskde); +- if (!partend) +- continue; +- found = check_directory(namebuffer, dev); +- if (found) +- break; +- } +- closedir(diskdir); +- if (found) +- break; +- } +- closedir(blockdir); +- if (found) { +- *diskend = 0; /* remove partition directory from name */ +- return extract_busid(namebuffer, busid); +- } else +- return -1; +-} +- +-/* +- * helper function that searches for a specific block device in +- * /proc/dasd/devices and returns it's bus-ID +- * maja, mina: , of the device +- * busid: buffer in which the bus-ID string will be returned +- * returns 0 for successful operation and -1 in case of an error +- * e.g. /proc/dasd/devices does not exist. +- * +- * An entry looks like: +- * 0.0.XXXX(DISCIPLINE) at ( MAJ: MIN) is dasdX : +- * active at blocksize: BLOCKSIZE, BLOCKS blocks, SIZE MB +- */ +-static int find_busid_in_proc(int maja, int mina, char *busid) +-{ +- FILE *filp; +- char bus[BUSIDSIZE]; +- int majb, minb, rc; +- size_t len; +- +- rc = -1; +- +- filp = fopen("/proc/dasd/devices", "r"); +- if (!filp) +- return rc; +- while (fscanf(filp, "%[^(] %*[^)] ) at ( %d : %d %*[^\n]\n", +- bus, &majb, &minb) != EOF) { +- if ((maja == majb) && (mina == minb)) { +- len = misc_strlcpy(busid, bus, BUSIDSIZE); +- if (len < BUSIDSIZE) +- rc = 0; +- break; +- } +- } +- +- fclose(filp); +- return rc; +-} +- +-/* +- * Return the busid of a given device node. +- * Works only for block devices. +- * devicenode: path to the device node +- * busid: buffer in which the busid string will be returned +- * returns 0 for successful operation and -1 in case of an error. +- */ +-int u2s_getbusid(char *devicenode, char *busid) +-{ +- int maj, min, rc; +- struct stat stat_buf; +- char dev_string[DEV_BUFFER_LENGTH]; +- +- /* +- * Get major and minor information of the device special file +- * and combine them to a : string, as returned by +- * the dev attributes in sysfs +- */ +- if (stat(devicenode, &stat_buf)) +- return -1; +- if (!S_ISBLK(stat_buf.st_mode)) +- return -1; +- maj = major(stat_buf.st_rdev); +- min = minor(stat_buf.st_rdev); +- +- rc = find_busid_in_proc(maj, min, busid); +- if (rc) { +- snprintf(dev_string, DEV_BUFFER_LENGTH, "%u:%u", maj, min); +- rc = find_busid_in_sysfs(dev_string, busid); +- } +- +- return rc; +-} +- +-/* +- * Attempts to find the sysfs entry for the given busid and reads +- * the contents of a specified attribute to the buffer +- */ +-int u2s_read_attribute(char *busid, char *attribute, char *buffer, +- size_t count) +-{ +- char path[100]; +- int rc, fd; +- ssize_t rcount; +- +- rc = 0; +- snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s", +- busid, attribute); +- fd = open(path, O_RDONLY); +- if (fd < 0) +- return errno; +- rcount = read(fd, buffer, count); +- if (rcount < 0) +- rc = errno; +- close(fd); +- return rc; +-} +- +-int u2s_get_host_access_count(char *devicenode) +-{ +- char busid[BUSIDSIZE]; +- unsigned long value; +- char buffer[10]; +- char *endp; +- +- u2s_getbusid(devicenode, busid); +- u2s_read_attribute(busid, "host_access_count", buffer, sizeof(buffer)); +- +- value = strtoul(buffer, &endp, 0); +- +- if (endp == buffer) +- return -EINVAL; +- +- return value; +-} +diff --git a/libutil/Makefile b/libutil/Makefile +index fd5e213..8a11db2 100644 +--- a/libutil/Makefile ++++ b/libutil/Makefile +@@ -26,7 +26,8 @@ objects = util_base.o \ + util_part.o \ + util_prg.o \ + util_proc.o \ +- util_rec.o ++ util_rec.o \ ++ util_sys.o + + util_base_example: util_base_example.o $(lib) + util_panic_example: util_panic_example.o $(lib) +diff --git a/libutil/util_sys.c b/libutil/util_sys.c +new file mode 100644 +index 0000000..05e3653 +--- /dev/null ++++ b/libutil/util_sys.c +@@ -0,0 +1,78 @@ ++/* ++ * util - Utility function library ++ * ++ * SysFS helper functions ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/util_path.h" ++#include "lib/util_sys.h" ++ ++/* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */ ++#define READLINK_SIZE 256 ++ ++/** ++ * Identify device address ++ * ++ * Identifying the device address with this function works for almost any ++ * character and block device (e.g. NVMe, SCSI, DASD, etc). ++ * The user must provide a buffer that is large enough for the desired device ++ * address to be read into \p addr. ++ * ++ * @param[in] dev Device node of interest ++ * @param[out] addr Identified device address ++ * ++ * @retval 0 Success ++ * @retval -1 Error while reading device information or ++ * constructed path ++ */ ++int util_sys_get_dev_addr(const char *dev, char *addr) ++{ ++ char device[READLINK_SIZE], *result; ++ unsigned int maj, min; ++ struct stat s; ++ ssize_t len; ++ char *path; ++ ++ if (stat(dev, &s) != 0) ++ return -1; ++ ++ maj = major(s.st_rdev); ++ min = minor(s.st_rdev); ++ ++ if (S_ISBLK(s.st_mode)) ++ path = util_path_sysfs("dev/block/%u:%u/device", maj, min); ++ else if (S_ISCHR(s.st_mode)) ++ path = util_path_sysfs("dev/char/%u:%u/device", maj, min); ++ else ++ return -1; ++ ++ len = readlink(path, device, READLINK_SIZE - 1); ++ free(path); ++ if (len != -1) ++ device[len] = '\0'; ++ else ++ return -1; ++ ++ result = strrchr(device, '/'); ++ if (result) ++ result++; ++ else ++ result = device; ++ strcpy(addr, result); ++ ++ return 0; ++} +diff --git a/libzds/libzds.c b/libzds/libzds.c +index 2430962..affb30c 100644 +--- a/libzds/libzds.c ++++ b/libzds/libzds.c +@@ -20,8 +20,8 @@ + #include + + #include "lib/dasd_base.h" ++#include "lib/dasd_sys.h" + #include "lib/libzds.h" +-#include "lib/u2s.h" + #include "lib/util_base.h" + #include "lib/util_list.h" + #include "lib/vtoc.h" +@@ -3727,7 +3727,7 @@ int lzds_analyse_open_count(struct zdsroot *root, int warn) + int rc = 0; + + util_list_iterate(root->dasdlist, dasd) { +- value = u2s_get_host_access_count(dasd->device); ++ value = dasd_get_host_access_count(dasd->device); + + if (value < 0) { + fprintf(stderr, +diff --git a/tunedasd/src/Makefile b/tunedasd/src/Makefile +index 959b8eb..c79a235 100644 +--- a/tunedasd/src/Makefile ++++ b/tunedasd/src/Makefile +@@ -3,7 +3,6 @@ include ../../common.mak + ALL_CPPFLAGS += -I../include -I../boot + + libs = $(rootdir)/libdasd/libdasd.a \ +- $(rootdir)/libu2s/libu2s.a \ + $(rootdir)/libutil/libutil.a + + all: tunedasd +diff --git a/zconf/lsdasd b/zconf/lsdasd +index a0af6ea..792efc0 100755 +--- a/zconf/lsdasd ++++ b/zconf/lsdasd +@@ -164,6 +164,7 @@ function gatherDeviceData() { + read DEV_UID 2> /dev/null < $DEVPATH/uid || continue + read READONLY 2> /dev/null < $DEVPATH/readonly || continue + read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue ++ read ESE 2> /dev/null < $DEVPATH/ese + + # Block device specific information is only available for + # devices that are online and not a PAV alias +@@ -244,7 +245,7 @@ function newoutput() + #-------------------------------------------# + + if [[ "$ONLINE" == 0 ]]; then +- printf "%s:%s:%-8s offline\n" \ ++ printf "%s:%s:%-8s offline\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" ; + return +@@ -252,7 +253,7 @@ function newoutput() + + if [[ "$ALIAS" == 1 ]]; then + if [[ "$BASEONLY" == "false" ]]; then +- printf "%s:%s:%-8s alias %28s\n" \ ++ printf "%s:%s:%-8s alias %26s\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" \ + "$DISCIPLINE" +@@ -286,7 +287,11 @@ function newoutput() + ACTIVE="active" + fi + +- printf "%s:%s:%-8s %-6s%-4s %-8s %-2s:%-2s %-4s %-4s %-8s %s\n" \ ++ if [[ "$ESE" == 1 ]]; then ++ DISCIPLINE="${DISCIPLINE} (ESE)" ++ fi ++ ++ printf "%s:%s:%-8s %-6s%-2s %-8s %-2s:%-2s %-11s %-4s %-8s %s\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" \ + "$ACTIVE" \ +@@ -378,6 +383,10 @@ function extended() + read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks + read -a C 2> /dev/null < $DEVPATH/../chpids + read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom ++ read ESE 2> /dev/null < $DEVPATH/ese ++ read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size ++ read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity ++ read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated + + # convert to hexadecimal values + PIM=0x$PIM +@@ -550,7 +559,11 @@ function extended() + COLON=":" + fi + +- printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ ++ if [[ "$ESE" == 1 ]]; then ++ DISCIPLINE="${DISCIPLINE} (ESE)" ++ fi ++ ++ printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" \ + "$BLOCKNAME" \ +@@ -562,6 +575,9 @@ function extended() + "$SSIZE" \ + "$MBSIZE" \ + "$BLOCKCOUNT" \ ++ "$EXTSZ" \ ++ "$CAPACITY" \ ++ "$ALLOCATED" \ + "$DIAG" \ + "$READONLY" \ + "$EER" \ +@@ -787,10 +803,10 @@ fi + + if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then + printf "Bus-ID Name UID\n" +- printf "==============================================================================\n" ++ printf "================================================================================\n" + elif [[ "$OUTPUT" == "new" ]]; then +- printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n" +- printf "==============================================================================\n" ++ printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n" ++ printf "================================================================================\n" + elif [[ "$OUTPUT" == "extended" ]]; then + PROCESSING=" $PROCESSING | sed 's/#/\n/g' " + fi +diff --git a/zdsfs/Makefile b/zdsfs/Makefile +index 579ce4b..07d7658 100644 +--- a/zdsfs/Makefile ++++ b/zdsfs/Makefile +@@ -2,9 +2,8 @@ include ../common.mak + + libs = $(rootdir)/libzds/libzds.a \ + $(rootdir)/libvtoc/libvtoc.a \ +- $(rootdir)/libu2s/libu2s.a \ +- $(rootdir)/libutil/libutil.a \ +- $(rootdir)/libdasd/libdasd.a ++ $(rootdir)/libdasd/libdasd.a \ ++ $(rootdir)/libutil/libutil.a + + ifeq (${HAVE_FUSE},0) + +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index bed970c..fd77670 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -7,8 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \ + -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) + ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) + +-libs = $(rootdir)/libutil/libutil.a \ +- $(rootdir)/libu2s/libu2s.a ++libs = $(rootdir)/libutil/libutil.a + + objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ + install.o zipl.o $(rootdir)/zipl/boot/data.o +diff --git a/zipl/src/install.c b/zipl/src/install.c +index a6c4333..20b53f5 100644 +--- a/zipl/src/install.c ++++ b/zipl/src/install.c +@@ -23,7 +23,7 @@ + #include + #include + +-#include "lib/u2s.h" ++#include "lib/util_sys.h" + + #include "boot.h" + #include "bootmap.h" +@@ -1132,7 +1132,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count, + parm.param[i].num_heads = info[i]->geo.heads; + parm.param[i].blocksize = info[i]->phy_block_size >> 8; + parm.param[i].devno = info[i]->devno; +- if (u2s_getbusid(device[i], busid)) { ++ if (util_sys_get_dev_addr(device[i], busid) != 0) { + error_text("Could not find bus-ID for '%s'", device[i]); + rc = -1; + goto out; +-- +2.21.3 + + +From 836d4b9f0f4387a3e35125be8ee50563501d6ab7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 12:29:42 +0100 +Subject: [PATCH 29/44] zdsfs: add online vtoc refresh (#1685536) + +Description: Enable zdsfs to access datasets that were created after zdsfs was + mounted without the need to remount zdsfs. + This is done by re-reading the VTOC with every readdir system + call. + To ensure a consistent VTOC state the DASD device is reserved for + every VTOC read and released afterwards. +Upstream-ID: bc053a975af6bd8eecd31269656f305d4c744fbe +--- + dasdview/dasdview.c | 2 +- + include/lib/dasd_base.h | 6 ++ + include/lib/libzds.h | 83 ++++++++++++++++-- + libdasd/dasd_ioctl.c | 39 +++++++++ + libzds/libzds.c | 184 +++++++++++++++++----------------------- + zdsfs/zdsfs.c | 97 +++++++++++++++++---- + 6 files changed, 280 insertions(+), 131 deletions(-) + +diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c +index 3cf5629..3dc5890 100644 +--- a/dasdview/dasdview.c ++++ b/dasdview/dasdview.c +@@ -1741,7 +1741,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info) + " rc=%d\n", rc); + exit(-1); + } +- rc = lzds_dasd_read_rawvtoc(info->dasd); ++ rc = lzds_dasd_alloc_rawvtoc(info->dasd); + if (rc == EINVAL) { + zt_error_print("dasdview: Cannot read VTOC because disk does" + " not contain valid VOL1 label.\n", +diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h +index abd17f5..b12e1f9 100644 +--- a/include/lib/dasd_base.h ++++ b/include/lib/dasd_base.h +@@ -203,6 +203,10 @@ struct hd_geometry { + #define BIODASDDISABLE _IO(DASD_IOCTL_LETTER, 0) + /* Enable the volume (for Linux) */ + #define BIODASDENABLE _IO(DASD_IOCTL_LETTER, 1) ++/* Reserve the device for the current LPAR */ ++#define BIODASDRSRV _IO(DASD_IOCTL_LETTER, 2) ++/* Release the device for the current LPAR */ ++#define BIODASDRLSE _IO(DASD_IOCTL_LETTER, 3) + /* Get information on a dasd device (enhanced) */ + #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t) + /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ +@@ -239,5 +243,7 @@ int dasd_get_geo(const char *device, struct hd_geometry *geo); + int dasd_get_info(const char *device, dasd_information2_t *info); + int dasd_is_ro(const char *device, bool *ro); + int dasd_reread_partition_table(const char *device, int ntries); ++int dasd_disk_reserve(const char *device); ++int dasd_disk_release(const char *device); + + #endif /* LIB_DASD_BASE_H */ +diff --git a/include/lib/libzds.h b/include/lib/libzds.h +index 355bf77..6ae767b 100644 +--- a/include/lib/libzds.h ++++ b/include/lib/libzds.h +@@ -112,6 +112,8 @@ + */ + #define LIB_LIBZDS_H + ++#include "lib/util_base.h" ++#include "lib/util_list.h" + #include "vtoc.h" + + +@@ -328,11 +330,74 @@ struct pds_member_entry { + */ + struct zdsroot; + ++/** ++ * @struct raw_vtoc ++ * @brief The VTOC is a directory of data sets on one DASD ++ * ++ * As the VTOC is the data area on the DASD that describes all data sets, ++ * this library will often have to refer to the various records in the VTOC. ++ * To make this more efficient, we will read the whole VTOC once and identify ++ * all elements (DSCBs). The raw data of the VTOC tracks and the index to the ++ * DSCBs is stored. ++ */ ++struct raw_vtoc { ++ /** @brief The raw track data */ ++ char *rawdata; ++ /** @brief This size of the raw track data in bytes */ ++ unsigned long long rawdatasize; ++ /** @brief An array with pointers to the various DSCBs in the rawdata */ ++ char **vtocindex; ++ /** @brief Number of entries in the index */ ++ unsigned int vtocindexcount; ++ /** @brief Number of records per VTOC track ++ * ++ * @note While the DS4DEVDT field in the format 4 DSCB names the number ++ * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1 ++ * for record 0. ++ */ ++ unsigned int vtoc_rec_per_track; ++ /** @brief The track number at which the vtoc begins on the DASD */ ++ unsigned int vtoctrackoffset; ++ /** @brief Start record of VTOC. ++ * ++ * The rawdata contains full tracks. This is the number of the first ++ * record that actually belongs to the VTOC ++ */ ++ unsigned int vtocrecno; ++ /** @brief The DASD this vtoc was read from */ ++ struct dasd *dasd; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ + /** + * @struct dasd + * @brief Represents one physical device, may have a vtoc + */ +-struct dasd; ++struct dasd { ++ /** @brief List head used to store a list of DASDs in struct zdsroot */ ++ struct util_list_node list; ++ /** @brief Name of the block device, e.g. /dev/dasde */ ++ char *device; ++ /** @brief File descriptor for the block device. ++ * ++ * The device is kept open for as along as the library uses it. ++ * This lets the system know that the device is still in use. ++ */ ++ int inusefd; ++ /* @brief where to find the volume label */ ++ unsigned int label_block; ++ /** @brief Device geometry. How many cylinders does the DASD have. */ ++ unsigned int cylinders; ++ /** @brief Device geometry. How many heads does the DASD have. */ ++ unsigned int heads; ++ /** @brief The VTOC data that has been read from this device */ ++ struct raw_vtoc *rawvtoc; ++ /** @brief The volume label that has been read from this device */ ++ volume_label_t *vlabel; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; + + /** + * @struct dasditerator +@@ -350,12 +415,6 @@ struct dasditerator; + */ + struct dasdhandle; + +-/** +- * @struct raw_vtoc +- * @brief The VTOC is a directory of data sets on one dasd +- */ +-struct raw_vtoc; +- + /** + * @struct dscbiterator + * @brief allows to iterate over all DSCBs in a vtoc +@@ -572,7 +631,13 @@ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel); + * @brief Read the vtoc data from device. The data as stored as part + * of the struct dasd. + */ +-int lzds_dasd_read_rawvtoc(struct dasd *dasd); ++int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc); ++ ++/** ++ * @brief Read the vtoc data from device. The data as stored as part ++ * of the struct dasd. ++ */ ++int lzds_dasd_alloc_rawvtoc(struct dasd *dasd); + + /** + * @brief Get the previously read raw_vtoc data. +@@ -787,6 +852,8 @@ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, + struct dasd *dasd); + + ++void lzds_dslist_free(struct zdsroot *root); ++ + + /** @} */ /* end of group libzds_functions_high */ + +diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c +index ef1605e..22e6d21 100644 +--- a/libdasd/dasd_ioctl.c ++++ b/libdasd/dasd_ioctl.c +@@ -281,3 +281,42 @@ int dasd_reread_partition_table(const char *device, int ntries) + + return err; + } ++ ++/* ++ * Reserve DASD disk. ++ * ++ * @param[in] device node device node's name ++ * ++ * @retval 0 in case of success ++ * @retval errno in case of failure ++ * ++ */ ++int dasd_disk_reserve(const char *device) ++{ ++ int fd; ++ ++ fd = dasd_open_device(device, O_RDONLY); ++ RUN_IOCTL(fd, BIODASDRSRV, NULL); ++ dasd_close_device(fd); ++ ++ return 0; ++} ++ ++/* ++ * Release DASD disk ++ * ++ * @param[in] device node device node's name ++ * ++ * @retval 0 in case of success ++ * @retval errno in case of failure ++ */ ++int dasd_disk_release(const char *device) ++{ ++ int fd; ++ ++ fd = dasd_open_device(device, O_RDONLY); ++ RUN_IOCTL(fd, BIODASDRLSE, NULL); ++ dasd_close_device(fd); ++ ++ return 0; ++} +diff --git a/libzds/libzds.c b/libzds/libzds.c +index affb30c..c444769 100644 +--- a/libzds/libzds.c ++++ b/libzds/libzds.c +@@ -22,8 +22,6 @@ + #include "lib/dasd_base.h" + #include "lib/dasd_sys.h" + #include "lib/libzds.h" +-#include "lib/util_base.h" +-#include "lib/util_list.h" + #include "lib/vtoc.h" + + /** @cond PRIVATE */ +@@ -70,42 +68,6 @@ struct errormsg { + char text[ERRORMSG]; + }; + +-/** +- * As the VTOC is the data area on the DASD that describes all data sets, +- * this library will often have to refer to the various records in the VTOC. +- * To make this more efficiant, we will read the whole VTOC once and identify +- * all elements (DSCBs). The raw data of the VTOC tracks and the index to the +- * DSCBs is stored. +- */ +-struct raw_vtoc { +- /** @brief The raw track data */ +- char *rawdata; +- /** @brief This size of the raw track data in bytes */ +- unsigned long long rawdatasize; +- /** @brief An array with pointers to the various DSCBs in the rawdata */ +- char **vtocindex; +- /** @brief Number of entries in the index */ +- unsigned int vtocindexcount; +- /** @brief Number of records per VTOC track +- * +- * @note While the DS4DEVDT field in the format 4 DSCB names the number +- * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1 +- * for record 0. +- */ +- unsigned int vtoc_rec_per_track; +- /** @brief The track number in which the vtoc begins on the DASD */ +- unsigned int vtoctrackoffset; +- /** @brief Start record of VTOC. +- * +- * The rawdata contains full tracks. This is the number of the first +- * record that actually belongs to the VTOC */ +- unsigned int vtocrecno; +- /** @brief The DASD this vtoc was read from */ +- struct dasd *dasd; +- /** @brief Detailed error messages in case of a problem */ +- struct errorlog *log; +-}; +- + struct dscbiterator { + /** @brief The raw_vtoc this iterator refers to */ + struct raw_vtoc *rawvtoc; +@@ -113,31 +75,6 @@ struct dscbiterator { + unsigned int i; + }; + +-struct dasd { +- /** @brief List head used to store a list of DASDs in struct zdsroot */ +- struct util_list_node list; +- /** @brief Name of the block device, e.g. /dev/dasde */ +- char *device; +- /** @brief File descriptor for the block device. +- * +- * The device is kept open for as along as the library uses it. +- * This lets the system know that the device is still in use. +- */ +- int inusefd; +- /* @brief where to find the volume label */ +- unsigned int label_block; +- /** @brief Device geometry. How many cylinders does the DASD have. */ +- unsigned int cylinders; +- /** @brief Device geometry. How many heads does the DASD have. */ +- unsigned int heads; +- /** @brief The VTOC data that has been read from this device */ +- struct raw_vtoc *rawvtoc; +- /** @brief The volume label that has been read from this device */ +- volume_label_t *vlabel; +- /** @brief Detailed error messages in case of a problem */ +- struct errorlog *log; +-}; +- + struct dasdhandle { + /** @brief The struct dasd this context relates to */ + struct dasd *dasd; +@@ -412,6 +349,7 @@ int lzds_zdsroot_alloc(struct zdsroot **root) + return 0; + } + ++ + /** + * It should be noted that this frees all structures that are owned by the + * root structure as well. For example, a pointer to a struct dasd that +@@ -419,21 +357,11 @@ int lzds_zdsroot_alloc(struct zdsroot **root) + * + * @param[in] root Reference to the zdsroot structure that is to be freed. + */ +-void lzds_zdsroot_free(struct zdsroot *root) ++void lzds_dslist_free(struct zdsroot *root) + { +- struct dasd *dasd, *nextdasd; + struct dataset *ds, *nextds; + int i; + +- if (!root) +- return; +- +- util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { +- util_list_remove(root->dasdlist, dasd); +- dasd_free(dasd); +- } +- util_list_free(root->dasdlist); +- + util_list_iterate_safe(root->datasetlist, ds, nextds) { + util_list_remove(root->datasetlist, ds); + dataset_free_memberlist(ds); +@@ -442,6 +370,28 @@ void lzds_zdsroot_free(struct zdsroot *root) + errorlog_free(ds->log); + free(ds); + } ++} ++ ++/** ++ * It should be noted that this frees all structures that are owned by the ++ * root structure as well. For example, a pointer to a struct dasd that ++ * has been returned by lzds_zdsroot_add_device is not valid anymore. ++ * ++ * @param[in] root Reference to the zdsroot structure that is to be freed. ++ */ ++void lzds_zdsroot_free(struct zdsroot *root) ++{ ++ struct dasd *dasd, *nextdasd; ++ ++ if (!root) ++ return; ++ ++ util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { ++ util_list_remove(root->dasdlist, dasd); ++ dasd_free(dasd); ++ } ++ util_list_free(root->dasdlist); ++ lzds_dslist_free(root); + util_list_free(root->datasetlist); + errorlog_free(root->log); + free(root); +@@ -1443,7 +1393,7 @@ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, + * - EPROTO The VTOC data is not in a valid format. + * - EIO Other I/O error + */ +-int lzds_dasd_read_rawvtoc(struct dasd *dasd) ++int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc) + { + unsigned long long vtoctrckno, vtocrecno; + unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize; +@@ -1455,26 +1405,11 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd) + format4_label_t *f4; + unsigned long long rawvtocsize; + +- struct raw_vtoc *rawvtoc = NULL; + volume_label_t *vlabel = NULL; + char *trackdata = NULL; + char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */ + + errorlog_clear(dasd->log); +- /* cleanup the old rawvtoc structures before we read new ones */ +- rawvtoc = dasd->rawvtoc; +- dasd->rawvtoc = NULL; +- if (rawvtoc) { +- free(rawvtoc->rawdata); +- free(rawvtoc->vtocindex); +- free(rawvtoc); +- } +- +- rawvtoc = malloc(sizeof(*rawvtoc)); +- if (!rawvtoc) +- return ENOMEM; +- memset(rawvtoc, 0, sizeof(*rawvtoc)); +- rawvtoc->dasd = dasd; + + rc = lzds_dasd_get_vlabel(dasd, &vlabel); + if (rc) { +@@ -1611,13 +1546,49 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd) + ++i; + } + +- dasd->rawvtoc = rawvtoc; + return 0; + + cleanup: +- free(rawvtoc->vtocindex); + free(trackdata); +- free(rawvtoc); ++ return rc; ++} ++ ++/** ++ * @param[in] dasd The struct dasd that represents the device we want to read ++ * the VTOC from. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - EINVAL The volume label has not yet been read or it is not valid. ++ * - EPROTO The VTOC data is not in a valid format. ++ * - EIO Other I/O error ++ */ ++int lzds_dasd_alloc_rawvtoc(struct dasd *dasd) ++{ ++ struct raw_vtoc *rawvtoc = NULL; ++ int rc; ++ ++ /* cleanup the old rawvtoc structures before we read new ones */ ++ rawvtoc = dasd->rawvtoc; ++ dasd->rawvtoc = NULL; ++ if (rawvtoc) { ++ free(rawvtoc->rawdata); ++ free(rawvtoc->vtocindex); ++ free(rawvtoc); ++ } ++ ++ rawvtoc = malloc(sizeof(*rawvtoc)); ++ if (!rawvtoc) ++ return ENOMEM; ++ memset(rawvtoc, 0, sizeof(*rawvtoc)); ++ rawvtoc->dasd = dasd; ++ ++ rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc); ++ if (rc) { ++ free(rawvtoc->vtocindex); ++ free(rawvtoc); ++ } else { ++ dasd->rawvtoc = rawvtoc; ++ } + return rc; + } + +@@ -2261,6 +2232,7 @@ out1: + static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds) + { + int k, l, dspcount; ++ + for (k = 0; k < MAXVOLUMESPERDS; ++k) { + /* if both datasets have a part in position k, + * then something is wrong */ +@@ -2280,18 +2252,20 @@ static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds) + * Since dsp[0] may not be set yet, we loop over the + * base dsp array until we find an entry. + */ +- for (l = 0; l < MAXVOLUMESPERDS; ++l) +- if (baseds->dsp[l]) { +- if (memcmp(baseds->dsp[l]->f1->DS1DSSN, +- newds->dsp[k]->f1->DS1DSSN, +- MAXVOLSER)) +- return errorlog_add_message( +- &baseds->log, NULL, EPROTO, +- "merge dataset: part %d has incompatible" +- " base volume serial\n", k); +- else +- break; +- } ++ for (l = 0; l < MAXVOLUMESPERDS; ++l) { ++ if (!baseds->dsp[l]) ++ continue; ++ if (memcmp(baseds->dsp[l]->f1->DS1DSSN, ++ newds->dsp[k]->f1->DS1DSSN, ++ MAXVOLSER)) ++ return errorlog_add_message( ++ &baseds->log, NULL, EPROTO, ++ "merge dataset: part %d has incompatible base volume serial\n", ++ k); ++ else ++ break; ++ } ++ + baseds->dsp[k] = newds->dsp[k]; + baseds->dspcount++; + +diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c +index 462fda7..d7a83ee 100644 +--- a/zdsfs/zdsfs.c ++++ b/zdsfs/zdsfs.c +@@ -52,6 +52,8 @@ struct zdsfs_info { + }; + + static struct zdsfs_info zdsfsinfo; ++static int zdsfs_create_meta_data_buffer(struct zdsfs_info *); ++static int zdsfs_verify_datasets(void); + + struct zdsfs_file_info { + struct dshandle *dsh; +@@ -194,6 +196,48 @@ static int zdsfs_getattr(const char *path, struct stat *stbuf) + return 0; + } + ++static void zdsfs_read_device(struct dasd *newdasd, const char *device) ++{ ++ struct errorlog *log; ++ int rc; ++ ++ rc = dasd_disk_reserve(device); ++ if (rc) { ++ fprintf(stderr, "error when reserving device %s: %s\n", ++ device, strerror(rc)); ++ lzds_dasd_get_errorlog(newdasd, &log); ++ lzds_errorlog_fprint(log, stderr); ++ exit(1); ++ } ++ rc = lzds_dasd_alloc_rawvtoc(newdasd); ++ if (rc) { ++ fprintf(stderr, "error when reading VTOC from device %s: %s\n", ++ device, strerror(rc)); ++ lzds_dasd_get_errorlog(newdasd, &log); ++ lzds_errorlog_fprint(log, stderr); ++ exit(1); ++ } ++ rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot, ++ newdasd); ++ if (rc) { ++ fprintf(stderr, ++ "error when extracting data sets from dasd %s: %s\n", ++ device, strerror(rc)); ++ lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log); ++ lzds_errorlog_fprint(log, stderr); ++ exit(1); ++ } ++ rc = dasd_disk_release(device); ++ if (rc) { ++ fprintf(stderr, "error when releasing device %s: %s\n", ++ device, strerror(rc)); ++ lzds_dasd_get_errorlog(newdasd, &log); ++ lzds_errorlog_fprint(log, stderr); ++ exit(1); ++ } ++} ++ ++ + static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs) + { + struct dasditerator *dasdit; +@@ -240,6 +284,33 @@ static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs) + return 0; + } + ++ ++static int zdsfs_update_vtoc(void) ++{ ++ struct dasditerator *dasdit; ++ struct dasd *dasd; ++ int rc; ++ ++ lzds_dslist_free(zdsfsinfo.zdsroot); ++ rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit); ++ if (rc) ++ return -ENOMEM; ++ ++ while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd)) ++ zdsfs_read_device(dasd, dasd->device); ++ ++ lzds_dasditerator_free(dasdit); ++ rc = zdsfs_verify_datasets(); ++ if (rc) ++ return rc; ++ ++ rc = zdsfs_create_meta_data_buffer(&zdsfsinfo); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ + static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi)) + { +@@ -253,6 +324,10 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + int rc; + int ispds, issupported; + ++ rc = zdsfs_update_vtoc(); ++ if (rc) ++ return rc; ++ + /* we have two type of directories + * type one: the root directory contains all data sets + */ +@@ -329,6 +404,9 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi) + goto error1; + + if (strcmp(path, "/"METADATAFILE) == 0) { ++ rc = zdsfs_update_vtoc(); ++ if (rc) ++ return rc; + zfi->dsh = NULL; + zfi->is_metadata_file = 1; + zfi->metaread = 0; +@@ -596,6 +674,7 @@ static int zdsfs_verify_datasets(void) + if (rc) + return ENOMEM; + while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { ++ lzds_dataset_get_name(ds, &dsname); + lzds_dataset_get_is_complete(ds, &iscomplete); + if (!iscomplete) { + lzds_dataset_get_name(ds, &dsname); +@@ -822,23 +901,7 @@ static void zdsfs_process_device(const char *device) + lzds_errorlog_fprint(log, stderr); + exit(1); + } +- rc = lzds_dasd_read_rawvtoc(newdasd); +- if (rc) { +- fprintf(stderr, "error when reading VTOC from device %s:" +- " %s\n", device, strerror(rc)); +- lzds_dasd_get_errorlog(newdasd, &log); +- lzds_errorlog_fprint(log, stderr); +- exit(1); +- } +- rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot, +- newdasd); +- if (rc) { +- fprintf(stderr, "error when extracting data sets from dasd %s:" +- " %s\n", device, strerror(rc)); +- lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log); +- lzds_errorlog_fprint(log, stderr); +- exit(1); +- } ++ zdsfs_read_device(newdasd, device); + } + + static void zdsfs_process_device_file(const char *devfile) +-- +2.21.3 + + +From db8ece16f1c2b4e514d39505397bc4d70052617b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 12:31:54 +0100 +Subject: [PATCH 30/44] zdev: add zfcp dix parameter handling (#1723852) + +Description: The zfcp kernel module was changed to introduce separate + parameters for selecting DIF and DIF&DIX. This patch + implements the corresponding changes in chzdev and + lszdev. +Upstream-ID: 2c4c210ca8c641ad6e051eea65493202a16a5f4a +--- + zdev/src/zfcp.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/zdev/src/zfcp.c b/zdev/src/zfcp.c +index 03aa252..82dac57 100644 +--- a/zdev/src/zfcp.c ++++ b/zdev/src/zfcp.c +@@ -75,16 +75,28 @@ static struct attrib zfcp_tattr_allow_lun_scan = { + + static struct attrib zfcp_tattr_dif = { + .name = "dif", +- .title = "Enable DIF/DIX data consistency checking", ++ .title = "Enable DIF data consistency checking", + .desc = +- "Control the use of the end-to-end data consistency checking\n" +- "mechanism (DIF/DIX):\n" ++ "Control the use of the DIF data consistency checking\n" ++ "mechanism:\n" + " 0: DIF is disabled\n" + " 1: DIF is enabled when supported by the FCP device hardware\n", + .defval = "0", + .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), + }; + ++static struct attrib zfcp_tattr_dix = { ++ .name = "dix", ++ .title = "Enable DIF&DIX data consistency checking", ++ .desc = ++ "Control the use of the end-to-end data consistency checking\n" ++ "mechanism (DIF&DIX):\n" ++ " 0: DIF&DIX is disabled\n" ++ " 1: DIF&DIX is enabled when supported by the FCP device hardware\n", ++ .defval = "0", ++ .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), ++}; ++ + static struct attrib zfcp_tattr_datarouter = { + .name = "datarouter", + .title = "Enable hardware data routing", +@@ -160,6 +172,7 @@ static void all_bools_to_num(struct setting_list *list) + util_list_iterate(&list->list, s) { + if (s->attrib == &zfcp_tattr_allow_lun_scan || + s->attrib == &zfcp_tattr_dif || ++ s->attrib == &zfcp_tattr_dix || + s->attrib == &zfcp_tattr_datarouter || + s->attrib == &zfcp_tattr_no_auto_port_rescan) { + /* Convert Y to 1 and N to 0. */ +@@ -295,6 +308,7 @@ struct devtype zfcp_devtype = { + &zfcp_tattr_queue_depth, + &zfcp_tattr_allow_lun_scan, + &zfcp_tattr_dif, ++ &zfcp_tattr_dix, + &zfcp_tattr_datarouter, + &zfcp_tattr_no_auto_port_rescan, + &zfcp_tattr_port_scan_ratelimit, +-- +2.21.3 + + +From 41bb1d87abfe5f4b856ba885109749d42959a812 Mon Sep 17 00:00:00 2001 +From: Philipp Rudo +Date: Wed, 21 Aug 2019 12:34:43 +0200 +Subject: [PATCH 31/44] zipl: Fix error message printed with --dumptofs + (#1750307) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The option --dump does not exist. Adjust it to --dumpto. + +Signed-off-by: Philipp Rudo +Signed-off-by: Jan Höppner +--- + zipl/src/job.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zipl/src/job.c b/zipl/src/job.c +index e68d1b7..5dcebaf 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -114,7 +114,7 @@ get_command_line(int argc, char* argv[], struct command_line* line) + break; + case 'D': + error_reason("dumptofs has been deprecated, use " +- "--dump instead"); ++ "--dumpto instead"); + rc = -1; + break; + case 'M': +-- +2.21.3 + + +From 47f3a82078811bd4dfad2726ed3ba38cc0fe2e3f Mon Sep 17 00:00:00 2001 +From: Stefan Haberland +Date: Wed, 4 Sep 2019 13:01:53 +0200 +Subject: [PATCH 32/44] zipl: set correct secure IPL default value (#1750326) + +Set secure IPL to auto as default value to match documented behavior. + +(cherry picked from commit 367598b18738d26e92302812deb5fdbdc2617ba6) +--- + zipl/src/job.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 5dcebaf..1178a7d 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -1880,6 +1880,7 @@ job_get(int argc, char* argv[], struct job_data** data) + job->add_files = cmdline.add_files; + job->data.mvdump.force = cmdline.force; + job->dry_run = cmdline.dry_run; ++ job->is_secure = SECURE_BOOT_AUTO; + /* Get job data from user input */ + if (cmdline.help) { + job->command_line = 1; +-- +2.21.3 + + +From d64cb3431d7cd51a4b4c24ed3e0a2e50a024b50b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 12:49:06 +0100 +Subject: [PATCH 33/44] zkey: various enhancements (#1725881) + +Description: Enhancements to zkey in response to first (customer) experiences + +Upstream-ID: 9561a0b98385180441b2f5aa338fb583a94d4c40 +Upstream-ID: a648dbb0149513885eba4ced84ca9e1e65c6caca +Upstream-ID: d95dc6d698b5a666920201e39f3ccd0b1afb918c +Upstream-ID: b26dbfe8320c9ddb72b86efbe532b616243e0e3f +Upstream-ID: a69470d7e02af2d7d6a4c781eef57727c0672fc0 +Upstream-ID: 79ce12055369009be3d5d0a170e8d6edde0e96f2 +Upstream-ID: ed6e3b727059e0533b5ccf7bb697a7fe1a5f151b +Upstream-ID: 951bd1ae54a074cf59a4719909d6f6122f5c0448 +Upstream-ID: ec9c67189e1b841a922a627a9066d17222b51a01 +Upstream-ID: 90dc65659fa868ab446db07cac82235fa8a184a5 +Upstream-ID: b0c7965234c80af83b0419dc3781aff323cd04d4 +Upstream-ID: 5a0c93443c6a1d31d238444c798272979f0c5bf3 +Upstream-ID: 3ed8ab4e2a071180cfdce1a673d5101b7a09e171 +Upstream-ID: 11bfa1d3c8bdec60c5d0b04e93431e55c264d5ef +Upstream-ID: e693173eef75b9c67ad5516c2e1bc3d5ded5bbee +Upstream-ID: e4cd42900f1922b25d99af1134e2695799d14248 +Upstream-ID: f97d048643ef105e2c4dcdcf656d4dee5b2d7a15 +--- + zkey/keystore.c | 315 ++++++++++++++++++++++++++++++----------- + zkey/keystore.h | 24 ++-- + zkey/pkey.c | 10 +- + zkey/zkey-cryptsetup.1 | 36 +++++ + zkey/zkey-cryptsetup.c | 81 ++++++++++- + zkey/zkey.1 | 244 +++++++++++++++++++++++++++++-- + zkey/zkey.c | 282 ++++++++++++++++++++++++++++++++++-- + 7 files changed, 866 insertions(+), 126 deletions(-) + +diff --git a/zkey/keystore.c b/zkey/keystore.c +index a4ad634..33c8f93 100644 +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -259,13 +259,13 @@ static int _keystore_set_file_permission(struct keystore *keystore, + + if (chmod(filename, keystore->mode) != 0) { + rc = -errno; +- warnx("chmod faild on file '%s': %s", filename, strerror(-rc)); ++ warnx("chmod failed on file '%s': %s", filename, strerror(-rc)); + return rc; + } + + if (chown(filename, geteuid(), keystore->owner) != 0) { + rc = -errno; +- warnx("chown faild on file '%s': %s", filename, strerror(-rc)); ++ warnx("chown failed on file '%s': %s", filename, strerror(-rc)); + return rc; + } + +@@ -369,7 +369,7 @@ out: + return 0; + } + +-typedef int (*check_association_t)(const char *value, bool remove, ++typedef int (*check_association_t)(const char *value, bool remove, bool set, + char **normalized, void *private); + + /** +@@ -407,7 +407,7 @@ static int _keystore_set_association(struct properties *key_props, + + for (i = 0; newvals[i] != NULL; i++) { + if (check_func != NULL) { +- rc = check_func(newvals[i], 0, &normalized, ++ rc = check_func(newvals[i], 0, 1, &normalized, + check_private); + if (rc != 0) + goto out; +@@ -488,7 +488,7 @@ static int _keystore_add_association(struct properties *key_props, + + for (i = 0; newvals[i] != NULL; i++) { + if (check_func != NULL) { +- rc = check_func(newvals[i], 0, &normalized, ++ rc = check_func(newvals[i], 0, 0, &normalized, + check_private); + if (rc != 0) + goto out; +@@ -567,7 +567,7 @@ static int _keystore_remove_association(struct properties *key_props, + + for (i = 0; delvals[i] != NULL; i++) { + if (check_func != NULL) { +- rc = check_func(delvals[i], 1, &normalized, ++ rc = check_func(delvals[i], 1, 0, &normalized, + check_private); + if (rc != 0) + goto out; +@@ -1085,20 +1085,27 @@ out: + return rc; + } + ++struct apqn_check { ++ bool noonlinecheck; ++ bool nomsg; ++}; ++ + /** + * Checks an APQN value for its syntax. This is a callback function for + * function _keystore_change_association(). + * + * @param[in] apqn the APQN value to check + * @param[in] remove if true the apqn is removed ++ * @param[in] set if true the apqn is set (not used here) + * @param[out] normalized normalized value on return or NULL if no change +- * @param[in] private private data (not used here) ++ * @param[in] private private data (struct apqn_check) + * + * @returns 0 if successful, a negative errno value otherwise + */ +-static int _keystore_apqn_check(const char *apqn, bool remove, +- char **normalized, void *UNUSED(private)) ++static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), ++ char **normalized, void *private) + { ++ struct apqn_check *info = (struct apqn_check *)private; + int rc, card, domain; + regmatch_t pmatch[1]; + regex_t reg_buf; +@@ -1124,15 +1131,16 @@ static int _keystore_apqn_check(const char *apqn, bool remove, + + util_asprintf(normalized, "%02x.%04x", card, domain); + +- if (remove) { ++ if (remove || info->noonlinecheck) { + rc = 0; + goto out; + } + + rc = _keystore_is_apqn_online(card, domain); + if (rc != 1) { +- warnx("The APQN %02x.%04x is %s", card, domain, +- rc == -1 ? "not a CCA card" : "not online"); ++ if (info->nomsg == 0) ++ warnx("The APQN %02x.%04x is %s", card, domain, ++ rc == -1 ? "not a CCA card" : "not online"); + rc = -EIO; + goto out; + } else { +@@ -1149,6 +1157,7 @@ struct volume_check { + struct keystore *keystore; + const char *name; + const char *volume; ++ bool set; + }; + + /** +@@ -1173,6 +1182,11 @@ static int _keystore_volume_check_process(struct keystore *UNUSED(keystore), + { + struct volume_check *info = (struct volume_check *)private; + ++ if (info->set) { ++ if (strcmp(name, info->name) == 0) ++ return 0; ++ } ++ + warnx("Key '%s' is already associated with volume '%s'", name, + info->volume); + return -EINVAL; +@@ -1204,12 +1218,13 @@ static int _keystore_is_block_device(const char *volume) + * + * @param[in] volume the Volume value to check + * @param[in] remove if true the volume is removed ++ * @param[in] set if true the volume is set + * @param[out] normalized normalized value on return or NULL if no change + * @param[in] private private data: struct volume_check + * + * @returns 0 if successful, a negative errno value otherwise + */ +-static int _keystore_volume_check(const char *volume, bool remove, ++static int _keystore_volume_check(const char *volume, bool remove, bool set, + char **normalized, void *private) + { + struct volume_check *info = (struct volume_check *)private; +@@ -1250,6 +1265,7 @@ static int _keystore_volume_check(const char *volume, bool remove, + goto out; + } + ++ info->set = set; + rc = _keystore_process_filtered(info->keystore, NULL, info->volume, + NULL, NULL, + _keystore_volume_check_process, info); +@@ -1359,7 +1375,7 @@ struct keystore *keystore_new(const char *directory, bool verbose) + util_assert(directory != NULL, "Internal error: directory is NULL"); + + if (stat(directory, &sb) != 0) { +- warnx("'%s' does not exist", directory); ++ warnx("Can not access '%s': %s", directory, strerror(errno)); + return NULL; + } + if (!(sb.st_mode & S_IFDIR)) { +@@ -1543,6 +1559,8 @@ static int _keystore_set_default_properties(struct properties *key_props) + * 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 power + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system +@@ -1554,10 +1572,14 @@ static int _keystore_create_info_file(struct keystore *keystore, + const struct key_filenames *filenames, + const char *description, + const char *volumes, const char *apqns, ++ bool noapqncheck, + size_t sector_size, + const char *volume_type) + { +- struct volume_check vol_check = { .keystore = keystore, .name = name }; ++ struct volume_check vol_check = { .keystore = keystore, .name = name, ++ .set = 0 }; ++ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, ++ .nomsg = 0 }; + struct properties *key_props; + char temp[10]; + int rc; +@@ -1583,7 +1605,8 @@ static int _keystore_create_info_file(struct keystore *keystore, + + rc = _keystore_change_association(key_props, PROP_NAME_APQNS, + apqns != NULL ? apqns : "", +- "APQN", _keystore_apqn_check, NULL); ++ "APQN", _keystore_apqn_check, ++ &apqn_check); + if (rc != 0) + goto out; + +@@ -1642,15 +1665,18 @@ out: + } + + /** +- * Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT +- * if no APQNs are specified. ++ * Extracts an online card/domain pair from the specified APQns. If none of the ++ * specified APQNs are online, then -ENODEV is returned. ++ * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero. + */ + static int _keystore_get_card_domain(const char *apqns, unsigned int *card, + unsigned int *domain) + { ++ struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 }; + char **apqn_list; + char *normalized = NULL; + int rc = 0; ++ int i; + + *card = AUTOSELECT; + *domain = AUTOSELECT; +@@ -1662,17 +1688,23 @@ static int _keystore_get_card_domain(const char *apqns, unsigned int *card, + if (apqn_list[0] == NULL) + goto out; + +- rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL); +- if (normalized != NULL) +- free(normalized); +- if (rc != 0) +- goto out; ++ for (i = 0; apqn_list[i] != NULL; i++) { ++ rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized, ++ &apqn_check); ++ if (normalized != NULL) ++ free(normalized); ++ if (rc == -EINVAL) ++ goto out; ++ if (rc != 0) ++ continue; + +- if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) { +- rc = -EINVAL; +- goto out; ++ if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2) ++ goto found; + } + ++ warnx("None of the specified APQNs is online or of type CCA"); ++ rc = -ENODEV; ++found: + out: + str_list_free_string_array(apqn_list); + return rc; +@@ -1688,6 +1720,8 @@ out: + * 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 power + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system +@@ -1704,9 +1738,10 @@ out: + */ + int keystore_generate_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, size_t sector_size, +- size_t keybits, bool xts, const char *clear_key_file, +- const char *volume_type, int pkey_fd) ++ const char *apqns, bool noapqncheck, ++ size_t sector_size, size_t keybits, bool xts, ++ const char *clear_key_file, const char *volume_type, ++ int pkey_fd) + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; +@@ -1748,7 +1783,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- sector_size, volume_type); ++ noapqncheck, sector_size, volume_type); + if (rc != 0) + goto out_free_props; + +@@ -1781,6 +1816,8 @@ out_free_key_filenames: + * 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 power + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system +@@ -1792,7 +1829,7 @@ out_free_key_filenames: + */ + int keystore_import_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, size_t sector_size, ++ const char *apqns, bool noapqncheck, size_t sector_size, + const char *import_file, const char *volume_type) + { + struct key_filenames file_names = { NULL, NULL, NULL }; +@@ -1832,7 +1869,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- sector_size, volume_type); ++ noapqncheck, sector_size, volume_type); + if (rc != 0) + goto out_free_props; + +@@ -1870,6 +1907,8 @@ out_free_key_filenames: + * key, or an APQN prefixed with '+' or '-' to add or + * remove that APQN respectively. If NULL then the APQNs + * are not changed. ++ * @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 power + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system +@@ -1883,10 +1922,13 @@ out_free_key_filenames: + */ + int keystore_change_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, long int sector_size, +- const char *volume_type) ++ const char *apqns, bool noapqncheck, ++ long int sector_size, const char *volume_type) + { +- struct volume_check vol_check = { .keystore = keystore, .name = name }; ++ struct volume_check vol_check = { .keystore = keystore, .name = name, ++ .set = 0 }; ++ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, ++ .nomsg = 0 }; + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; + char temp[30]; +@@ -1931,7 +1973,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, + if (apqns != NULL) { + rc = _keystore_change_association(key_props, PROP_NAME_APQNS, + apqns, "APQN", +- _keystore_apqn_check, NULL); ++ _keystore_apqn_check, ++ &apqn_check); + if (rc != 0) + goto out; + } +@@ -1982,10 +2025,6 @@ int keystore_change_key(struct keystore *keystore, const char *name, + goto out; + } + +- rc = _keystore_set_file_permission(keystore, file_names.info_filename); +- if (rc != 0) +- goto out; +- + pr_verbose(keystore, "Successfully changed key '%s'", name); + + out: +@@ -2269,6 +2308,7 @@ static void _keystore_print_record(struct util_rec *rec, + struct validate_info { + struct util_rec *rec; + int pkey_fd; ++ bool noapqncheck; + unsigned long int num_valid; + unsigned long int num_invalid; + unsigned long int num_warnings; +@@ -2422,8 +2462,9 @@ static int _keystore_process_validate(struct keystore *keystore, + "master key\n", 0); + info->num_warnings++; + } +- if (_keystore_display_apqn_status(properties, name) != 0) +- info->num_warnings++; ++ if (info->noapqncheck == 0) ++ if (_keystore_display_apqn_status(properties, name) != 0) ++ info->num_warnings++; + if (_keystore_display_volume_status(properties, name) != 0) + info->num_warnings++; + +@@ -2439,11 +2480,16 @@ out: + * + * @param[in] keystore the key store + * @param[in] name_filter the name filter to select the key (can be NULL) ++ * @param[in] apqn_filter the APQN filter to select the key (can be NULL) ++ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for ++ * existence and type. ++ * @param[in] pkey_fd the file descriptor of /dev/pkey + * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_validate_key(struct keystore *keystore, const char *name_filter, +- const char *apqn_filter, int pkey_fd) ++ const char *apqn_filter, bool noapqncheck, ++ int pkey_fd) + { + struct validate_info info; + struct util_rec *rec; +@@ -2454,6 +2500,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter, + rec = _keystore_setup_record(1); + + info.pkey_fd = pkey_fd; ++ info.noapqncheck = noapqncheck; + info.rec = rec; + info.num_valid = 0; + info.num_invalid = 0; +@@ -2683,10 +2730,6 @@ static int _keystore_process_reencipher(struct keystore *keystore, + if (rc != 0) + goto out; + +- rc = _keystore_set_file_permission(keystore, out_file); +- if (rc != 0) +- goto out; +- + if (params.complete || params.inplace == 1) { + rc = _keystore_set_timestamp_property(properties, + PROP_NAME_REENC_TIME); +@@ -2712,11 +2755,6 @@ static int _keystore_process_reencipher(struct keystore *keystore, + goto out; + } + +- rc = _keystore_set_file_permission(keystore, +- file_names->info_filename); +- if (rc != 0) +- goto out; +- + util_asprintf(&temp, "The following LUKS2 volumes are " + "encrypted with key '%s'. You should also " + "re-encipher the volume key of those volumes " +@@ -2846,7 +2884,7 @@ int keystore_copy_key(struct keystore *keystore, const char *name, + const char *newname, const char *volumes) + { + struct volume_check vol_check = { .keystore = keystore, +- .name = newname }; ++ .name = newname, .set = 0 }; + struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames new_names = { NULL, NULL, NULL }; + struct properties *key_prop = NULL; +@@ -3255,6 +3293,13 @@ static int _keystore_execute_cmd(const char *cmd, + + struct crypt_info { + bool execute; ++ bool batch_mode; ++ const char *keyfile; ++ size_t keyfile_offset; ++ size_t keyfile_size; ++ size_t tries; ++ bool open; ++ bool format; + char **volume_filter; + int (*process_func)(struct keystore *keystore, + const char *volume, +@@ -3293,16 +3338,46 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, + const char *volume_type, + struct crypt_info *info) + { ++ char *keyfile_opt = NULL, *offset_opt = NULL; ++ char *size_opt = NULL, *tries_opt = NULL; ++ char *common_passphrase_options; ++ size_t common_len; + char temp[100]; + int rc = 0; + char *cmd; + + sprintf(temp, "--sector-size %lu ", sector_size); + ++ if (info->keyfile) { ++ util_asprintf(&keyfile_opt, "--key-file '%s' ", info->keyfile); ++ if (info->keyfile_offset > 0) ++ util_asprintf(&offset_opt, "--keyfile-offset %lu ", ++ info->keyfile_offset); ++ if (info->keyfile_size > 0) ++ util_asprintf(&size_opt, "--keyfile-size %lu ", ++ info->keyfile_size); ++ } ++ if (info->tries > 0) ++ util_asprintf(&tries_opt, "--tries %lu ", info->tries); ++ util_asprintf(&common_passphrase_options, "%s%s%s%s", ++ keyfile_opt != NULL ? keyfile_opt : "", ++ offset_opt != NULL ? offset_opt : "", ++ size_opt != NULL ? size_opt : "", ++ tries_opt != NULL ? tries_opt : ""); ++ common_len = strlen(common_passphrase_options); ++ free(keyfile_opt); ++ free(offset_opt); ++ free(size_opt); ++ free(tries_opt); ++ + if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) { ++ if (info->format) ++ return 0; ++ + util_asprintf(&cmd, +- "cryptsetup plainOpen %s--key-file '%s' " ++ "cryptsetup plainOpen %s%s--key-file '%s' " + "--key-size %lu --cipher %s %s%s %s", ++ info->batch_mode ? "-q " : "", + keystore->verbose ? "-v " : "", key_file_name, + key_file_size * 8, cipher_spec, + sector_size > 0 ? temp : "", volume, dmname); +@@ -3314,39 +3389,69 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, + printf("%s\n", cmd); + } + } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { +- util_asprintf(&cmd, +- "cryptsetup luksFormat %s--type luks2 " +- "--master-key-file '%s' --key-size %lu " +- "--cipher %s %s%s", +- keystore->verbose ? "-v " : "", key_file_name, +- key_file_size * 8, cipher_spec, +- sector_size > 0 ? temp : "", volume); +- +- if (info->execute) { +- printf("Executing: %s\n", cmd); +- rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ if (info->open) { ++ util_asprintf(&cmd, ++ "cryptsetup luksOpen %s%s%s%s %s", ++ info->batch_mode ? "-q " : "", ++ keystore->verbose ? "-v " : "", ++ common_len > 0 ? ++ common_passphrase_options : "", ++ volume, dmname); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } + } else { +- printf("%s\n", cmd); +- } +- +- free(cmd); +- if (rc != 0) +- return rc; +- +- util_asprintf(&cmd, +- "zkey-cryptsetup setvp %s%s", volume, +- keystore->verbose ? " -V " : ""); ++ /* ++ * Use PBKDF2 as key derivation function for LUKS2 ++ * volumes. LUKS2 uses Argon2i as default, but this ++ * might cause out-of-memory errors when multiple LUKS2 ++ * volumes are opened automatically via /etc/crypttab ++ */ ++ util_asprintf(&cmd, ++ "cryptsetup luksFormat %s%s--type luks2 " ++ "--master-key-file '%s' --key-size %lu " ++ "--cipher %s --pbkdf pbkdf2 %s%s%s", ++ info->batch_mode ? "-q " : "", ++ keystore->verbose ? "-v " : "", ++ key_file_name, key_file_size * 8, ++ cipher_spec, common_len > 0 ? ++ common_passphrase_options : "", ++ sector_size > 0 ? temp : "", volume); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } + +- if (info->execute) { +- printf("Executing: %s\n", cmd); +- rc = _keystore_execute_cmd(cmd, "zkey-cryptsetup"); +- } else { +- printf("%s\n", cmd); ++ free(cmd); ++ if (rc != 0) ++ return rc; ++ ++ util_asprintf(&cmd, ++ "zkey-cryptsetup setvp %s %s%s", volume, ++ common_len > 0 ? ++ common_passphrase_options : "", ++ keystore->verbose ? "-V" : ""); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, ++ "zkey-cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } + } + } else { + return -EINVAL; + } + ++ free(common_passphrase_options); + free(cmd); + return rc; + } +@@ -3376,7 +3481,7 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), + size_t key_file_size, + size_t sector_size, + const char *volume_type, +- struct crypt_info *UNUSED(info)) ++ struct crypt_info *info) + { + char temp[1000]; + +@@ -3395,11 +3500,22 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), + } + + sprintf(temp, ",sector-size=%lu", sector_size); +- printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n", ++ printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu%s\n", + dmname, volume, key_file_name, cipher_spec, + key_file_size * 8, sector_size > 0 ? temp : ""); + } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { +- printf("%s\t%s\n", dmname, volume); ++ printf("%s\t%s\t%s\tluks", dmname, volume, ++ info->keyfile != NULL ? info->keyfile : "none"); ++ if (info->keyfile != NULL) { ++ if (info->keyfile_offset > 0) ++ printf(",keyfile-offset=%lu", ++ info->keyfile_offset); ++ if (info->keyfile_size > 0) ++ printf(",keyfile-size=%lu", info->keyfile_size); ++ } ++ if (info->tries > 0) ++ printf(",tries=%lu", info->tries); ++ printf("\n"); + } else { + return -EINVAL; + } +@@ -3584,11 +3700,21 @@ out: + * @param[in] execute If TRUE the cryptsetup command is executed, + * otherwise it is printed to stdout + * @param[in] volume_type the type of volume to generate cryptsetup cmds for +- * * ++ * @param[in] keyfile If non-NULL, specifies the name of the file to ++ * read the passphrase from. ++ * @param[in] keyfile_offset the offset in bytes for reading from keyfile ++ * @param[in] keyfile_size the size in bytes for reading from keyfile ++ * @param[in] tries the number of tries for passphrase entry ++ * @param[in] batch_mode If TRUE, suppress cryptsetup confirmation questions ++ * @param[in] open If TRUE, generate luksOpen/plainOpen commands ++ * @param[in] format If TRUE, generate luksFormat commands + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, +- bool execute, const char *volume_type) ++ bool execute, const char *volume_type, ++ const char *keyfile, size_t keyfile_offset, ++ size_t keyfile_size, size_t tries, bool batch_mode, ++ bool open, bool format) + { + struct crypt_info info = { 0 }; + int rc; +@@ -3605,6 +3731,13 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, + } + + info.execute = execute; ++ info.open = open; ++ info.format = format; ++ info.batch_mode = batch_mode; ++ info.keyfile = keyfile; ++ info.keyfile_offset = keyfile_offset; ++ info.keyfile_size = keyfile_size; ++ info.tries = tries; + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_cryptsetup; + +@@ -3636,11 +3769,17 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, + * for the volume filter. If not specified, the filter + * checks the volume part only. + * @param[in] volume_type the type of volume to generate crypttab entries for ++ * @param[in] keyfile If non-NULL, specifies the name of the file to ++ * read the passphrase from. ++ * @param[in] keyfile_offset the offset in bytes for reading from keyfile ++ * @param[in] keyfile_size the size in bytes for reading from keyfile ++ * @param[in] tries the number of tries for passphrase entry + * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_crypttab(struct keystore *keystore, const char *volume_filter, +- const char *volume_type) ++ const char *volume_type, const char *keyfile, ++ size_t keyfile_offset, size_t keyfile_size, size_t tries) + { + struct crypt_info info = { 0 }; + int rc; +@@ -3656,6 +3795,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, + return -EINVAL; + } + ++ info.keyfile = keyfile; ++ info.keyfile_offset = keyfile_offset; ++ info.keyfile_size = keyfile_size; ++ info.tries = tries; + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_crypttab; + +diff --git a/zkey/keystore.h b/zkey/keystore.h +index 11a2103..16ecc62 100644 +--- a/zkey/keystore.h ++++ b/zkey/keystore.h +@@ -28,25 +28,27 @@ struct keystore *keystore_new(const char *directory, bool verbose); + + int keystore_generate_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, size_t sector_size, +- size_t keybits, bool xts, const char *clear_key_file, +- const char *volume_type, int pkey_fd); ++ const char *apqns, bool noapqncheck, ++ size_t sector_size, size_t keybits, bool xts, ++ const char *clear_key_file, const char *volume_type, ++ int pkey_fd); + + int keystore_import_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, size_t sector_size, ++ const char *apqns, bool noapqncheck, size_t sector_size, + const char *import_file, const char *volume_type); + + int keystore_change_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, long int sector_size, +- const char *volume_type); ++ const char *apqns, bool noapqncheck, ++ long int sector_size, const char *volume_type); + + int keystore_rename_key(struct keystore *keystore, const char *name, + const char *newname); + + int keystore_validate_key(struct keystore *keystore, const char *name_filter, +- const char *apqn_filter, int pkey_fd); ++ const char *apqn_filter, bool noapqncheck, ++ int pkey_fd); + + int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, + const char *apqn_filter, +@@ -68,10 +70,14 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter, + const char *volume_type); + + int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, +- bool execute, const char *volume_type); ++ bool execute, const char *volume_type, ++ const char *keyfile, size_t keyfile_offset, ++ size_t keyfile_size, size_t tries, bool batch_mode, ++ bool open, bool format); + + int keystore_crypttab(struct keystore *keystore, const char *volume_filter, +- const char *volume_type); ++ const char *volume_type, const char *keyfile, ++ size_t keyfile_offset, size_t keyfile_size, size_t tries); + + void keystore_free(struct keystore *keystore); + +diff --git a/zkey/pkey.c b/zkey/pkey.c +index 15e606a..a88c4e9 100644 +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -163,8 +163,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, + + buf = util_malloc(size); + count = fread(buf, 1, size, fp); +- if (count <= 0) { +- msg = feof(fp) ? "File is too small" : strerror(errno); ++ if (count != size) { ++ msg = ferror(fp) ? strerror(errno) : "File is too small"; + warnx("File '%s': %s", keyfile, msg); + free(buf); + buf = NULL; +@@ -211,7 +211,7 @@ int write_secure_key(const char *keyfile, const u8 *secure_key, + } + + count = fwrite(secure_key, 1, secure_key_size, fp); +- if (count <= 0) { ++ if (count != secure_key_size) { + warnx("File '%s': %s", keyfile, strerror(errno)); + fclose(fp); + return -EIO; +@@ -301,8 +301,8 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, + + buf = util_malloc(size); + count = fread(buf, 1, size, fp); +- if (count <= 0) { +- msg = feof(fp) ? "File is too small" : strerror(errno); ++ if (count != size) { ++ msg = ferror(fp) ? strerror(errno) : "File is too small"; + warnx("File '%s': %s", keyfile, msg); + free(buf); + buf = NULL; +diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 +index 988ef76..e93b5f0 100644 +--- a/zkey/zkey-cryptsetup.1 ++++ b/zkey/zkey-cryptsetup.1 +@@ -102,6 +102,7 @@ behave in the same way as with \fBcryptsetup\fP. + .IR bytes ] + .RB [ \-\-tries | \-T + .IR number ] ++.RB [ \-\-batch\-mode | \-q ] + .RB [ \-\-verbose | \-V ] + .RB [ \-\-debug | \-D ] + .PP +@@ -180,6 +181,20 @@ and + to control which part of the key file is used as passphrase. These options + behave in the same way as with \fBcryptsetup\fP. + .PP ++The ++.B reencipher ++command creates a new key slot with the re-enciphered secure AES volume key. ++The new key slot uses ++.B PBKDF2 ++as password based key derivation function. LUKS2 volumes typically default to ++.B Argon2i ++as password based key derivation function, ++but this might cause out-of-memory errors when multiple encrypted volumes are ++unlocked automatically at boot through /etc/crypttab. Because PAES ++uses secure AES keys as volume keys, the security of the key derivation ++function used to encrypt the volume key in the LUKS key slots is of less ++relevance. ++.PP + .B Note: + The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) + to be installed. For the supported environments and downloads, see: +@@ -247,6 +262,7 @@ behave in the same way as with \fBcryptsetup\fP. + .IR bytes ] + .RB [ \-\-tries | \-T + .IR number ] ++.RB [ \-\-batch\-mode | \-q ] + .RB [ \-\-verbose | \-V ] + .RB [ \-\-debug | \-D ] + .PP +@@ -288,6 +304,20 @@ and + .B \-\-keyfile\-size + to control which part of the key file is used as passphrase. These options + behave in the same way the same as with \fBcryptsetup\fP. ++.PP ++The ++.B setkey ++command creates a new key slot with the re-enciphered secure AES volume key. ++The new key slot uses ++.B PBKDF2 ++as password based key derivation function. LUKS2 volumes typically default to ++.B Argon2i ++as password based key derivation function, ++but this might cause out-of-memory errors when multiple encrypted volumes are ++unlocked automatically at boot through /etc/crypttab. Because PAES ++uses secure AES keys as volume keys, the security of the key derivation ++function used to encrypt the volume key in the LUKS key slots is of less ++relevance. + . + . + . +@@ -319,6 +349,9 @@ has been set (made active). When completing the staged re-enciphering, the + (unbound) key slot containing the re-enciphered secure volume key becomes + the active key slot and, optionally, all key slots containing the old secure + volume key are removed. ++.TP ++.BR \-q ", " \-\-batch\-mode ++Suppresses all confirmation questions. Use with care! + . + . + . +@@ -327,6 +360,9 @@ volume key are removed. + .BR \-m ", " \-\-master\-key\-file\~\fIfile\-name\fP + Specifies the name of a file containing the secure AES key that is set as the + new volume key. ++.TP ++.BR \-q ", " \-\-batch\-mode ++Suppresses all confirmation questions. Use with care! + . + . + . +diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c +index f7098f8..8180b4a 100644 +--- a/zkey/zkey-cryptsetup.c ++++ b/zkey/zkey-cryptsetup.c +@@ -35,6 +35,11 @@ + #include "misc.h" + #include "pkey.h" + ++/* Detect if cryptsetup 2.1 or later is available */ ++#ifdef CRYPT_LOG_DEBUG_JSON ++#define HAVE_CRYPT_KEYSLOT_GET_PBKDF ++#endif ++ + #define MAX_KEY_SIZE (8 * 1024 * 1024) + #define MAX_PASSWORD_SIZE 512 + #define KEYFILE_BUFLEN 4096 +@@ -93,6 +98,7 @@ static struct zkey_cryptsetup_globals { + bool inplace; + bool staged; + char *master_key_file; ++ bool batch_mode; + bool debug; + bool verbose; + void *lib_csulcca; +@@ -176,6 +182,11 @@ static struct util_opt opt_vec[] = { + .command = COMMAND_REENCIPHER, + }, + OPT_PASSPHRASE_ENTRY(COMMAND_REENCIPHER), ++ { ++ .option = {"batch-mode", 0, NULL, 'q'}, ++ .desc = "Suppresses all confirmation questions. Use with care!", ++ .command = COMMAND_REENCIPHER, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -204,6 +215,11 @@ static struct util_opt opt_vec[] = { + .command = COMMAND_SETKEY, + }, + OPT_PASSPHRASE_ENTRY(COMMAND_SETKEY), ++ { ++ .option = {"batch-mode", 0, NULL, 'q'}, ++ .desc = "Suppresses all confirmation questions. Use with care!", ++ .command = COMMAND_SETKEY, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -414,8 +430,13 @@ static void cryptsetup_log(int level, const char *msg, + fprintf(stderr, "%s: %s", program_invocation_short_name, msg); + break; + case CRYPT_LOG_DEBUG: +- fprintf(stderr, "%s: # %s", program_invocation_short_name, msg); ++ fprintf(stderr, "%s: # %s\n", program_invocation_short_name, msg); ++ break; ++#ifdef CRYPT_DEBUG_JSON ++ case CRYPT_DEBUG_JSON: ++ fprintf(stderr, "%s\n", msg); + break; ++#endif + default: + warnx("Internal error on logging class for msg: %s", msg); + break; +@@ -1096,7 +1117,11 @@ static int put_vp_token(struct crypt_device *cd, int token, + */ + static int open_device(const char *device, struct crypt_device **cd) + { +- const struct crypt_pbkdf_type *pbkdf; ++ const struct crypt_pbkdf_type pbkdf2 = { ++ .type = CRYPT_KDF_PBKDF2, ++ .hash = "sha256", ++ .time_ms = 2000, ++ }; + struct crypt_device *cdev = NULL; + int rc; + +@@ -1128,10 +1153,14 @@ static int open_device(const char *device, struct crypt_device **cd) + goto out; + } + +- pbkdf = crypt_get_pbkdf_type(cdev); +- rc = crypt_set_pbkdf_type(cdev, pbkdf); ++ /* ++ * Set PBKDF2 as default key derivation function. LUKS2 uses ++ * Argon2i as default, but this might cause out-of-memory errors when ++ * multiple LUKS2 volumes are opened automatically via /etc/crypttab ++ */ ++ rc = crypt_set_pbkdf_type(cdev, &pbkdf2); + if (rc != 0) { +- warnx("Failed to set the PBKDF-type for device '%s': %s", ++ warnx("Failed to set the PBKDF for device '%s': %s", + device, strerror(-rc)); + goto out; + } +@@ -1155,6 +1184,12 @@ static bool prompt_for_yes(void) + { + char str[20]; + ++ if (g.batch_mode) { ++ printf("(yes implied because '--batch-mode' | '-q' option is " ++ "specified)\n"); ++ return true; ++ } ++ + if (fgets(str, sizeof(str), stdin) == NULL) + return false; + +@@ -1311,6 +1346,9 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, + char **password, size_t *password_len, + const char *prompt) + { ++#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF ++ struct crypt_pbkdf_type pbkdf; ++#endif + char *vkey = NULL; + char *pw = NULL; + long long tries; +@@ -1362,6 +1400,30 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, + keyslot = rc; + pr_verbose("Volume key obtained from key slot %d", keyslot); + ++#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF ++ /* ++ * Get PBKDF of the key slot that was opened, and use its PBKDF for ++ * new key slots. ++ */ ++ memset(&pbkdf, 0, sizeof(pbkdf)); ++ rc = crypt_keyslot_get_pbkdf(g.cd, keyslot, &pbkdf); ++ if (rc != 0) { ++ warnx("Failed to get the PBKDF for key slot %d: %s", ++ keyslot, strerror(-rc)); ++ goto out; ++ } ++ ++ /* Reuse already benchmarked number of iterations */ ++ pbkdf.flags |= CRYPT_PBKDF_NO_BENCHMARK; ++ ++ rc = crypt_set_pbkdf_type(g.cd, &pbkdf); ++ if (rc != 0) { ++ warnx("Failed to set the PBKDF for new key slots: %s", ++ strerror(-rc)); ++ goto out; ++ } ++#endif ++ + if (key != NULL) + *key = vkey; + else +@@ -2191,6 +2253,9 @@ int main(int argc, char *argv[]) + case 'm': + g.master_key_file = optarg; + break; ++ case 'q': ++ g.batch_mode = true; ++ break; + case 'D': + g.debug = true; + g.verbose = true; +@@ -2238,7 +2303,11 @@ int main(int argc, char *argv[]) + + crypt_set_log_callback(NULL, cryptsetup_log, NULL); + if (g.debug) +- crypt_set_debug_level(-1); ++#ifdef CRYPT_DEBUG_JSON ++ crypt_set_debug_level(CRYPT_DEBUG_JSON); ++#else ++ crypt_set_debug_level(CRYPT_DEBUG_ALL); ++#endif + + if (command->open_device) { + if (g.pos_arg == NULL) { +diff --git a/zkey/zkey.1 b/zkey/zkey.1 +index 0837b27..c7cea83 100644 +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -92,6 +92,7 @@ key repository. + .IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a + .IR card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-no\-apqn\-check ] + .RB [ \-\-sector-size | \-S + .IR bytes ] + .RB [ \-\-volume-type | \-t +@@ -141,6 +142,9 @@ options. + .BR validate | val + .RB [ \-\-name | \-N + .IR key-name ] ++.RB [ \-\-apqns | \-a ++.IR card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-no\-apqn\-check ] + .RB [ \-\-verbose | \-V ] + .PP + Use the +@@ -160,8 +164,21 @@ contained in the secure key repository, specify the name of the key + or a pattern containing wildcards using the + .B \-\-name + option. When wildcards are used you must quote the value. +-If neither option \fIsecure\-key\-file\fP nor option ++You can also specify the ++.B \-\-apqns ++option to validate those secure keys which are associated with the specified ++cryptographic adapters (APQNs). You can use wildcards for the APQN ++specification. When wildcards are used you must quote the value. ++If both option ++.B \-\-name ++and option ++.B \-\-apqns ++are specified then all secure keys contained in the key repository that match ++both patterns are validated. ++If neither option \fIsecure\-key\-file\fP nor options + .B \-\-name ++or ++.B \-\-apqns + are specified, then all secure keys contained in the key repository + are validated. + . +@@ -259,7 +276,7 @@ and option + .B \-\-apqns + are specified then all secure keys + contained in the key repository that match both patterns are re-enciphered. +-If all both options are omitted, then all secure keys contained in the key ++If both options are omitted, then all secure keys contained in the key + repository are re-enciphered. + .PP + Re-enciphering a secure key contained in the secure key repository can be +@@ -298,6 +315,7 @@ to be installed. For the supported environments and downloads, see: + .IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a + .IR card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-no\-apqn\-check ] + .RB [ \-\-sector-size | \-S + .IR bytes ] + .RB [ \-\-volume-type | \-t +@@ -409,6 +427,7 @@ secure key. + .IR [+|-]volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a + .IR [+|-]card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-no\-apqn\-check ] + .RB [ \-\-sector-size | \-S + .IR bytes ] + .RB [ \-\-volume-type | \-t +@@ -477,8 +496,8 @@ option. You cannot use wildcards. + .B copy | co + .RB \-\-name | \-N + .IR key-name +-.B \-\-new-key-name | \-w +-.IR new-name ++.B \-\-new\-name | \-w ++.IR new-key-name + .RB [ \-\-volumes | \-l + .IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-verbose | \-V ] +@@ -509,6 +528,14 @@ volumes afterwards. + .IR volume1[:dmname1][,volume2[:dmname2][,...]] ] + .RB [ \-\-volume-type | \-t + .IR type ] ++.RB [ \-\-key\-file ++.IR file-name ] ++.RB [ \-\-keyfile\-offset ++.IR bytes ] ++.RB [ \-\-keyfile\-size ++.IR bytes ] ++.RB [ \-\-tries ++.IR number ] + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -527,6 +554,23 @@ name are selected. + Specify the + .B \-\-volume-type + option to generate crypttab entries for the specified volume type only. ++.P ++For LUKS2 volumes, a passphrase is required. You are prompted for the ++passphrase during system startup when crypttab is evaluated, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options are ++passed to the generated crypttab entries and are only available if ++.B zkey ++has been compiled with LUKS2 support enabled. + . + .SS "Generate cryptsetup commands for volumes associated with secure AES keys" + . +@@ -537,14 +581,24 @@ option to generate crypttab entries for the specified volume type only. + .RB [ \-\-volume-type | \-t + .IR type ] + .RB [ \-\-run | \-r ] ++.RB [ \-\-open ] ++.RB [ \-\-format ] ++.RB [ \-\-key\-file ++.IR file-name ] ++.RB [ \-\-keyfile\-offset ++.IR bytes ] ++.RB [ \-\-keyfile\-size ++.IR bytes ] ++.RB [ \-\-tries ++.IR number ] + .RB [ \-\-verbose | \-V ] + . + .PP + Use the + .B cryptsetup +-command to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksFormat\fP +-commands for volumes that are associated with secure keys contained in the +-secure key repository. Specify the ++command to generate \fBcryptsetup plainOpen\fP, \fBcryptsetup luksOpen\fP, or ++\fBcryptsetup luksFormat\fP commands for volumes that are associated with ++secure keys contained in the secure key repository. Specify the + .B \-\-volumes + option to limit the list + of volumes where cryptsetup commands are generated for. You can use wildcards. +@@ -556,7 +610,44 @@ name are selected. Specify the + option to generate cryptsetup commands for the specified volume type only. + Specify the + .B \-\-run +-option to run the generated cryptsetup commands. ++option to run the generated cryptsetup commands. Specify the ++.B \-\-open ++to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksOpen\fP commands. ++For the plain volume type, this is the default. Specify the ++.B \-\-format ++option to generate \fBcryptsetup luksFormat\fP commands. For the LUKS2 volume ++type, this is the default. If specified for the plain volume type, then no ++command is generated. ++.P ++For LUKS2 volumes, the generated \fBcryptsetup luksFormat\fP contains ++option \fB\-\-pbkdf pbkdf2\fP to set \fBPBKDF2\fP as password based key ++derivation function. LUKS2 volumes typically default to \fBArgon2i\fP as ++password based key derivation function, but this might cause out-of-memory ++errors when multiple encrypted volumes are unlocked automatically at boot ++through /etc/crypttab. Because PAES uses secure AES keys as volume keys, the ++security of the key derivation function used to encrypt the volume key in the ++LUKS key slots is of less relevance. ++.P ++For LUKS2 volumes, a passphrase is required. You are prompted for the ++passphrase when running the generated commands, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options are ++only available if ++.B zkey ++has been compiled with LUKS2 support enabled. To avoid cryptsetup confirmation ++questions, you can specify the ++.B \-\-batch\-mode ++option. These options are passed to the generated command(s) and behave in the ++same way as with \fBcryptsetup\fP. + . + . + . +@@ -603,8 +694,14 @@ Specifies a comma-separated list of cryptographic adapters in CCA + coprocessor mode (APQN) which are associated with the secure AES key in the + repository. Each APQN association specifies a card and domain number separated + by a period (like lszcrypt displays it). When at least one APQN is specified, +-then the first one is used to generate the key. If no APQNs are specified, +-then an APQN is selected automatically. All specified APQNs must be online. ++then the first online APQN is used to generate the key. If no APQNs are ++specified, then an APQN is selected automatically. All specified APQNs must be ++online, unless the \fB\-\-no\-apqn\-check\fP option is specified. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-no\-apqn\-check ++Do not check if the specified APQNs are available. Use this option to ++associate APQNs with a secure AES key that are currently not available. + This option is only used for secure keys contained in the secure key repository. + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP +@@ -631,6 +728,19 @@ Specifies the name of the secure key in the secure key repository. You can + use wildcards to select multiple secure keys in the secure key repository. + When wildcards are used you must quote the value. + This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQNs). You can use wildcards in the APQN specification. ++All secure keys contained in the secure key repository ++which are associated with the specified APQNs are validated. ++Each APQN specifies a card and domain number separated by a period (like ++lszcrypt displays it). ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-no\-apqn\-check ++Do not check if the associated APQNs are available. ++This option is only used for secure keys contained in the secure key repository. + . + . + . +@@ -714,7 +824,13 @@ This option is only used for secure keys contained in the secure key repository. + Specifies a comma-separated list of cryptographic adapters in CCA + coprocessor mode (APQN) which are associated with the secure AES key in the + repository. Each APQN association specifies a card and domain number separated +-by a period (like lszcrypt displays it). All specified APQNs must be online. ++by a period (like lszcrypt displays it). All specified APQNs must be online, ++unless option \fB\-\-no\-apqn\-check\fP is specified. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-no\-apqn\-check ++Do not check if the specified APQNs are available. Use this option to ++associate APQNs with a secure AES key that are currently not available. + This option is only used for secure keys contained in the secure key repository. + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP +@@ -833,7 +949,13 @@ To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP. + To set (replace) the APQN association do not specify a prefix. + You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or + remove (or set) the associations with one command. +-All APQNs being added or set (replaced) must be online. ++All APQNs being added or set (replaced) must be online, unless option ++\fB\-\-no\-apqn\-check\fP is specified. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-no\-apqn\-check ++Do not check if the specified APQNs are available. Use this option to ++associate APQNs with a secure AES key that are currently not available. + This option is only used for secure keys contained in the secure key repository. + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP +@@ -908,6 +1030,46 @@ This option is only available if + .B zkey + has been compiled with LUKS2 support enabled. + This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-key\-file\~\fIfile\-name\fP ++Reads the passphrase from the specified file. If this option is omitted, then ++you are prompted to enter the passphrase interactively during system startup. ++This option is passed to the generated crypttab entries for LUKS2 volumes, and ++is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-keyfile\-offset\~\fIbytes\fP ++Specifies the number of bytes to skip before starting to read in the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++from the beginning. When option \fB\-\-key\-file\fP is not specified, this ++option is ignored. This option is passed to the generated crypttab entries ++for LUKS2 volumes, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. Not all distributions support the ++.B keyfile-offset ++option in crypttab entries. ++.TP ++.BR \-\-keyfile\-size\~\fIbytes\fP ++Specifies the number of bytes to be read from the beginning of the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts ++at the offset. When option \fB\-\-key\-file\fP is not specified, this option is ++ignored. This option is passed to the generated crypttab entries for LUKS2 ++volumes, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. Not all distributions support the ++.B keyfile-size ++option in crypttab entries. ++.TP ++.BR \-\-tries\~\fInumber\fP ++Specifies how often the interactive input of the passphrase can be re-entered ++during system startup. The default is 3 times. When option \fB\-\-key\-file\fP ++is specified, this option is ignored, and the passphrase is read only once from ++the file. This option is passed to the generated crypttab entries for LUKS2 ++volumes, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. + . + . + . +@@ -937,6 +1099,64 @@ This option is only used for secure keys contained in the secure key repository. + Runs the generated cryptsetup commands. When one of the cryptsetup command fail, + no further cryptsetup commands are run, and zkey ends with an error. + This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-\-open ++Generates \fBcryptsetup luksOpen\fP or \fBcryptsetup plainOpen\fP commands. ++For a plain volume type, this is the default. This option can not be specified ++together with the ++.BR \-\-format ++option, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-format ++Generates \fBcryptsetup luksFormat\fP commands. For a LUKS2 volume type, this ++is the default. If specified for a plain volume type, then no command is ++generated. This option can not be specified together with the ++.BR \-\-open ++option, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-key\-file\~\fIfile\-name\fP ++Reads the passphrase from the specified file. If this option is omitted, ++or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the ++passphrase interactively. This option is passed to the generated command(s) ++for LUKS2 volumes, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-keyfile\-offset\~\fIbytes\fP ++Specifies the number of bytes to skip before starting to read in the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++from the beginning. When option \fB\-\-key\-file\fP is not specified, this ++option is ignored. This option is passed to the generated command(s) ++for LUKS2 volumes, and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-keyfile\-size\~\fIbytes\fP ++Specifies the number of bytes to be read from the beginning of the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts ++at the offset. When option \fB\-\-key\-file\fP is not specified, this option is ++ignored. This option is passed to the generated command(s) for LUKS2 volumes, ++and is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-\-tries\~\fInumber\fP ++Specifies how often the interactive input of the passphrase can be re-entered. ++The default is 3 times. When option \fB\-\-key\-file\fP is specified, this ++option is ignored, and the passphrase is read only once from the file. ++This option is passed to the generated command(s) for LUKS2 volumes, and is ++only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++.TP ++.BR \-q ", " \-\-batch\-mode ++Suppress cryptsetup confirmation questions. This option is passed to the generated ++cryptsetup command(s). + . + . + . +diff --git a/zkey/zkey.c b/zkey/zkey.c +index 3a8909a..2ecbb90 100644 +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -67,11 +67,19 @@ static struct zkey_globals { + char *description; + char *volumes; + char *apqns; ++ bool noapqncheck; + long int sector_size; + char *volume_type; + char *newname; + bool run; ++ bool batch_mode; ++ char *keyfile; ++ long long keyfile_offset; ++ long long keyfile_size; ++ long long tries; + bool force; ++ bool open; ++ bool format; + void *lib_csulcca; + t_CSNBKTC dll_CSNBKTC; + int pkey_fd; +@@ -102,6 +110,14 @@ static struct zkey_globals { + #define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY" + #define DEFAULT_KEYSTORE "/etc/zkey/repository" + ++#define OPT_CRYPTSETUP_KEYFILE 256 ++#define OPT_CRYPTSETUP_KEYFILE_OFFSET 257 ++#define OPT_CRYPTSETUP_KEYFILE_SIZE 258 ++#define OPT_CRYPTSETUP_TRIES 259 ++#define OPT_CRYPTSETUP_OPEN 260 ++#define OPT_CRYPTSETUP_FORMAT 261 ++#define OPT_NO_APQN_CHECK 262 ++ + /* + * Configuration of command line options + */ +@@ -172,6 +188,14 @@ static struct util_opt opt_vec[] = { + "repository", + .command = COMMAND_GENERATE, + }, ++ { ++ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, ++ .desc = "Do not check if the specified APQN(s) are available. " ++ "Use this option to associate APQN(s) with a secure " ++ "AES key that are currently not available.", ++ .command = COMMAND_GENERATE, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + { + .option = { "sector-size", required_argument, NULL, 'S'}, + .argument = "bytes", +@@ -281,6 +305,12 @@ static struct util_opt opt_vec[] = { + "associated with specific crypto cards", + .command = COMMAND_VALIDATE, + }, ++ { ++ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, ++ .desc = "Do not check if the associated APQN(s) are available", ++ .command = COMMAND_VALIDATE, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -316,6 +346,14 @@ static struct util_opt opt_vec[] = { + "repository", + .command = COMMAND_IMPORT, + }, ++ { ++ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, ++ .desc = "Do not check if the specified APQN(s) are available. " ++ "Use this option to associate APQN(s) with a secure " ++ "AES key that are currently not available.", ++ .command = COMMAND_IMPORT, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + { + .option = { "sector-size", required_argument, NULL, 'S'}, + .argument = "512|4096", +@@ -453,6 +491,14 @@ static struct util_opt opt_vec[] = { + "specify '-CARD.DOMAIN[,...]'", + .command = COMMAND_CHANGE, + }, ++ { ++ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, ++ .desc = "Do not check if the specified APQN(s) are available. " ++ "Use this option to associate APQN(s) with a secure " ++ "AES key that are currently not available.", ++ .command = COMMAND_CHANGE, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + { + .option = { "sector-size", required_argument, NULL, 'S'}, + .argument = "0|512|4096", +@@ -547,6 +593,53 @@ static struct util_opt opt_vec[] = { + "entry is to be generated", + .command = COMMAND_CRYPTTAB, + }, ++ { ++ .option = {"key-file", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE}, ++ .argument = "FILE-NAME", ++ .desc = "Read the passphrase from the specified file. " ++ "The specified file is passed to the generated " ++ "crypttab entry for LUKS2 volumes", ++ .command = COMMAND_CRYPTTAB, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"keyfile-offset", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE_OFFSET}, ++ .argument = "BYTES", ++ .desc = "Specifies the number of bytes to skip in the file " ++ "specified with option '--key-file'. " ++ "The specified offset is passed to the generated " ++ "crypttab entry for LUKS2 volumes. Not all " ++ "distributions support the 'keyfile-offset' option in " ++ "crypttab entries", ++ .command = COMMAND_CRYPTTAB, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"keyfile-size", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE_SIZE}, ++ .argument = "BYTES", ++ .desc = "Specifies the number of bytes to read from the file " ++ "specified with option '--key-file'. " ++ "The specified size is passed to the generated " ++ "crypttab entry for LUKS2 volumes. Not all " ++ "distributions support the 'keyfile-size' option in " ++ "crypttab entries", ++ .command = COMMAND_CRYPTTAB, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"tries", required_argument, NULL, ++ OPT_CRYPTSETUP_TRIES}, ++ .argument = "NUMBER", ++ .desc = "Specifies how often the interactive input of the " ++ "passphrase can be retried. " ++ "The specified number is passed to the generated " ++ "crypttab entry for LUKS2 volumes", ++ .command = COMMAND_CRYPTTAB, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, + #endif + /***********************************************************/ + { +@@ -582,6 +675,75 @@ static struct util_opt opt_vec[] = { + .desc = "Runs the generated cryptsetup command", + .command = COMMAND_CRYPTSETUP, + }, ++#ifdef HAVE_LUKS2_SUPPORT ++ { ++ .option = {"key-file", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE}, ++ .argument = "FILE-NAME", ++ .desc = "Read the passphrase from the specified file. " ++ "This option is passed to the generated command(s) for " ++ "LUKS2 volumes", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"keyfile-offset", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE_OFFSET}, ++ .argument = "BYTES", ++ .desc = "Specifies the number of bytes to skip in the file " ++ "specified with option '--key-file'. " ++ "This option is passed to the generated command(s) for " ++ "LUKS2 volumes", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"keyfile-size", required_argument, NULL, ++ OPT_CRYPTSETUP_KEYFILE_SIZE}, ++ .argument = "BYTES", ++ .desc = "Specifies the number of bytes to read from the file " ++ "specified with option '--key-file'. " ++ "This option is passed to the generated command(s) for " ++ "LUKS2 volumes", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"tries", required_argument, NULL, ++ OPT_CRYPTSETUP_TRIES}, ++ .argument = "NUMBER", ++ .desc = "Specifies how often the interactive input of the " ++ "passphrase can be retried. " ++ "This option is passed to the generated command(s) for " ++ "LUKS2 volumes", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++#endif ++ { ++ .option = {"batch-mode", 0, NULL, 'q'}, ++ .desc = "Suppresses cryptsetup confirmation questions. " ++ "This option is passed to the generated cryptsetup " ++ "command(s)", ++ .command = COMMAND_CRYPTSETUP, ++ }, ++#ifdef HAVE_LUKS2_SUPPORT ++ { ++ .option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN}, ++ .desc = "Generates luksOpen or plainOpen commands. For the " ++ "plain volume type, this is the default", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"format", 0, NULL, OPT_CRYPTSETUP_FORMAT}, ++ .desc = "Generates luksFormat commands. For the LUKS2 volume " ++ "type, this is the default. If specified for the " ++ "plain volume type, then no command is generated", ++ .command = COMMAND_CRYPTSETUP, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++#endif + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -883,8 +1045,9 @@ static int command_generate_repository(void) + g.sector_size = 0; + + rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, +- g.apqns, g.sector_size, g.keybits, g.xts, +- g.clearkeyfile, g.volume_type, g.pkey_fd); ++ g.apqns, g.noapqncheck, g.sector_size, ++ g.keybits, g.xts, g.clearkeyfile, ++ g.volume_type, g.pkey_fd); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -903,6 +1066,12 @@ static int command_generate(void) + util_prg_print_parse_error(); + return EXIT_FAILURE; + } ++ if (g.apqns == NULL && g.noapqncheck) { ++ warnx("Option '--noapqncheck' is only valid together with " ++ "the '--apqns|-a' option"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } + if (g.name != NULL) + return command_generate_repository(); + if (g.pos_arg != NULL) { +@@ -918,6 +1087,12 @@ static int command_generate(void) + util_prg_print_parse_error(); + return EXIT_FAILURE; + } ++ if (g.noapqncheck) { ++ warnx("Option '--noapqncheck' is not valid for " ++ "generating a key outside of the repository"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } + if (g.description != NULL) { + warnx("Option '--description|-d' is not valid for " + "generating a key outside of the repository"); +@@ -1141,6 +1316,12 @@ static int command_validate_file(void) + util_prg_print_parse_error(); + return EXIT_FAILURE; + } ++ if (g.noapqncheck) { ++ warnx("Option '--noapqncheck' is not valid for " ++ "validating a key outside of the repository"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } + + /* Read the secure key to be re-enciphered */ + secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); +@@ -1194,7 +1375,15 @@ static int command_validate_repository(void) + { + int rc; + +- rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.pkey_fd); ++ if (g.apqns == NULL && g.noapqncheck) { ++ warnx("Option '--noapqncheck' is only valid together with " ++ "the '--apqns|-a' option"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ ++ rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck, ++ g.pkey_fd); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1231,9 +1420,16 @@ static int command_import(void) + if (g.sector_size < 0) + g.sector_size = 0; + ++ if (g.apqns == NULL && g.noapqncheck) { ++ warnx("Option '--noapqncheck' is only valid together with " ++ "the '--apqns|-a' option"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, +- g.apqns, g.sector_size, g.pos_arg, +- g.volume_type); ++ g.apqns, g.noapqncheck, g.sector_size, ++ g.pos_arg, g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1304,9 +1500,16 @@ static int command_change(void) + misc_print_required_parm("--name/-N"); + return EXIT_FAILURE; + } ++ if (g.apqns == NULL && g.noapqncheck) { ++ warnx("Option '--noapqncheck' is only valid together with " ++ "the '--apqns|-a' option"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } + + rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, +- g.apqns, g.sector_size, g.volume_type); ++ g.apqns, g.noapqncheck, g.sector_size, ++ g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1366,7 +1569,8 @@ static int command_crypttab(void) + { + int rc; + +- rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type); ++ rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type, g.keyfile, ++ g.keyfile_offset, g.keyfile_size, g.tries); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1380,7 +1584,16 @@ static int command_cryptsetup(void) + { + int rc; + +- rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type); ++ if (g.open && g.format) { ++ warnx("Either '--open' or '--format' can be specified, but " ++ "not both"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ ++ rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type, ++ g.keyfile, g.keyfile_offset, g.keyfile_size, ++ g.tries, g.batch_mode, g.open, g.format); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1546,6 +1759,9 @@ int main(int argc, char *argv[]) + case 'a': + g.apqns = optarg; + break; ++ case OPT_NO_APQN_CHECK: ++ g.noapqncheck = 1; ++ break; + case 'S': + g.sector_size = strtol(optarg, &endp, 0); + if (*optarg == '\0' || *endp != '\0' || +@@ -1574,6 +1790,56 @@ int main(int argc, char *argv[]) + case 'V': + g.verbose = 1; + break; ++#ifdef HAVE_LUKS2_SUPPORT ++ case OPT_CRYPTSETUP_KEYFILE: ++ g.keyfile = optarg; ++ break; ++ case OPT_CRYPTSETUP_KEYFILE_OFFSET: ++ g.keyfile_offset = strtoll(optarg, &endp, 0); ++ if (*optarg == '\0' || *endp != '\0' || ++ g.keyfile_offset < 0 || ++ (g.keyfile_offset == LLONG_MAX && ++ errno == ERANGE)) { ++ warnx("Invalid value for '--keyfile-offset': " ++ "'%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case OPT_CRYPTSETUP_KEYFILE_SIZE: ++ g.keyfile_size = strtoll(optarg, &endp, 0); ++ if (*optarg == '\0' || *endp != '\0' || ++ g.keyfile_size <= 0 || ++ (g.keyfile_size == LLONG_MAX && errno == ERANGE)) { ++ warnx("Invalid value for '--keyfile-size': " ++ "'%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case OPT_CRYPTSETUP_TRIES: ++ g.tries = strtoll(optarg, &endp, 0); ++ if (*optarg == '\0' || *endp != '\0' || ++ g.tries <= 0 || ++ (g.tries == LLONG_MAX && errno == ERANGE)) { ++ warnx("Invalid value for '--tries': '%s'", ++ optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++#endif ++ case 'q': ++ g.batch_mode = 1; ++ break; ++#ifdef HAVE_LUKS2_SUPPORT ++ case OPT_CRYPTSETUP_OPEN: ++ g.open = 1; ++ break; ++ case OPT_CRYPTSETUP_FORMAT: ++ g.format = 1; ++ break; ++#endif + case 'h': + print_help(command); + return EXIT_SUCCESS; +-- +2.21.3 + + +From 86c3031204f6e52455bba1dfc89d619ce26debe9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 13:16:36 +0100 +Subject: [PATCH 34/44] zkey: check master key consistency (#1753153) + +Description: Enhances the zkey tool to perform a cross check whether the + APQNs associated with a secure key have the same master key. + Display the master key verification pattern of a secure key + during the zkey validate command. This helps to better identify + which master key is the correct one, in case of master key + inconsistencies. + Select an appropriate APQN when re-enciphering a secure key. + Re-enciphering is done using the CCA host library. Special + handling is required to select an appropriate APQN for use with + the CCA host library. + +Upstream-ID: a6507b330d05a3033837768a54ce9959c2a84870 +Upstream-ID: 8d1a7fecd04e620138a3828d58900d35a396c33a +Upstream-ID: 7e6d782699adf681eaaf63fd43d65e6c0627ee6e +Upstream-ID: 819513ab385fa37306cff3f9ac32e3be7f225793 +Upstream-ID: 5dbe73403f455cb880aa256598b9527a465beb39 +Upstream-ID: 99ab2db6ad3f79c4e3a7287f4c724d1c635ebd2f +Upstream-ID: 7d4c8c27813c792237f92db5038b9dc4bea2fa7c +Upstream-ID: 94227b44d5e5538b221b7f33ae6e627b86d07593 +Upstream-ID: 139dff3525e7140a1d2e6aefdc1d35930d05c6d2 +Upstream-ID: 95c7258ea783c5bd6aa12fc0e3d5fbe65647af03 +Upstream-ID: 696e8458f0c117e3a084e1a083de89ec19baaff9 +Upstream-ID: a84d1c5d58fa4a0c9e087357eec009803ea06ef2 +Upstream-ID: bf8872e94a2dc4810df388d1539560b00b1acf6e +Upstream-ID: 625b81130ab2c9d184aaede2749f1fd776f51062 +Upstream-ID: bfc3dd018c4f0cc17f8463d8bd6be16aab8de4a4 +Upstream-ID: b32c0314746ddee69e59f892f105acd720d06452 +Upstream-ID: ea7cc9ea606dd879e4cdfae06a6f13d8fa3afff4 +Upstream-ID: c2244a57950f4eb35e3209151dcf48de66828df1 +Upstream-ID: a5b58038a0dbf1c3eb202a6933265f0d2e57e130 +Upstream-ID: 7f8e31e8619b32297b432a4882d78af79de37a58 +Upstream-ID: d854aed4b8154e7420def8749db2106a049dd80a +Upstream-ID: 0b4cbf00412f27456d28ff7f86ec5335a39e3416 +Upstream-ID: 016a0a56fcb3dd0bf8bed693e5d64873f6288995 +Upstream-ID: 1091b0bf65328aff94055a2e333aff2c737b6744 +Upstream-ID: 552a915465301b768268cddc7ccb65a6d167e432 +Upstream-ID: a0ed6709cf3c62b1fc9dfa28358e70215c1da55a +--- + zkey/Makefile | 12 +- + zkey/cca.c | 628 +++++++++++++++++++++++++++++++++++ + zkey/cca.h | 95 ++++++ + zkey/keystore.c | 306 +++++++++-------- + zkey/keystore.h | 3 +- + zkey/pkey.c | 165 ++-------- + zkey/pkey.h | 21 +- + zkey/properties.c | 28 +- + zkey/properties.h | 3 + + zkey/utils.c | 725 +++++++++++++++++++++++++++++++++++++++++ + zkey/utils.h | 54 +++ + zkey/zkey-cryptsetup.1 | 49 ++- + zkey/zkey-cryptsetup.c | 198 +++++++++-- + zkey/zkey.c | 122 +++++-- + 14 files changed, 2034 insertions(+), 375 deletions(-) + create mode 100644 zkey/cca.c + create mode 100644 zkey/cca.h + create mode 100644 zkey/utils.c + create mode 100644 zkey/utils.h + +diff --git a/zkey/Makefile b/zkey/Makefile +index a44b14b..f92de4f 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -64,18 +64,20 @@ zkey-cryptsetup-skip-jsonc: + + all: $(BUILD_TARGETS) + +-zkey.o: zkey.c pkey.h misc.h ++zkey.o: zkey.c pkey.h cca.h misc.h + pkey.o: pkey.c pkey.h ++cca.o: cca.c cca.h pkey.h utils.h ++utils.o: utils.h + properties.o: check-dep-zkey properties.c properties.h +-keystore.o: keystore.c keystore.h properties.h +-zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h ++keystore.o: keystore.c keystore.h properties.h pkey.h cca.h utils.h ++zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h misc.h + + zkey: LDLIBS = -ldl -lcrypto +-zkey: zkey.o pkey.o properties.o keystore.o $(libs) ++zkey: zkey.o pkey.o cca.o properties.o keystore.o utils.o $(libs) + $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c +-zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) ++zkey-cryptsetup: zkey-cryptsetup.o pkey.o cca.o utils.o $(libs) + $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + + install-common: +diff --git a/zkey/cca.c b/zkey/cca.c +new file mode 100644 +index 0000000..2669c75 +--- /dev/null ++++ b/zkey/cca.c +@@ -0,0 +1,628 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/util_base.h" ++#include "lib/util_libc.h" ++#include "lib/util_panic.h" ++ ++#include "cca.h" ++#include "pkey.h" ++#include "utils.h" ++ ++#define pr_verbose(verbose, fmt...) do { \ ++ if (verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++/* ++ * Definitions for the CCA library ++ */ ++#define CCA_LIBRARY_NAME "libcsulcca.so" ++#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" ++#define CCA_DOMAIN_ENVAR "CSU_DEFAULT_DOMAIN" ++#define CCA_ADAPTER_ENVAR "CSU_DEFAULT_ADAPTER" ++ ++/** ++ * Prints CCA return and reason code information for certain known CCA ++ * error situations. ++ * ++ * @param return_code the CCA return code ++ * @param reason_code the CCA reason code ++ */ ++static void print_CCA_error(int return_code, int reason_code) ++{ ++ switch (return_code) { ++ case 8: ++ switch (reason_code) { ++ case 48: ++ warnx("The secure key has a CCA master key " ++ "verification pattern that is not valid"); ++ break; ++ } ++ break; ++ case 12: ++ switch (reason_code) { ++ case 764: ++ warnx("The CCA master key is not loaded and " ++ "therefore a secure key cannot be enciphered"); ++ break; ++ } ++ break; ++ } ++} ++ ++/** ++ * Returns the version, release and modification number of the used CCA library. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int get_cca_version(struct cca_lib *cca, bool verbose) ++{ ++ unsigned char exit_data[4] = { 0, }; ++ unsigned char version_data[20]; ++ long return_code, reason_code; ++ long version_data_length; ++ long exit_data_len = 0; ++ char date[20]; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ memset(version_data, 0, sizeof(version_data)); ++ version_data_length = sizeof(version_data); ++ cca->dll_CSUACFV(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &version_data_length, version_data); ++ pr_verbose(verbose, "CSUACFV (Cryptographic Facility Version) " ++ "returned: return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ version_data[sizeof(version_data) - 1] = '\0'; ++ pr_verbose(verbose, "CCA Version string: %s", version_data); ++ ++ if (sscanf((char *)version_data, "%u.%u.%uz%s", &cca->version.ver, ++ &cca->version.rel, &cca->version.mod, date) != 4) { ++ warnx("CCA library version is invalid: %s", version_data); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Loads the CCA library and provides the entry point of the CSNBKTC function. ++ * ++ * @param[out] cca on return this contains the address of the CCA ++ * library and certain CCA symbols. dlclose() should ++ * be used to free the library when no longer needed. ++ * @param verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, -ELIBACC in case of library load errors ++ */ ++int load_cca_library(struct cca_lib *cca, bool verbose) ++{ ++ util_assert(cca != NULL, "Internal error: caa is NULL"); ++ ++ /* Load the CCA library */ ++ cca->lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); ++ if (cca->lib_csulcca == NULL) { ++ pr_verbose(verbose, "%s", dlerror()); ++ warnx("The command requires the IBM CCA Host Libraries and " ++ "Tools.\nFor the supported environments and downloads, " ++ "see:\n%s", CCA_WEB_PAGE); ++ return -ELIBACC; ++ } ++ ++ /* Get the Cryptographic Facility Version function */ ++ cca->dll_CSUACFV = (t_CSUACFV)dlsym(cca->lib_csulcca, "CSUACFV"); ++ ++ /* Get the Key Token Change function */ ++ cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC"); ++ ++ /* Get the Cryptographic Facility Query function */ ++ cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ"); ++ ++ /* Get the Cryptographic Resource Allocate function */ ++ cca->dll_CSUACRA = (t_CSUACRA)dlsym(cca->lib_csulcca, "CSUACRA"); ++ ++ /* Cryptographic Resource Deallocate function */ ++ cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD"); ++ ++ if (cca->dll_CSUACFV == NULL || ++ cca->dll_CSNBKTC == NULL || ++ cca->dll_CSUACFQ == NULL || ++ cca->dll_CSUACRA == NULL || ++ cca->dll_CSUACRD == NULL) { ++ pr_verbose(verbose, "%s", dlerror()); ++ warnx("The command requires the IBM CCA Host Libraries and " ++ "Tools.\nFor the supported environments and downloads, " ++ "see:\n%s", CCA_WEB_PAGE); ++ dlclose(cca->lib_csulcca); ++ cca->lib_csulcca = NULL; ++ return -ELIBACC; ++ } ++ ++ pr_verbose(verbose, "CCA library '%s' has been loaded successfully", ++ CCA_LIBRARY_NAME); ++ ++ return get_cca_version(cca, verbose); ++} ++ ++/** ++ * Re-enciphers a secure key. ++ * ++ * @param[in] cca the CCA libraray structure ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the size of the secure key ++ * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT ++ * or METHOD_CURRENT_TO_NEW. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, -EIO in case of an error ++ */ ++int key_token_change(struct cca_lib *cca, ++ u8 *secure_key, unsigned int secure_key_size, ++ char *method, bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count; ++ unsigned char rule_array[2 * 8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(secure_key_size > 0, ++ "Internal error: secure_key_size is 0"); ++ util_assert(method != NULL, "Internal error: method is NULL"); ++ ++ memcpy(rule_array, method, 8); ++ memcpy(rule_array + 8, "AES ", 8); ++ rule_array_count = 2; ++ ++ cca->dll_CSNBKTC(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ secure_key); ++ ++ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " ++ "return_code: %ld, reason_code: %ld", method, return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ if (secure_key_size == 2 * SECURE_KEY_SIZE) { ++ cca->dll_CSNBKTC(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ secure_key + SECURE_KEY_SIZE); ++ ++ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " ++ "returned: return_code: %ld, reason_code: %ld", ++ method, return_code, reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * Queries the number of adapters known by the CCA host library ++ * ++ * @param[in] cca the CCA library structure ++ * @param[out] adapters the number of adapters ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. ++ */ ++static int get_number_of_cca_adapters(struct cca_lib *cca, ++ unsigned int *adapters, bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count, verb_data_length = 0; ++ unsigned char rule_array[16 * 8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ util_assert(adapters != NULL, "Internal error: adapters is NULL"); ++ ++ memset(rule_array, 0, sizeof(rule_array)); ++ memcpy(rule_array, "STATCRD2", 8); ++ rule_array_count = 1; ++ ++ cca->dll_CSUACFQ(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &verb_data_length, NULL); ++ ++ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " ++ "return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ rule_array[8] = '\0'; ++ if (sscanf((char *)rule_array, "%u", adapters) != 1) { ++ pr_verbose(verbose, "Unparsable output: %s", rule_array); ++ return -EIO; ++ } ++ ++ pr_verbose(verbose, "Number of CCA adapters: %u", *adapters); ++ return 0; ++} ++ ++/** ++ * Allocate a specific CCA adapter. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] adapter the adapter number, starting at 1. If 0 is ++ * specified, then the AUTOSELECT option is ++ * enabled. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. -ENODEV is ++ * returned if the adapter is not available. ++ */ ++static int allocate_cca_adapter(struct cca_lib *cca, unsigned int adapter, ++ bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count; ++ unsigned char rule_array[8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ char res_name[9]; ++ long res_name_len; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ if (adapter > 0) ++ memcpy(rule_array, "DEVICE ", 8); ++ else ++ memcpy(rule_array, "DEV-ANY ", 8); ++ rule_array_count = 1; ++ ++ sprintf(res_name, "CRP%02d", adapter); ++ res_name_len = strlen(res_name); ++ ++ cca->dll_CSUACRA(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &res_name_len, (unsigned char *)res_name); ++ ++ pr_verbose(verbose, "CSUACRA (Cryptographic Resource Allocate) " ++ "returned: return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -ENODEV; ++ } ++ ++ pr_verbose(verbose, "Adapter %u (%s) allocated", adapter, res_name); ++ return 0; ++} ++ ++/** ++ * Deallocate a specific CCA adapter. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] adapter the adapter number, starting at 1. If 0 is ++ * specified, then the AUTOSELECT option is ++ * disabled. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. -ENODEV is ++ * returned if the adapter is not available. ++ */ ++static int deallocate_cca_adapter(struct cca_lib *cca, unsigned int adapter, ++ bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count; ++ unsigned char rule_array[8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ char res_name[9]; ++ long res_name_len; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ if (adapter > 0) ++ memcpy(rule_array, "DEVICE ", 8); ++ else ++ memcpy(rule_array, "DEV-ANY ", 8); ++ rule_array_count = 1; ++ ++ sprintf(res_name, "CRP%02d", adapter); ++ res_name_len = strlen(res_name); ++ ++ cca->dll_CSUACRD(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &res_name_len, (unsigned char *)res_name); ++ ++ pr_verbose(verbose, "CSUACRD (Cryptographic Resource Deallocate) " ++ "returned: return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -ENODEV; ++ } ++ ++ pr_verbose(verbose, "Adapter %u (%s) deallocated", adapter, res_name); ++ return 0; ++} ++ ++/** ++ * Queries the serial number of the current CCA adapter ++ * ++ * @param[in] cca the CCA library structure ++ * @param[out] serialnr the buffer where the serial number is returned ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. ++ */ ++static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9], ++ bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count, verb_data_length = 0; ++ unsigned char rule_array[16 * 8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ memset(rule_array, 0, sizeof(rule_array)); ++ memcpy(rule_array, "STATCRD2", 8); ++ rule_array_count = 1; ++ ++ cca->dll_CSUACFQ(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &verb_data_length, NULL); ++ ++ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " ++ "return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ memcpy(serialnr, rule_array+14*8, 8); ++ serialnr[8] = '\0'; ++ ++ pr_verbose(verbose, "Serial number of CCA adapter: %s", serialnr); ++ return 0; ++} ++ ++/** ++ * Selects the specified APQN to be used for the CCA host library. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] card the card number ++ * @param[in] domain the domain number ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is ++ * returned when the serialnr sysfs attribute is not available, ++ * because the zcrypt kernel module is on an older level. -ENODEV is ++ * returned if the APQN is not available. ++ */ ++int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose) ++{ ++ unsigned int adapters, adapter; ++ char adapter_serialnr[9]; ++ char apqn_serialnr[9]; ++ char temp[10]; ++ int rc, found = 0; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ pr_verbose(verbose, "Select %02x.%04x for the CCA host library", card, ++ domain); ++ ++ rc = sysfs_get_serialnr(card, apqn_serialnr, verbose); ++ if (rc != 0) { ++ pr_verbose(verbose, "Failed to get the serial number: %s", ++ strerror(-rc)); ++ return rc; ++ } ++ ++ sprintf(temp, "%u", domain); ++ if (setenv(CCA_DOMAIN_ENVAR, temp, 1) != 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to set the %s environment variable:" ++ " %s", CCA_DOMAIN_ENVAR, strerror(-rc)); ++ return rc; ++ } ++ unsetenv(CCA_ADAPTER_ENVAR); ++ ++ /* ++ * Unload and reload the CCA host library so that it recognizes the ++ * changed CSU_DEFAULT_DOMAIN environment variable value. ++ */ ++ if (cca->lib_csulcca != NULL) ++ dlclose(cca->lib_csulcca); ++ memset(cca, 0, sizeof(struct cca_lib)); ++ ++ rc = load_cca_library(cca, verbose); ++ if (rc != 0) ++ return rc; ++ ++ rc = get_number_of_cca_adapters(cca, &adapters, verbose); ++ if (rc != 0) ++ return rc; ++ ++ /* Disable the AUTOSELECT option */ ++ rc = deallocate_cca_adapter(cca, 0, verbose); ++ if (rc != 0) ++ return rc; ++ ++ for (adapter = 1; adapter <= adapters; adapter++) { ++ rc = allocate_cca_adapter(cca, adapter, verbose); ++ if (rc != 0) ++ return rc; ++ ++ rc = get_cca_adapter_serialnr(cca, adapter_serialnr, verbose); ++ if (rc == 0) { ++ if (memcmp(apqn_serialnr, adapter_serialnr, 8) == 0) { ++ found = 1; ++ break; ++ } ++ } ++ ++ rc = deallocate_cca_adapter(cca, adapter, verbose); ++ if (rc != 0) ++ return rc; ++ } ++ ++ if (!found) ++ return -ENODEV; ++ ++ pr_verbose(verbose, "Selected adapter %u (CRP%02d)", adapter, adapter); ++ return 0; ++} ++ ++struct find_mkvp_info { ++ u64 mkvp; ++ unsigned int flags; ++ bool found; ++ int card; ++ int domain; ++ bool verbose; ++}; ++ ++static int find_mkvp(int card, int domain, void *handler_data) ++{ ++ struct find_mkvp_info *info = (struct find_mkvp_info *)handler_data; ++ struct mk_info mk_info; ++ bool found = false; ++ int rc; ++ ++ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); ++ if (rc == -ENODEV) ++ return 0; ++ if (rc != 0) ++ return rc; ++ ++ if (info->flags & FLAG_SEL_CCA_MATCH_CUR_MKVP) ++ if (mk_info.cur_mk.mk_state == MK_STATE_VALID && ++ mk_info.cur_mk.mkvp == info->mkvp) ++ found = true; ++ ++ if (info->flags & FLAG_SEL_CCA_MATCH_OLD_MKVP) ++ if (mk_info.old_mk.mk_state == MK_STATE_VALID && ++ mk_info.old_mk.mkvp == info->mkvp) ++ found = true; ++ ++ if (info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET) ++ if (mk_info.new_mk.mk_state != MK_STATE_FULL) ++ found = false; ++ ++ ++ if (found) { ++ info->card = card; ++ info->domain = domain; ++ info->found = true; ++ ++ pr_verbose(info->verbose, "%02x.%04x has the desired mkvp%s", ++ card, domain, ++ info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET ? ++ " and NEW MK set" : ""); ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Selects an APQN to be used for the CCA host library that has the specified ++ * master key verification pattern ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] mkvp the master key verification pattern to search for ++ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, ++ * or an empty string, then all online CCA APQNs are ++ * checked. ++ * @param[in] flags Flags that control the MKVM matching and NEW register ++ * checking. Multiple flags can be combined. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is ++ * returned when the serialnr sysfs attribute is not available, ++ * because the zcrypt kernel module is on an older level. -ENODEV is ++ * returned if no APQN is available with the desired mkvp. ++ */ ++int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, ++ unsigned int flags, bool verbose) ++{ ++ struct find_mkvp_info info; ++ int rc; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ pr_verbose(verbose, "Select mkvp %016llx in APQNs %s for the CCA host " ++ "library", mkvp, apqns == 0 ? "ANY" : apqns); ++ ++ info.mkvp = mkvp; ++ info.flags = flags; ++ info.found = false; ++ info.card = 0; ++ info.domain = 0; ++ info.verbose = verbose; ++ ++ rc = handle_apqns(apqns, find_mkvp, &info, verbose); ++ if (rc < 0) ++ return rc; ++ ++ if (!info.found) ++ return -ENODEV; ++ ++ rc = select_cca_adapter(cca, info.card, info.domain, verbose); ++ return rc; ++} ++ ++void print_msg_for_cca_envvars(const char *key_name) ++{ ++ char *msg; ++ ++ util_asprintf(&msg, "WARNING: You must set environment variables " ++ "%s and %s to the desired card and domain that is " ++ "set up with the AES master key used by this %s. " ++ "%s specifies the domain as decimal number. %s " ++ "specifies the adapter number as 'CRPnn', where " ++ "'nn' is the adapter number. See the CCA " ++ "documentation for more details.\n", ++ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR, key_name, ++ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR); ++ util_print_indented(msg, 0); ++ free(msg); ++} +diff --git a/zkey/cca.h b/zkey/cca.h +new file mode 100644 +index 0000000..a79e2ee +--- /dev/null ++++ b/zkey/cca.h +@@ -0,0 +1,95 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * This header file defines the interface to the CCA host library. ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 CCA_H ++#define CCA_H ++ ++#include "lib/zt_common.h" ++ ++#define METHOD_OLD_TO_CURRENT "RTCMK " ++#define METHOD_CURRENT_TO_NEW "RTNMK " ++ ++typedef void (*t_CSNBKTC)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ unsigned char *key_identifier); ++ ++typedef void (*t_CSUACFV)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *version_data_length, ++ unsigned char *version_data); ++ ++typedef void (*t_CSUACFQ)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *verb_data_length, ++ unsigned char *verb_data); ++ ++typedef void (*t_CSUACRA)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *ressource_name_length, ++ unsigned char *ressource_name); ++ ++typedef void (*t_CSUACRD)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *ressource_name_length, ++ unsigned char *ressource_name); ++ ++struct cca_version { ++ unsigned int ver; ++ unsigned int rel; ++ unsigned int mod; ++}; ++ ++struct cca_lib { ++ void *lib_csulcca; ++ t_CSNBKTC dll_CSNBKTC; ++ t_CSUACFV dll_CSUACFV; ++ t_CSUACFQ dll_CSUACFQ; ++ t_CSUACRA dll_CSUACRA; ++ t_CSUACRD dll_CSUACRD; ++ struct cca_version version; ++}; ++ ++int load_cca_library(struct cca_lib *cca, bool verbose); ++ ++int key_token_change(struct cca_lib *cca, ++ u8 *secure_key, unsigned int secure_key_size, ++ char *method, bool verbose); ++ ++int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose); ++ ++#define FLAG_SEL_CCA_MATCH_CUR_MKVP 0x01 ++#define FLAG_SEL_CCA_MATCH_OLD_MKVP 0x02 ++#define FLAG_SEL_CCA_NEW_MUST_BE_SET 0x80 ++ ++int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, ++ unsigned int flags, bool verbose); ++ ++void print_msg_for_cca_envvars(const char *key_name); ++ ++#endif +diff --git a/zkey/keystore.c b/zkey/keystore.c +index 33c8f93..957ebb0 100644 +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -3,7 +3,7 @@ + * + * Keystore handling functions + * +- * Copyright IBM Corp. 2018 ++ * Copyright IBM Corp. 2018, 2019 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. +@@ -25,7 +25,6 @@ + #include + + #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" +@@ -33,7 +32,9 @@ + + #include "keystore.h" + #include "pkey.h" ++#include "cca.h" + #include "properties.h" ++#include "utils.h" + + struct key_filenames { + char *skey_filename; +@@ -133,14 +134,7 @@ static int _keystore_get_key_filenames(struct keystore *keystore, + */ + static int _keystore_reencipher_key_exists(struct key_filenames *file_names) + { +- struct stat sb; +- int rc; +- +- rc = stat(file_names->renc_filename, &sb); +- if (rc == 0 && !S_ISREG(sb.st_mode)) +- rc = 1; +- +- return !rc; ++ return util_path_is_reg_file("%s", file_names->renc_filename); + } + + /** +@@ -153,20 +147,14 @@ static int _keystore_reencipher_key_exists(struct key_filenames *file_names) + */ + static int _keystore_exists_keyfiles(struct key_filenames *file_names) + { +- struct stat sb_skey, sb_info; +- int rc_skey, rc_info; +- +- rc_skey = stat(file_names->skey_filename, &sb_skey); +- if (rc_skey == 0 && !S_ISREG(sb_skey.st_mode)) +- rc_skey = 1; ++ bool rc_skey, rc_info; + +- rc_info = stat(file_names->info_filename, &sb_info); +- if (rc_info == 0 && !S_ISREG(sb_info.st_mode)) +- rc_info = 1; ++ rc_skey = util_path_is_reg_file("%s", file_names->skey_filename); ++ rc_info = util_path_is_reg_file("%s", file_names->info_filename); + +- if (rc_skey == 0 && rc_info == 0) ++ if (rc_skey && rc_info) + return 1; +- if (rc_skey != 0 && rc_info != 0 && ++ if (!rc_skey && !rc_info && + _keystore_reencipher_key_exists(file_names) == 0) + return 0; + return -1; +@@ -1022,69 +1010,6 @@ free: + return rc; + } + +-/** +- * Checks if the specified APQN is of type CCA and is online +- * +- * @param[in] card card number +- * @param[in] domain the domain +- * +- * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its +- * not a CCA card. +- */ +-static int _keystore_is_apqn_online(int card, int domain) +-{ +- long int online; +- char *dev_path; +- char type[20]; +- int rc = 1; +- +- dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); +- if (!util_path_is_dir(dev_path)) { +- rc = 0; +- goto out; +- } +- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { +- rc = 0; +- goto out; +- } +- if (online == 0) { +- rc = 0; +- goto out; +- } +- if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { +- rc = 0; +- goto out; +- } +- if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { +- rc = 0; +- goto out; +- } +- if (type[4] != 'C') { +- rc = -1; +- goto out; +- } +- free(dev_path); +- +- dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card, +- card, domain); +- if (!util_path_is_dir(dev_path)) { +- rc = 0; +- goto out; +- } +- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { +- rc = 0; +- goto out; +- } +- if (online == 0) { +- rc = 0; +- goto out; +- } +- +-out: +- free(dev_path); +- return rc; +-} +- + struct apqn_check { + bool noonlinecheck; + bool nomsg; +@@ -1136,7 +1061,7 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), + goto out; + } + +- rc = _keystore_is_apqn_online(card, domain); ++ rc = sysfs_is_apqn_online(card, domain); + if (rc != 1) { + if (info->nomsg == 0) + warnx("The APQN %02x.%04x is %s", card, domain, +@@ -1378,7 +1303,7 @@ struct keystore *keystore_new(const char *directory, bool verbose) + warnx("Can not access '%s': %s", directory, strerror(errno)); + return NULL; + } +- if (!(sb.st_mode & S_IFDIR)) { ++ if (!S_ISDIR(sb.st_mode)) { + warnx("'%s' is not a directory", directory); + return NULL; + } +@@ -1630,7 +1555,8 @@ static int _keystore_create_info_file(struct keystore *keystore, + rc = -EINVAL; + goto out; + } +- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, volume_type); ++ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, volume_type, ++ true); + if (rc != 0) { + warnx("Invalid characters in volume-type"); + goto out; +@@ -1759,6 +1685,14 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + if (rc != 0) + goto out_free_key_filenames; + ++ rc = cross_check_apqns(apqns, 0, true, keystore->verbose); ++ if (rc == -EINVAL) ++ goto out_free_key_filenames; ++ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { ++ warnx("Your master key setup is improper"); ++ goto out_free_key_filenames; ++ } ++ + rc = _keystore_get_card_domain(apqns, &card, &domain); + if (rc != 0) + goto out_free_key_filenames; +@@ -1836,6 +1770,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, + struct properties *key_props = NULL; + size_t secure_key_size; + u8 *secure_key; ++ u64 mkvp; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); +@@ -1857,9 +1792,26 @@ int keystore_import_key(struct keystore *keystore, const char *name, + goto out_free_key_filenames; + } + ++ 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; ++ } ++ ++ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); ++ if (rc == -EINVAL) ++ goto out_free_key; ++ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { ++ warnx("Your master key setup is improper"); ++ goto out_free_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; + +@@ -1877,6 +1829,9 @@ 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); +@@ -1931,7 +1886,11 @@ int keystore_change_key(struct keystore *keystore, const char *name, + .nomsg = 0 }; + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; ++ size_t secure_key_size; ++ char *apqns_prop; ++ u8 *secure_key; + char temp[30]; ++ u64 mkvp; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); +@@ -1977,6 +1936,33 @@ int keystore_change_key(struct keystore *keystore, const char *name, + &apqn_check); + if (rc != 0) + goto out; ++ ++ secure_key = read_secure_key(file_names.skey_filename, ++ &secure_key_size, ++ keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ rc = get_master_key_verification_pattern(secure_key, ++ secure_key_size, ++ &mkvp, ++ keystore->verbose); ++ free(secure_key); ++ if (rc) ++ goto out; ++ ++ apqns_prop = properties_get(key_props, PROP_NAME_APQNS); ++ rc = cross_check_apqns(apqns_prop, mkvp, true, ++ keystore->verbose); ++ free(apqns_prop); ++ if (rc == -ENOTSUP) ++ rc = 0; ++ if (rc != 0 && noapqncheck == 0) { ++ warnx("Your master key setup is improper"); ++ goto out; ++ } + } + + if (sector_size >= 0) { +@@ -2002,8 +1988,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, + goto out; + } + +- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, +- volume_type); ++ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, ++ volume_type, true); + if (rc != 0) { + warnx("Invalid characters in volume-type"); + goto out; +@@ -2181,7 +2167,7 @@ static void _keystore_print_record(struct util_rec *rec, + bool validation, const char *skey_filename, + size_t secure_key_size, + size_t clear_key_bitsize, bool valid, +- bool is_old_mk, bool reenc_pending) ++ bool is_old_mk, bool reenc_pending, u64 mkvp) + { + char temp_vp[VERIFICATION_PATTERN_LEN + 2]; + char *volumes_argz = NULL; +@@ -2243,10 +2229,11 @@ static void _keystore_print_record(struct util_rec *rec, + if (validation) { + if (valid) + util_rec_set(rec, REC_MASTERKEY, +- is_old_mk ? "OLD CCA master key" : +- "CURRENT CCA master key"); ++ "%s CCA master key (MKVP: %016llx)", ++ is_old_mk ? "OLD" : "CURRENT", mkvp); + else +- util_rec_set(rec, REC_MASTERKEY, "(unknown)"); ++ util_rec_set(rec, REC_MASTERKEY, ++ "(unknown, MKVP: %016llx)", mkvp); + } + if (volumes_argz != NULL) + util_rec_set_argz(rec, REC_VOLUMES, volumes_argz, +@@ -2317,43 +2304,32 @@ struct validate_info { + /** + * Displays the status of the associated APQNs. + * ++ * @param[in] keystore the key store + * @param[in] properties the properties of the key +- * @param[in] name the name of the key ++ * @param[in] mkvp the master key verification pattern of the key + * + * @returns 0 in case of success, 1 if at least one of the APQNs is not +- * available ++ * available or has a master key mismatch + */ +-static int _keystore_display_apqn_status(struct properties *properties, +- const char *name) ++static int _keystore_display_apqn_status(struct keystore *keystore, ++ struct properties *properties, ++ u64 mkvp) + { +- int i, rc, card, domain, warning = 0; +- char **apqn_list; ++ int rc, warning = 0; + char *apqns; + + apqns = properties_get(properties, PROP_NAME_APQNS); + if (apqns == NULL) + return 0; +- apqn_list = str_list_split(apqns); + +- for (i = 0; apqn_list[i] != NULL; i++) { +- +- if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) +- continue; +- +- rc = _keystore_is_apqn_online(card, domain); +- if (rc != 1) { +- printf("WARNING: The APQN %02x.%04x associated with " +- "key '%s' is %s\n", card, domain, name, +- rc == -1 ? "not a CCA card" : "not online"); +- warning = 1; +- } +- } ++ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); ++ if (rc != 0 && rc != -ENOTSUP) ++ warning = 1; + + if (warning) + printf("\n"); + + free(apqns); +- str_list_free_string_array(apqn_list); + return warning; + } + /** +@@ -2424,6 +2400,7 @@ static int _keystore_process_validate(struct keystore *keystore, + u8 *secure_key; + int is_old_mk; + int rc, valid; ++ u64 mkvp; + + rc = _keystore_ensure_keyfiles_exist(file_names, name); + if (rc != 0) +@@ -2447,12 +2424,18 @@ static int _keystore_process_validate(struct keystore *keystore, + info->num_valid++; + valid = 1; + } ++ ++ rc = get_master_key_verification_pattern(secure_key, secure_key_size, ++ &mkvp, keystore->verbose); + free(secure_key); ++ if (rc) ++ goto out; + + _keystore_print_record(info->rec, name, properties, 1, + file_names->skey_filename, secure_key_size, + clear_key_bitsize, valid, is_old_mk, +- _keystore_reencipher_key_exists(file_names)); ++ _keystore_reencipher_key_exists(file_names), ++ mkvp); + + if (valid && is_old_mk) { + util_print_indented("WARNING: The secure key is currently " +@@ -2463,7 +2446,8 @@ static int _keystore_process_validate(struct keystore *keystore, + info->num_warnings++; + } + if (info->noapqncheck == 0) +- if (_keystore_display_apqn_status(properties, name) != 0) ++ if (_keystore_display_apqn_status(keystore, properties, ++ mkvp) != 0) + info->num_warnings++; + if (_keystore_display_volume_status(properties, name) != 0) + info->num_warnings++; +@@ -2534,7 +2518,7 @@ struct reencipher_params { + struct reencipher_info { + struct reencipher_params params; + int pkey_fd; +- t_CSNBKTC dll_CSNBKTC; ++ struct cca_lib *cca; + unsigned long num_reenciphered; + unsigned long num_failed; + unsigned long num_skipped; +@@ -2545,23 +2529,33 @@ struct reencipher_info { + * + * @param[in] keystore the keystore + * @param[in] name the name of the key +- * @param[in] dll_CSNBKTC the CCA key token change function ++ * @param[in] cca the CCA library struct + * @param[in] params reenciphering parameters + * @param[in] secure_key a buffer containing the secure key + * @param[in] secure_key_size the size of the secure key + * @param[in] is_old_mk if true the key is currently re-enciphered with the + * OLD master key ++ * @param[in] apqns the associated APQNs (or NULL if none) + * @returns 0 if the re-enciphering is successful, a negative errno value + * otherwise, 1 if it was skipped + */ + static int _keystore_perform_reencipher(struct keystore *keystore, + const char *name, +- t_CSNBKTC dll_CSNBKTC, ++ struct cca_lib *cca, + struct reencipher_params *params, + u8 *secure_key, size_t secure_key_size, +- bool is_old_mk) ++ bool is_old_mk, const char *apqns) + { +- int rc; ++ int rc, selected = 1; ++ u64 mkvp; ++ ++ 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)); ++ return rc; ++ } + + if (!params->from_old && !params->to_new) { + /* Autodetect reencipher mode */ +@@ -2583,12 +2577,6 @@ static int _keystore_perform_reencipher(struct keystore *keystore, + } + + if (params->from_old) { +- if (!is_old_mk) { +- printf("The secure key '%s' is already enciphered " +- "with the CURRENT CCA master key\n", name); +- return 1; +- } +- + if (params->inplace == -1) + params->inplace = 1; + +@@ -2596,13 +2584,27 @@ static int _keystore_perform_reencipher(struct keystore *keystore, + "Secure key '%s' will be re-enciphered from OLD " + "to the CURRENT CCA master key", name); + +- rc = key_token_change(dll_CSNBKTC, +- secure_key, secure_key_size, ++ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, ++ FLAG_SEL_CCA_MATCH_OLD_MKVP, ++ keystore->verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ warnx("No APQN found that is suitable for " ++ "re-enciphering this secure AES key"); ++ return rc; ++ } ++ ++ rc = key_token_change(cca, secure_key, secure_key_size, + METHOD_OLD_TO_CURRENT, + keystore->verbose); + if (rc != 0) { + warnx("Failed to re-encipher '%s' from OLD to " + "CURRENT CCA master key", name); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); + return rc; + } + } +@@ -2614,13 +2616,30 @@ static int _keystore_perform_reencipher(struct keystore *keystore, + if (params->inplace == -1) + params->inplace = 0; + +- rc = key_token_change(dll_CSNBKTC, +- secure_key, secure_key_size, ++ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP | ++ FLAG_SEL_CCA_NEW_MUST_BE_SET, ++ keystore->verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ util_print_indented("No APQN found that is suitable " ++ "for re-enciphering this secure " ++ "AES key and has the NEW master " ++ "key loaded", 0); ++ return rc; ++ } ++ ++ rc = key_token_change(cca, secure_key, secure_key_size, + METHOD_CURRENT_TO_NEW, + keystore->verbose); + if (rc != 0) { + warnx("Failed to re-encipher '%s' from CURRENT to " + "NEW CCA master key", name); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); + return rc; + } + } +@@ -2708,10 +2727,11 @@ static int _keystore_process_reencipher(struct keystore *keystore, + if (!params.complete) { + printf("Re-enciphering key '%s'\n", name); + +- rc = _keystore_perform_reencipher(keystore, name, +- info->dll_CSNBKTC, ¶ms, +- secure_key, secure_key_size, +- is_old_mk); ++ rc = _keystore_perform_reencipher(keystore, name, info->cca, ++ ¶ms, secure_key, ++ secure_key_size, is_old_mk, ++ properties_get(properties, ++ PROP_NAME_APQNS)); + if (rc < 0) + goto out; + if (rc > 0) { +@@ -2814,6 +2834,8 @@ out: + * @param[in] inplace if true, the key will be re-enciphere in-place + * @param[in] staged if true, the key will be re-enciphere not in-place + * @param[in] complete if true, a pending re-encipherment is completed ++ * @param[in] pkey_fd the file descriptor of /dev/pkey ++ * @param[in] cca the CCA library struct + * Note: if both from Old and toNew are FALSE, then the reencipherement mode is + * detected automatically. If both are TRUE then the key is reenciphered + * from the OLD to the NEW CCA master key. +@@ -2826,7 +2848,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, + const char *apqn_filter, + bool from_old, bool to_new, bool inplace, + bool staged, bool complete, int pkey_fd, +- t_CSNBKTC dll_CSNBKTC) ++ struct cca_lib *cca) + { + struct reencipher_info info; + int rc; +@@ -2842,7 +2864,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, + info.params.inplace = 0; + info.params.complete = complete; + info.pkey_fd = pkey_fd; +- info.dll_CSNBKTC = dll_CSNBKTC; ++ info.cca = cca; + info.num_failed = 0; + info.num_reenciphered = 0; + info.num_skipped = 0; +@@ -3063,7 +3085,7 @@ out: + * @returnd 0 if the user confirmed the deletion, a negative errno value + * otherwise + */ +-static int _keystore_propmp_for_remove(struct keystore *keystore, ++static int _keystore_prompt_for_remove(struct keystore *keystore, + const char *name, + struct key_filenames *file_names) + { +@@ -3084,7 +3106,8 @@ static int _keystore_propmp_for_remove(struct keystore *keystore, + _keystore_msg_for_volumes(msg, key_prop, VOLUME_TYPE_PLAIN); + free(msg); + +- printf("%s: Remove key '%s'? ", program_invocation_short_name, name); ++ printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name, ++ name); + if (fgets(str, sizeof(str), stdin) == NULL) { + rc = -EIO; + goto out; +@@ -3093,6 +3116,7 @@ static int _keystore_propmp_for_remove(struct keystore *keystore, + str[strlen(str) - 1] = '\0'; + pr_verbose(keystore, "Prompt reply: '%s'", str); + if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) { ++ warnx("Operation aborted"); + rc = -ECANCELED; + goto out; + } +@@ -3129,7 +3153,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name, + goto out; + + if (!quiet) { +- if (_keystore_propmp_for_remove(keystore, name, ++ if (_keystore_prompt_for_remove(keystore, name, + &file_names) != 0) + goto out; + } +@@ -3204,7 +3228,7 @@ static int _keystore_display_key(struct keystore *keystore, + IS_XTS(secure_key_size) ? secure_key->bitsize * 2 + : secure_key->bitsize, + 0, 0, +- _keystore_reencipher_key_exists(file_names)); ++ _keystore_reencipher_key_exists(file_names), 0); + + out: + free(secure_key); +diff --git a/zkey/keystore.h b/zkey/keystore.h +index 16ecc62..8e888d1 100644 +--- a/zkey/keystore.h ++++ b/zkey/keystore.h +@@ -14,6 +14,7 @@ + + #include + ++#include "cca.h" + #include "pkey.h" + + struct keystore { +@@ -54,7 +55,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, + const char *apqn_filter, + bool from_old, bool to_new, bool inplace, + bool staged, bool complete, int pkey_fd, +- t_CSNBKTC dll_CSNBKTC); ++ struct cca_lib *cca); + + int keystore_copy_key(struct keystore *keystore, const char *name, + const char *newname, const char *volumes); +diff --git a/zkey/pkey.c b/zkey/pkey.c +index a88c4e9..8471f3d 100644 +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -44,57 +44,7 @@ + + #define MAX_CIPHER_LEN 32 + +-/* +- * Definitions for the CCA library +- */ +-#define CCA_LIBRARY_NAME "libcsulcca.so" +-#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" +- +-#define DEFAULT_KEYBITS 256 +- +-/** +- * Loads the CCA library and provides the entry point of the CSNBKTC function. +- * +- * @param[out] lib_csulcca on return this contains the address of the CCA +- * library. dlclose() should be used to free this +- * when no longer needed. +- * @param[out] dll_CSNBKTC on return this contains the address of the +- * CSNBKTC function. +- * @param verbose if true, verbose messages are printed +- * +- * @returns 0 on success, -ELIBACC in case of library load errors +- */ +-int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose) +-{ +- util_assert(lib_csulcca != NULL, "Internal error: lib_csulcca is NULL"); +- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); +- +- /* Load the CCA library */ +- *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); +- if (*lib_csulcca == NULL) { +- pr_verbose(verbose, "%s", dlerror()); +- warnx("The command requires the IBM CCA Host Libraries and " +- "Tools.\nFor the supported environments and downloads, " +- "see:\n%s", CCA_WEB_PAGE); +- return -ELIBACC; +- } +- +- /* Get the Key Token Change function */ +- *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC"); +- if (*dll_CSNBKTC == NULL) { +- pr_verbose(verbose, "%s", dlerror()); +- warnx("The command requires the IBM CCA Host Libraries and " +- "Tools.\nFor the supported environments and downloads, " +- "see:\n%s", CCA_WEB_PAGE); +- dlclose(*lib_csulcca); +- *lib_csulcca = NULL; +- return -ELIBACC; +- } +- +- pr_verbose(verbose, "CCA library '%s' has been loaded successfully", +- CCA_LIBRARY_NAME); +- return 0; +-} ++#define DEFAULT_KEYBITS 256 + + /** + * Opens the pkey device and returns its file descriptor. +@@ -267,7 +217,7 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, + return NULL; + } + } else { +- keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, xts); ++ keybits = HALF_KEYSIZE_FOR_XTS(size * 8, xts); + } + + switch (keybits) { +@@ -522,96 +472,6 @@ out: + return rc; + } + +-/** +- * Prints CCA return and reason code information for certain known CCA +- * error situations. +- * +- * @param return_code the CCA return code +- * @param reason_code the CCA reason code +- */ +-static void print_CCA_error(int return_code, int reason_code) +-{ +- switch (return_code) { +- case 8: +- switch (reason_code) { +- case 48: +- warnx("The secure key has a CCA master key " +- "verification pattern that is not valid"); +- break; +- } +- break; +- case 12: +- switch (reason_code) { +- case 764: +- warnx("The CCA master key is not loaded and " +- "therefore a secure key cannot be enciphered"); +- break; +- } +- break; +- } +-} +- +-/** +- * Re-enciphers a secure key. +- * +- * @param[in] dll_CSNBKTC the address of the CCA CSNBKTC function +- * @param[in] secure_key a buffer containing the secure key +- * @param[in] secure_key_size the size of the secure key +- * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT +- * or METHOD_CURRENT_TO_NEW. +- * @param[in] verbose if true, verbose messages are printed +- * +- * @returns 0 on success, -EIO in case of an error +- */ +-int key_token_change(t_CSNBKTC dll_CSNBKTC, +- u8 *secure_key, unsigned int secure_key_size, +- char *method, bool verbose) +-{ +- long exit_data_len = 0, rule_array_count; +- unsigned char rule_array[2 * 80] = { 0, }; +- unsigned char exit_data[4] = { 0, }; +- long return_code, reason_code; +- +- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); +- util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); +- util_assert(secure_key_size > 0, +- "Internal error: secure_key_size is 0"); +- util_assert(method != NULL, "Internal error: method is NULL"); +- +- memcpy(rule_array, method, 8); +- memcpy(rule_array + 8, "AES ", 8); +- rule_array_count = 2; +- +- dll_CSNBKTC(&return_code, &reason_code, +- &exit_data_len, exit_data, +- &rule_array_count, rule_array, +- secure_key); +- +- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " +- "return_code: %ld, reason_code: %ld", method, return_code, +- reason_code); +- if (return_code != 0) { +- print_CCA_error(return_code, reason_code); +- return -EIO; +- } +- +- if (secure_key_size == 2 * SECURE_KEY_SIZE) { +- dll_CSNBKTC(&return_code, &reason_code, +- &exit_data_len, exit_data, +- &rule_array_count, rule_array, +- secure_key + SECURE_KEY_SIZE); +- +- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " +- "returned: return_code: %ld, reason_code: %ld", +- method, return_code, reason_code); +- if (return_code != 0) { +- print_CCA_error(return_code, reason_code); +- return -EIO; +- } +- } +- return 0; +-} +- + /** + * Validates an XTS secure key (the second part) + * +@@ -909,3 +769,24 @@ out: + + return rc; + } ++ ++int get_master_key_verification_pattern(const u8 *secure_key, ++ size_t secure_key_size, u64 *mkvp, ++ bool verbose) ++{ ++ struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; ++ ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); ++ ++ if (secure_key_size < SECURE_KEY_SIZE) { ++ pr_verbose(verbose, "Size of secure key is too small: " ++ "%lu expected %lu", secure_key_size, ++ SECURE_KEY_SIZE); ++ return -EINVAL; ++ } ++ ++ *mkvp = token->mkvp; ++ ++ return 0; ++} +diff --git a/zkey/pkey.h b/zkey/pkey.h +index 3c524cd..c0aac2e 100644 +--- a/zkey/pkey.h ++++ b/zkey/pkey.h +@@ -82,23 +82,10 @@ struct pkey_verifykey { + + #define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey) + +-#define METHOD_OLD_TO_CURRENT "RTCMK " +-#define METHOD_CURRENT_TO_NEW "RTNMK " +- +-typedef void (*t_CSNBKTC)(long *return_code, +- long *reason_code, +- long *exit_data_length, +- unsigned char *exit_data, +- long *rule_array_count, +- unsigned char *rule_array, +- unsigned char *key_identifier); +- + #define PAES_BLOCK_SIZE 16 + #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) + #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) + +-int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose); +- + int open_pkey_device(bool verbose); + + int generate_secure_key_random(int pkey_fd, const char *keyfile, +@@ -122,11 +109,11 @@ int validate_secure_key(int pkey_fd, + size_t *clear_key_bitsize, int *is_old_mk, + bool verbose); + +-int key_token_change(t_CSNBKTC dll_CSNBKTC, +- u8 *secure_key, unsigned int secure_key_size, +- char *method, bool verbose); +- + int generate_key_verification_pattern(const char *key, size_t key_size, + char *vp, size_t vp_len, bool verbose); + ++int get_master_key_verification_pattern(const u8 *secure_key, ++ size_t secure_key_size, u64 *mkvp, ++ bool verbose); ++ + #endif +diff --git a/zkey/properties.c b/zkey/properties.c +index c20e51b..147bcc5 100644 +--- a/zkey/properties.c ++++ b/zkey/properties.c +@@ -9,6 +9,7 @@ + * it under the terms of the MIT license. See LICENSE for details. + */ + ++#include + #include + #include + #include +@@ -183,14 +184,16 @@ static struct property *properties_find(struct properties *properties, + * @param[in] properties the properties object + * @param[in] name the name of the property + * @param[in] value the value of the property ++ * @param[in] uppercase if true the value is set all uppercase + * + * @returns 0 on success, + * -EINVAL if the name or value contains invalid characters + */ +-int properties_set(struct properties *properties, +- const char *name, const char *value) ++int properties_set2(struct properties *properties, ++ const char *name, const char *value, bool uppercase) + { + struct property *property; ++ int i; + + util_assert(properties != NULL, "Internal error: properties is NULL"); + util_assert(name != NULL, "Internal error: name is NULL"); +@@ -211,9 +214,30 @@ int properties_set(struct properties *properties, + property->value = util_strdup(value); + util_list_add_tail(&properties->list, property); + } ++ if (uppercase) { ++ for (i = 0; property->value[i] != '\0'; i++) ++ property->value[i] = toupper(property->value[i]); ++ } ++ + return 0; + } + ++/** ++ * Adds or updates a property ++ * ++ * @param[in] properties the properties object ++ * @param[in] name the name of the property ++ * @param[in] value the value of the property ++ * ++ * @returns 0 on success, ++ * -EINVAL if the name or value contains invalid characters ++ */ ++int properties_set(struct properties *properties, ++ const char *name, const char *value) ++{ ++ return properties_set2(properties, name, value, false); ++} ++ + /** + * Gets a property + * +diff --git a/zkey/properties.h b/zkey/properties.h +index 234948e..f2c8a15 100644 +--- a/zkey/properties.h ++++ b/zkey/properties.h +@@ -23,6 +23,9 @@ void properties_free(struct properties *properties); + int properties_set(struct properties *properties, + const char *name, const char *value); + ++int properties_set2(struct properties *properties, ++ const char *name, const char *value, bool uppercase); ++ + char *properties_get(struct properties *properties, const char *name); + + int properties_remove(struct properties *properties, const char *name); +diff --git a/zkey/utils.c b/zkey/utils.c +new file mode 100644 +index 0000000..9dc4af7 +--- /dev/null ++++ b/zkey/utils.c +@@ -0,0 +1,725 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/util_path.h" ++#include "lib/util_file.h" ++#include "lib/util_scandir.h" ++#include "lib/util_libc.h" ++#include "lib/util_rec.h" ++#include "lib/util_base.h" ++ ++#include "utils.h" ++#include "properties.h" ++ ++#define pr_verbose(verbose, fmt...) do { \ ++ if (verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++/** ++ * Checks if the specified card is of type CCA and is online ++ * ++ * @param[in] card card number ++ * ++ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its ++ * not a CCA card. ++ */ ++int sysfs_is_card_online(int card) ++{ ++ long int online; ++ char *dev_path; ++ char type[20]; ++ int rc = 1; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); ++ if (!util_path_is_dir(dev_path)) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (online == 0) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { ++ rc = 0; ++ goto out; ++ } ++ if (type[4] != 'C') { ++ rc = -1; ++ goto out; ++ } ++ ++out: ++ free(dev_path); ++ return rc; ++} ++ ++/** ++ * Checks if the specified APQN is of type CCA and is online ++ * ++ * @param[in] card card number ++ * @param[in] domain the domain ++ * ++ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its ++ * not a CCA card. ++ */ ++int sysfs_is_apqn_online(int card, int domain) ++{ ++ long int online; ++ char *dev_path; ++ int rc = 1; ++ ++ rc = sysfs_is_card_online(card); ++ if (rc != 1) ++ return rc; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card, ++ card, domain); ++ if (!util_path_is_dir(dev_path)) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (online == 0) { ++ rc = 0; ++ goto out; ++ } ++ ++out: ++ free(dev_path); ++ return rc; ++} ++ ++/** ++ * Gets the 8 character ASCII serial number string of an card from the sysfs. ++ * ++ * @param[in] card card number ++ * @param[out] serialnr Result buffer ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 if the serial number was returned. -ENODEV if the APQN is not ++ * available, or is not a CCA card. -ENOTSUP if the serialnr sysfs ++ * attribute is not available, because the zcrypt kernel module is ++ * on an older level. ++ */ ++int sysfs_get_serialnr(int card, char serialnr[9], bool verbose) ++{ ++ char *dev_path; ++ int rc = 0; ++ ++ if (serialnr == NULL) ++ return -EINVAL; ++ ++ if (sysfs_is_card_online(card) != 1) ++ return -ENODEV; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); ++ if (!util_path_is_dir(dev_path)) { ++ rc = -ENODEV; ++ goto out; ++ } ++ if (util_file_read_line(serialnr, 9, "%s/serialnr", dev_path) != 0) { ++ rc = -ENOTSUP; ++ goto out; ++ } ++ ++ if (strlen(serialnr) == 0) { ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ pr_verbose(verbose, "Serial number of %02x: %s", card, serialnr); ++out: ++ if (rc != 0) ++ pr_verbose(verbose, "Failed to get serial number for " ++ "%02x: %s", card, strerror(-rc)); ++ ++ free(dev_path); ++ return rc; ++} ++ ++static int parse_mk_info(char *line, struct mk_info *mk_info) ++{ ++ struct mk_info_reg *mk_reg; ++ char *save; ++ char *tok; ++ ++ tok = strtok_r(line, " ", &save); ++ if (tok == NULL) ++ return -EIO; ++ ++ if (strcasecmp(tok, "AES") != 0) ++ return 0; ++ ++ tok = strtok_r(NULL, " ", &save); ++ if (tok == NULL) ++ return -EIO; ++ ++ if (strcasecmp(tok, "NEW:") == 0) ++ mk_reg = &mk_info->new_mk; ++ else if (strcasecmp(tok, "CUR:") == 0) ++ mk_reg = &mk_info->cur_mk; ++ else if (strcasecmp(tok, "OLD:") == 0) ++ mk_reg = &mk_info->old_mk; ++ else ++ return -EIO; ++ ++ tok = strtok_r(NULL, " ", &save); ++ if (tok == NULL) ++ return -EIO; ++ ++ if (strcasecmp(tok, "empty") == 0) ++ mk_reg->mk_state = MK_STATE_EMPTY; ++ else if (strcasecmp(tok, "partial") == 0) ++ mk_reg->mk_state = MK_STATE_PARTIAL; ++ else if (strcasecmp(tok, "full") == 0) ++ mk_reg->mk_state = MK_STATE_FULL; ++ else if (strcasecmp(tok, "valid") == 0) ++ mk_reg->mk_state = MK_STATE_VALID; ++ else if (strcasecmp(tok, "invalid") == 0) ++ mk_reg->mk_state = MK_STATE_INVALID; ++ else ++ mk_reg->mk_state = MK_STATE_UNKNOWN; ++ ++ tok = strtok_r(NULL, " ", &save); ++ if (tok == NULL) ++ return -EIO; ++ ++ if (sscanf(tok, "%llx", &mk_reg->mkvp) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++/** ++ * Gets the master key states and verification patterns of an APQN from the ++ * sysfs. ++ * ++ * @param[in] card card number ++ * @param[in] domain the domain ++ * @param[out] mk_info structure is filled on return with master key infos ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 if the master key info was returned. -ENODEV if the APQN is not ++ * available, or is not a CCA card. -ENOTSUP if the mkvps sysfs ++ * attribute is not available, because the zcrypt kernel module is ++ * on an older level. ++ */ ++int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) ++{ ++ char *dev_path; ++ char *p, *end; ++ char buf[100]; ++ int rc = 0; ++ FILE *fp; ++ ++ if (mk_info == NULL) ++ return -EINVAL; ++ ++ memset(mk_info, 0, sizeof(struct mk_info)); ++ mk_info->new_mk.mk_state = MK_STATE_UNKNOWN; ++ mk_info->cur_mk.mk_state = MK_STATE_UNKNOWN; ++ mk_info->old_mk.mk_state = MK_STATE_UNKNOWN; ++ ++ if (sysfs_is_apqn_online(card, domain) != 1) ++ return -ENODEV; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/mkvps", ++ card, card, domain); ++ if (!util_path_is_reg_file(dev_path)) { ++ rc = -ENOTSUP; ++ goto out; ++ } ++ ++ fp = fopen(dev_path, "r"); ++ if (fp == NULL) { ++ rc = -ENOTSUP; ++ goto out; ++ } ++ ++ /* ++ * Expected contents: ++ * AES NEW: ++ * AES CUR: ++ * AES OLD: ++ * with ++ * : 'empty' or 'partial' or 'full' ++ * , : 'valid' or 'invalid' ++ * , , new_mk.mk_state == MK_STATE_UNKNOWN && ++ mk_info->cur_mk.mk_state == MK_STATE_UNKNOWN && ++ mk_info->old_mk.mk_state == MK_STATE_UNKNOWN) ++ rc = -EIO; ++out: ++ if (rc != 0) ++ pr_verbose(verbose, "Failed to get mkvps for %02x.%04x: %s", ++ card, domain, strerror(-rc)); ++ ++ free(dev_path); ++ return rc; ++} ++ ++static int scan_for_domains(int card, apqn_handler_t handler, ++ void *handler_data, bool verbose) ++{ ++ struct dirent **namelist; ++ char fname[290]; ++ int i, n, domain, rc = 0; ++ ++ sprintf(fname, "/sys/devices/ap/card%02x/", card); ++ n = util_scandir(&namelist, alphasort, fname, ++ "[0-9a-fA-F]+\\.[0-9a-fA-F]+"); ++ ++ if (n < 0) ++ return -EIO; ++ ++ for (i = 0; i < n; i++) { ++ if (sscanf(namelist[i]->d_name, "%x.%x", &card, &domain) != 2) ++ continue; ++ ++ pr_verbose(verbose, "Found %02x.%04x", card, domain); ++ ++ if (sysfs_is_apqn_online(card, domain) != 1) { ++ pr_verbose(verbose, "APQN %02x.%04x is offline or not " ++ "CCA", card, domain); ++ continue; ++ } ++ ++ rc = handler(card, domain, handler_data); ++ if (rc != 0) ++ break; ++ } ++ ++ util_scandir_free(namelist, n); ++ return rc; ++} ++ ++ ++static int scan_for_apqns(apqn_handler_t handler, void *handler_data, ++ bool verbose) ++{ ++ struct dirent **namelist; ++ int i, n, card, rc = 0; ++ ++ if (handler == NULL) ++ return -EINVAL; ++ ++ n = util_scandir(&namelist, alphasort, "/sys/devices/ap/", ++ "card[0-9a-fA-F]+"); ++ if (n < 0) ++ return -EIO; ++ ++ for (i = 0; i < n; i++) { ++ if (sscanf(namelist[i]->d_name, "card%x", &card) != 1) ++ continue; ++ ++ pr_verbose(verbose, "Found card %02x", card); ++ ++ if (sysfs_is_card_online(card) != 1) { ++ pr_verbose(verbose, "Card %02x is offline or not CCA", ++ card); ++ continue; ++ } ++ ++ rc = scan_for_domains(card, handler, handler_data, verbose); ++ if (rc != 0) ++ break; ++ } ++ ++ util_scandir_free(namelist, n); ++ return rc; ++} ++ ++/** ++ * Calls the handler for all APQNs specified in the apqns parameter, or of this ++ * is NULL, for all online CCA APQNs found in sysfs. In case sysfs is inspected, ++ * the cards and domains are processed in alphabetical order. ++ * ++ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, ++ * or an empty string, then all online CCA APQNs are ++ * handled. ++ * @param[in] handler a handler function that is called for each APQN ++ * @param[in] handler_data private data that is passed to the handler ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, ++ bool verbose) ++{ ++ int card, domain; ++ char *copy, *tok; ++ char *save; ++ int rc = 0; ++ ++ if (apqns == NULL || (apqns != NULL && strlen(apqns) == 0)) { ++ rc = scan_for_apqns(handler, handler_data, verbose); ++ } else { ++ copy = util_strdup(apqns); ++ tok = strtok_r(copy, ",", &save); ++ while (tok != NULL) { ++ ++ if (sscanf(tok, "%x.%x", &card, &domain) != 2) { ++ warnx("the APQN '%s' is not valid", ++ tok); ++ rc = -EINVAL; ++ break; ++ } ++ ++ pr_verbose(verbose, "Specified: %02x.%04x", card, ++ domain); ++ rc = handler(card, domain, handler_data); ++ if (rc != 0) ++ break; ++ ++ tok = strtok_r(NULL, ",", &save); ++ } ++ free(copy); ++ } ++ ++ return rc; ++} ++ ++struct print_apqn_info { ++ struct util_rec *rec; ++ bool verbose; ++}; ++ ++static int print_apqn_mk_info(int card, int domain, void *handler_data) ++{ ++ struct print_apqn_info *info = (struct print_apqn_info *)handler_data; ++ struct mk_info mk_info; ++ int rc; ++ ++ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); ++ if (rc == -ENOTSUP) ++ return rc; ++ ++ util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain); ++ ++ if (rc == 0) { ++ if (mk_info.new_mk.mk_state == MK_STATE_FULL) ++ util_rec_set(info->rec, "NEW", "%016llx", ++ mk_info.new_mk.mkvp); ++ else if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) ++ util_rec_set(info->rec, "NEW", "partially loaded"); ++ else ++ util_rec_set(info->rec, "NEW", "-"); ++ ++ if (mk_info.cur_mk.mk_state == MK_STATE_VALID) ++ util_rec_set(info->rec, "CUR", "%016llx", ++ mk_info.cur_mk.mkvp); ++ else ++ util_rec_set(info->rec, "CUR", "-"); ++ ++ if (mk_info.old_mk.mk_state == MK_STATE_VALID) ++ util_rec_set(info->rec, "OLD", "%016llx", ++ mk_info.old_mk.mkvp); ++ else ++ util_rec_set(info->rec, "OLD", "-"); ++ } else { ++ util_rec_set(info->rec, "NEW", "?"); ++ util_rec_set(info->rec, "CUR", "?"); ++ util_rec_set(info->rec, "OLD", "?"); ++ } ++ ++ util_rec_print(info->rec); ++ ++ return 0; ++} ++ ++/** ++ * Prints master key information for all specified APQNs ++ * ++ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, ++ * or an empty string, then all online CCA APQNs are ++ * printed. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 for success or a negative errno in case of an error. -ENOTSUP is ++ * returned when the mkvps sysfs attribute is not available, because ++ * the zcrypt kernel module is on an older level. ++ */ ++int print_mk_info(const char *apqns, bool verbose) ++{ ++ struct print_apqn_info info; ++ int rc; ++ ++ info.verbose = verbose; ++ info.rec = util_rec_new_wide("-"); ++ ++ util_rec_def(info.rec, "APQN", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN"); ++ util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK"); ++ util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK"); ++ util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK"); ++ util_rec_print_hdr(info.rec); ++ ++ rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose); ++ ++ util_rec_free(info.rec); ++ return rc; ++} ++ ++struct cross_check_info { ++ u64 mkvp; ++ u64 new_mkvp; ++ bool key_mkvp; ++ u32 num_cur_match; ++ u32 num_old_match; ++ u32 num_new_match; ++ bool mismatch; ++ bool print_mks; ++ int num_checked; ++ bool verbose; ++}; ++ ++static int cross_check_mk_info(int card, int domain, void *handler_data) ++{ ++ struct cross_check_info *info = (struct cross_check_info *)handler_data; ++ struct mk_info mk_info; ++ char temp[200]; ++ int rc; ++ ++ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); ++ if (rc == -ENODEV) { ++ info->print_mks = 1; ++ printf("WARNING: APQN %02x.%04x: Not available or not of " ++ "type CCA\n", card, domain); ++ return 0; ++ } ++ if (rc != 0) ++ return rc; ++ ++ info->num_checked++; ++ ++ if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " ++ "register is only partially loaded.", card, domain); ++ util_print_indented(temp, 0); ++ } ++ ++ if (info->new_mkvp == 0 && ++ mk_info.new_mk.mk_state == MK_STATE_FULL) ++ info->new_mkvp = mk_info.new_mk.mkvp; ++ ++ if (mk_info.new_mk.mk_state == MK_STATE_FULL && ++ mk_info.new_mk.mkvp != info->new_mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "WARNING: APQN %02x.%04x: The NEW master key " ++ "register contains a different master key than " ++ "the NEW register of other APQNs.", card, ++ domain); ++ util_print_indented(temp, 0); ++ } ++ ++ if (mk_info.cur_mk.mk_state != MK_STATE_VALID) { ++ info->print_mks = 1; ++ info->mismatch = 1; ++ printf("WARNING: APQN %02x.%04x: No master key is set.\n", card, ++ domain); ++ return 0; ++ } ++ ++ if (mk_info.old_mk.mk_state == MK_STATE_VALID && ++ mk_info.old_mk.mkvp == mk_info.cur_mk.mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The OLD master key " ++ "register contains the same master key as the CURRENT " ++ "master key register.", card, domain); ++ util_print_indented(temp, 0); ++ } ++ if (mk_info.new_mk.mk_state == MK_STATE_FULL && ++ mk_info.new_mk.mkvp == mk_info.cur_mk.mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " ++ "register contains the same master key as the CURRENT " ++ "master key register.", card, domain); ++ util_print_indented(temp, 0); ++ } ++ if (mk_info.new_mk.mk_state == MK_STATE_FULL && ++ mk_info.old_mk.mk_state == MK_STATE_VALID && ++ mk_info.new_mk.mkvp == mk_info.old_mk.mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " ++ "register contains the same master key as the OLD " ++ "master key register.", card, domain); ++ util_print_indented(temp, 0); ++ } ++ ++ if (info->mkvp == 0) ++ info->mkvp = mk_info.cur_mk.mkvp; ++ ++ if (info->key_mkvp) { ++ if (mk_info.cur_mk.mk_state == MK_STATE_VALID && ++ mk_info.cur_mk.mkvp == info->mkvp) ++ info->num_cur_match++; ++ ++ if (mk_info.old_mk.mk_state == MK_STATE_VALID && ++ mk_info.old_mk.mkvp == info->mkvp) ++ info->num_old_match++; ++ ++ if (mk_info.new_mk.mk_state == MK_STATE_FULL && ++ mk_info.new_mk.mkvp == info->mkvp) ++ info->num_new_match++; ++ } ++ ++ if (mk_info.cur_mk.mkvp != info->mkvp) { ++ ++ if (info->key_mkvp) { ++ if (mk_info.old_mk.mk_state == MK_STATE_VALID && ++ mk_info.old_mk.mkvp == info->mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The master" ++ " key has been changed to a new " ++ "master key, but the secure key has " ++ "not yet been re-enciphered.", card, ++ domain); ++ util_print_indented(temp, 0); ++ } else if (mk_info.new_mk.mk_state == MK_STATE_FULL && ++ mk_info.new_mk.mkvp == info->mkvp) { ++ info->print_mks = 1; ++ sprintf(temp, "INFO: APQN %02x.%04x: The master" ++ " key has been changed but is not " ++ "yet been set (made active).", card, ++ domain); ++ util_print_indented(temp, 0); ++ } else { ++ info->print_mks = 1; ++ info->mismatch = 1; ++ sprintf(temp, "WARNING: APQN %02x.%04x: The " ++ "CURRENT master key register contains " ++ "a master key that is different from " ++ "the one used by the secure key.", card, ++ domain); ++ util_print_indented(temp, 0); ++ } ++ } else { ++ info->print_mks = 1; ++ info->mismatch = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Cross checks the master key information for all specified APQNs. It checks ++ * if all specified APQNs have the same current master key, and if it matches ++ * the master key specified by the mkvp parameter (optional). If not, it prints ++ * out an information message about the APQNs that have a different master key. ++ * ++ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, ++ * or an empty string, then all online CCA APQNs are ++ * checked. ++ * @param[in] mkvp The master key verification pattern of a secure key. ++ * If this is all zero, then the master keys are not ++ * matched against it. ++ * @param[in] print_mks if true, then a the full master key info of all ++ * specified APQns is printed, in case of a mismatch. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 for success or a negative errno in case of an error. -ENODEV is ++ * returned if at least one APQN has a mismatching master key. ++ * -ENOTSUP is returned when the mkvps sysfs attribute is not ++ * available, because the zcrypt kernel module is on an older level. ++ */ ++int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) ++{ ++ struct cross_check_info info; ++ char temp[200]; ++ int rc; ++ ++ memset(&info, 0, sizeof(info)); ++ info.key_mkvp = mkvp != 0; ++ info.mkvp = mkvp; ++ info.verbose = verbose; ++ ++ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s", ++ mkvp, apqns != NULL ? apqns : "ANY"); ++ ++ rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose); ++ if (rc != 0) ++ return rc; ++ ++ if (info.mismatch) { ++ if (info.key_mkvp) ++ printf("WARNING: Not all APQNs have the correct master " ++ "key (%016llx).\n", mkvp); ++ else ++ printf("WARNING: Not all APQNs have the same master " ++ "key.\n"); ++ ++ rc = -ENODEV; ++ } ++ if (info.num_checked == 0) { ++ printf("WARNING: None of the APQNs is available or of " ++ "type CCA\n"); ++ rc = -ENODEV; ++ } ++ if (info.num_old_match > 0 && info.num_new_match > 0) { ++ sprintf(temp, "WARNING: On %u APQNs the OLD master key " ++ "register contains the master key use by the secure " ++ "key, and on %u APQNs the NEW master key register " ++ "contains the master key use by the secure key.", ++ info.num_old_match, info.num_new_match); ++ util_print_indented(temp, 0); ++ info.print_mks = 1; ++ rc = -ENODEV; ++ } ++ ++ if (print_mks && info.print_mks) { ++ printf("\n"); ++ print_mk_info(apqns, verbose); ++ printf("\n"); ++ } ++ ++ return rc; ++} +diff --git a/zkey/utils.h b/zkey/utils.h +new file mode 100644 +index 0000000..c4dfb2b +--- /dev/null ++++ b/zkey/utils.h +@@ -0,0 +1,54 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * This header file defines the interface to the CCA host library. ++ * ++ * Copyright IBM Corp. 2019 ++ * ++ * 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 UTILS_H ++#define UTILS_H ++ ++#include "lib/zt_common.h" ++ ++int sysfs_is_card_online(int card); ++ ++int sysfs_is_apqn_online(int card, int domain); ++ ++int sysfs_get_serialnr(int card, char serialnr[9], bool verbose); ++ ++#define MK_STATE_EMPTY 0 ++#define MK_STATE_PARTIAL 1 ++#define MK_STATE_FULL 2 ++#define MK_STATE_VALID 3 ++#define MK_STATE_INVALID 4 ++#define MK_STATE_UNKNOWN -1 ++ ++struct mk_info_reg { ++ int mk_state; ++ u64 mkvp; ++}; ++ ++struct mk_info { ++ struct mk_info_reg new_mk; ++ struct mk_info_reg cur_mk; ++ struct mk_info_reg old_mk; ++}; ++ ++int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, ++ bool verbose); ++ ++typedef int(*apqn_handler_t) (int card, int domain, void *handler_data); ++ ++int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, ++ bool verbose); ++ ++int print_mk_info(const char *apqns, bool verbose); ++ ++int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, ++ bool verbose); ++ ++#endif +diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 +index e93b5f0..35e3591 100644 +--- a/zkey/zkey-cryptsetup.1 ++++ b/zkey/zkey-cryptsetup.1 +@@ -91,6 +91,8 @@ behave in the same way as with \fBcryptsetup\fP. + .B zkey\-cryptsetup + .BR reencipher | re + .I device ++.RB [ \-\-to\-new | \-N ] ++.RB [ \-\-from\-old | \-O ] + .RB [ \-\-staged | \-s ] + .RB [ \-\-in\-place | \-i ] + .RB [ \-\-complete | \-c ] +@@ -128,17 +130,36 @@ register can still be used until the master key is changed again. + The \fBNEW\fP register contains the new master key to be set. + The master key in the \fBNEW\fP register cannot be used until it is made + the current master key. You can pro-actively re-encipher a secure key with the +-\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. ++\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. Use the ++.B \-\-to-new ++option to do this. + .RE + .PP +-\fBzkey\-cryptsetup\fP automatically detects whether the secure volume key +-is currently enciphered with the master key in the \fBOLD\fP register or with +-the master key in the \fBCURRENT\fP register. If currently enciphered with the +-master key in the \fBOLD\fP register, it is re-enciphered with the master key +-in the \fBCURRENT\fP register. If it is currently enciphered with the master +-key in the \fBCURRENT\fP register, it is re-enciphered with the master key in +-the \fBNEW\fP register. If for this case the \fBNEW\fP register does not +-contain a valid master key, then the re-encipher operation fails. ++Use the ++.B \-\-from\-old ++option to re-encipher a secure volume key that is currently enciphered with ++the master key in the \fBOLD\fP register with the master key in the ++\fBCURRENT\fP register. ++.PP ++.PP ++If both the ++.B \-\-from-old ++and ++.B \-\-to-new ++options are specified, a secure volume key that is currently enciphered ++with the master key in the \fBOLD\fP register is re-enciphered with the ++master key in the \fBNEW\fP register. ++.RE ++.PP ++If both options are omitted, \fBzkey-cryptsetup\fP automatically detects whether ++the secure volume key is currently enciphered with the master key in the ++\fBOLD\fP register or with the master key in the \fBCURRENT\fP register. ++If currently enciphered with the master key in the \fBOLD\fP register, ++it is re-enciphered with the master key in the \fBCURRENT\fP register. ++If it is currently enciphered with the master key in the \fBCURRENT\fP ++register, it is re-enciphered with the master key in the \fBNEW\fP register. ++If for this case the \fBNEW\fP register does not contain a valid master key, ++then the re-encipher operation fails. + .PP + Re-enciphering a secure volume key of a volume encrypted with + \fBLUKS2\fP and the \fBpaes\fP cipher can be performed \fBin-place\fP, or in +@@ -326,6 +347,16 @@ relevance. + . + .SS "Options for the reencipher command" + .TP ++.BR \-N ", " \-\-to\-new ++Re-enciphers a secure volume key in the LUKS2 header that is currently ++enciphered with the master key in the CURRENT register with the master key in ++the NEW register. ++.TP ++.BR \-O ", " \-\-from\-old ++Re-enciphers a secure volume key in the LUKS2 header that is currently ++enciphered with the master key in the OLD register with the master key in the ++CURRENT register. ++.TP + .BR \-i ", " \-\-in-place + Forces an in-place re-enciphering of a secure volume key in the LUKS2 + header. This option immediately replaces the secure volume key in the LUKS2 +diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c +index 8180b4a..c683fd4 100644 +--- a/zkey/zkey-cryptsetup.c ++++ b/zkey/zkey-cryptsetup.c +@@ -34,6 +34,7 @@ + + #include "misc.h" + #include "pkey.h" ++#include "cca.h" + + /* Detect if cryptsetup 2.1 or later is available */ + #ifdef CRYPT_LOG_DEBUG_JSON +@@ -94,6 +95,8 @@ static struct zkey_cryptsetup_globals { + long long keyfile_offset; + long long keyfile_size; + long long tries; ++ bool tonew; ++ bool fromold; + bool complete; + bool inplace; + bool staged; +@@ -101,8 +104,7 @@ static struct zkey_cryptsetup_globals { + bool batch_mode; + bool debug; + bool verbose; +- void *lib_csulcca; +- t_CSNBKTC dll_CSNBKTC; ++ struct cca_lib cca; + int pkey_fd; + struct crypt_device *cd; + } g = { +@@ -162,6 +164,22 @@ static struct util_opt opt_vec[] = { + .desc = "OPTIONS", + .command = COMMAND_REENCIPHER, + }, ++ { ++ .option = {"to-new", 0, NULL, 'N'}, ++ .desc = "Re-enciphers a secure volume key in the LUKS2 header " ++ "that is currently enciphered with the master key in " ++ "the CURRENT register with the master key in the NEW " ++ "register", ++ .command = COMMAND_REENCIPHER, ++ }, ++ { ++ .option = {"from-old", 0, NULL, 'O'}, ++ .desc = "Re-enciphers a secure volume key in the LUKS2 header " ++ "that is currently enciphered with the master key in " ++ "the OLD register with the master key in the CURRENT " ++ "register", ++ .command = COMMAND_REENCIPHER, ++ }, + { + .option = {"staged", 0, NULL, 's'}, + .desc = "Forces that the re-enciphering of a secure volume " +@@ -1275,7 +1293,7 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key, + util_print_indented(complete_msg, 0); + util_print_indented("All key slots containing the old volume key are " + "now in unbound state. Do you want to remove " +- "these key slots?", 0); ++ "these key slots [y/N]?", 0); + + if (!prompt_for_yes()) + return 0; +@@ -1514,17 +1532,19 @@ static int reencipher_prepare(int token) + char *password = NULL; + size_t password_len; + char *key = NULL; ++ int selected = 1; + size_t keysize; + int is_old_mk; + char *prompt; + char *msg; ++ u64 mkvp; + int rc; + + if (token >= 0) { + util_asprintf(&msg, "Staged volume key re-enciphering is " + "already initiated for device '%s'. Do you want to " + "cancel the pending re-enciphering and start a " +- "new re-enciphering process?", g.pos_arg); ++ "new re-enciphering process [y/N]?", g.pos_arg); + util_print_indented(msg, 0); + free(msg); + +@@ -1570,25 +1590,97 @@ static int reencipher_prepare(int token) + if (rc < 0) + goto out; + +- util_asprintf(&msg, "The secure volume key of device '%s' is " +- "enciphered with the %s CCA master key and is being " +- "re-enciphered with the %s CCA master key.", +- g.pos_arg, is_old_mk ? "OLD" : "CURRENT", +- is_old_mk ? "CURRENT" : "NEW"); +- util_print_indented(msg, 0); +- free(msg); ++ if (!g.fromold && !g.tonew) { ++ /* Autodetect reencipher mode */ ++ if (is_old_mk) { ++ g.fromold = 1; ++ util_asprintf(&msg, "The secure volume key of device " ++ "'%s' is enciphered with the OLD CCA " ++ "master key and is being re-enciphered " ++ "with the CURRENT CCA master key.", ++ g.pos_arg); ++ util_print_indented(msg, 0); ++ free(msg); ++ } else { ++ g.tonew = 1; ++ util_asprintf(&msg, "The secure volume key of device " ++ "'%s' is enciphered with the CURRENT CCA " ++ "master key and is being re-enciphered " ++ "with the NEW CCA master key.", ++ g.pos_arg); ++ util_print_indented(msg, 0); ++ free(msg); ++ } ++ } + +- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, +- is_old_mk ? METHOD_OLD_TO_CURRENT : +- METHOD_CURRENT_TO_NEW, +- g.verbose); ++ rc = get_master_key_verification_pattern((u8 *)key, keysize, &mkvp, ++ g.verbose); + if (rc != 0) { +- warnx("Failed to re-encipher the secure volume key of device " +- "'%s'", g.pos_arg); +- rc = -EINVAL; ++ warnx("Failed to get the master key verification pattern: %s", ++ strerror(-rc)); + goto out; + } + ++ if (g.fromold) { ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_OLD_MKVP, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ util_print_indented("No APQN found that is suitable " ++ "for re-enciphering the secure AES " ++ "volume key from the OLD to the " ++ "CURRENT CCA master key.", 0); ++ goto out; ++ } ++ ++ rc = key_token_change(&g.cca, (u8 *)key, keysize, ++ METHOD_OLD_TO_CURRENT, g.verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher the secure volume key of " ++ "device '%s'\n", g.pos_arg); ++ if (!selected) ++ print_msg_for_cca_envvars( ++ "secure AES volume key"); ++ rc = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (g.tonew) { ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP | ++ FLAG_SEL_CCA_NEW_MUST_BE_SET, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ util_print_indented("No APQN found that is suitable " ++ "for re-enciphering the secure AES " ++ "volume key from the CURRENT to " ++ "the NEW CCA master key.", 0); ++ goto out; ++ } ++ ++ rc = key_token_change(&g.cca, (u8 *)key, keysize, ++ METHOD_CURRENT_TO_NEW, ++ g.verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher the secure volume key of " ++ "device '%s'\n", g.pos_arg); ++ if (!selected) ++ print_msg_for_cca_envvars( ++ "secure AES volume key"); ++ rc = -EINVAL; ++ goto out; ++ } ++ } ++ + rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, key, keysize, + password, password_len, + CRYPT_VOLUME_KEY_NO_SEGMENT); +@@ -1651,10 +1743,12 @@ static int reencipher_complete(int token) + char *password = NULL; + size_t password_len; + char *key = NULL; ++ int selected = 1; + size_t keysize; + int is_old_mk; + char *prompt; + char *msg; ++ u64 mkvp; + int rc; + + rc = get_reencipher_token(g.cd, token, &tok, true); +@@ -1690,7 +1784,7 @@ static int reencipher_complete(int token) + "was completed.\n" + "Do you want to re-encipher the secure key with " + "the CCA master key in the CURRENT master key " +- "register?", g.pos_arg); ++ "register [y/N]?", g.pos_arg); + util_print_indented(msg, 0); + free(msg); + +@@ -1700,11 +1794,38 @@ static int reencipher_complete(int token) + goto out; + } + +- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, ++ rc = get_master_key_verification_pattern((u8 *)key, keysize, ++ &mkvp, g.verbose); ++ if (rc != 0) { ++ warnx("Failed to get the master key verification " ++ "pattern: %s", ++ strerror(-rc)); ++ goto out; ++ } ++ ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_OLD_MKVP, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ util_print_indented("No APQN found that is suitable " ++ "for re-enciphering the secure AES " ++ "volume key from the OLD to the " ++ "CURRENT CCA master key.", 0); ++ goto out; ++ } ++ ++ rc = key_token_change(&g.cca, (u8 *)key, keysize, + METHOD_OLD_TO_CURRENT, g.verbose); + if (rc != 0) { + warnx("Failed to re-encipher the secure volume key for " +- "device '%s'", g.pos_arg); ++ "device '%s'\n", g.pos_arg); ++ if (!selected) ++ print_msg_for_cca_envvars( ++ "secure AES volume key"); + rc = -EINVAL; + goto out; + } +@@ -1834,6 +1955,7 @@ static int command_validate(void) + char *prompt; + char *msg; + int token; ++ u64 mkvp; + int rc; + + util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); +@@ -1864,6 +1986,14 @@ static int command_validate(void) + vp_tok_avail = 1; + } + ++ rc = get_master_key_verification_pattern((u8 *)key, keysize, ++ &mkvp, g.verbose); ++ if (rc != 0) { ++ warnx("Failed to get the master key verification pattern: %s", ++ strerror(-rc)); ++ goto out; ++ } ++ + printf("Validation of secure volume key of device '%s':\n", g.pos_arg); + printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); + printf(" Secure key size: %lu bytes\n", keysize); +@@ -1871,11 +2001,12 @@ static int command_validate(void) + keysize > SECURE_KEY_SIZE ? "Yes" : "No"); + if (is_valid) { + printf(" Clear key size: %lu bits\n", clear_keysize); +- printf(" Enciphered with: %s CCA master key\n", +- is_old_mk ? "OLD" : "CURRENT"); ++ printf(" Enciphered with: %s CCA master key (MKVP: " ++ "%016llx)\n", is_old_mk ? "OLD" : "CURRENT", mkvp); + } else { + printf(" Clear key size: (unknown)\n"); +- printf(" Enciphered with: (unknown)\n"); ++ printf(" Enciphered with: (unknown, MKVP: %016llx)\n", ++ mkvp); + } + if (vp_tok_avail) + print_verification_pattern(vp_tok.verification_pattern); +@@ -2014,12 +2145,13 @@ static int command_setkey(void) + util_asprintf(&msg, "The secure key in file '%s' is " + "enciphered with the CCA master key in the OLD " + "master key register. Do you want to set this " +- "key as the new volume key anyway?", ++ "key as the new volume key anyway [y/N]?", + g.master_key_file); + util_print_indented(msg, 0); + free(msg); + + if (!prompt_for_yes()) { ++ warnx("Device '%s' is left unchanged", g.pos_arg); + rc = -EINVAL; + goto out; + } +@@ -2078,12 +2210,13 @@ static int command_setkey(void) + "be correct. You will lose all data on the " + "volume if you set the wrong volume key!\n" + "Are you sure that the key in file '%s' is the " +- "correct volume key for volume '%s'?", ++ "correct volume key for volume '%s' [y/N]?", + g.master_key_file, g.pos_arg); + util_print_indented(msg, 0); + free(msg); + + if (!prompt_for_yes()) { ++ warnx("Device '%s' is left unchanged", g.pos_arg); + rc = -EINVAL; + goto out; + } +@@ -2204,6 +2337,12 @@ int main(int argc, char *argv[]) + if (c == -1) + break; + switch (c) { ++ case 'N': ++ g.tonew = 1; ++ break; ++ case 'O': ++ g.fromold = 1; ++ break; + case 'c': + g.complete = 1; + break; +@@ -2286,8 +2425,7 @@ int main(int argc, char *argv[]) + } + + if (command->need_cca_library) { +- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, +- g.verbose); ++ rc = load_cca_library(&g.cca, g.verbose); + if (rc != 0) { + rc = EXIT_FAILURE; + goto out; +@@ -2329,8 +2467,8 @@ int main(int argc, char *argv[]) + rc = command->function(); + + out: +- if (g.lib_csulcca) +- dlclose(g.lib_csulcca); ++ if (g.cca.lib_csulcca) ++ dlclose(g.cca.lib_csulcca); + if (g.pkey_fd >= 0) + close(g.pkey_fd); + if (g.cd) +diff --git a/zkey/zkey.c b/zkey/zkey.c +index 2ecbb90..a0dbc0b 100644 +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -27,9 +27,11 @@ + #include "lib/util_prg.h" + #include "lib/zt_common.h" + ++#include "cca.h" + #include "keystore.h" + #include "misc.h" + #include "pkey.h" ++#include "utils.h" + + /* + * Program configuration +@@ -80,8 +82,7 @@ static struct zkey_globals { + bool force; + bool open; + bool format; +- void *lib_csulcca; +- t_CSNBKTC dll_CSNBKTC; ++ struct cca_lib cca; + int pkey_fd; + struct keystore *keystore; + } g = { +@@ -832,7 +833,7 @@ static struct zkey_command zkey_commands[] = { + .need_pkey_device = 1, + .short_desc = "Validate an existing secure AES key", + .long_desc = "Validate an existing secure AES key that is " +- "either contained in SECURE-KEY-FILE or is stored" ++ "either contained in SECURE-KEY-FILE or is stored " + "in the repository and print information about " + "the key", + .has_options = 1, +@@ -1060,6 +1061,8 @@ static int command_generate_repository(void) + */ + static int command_generate(void) + { ++ int rc; ++ + if (g.pos_arg != NULL && g.name != NULL) { + warnx(" Option '--name|-N' is not valid for generating a key " + "outside of the repository"); +@@ -1067,7 +1070,7 @@ static int command_generate(void) + return EXIT_FAILURE; + } + if (g.apqns == NULL && g.noapqncheck) { +- warnx("Option '--noapqncheck' is only valid together with " ++ warnx("Option '--no-apqn-check' is only valid together with " + "the '--apqns|-a' option"); + util_prg_print_parse_error(); + return EXIT_FAILURE; +@@ -1088,7 +1091,7 @@ static int command_generate(void) + return EXIT_FAILURE; + } + if (g.noapqncheck) { +- warnx("Option '--noapqncheck' is not valid for " ++ warnx("Option '--no-apqn-check' is not valid for " + "generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; +@@ -1100,6 +1103,14 @@ static int command_generate(void) + return EXIT_FAILURE; + } + ++ rc = cross_check_apqns(NULL, 0, true, g.verbose); ++ if (rc == -EINVAL) ++ return EXIT_FAILURE; ++ if (rc != 0 && rc != -ENOTSUP) { ++ warnx("Your master key setup is improper"); ++ return EXIT_FAILURE; ++ } ++ + return g.clearkeyfile ? command_generate_clear() + : command_generate_random(); + } +@@ -1117,7 +1128,9 @@ static int command_reencipher_file(void) + { + size_t secure_key_size; + int rc, is_old_mk; ++ int selected = 1; + u8 *secure_key; ++ u64 mkvp; + + if (g.name != NULL) { + warnx("Option '--name|-N' is not valid for " +@@ -1163,6 +1176,15 @@ static int command_reencipher_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 (!g.fromold && !g.tonew) { + /* Autodetect reencipher option */ + if (is_old_mk) { +@@ -1194,13 +1216,28 @@ static int command_reencipher_file(void) + pr_verbose("Secure key will be re-enciphered from OLD to the " + "CURRENT CCA master key"); + +- rc = key_token_change(g.dll_CSNBKTC, +- secure_key, secure_key_size, ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_OLD_MKVP, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ warnx("No APQN found that is suitable for " ++ "re-enciphering the secure AES volume key"); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = key_token_change(&g.cca, secure_key, secure_key_size, + METHOD_OLD_TO_CURRENT, + g.verbose); + if (rc != 0) { + warnx("Re-encipher from OLD to CURRENT CCA " +- "master key has failed"); ++ "master key has failed\n"); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); + rc = EXIT_FAILURE; + goto out; + } +@@ -1209,12 +1246,30 @@ static int command_reencipher_file(void) + pr_verbose("Secure key will be re-enciphered from CURRENT " + "to the NEW CCA master key"); + +- rc = key_token_change(g.dll_CSNBKTC, +- secure_key, secure_key_size, ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP | ++ FLAG_SEL_CCA_NEW_MUST_BE_SET, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ util_print_indented("No APQN found that is suitable " ++ "for re-enciphering this secure " ++ "AES key and has the NEW master " ++ "key loaded", 0); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = key_token_change(&g.cca, secure_key, secure_key_size, + METHOD_CURRENT_TO_NEW, g.verbose); + if (rc != 0) { + warnx("Re-encipher from CURRENT to NEW CCA " +- "master key has failed"); ++ "master key has failed\n"); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); + rc = EXIT_FAILURE; + goto out; + } +@@ -1270,7 +1325,7 @@ static int command_reencipher_repository(void) + + rc = keystore_reencipher_key(g.keystore, g.name, g.apqns, g.fromold, + g.tonew, g.inplace, g.staged, g.complete, +- g.pkey_fd, g.dll_CSNBKTC); ++ g.pkey_fd, &g.cca); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1302,6 +1357,7 @@ static int command_validate_file(void) + size_t clear_key_size; + u8 *secure_key; + int is_old_mk; ++ u64 mkvp; + int rc; + + if (g.name != NULL) { +@@ -1317,7 +1373,7 @@ static int command_validate_file(void) + return EXIT_FAILURE; + } + if (g.noapqncheck) { +- warnx("Option '--noapqncheck' is not valid for " ++ warnx("Option '--no-apqn-check' is not valid for " + "validating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; +@@ -1348,19 +1404,37 @@ 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; ++ } ++ + printf("Validation of secure key in file '%s':\n", g.pos_arg); + printf(" Status: Valid\n"); + printf(" Secure key size: %lu bytes\n", secure_key_size); + printf(" Clear key size: %lu bits\n", clear_key_size); + printf(" XTS type key: %s\n", + secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No"); +- printf(" Enciphered with: %s CCA master key\n", +- is_old_mk ? "OLD" : "CURRENT"); ++ printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n", ++ is_old_mk ? "OLD" : "CURRENT", 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, 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: + free(secure_key); + return rc; +@@ -1375,13 +1449,6 @@ static int command_validate_repository(void) + { + int rc; + +- if (g.apqns == NULL && g.noapqncheck) { +- warnx("Option '--noapqncheck' is only valid together with " +- "the '--apqns|-a' option"); +- util_prg_print_parse_error(); +- return EXIT_FAILURE; +- } +- + rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck, + g.pkey_fd); + +@@ -1421,7 +1488,7 @@ static int command_import(void) + g.sector_size = 0; + + if (g.apqns == NULL && g.noapqncheck) { +- warnx("Option '--noapqncheck' is only valid together with " ++ warnx("Option '--no-apqn-check' is only valid together with " + "the '--apqns|-a' option"); + util_prg_print_parse_error(); + return EXIT_FAILURE; +@@ -1501,7 +1568,7 @@ static int command_change(void) + return EXIT_FAILURE; + } + if (g.apqns == NULL && g.noapqncheck) { +- warnx("Option '--noapqncheck' is only valid together with " ++ warnx("Option '--no-apqn-check' is only valid together with " + "the '--apqns|-a' option"); + util_prg_print_parse_error(); + return EXIT_FAILURE; +@@ -1874,8 +1941,7 @@ int main(int argc, char *argv[]) + } + + if (command->need_cca_library) { +- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, +- g.verbose); ++ rc = load_cca_library(&g.cca, g.verbose); + if (rc != 0) { + rc = EXIT_FAILURE; + goto out; +@@ -1894,8 +1960,8 @@ int main(int argc, char *argv[]) + rc = command->function(); + + out: +- if (g.lib_csulcca) +- dlclose(g.lib_csulcca); ++ if (g.cca.lib_csulcca) ++ dlclose(g.cca.lib_csulcca); + if (g.pkey_fd >= 0) + close(g.pkey_fd); + if (g.keystore) +-- +2.21.3 + + +From c5e94c1cf3b03d290752227d21540b8786df8131 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 13:23:55 +0100 +Subject: [PATCH 35/44] zkey: Add support for CCA AES CIPHER keys (#1719623) + +Description: With CCA 5 there is a new secure key type, the so called + variable length symmetric cipher key token. This token format + can hold AES keys with size 128, 192 and 256 bits together + with additional attributes cryptographic bound to the key + token. The attributes may limit the usage of the key, for + example restrict export or usability scope. So this key type + is considered to be even more secure than the traditional + secure key token. This key token type is also called "CCA + AES CIPHER key", where the formerly used key token is called + "CCA AES DATA key". + The zkey as well as the zkey-cryptsetup tools are enhanced + to support AES CIPHER keys. That is, zkey can manage AES DATA + keys, as well as AES CIPHER keys. The key type must be specified + at key generation time, the default is to generate AED DATA + keys. + +Upstream-ID: 9de85f42951e0b1a3d083363d7000b1950aebcd7 +Upstream-ID: 91c35543ca7fd25691487c61ec2e308f2903a6b8 +Upstream-ID: b47007b8ac8b446eb94b06e7ed3050b3df3e80e8 +Upstream-ID: 298fab68fee86cb9b1862d60ca274971d4c39638 +Upstream-ID: ddde3f354f3506521877a4e2a6082c4d597629cb +Upstream-ID: d4027e6506963fbf995992e32490d56a6f7ea587 +Upstream-ID: 663d362ff3b1036476bfce9e2563272bab087013 +Upstream-ID: b56c74fe7b6100b9a2ef17b8847cd850309cf487 +Upstream-ID: 0fab6bdf2aa01e093f8a4f3d86c9183889a587fe +Upstream-ID: 560b672bfad3c7bb6631ed4e1676a8b2c836e030 +Upstream-ID: b7bb90c552f9b62c0b4ddc1295e76769149ee6bb +Upstream-ID: b0cc0e47378de9cd82b0cd14228b26be4d615ffc +Upstream-ID: 7d4b1e18b6195f48414f42b4655f900872fed1e7 +Upstream-ID: e7d79d5c5c0928c1bdbd6b669a6e70b8fd3352a5 +Upstream-ID: 7fede7021ece58b9960532e17d963f844fe0de02 +Upstream-ID: 0d9e42264db9935e28f663802c5b95795af79160 +Upstream-ID: a86e41a51827b524c5f88db5e24282166df9b3c8 +Upstream-ID: bc987c8d18ddeb6fec46113a7fe7588555b592e7 +Upstream-ID: 9894e391ef26fd31e2995f19965b7ce5bf006caa +--- + zkey/cca.c | 385 ++++++++++- + zkey/cca.h | 51 ++ + zkey/keystore.c | 605 +++++++++++++----- + zkey/keystore.h | 11 +- + zkey/pkey.c | 1385 ++++++++++++++++++++++++++++++++++------ + zkey/pkey.h | 193 +++++- + zkey/utils.c | 104 ++- + zkey/utils.h | 8 +- + zkey/zkey-cryptsetup.c | 44 +- + zkey/zkey.1 | 142 +++- + zkey/zkey.c | 308 ++++++++- + 11 files changed, 2809 insertions(+), 427 deletions(-) + +diff --git a/zkey/cca.c b/zkey/cca.c +index 2669c75..01f7bfd 100644 +--- a/zkey/cca.c ++++ b/zkey/cca.c +@@ -55,6 +55,14 @@ static void print_CCA_error(int return_code, int reason_code) + warnx("The secure key has a CCA master key " + "verification pattern that is not valid"); + break; ++ case 90: ++ warnx("The operation has been rejected due to access " ++ "control checking"); ++ break; ++ case 2143: ++ warnx("The operation has been rejected due to key " ++ "export restrictions of the secure key"); ++ break; + } + break; + case 12: +@@ -142,6 +150,9 @@ int load_cca_library(struct cca_lib *cca, bool verbose) + /* Get the Key Token Change function */ + cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC"); + ++ /* Get the Key Token Change 2 function */ ++ cca->dll_CSNBKTC2 = (t_CSNBKTC2)dlsym(cca->lib_csulcca, "CSNBKTC2"); ++ + /* Get the Cryptographic Facility Query function */ + cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ"); + +@@ -151,11 +162,20 @@ int load_cca_library(struct cca_lib *cca, bool verbose) + /* Cryptographic Resource Deallocate function */ + cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD"); + ++ /* Get the Key Translate 2 function */ ++ cca->dll_CSNBKTR2 = (t_CSNBKTR2)dlsym(cca->lib_csulcca, "CSNBKTR2"); ++ ++ /* Get the Restrict Key Attribute function */ ++ cca->dll_CSNBRKA = (t_CSNBRKA)dlsym(cca->lib_csulcca, "CSNBRKA"); ++ + if (cca->dll_CSUACFV == NULL || + cca->dll_CSNBKTC == NULL || ++ cca->dll_CSNBKTC2 == NULL || + cca->dll_CSUACFQ == NULL || + cca->dll_CSUACRA == NULL || +- cca->dll_CSUACRD == NULL) { ++ cca->dll_CSUACRD == NULL || ++ cca->dll_CSNBKTR2 == NULL || ++ cca->dll_CSNBRKA == NULL) { + pr_verbose(verbose, "%s", dlerror()); + warnx("The command requires the IBM CCA Host Libraries and " + "Tools.\nFor the supported environments and downloads, " +@@ -187,10 +207,13 @@ int key_token_change(struct cca_lib *cca, + u8 *secure_key, unsigned int secure_key_size, + char *method, bool verbose) + { ++ struct aescipherkeytoken *cipherkey = ++ (struct aescipherkeytoken *)secure_key; + long exit_data_len = 0, rule_array_count; + unsigned char rule_array[2 * 8] = { 0, }; + unsigned char exit_data[4] = { 0, }; + long return_code, reason_code; ++ long key_token_length; + + util_assert(cca != NULL, "Internal error: cca is NULL"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); +@@ -202,33 +225,77 @@ int key_token_change(struct cca_lib *cca, + memcpy(rule_array + 8, "AES ", 8); + rule_array_count = 2; + +- cca->dll_CSNBKTC(&return_code, &reason_code, +- &exit_data_len, exit_data, +- &rule_array_count, rule_array, +- secure_key); +- +- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " +- "return_code: %ld, reason_code: %ld", method, return_code, +- reason_code); +- if (return_code != 0) { +- print_CCA_error(return_code, reason_code); +- return -EIO; +- } +- +- if (secure_key_size == 2 * SECURE_KEY_SIZE) { ++ if (is_cca_aes_data_key(secure_key, secure_key_size)) { + cca->dll_CSNBKTC(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, +- secure_key + SECURE_KEY_SIZE); ++ secure_key); + + pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " + "returned: return_code: %ld, reason_code: %ld", + method, return_code, reason_code); ++ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { ++ key_token_length = cipherkey->length; ++ cca->dll_CSNBKTC2(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &key_token_length, ++ (unsigned char *)cipherkey); ++ ++ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with '%s' " ++ "returned: return_code: %ld, reason_code: %ld", ++ method, return_code, reason_code); ++ ++ pr_verbose(verbose, "key_token_length: %lu", key_token_length); ++ } else { ++ warnx("Invalid key type specified"); ++ return -EINVAL; ++ } ++ ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ if (is_xts_key(secure_key, secure_key_size)) { ++ if (is_cca_aes_data_key(secure_key, secure_key_size)) { ++ cca->dll_CSNBKTC(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ secure_key + AESDATA_KEY_SIZE); ++ ++ pr_verbose(verbose, "CSNBKTC (Key Token Change) with " ++ "'%s' returned: return_code: %ld, " ++ "reason_code: %ld", method, return_code, ++ reason_code); ++ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { ++ cipherkey = (struct aescipherkeytoken *)(secure_key + ++ AESCIPHER_KEY_SIZE); ++ key_token_length = cipherkey->length; ++ cca->dll_CSNBKTC2(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &key_token_length, ++ (unsigned char *)cipherkey); ++ ++ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with " ++ "'%s' returned: return_code: %ld, " ++ "reason_code: %ld", method, return_code, ++ reason_code); ++ ++ pr_verbose(verbose, "key_token_length: %lu", ++ key_token_length); ++ } else { ++ warnx("Invalid key type specified"); ++ return -EINVAL; ++ } ++ + if (return_code != 0) { + print_CCA_error(return_code, reason_code); + return -EIO; + } + } ++ + return 0; + } + +@@ -422,6 +489,58 @@ static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9], + return 0; + } + ++/** ++ * Queries the firmware version of the current CCA adapter ++ * ++ * @param[in] cca the CCA library structure ++ * @param[out] version the struct where the version is returned ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. ++ */ ++static int get_cca_adapter_version(struct cca_lib *cca, ++ struct cca_version *version, ++ bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count, verb_data_length = 0; ++ unsigned char rule_array[6 * 8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ char version_data[9]; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ ++ memset(rule_array, 0, sizeof(rule_array)); ++ memcpy(rule_array, "STATCCA ", 8); ++ rule_array_count = 1; ++ ++ cca->dll_CSUACFQ(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &verb_data_length, NULL); ++ ++ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " ++ "return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ memcpy(version_data, rule_array+3*8, 8); ++ version_data[8] = '\0'; ++ ++ pr_verbose(verbose, "CCA firmware version string: %s", version_data); ++ ++ if (sscanf((char *)version_data, "%u.%u.%uz", &version->ver, ++ &version->rel, &version->mod) != 3) { ++ warnx("CCA formware version is invalid: %s", version_data); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /** + * Selects the specified APQN to be used for the CCA host library. + * +@@ -626,3 +745,237 @@ void print_msg_for_cca_envvars(const char *key_name) + util_print_indented(msg, 0); + free(msg); + } ++ ++/* ++ * Convert a secure key of type CCA-AESDATA into a secure key of type ++ * CCA-AESCIPHER. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] input_key the secure key to convert ++ * @param[in] input_key_size the size of the secure key to convert ++ * @param[in] output_key buffer for the converted secure key ++ * @param[in/out] output_key_size on input: size of the output buffer. ++ * on exit: size of the converted secure key ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. ++ */ ++int convert_aes_data_to_cipher_key(struct cca_lib *cca, ++ u8 *input_key, unsigned int input_key_size, ++ u8 *output_key, ++ unsigned int *output_key_size, ++ bool verbose) ++{ ++ long input_token_size, output_token_size, zero = 0; ++ long exit_data_len = 0, rule_array_count = 0; ++ unsigned char *input_token, *output_token; ++ unsigned char rule_array[8 * 2] = { 0, }; ++ unsigned char null_token[64] = { 0, }; ++ long null_token_len = sizeof(null_token); ++ unsigned char exit_data[4] = { 0, }; ++ struct aescipherkeytoken *cipherkey; ++ long return_code, reason_code; ++ struct cca_version version; ++ unsigned char buffer[800]; ++ int rc; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ util_assert(input_key != NULL, "Internal error: input_key is NULL"); ++ util_assert(output_key != NULL, "Internal error: output_key is NULL"); ++ util_assert(output_key_size != NULL, ++ "Internal error: output_key_size is NULL"); ++ ++ if (is_cca_aes_cipher_key(input_key, input_key_size)) { ++ warnx("Invalid key-type specified"); ++ return -EINVAL; ++ } ++ ++ if (*output_key_size < (is_xts_key(input_key, input_key_size) ? ++ 2 * AESCIPHER_KEY_SIZE : AESCIPHER_KEY_SIZE)) ++ return -EINVAL; ++ ++ /* ++ * We need a CCA firmware version 6.3.27 or later to support ++ * conversion of secure keys that are exportable to CPACF protected keys ++ */ ++ rc = get_cca_adapter_version(cca, &version, verbose); ++ if (rc != 0) ++ return rc; ++ if (version.ver < 6 || ++ (version.ver == 6 && version.rel < 3) || ++ (version.ver == 6 && version.rel < 3 && version.mod < 27)) { ++ util_print_indented("The used CCA firmware version does not " ++ "support converting a secure key that can " ++ "be used with the PAES cipher. The " ++ "required CCA firmware version is 6.3.27 " ++ "or later. For the supported environments " ++ "and updates, see: " CCA_WEB_PAGE, 0); ++ return -ENOTSUP; ++ } ++ ++ input_token = input_key; ++ input_token_size = AESDATA_KEY_SIZE; ++ output_token = buffer; ++ output_token_size = sizeof(buffer); ++ memset(buffer, 0, sizeof(buffer)); ++ ++ memcpy(rule_array, "AES ", 8); ++ memcpy(rule_array + 8, "REFORMAT", 8); ++ rule_array_count = 2; ++ ++ cca->dll_CSNBKTR2(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &input_token_size, input_token, ++ &null_token_len, null_token, ++ &zero, NULL, ++ &output_token_size, output_token); ++ ++ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) " ++ "returned: return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ pr_verbose(verbose, "output_token_size: %lu", output_token_size); ++ if (output_token_size > (long)AESCIPHER_KEY_SIZE) { ++ pr_verbose(verbose, "Output key token too large"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Check if the converted key allows export to CPACF protected key. ++ * If not, then the CCA host library or firmware code level is too low. ++ */ ++ cipherkey = (struct aescipherkeytoken *)buffer; ++ if ((cipherkey->kmf1 & 0x0800) == 0) { ++ util_print_indented("The used CCA firmware version does not " ++ "support converting a secure key that can " ++ "be used with the PAES cipher. The " ++ "required CCA firmware version is 6.3.27 " ++ "or later. For the supported environments " ++ "and updates, see: " CCA_WEB_PAGE, 0); ++ return -ENOTSUP; ++ } ++ ++ memset(output_key, 0, *output_key_size); ++ memcpy(output_key, buffer, output_token_size); ++ *output_key_size = AESCIPHER_KEY_SIZE; ++ ++ if (is_xts_key(input_key, input_key_size)) { ++ input_token = input_key + AESDATA_KEY_SIZE; ++ input_token_size = AESDATA_KEY_SIZE; ++ output_token = buffer; ++ output_token_size = sizeof(buffer); ++ memset(buffer, 0, sizeof(buffer)); ++ ++ cca->dll_CSNBKTR2(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &input_token_size, input_token, ++ &null_token_len, null_token, ++ &zero, NULL, ++ &output_token_size, output_token); ++ ++ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) " ++ "returned: return_code: %ld, reason_code: %ld", ++ return_code, reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ pr_verbose(verbose, "output_token_size: %lu", ++ output_token_size); ++ if (output_token_size > (long)AESCIPHER_KEY_SIZE) { ++ pr_verbose(verbose, "Output key token too large"); ++ return -EINVAL; ++ } ++ ++ memcpy(output_key + AESCIPHER_KEY_SIZE, buffer, ++ output_token_size); ++ *output_key_size += AESCIPHER_KEY_SIZE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Restrict the exportability of an AES CIPHER key. It restricts export by means ++ * of NOEX-AES, NOEX-DES, NOEX-RSA, NOEX-SYM, NOEXUASY, NOEXAASY, NOEX-RAW ++ * keywords. ++ * When this function is called with an AES DATA key, it does nothing and ++ * returns 0. AES DATA keys can not be export restricted. ++ * ++ * @param[in] cca the CCA library structure ++ * @param[in] secure_key the secure key to restrict ++ * @param[in] secure_key_size the size of the secure key to restrict ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error. ++ */ ++int restrict_key_export(struct cca_lib *cca, u8 *secure_key, ++ unsigned int secure_key_size, bool verbose) ++{ ++ struct aescipherkeytoken *cipherkey = ++ (struct aescipherkeytoken *)secure_key; ++ long exit_data_len = 0, rule_array_count = 0; ++ unsigned char rule_array[8 * 8] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ long token_length, zero = 0; ++ ++ util_assert(cca != NULL, "Internal error: cca is NULL"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ ++ if (!is_cca_aes_cipher_key(secure_key, secure_key_size)) ++ return 0; ++ ++ memcpy(rule_array, "AES ", 8); ++ memcpy(rule_array + 8, "NOEX-AES", 8); ++ memcpy(rule_array + 16, "NOEX-DES", 8); ++ memcpy(rule_array + 24, "NOEX-RSA", 8); ++ memcpy(rule_array + 32, "NOEX-SYM", 8); ++ memcpy(rule_array + 40, "NOEXUASY", 8); ++ memcpy(rule_array + 48, "NOEXAASY", 8); ++ memcpy(rule_array + 56, "NOEX-RAW", 8); ++ rule_array_count = 8; ++ ++ token_length = cipherkey->length; ++ cca->dll_CSNBRKA(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &token_length, (unsigned char *)secure_key, ++ &zero, NULL, &zero, NULL, &zero, NULL); ++ ++ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) " ++ "returned: return_code: %ld, reason_code: %ld", return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ if (is_xts_key(secure_key, secure_key_size)) { ++ cipherkey = (struct aescipherkeytoken *)(secure_key + ++ AESCIPHER_KEY_SIZE); ++ token_length = cipherkey->length; ++ cca->dll_CSNBRKA(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ &token_length, (unsigned char *)cipherkey, ++ &zero, NULL, &zero, NULL, &zero, NULL); ++ ++ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) " ++ "returned: return_code: %ld, reason_code: %ld", ++ return_code, reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ } ++ ++ return 0; ++} +diff --git a/zkey/cca.h b/zkey/cca.h +index a79e2ee..2b248ec 100644 +--- a/zkey/cca.h ++++ b/zkey/cca.h +@@ -25,6 +25,15 @@ typedef void (*t_CSNBKTC)(long *return_code, + unsigned char *rule_array, + unsigned char *key_identifier); + ++typedef void (*t_CSNBKTC2)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *key_identifier_length, ++ unsigned char *key_identifier); ++ + typedef void (*t_CSUACFV)(long *return_code, + long *reason_code, + long *exit_data_length, +@@ -59,6 +68,36 @@ typedef void (*t_CSUACRD)(long *return_code, + long *ressource_name_length, + unsigned char *ressource_name); + ++typedef void (*t_CSNBKTR2)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *input_key_token_length, ++ unsigned char *input_key_token, ++ long *input_KEK_key_identifier_length, ++ unsigned char *input_KEK_key_identifier, ++ long *output_KEK_key_identifier_length, ++ unsigned char *output_KEK_key_identifier, ++ long *output_key_token_length, ++ unsigned char *output_key_token); ++ ++typedef void (*t_CSNBRKA)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ long *key_identifier_length, ++ unsigned char *key_identifier, ++ long *ey_encrypting_key_identifier_length, ++ unsigned char *ey_encrypting_key_identifier, ++ long *opt_parameter1_length, ++ unsigned char *opt_parameter1, ++ long *opt_parameter2_length, ++ unsigned char *opt_parameter2); ++ + struct cca_version { + unsigned int ver; + unsigned int rel; +@@ -68,10 +107,13 @@ struct cca_version { + struct cca_lib { + void *lib_csulcca; + t_CSNBKTC dll_CSNBKTC; ++ t_CSNBKTC2 dll_CSNBKTC2; + t_CSUACFV dll_CSUACFV; + t_CSUACFQ dll_CSUACFQ; + t_CSUACRA dll_CSUACRA; + t_CSUACRD dll_CSUACRD; ++ t_CSNBKTR2 dll_CSNBKTR2; ++ t_CSNBRKA dll_CSNBRKA; + struct cca_version version; + }; + +@@ -92,4 +134,13 @@ int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, + + void print_msg_for_cca_envvars(const char *key_name); + ++int convert_aes_data_to_cipher_key(struct cca_lib *cca, ++ u8 *input_key, unsigned int input_key_size, ++ u8 *output_key, ++ unsigned int *output_key_size, ++ bool verbose); ++ ++int restrict_key_export(struct cca_lib *cca, u8 *secure_key, ++ unsigned int secure_key_size, bool verbose); ++ + #endif +diff --git a/zkey/keystore.c b/zkey/keystore.c +index 957ebb0..af67721 100644 +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -70,13 +70,12 @@ struct key_filenames { + #define DEFAULT_VOLUME_TYPE VOLUME_TYPE_PLAIN + #endif + +-#define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0) +- + #define REC_KEY "Key" + #define REC_DESCRIPTION "Description" + #define REC_SEC_KEY_SIZE "Secure key size" + #define REC_CLR_KEY_SIZE "Clear key size" + #define REC_XTS "XTS type key" ++#define REC_KEY_TYPE "Key type" + #define REC_VOLUMES "Volumes" + #define REC_APQNS "APQNs" + #define REC_KEY_FILE "Key file name" +@@ -313,6 +312,41 @@ static char *_keystore_get_volume_type(struct properties *properties) + return type; + } + ++/** ++ * Returns the key type contained in the properties. If no key type ++ * property is contained, then 'CCA-AESDATA' is assumed (for backward ++ * compatibility). ++ * ++ * @returns a string containing the key type. Must be freed by the caller. ++ */ ++static char *_keystore_get_key_type(struct properties *properties) ++{ ++ char *type; ++ ++ type = properties_get(properties, PROP_NAME_KEY_TYPE); ++ if (type == NULL) ++ type = util_strdup(KEY_TYPE_CCA_AESDATA); ++ ++ return type; ++} ++ ++/** ++ * Checks if the key type is supported. ++ * ++ * @param[in] key_type the key type ++ * ++ * @returns 1 if the key type is valid, 0 otherwise ++ */ ++static int _keystore_valid_key_type(const char *key_type) ++{ ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) ++ return 1; ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) ++ return 1; ++ ++ return 0; ++} ++ + /** + * Prints a message followed by a list of associated volumes, if volumes are + * associated and the volume-type matches (if specified) +@@ -816,6 +850,33 @@ static int _keystore_match_volume_type_property(struct properties *properties, + return rc; + } + ++/** ++ * Checks if the key type property matches the specified key type. ++ * If the properties do not contain a key type property, then the default ++ * key type is assumed. ++ * ++ * @param[in] properties a properties object ++ * @param[in] key_type the key type to match. Can be NULL. In this case ++ * it always matches. ++ * ++ * @returns 1 for a match, 0 for not matched ++ */ ++static int _keystore_match_key_type_property(struct properties *properties, ++ const char *key_type) ++{ ++ char *type; ++ int rc = 0; ++ ++ if (key_type == NULL) ++ return 1; ++ ++ type = _keystore_get_key_type(properties); ++ if (strcasecmp(type, key_type) == 0) ++ rc = 1; ++ ++ free(type); ++ return rc; ++} + + /** + * Checks if a key name matches a name filter +@@ -881,6 +942,7 @@ typedef int (*process_key_t)(struct keystore *keystore, + * mutliple APQN filters separated by commas. + * NULL means no APQN filter. + * @param[in] volume_type If not NULL, specifies the volume type. ++ * @param[in] key_type The key type. NULL means no key type filter. + * @param[in] process_func the callback function called for a matching key + * @param[in/out] process_private private data passed to the process_func + * +@@ -893,6 +955,7 @@ static int _keystore_process_filtered(struct keystore *keystore, + const char *volume_filter, + const char *apqn_filter, + const char *volume_type, ++ const char *key_type, + process_key_t process_func, + void *process_private) + { +@@ -984,6 +1047,15 @@ static int _keystore_process_filtered(struct keystore *keystore, + goto free_prop; + } + ++ rc = _keystore_match_key_type_property(key_props, ++ key_type); ++ if (rc == 0) { ++ pr_verbose(keystore, ++ "Key '%s' filtered out due to key type", ++ name); ++ goto free_prop; ++ } ++ + rc = process_func(keystore, name, key_props, &file_names, + process_private); + if (rc != 0) { +@@ -1192,7 +1264,7 @@ static int _keystore_volume_check(const char *volume, bool remove, bool set, + + info->set = set; + rc = _keystore_process_filtered(info->keystore, NULL, info->volume, +- NULL, NULL, ++ NULL, NULL, NULL, + _keystore_volume_check_process, info); + out: + free((void *)info->volume); +@@ -1368,7 +1440,7 @@ static int _keystore_generate_verification_pattern(struct keystore *keystore, + if (key == NULL) + return -EIO; + +- rc = generate_key_verification_pattern((const char *)key, key_size, ++ rc = generate_key_verification_pattern(key, key_size, + vp, vp_len, keystore->verbose); + + free(key); +@@ -1453,10 +1525,6 @@ static int _keystore_set_default_properties(struct properties *key_props) + { + int rc; + +- rc = properties_set(key_props, PROP_NAME_KEY_TYPE, "CCA-AESDATA"); +- if (rc != 0) +- return rc; +- + rc = properties_set(key_props, PROP_NAME_CIPHER, "paes"); + if (rc != 0) + return rc; +@@ -1491,6 +1559,7 @@ static int _keystore_set_default_properties(struct properties *key_props) + * the sector size is not specified and the system + * default is used. + * @param[in] volume_type the type of volume ++ * @param[in] key_type the type of the key + */ + static int _keystore_create_info_file(struct keystore *keystore, + const char *name, +@@ -1499,7 +1568,8 @@ static int _keystore_create_info_file(struct keystore *keystore, + const char *volumes, const char *apqns, + bool noapqncheck, + size_t sector_size, +- const char *volume_type) ++ const char *volume_type, ++ const char *key_type) + { + struct volume_check vol_check = { .keystore = keystore, .name = name, + .set = 0 }; +@@ -1521,6 +1591,12 @@ static int _keystore_create_info_file(struct keystore *keystore, + goto out; + } + ++ rc = properties_set2(key_props, PROP_NAME_KEY_TYPE, key_type, true); ++ if (rc != 0) { ++ warnx("Invalid characters in key-type"); ++ goto out; ++ } ++ + rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, + volumes != NULL ? volumes : "", + "volume", _keystore_volume_check, +@@ -1590,52 +1666,6 @@ out: + return rc; + } + +-/** +- * Extracts an online card/domain pair from the specified APQns. If none of the +- * specified APQNs are online, then -ENODEV is returned. +- * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero. +- */ +-static int _keystore_get_card_domain(const char *apqns, unsigned int *card, +- unsigned int *domain) +-{ +- struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 }; +- char **apqn_list; +- char *normalized = NULL; +- int rc = 0; +- int i; +- +- *card = AUTOSELECT; +- *domain = AUTOSELECT; +- +- if (apqns == NULL) +- return 0; +- +- apqn_list = str_list_split(apqns); +- if (apqn_list[0] == NULL) +- goto out; +- +- for (i = 0; apqn_list[i] != NULL; i++) { +- rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized, +- &apqn_check); +- if (normalized != NULL) +- free(normalized); +- if (rc == -EINVAL) +- goto out; +- if (rc != 0) +- continue; +- +- if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2) +- goto found; +- } +- +- warnx("None of the specified APQNs is online or of type CCA"); +- rc = -ENODEV; +-found: +-out: +- str_list_free_string_array(apqn_list); +- return rc; +-} +- + /** + * Generates a secure key by random and adds it to the key store + * +@@ -1658,6 +1688,7 @@ out: + * clear key contained in the file denoted here. + * if NULL, the secure key is generated by random. + * @param[in] volume_type the type of volume ++ * @param[in] key_type the type of the key + * @param[in] pkey_fd the file descriptor of /dev/pkey + * + * @returns 0 for success or a negative errno in case of an error +@@ -1667,15 +1698,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + const char *apqns, bool noapqncheck, + size_t sector_size, size_t keybits, bool xts, + const char *clear_key_file, const char *volume_type, +- int pkey_fd) ++ const char *key_type, int pkey_fd) + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; +- unsigned int card, domain; ++ char **apqn_list = NULL; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(key_type != NULL, "Internal error: key_type is NULL"); ++ ++ if (!_keystore_valid_key_type(key_type)) { ++ warnx("Invalid key-type specified"); ++ return -EINVAL; ++ } + + rc = _keystore_get_key_filenames(keystore, name, &file_names); + if (rc != 0) +@@ -1685,7 +1722,9 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + if (rc != 0) + goto out_free_key_filenames; + +- rc = cross_check_apqns(apqns, 0, true, keystore->verbose); ++ rc = cross_check_apqns(apqns, 0, ++ get_min_card_level_for_keytype(key_type), true, ++ keystore->verbose); + if (rc == -EINVAL) + goto out_free_key_filenames; + if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { +@@ -1693,20 +1732,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + goto out_free_key_filenames; + } + +- rc = _keystore_get_card_domain(apqns, &card, &domain); +- if (rc != 0) +- goto out_free_key_filenames; ++ if (apqns != NULL) ++ apqn_list = str_list_split(apqns); + + if (clear_key_file == NULL) + rc = generate_secure_key_random(pkey_fd, + file_names.skey_filename, +- keybits, xts, card, domain, ++ keybits, xts, key_type, ++ (const char **)apqn_list, + keystore->verbose); + else + rc = generate_secure_key_clear(pkey_fd, + file_names.skey_filename, + keybits, xts, clear_key_file, +- card, domain, ++ key_type, ++ (const char **)apqn_list, + keystore->verbose); + if (rc != 0) + goto out_free_props; +@@ -1717,7 +1757,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- noapqncheck, sector_size, volume_type); ++ noapqncheck, sector_size, volume_type, ++ key_type); + if (rc != 0) + goto out_free_props; + +@@ -1727,6 +1768,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + file_names.info_filename); + + out_free_props: ++ if (apqn_list != NULL) ++ str_list_free_string_array(apqn_list); + if (key_props != NULL) + properties_free(key_props); + if (rc != 0) +@@ -1758,17 +1801,21 @@ out_free_key_filenames: + * 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] cca the CCA 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) ++ const char *import_file, const char *volume_type, ++ struct cca_lib *cca) + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; + size_t secure_key_size; ++ const char *key_type; ++ int selected = 1; + u8 *secure_key; + u64 mkvp; + int rc; +@@ -1792,6 +1839,14 @@ int keystore_import_key(struct keystore *keystore, const char *name, + 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; ++ } ++ + rc = get_master_key_verification_pattern(secure_key, secure_key_size, + &mkvp, keystore->verbose); + if (rc != 0) { +@@ -1800,7 +1855,9 @@ int keystore_import_key(struct keystore *keystore, const char *name, + goto out_free_key; + } + +- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); ++ rc = cross_check_apqns(apqns, mkvp, ++ get_min_card_level_for_keytype(key_type), true, ++ keystore->verbose); + if (rc == -EINVAL) + goto out_free_key; + if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { +@@ -1808,6 +1865,51 @@ int keystore_import_key(struct keystore *keystore, const char *name, + goto out_free_key; + } + ++ if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { ++ if (cca->lib_csulcca == NULL) { ++ rc = load_cca_library(cca, keystore->verbose); ++ if (rc != 0) ++ goto out_free_key; ++ } ++ ++ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP | ++ FLAG_SEL_CCA_MATCH_OLD_MKVP, ++ keystore->verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ warnx("No APQN found that is suitable for " ++ "working with the secure AES key '%s'", name); ++ rc = 0; ++ goto out_free_key; ++ } ++ ++ rc = restrict_key_export(cca, secure_key, secure_key_size, ++ keystore->verbose); ++ if (rc != 0) { ++ warnx("Failed to export-restrict the imported secure " ++ "key: %s", strerror(-rc)); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); ++ goto out_free_key; ++ } ++ ++ rc = check_aes_cipher_key(secure_key, secure_key_size); ++ if (rc != 0) { ++ warnx("The secure key to import might not be secure"); ++ printf("%s: Do you want to import it anyway [y/N]? ", ++ program_invocation_short_name); ++ if (!prompt_for_yes(keystore->verbose)) { ++ warnx("Operation aborted"); ++ rc = -ECANCELED; ++ goto out_free_key; ++ } ++ } ++ } ++ + rc = write_secure_key(file_names.skey_filename, secure_key, + secure_key_size, keystore->verbose); + free(secure_key); +@@ -1821,7 +1923,8 @@ int keystore_import_key(struct keystore *keystore, const char *name, + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- noapqncheck, sector_size, volume_type); ++ noapqncheck, sector_size, volume_type, ++ key_type); + if (rc != 0) + goto out_free_props; + +@@ -1886,8 +1989,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, + .nomsg = 0 }; + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; ++ char *apqns_prop, *key_type; + size_t secure_key_size; +- char *apqns_prop; + u8 *secure_key; + char temp[30]; + u64 mkvp; +@@ -1954,9 +2057,12 @@ int keystore_change_key(struct keystore *keystore, const char *name, + goto out; + + apqns_prop = properties_get(key_props, PROP_NAME_APQNS); +- rc = cross_check_apqns(apqns_prop, mkvp, true, +- keystore->verbose); ++ key_type = properties_get(key_props, PROP_NAME_KEY_TYPE); ++ rc = cross_check_apqns(apqns_prop, mkvp, ++ get_min_card_level_for_keytype(key_type), ++ true, keystore->verbose); + free(apqns_prop); ++ free(key_type); + if (rc == -ENOTSUP) + rc = 0; + if (rc != 0 && noapqncheck == 0) { +@@ -2140,6 +2246,7 @@ static struct util_rec *_keystore_setup_record(bool validation) + util_rec_def(rec, REC_CLR_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20, + REC_CLR_KEY_SIZE); + util_rec_def(rec, REC_XTS, UTIL_REC_ALIGN_LEFT, 3, REC_XTS); ++ util_rec_def(rec, REC_KEY_TYPE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_TYPE); + if (validation) + util_rec_def(rec, REC_MASTERKEY, UTIL_REC_ALIGN_LEFT, 54, + REC_MASTERKEY); +@@ -2165,7 +2272,7 @@ static void _keystore_print_record(struct util_rec *rec, + const char *name, + struct properties *properties, + bool validation, const char *skey_filename, +- size_t secure_key_size, ++ size_t secure_key_size, bool is_xts, + size_t clear_key_bitsize, bool valid, + bool is_old_mk, bool reenc_pending, u64 mkvp) + { +@@ -2178,6 +2285,7 @@ static void _keystore_print_record(struct util_rec *rec, + char *description; + char *volume_type; + char *reencipher; ++ char *key_type; + char *creation; + char *volumes; + char *change; +@@ -2212,6 +2320,7 @@ static void _keystore_print_record(struct util_rec *rec, + reencipher = properties_get(properties, PROP_NAME_REENC_TIME); + vp = properties_get(properties, PROP_NAME_KEY_VP); + volume_type = _keystore_get_volume_type(properties); ++ key_type = properties_get(properties, PROP_NAME_KEY_TYPE); + + util_rec_set(rec, REC_KEY, name); + if (validation) +@@ -2219,13 +2328,13 @@ static void _keystore_print_record(struct util_rec *rec, + util_rec_set(rec, REC_DESCRIPTION, + description != NULL ? description : ""); + util_rec_set(rec, REC_SEC_KEY_SIZE, "%lu bytes", secure_key_size); +- if (!validation || valid) ++ if ((!validation || valid) && clear_key_bitsize != 0) + util_rec_set(rec, REC_CLR_KEY_SIZE, "%lu bits", + clear_key_bitsize); + else + util_rec_set(rec, REC_CLR_KEY_SIZE, "(unknown)"); +- util_rec_set(rec, REC_XTS, +- IS_XTS(secure_key_size) ? "Yes" : "No"); ++ 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, +@@ -2290,6 +2399,8 @@ static void _keystore_print_record(struct util_rec *rec, + free(vp); + if (volume_type != NULL) + free(volume_type); ++ if (key_type != NULL) ++ free(key_type); + } + + struct validate_info { +@@ -2317,12 +2428,17 @@ static int _keystore_display_apqn_status(struct keystore *keystore, + { + int rc, warning = 0; + char *apqns; ++ char *key_type; + + apqns = properties_get(properties, PROP_NAME_APQNS); + if (apqns == NULL) + return 0; + +- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ key_type = properties_get(properties, PROP_NAME_KEY_TYPE); ++ rc = cross_check_apqns(apqns, mkvp, ++ get_min_card_level_for_keytype(key_type), true, ++ keystore->verbose); + if (rc != 0 && rc != -ENOTSUP) + warning = 1; + +@@ -2330,6 +2446,7 @@ static int _keystore_display_apqn_status(struct keystore *keystore, + printf("\n"); + + free(apqns); ++ free(key_type); + return warning; + } + /** +@@ -2395,8 +2512,10 @@ static int _keystore_process_validate(struct keystore *keystore, + void *private) + { + struct validate_info *info = (struct validate_info *)private; ++ char **apqn_list = NULL; + size_t clear_key_bitsize; + size_t secure_key_size; ++ char *apqns = NULL; + u8 *secure_key; + int is_old_mk; + int rc, valid; +@@ -2413,9 +2532,13 @@ static int _keystore_process_validate(struct keystore *keystore, + goto out; + } + ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ if (apqns != NULL) ++ apqn_list = str_list_split(apqns); ++ + rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, + &clear_key_bitsize, &is_old_mk, +- keystore->verbose); ++ (const char **)apqn_list, keystore->verbose); + if (rc != 0) { + valid = 0; + info->num_invalid++; +@@ -2433,6 +2556,7 @@ static int _keystore_process_validate(struct keystore *keystore, + + _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); +@@ -2453,6 +2577,10 @@ static int _keystore_process_validate(struct keystore *keystore, + info->num_warnings++; + + out: ++ if (apqns != NULL) ++ free(apqns); ++ if (apqn_list != NULL) ++ str_list_free_string_array(apqn_list); + if (rc != 0) + pr_verbose(keystore, "Failed to validate key '%s': %s", + name, strerror(-rc)); +@@ -2491,7 +2619,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter, + info.num_warnings = 0; + + rc = _keystore_process_filtered(keystore, name_filter, NULL, +- apqn_filter, NULL, ++ apqn_filter, NULL, NULL, + _keystore_process_validate, &info); + + util_rec_free(rec); +@@ -2669,7 +2797,9 @@ static int _keystore_process_reencipher(struct keystore *keystore, + struct reencipher_params params = info->params; + size_t clear_key_bitsize; + size_t secure_key_size; ++ char **apqn_list = NULL; + u8 *secure_key = NULL; ++ char *apqns = NULL; + char *out_file; + int is_old_mk; + char *temp; +@@ -2706,9 +2836,13 @@ static int _keystore_process_reencipher(struct keystore *keystore, + goto out; + } + ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ if (apqns != NULL) ++ apqn_list = str_list_split(apqns); ++ + rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, + &clear_key_bitsize, &is_old_mk, +- keystore->verbose); ++ (const char **)apqn_list, keystore->verbose); + if (rc != 0) { + if (params.complete) { + warnx("Key '%s' is not valid, re-enciphering is not " +@@ -2807,6 +2941,10 @@ static int _keystore_process_reencipher(struct keystore *keystore, + info->num_reenciphered++; + + out: ++ if (apqns != NULL) ++ free(apqns); ++ if (apqn_list != NULL) ++ str_list_free_string_array(apqn_list); + if (secure_key != NULL) + free(secure_key); + +@@ -2870,7 +3008,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, + info.num_skipped = 0; + + rc = _keystore_process_filtered(keystore, name_filter, NULL, +- apqn_filter, NULL, ++ apqn_filter, NULL, NULL, + _keystore_process_reencipher, &info); + + if (rc != 0) { +@@ -3090,7 +3228,6 @@ static int _keystore_prompt_for_remove(struct keystore *keystore, + struct key_filenames *file_names) + { + struct properties *key_prop; +- char str[20]; + char *msg; + int rc; + +@@ -3108,14 +3245,7 @@ static int _keystore_prompt_for_remove(struct keystore *keystore, + + printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name, + name); +- if (fgets(str, sizeof(str), stdin) == NULL) { +- rc = -EIO; +- goto out; +- } +- if (str[strlen(str) - 1] == '\n') +- str[strlen(str) - 1] = '\0'; +- pr_verbose(keystore, "Prompt reply: '%s'", str); +- if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) { ++ if (!prompt_for_yes(keystore->verbose)) { + warnx("Operation aborted"); + rc = -ECANCELED; + goto out; +@@ -3205,29 +3335,29 @@ static int _keystore_display_key(struct keystore *keystore, + void *private) + { + struct util_rec *rec = (struct util_rec *)private; +- struct secaeskeytoken *secure_key; +- size_t secure_key_size; ++ u8 *secure_key; ++ size_t secure_key_size, clear_key_bitsize = 0; + int rc = 0; + +- secure_key = (struct secaeskeytoken *) +- read_secure_key(file_names->skey_filename, ++ secure_key = read_secure_key(file_names->skey_filename, + &secure_key_size, keystore->verbose); + if (secure_key == NULL) + return -EIO; + +- if (secure_key_size < SECURE_KEY_SIZE) { ++ if (secure_key_size < MIN_SECURE_KEY_SIZE) { + pr_verbose(keystore, + "Size of secure key is too small: %lu expected %lu", +- secure_key_size, SECURE_KEY_SIZE); ++ secure_key_size, MIN_SECURE_KEY_SIZE); + rc = -EIO; + goto out; + } + ++ get_key_bit_size(secure_key, secure_key_size, &clear_key_bitsize); ++ + _keystore_print_record(rec, name, properties, 0, + file_names->skey_filename, secure_key_size, +- IS_XTS(secure_key_size) ? secure_key->bitsize * 2 +- : secure_key->bitsize, +- 0, 0, ++ is_xts_key(secure_key, secure_key_size), ++ clear_key_bitsize, 0, 0, + _keystore_reencipher_key_exists(file_names), 0); + + out: +@@ -3251,12 +3381,13 @@ out: + * mutliple APQN filters separated by commas. + * NULL means no APQN filter. + * @param[in] volume_type The volume type. NULL means no volume type filter. ++ * @param[in] key_type The key type. NULL means no key type filter. + * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_list_keys(struct keystore *keystore, const char *name_filter, + const char *volume_filter, const char *apqn_filter, +- const char *volume_type) ++ const char *volume_type, const char *key_type) + { + struct util_rec *rec; + int rc; +@@ -3269,10 +3400,16 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter, + return -EINVAL; + } + ++ if (key_type != NULL && ++ !_keystore_valid_key_type(key_type)) { ++ warnx("Invalid key-type specified"); ++ return -EINVAL; ++ } ++ + rec = _keystore_setup_record(0); + + rc = _keystore_process_filtered(keystore, name_filter, volume_filter, +- apqn_filter, volume_type, ++ apqn_filter, volume_type, key_type, + _keystore_display_key, rec); + util_rec_free(rec); + +@@ -3582,37 +3719,6 @@ out: + return cipher_spec; + } + +-/** +- * Returns the size of the secure key file +- * +- * @param[in] keystore the keystore +- * @param[in] skey_filename the file name of the secure key +- * +- * @returns the size of the secure key, or -1 in case of an error +- */ +-static size_t _keystore_get_key_file_size(struct keystore *keystore, +- const char *skey_filename) +-{ +- size_t secure_key_size; +- struct stat sb; +- +- if (stat(skey_filename, &sb)) { +- pr_verbose(keystore, "Key file '%s': %s", +- skey_filename, strerror(errno)); +- return -1; +- } +- +- secure_key_size = sb.st_size; +- if (secure_key_size < SECURE_KEY_SIZE) { +- pr_verbose(keystore, +- "Size of secure key is too small: %lu expected %lu", +- secure_key_size, SECURE_KEY_SIZE); +- return -1; +- } +- +- return secure_key_size; +-} +- + /** + * Processing function for the cryptsetup and crypttab functions. + * Extracts the required information and calls the secondary processing function +@@ -3639,6 +3745,7 @@ static int _keystore_process_crypt(struct keystore *keystore, + size_t secure_key_size; + size_t sector_size = 0; + char *volumes = NULL; ++ u8 *secure_key = NULL; + char *dmname; + char *temp; + int rc = 0; +@@ -3646,18 +3753,14 @@ static int _keystore_process_crypt(struct keystore *keystore, + char *ch; + int i; + +- secure_key_size = _keystore_get_key_file_size(keystore, +- file_names->skey_filename); +- if (secure_key_size < SECURE_KEY_SIZE) { +- pr_verbose(keystore, +- "Size of secure key is too small: %lu expected %lu", +- secure_key_size, SECURE_KEY_SIZE); +- rc = -EIO; +- goto out; +- } ++ secure_key = read_secure_key(file_names->skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) ++ return -EIO; + + cipher_spec = _keystore_build_cipher_spec(properties, +- IS_XTS(secure_key_size)); ++ is_xts_key(secure_key, ++ secure_key_size)); + if (cipher_spec == NULL) { + rc = -EINVAL; + goto out; +@@ -3709,6 +3812,8 @@ out: + free(cipher_spec); + if (volume_type != NULL) + free(volume_type); ++ if (secure_key != NULL) ++ free(secure_key); + return rc; + } + +@@ -3766,8 +3871,8 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, + info.process_func = _keystore_process_cryptsetup; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, +- volume_type, _keystore_process_crypt, +- &info); ++ volume_type, NULL, ++ _keystore_process_crypt, &info); + + str_list_free_string_array(info.volume_filter); + +@@ -3827,8 +3932,8 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, + info.process_func = _keystore_process_crypttab; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, +- volume_type, _keystore_process_crypt, +- &info); ++ volume_type, NULL, ++ _keystore_process_crypt, &info); + + str_list_free_string_array(info.volume_filter); + +@@ -3841,6 +3946,218 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, + return rc; + } + ++/** ++ * Converts a secure keys in the keystore ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key to convert ++ * @param[in] key_type the type of the key to convert it to ++ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for ++ * existence and type. ++ * @param[in] pkey_fd the file descriptor of /dev/pkey ++ * @param[in] cca the CCA library struct ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_convert_key(struct keystore *keystore, const char *name, ++ const char *key_type, bool noapqncheck, bool quiet, ++ int pkey_fd, struct cca_lib *cca) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ u8 output_key[2 * MAX_SECURE_KEY_SIZE]; ++ struct properties *properties = NULL; ++ int rc, min_level, selected = 1; ++ unsigned int output_key_size; ++ char *cur_key_type = NULL; ++ char **apqn_list = NULL; ++ size_t secure_key_size; ++ u8 *secure_key = NULL; ++ char *apqns = NULL; ++ char *temp; ++ u64 mkvp; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ properties = properties_new(); ++ rc = properties_load(properties, file_names.info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", name); ++ goto out; ++ } ++ ++ cur_key_type = _keystore_get_key_type(properties); ++ if (strcasecmp(cur_key_type, key_type) == 0) { ++ warnx("The secure key '%s' is already of type %s", name, ++ cur_key_type); ++ rc = 0; ++ goto out; ++ } ++ if (strcasecmp(cur_key_type, KEY_TYPE_CCA_AESDATA) != 0) { ++ warnx("Only secure keys of type %s can " ++ "be converted. The secure key '%s' is of type %s", ++ KEY_TYPE_CCA_AESDATA, name, cur_key_type); ++ rc = 0; ++ goto out; ++ } ++ ++ secure_key = read_secure_key(file_names.skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ min_level = get_min_card_level_for_keytype(key_type); ++ if (min_level < 0) { ++ warnx("Invalid key-type specified: %s", key_type); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ if (apqns != NULL) ++ apqn_list = str_list_split(apqns); ++ ++ rc = cross_check_apqns(apqns, 0, min_level, true, keystore->verbose); ++ if (rc == -EINVAL) ++ goto out; ++ if (rc != 0 && rc != -ENOTSUP && !noapqncheck) { ++ warnx("Your master key setup is improper for converting key " ++ "'%s'", name); ++ goto out; ++ } ++ ++ rc = validate_secure_key(pkey_fd, secure_key, secure_key_size, ++ NULL, NULL, (const char **)apqn_list, ++ keystore->verbose); ++ if (rc != 0) ++ goto out; ++ ++ rc = get_master_key_verification_pattern(secure_key, secure_key_size, ++ &mkvp, keystore->verbose); ++ if (rc) ++ goto out; ++ ++ rc = select_cca_adapter_by_mkvp(cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP, ++ keystore->verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ warnx("No APQN found that is suitable for " ++ "converting the secure AES key '%s'", name); ++ goto out; ++ } ++ ++ if (!quiet) { ++ util_print_indented("ATTENTION: Converting a secure key is " ++ "irreversible, and might have an effect " ++ "on the volumes encrypted with it!", 0); ++ _keystore_msg_for_volumes("The following volumes are encrypted " ++ "with this key:", properties, NULL); ++ printf("%s: Convert key '%s [y/N]'? ", ++ program_invocation_short_name, name); ++ if (!prompt_for_yes(keystore->verbose)) { ++ warnx("Operation aborted"); ++ rc = -ECANCELED; ++ goto out; ++ } ++ } ++ ++ memset(output_key, 0, sizeof(output_key)); ++ output_key_size = sizeof(output_key); ++ rc = convert_aes_data_to_cipher_key(cca, secure_key, ++ secure_key_size, output_key, ++ &output_key_size, ++ keystore->verbose); ++ if (rc != 0) { ++ warnx("Converting the secure key '%s' from %s to %s has failed", ++ name, KEY_TYPE_CCA_AESDATA, key_type); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); ++ goto out; ++ } ++ ++ rc = restrict_key_export(cca, output_key, output_key_size, ++ keystore->verbose); ++ if (rc != 0) { ++ warnx("Export restricting the converted secure key '%s' has " ++ "failed", name); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); ++ goto out; ++ } ++ ++ rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true); ++ if (rc != 0) { ++ warnx("Invalid characters in key-type"); ++ goto out; ++ } ++ ++ rc = properties_save(properties, file_names.info_filename, 1); ++ if (rc != 0) { ++ pr_verbose(keystore, ++ "Failed to write key info file '%s': %s", ++ file_names.info_filename, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = write_secure_key(file_names.skey_filename, output_key, ++ output_key_size, keystore->verbose); ++ if (rc != 0) ++ goto out; ++ ++ pr_verbose(keystore, "Secure key '%s' was converted successfully", ++ name); ++ ++ util_asprintf(&temp, "The following LUKS2 volumes are " ++ "encrypted with key '%s'. These volumes still contain " ++ "the secure AES volume key of type CCA-AESDATA. To " ++ "change the secure AES volume key in the LUKS2 header, " ++ "run command 'zkey-cryptsetup setkey " ++ "--master-key-file %s':", name, ++ file_names.skey_filename); ++ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_LUKS2); ++ free(temp); ++ util_asprintf(&temp, "The following plain mode volumes are " ++ "encrypted with key '%s'. You must adapt the crypttab " ++ "entries for this volumes and change the key size " ++ "parameter to 'size=%u' or run command 'zkey crypttab " ++ "--volumes ' for each volume to re-generate the " ++ "crypttab entries:", name, output_key_size * 8, name); ++ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_PLAIN); ++ free(temp); ++ ++out: ++ _keystore_free_key_filenames(&file_names); ++ if (properties != NULL) ++ properties_free(properties); ++ if (secure_key != NULL) ++ free(secure_key); ++ if (apqns != NULL) ++ free(apqns); ++ if (apqn_list != NULL) ++ str_list_free_string_array(apqn_list); ++ if (cur_key_type != NULL) ++ free(cur_key_type); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to convert key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ + /** + * Frees a keystore object + * +diff --git a/zkey/keystore.h b/zkey/keystore.h +index 8e888d1..b17a575 100644 +--- a/zkey/keystore.h ++++ b/zkey/keystore.h +@@ -32,12 +32,13 @@ int keystore_generate_key(struct keystore *keystore, const char *name, + const char *apqns, bool noapqncheck, + size_t sector_size, size_t keybits, bool xts, + const char *clear_key_file, const char *volume_type, +- int pkey_fd); ++ const char *key_type, int pkey_fd); + + 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); ++ const char *import_file, const char *volume_type, ++ struct cca_lib *cca); + + int keystore_change_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +@@ -68,7 +69,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name, + + int keystore_list_keys(struct keystore *keystore, const char *name_filter, + const char *volume_filter, const char *apqn_filter, +- const char *volume_type); ++ const char *volume_type, const char *key_type); + + int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, + bool execute, const char *volume_type, +@@ -80,6 +81,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, + const char *volume_type, const char *keyfile, + size_t keyfile_offset, size_t keyfile_size, size_t tries); + ++int keystore_convert_key(struct keystore *keystore, const char *name, ++ const char *key_type, bool noapqncheck, bool quiet, ++ int pkey_fd, struct cca_lib *cca); ++ + void keystore_free(struct keystore *keystore); + + +diff --git a/zkey/pkey.c b/zkey/pkey.c +index 8471f3d..462f9fe 100644 +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -46,6 +46,8 @@ + + #define DEFAULT_KEYBITS 256 + ++#define INITIAL_APQN_ENTRIES 16 ++ + /** + * Opens the pkey device and returns its file descriptor. + * +@@ -98,10 +100,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, + } + size = sb.st_size; + +- if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) { +- warnx("File '%s' has an invalid size, %lu or %lu bytes " +- "expected", keyfile, SECURE_KEY_SIZE, +- 2 * SECURE_KEY_SIZE); ++ if (size < MIN_SECURE_KEY_SIZE || size > 2 * MAX_SECURE_KEY_SIZE) { ++ warnx("File '%s' has an invalid size: %lu", keyfile, size); + return NULL; + } + +@@ -271,6 +271,615 @@ out: + return buf; + } + ++/** ++ * Returns the PKEY_KEYTYPE_xxx value for the specified key size. ++ * ++ * @param[in] keysize the key size in bits ++ * ++ * @returns the PKEY_KEYTYPE_xxx value or 0 for an unknown key size ++ */ ++static u32 keysize_to_keytype(enum pkey_key_size keysize) ++{ ++ switch (keysize) { ++ case PKEY_SIZE_AES_128: ++ return PKEY_KEYTYPE_AES_128; ++ case PKEY_SIZE_AES_192: ++ return PKEY_KEYTYPE_AES_192; ++ case PKEY_SIZE_AES_256: ++ return PKEY_KEYTYPE_AES_256; ++ default: ++ return 0; ++ } ++} ++ ++/** ++ * Returns the PKEY_SIZE_xxx value for the specified keybits. ++ * ++ * @param[in] keybits the key size in bits ++ * ++ * @returns thePKEY_SIZE_xxx value or 0 for an unknown key size ++ */ ++static enum pkey_key_size keybits_to_keysize(u32 keybits) ++{ ++ switch (keybits) { ++ case 128: ++ return PKEY_SIZE_AES_128; ++ case 192: ++ return PKEY_SIZE_AES_192; ++ case 256: ++ return PKEY_SIZE_AES_256; ++ default: ++ return PKEY_SIZE_UNKNOWN; ++ } ++} ++ ++/* ++ * Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure ++ * key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported ++ * by the pkey device, then it falls back to the older PKEY_GENSECK IOCTL ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in/out] genseck info about key to generate ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int pkey_genseck2(int pkey_fd, struct pkey_genseck2 *genseck2, ++ bool verbose) ++{ ++ struct pkey_genseck genseck; ++ int rc; ++ u32 i; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(genseck2 != NULL, "Internal error: genseck2 is NULL"); ++ ++ rc = ioctl(pkey_fd, PKEY_GENSECK2, genseck2); ++ if (rc != 0 && errno != ENOTTY) ++ return -errno; ++ if (rc == 0) ++ return 0; ++ ++ /* New IOCTL is not available, fall back to old one */ ++ pr_verbose(verbose, "ioctl PKEY_GENSECK2 not supported, fall back to " ++ "PKEY_GENSECK"); ++ ++ if (genseck2->type != PKEY_TYPE_CCA_DATA) { ++ warnx("Key-type is not supported"); ++ return -ENOTSUP; ++ } ++ ++ if (genseck2->keylen < AESDATA_KEY_SIZE) ++ return -EINVAL; ++ ++ memset(&genseck, 0, sizeof(genseck)); ++ ++ genseck.keytype = keysize_to_keytype(genseck2->size); ++ if (genseck.keytype == 0) ++ return -EINVAL; ++ ++ for (i = 0; i < genseck2->apqn_entries; i++) { ++ genseck.cardnr = genseck2->apqns[i].card; ++ genseck.domain = genseck2->apqns[i].domain; ++ ++ rc = ioctl(pkey_fd, PKEY_GENSECK, &genseck); ++ if (rc != 0) ++ continue; ++ ++ memcpy(genseck2->key, &genseck.seckey.seckey, AESDATA_KEY_SIZE); ++ genseck2->keylen = AESDATA_KEY_SIZE; ++ return 0; ++ } ++ ++ return -errno; ++} ++ ++/* ++ * Wrapper for the PKEY_CLR2SECK/PKEY_CLR2SECK2 IOCTL to generate a secure ++ * key of any type from a clear key. If the newer PKEY_CLR2SECK2 IOCTL is not ++ * supported by the pkey device, then it falls back to the older PKEY_CLR2SECK ++ * IOCTL ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in/out] clr2seck2 info about key to generate ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int pkey_clr2seck2(int pkey_fd, struct pkey_clr2seck2 *clr2seck2, ++ bool verbose) ++{ ++ struct pkey_clr2seck clr2seck; ++ int rc; ++ u32 i; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(clr2seck2 != NULL, "Internal error: clr2seck2 is NULL"); ++ ++ rc = ioctl(pkey_fd, PKEY_CLR2SECK2, clr2seck2); ++ if (rc != 0 && errno != ENOTTY) ++ return -errno; ++ if (rc == 0) ++ return 0; ++ ++ /* New IOCTL is not available, fall back to old one */ ++ pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 not supported, fall back to " ++ "PKEY_CLR2SECK"); ++ ++ if (clr2seck2->type != PKEY_TYPE_CCA_DATA) { ++ warnx("Key-type is not supported"); ++ return -ENOTSUP; ++ } ++ ++ if (clr2seck2->keylen < AESDATA_KEY_SIZE) ++ return -EINVAL; ++ ++ memset(&clr2seck, 0, sizeof(clr2seck)); ++ clr2seck.clrkey = clr2seck2->clrkey; ++ ++ clr2seck.keytype = keysize_to_keytype(clr2seck2->size); ++ if (clr2seck.keytype == 0) ++ return -EINVAL; ++ ++ for (i = 0; i < clr2seck2->apqn_entries; i++) { ++ clr2seck.cardnr = clr2seck2->apqns[i].card; ++ clr2seck.domain = clr2seck2->apqns[i].domain; ++ ++ rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2seck); ++ if (rc != 0) ++ continue; ++ ++ memcpy(clr2seck2->key, &clr2seck.seckey.seckey, ++ AESDATA_KEY_SIZE); ++ clr2seck2->keylen = AESDATA_KEY_SIZE; ++ return 0; ++ } ++ ++ return -errno; ++} ++ ++/* ++ * Wrapper for the PKEY_VERIFYKEY/PKEY_VERIFYKEY2 IOCTL to verify a secure ++ * key of any type. If the newer PKEY_VERIFYKEY2 IOCTL is not supported ++ * by the pkey device, then it falls back to the older PKEY_VERIFYKEY IOCTL ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in/out] verifykey2 info about key to verify ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int pkey_verifyseck2(int pkey_fd, struct pkey_verifykey2 *verifykey2, ++ bool verbose) ++{ ++ struct pkey_verifykey verifykey; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(verifykey2 != NULL, "Internal error: verifyseck2 is NULL"); ++ ++ rc = ioctl(pkey_fd, PKEY_VERIFYKEY2, verifykey2); ++ if (rc != 0 && errno != ENOTTY) ++ return -errno; ++ if (rc == 0) ++ return 0; ++ ++ /* New IOCTL is not available, fall back to old one */ ++ pr_verbose(verbose, "ioctl PKEY_VERIFYKEY2 not supported, fall back to " ++ "PKEY_VERIFYKEY"); ++ ++ if (!is_cca_aes_data_key(verifykey2->key, verifykey2->keylen)) ++ return -ENODEV; ++ ++ memset(&verifykey, 0, sizeof(verifykey)); ++ memcpy(&verifykey.seckey, verifykey2->key, sizeof(verifykey.seckey)); ++ ++ /* ++ * Note: the old IOCTL does not support to check a specific card and ++ * domain. If falling back to the old IOCTL, this input is silently ++ * ignored, and all APQNs currently available in the system are used. ++ */ ++ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); ++ if (rc != 0) ++ return -errno; ++ ++ if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) ++ return -ENODEV; ++ ++ verifykey2->type = PKEY_TYPE_CCA_DATA; ++ verifykey2->cardnr = verifykey.cardnr; ++ verifykey2->domain = verifykey.domain; ++ verifykey2->size = keybits_to_keysize(verifykey.keysize); ++ ++ if (verifykey.attributes & PKEY_VERIFY_ATTR_OLD_MKVP) ++ verifykey2->flags = PKEY_FLAGS_MATCH_ALT_MKVP; ++ else ++ verifykey2->flags = PKEY_FLAGS_MATCH_CUR_MKVP; ++ ++ return 0; ++} ++ ++/** ++ * Print a list of APQNs if verbose is set ++ */ ++static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num) ++{ ++ u32 i; ++ ++ if (!verbose) ++ return; ++ ++ for (i = 0; i < num ; i++) ++ warnx(" APQN: %02x.%04x", list[i].card, list[i].domain); ++} ++ ++/** ++ * Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings. ++ * ++ * @param[in] apqn_list a zero terminated array of pointers to C-strings ++ * @param[in/out] apqns A list of APQNs as array of struct pkey_apqn to ++ * filter. The list is modified during filtering. ++ * @param[in/out] apqn_entries Number of entries in the list of APQNs. The ++ * number is modified during filtering. ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns, ++ u32 *apqn_entries) ++{ ++ unsigned int count, i, k, card, domain; ++ struct pkey_apqn *list = *apqns; ++ bool found; ++ ++ if (apqn_list == NULL) ++ return 0; ++ ++ for (count = 0; apqn_list[count] != NULL; count++) ++ ; ++ if (count == 0) ++ return 0; ++ ++ for (i = 0; i < *apqn_entries; i++) { ++ found = false; ++ for (k = 0; apqn_list[k] != NULL; k++) { ++ if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2) ++ return -EINVAL; ++ ++ if (list[i].card == card && list[i].domain == domain) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ if (i < *apqn_entries - 1) ++ memmove(&list[i], &list[i+1], ++ (*apqn_entries - i - 1) * ++ sizeof(struct pkey_apqn)); ++ (*apqn_entries)--; ++ i--; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the ++ * List of APQNs as zero terminated array of pointers to C-strings that ++ * are usable for the CCA-AESDATA key type. ++ * ++ * @param[in] apqn_list a zero terminated array of pointers to C-strings ++ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The ++ * list must be freed by the caller using free(). ++ * @param[out] apqn_entries Number of entries in the list of APQNs ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int build_apqn_list_for_aes_data(const char **apqn_list, ++ struct pkey_apqn **apqns, ++ u32 *apqn_entries, bool verbose) ++{ ++ unsigned int card, domain, count = 0; ++ struct pkey_apqn *list = NULL; ++ u32 list_entries = 0; ++ int i; ++ ++ pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA"); ++ ++ if (apqn_list != NULL) ++ for (count = 0; apqn_list[count] != NULL; count++) ++ ; ++ ++ if (count > 0) { ++ list = util_malloc(count * sizeof(struct pkey_apqn)); ++ list_entries = count; ++ ++ for (i = 0; apqn_list[i] != NULL; i++) { ++ if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) ++ return -EINVAL; ++ ++ list[i].card = card; ++ list[i].domain = domain; ++ } ++ ++ } else { ++ /* ++ * Although the new pkey IOCTLs do not support APQN entries ++ * with ANY indication, build an ANY-list here. If we get here, ++ * then the new IOCTLs are not available, and it will fall back ++ * to the old IOCTL which do support ANY specifications. ++ */ ++ list = util_malloc(sizeof(struct pkey_apqn)); ++ list_entries = 1; ++ ++ list[0].card = AUTOSELECT; ++ list[0].domain = AUTOSELECT; ++ } ++ ++ *apqns = list; ++ *apqn_entries = list_entries; ++ ++ pr_verbose(verbose, "%u APQNs found", list_entries); ++ pr_verbose_apqn_list(verbose, list, list_entries); ++ return 0; ++} ++ ++/** ++ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the ++ * List of APQNs as zero terminated array of pointers to C-strings that ++ * are usable for the specified key type. ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] type the key type ++ * @param[in] apqn_list a zero terminated array of pointers to C-strings ++ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The ++ * list must be freed by the caller using free(). ++ * @param[out] apqn_entries Number of entries in the list of APQNs ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type, ++ const char **apqn_list, ++ struct pkey_apqn **apqns, ++ u32 *apqn_entries, bool verbose) ++{ ++ struct pkey_apqns4keytype apqns4keytype; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(apqns != NULL, "Internal error: apqns is NULL"); ++ util_assert(apqn_entries != NULL, ++ "Internal error: apqn_entries is NULL"); ++ ++ pr_verbose(verbose, "Build a list of APQNs for key type %d", type); ++ ++ memset(&apqns4keytype, 0, sizeof(apqns4keytype)); ++ apqns4keytype.type = type; ++ apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES; ++ apqns4keytype.apqns = (struct pkey_apqn *)util_malloc( ++ apqns4keytype.apqn_entries * sizeof(struct pkey_apqn)); ++ ++ do { ++ rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype); ++ if (rc == 0) ++ break; ++ rc = -errno; ++ pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s", ++ strerror(-rc)); ++ ++ switch (rc) { ++ case -ENOSPC: ++ free(apqns4keytype.apqns); ++ apqns4keytype.apqns = (struct pkey_apqn *) ++ util_malloc(apqns4keytype.apqn_entries * ++ sizeof(struct pkey_apqn)); ++ continue; ++ case -ENOTTY: ++ /* ++ * New IOCTL is not available: build the list ++ * manually (Key type CCA-AESDATA only) ++ */ ++ free(apqns4keytype.apqns); ++ ++ if (type != PKEY_TYPE_CCA_DATA) ++ return -ENOTSUP; ++ ++ rc = build_apqn_list_for_aes_data(apqn_list, apqns, ++ apqn_entries, ++ verbose); ++ return rc; ++ default: ++ goto out; ++ } ++ } while (rc != 0); ++ ++ if (apqns4keytype.apqn_entries == 0) { ++ pr_verbose(verbose, "No APQN available for key type %d", type); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns, ++ &apqns4keytype.apqn_entries); ++ if (rc != 0) ++ goto out; ++ ++ if (apqns4keytype.apqn_entries == 0) { ++ pr_verbose(verbose, "No APQN available for key type %d", type); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries); ++ pr_verbose_apqn_list(verbose, apqns4keytype.apqns, ++ apqns4keytype.apqn_entries); ++ ++out: ++ if (rc == 0) { ++ *apqns = apqns4keytype.apqns; ++ *apqn_entries = apqns4keytype.apqn_entries; ++ } else { ++ *apqns = NULL; ++ *apqn_entries = 0; ++ free(apqns4keytype.apqns); ++ } ++ ++ return rc; ++} ++ ++/** ++ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the ++ * List of APQNs as zero terminated array of pointers to C-strings that are ++ * usable for the specufied key. ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] key the key ++ * @param[in] keylen the length of the key ++ * @param[in] flags PKEY_FLAGS_MATCH_xxx flags ++ * @param[in] apqn_list a zero terminated array of pointers to C-strings ++ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The ++ * list must be freed by the caller using free(). ++ * @param[out] apqn_entries Number of entries in the list of APQNs ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags, ++ const char **apqn_list, ++ struct pkey_apqn **apqns, ++ u32 *apqn_entries, bool verbose) ++{ ++ struct pkey_apqns4key apqns4key; ++ u64 mkvp; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(key != NULL, "Internal error: key is NULL"); ++ util_assert(apqns != NULL, "Internal error: apqns is NULL"); ++ util_assert(apqn_entries != NULL, ++ "Internal error: apqn_entries is NULL"); ++ ++ pr_verbose(verbose, "Build a list of APQNs for the key"); ++ ++ memset(&apqns4key, 0, sizeof(apqns4key)); ++ apqns4key.key = key; ++ apqns4key.keylen = keylen; ++ apqns4key.flags = flags; ++ apqns4key.apqn_entries = INITIAL_APQN_ENTRIES; ++ apqns4key.apqns = (struct pkey_apqn *)util_malloc( ++ apqns4key.apqn_entries * sizeof(struct pkey_apqn)); ++ ++ do { ++ rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key); ++ if (rc == 0) ++ break; ++ rc = -errno; ++ pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc)); ++ ++ switch (rc) { ++ case -ENOSPC: ++ free(apqns4key.apqns); ++ apqns4key.apqns = (struct pkey_apqn *) ++ util_malloc(apqns4key.apqn_entries * ++ sizeof(struct pkey_apqn)); ++ continue; ++ case -ENOTTY: ++ /* ++ * New IOCTL is not available: build the list manually ++ * (Key type CCA-AESDATA only) ++ */ ++ free(apqns4key.apqns); ++ ++ if (!is_cca_aes_data_key(key, keylen)) ++ return -ENOTSUP; ++ ++ rc = get_master_key_verification_pattern(key, keylen, ++ &mkvp, ++ verbose); ++ if (rc != 0) ++ return rc; ++ ++ rc = build_apqn_list_for_aes_data(apqn_list, apqns, ++ apqn_entries, ++ verbose); ++ return rc; ++ default: ++ goto out; ++ } ++ } while (rc != 0); ++ ++ if (apqns4key.apqn_entries == 0) { ++ pr_verbose(verbose, "No APQN available for the key"); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ rc = filter_apqn_list(apqn_list, &apqns4key.apqns, ++ &apqns4key.apqn_entries); ++ if (rc != 0) ++ goto out; ++ ++ if (apqns4key.apqn_entries == 0) { ++ pr_verbose(verbose, "No APQN available for the key"); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries); ++ pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries); ++ ++out: ++ if (rc == 0) { ++ *apqns = apqns4key.apqns; ++ *apqn_entries = apqns4key.apqn_entries; ++ } else { ++ *apqns = NULL; ++ *apqn_entries = 0; ++ free(apqns4key.apqns); ++ } ++ ++ return rc; ++} ++ ++/** ++ * Convert the key type string into the pkey enumeration ++ * ++ * @param[in] key_type the type of the key ++ * ++ * @returns the pkey key type or 0 for an u known key type ++ */ ++static enum pkey_key_type key_type_to_pkey_type(const char *key_type) ++{ ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) ++ return PKEY_TYPE_CCA_DATA; ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) ++ return PKEY_TYPE_CCA_CIPHER; ++ ++ return 0; ++} ++ ++/** ++ * Return the size of a key blob for a specific type ++ * ++ * @param[in] type the type of the key ++ * ++ * @returns the size of the key or 0 for an invalid key type ++ */ ++static size_t key_size_for_type(enum pkey_key_type type) ++{ ++ switch (type) { ++ case PKEY_TYPE_CCA_DATA: ++ return AESDATA_KEY_SIZE; ++ case PKEY_TYPE_CCA_CIPHER: ++ return AESCIPHER_KEY_SIZE; ++ default: ++ return 0; ++ } ++} ++ + /** + * Generate a secure key by random + * +@@ -278,80 +887,110 @@ out: + * @param[in] keyfile the file name of the secure key to generate + * @param[in] keybits the cryptographic size of the key in bits + * @param[in] xts if true an XTS key is generated +- * @param[in] card the card number to use (or AUTOSELECT) +- * @param[in] domain the domain number to use (or AUTOSELECT) ++ * @param[in] key_type the type of the key ++ * @param[in] apqns a zero terminated array of pointers to APQN-strings, ++ * or NULL for AUTOSELECT + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 on success, a negative errno in case of an error + */ + int generate_secure_key_random(int pkey_fd, const char *keyfile, +- size_t keybits, bool xts, u16 card, u16 domain, +- bool verbose) ++ size_t keybits, bool xts, const char *key_type, ++ const char **apqns, bool verbose) + { +- struct pkey_genseck gensec; +- size_t secure_key_size; +- u8 *secure_key; ++ struct pkey_genseck2 genseck2; ++ size_t secure_key_size, size; ++ u8 *secure_key = NULL; + int rc; + + util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); + util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(key_type != NULL, "Internal error: key_type is NULL"); + + if (keybits == 0) + keybits = DEFAULT_KEYBITS; + +- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); +- secure_key = util_malloc(secure_key_size); ++ pr_verbose(verbose, "Generate secure key by random"); + +- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); ++ memset(&genseck2, 0, sizeof(genseck2)); + +- gensec.cardnr = card; +- gensec.domain = domain; +- switch (keybits) { +- case 128: +- gensec.keytype = PKEY_KEYTYPE_AES_128; +- break; +- case 192: +- if (xts) { +- warnx("Invalid value for '--keybits'|'-c' " +- "for XTS: '%lu'", keybits); +- rc = -EINVAL; +- goto out; +- } +- gensec.keytype = PKEY_KEYTYPE_AES_192; +- break; +- case 256: +- gensec.keytype = PKEY_KEYTYPE_AES_256; +- break; +- default: ++ genseck2.type = key_type_to_pkey_type(key_type); ++ if (genseck2.type == 0) { ++ warnx("Key-type not supported; %s", key_type); ++ return -ENOTSUP; ++ } ++ ++ genseck2.size = keybits_to_keysize(keybits); ++ if (genseck2.size == 0) { + warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits); +- rc = -EINVAL; +- goto out; ++ return -EINVAL; ++ } ++ if (keybits == 192 && xts) { ++ warnx("Invalid value for '--keybits'|'-c' " ++ "for XTS: '%lu'", keybits); ++ return -EINVAL; + } + +- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); +- if (rc < 0) { +- rc = -errno; +- warnx("Failed to generate a secure key: %s", strerror(errno)); +- warnx("Make sure that all available CCA crypto adapters are " +- "setup with the same master key"); +- goto out; ++ rc = build_apqn_list_for_key_type(pkey_fd, genseck2.type, apqns, ++ &genseck2.apqns, ++ &genseck2.apqn_entries, verbose); ++ if (rc != 0) { ++ if (rc == -ENODEV || rc == -ENOTSUP) ++ warnx("No APQN is available that can generate a secure " ++ "key of type %s", key_type); ++ else ++ warnx("Failed to build a list of APQNs that can " ++ "generate a secure key of type %s: %s", key_type, ++ strerror(-rc)); ++ return rc; + } + +- memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE); ++ size = key_size_for_type(genseck2.type); ++ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); ++ secure_key = util_zalloc(secure_key_size); ++ ++ genseck2.key = secure_key; ++ genseck2.keylen = size; ++ ++ rc = pkey_genseck2(pkey_fd, &genseck2, verbose); ++ if (rc != 0) { ++ warnx("Failed to generate a secure key: %s", strerror(-rc)); ++ goto out; ++ } + + if (xts) { +- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); +- if (rc < 0) { +- rc = -errno; +- warnx("Failed to generate a secure key: %s", +- strerror(errno)); +- warnx("Make sure that all available CCA crypto " +- "adapters are setup with the same master key"); ++ free(genseck2.apqns); ++ genseck2.apqns = NULL; ++ genseck2.apqn_entries = 0; ++ ++ /* ++ * Ensure to generate 2nd key with an APQN that has the same ++ * master key that is used by the 1st key. ++ */ ++ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, ++ PKEY_FLAGS_MATCH_CUR_MKVP, apqns, ++ &genseck2.apqns, ++ &genseck2.apqn_entries, verbose); ++ if (rc != 0) { ++ if (rc == -ENODEV || rc == -ENOTSUP) ++ warnx("No APQN is available that can generate " ++ "a secure key of type %s", key_type); ++ else ++ warnx("Failed to build a list of APQNs that " ++ "can generate a secure key of type %s: " ++ "%s", key_type, strerror(-rc)); + goto out; + } + +- memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey, +- SECURE_KEY_SIZE); ++ genseck2.key = secure_key + size; ++ genseck2.keylen = size; ++ ++ rc = pkey_genseck2(pkey_fd, &genseck2, verbose); ++ if (rc != 0) { ++ warnx("Failed to generate a secure key: %s", ++ strerror(-rc)); ++ goto out; ++ } + } + + pr_verbose(verbose, "Successfully generated a secure key"); +@@ -359,6 +998,7 @@ int generate_secure_key_random(int pkey_fd, const char *keyfile, + rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); + + out: ++ free(genseck2.apqns); + free(secure_key); + return rc; + } +@@ -374,89 +1014,125 @@ out: + * determines the keybits. + * @param[in] xts if true an XTS key is generated + * @param[in] clearkeyfile the file name of the clear key to read +- * @param[in] card the card number to use (or AUTOSELECT) +- * @param[in] domain the domain number to use (or AUTOSELECT) ++ * @param[in] key_type the type of the key ++ * @param[in] apqns a zero terminated array of pointers to APQN-strings, ++ * or NULL for AUTOSELECT + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 on success, a negative errno in case of an error + */ + int generate_secure_key_clear(int pkey_fd, const char *keyfile, + size_t keybits, bool xts, +- const char *clearkeyfile, +- u16 card, u16 domain, +- bool verbose) ++ const char *clearkeyfile, const char *key_type, ++ const char **apqns, bool verbose) + { +- struct pkey_clr2seck clr2sec; ++ struct pkey_clr2seck2 clr2seck2; + size_t secure_key_size; + size_t clear_key_size; + u8 *secure_key; + u8 *clear_key; ++ size_t size; + int rc; + + util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); + util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); + util_assert(clearkeyfile != NULL, + "Internal error: clearkeyfile is NULL"); ++ util_assert(key_type != NULL, "Internal error: key_type is NULL"); + +- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); +- secure_key = util_malloc(secure_key_size); ++ pr_verbose(verbose, "Generate secure key from a clear key"); + + clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size, + verbose); + if (clear_key == NULL) + return -EINVAL; + +- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); ++ memset(&clr2seck2, 0, sizeof(clr2seck2)); + +- clr2sec.cardnr = card; +- clr2sec.domain = domain; +- switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, xts)) { +- case 128: +- clr2sec.keytype = PKEY_KEYTYPE_AES_128; +- break; +- case 192: +- clr2sec.keytype = PKEY_KEYTYPE_AES_192; +- break; +- case 256: +- clr2sec.keytype = PKEY_KEYTYPE_AES_256; +- break; +- default: ++ memcpy(&clr2seck2.clrkey, clear_key, ++ HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); ++ ++ clr2seck2.type = key_type_to_pkey_type(key_type); ++ if (clr2seck2.type == 0) { ++ warnx("Key-type not supported; %s", key_type); ++ return -ENOTSUP; ++ } ++ ++ clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS( ++ clear_key_size * 8, xts)); ++ if (clr2seck2.size == 0) { + warnx("Invalid clear key size: '%lu' bytes", clear_key_size); +- rc = -EINVAL; +- goto out; ++ return -EINVAL; ++ } ++ if (keybits == 192 && xts) { ++ warnx("Invalid clear key size for XTS: '%lu' bytes", ++ clear_key_size); ++ return -EINVAL; + } + +- memcpy(&clr2sec.clrkey, clear_key, +- HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); ++ rc = build_apqn_list_for_key_type(pkey_fd, clr2seck2.type, apqns, ++ &clr2seck2.apqns, ++ &clr2seck2.apqn_entries, verbose); ++ if (rc != 0) { ++ if (rc == -ENODEV || rc == -ENOTSUP) ++ warnx("No APQN is available that can generate a secure " ++ "key of type %s", key_type); ++ else ++ warnx("Failed to build a list of APQNs that can " ++ "generate a secure key of type %s: %s", key_type, ++ strerror(-rc)); ++ return rc; ++ } + +- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); +- if (rc < 0) { +- rc = -errno; +- warnx("Failed to generate a secure key from a " +- "clear key: %s", strerror(errno)); +- warnx("Make sure that all available CCA crypto adapters are " +- "setup with the same master key"); ++ size = key_size_for_type(clr2seck2.type); ++ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); ++ secure_key = util_zalloc(secure_key_size); ++ ++ clr2seck2.key = secure_key; ++ clr2seck2.keylen = size; ++ ++ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); ++ if (rc != 0) { ++ warnx("Failed to generate a secure key: %s", strerror(-rc)); + goto out; + } + +- memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE); +- + if (xts) { +- memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2, ++ free(clr2seck2.apqns); ++ clr2seck2.apqns = NULL; ++ clr2seck2.apqn_entries = 0; ++ ++ memcpy(&clr2seck2.clrkey, clear_key + clear_key_size / 2, + clear_key_size / 2); + +- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); +- if (rc < 0) { +- rc = -errno; +- warnx("Failed to generate a secure key from " +- "a clear key: %s", strerror(errno)); +- warnx("Make sure that all available CCA crypto " +- "adapters are setup with the same master key"); ++ /* ++ * Ensure to generate 2nd key with an APQN that has the same ++ * master key that is used by the 1st key. ++ */ ++ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, ++ PKEY_FLAGS_MATCH_CUR_MKVP, apqns, ++ &clr2seck2.apqns, ++ &clr2seck2.apqn_entries, verbose); ++ if (rc != 0) { ++ if (rc == -ENODEV || rc == -ENOTSUP) ++ warnx("No APQN is available that can generate " ++ "a secure key of type %s", key_type); ++ else ++ warnx("Failed to build a list of APQNs that " ++ "can generate a secure key of type %s: " ++ "%s", key_type, strerror(-rc)); + goto out; + } + +- memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey, +- SECURE_KEY_SIZE); ++ clr2seck2.key = secure_key + size; ++ clr2seck2.keylen = size; ++ ++ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); ++ if (rc != 0) { ++ warnx("Failed to generate a secure key: %s", ++ strerror(-rc)); ++ goto out; ++ } + } + + pr_verbose(verbose, +@@ -465,10 +1141,11 @@ int generate_secure_key_clear(int pkey_fd, const char *keyfile, + rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); + + out: +- memset(&clr2sec, 0, sizeof(clr2sec)); ++ memset(&clr2seck2, 0, sizeof(clr2seck2)); + memset(clear_key, 0, clear_key_size); + free(clear_key); + free(secure_key); ++ free(clr2seck2.apqns); + return rc; + } + +@@ -476,84 +1153,58 @@ out: + * Validates an XTS secure key (the second part) + * + * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] apqn the APQN to verify the key with + * @param[in] secure_key a buffer containing the secure key + * @param[in] secure_key_size the secure key size + * @param[in] part1_keysize the key size of the first key part +- * @param[in] part1_attributes the attributes of the first key part ++ * @param[in] part1_flags the flags of the first key part + * @param[out] clear_key_bitsize on return , the cryptographic size of the + * clear key + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 on success, a negative errno in case of an error + */ +-static int validate_secure_xts_key(int pkey_fd, ++static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn, + u8 *secure_key, size_t secure_key_size, +- u16 part1_keysize, u32 part1_attributes, +- size_t *clear_key_bitsize, bool verbose) ++ enum pkey_key_size part1_keysize, ++ u32 part1_flags, size_t *clear_key_bitsize, ++ bool verbose) + { +- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; +- struct pkey_verifykey verifykey; +- struct secaeskeytoken *token2; ++ struct pkey_verifykey2 verifykey2; + int rc; + + util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(apqn != NULL, "Internal error: apqn is NULL"); + +- /* XTS uses 2 secure key tokens concatenated to each other */ +- token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE); +- +- if (secure_key_size != 2 * SECURE_KEY_SIZE) { +- pr_verbose(verbose, "Size of secure key is too small: " +- "%lu expected %lu", secure_key_size, +- 2 * SECURE_KEY_SIZE); +- return -EINVAL; +- } +- +- if (token->bitsize != token2->bitsize) { +- pr_verbose(verbose, "XTS secure key contains 2 clear keys of " +- "different sizes"); +- return -EINVAL; +- } +- if (token->keysize != token2->keysize) { +- pr_verbose(verbose, "XTS secure key contains 2 keys of " +- "different sizes"); +- return -EINVAL; +- } +- if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) { +- pr_verbose(verbose, "XTS secure key contains 2 keys using " +- "different CCA master keys"); +- return -EINVAL; +- } +- +- memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey)); ++ memset(&verifykey2, 0, sizeof(verifykey2)); ++ verifykey2.key = secure_key + (secure_key_size / 2); ++ verifykey2.keylen = secure_key_size / 2; ++ verifykey2.cardnr = apqn->card; ++ verifykey2.domain = apqn->domain; + +- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); ++ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); + if (rc < 0) { +- rc = -errno; +- pr_verbose(verbose, "Failed to validate a secure key: %s", +- strerror(-rc)); ++ pr_verbose(verbose, "Failed to validate the 2nd part of the " ++ "XTS secure key on APQN %02x.%04x: %s", apqn->card, ++ apqn->domain, strerror(-rc)); + return rc; + } + +- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { +- pr_verbose(verbose, "Secure key is not an AES key"); +- return -EINVAL; +- } +- +- if (verifykey.keysize != part1_keysize) { ++ if (verifykey2.size != part1_keysize) { + pr_verbose(verbose, "XTS secure key contains 2 keys using " + "different key sizes"); + return -EINVAL; + } + +- if (verifykey.attributes != part1_attributes) { ++ if (verifykey2.flags != part1_flags) { + pr_verbose(verbose, "XTS secure key contains 2 keys using " +- "different attributes"); ++ "different master keys"); + return -EINVAL; + } + +- if (clear_key_bitsize) +- *clear_key_bitsize += verifykey.keysize; ++ if (clear_key_bitsize && verifykey2.size != PKEY_SIZE_UNKNOWN) ++ *clear_key_bitsize += verifykey2.size; + + return 0; + } +@@ -568,6 +1219,8 @@ static int validate_secure_xts_key(int pkey_fd, + * clear key + * @param[out] is_old_mk in return set to 1 to indicate if the secure key + * is currently enciphered by the OLD CCA master key ++ * @param[in] apqns a zero terminated array of pointers to APQN-strings, ++ * or NULL for AUTOSELECT + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 on success, a negative errno in case of an error +@@ -575,59 +1228,89 @@ static int validate_secure_xts_key(int pkey_fd, + int validate_secure_key(int pkey_fd, + u8 *secure_key, size_t secure_key_size, + size_t *clear_key_bitsize, int *is_old_mk, +- bool verbose) ++ const char **apqns, bool verbose) + { +- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; +- struct pkey_verifykey verifykey; ++ struct pkey_verifykey2 verifykey2; ++ struct pkey_apqn *list = NULL; ++ u32 i, list_entries = 0; ++ bool xts, valid; + int rc; + + util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); + +- if (secure_key_size < SECURE_KEY_SIZE) { +- pr_verbose(verbose, "Size of secure key is too small: " +- "%lu expected %lu", secure_key_size, +- SECURE_KEY_SIZE); +- return -EINVAL; +- } +- +- memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey)); ++ xts = is_xts_key(secure_key, secure_key_size); + +- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); +- if (rc < 0) { +- rc = -errno; +- pr_verbose(verbose, "Failed to validate a secure key: %s", +- strerror(-rc)); ++ rc = build_apqn_list_for_key(pkey_fd, secure_key, ++ HALF_KEYSIZE_FOR_XTS(secure_key_size, xts), ++ PKEY_FLAGS_MATCH_CUR_MKVP | ++ PKEY_FLAGS_MATCH_ALT_MKVP, ++ 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; + } + +- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { +- pr_verbose(verbose, "Secure key is not an AES key"); +- return -EINVAL; +- } ++ if (is_old_mk != NULL) ++ *is_old_mk = true; ++ if (clear_key_bitsize != NULL) ++ *clear_key_bitsize = 0; + +- if (clear_key_bitsize) +- *clear_key_bitsize = verifykey.keysize; ++ valid = false; ++ for (i = 0; i < list_entries; i++) { ++ memset(&verifykey2, 0, sizeof(verifykey2)); ++ verifykey2.key = secure_key; ++ verifykey2.keylen = HALF_KEYSIZE_FOR_XTS(secure_key_size, xts); ++ verifykey2.cardnr = list[i].card; ++ verifykey2.domain = list[i].domain; + +- /* XTS uses 2 secure key tokens concatenated to each other */ +- if (secure_key_size > SECURE_KEY_SIZE) { +- rc = validate_secure_xts_key(pkey_fd, +- secure_key, secure_key_size, +- verifykey.keysize, +- verifykey.attributes, +- clear_key_bitsize, +- verbose); +- if (rc != 0) +- return rc; ++ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); ++ if (rc < 0) { ++ pr_verbose(verbose, "Failed to validate the secure key " ++ "on APQN %02x.%04x: %s", list[i].card, ++ list[i].domain, strerror(-rc)); ++ continue; ++ } ++ ++ if (is_xts_key(secure_key, secure_key_size)) { ++ rc = validate_secure_xts_key(pkey_fd, &list[i], ++ secure_key, ++ secure_key_size, ++ verifykey2.size, ++ verifykey2.flags, ++ clear_key_bitsize, ++ verbose); ++ if (rc != 0) ++ continue; ++ ++ } ++ ++ valid = true; ++ ++ if (clear_key_bitsize) { ++ if (verifykey2.size != PKEY_SIZE_UNKNOWN) ++ *clear_key_bitsize += verifykey2.size; ++ clear_key_bitsize = NULL; /* Set it only once */ ++ } ++ ++ /* ++ * 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 && ++ (verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP)) ++ *is_old_mk = false; + } + +- if (is_old_mk) +- *is_old_mk = (verifykey.attributes & +- PKEY_VERIFY_ATTR_OLD_MKVP) != 0; ++ if (!valid) ++ return -ENODEV; + + pr_verbose(verbose, "Secure key validation completed successfully"); + +- return 0; ++ if (list != NULL) ++ free(list); ++ return rc; + } + + /** +@@ -642,7 +1325,7 @@ int validate_secure_key(int pkey_fd, + * + * @returns 0 on success, a negative errno in case of an error + */ +-int generate_key_verification_pattern(const char *key, size_t key_size, ++int generate_key_verification_pattern(const u8 *key, size_t key_size, + char *vp, size_t vp_len, bool verbose) + { + int tfmfd = -1, opfd = -1, rc = 0; +@@ -677,7 +1360,7 @@ int generate_key_verification_pattern(const char *key, size_t key_size, + } + + snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)", +- key_size > SECURE_KEY_SIZE ? "xts" : "cbc"); ++ is_xts_key(key, key_size) ? "xts" : "cbc"); + + tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (tfmfd < 0) { +@@ -770,23 +1453,337 @@ out: + return rc; + } + +-int get_master_key_verification_pattern(const u8 *secure_key, +- size_t secure_key_size, u64 *mkvp, +- bool verbose) ++int get_master_key_verification_pattern(const u8 *key, size_t key_size, ++ u64 *mkvp, bool UNUSED(verbose)) + { +- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; ++ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; ++ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; + +- util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(key != NULL, "Internal error: secure_key is NULL"); + util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); + +- if (secure_key_size < SECURE_KEY_SIZE) { +- pr_verbose(verbose, "Size of secure key is too small: " +- "%lu expected %lu", secure_key_size, +- SECURE_KEY_SIZE); ++ if (is_cca_aes_data_key(key, key_size)) ++ *mkvp = datakey->mkvp; ++ else if (is_cca_aes_cipher_key(key, key_size)) ++ memcpy(mkvp, cipherkey->kvp, sizeof(*mkvp)); ++ else + return -EINVAL; ++ ++ return 0; ++} ++ ++/** ++ * Check if the specified key is a CCA AESDATA 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 an CCA AESDATA token type ++ */ ++bool is_cca_aes_data_key(const u8 *key, size_t key_size) ++{ ++ struct tokenheader *hdr = (struct tokenheader *)key; ++ ++ if (key == NULL || key_size < AESDATA_KEY_SIZE) ++ return false; ++ ++ if (hdr->type != TOKEN_TYPE_CCA_INTERNAL) ++ return false; ++ if (hdr->version != TOKEN_VERSION_AESDATA) ++ return false; ++ ++ return true; ++} ++ ++/** ++ * Check if the specified key is a CCA AESCIPHER 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 an CCA AESCIPHER token type ++ */ ++bool is_cca_aes_cipher_key(const u8 *key, size_t key_size) ++{ ++ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; ++ ++ if (key == NULL || key_size < AESCIPHER_KEY_SIZE) ++ return false; ++ ++ if (cipherkey->type != TOKEN_TYPE_CCA_INTERNAL) ++ return false; ++ if (cipherkey->version != TOKEN_VERSION_AESCIPHER) ++ return false; ++ if (cipherkey->length > key_size) ++ return false; ++ ++ if (cipherkey->kms != 0x03) /* key wrapped by master key */ ++ return false; ++ if (cipherkey->kwm != 0x02) /* key wrapped using AESKW */ ++ return false; ++ if (cipherkey->pfv != 0x00 && cipherkey->pfv != 0x01) /* V0 or V1 */ ++ return false; ++ if (cipherkey->adv != 0x01) /* Should have ass. data sect. version 1 */ ++ return false; ++ if (cipherkey->at != 0x02) /* Algorithm: AES */ ++ return false; ++ if (cipherkey->kt != 0x0001) /* Key type: CIPHER */ ++ return false; ++ if (cipherkey->adl != 26) /* Ass. data section length should be 26 */ ++ return false; ++ if (cipherkey->kll != 0) /* Should have no key label */ ++ return false; ++ if (cipherkey->eadl != 0) /* Should have no ext associated data */ ++ return false; ++ if (cipherkey->uadl != 0) /* Should have no user associated data */ ++ return false; ++ if (cipherkey->kufc != 2) /* Should have 2 KUFs */ ++ return false; ++ if (cipherkey->kmfc != 3) /* Should have 3 KMFs */ ++ return false; ++ ++ return true; ++} ++ ++/** ++ * Check if the specified key is an XTS type key ++ * ++ * @param[in] key the secure key token ++ * @param[in] key_size the size of the secure key ++ * ++ * @returns true if the key is an XTS key type ++ */ ++bool is_xts_key(const u8 *key, size_t key_size) ++{ ++ 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, ++ key_size - AESDATA_KEY_SIZE)) ++ return true; ++ } else if (is_cca_aes_cipher_key(key, key_size)) { ++ if (key_size == 2 * AESCIPHER_KEY_SIZE && ++ is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE, ++ key_size - AESCIPHER_KEY_SIZE)) ++ return true; + } + +- *mkvp = token->mkvp; ++ return false; ++} ++ ++/** ++ * Gets the size in bits of the effective key of the specified secure key ++ * ++ * @param[in] key the secure key token ++ * @param[in] key_size the size of the secure key ++ * @param[out] bitsize On return, contains the size in bits of the key. ++ * If the key size can not be determined, then 0 is ++ * passed back as bitsize. ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) ++{ ++ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; ++ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; ++ ++ util_assert(bitsize != NULL, "Internal error: bitsize is NULL"); ++ ++ if (is_cca_aes_data_key(key, key_size)) { ++ *bitsize = datakey->bitsize; ++ if (key_size == 2 * AESDATA_KEY_SIZE) { ++ datakey = (struct aesdatakeytoken *)key + ++ AESDATA_KEY_SIZE; ++ *bitsize += datakey->bitsize; ++ } ++ } else if (is_cca_aes_cipher_key(key, key_size)) { ++ if (cipherkey->pfv == 0x00) /* V0 payload */ ++ *bitsize = cipherkey->pl - 384; ++ else ++ *bitsize = 0; /* Unknown */ ++ if (key_size > cipherkey->length) { ++ cipherkey = (struct aescipherkeytoken *)key + ++ cipherkey->length; ++ if (cipherkey->pfv == 0x00) /* V0 payload */ ++ *bitsize += cipherkey->pl - 384; ++ } ++ } else { ++ return -EINVAL; ++ } + + return 0; + } ++ ++/** ++ * Returns the type of the key ++ * ++ * @param[in] key the secure key token ++ * @param[in] key_size the size of the secure key ++ * ++ * @returns a static string on success, NULL in case of an error ++ */ ++const char *get_key_type(const u8 *key, size_t key_size) ++{ ++ if (is_cca_aes_data_key(key, key_size)) ++ return KEY_TYPE_CCA_AESDATA; ++ if (is_cca_aes_cipher_key(key, key_size)) ++ return KEY_TYPE_CCA_AESCIPHER; ++ ++ return NULL; ++} ++ ++/** ++ * Returns the minimum card level for a specific key type ++ * ++ * @param[in] key_type the type of the key ++ * ++ * @returns the minimum card level, or -1 for unknown key types ++ */ ++int get_min_card_level_for_keytype(const char *key_type) ++{ ++ if (key_type == NULL) ++ return -1; ++ ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) ++ return 3; ++ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) ++ return 6; ++ ++ return -1; ++} ++ ++/** ++ * Performs extended checks on an AES CIPHER key. It checks the key usage ++ * fields (KUFs) and key management fields (KMFs) of the key. The function ++ * returns -EINVAL and issues warning messages if a mismatch is detected. ++ * ++ * @param[in] key the secure key token ++ * @param[in] key_size the size of the secure key ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int check_aes_cipher_key(const u8 *key, size_t key_size) ++{ ++ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; ++ bool mismatch = false; ++ ++ if (!is_cca_aes_cipher_key(key, key_size)) { ++ warnx("The key is not of type '"KEY_TYPE_CCA_AESCIPHER"'"); ++ return -EINVAL; ++ } ++ ++ if ((cipherkey->kuf1 & 0x8000) == 0) { ++ printf("WARNING: The secure key can not be used for " ++ "encryption\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kuf1 & 0x4000) == 0) { ++ printf("WARNING: The secure key can not be used for " ++ "decryption\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kuf1 & 0x2000) { ++ printf("INFO: The secure key can be used for data translate\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kuf1 & 0x1000) { ++ printf("WARNING: The secure key can only be used in UDXs\n"); ++ mismatch = true; ++ } ++ ++ if (cipherkey->kmf1 & 0x8000) { ++ printf("WARNING: The secure key can be exported using a " ++ "symmetric key\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf1 & 0x4000) { ++ printf("WARNING: The secure key can be exported using an " ++ "unauthenticated asymmetric key\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf1 & 0x2000) { ++ printf("WARNING: The secure key can be exported using an " ++ "authenticated asymmetric key\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf1 & 0x1000) { ++ printf("WARNING: The secure key can be exported using a RAW " ++ "key\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf1 & 0x0800) == 0) { ++ printf("WARNING: The secure key can not be transformed into a " ++ "CPACF protected key\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf1 & 0x0080) == 0) { ++ printf("WARNING: The secure key can be exported using a DES " ++ "key\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf1 & 0x0040) == 0) { ++ printf("WARNING: The secure key can be exported using an AES " ++ "key\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf1 & 0x0008) == 0) { ++ printf("WARNING: The secure key can be exported using an RSA " ++ "key\n"); ++ mismatch = true; ++ } ++ ++ if (cipherkey->kmf2 & 0xC000) { ++ printf("WARNING: The secure key is incomplete\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf2 & 0x0010) { ++ printf("WARNING: The secure key was previously encrypted with " ++ "an untrusted KEK\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf2 & 0x0008) { ++ printf("WARNING: The secure key was previously in a format " ++ "without type or usage attributes\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf2 & 0x0004) { ++ printf("WARNING: The secure key was previously encrypted with " ++ "a key weaker than itself\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf2 & 0x0002) { ++ printf("WARNING: The secure key was previously in a non-CCA " ++ "format\n"); ++ mismatch = true; ++ } ++ if (cipherkey->kmf2 & 0x0001) { ++ printf("WARNING: The secure key was previously encrypted in " ++ "ECB mode\n"); ++ mismatch = true; ++ } ++ ++ if ((cipherkey->kmf3 & 0xFF00) == 0x0000 || ++ (cipherkey->kmf3 & 0x00FF) == 0x0000) { ++ printf("WARNING: The secure key was created by an unknown " ++ "method\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf3 & 0xFF00) == 0x0400 || ++ (cipherkey->kmf3 & 0x00FF) == 0x0004) { ++ printf("WARNING: The secure key was created from cleartext key " ++ "components\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf3 & 0xFF00) == 0x0500 || ++ (cipherkey->kmf3 & 0x00FF) == 0x0005) { ++ printf("WARNING: The secure key was entered as a cleartext key " ++ "value\n"); ++ mismatch = true; ++ } ++ if ((cipherkey->kmf3 & 0x00FF) == 0x0012) { ++ printf("WARNING: The secure key was converted from a CCA " ++ "key-token that had no export control attributes\n"); ++ mismatch = true; ++ } ++ ++ return mismatch ? -EINVAL : 0; ++} +diff --git a/zkey/pkey.h b/zkey/pkey.h +index c0aac2e..3dfd588 100644 +--- a/zkey/pkey.h ++++ b/zkey/pkey.h +@@ -18,10 +18,23 @@ + /* + * Definitions for the /dev/pkey kernel module interface + */ +-struct secaeskeytoken { +- u8 type; /* 0x01 for internal key token */ ++struct tokenheader { ++ u8 type; + u8 res0[3]; +- u8 version; /* should be 0x04 */ ++ u8 version; ++ u8 res1[3]; ++} __packed; ++ ++#define TOKEN_TYPE_NON_CCA 0x00 ++#define TOKEN_TYPE_CCA_INTERNAL 0x01 ++ ++#define TOKEN_VERSION_AESDATA 0x04 ++#define TOKEN_VERSION_AESCIPHER 0x05 ++ ++struct aesdatakeytoken { ++ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ ++ u8 res0[3]; ++ u8 version; /* should be TOKEN_VERSION_AESDATA (0x04) */ + u8 res1[1]; + u8 flag; /* key flags */ + u8 res2[1]; +@@ -33,22 +46,60 @@ struct secaeskeytoken { + u8 tvv[4]; /* token validation value */ + } __packed; + +-#define SECURE_KEY_SIZE sizeof(struct secaeskeytoken) ++struct aescipherkeytoken { ++ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ ++ u8 res0; ++ u16 length; /* length of token */ ++ u8 version; /* should be TOKEN_VERSION_CIPHER (0x05) */ ++ u8 res1[3]; ++ u8 kms; /* key material state, should be 0x03 */ ++ u8 kvptype; /* key verification pattern type */ ++ u8 kvp[16]; /* key verification pattern */ ++ u8 kwm; /* key wrapping method, should be 0x02 */ ++ u8 kwh; /* key wrapping hash algorithm */ ++ u8 pfv; /* payload format version, should be 0x00*/ ++ u8 res2; ++ u8 adv; /* associated data section version */ ++ u8 res3; ++ u16 adl; /* associated data length */ ++ u8 kll; /* length of optional key label */ ++ u8 eadl; /* extended associated data length */ ++ u8 uadl; /* user associated data length */ ++ u8 res4; ++ u16 pl; /* payload bit length */ ++ u8 res5; ++ u8 at; /* algorithm type, should be 0x02 (AES) */ ++ u16 kt; /* key type, should be 0x001 (CIPHER) */ ++ u8 kufc; /* key usage field count */ ++ u16 kuf1; /* key usage field 1 */ ++ u16 kuf2; /* key usage field 2 */ ++ u8 kmfc; /* key management field count */ ++ u16 kmf1; /* key management field 1 */ ++ u16 kmf2; /* key management field 2 */ ++ u16 kmf3; /* key management field 3 */ ++ u8 varpart[80]; /* variable part */ ++} __packed; ++ ++#define AESDATA_KEY_SIZE sizeof(struct aesdatakeytoken) ++#define AESCIPHER_KEY_SIZE sizeof(struct aescipherkeytoken) ++ ++#define MAX_SECURE_KEY_SIZE MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) ++#define MIN_SECURE_KEY_SIZE MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) + + struct pkey_seckey { +- u8 seckey[SECURE_KEY_SIZE]; /* the secure key blob */ ++ u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */ + }; + + struct pkey_clrkey { + u8 clrkey[32]; /* 16, 24, or 32 byte clear key value */ + }; + +-#define PKEY_IOCTL_MAGIC 'p' +-#define AUTOSELECT 0xFFFF +-#define PKEYDEVICE "/dev/pkey" +-#define PKEY_KEYTYPE_AES_128 1 +-#define PKEY_KEYTYPE_AES_192 2 +-#define PKEY_KEYTYPE_AES_256 3 ++#define PKEY_IOCTL_MAGIC 'p' ++#define AUTOSELECT 0xFFFF ++#define PKEYDEVICE "/dev/pkey" ++#define PKEY_KEYTYPE_AES_128 1 ++#define PKEY_KEYTYPE_AES_192 2 ++#define PKEY_KEYTYPE_AES_256 3 + + struct pkey_genseck { + u16 cardnr; /* in: card to use or FFFF for any */ +@@ -82,6 +133,100 @@ struct pkey_verifykey { + + #define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey) + ++enum pkey_key_type { ++ PKEY_TYPE_CCA_DATA = (u32) 1, ++ PKEY_TYPE_CCA_CIPHER = (u32) 2, ++}; ++ ++enum pkey_key_size { ++ PKEY_SIZE_AES_128 = (u32) 128, ++ PKEY_SIZE_AES_192 = (u32) 192, ++ PKEY_SIZE_AES_256 = (u32) 256, ++ PKEY_SIZE_UNKNOWN = (u32) 0xFFFFFFFF, ++}; ++ ++#define PKEY_FLAGS_MATCH_CUR_MKVP 0x00000002 ++#define PKEY_FLAGS_MATCH_ALT_MKVP 0x00000004 ++ ++#define PKEY_KEYGEN_XPRT_SYM 0x00008000 ++#define PKEY_KEYGEN_XPRT_UASY 0x00004000 ++#define PKEY_KEYGEN_XPRT_AASY 0x00002000 ++#define PKEY_KEYGEN_XPRT_RAW 0x00001000 ++#define PKEY_KEYGEN_XPRT_CPAC 0x00000800 ++#define PKEY_KEYGEN_XPRT_DES 0x00000080 ++#define PKEY_KEYGEN_XPRT_AES 0x00000040 ++#define PKEY_KEYGEN_XPRT_RSA 0x00000008 ++ ++struct pkey_apqn { ++ u16 card; ++ u16 domain; ++}; ++ ++struct pkey_genseck2 { ++ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */ ++ u32 apqn_entries; /* in: # of apqn target list entries */ ++ enum pkey_key_type type; /* in: key type to generate */ ++ enum pkey_key_size size; /* in: key size to generate */ ++ u32 keygenflags; /* in: key generation flags */ ++ u8 *key; /* in: pointer to key blob buffer */ ++ u32 keylen; /* in: available key blob buffer size */ ++ /* out: actual key blob size */ ++}; ++ ++#define PKEY_GENSECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x11, struct pkey_genseck2) ++ ++struct pkey_clr2seck2 { ++ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */ ++ u32 apqn_entries; /* in: # of apqn target list entries */ ++ enum pkey_key_type type; /* in: key type to generate */ ++ enum pkey_key_size size; /* in: key size to generate */ ++ u32 keygenflags; /* in: key generation flags */ ++ struct pkey_clrkey clrkey; /* in: the clear key value */ ++ u8 *key; /* in: pointer to key blob buffer */ ++ u32 keylen; /* in: available key blob buffer size */ ++ /* out: actual key blob size */ ++}; ++ ++#define PKEY_CLR2SECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x12, struct pkey_clr2seck2) ++ ++struct pkey_verifykey2 { ++ u8 *key; /* in: pointer to key blob */ ++ u32 keylen; /* in: key blob size */ ++ u16 cardnr; /* in/out: card number */ ++ u16 domain; /* in/out: domain number */ ++ enum pkey_key_type type; /* out: the key type */ ++ enum pkey_key_size size; /* out: the key size */ ++ u32 flags; /* out: additional key info flags */ ++}; ++ ++#define PKEY_VERIFYKEY2 _IOWR(PKEY_IOCTL_MAGIC, 0x17, struct pkey_verifykey2) ++ ++struct pkey_apqns4key { ++ u8 *key; /* in: pointer to key blob */ ++ u32 keylen; /* in: key blob size */ ++ u32 flags; /* in: match controlling flags */ ++ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/ ++ u32 apqn_entries; /* in: max # of apqn entries in list */ ++ /* out: # apqns stored into the list */ ++}; ++ ++#define PKEY_APQNS4K _IOWR(PKEY_IOCTL_MAGIC, 0x1B, struct pkey_apqns4key) ++ ++struct pkey_apqns4keytype { ++ enum pkey_key_type type; /* in: key type */ ++ u8 cur_mkvp[32]; /* in: current mkvp */ ++ u8 alt_mkvp[32]; /* in: alternate mkvp */ ++ u32 flags; /* in: match controlling flags */ ++ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/ ++ u32 apqn_entries; /* in: max # of apqn entries in list */ ++ /* out: # apqns stored into the list */ ++}; ++ ++#define PKEY_APQNS4KT _IOWR(PKEY_IOCTL_MAGIC, 0x1C, struct pkey_apqns4keytype) ++ ++#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA" ++#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER" ++ + #define PAES_BLOCK_SIZE 16 + #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) + #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) +@@ -89,14 +234,13 @@ struct pkey_verifykey { + int open_pkey_device(bool verbose); + + int generate_secure_key_random(int pkey_fd, const char *keyfile, +- size_t keybits, bool xts, u16 card, u16 domain, +- bool verbose); ++ size_t keybits, bool xts, const char *key_type, ++ const char **apqns, bool verbose); + + int generate_secure_key_clear(int pkey_fd, const char *keyfile, + size_t keybits, bool xts, +- const char *clearkeyfile, +- u16 card, u16 domain, +- bool verbose); ++ const char *clearkeyfile, const char *key_type, ++ const char **apqns, bool verbose); + + u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, + bool verbose); +@@ -107,13 +251,20 @@ int write_secure_key(const char *keyfile, const u8 *secure_key, + int validate_secure_key(int pkey_fd, + u8 *secure_key, size_t secure_key_size, + size_t *clear_key_bitsize, int *is_old_mk, +- bool verbose); ++ const char **apqns, bool verbose); + +-int generate_key_verification_pattern(const char *key, size_t key_size, ++int generate_key_verification_pattern(const u8 *key, size_t key_size, + char *vp, size_t vp_len, bool verbose); + +-int get_master_key_verification_pattern(const u8 *secure_key, +- size_t secure_key_size, u64 *mkvp, +- bool verbose); ++int get_master_key_verification_pattern(const u8 *key, size_t key_size, ++ u64 *mkvp, bool verbose); ++ ++bool is_cca_aes_data_key(const u8 *key, size_t key_size); ++bool is_cca_aes_cipher_key(const u8 *key, size_t key_size); ++bool is_xts_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); ++int get_min_card_level_for_keytype(const char *key_type); ++int check_aes_cipher_key(const u8 *key, size_t key_size); + + #endif +diff --git a/zkey/utils.c b/zkey/utils.c +index 9dc4af7..e70ebcd 100644 +--- a/zkey/utils.c ++++ b/zkey/utils.c +@@ -118,6 +118,49 @@ out: + return rc; + } + ++/** ++ * Returns the level of the card. For a CEX3C 3 is returned, for a CEX4C 4, ++ * and so on. ++ * ++ * @param[in] card card number ++ * ++ * @returns The card level, or -1 of the level can not be determined. ++ */ ++int sysfs_get_card_level(int card) ++{ ++ char *dev_path; ++ char type[20]; ++ int rc; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); ++ if (!util_path_is_dir(dev_path)) { ++ rc = -1; ++ goto out; ++ } ++ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { ++ rc = -1; ++ goto out; ++ } ++ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { ++ rc = -1; ++ goto out; ++ } ++ if (type[4] != 'C') { ++ rc = -1; ++ goto out; ++ } ++ if (type[3] < '1' || type[3] > '9') { ++ rc = -1; ++ goto out; ++ } ++ ++ rc = type[3] - '0'; ++ ++out: ++ free(dev_path); ++ return rc; ++} ++ + /** + * Gets the 8 character ASCII serial number string of an card from the sysfs. + * +@@ -436,12 +479,14 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) + { + struct print_apqn_info *info = (struct print_apqn_info *)handler_data; + struct mk_info mk_info; +- int rc; ++ int rc, level; + + rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); + if (rc == -ENOTSUP) + return rc; + ++ level = sysfs_get_card_level(card); ++ + util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain); + + if (rc == 0) { +@@ -470,6 +515,11 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) + util_rec_set(info->rec, "OLD", "?"); + } + ++ if (level > 0) ++ util_rec_set(info->rec, "TYPE", "CEX%dC", level); ++ else ++ util_rec_set(info->rec, "TYPE", "?"); ++ + util_rec_print(info->rec); + + return 0; +@@ -499,6 +549,7 @@ int print_mk_info(const char *apqns, bool verbose) + util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK"); + util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK"); + util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK"); ++ util_rec_def(info.rec, "TYPE", UTIL_REC_ALIGN_LEFT, 6, "TYPE"); + util_rec_print_hdr(info.rec); + + rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose); +@@ -511,6 +562,7 @@ struct cross_check_info { + u64 mkvp; + u64 new_mkvp; + bool key_mkvp; ++ int min_level; + u32 num_cur_match; + u32 num_old_match; + u32 num_new_match; +@@ -525,7 +577,7 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) + struct cross_check_info *info = (struct cross_check_info *)handler_data; + struct mk_info mk_info; + char temp[200]; +- int rc; ++ int rc, level; + + rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); + if (rc == -ENODEV) { +@@ -539,6 +591,19 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) + + info->num_checked++; + ++ if (info->min_level >= 0) { ++ level = sysfs_get_card_level(card); ++ ++ if (level < info->min_level) { ++ info->print_mks = 1; ++ info->mismatch = 1; ++ sprintf(temp, "WARNING: APQN %02x.%04x: The card level " ++ "is less than CEX%dC.", card, domain, ++ info->min_level); ++ util_print_indented(temp, 0); ++ } ++ } ++ + if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) { + info->print_mks = 1; + sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " +@@ -662,6 +727,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) + * @param[in] mkvp The master key verification pattern of a secure key. + * If this is all zero, then the master keys are not + * matched against it. ++ * @param[in] min_level The minimum card level required. If min_level is -1 then ++ * the card level is not checked. + * @param[in] print_mks if true, then a the full master key info of all + * specified APQns is printed, in case of a mismatch. + * @param[in] verbose if true, verbose messages are printed +@@ -671,7 +738,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) + * -ENOTSUP is returned when the mkvps sysfs attribute is not + * available, because the zcrypt kernel module is on an older level. + */ +-int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) ++int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, ++ bool print_mks, bool verbose) + { + struct cross_check_info info; + char temp[200]; +@@ -680,10 +748,12 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) + memset(&info, 0, sizeof(info)); + info.key_mkvp = mkvp != 0; + info.mkvp = mkvp; ++ info.min_level = min_level; + info.verbose = verbose; + +- pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s", +- mkvp, apqns != NULL ? apqns : "ANY"); ++ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx and " ++ "min-level %d: %s", mkvp, min_level, ++ apqns != NULL ? apqns : "ANY"); + + rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose); + if (rc != 0) +@@ -723,3 +793,27 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) + + return rc; + } ++ ++/* ++ * Prompts for yes or no. Returns true if 'y' or 'yes' was entered. ++ * ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns true if 'y' or 'yes' was entered (case insensitive). Returns false ++ * otherwise. ++ */ ++bool prompt_for_yes(bool verbose) ++{ ++ char str[20]; ++ ++ if (fgets(str, sizeof(str), stdin) == NULL) ++ return false; ++ ++ if (str[strlen(str) - 1] == '\n') ++ str[strlen(str) - 1] = '\0'; ++ pr_verbose(verbose, "Prompt reply: '%s'", str); ++ if (strcasecmp(str, "y") == 0 || strcasecmp(str, "yes") == 0) ++ return true; ++ ++ return false; ++} +diff --git a/zkey/utils.h b/zkey/utils.h +index c4dfb2b..2a915eb 100644 +--- a/zkey/utils.h ++++ b/zkey/utils.h +@@ -18,6 +18,8 @@ int sysfs_is_card_online(int card); + + int sysfs_is_apqn_online(int card, int domain); + ++int sysfs_get_card_level(int card); ++ + int sysfs_get_serialnr(int card, char serialnr[9], bool verbose); + + #define MK_STATE_EMPTY 0 +@@ -48,7 +50,9 @@ int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, + + int print_mk_info(const char *apqns, bool verbose); + +-int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, +- bool verbose); ++int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, ++ bool print_mks, bool verbose); ++ ++bool prompt_for_yes(bool verbose); + + #endif +diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c +index c683fd4..7ba2842 100644 +--- a/zkey/zkey-cryptsetup.c ++++ b/zkey/zkey-cryptsetup.c +@@ -1329,22 +1329,25 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key, + return rc; + } + +-static int check_keysize_and_cipher_mode(size_t keysize) ++static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize) + { +- if (keysize == 0) { ++ if (keysize < MIN_SECURE_KEY_SIZE || ++ keysize > 2 * MAX_SECURE_KEY_SIZE) { + warnx("Invalid volume key size"); + return -EINVAL; + } + + if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) { +- if (keysize != 2 * SECURE_KEY_SIZE) { ++ if (keysize < 2 * MIN_SECURE_KEY_SIZE || ++ (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)); + return -EINVAL; + } + } else { +- if (keysize != SECURE_KEY_SIZE) { ++ if (keysize > MAX_SECURE_KEY_SIZE || ++ (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)); +@@ -1377,7 +1380,7 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, + vkeysize = crypt_get_volume_key_size(g.cd); + pr_verbose("Volume key size: %lu", vkeysize); + +- rc = check_keysize_and_cipher_mode(vkeysize); ++ rc = check_keysize_and_cipher_mode(NULL, vkeysize); + if (rc != 0) + return rc; + +@@ -1489,7 +1492,7 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize, + keyslot = rc; + + rc = validate_secure_key(g.pkey_fd, (u8 *)vkey, vkeysize, clear_keysize, +- &is_old, g.verbose); ++ &is_old, NULL, g.verbose); + if (rc != 0) { + if (invalid_msg != NULL) + warnx("%s", invalid_msg); +@@ -1571,7 +1574,7 @@ static int reencipher_prepare(int token) + if (rc != 0) + goto out; + +- rc = generate_key_verification_pattern(key, keysize, ++ rc = generate_key_verification_pattern((u8 *)key, keysize, + reenc_tok.verification_pattern, + sizeof(reenc_tok.verification_pattern), + g.verbose); +@@ -1851,8 +1854,8 @@ static int reencipher_complete(int token) + + } + +- rc = generate_key_verification_pattern(key, keysize, vp, sizeof(vp), +- g.verbose); ++ rc = generate_key_verification_pattern((u8 *)key, keysize, vp, ++ sizeof(vp), g.verbose); + if (rc != 0) { + warnx("Failed to generate the verification pattern: %s", + strerror(-rc)); +@@ -1969,7 +1972,7 @@ static int command_validate(void) + goto out; + + rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize, +- &is_old_mk, g.verbose); ++ &is_old_mk, NULL, g.verbose); + is_valid = (rc == 0); + + token = find_token(g.cd, PAES_REENC_TOKEN_NAME); +@@ -1998,7 +2001,9 @@ static int command_validate(void) + printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); + printf(" Secure key size: %lu bytes\n", keysize); + printf(" XTS type key: %s\n", +- keysize > SECURE_KEY_SIZE ? "Yes" : "No"); ++ is_xts_key((u8 *)key, keysize) ? "Yes" : "No"); ++ printf(" Key type: %s\n", ++ get_key_type((u8 *)key, keysize)); + if (is_valid) { + printf(" Clear key size: %lu bits\n", clear_keysize); + printf(" Enciphered with: %s CCA master key (MKVP: " +@@ -2074,7 +2079,7 @@ static int command_setvp(void) + + token = find_token(g.cd, PAES_VP_TOKEN_NAME); + +- rc = generate_key_verification_pattern(key, keysize, ++ rc = generate_key_verification_pattern((const u8 *)key, keysize, + vp_tok.verification_pattern, + sizeof(vp_tok.verification_pattern), + g.verbose); +@@ -2129,12 +2134,12 @@ static int command_setkey(void) + if (newkey == NULL) + return EXIT_FAILURE; + +- rc = check_keysize_and_cipher_mode(newkey_size); ++ rc = check_keysize_and_cipher_mode(newkey, newkey_size); + if (rc != 0) + goto out; + + rc = validate_secure_key(g.pkey_fd, newkey, newkey_size, NULL, +- &is_old_mk, g.verbose); ++ &is_old_mk, NULL, g.verbose); + if (rc != 0) { + warnx("The secure key in file '%s' is not valid", + g.master_key_file); +@@ -2164,21 +2169,14 @@ static int command_setkey(void) + if (rc < 0) + goto out; + +- if (keysize != newkey_size) { +- warnx("The secure key in file '%s' has an invalid size", +- g.master_key_file); +- rc = -EINVAL; +- goto out; +- } +- +- if (memcmp(newkey, key, keysize) == 0) { ++ if (keysize == newkey_size && memcmp(newkey, key, keysize) == 0) { + warnx("The secure key in file '%s' is equal to the current " + "volume key, setkey is ignored", g.master_key_file); + rc = 0; + goto out; + } + +- rc = generate_key_verification_pattern((char *)newkey, newkey_size, vp, ++ rc = generate_key_verification_pattern(newkey, newkey_size, vp, + sizeof(vp), g.verbose); + if (rc != 0) { + warnx("Failed to generate the verification pattern: %s", +diff --git a/zkey/zkey.1 b/zkey/zkey.1 +index c7cea83..31fff43 100644 +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -62,7 +62,8 @@ in size. + The \fBzkey\fP tool can operate in two modes. When argument + .I secure\-key\-file + is specified then it operates on the secure key contained in the specified file. +-This applies to commands \fBgenerate\fP, \fBvalidate\fP, and \fBreencipher\fP. ++This applies to commands \fBgenerate\fP, \fBvalidate\fP, \fBreencipher\fP, and ++\fBconvert\fP. + When the + .B \-\-name + option is specified then it operates on a secure key contained in the secure +@@ -79,6 +80,8 @@ key repository. + .RB [ \-\-xts | \-x ] + .RB [ \-\-clearkey | \-c + .IR clear\-key\-file ] ++.RB [ \-\-key-type | \-K ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -102,6 +105,8 @@ key repository. + .RB [ \-\-xts | \-x ] + .RB [ \-\-clearkey | \-c + .IR clear\-key\-file ] ++.RB [ \-\-key-type | \-K ++.IR type ] + .RB [ \-\-verbose | \-V ] + .PP + Use the +@@ -129,6 +134,15 @@ additional information can be associated with a secure key using the + , or the + .B \-\-sector-size + options. ++.PP ++You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, and ++\fBCCA-AESCIPHER\fP keys. Specify the type of the secure key using the ++.B \-\-key\-type ++option. The default key type is CCA-AESDATA. ++.PP ++.B Note: ++Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic ++adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. + . + .SS "Validating secure AES keys" + . +@@ -336,6 +350,12 @@ additional information can be associated with a secure key using the + , or the + .B \-\-sector-size + options. ++.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. ++For the supported environments and downloads, see: ++\fIhttp://www.ibm.com/security/cryptocards\fP + . + .SS "Export AES secure keys from the secure key repository" + . +@@ -354,7 +374,6 @@ to a file in the file system. Specify the name of the key that is to be exported + using the + .B \-\-name + option. You cannot use wildcards. +-When wildcards are used you must quote the value. + The exported secure key also remains in the secure key repository. + . + .SS "List AES secure keys contained in the secure key repository" +@@ -369,6 +388,8 @@ The exported secure key also remains in the secure key repository. + .IR card1.domain1[,card2.domain2[,...]] ] + .RB [ \-\-volume-type | \-t + .IR type ] ++.RB [ \-\-key-type | \-K ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -383,7 +404,7 @@ listed that are associated with the specified volume and device-mapper name. + .PP + The + .B list +-command displays the attributes of the secure keys, such as key sizes, ++command displays the attributes of the secure keys, such as key sizes, key type, + whether it is a secure key that can be used for the XTS cipher mode, the textual + description, associated cryptographic adapters (APQNs) and volumes, the + sector size, the key verification pattern, and timestamps for key creation, last +@@ -411,9 +432,11 @@ option. + .PP + .B Note: + When removing a secure key that is associated with one or multiple volumes, ++and the key's volume type is \fBplain\fP, + a message informs you about the associated volumes. When the secure key is + removed, these volumes can no longer be used, unless you have a backup of the +-secure key. ++secure key. For keys with volume type \fBluks2\fP no such message is issued, ++because the secure key is contained in the LUKS2 header. + . + .SS "Change existing AES secure keys contained the secure key repository" + . +@@ -490,6 +513,15 @@ option and the new name using the + .B \-\-new-name + option. You cannot use wildcards. + . ++.B Note: ++When renaming a secure key that is associated with one or multiple volumes and ++the key's volume type is \fBplain\fP, a message informs you about the ++associated volumes. When the secure key is renamed, these volumes can no ++longer be used, unless you change the name of the secure key in the 'cryptsetup ++plainOpen' commands and in the '/etc/crypttab' entries. ++For keys with volume type \fBluks2\fP no such message is issued, because the ++secure key is contained in the LUKS2 header. ++. + .SS "Copy (duplicate) existing AES secure keys in the secure key repository" + . + .B zkey +@@ -649,6 +681,72 @@ questions, you can specify the + option. These options are passed to the generated command(s) and behave in the + same way as with \fBcryptsetup\fP. + . ++.SS "Convert existing AES secure keys from one key type to another type" ++. ++.B zkey ++.BR convert | con ++.I secure\-key\-file ++.RB \-\-key-type | \-K ++.IR type ++.RB [ \-\-no\-apqn\-check ] ++.RB [ \-\-force | \-F ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++.B zkey ++.BR convert | con ++.B \-\-name | \-N ++.IR key-name ++.RB \-\-key-type | \-K ++.IR type ++.RB [ \-\-no\-apqn\-check ] ++.RB [ \-\-force | \-F ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B convert ++command to convert an existing secure key from one key type to another type. ++You can convert secure keys of type CCA-AESDATA to type CCA-AESCIPHER only. ++ ++.B Note: ++Secure keys converted to type \fBCCA-AESCIPHER\fP require an IBM cryptographic ++adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. ++ ++The secure key can either be contained in a file in the file system, or in a ++secure key repository. To convert a secure key contained in a file, specify ++the file name with option \fIsecure\-key\-file\fP. To convert a secure key ++contained in the secure key repository, specify the name of the key ++that is to be converted using the ++.B \-\-name ++option. You cannot use wildcards. The convert command prompts for ++a confirmation, unless you specify the ++.B \-\-force ++option. ++.PP ++.B Note: ++Converting a secure key is irreversible! ++When converting a secure key that is associated with one or multiple volumes, ++a message informs you about the associated volumes. When the secure key is ++converted, this might have an effect on these volumes. ++.P ++For volumes with volume type \fBplain\fP, you must adapt the crypttab entries ++and change the key size parameter to \fBsize=\fP or run ++command \fBzkey crypttab --volumes \fP for each associated volume to ++re-generate the crypttab entries. ++.P ++Associated volumes of type \fLUKS2\fP still contain the secure AES volume key of ++the original type. To change the secure AES volume key in the LUKS2 header, ++run command \fBzkey-cryptsetup setkey --master-key-file ++\fP for each associated volume. ++. ++.P ++.B Note: ++The \fBconvert\fP command requires the CCA host library (libcsulcca.so) ++to be installed. The required CCA IBM cryptographic adapter firmware version ++is 6.3.27 or later. For the supported environments and downloads, see: ++\fIhttp://www.ibm.com/security/cryptocards\fP ++. + . + . + . +@@ -718,6 +816,13 @@ This option is only available if + has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, + the default volume type is \fBplain\fP. + 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 ++and \fBCCA-AESCIPHER\fP. If this option is omitted, then a secure key of type ++CCA-AESDATA is generated. Secure keys of type \fBCCA-AESCIPHER\fP require an ++IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g. ++a CEX6C. + . + . + . +@@ -897,6 +1002,11 @@ This option is only available if + .B zkey + has been compiled with LUKS2 support enabled. + 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 ++and \fBCCA-AESCIPHER\fP. Only keys with the specified key type are listed. ++This option is only used for secure keys contained in the secure key repository. + . + . + . +@@ -1160,6 +1270,30 @@ cryptsetup command(s). + . + . + . ++.SS "Options for the convert command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You cannot ++use wildcards. ++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 to which the secure key shall be converted to. ++Possible values are \fBCCA-AESCIPHER\fP. Secure keys of type \fBCCA-AESCIPHER\fP ++require an IBM cryptographic adapter in CCA coprocessor mode of version 6 or ++later, e.g. a CEX6C. ++.TP ++.BR \-\-no\-apqn\-check ++Do not check if the associated APQNs are available and capable of converting ++the secure key to type CCA-AESCIPHER. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-F ", " \-\-force\fP ++The user is prompted to confirm the convertion of a secure key. Use this option ++to convert a secure key without prompting for a confirmation. ++. ++. ++. + .SS "General options" + .TP + .BR \-V ", " \-\-verbose +diff --git a/zkey/zkey.c b/zkey/zkey.c +index a0dbc0b..3ae9a9b 100644 +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -73,6 +73,7 @@ static struct zkey_globals { + long int sector_size; + char *volume_type; + char *newname; ++ char *key_type; + bool run; + bool batch_mode; + char *keyfile; +@@ -105,6 +106,7 @@ static struct zkey_globals { + #define COMMAND_COPY "copy " + #define COMMAND_CRYPTTAB "crypttab" + #define COMMAND_CRYPTSETUP "cryptsetup" ++#define COMMAND_CONVERT "convert" + + #define ZKEY_COMMAND_MAX_LEN 10 + +@@ -216,6 +218,15 @@ static struct util_opt opt_vec[] = { + .command = COMMAND_GENERATE, + }, + #endif ++ { ++ .option = { "key-type", required_argument, NULL, 'K'}, ++ .argument = "type", ++ .desc = "The type of the key. Possible values are '" ++ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " ++ "When this option is omitted, the default is '" ++ KEY_TYPE_CCA_AESDATA"'", ++ .command = COMMAND_GENERATE, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -432,6 +443,15 @@ static struct util_opt opt_vec[] = { + .command = COMMAND_LIST, + }, + #endif ++ { ++ .option = { "key-type", required_argument, NULL, 'K'}, ++ .argument = "type", ++ .desc = "The type of the key. Possible values are '" ++ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " ++ "Use this option to list all keys with the specified " ++ "key type.", ++ .command = COMMAND_LIST, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -745,6 +765,38 @@ static struct util_opt opt_vec[] = { + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + #endif ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ .command = COMMAND_CONVERT, ++ }, ++ { ++ .option = { "name", required_argument, NULL, 'N'}, ++ .argument = "NAME", ++ .desc = "Name of the secure AES key in the repository that is " ++ "to be converted", ++ .command = COMMAND_CONVERT, ++ }, ++ { ++ .option = { "key-type", required_argument, NULL, 'K'}, ++ .argument = "type", ++ .desc = "The type of the key to convert the secure key to. " ++ "Possible values are '"KEY_TYPE_CCA_AESCIPHER"'. ", ++ .command = COMMAND_CONVERT, ++ }, ++ { ++ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, ++ .desc = "Do not check if the associated APQN(s) are available", ++ .command = COMMAND_CONVERT, ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = {"force", 0, NULL, 'F'}, ++ .desc = "Do not prompt for a confirmation when converting a " ++ "key", ++ .command = COMMAND_CONVERT, ++ }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -793,6 +845,7 @@ static int command_rename(void); + static int command_copy(void); + static int command_crypttab(void); + static int command_cryptsetup(void); ++static int command_convert(void); + + static struct zkey_command zkey_commands[] = { + { +@@ -927,6 +980,21 @@ static struct zkey_command zkey_commands[] = { + .has_options = 1, + .need_keystore = 1, + }, ++ { ++ .command = COMMAND_CONVERT, ++ .abbrev_len = 3, ++ .function = command_convert, ++ .need_cca_library = 1, ++ .need_pkey_device = 1, ++ .short_desc = "Convert a secure AES key", ++ .long_desc = "Convert an existing secure AES key that is " ++ "either contained in SECURE-KEY-FILE or is stored " ++ "in the repository from one key type to another " ++ "type.", ++ .has_options = 1, ++ .pos_arg = "[SECURE-KEY-FILE]", ++ .pos_arg_optional = 1, ++ }, + { .command = NULL } + }; + +@@ -1009,9 +1077,8 @@ static int command_generate_clear(void) + + rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg, + g.keybits, g.xts, +- g.clearkeyfile, +- AUTOSELECT, AUTOSELECT, +- g.verbose); ++ g.clearkeyfile, g.key_type, ++ NULL, g.verbose); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1026,9 +1093,8 @@ static int command_generate_random(void) + int rc; + + rc = generate_secure_key_random(g.pkey_fd, g.pos_arg, +- g.keybits, g.xts, +- AUTOSELECT, AUTOSELECT, +- g.verbose); ++ g.keybits, g.xts, g.key_type, ++ NULL, g.verbose); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1048,7 +1114,7 @@ static int command_generate_repository(void) + rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, + g.apqns, g.noapqncheck, g.sector_size, + g.keybits, g.xts, g.clearkeyfile, +- g.volume_type, g.pkey_fd); ++ g.volume_type, g.key_type, g.pkey_fd); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1075,6 +1141,8 @@ static int command_generate(void) + util_prg_print_parse_error(); + return EXIT_FAILURE; + } ++ if (g.key_type == NULL) ++ g.key_type = KEY_TYPE_CCA_AESDATA; + if (g.name != NULL) + return command_generate_repository(); + if (g.pos_arg != NULL) { +@@ -1103,7 +1171,9 @@ static int command_generate(void) + return EXIT_FAILURE; + } + +- rc = cross_check_apqns(NULL, 0, true, g.verbose); ++ rc = cross_check_apqns(NULL, 0, ++ get_min_card_level_for_keytype(g.key_type), ++ true, g.verbose); + if (rc == -EINVAL) + return EXIT_FAILURE; + if (rc != 0 && rc != -ENOTSUP) { +@@ -1169,7 +1239,7 @@ static int command_reencipher_file(void) + return EXIT_FAILURE; + + rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL, +- &is_old_mk, g.verbose); ++ &is_old_mk, NULL, g.verbose); + if (rc != 0) { + warnx("The secure key in file '%s' is not valid", g.pos_arg); + rc = EXIT_FAILURE; +@@ -1385,14 +1455,14 @@ static int command_validate_file(void) + return EXIT_FAILURE; + + rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, +- &clear_key_size, &is_old_mk, g.verbose); ++ &clear_key_size, &is_old_mk, NULL, g.verbose); + if (rc != 0) { + warnx("The secure key in file '%s' is not valid", g.pos_arg); + rc = EXIT_FAILURE; + goto out; + } + +- rc = generate_key_verification_pattern((char *)secure_key, ++ rc = generate_key_verification_pattern(secure_key, + secure_key_size, vp, sizeof(vp), + g.verbose); + if (rc != 0) { +@@ -1416,9 +1486,11 @@ static int command_validate_file(void) + printf("Validation of secure key in file '%s':\n", g.pos_arg); + printf(" Status: Valid\n"); + printf(" Secure key size: %lu bytes\n", secure_key_size); ++ printf(" Key type: %s\n", ++ get_key_type(secure_key, secure_key_size)); + printf(" Clear key size: %lu bits\n", clear_key_size); + printf(" XTS type key: %s\n", +- secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No"); ++ is_xts_key(secure_key, secure_key_size) ? "Yes" : "No"); + printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n", + is_old_mk ? "OLD" : "CURRENT", mkvp); + printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, +@@ -1426,7 +1498,10 @@ static int command_validate_file(void) + printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, + &vp[VERIFICATION_PATTERN_LEN / 2]); + +- rc = cross_check_apqns(NULL, mkvp, true, g.verbose); ++ rc = cross_check_apqns(NULL, mkvp, ++ get_min_card_level_for_keytype( ++ get_key_type(secure_key, secure_key_size)), ++ true, g.verbose); + if (rc == -EINVAL) + return EXIT_FAILURE; + if (rc != 0 && rc != -ENOTSUP) { +@@ -1496,7 +1571,7 @@ static int command_import(void) + + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, + g.apqns, g.noapqncheck, g.sector_size, +- g.pos_arg, g.volume_type); ++ g.pos_arg, g.volume_type, &g.cca); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1530,7 +1605,7 @@ static int command_list(void) + int rc; + + rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns, +- g.volume_type); ++ g.volume_type, g.key_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1665,6 +1740,206 @@ static int command_cryptsetup(void) + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + ++/* ++ * Command handler for 'convert'. ++ * ++ * Converts secure keys from one key type to another ++ */ ++static int command_convert_file(void) ++{ ++ u8 output_key[2 * MAX_SECURE_KEY_SIZE]; ++ unsigned int output_key_size; ++ size_t secure_key_size; ++ int rc, is_old_mk; ++ int selected = 1; ++ u8 *secure_key; ++ int min_level; ++ u64 mkvp; ++ ++ if (g.name != NULL) { ++ warnx("Option '--name|-N' is not valid for " ++ "re-enciphering a key outside of the repository"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ if (g.noapqncheck) { ++ warnx("Option '--no-apqn-check' is not valid for " ++ "converting a key outside of the repository"); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ ++ min_level = get_min_card_level_for_keytype(g.key_type); ++ if (min_level < 0) { ++ warnx("Invalid key-type specified: %s", g.key_type); ++ return EXIT_FAILURE; ++ } ++ ++ rc = cross_check_apqns(NULL, 0, min_level, true, g.verbose); ++ if (rc == -EINVAL) ++ return EXIT_FAILURE; ++ if (rc != 0 && rc != -ENOTSUP) { ++ warnx("Your master key setup is improper"); ++ return EXIT_FAILURE; ++ } ++ ++ /* Read the secure key to be re-enciphered */ ++ secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); ++ if (secure_key == NULL) ++ return EXIT_FAILURE; ++ ++ rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL, ++ &is_old_mk, NULL, g.verbose); ++ if (rc != 0) { ++ warnx("The secure key in file '%s' is not valid", g.pos_arg); ++ rc = EXIT_FAILURE; ++ 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 (strcasecmp(get_key_type(secure_key, secure_key_size), ++ g.key_type) == 0) { ++ warnx("The secure key in file '%s' is already of type %s", ++ g.pos_arg, get_key_type(secure_key, secure_key_size)); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ if (is_cca_aes_data_key(secure_key, secure_key_size)) { ++ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) { ++ warnx("The secure key in file '%s' can not be " ++ "converted into type %s", g.pos_arg, g.key_type); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { ++ warnx("The secure key in file '%s' is already of type %s", ++ g.pos_arg, KEY_TYPE_CCA_AESCIPHER); ++ rc = EXIT_FAILURE; ++ goto out; ++ } else { ++ warnx("The secure key in file '%s' has an unsupported key type", ++ g.pos_arg); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, ++ FLAG_SEL_CCA_MATCH_CUR_MKVP, ++ g.verbose); ++ if (rc == -ENOTSUP) { ++ rc = 0; ++ selected = 0; ++ } ++ if (rc != 0) { ++ warnx("No APQN found that is suitable for " ++ "converting the secure AES key in file '%s'", g.pos_arg); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ if (!g.force) { ++ util_print_indented("ATTENTION: Converting a secure key is " ++ "irreversible, and might have an effect " ++ "on the volumes encrypted with it!", 0); ++ printf("%s: Convert key in file '%s' [y/N]? ", ++ program_invocation_short_name, g.pos_arg); ++ if (!prompt_for_yes(g.verbose)) { ++ warnx("Operation aborted"); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ } ++ ++ memset(output_key, 0, sizeof(output_key)); ++ output_key_size = sizeof(output_key); ++ rc = convert_aes_data_to_cipher_key(&g.cca, secure_key, secure_key_size, ++ output_key, &output_key_size, ++ g.verbose); ++ if (rc != 0) { ++ warnx("Converting the secure key from %s to %s has failed", ++ get_key_type(secure_key, secure_key_size), g.key_type); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = restrict_key_export(&g.cca, output_key, output_key_size, ++ g.verbose); ++ if (rc != 0) { ++ warnx("Export restricting the converted secure key has failed"); ++ if (!selected) ++ print_msg_for_cca_envvars("secure AES key"); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ pr_verbose("Secure key was converted successfully"); ++ ++ /* Write the converted secure key */ ++ rc = write_secure_key(g.outputfile ? g.outputfile : g.pos_arg, ++ output_key, output_key_size, g.verbose); ++ if (rc != 0) ++ rc = EXIT_FAILURE; ++out: ++ free(secure_key); ++ return rc; ++} ++ ++/* ++ * Command handler for 'convert in repository'. ++ * ++ * Converts secure keys from one key type to another ++ */ ++static int command_convert_repository(void) ++{ ++ int rc; ++ ++ if (g.name == NULL) { ++ misc_print_required_parm("--name/-N"); ++ return EXIT_FAILURE; ++ } ++ ++ rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck, ++ g.force, g.pkey_fd, &g.cca); ++ ++ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++/* ++ * Command handler for 'convert'. ++ * ++ * Converts secure keys from one key type to another ++ */ ++static int command_convert(void) ++{ ++ if (g.key_type == NULL) { ++ misc_print_required_parm("--key-type/-K"); ++ return EXIT_FAILURE; ++ } ++ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) { ++ warnx("Secure keys can only be converted into key type %s", ++ KEY_TYPE_CCA_AESCIPHER); ++ return EXIT_FAILURE; ++ } ++ ++ if (g.pos_arg != NULL) ++ return command_convert_file(); ++ else ++ return command_convert_repository(); ++ ++ return EXIT_SUCCESS; ++} ++ + /** + * Opens the keystore. The keystore directory is either the + * default directory or as specified in an environment variable +@@ -1851,6 +2126,9 @@ int main(int argc, char *argv[]) + case 'r': + g.run = 1; + break; ++ case 'K': ++ g.key_type = optarg; ++ break; + case 'F': + g.force = 1; + break; +-- +2.21.3 + + +From a9fe5d1477161d658bcade0f85309eefe9fcb0bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 13:29:28 +0100 +Subject: [PATCH 36/44] zcrypt: CEX7S exploitation support (#1723837) + +Description: CEX7S exploitation support to lszcrypt, chzcrypt + and zcryptstats. + +Upstream-ID: b631c74df57e9ea52f54b4d6be79b63bc89e5eee +Upstream-ID: e9c030f2026b1b8e0399679600845c298aeb508d +Upstream-ID: 784ac7d190080a3cf230995eedd3743bddfa4b5c +Upstream-ID: 4fc0c3cfefb8fb23a83ef629ac3f4a967fc0e77f +Upstream-ID: e15e8a1bfa15e2179f30c6fe2e937ddd1a5e53c1 +--- + zconf/zcrypt/chzcrypt.8 | 4 +-- + zconf/zcrypt/chzcrypt.c | 4 +-- + zconf/zcrypt/lszcrypt.8 | 66 ++++++++++++++++++++++++++++++++++++-- + zconf/zcrypt/lszcrypt.c | 20 +++++++----- + zconf/zcrypt/zcryptstats.8 | 5 +++ + zconf/zcrypt/zcryptstats.c | 27 ++++++++++------ + 6 files changed, 102 insertions(+), 24 deletions(-) + +diff --git a/zconf/zcrypt/chzcrypt.8 b/zconf/zcrypt/chzcrypt.8 +index 2021645..2697c96 100644 +--- a/zconf/zcrypt/chzcrypt.8 ++++ b/zconf/zcrypt/chzcrypt.8 +@@ -1,8 +1,8 @@ +-.\" Copyright 2017 IBM Corp. ++.\" Copyright 2019 IBM Corp. + .\" 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 CHZCRYPT 8 "OCT 2017" "s390-tools" ++.TH CHZCRYPT 8 "AUG 2019" "s390-tools" + .SH NAME + chzcrypt \- modify zcrypt configuration + .SH SYNOPSIS +diff --git a/zconf/zcrypt/chzcrypt.c b/zconf/zcrypt/chzcrypt.c +index 6521056..2fc8441 100644 +--- a/zconf/zcrypt/chzcrypt.c ++++ b/zconf/zcrypt/chzcrypt.c +@@ -1,7 +1,7 @@ + /* + * chzcrypt - Tool to modify zcrypt configuration + * +- * Copyright IBM Corp. 2008, 2017 ++ * Copyright IBM Corp. 2008, 2019 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. +@@ -47,7 +47,7 @@ const struct util_prg prg = { + { + .owner = "IBM Corp.", + .pub_first = 2008, +- .pub_last = 2017, ++ .pub_last = 2019, + }, + UTIL_PRG_COPYRIGHT_END + } +diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8 +index 826e109..f9ce4a5 100644 +--- a/zconf/zcrypt/lszcrypt.8 ++++ b/zconf/zcrypt/lszcrypt.8 +@@ -1,6 +1,6 @@ + .\" lszcrypt.8 + .\" +-.\" Copyright 2017 IBM Corp. ++.\" Copyright 2019 IBM Corp. + .\" s390-tools is free software; you can redistribute it and/or modify + .\" it under the terms of the MIT license. See LICENSE for details. + .\" +@@ -10,7 +10,7 @@ + .\" nroff -man lszcrypt.8 + .\" to process this source + .\" +-.TH LSZCRYPT 8 "OCT 2017" "s390-tools" ++.TH LSZCRYPT 8 "AUG 2019" "s390-tools" + .SH NAME + lszcrypt \- display zcrypt device and configuration information + .SH SYNOPSIS +@@ -111,6 +111,68 @@ Displays help text and exits. + .TP 8 + .B -v, --version + Displays version information and exits. ++.SH VERBOSE LISTING DETAILS ++Some of the columns showing up in verbose listing mode may need some ++explanation: ++.TP ++.B TYPE and HWTYPE ++The HWTYPE is a numeric value showing which type of hardware the zcrypt ++device driver presumes that this crypto card is. The currently known values ++are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6 and 13=CEX7. ++.br ++The TYPE is a human readable value showing the hardware type and the basic ++function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So ++for example CEX6P means a CEX6 card in EP11 Coprocessor mode. ++.TP ++.B REQUESTS ++This is the counter value of successful processed requests on card or queue ++level. Successful here means the request was processed without any failure ++in the whole processing chain. ++.TP ++.B PENDING ++The underlying firmware and hardware layer usually provide some queuing ++space for requests. When this queue is already filled up, the zcrypt device ++driver maintains a software queue of pending requests. The sum of these ++both values is displayed here and shows the amount of requests waiting for ++processing on card or queue level. ++.TP ++.B FUNCTIONS ++This column shows firmware and hardware function details: ++.br ++S - APSC available: card/queue can handle requests with the special bit ++enabled. ++.br ++M - Accelerator card/queue with support for RSA ME with up to 4k key size. ++.br ++C - Accelerator card/queue with support for RSA CRT with up to 4k key size. ++.br ++D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode). ++.br ++A - Card/queue is providing Accelerator functions (this is the Accelerator mode). ++.br ++X - Card/queue is providing EP11 functions (this is the EP11 Coprocessor mode). ++.br ++N - APXA available (ability to address more than 16 crypto cards and domains). ++.br ++F - Full function support (opposed to restricted function support, see below). ++.br ++R - Restricted function support. The F and R flag both reflect if a ++hypervisor is somehow restricting this crypto resource in a virtual ++environment. Dependent on the hypervisor configuration the crypto requests ++may be filtered by the hypervisor to allow only a subset of functions ++within the virtual runtime environment. For example a shared CCA ++Coprocessor may be restricted by the hypervisor to allow only clear key ++operations within the guests. ++.TP ++.B DRIVER ++.br ++Shows which card or queue device driver currently handles this crypto ++resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX7 ++hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware), ++cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved ++for use by kvm hypervisor for kvm guests and not accessible to host ++applications). It is also valid to have no driver handling a queue which is ++shown as a -no-driver- entry. + .SH EXAMPLES + .TP + .B lszcrypt +diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c +index 580407f..1b2befe 100644 +--- a/zconf/zcrypt/lszcrypt.c ++++ b/zconf/zcrypt/lszcrypt.c +@@ -1,7 +1,7 @@ + /** + * lszcrypt - Display zcrypt devices and configuration settings + * +- * Copyright IBM Corp. 2008, 2018 ++ * Copyright IBM Corp. 2008, 2019 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. +@@ -12,6 +12,7 @@ + + #include "lib/util_base.h" + #include "lib/util_file.h" ++#include "lib/util_libc.h" + #include "lib/util_opt.h" + #include "lib/util_panic.h" + #include "lib/util_path.h" +@@ -85,7 +86,7 @@ const struct util_prg prg = { + { + .owner = "IBM Corp.", + .pub_first = 2008, +- .pub_last = 2018, ++ .pub_last = 2019, + }, + UTIL_PRG_COPYRIGHT_END + } +@@ -301,9 +302,10 @@ static void show_capability(const char *id_str) + printf("%s\n", CAP_CCA); + printf("%s", CAP_RNG); + break; +- case 10: +- case 11: +- case 12: ++ case 10: /* CEX4S */ ++ case 11: /* CEX5S */ ++ case 12: /* CEX6S */ ++ case 13: /* CEX7S */ + if (func_val & MASK_ACCEL) { + if (func_val & MASK_RSA4K) + printf("%s", CAP_RSA4K); +@@ -620,7 +622,7 @@ static void show_devices_argv(char *argv[]) + { + struct util_rec *rec = util_rec_new_wide("-"); + struct dirent **dev_vec, **subdev_vec; +- char *ap, *grp_dev, *path, card[16], sub_dev[16]; ++ char *ap, *grp_dev, *path, *card, *sub_dev; + int id, dom, i, n, dev_cnt, sub_cnt; + + /* check if ap driver is available */ +@@ -639,14 +641,16 @@ static void show_devices_argv(char *argv[]) + if (sscanf(argv[i], "%x.%x", &id, &dom) >= 1) { + /* at least the id field was valid */ + if (id >= 0 && dom >= 0) { /* single subdevice */ +- sprintf(sub_dev, "%02x.%04x", id, dom); ++ util_asprintf(&sub_dev, "%02x.%04x", id, dom); + grp_dev = util_path_sysfs("devices/ap/card%02x", + id); + show_subdevice(rec, grp_dev, sub_dev); + free(grp_dev); ++ free(sub_dev); + } else { /* group device */ +- sprintf(card, "card%02x", id); ++ util_asprintf(&card, "card%02x", id); + show_device(rec, card); ++ free(card); + } + return; + } +diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8 +index 8c31c69..7d000d2 100644 +--- a/zconf/zcrypt/zcryptstats.8 ++++ b/zconf/zcrypt/zcryptstats.8 +@@ -72,6 +72,11 @@ the parallel execution of cryptographic operations. + Cryptographic performance measurement data might not be available when Linux + is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an + error message and exits. ++.PP ++.B Note: ++\fBzcryptstats\fP utilizes the device node \fB/dev/chsc\fP. When this device ++node is not available, you might have to load kernel module \fBchsc_sch\fP using ++\fBmodprobe chsc_sch\fP to make it available. + . + . + . +diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c +index 136d5ba..3bb2078 100644 +--- a/zconf/zcrypt/zcryptstats.c ++++ b/zconf/zcrypt/zcryptstats.c +@@ -147,8 +147,9 @@ struct chsc_cmb_area { + #define CRYPTO_TYPE_CEX4S 10 + #define CRYPTO_TYPE_CEX5S 11 + #define CRYPTO_TYPE_CEX6S 12 ++#define CRYPTO_TYPE_CEX7S 13 + +-#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S ++#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX7S + + struct crypto_counter { + const char *name; +@@ -235,8 +236,8 @@ const struct crypto_mode mode_pcica[1] = { + .counters = counter_pcica }, + }; + +-#define NUM_CEX456_MODES 11 +-const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { ++#define NUM_CEX4567_MODES 11 ++const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = { + { 0 }, + { 0 }, + { 0 }, +@@ -256,7 +257,7 @@ const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { + .counters = counter_ep11 }, + }; + +-#define NUM_CRYPTO_TYPES 13 ++#define NUM_CRYPTO_TYPES 14 + const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { + { 0 }, + { 0 }, +@@ -275,12 +276,14 @@ const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { + .modes = mode_accel }, + { .name = "CEX3C", .num_modes = NUM_COPROC_MODES, + .modes = mode_coproc }, +- { .name = "CEX4", .num_modes = NUM_CEX456_MODES, +- .modes = mode_cex456 }, +- { .name = "CEX5", .num_modes = NUM_CEX456_MODES, +- .modes = mode_cex456 }, +- { .name = "CEX6", .num_modes = NUM_CEX456_MODES, +- .modes = mode_cex456 }, ++ { .name = "CEX4", .num_modes = NUM_CEX4567_MODES, ++ .modes = mode_cex4567 }, ++ { .name = "CEX5", .num_modes = NUM_CEX4567_MODES, ++ .modes = mode_cex4567 }, ++ { .name = "CEX6", .num_modes = NUM_CEX4567_MODES, ++ .modes = mode_cex4567 }, ++ { .name = "CEX7", .num_modes = NUM_CEX4567_MODES, ++ .modes = mode_cex4567 }, + }; + + +@@ -2393,7 +2396,11 @@ int main(int argc, char *argv[]) + + g.chsc_fd = open(CHSC_DEVICE, O_RDWR); + if (g.chsc_fd < 0) { ++ rc = errno; + warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno)); ++ if (rc == ENOENT) ++ warnx("You might have to load kernel module 'chsc_sch' " ++ "using 'modprobe chsc_sch'"); + return EXIT_FAILURE; + } + pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE); +-- +2.21.3 + + +From afcf268f8af2a6e925c44c057cb04c2dc38c9b10 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 13:48:23 +0100 +Subject: [PATCH 37/44] lstape, lsluns: handle non-zfcp; lin_tape multiple + paths (#1766569) + +Symptom: lstape shows unexpected additional Device suffix numbers in + excess output columns for each additional path of the same + tape/changer driven by the IBM lin_tape device driver + (independent of actual path failover enablement in lin_tape). + It also shows a wrong number of found devices in the header + (without --scsi-only). + + lstape prints error about "Unexpected extra argument:" and the + usage for "sg_inq" along with wrong tabular output. + + lsluns prints ENOENT error text for "cat" on SCSI device sysfs + attributes hba_id, wwpn, and fcp_lun. + + lstape with --verbose option prints ENOENT error text for + "cat" on SCSI device sysfs attributes hba_id and wwpn. + + lstape man page: Description of --type and filter + for channel tapes is incomplete. SCSI output description is + incomplete. + + lstape shows "N/A" instead of the HBA device bus-ID with + virtio-scsi-ccw. + +Problem: s390-tools-1.8.0 before the first upstream commit b627b8d8e1ab + ("Initial s390-tools-2.0.0 import") introduced SCSI + tape/changer output for lstape. It used the SCSI device serial + number as lookup key to find a match in IBM lin_tape device + driver proc-fs output for a given SCSI device name. Multiple + paths to the same tape/changer have the same serial number. + Multiple matches cause excess arguments to printf. Explaining + the resulting output, the bash man page says: "The format is + reused as necessary to consume all of the arguments." This + also causes a wrong number of found devices. + + The default bash settings have nullglob disabled so if + $SCSI_DEV/scsi_generic* aka + /sys/bus/scsi/devices/*:*:*:*/scsi_generic* does not match + anything, it leaves the glob pattern unmodified and + SG_DEV=$(basename $SG_DEV/*) results in the literal "*". If + $SG_INQ exists, it invoked sg_inq with more than the one + allowed positional argument for a SCSI generic device node + "sg_inq /dev/*". Causing error messages and the usage of + sg_inq to land in $TAPE_SERIAL. + + lsluns iterates SCSI generic devices and unconditionally + reads zfcp-specific SCSI device sysfs attributes hba_id, wwpn, + and fcp_lun. + + lstape --verbose unconditionally reads zfcp-specific SCSI + device sysfs attributes hba_id and wwpn. + + filter missing from synopsis. example + at wrong place with filter. filter + option description is a duplicate of filter + option description. SCSI output description misses fields. + + Lstape only used the zfcp-specific sysfs attribute hba_id. + +Solution: Prefer sysfs to find lin_tape device name for SCSI device. + Fallback: The lin_tape proc-fs output format has changed over + the years. The HBA device driver string can contain whitespace + (e.g. "Virtio SCSI HBA") and breaks the field numbers with + tokenized parsing. Grep for the SCSI device name as word (to + skip names with same substring, such as 0:0:1:1 also matching + 0:0:1:10) and cut the first field 'Number' (lin_tape device + name suffix). If there is no SCSI column at all [lin_tape + before v2.2.0] (and no SCSI LLDD or other column with a name + accidentally matching an existing SCSI device name), we get no + match and better bail out with the initialized "N/A" for the + lstape column "Device". + + To not have to rely on the nullglob setting, explicitly check + for the existence of $SCSI_DEV/scsi_generic before evaluating + SG_DEV=$(basename $SG_DEV/*). Also handle availability of + sg_inq but absence of scsi_generic individually to provide the + user with a hint if only sg is missing. + + Simply skip non-zfcp SCSI devices, such as iSCSI or + virtio-scsi-ccw, to not erroneously access absent attributes. + + Assume "N/A" for HBA and WWPN of non-zfcp SCSI devices, such + as iSCSI or virtio-scsi-ccw, to not erroneously access absent + zfcp-specific sysfs attributes. + + Add filter to synopsis. Move example + to option. Replace filter option + description. Move existing SCSI output description to a new + subsection and add description of missing fields. + + Also search sysfs for an ancestor with subsystem ccw. + +Reproduction: Attach more than one path to the same SCSI tape or changer and + load the IBM lin_tape device driver. + + Unload sg kernel module. + + Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw. + + Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw. + + man lstape + + Attach SCSI tape or changer with virtio-scsi-ccw to a KVM + guest and run "lstape --verbose". + +Upstream-ID: ef4dc7a45b1b80c58076a0a317245569d84b2754 +Upstream-ID: cdc787db1ba17dd2c0ed841fd7443bc32be63b65 +Upstream-ID: 80e0c41b896e1eeffc594416b4c2787ed29363b6 +Upstream-ID: eba744a25edcc7202068c1d46fd99d4c98c1acf2 +Upstream-ID: 4cf8f5f46c0f80b49a2a70854372289c3fda932b +Upstream-ID: b9b3d2d23069e021693a251a3aadd4eefc30e6ea +Upstream-ID: 34260f17360f1034c562c941b0f879c2204e40b7 +Upstream-ID: d5291eed1c46e1c97831e771b2975575ba268992 +--- + zconf/lsluns | 14 ++++++--- + zconf/lsluns.8 | 5 ++- + zconf/lstape | 69 ++++++++++++++++++++++++++++++++++------- + zconf/lstape.8 | 84 +++++++++++++++++++++++++++++++++++++++----------- + 4 files changed, 135 insertions(+), 37 deletions(-) + +diff --git a/zconf/lsluns b/zconf/lsluns +index f88aa0f..bfe48f1 100755 +--- a/zconf/lsluns ++++ b/zconf/lsluns +@@ -2,7 +2,7 @@ + # + # lsluns - list LUNs discovered in the FC SAN, or show encryption state of attached LUNs + # +-# Copyright IBM Corp. 2008, 2017 ++# Copyright IBM Corp. 2008, 2018 + # + # s390-tools is free software; you can redistribute it and/or modify + # it under the terms of the MIT license. See LICENSE for details. +@@ -152,6 +152,11 @@ sub get_lun_hash + my %lun_hash; + + foreach my $device () { ++ # skip non-zfcp SCSI devices and avoid file access error messages ++ next unless -r "$device/device/fcp_lun"; ++ next unless -r "$device/device/wwpn"; ++ next unless -r "$device/device/hba_id"; ++ + my $l = `cat $device/device/fcp_lun`; + my $p = `cat $device/device/wwpn`; + my $a = `cat $device/device/hba_id`; +@@ -170,9 +175,8 @@ sub get_lun_hash + sub lsluns_usage { + print <] ... [-p ] ... [-h] [-v] + +@@ -220,7 +224,7 @@ EOD + + sub lsluns_version { + print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n"; +- print "Copyright IBM Corp. 2008, 2017\n"; ++ print "Copyright IBM Corp. 2008, 2018\n"; + } + + sub lsluns_invalid_usage { +diff --git a/zconf/lsluns.8 b/zconf/lsluns.8 +index cca649a..a4a1849 100644 +--- a/zconf/lsluns.8 ++++ b/zconf/lsluns.8 +@@ -28,9 +28,8 @@ zfcp-attached LUNs + + .SH DESCRIPTION + .PP +-This tool is designed for environments where all SCSI devices are attached +-through the zfcp device driver. Expect error messages in mixed environments +-such as with iSCSI. ++This tool is designed for environments with SCSI devices attached ++through the zfcp device driver. + + .B lsluns + lists all logical unit numbers (LUNs) discovered in the +diff --git a/zconf/lstape b/zconf/lstape +index 3090451..e2410e1 100755 +--- a/zconf/lstape ++++ b/zconf/lstape +@@ -2,7 +2,7 @@ + # + # lstape - Tool to show information about tape devices + # +-# Copyright IBM Corp. 2003, 2017 ++# Copyright IBM Corp. 2003, 2018 + # + # s390-tools is free software; you can redistribute it and/or modify + # it under the terms of the MIT license. See LICENSE for details. +@@ -48,6 +48,9 @@ function PrintUsage() { + : -v|--version + : Display the version of the tools package and + : the lstape command. ++ : ++ :$(basename $0) without the --ccw-only option causes extra SAN traffic ++ :for each SCSI tape or changer device by invoking the sg_inq command. + EOD + } + +@@ -55,7 +58,7 @@ function PrintVersion() + { + cat <<-EOD + $CMD: version %S390_TOOLS_VERSION% +- Copyright IBM Corp. 2003, 2017 ++ Copyright IBM Corp. 2003, 2018 + EOD + } + +@@ -220,6 +223,24 @@ function SysfsCreateListCCW() { + ' | sort + } + ++# handle SCSI device not necessarily zfcp-attached, e.g. virtio-scsi-ccw ++function SCSISearchCCWBusid() ++{ ++ local SCSI_DEV=$1 ++ local SDEVCAN=$(readlink -e $SCSI_DEV) ++ while [ -n "$SDEVCAN" ]; do ++ # ascend to parent: strip last path part ++ SDEVCAN=${SDEVCAN%/*} ++ [ -h $SDEVCAN/subsystem ] || continue ++ local SUBSYSTEM=$(readlink -e $SDEVCAN/subsystem) ++ if [ "${SUBSYSTEM##*/}" = "ccw" ]; then ++ echo ${SDEVCAN##*/} ++ return ++ fi ++ done ++ echo "N/A" ++} ++ + function SysfsCreateListSCSI() + { + for SCSI_DEV in $1/bus/scsi/devices/*:*:*:*; do +@@ -249,11 +270,16 @@ function SysfsCreateListSCSI() + if [ -h $SG_DEV ]; then + # deprecated sysfs layout + SG_DEV=$(echo $SG_DEV | awk -F: '{print $NF}') +- else ++ elif [ -d $SCSI_DEV/scsi_generic ]; then + SG_DEV=$(basename $SG_DEV/*) ++ else ++ SG_DEV="" + fi + +- if [ "$SG_INQ" != "" ]; then ++ if [ -z "$SG_DEV" ]; then ++ SG_DEV="N/A" ++ TAPE_SERIAL="NO/SG" ++ elif [ "$SG_INQ" != "" ]; then + TAPE_SERIAL=$( + sg_inq /dev/$SG_DEV | + awk '/serial/{print $NF}' +@@ -291,17 +317,31 @@ function SysfsCreateListSCSI() + if [ "$CHG_IDX" != "" ]; then + TAPE_DEV=$CHG_IDX + fi ++ elif [ "$(echo "$SCSI_LIST"|grep lin_tape)" != "" ]; then ++ # bash glob sorts so IBMtape0 comes before IBMtape0n ++ local IBM_PATH=$( ++ ls -1d $SCSI_DEV/lin_tape/$DEV_NAME[0-9]* | ++ head -n 1) ++ if [ -d "$IBM_PATH" ]; then ++ IBM_IDX=${IBM_PATH##*/} ++ else ++ # deprecated sysfs layout ++ IBM_IDX=$( ++ echo "$SCSI_LIST" | ++ awk -F: '/lin_tape\:'"$DEV_NAME"'[0-9]+$/{print $NF}' ++ ) ++ fi ++ if [ "$IBM_IDX" != "" ]; then ++ TAPE_DEV=$IBM_IDX ++ fi + elif [ -r /proc/scsi/$DEV_NAME ]; then +- if [ "$TAPE_SERIAL" != "NO/INQ" ]; then + IBM_IDX=$( +- awk '$3 == "'$TAPE_SERIAL'"{ +- print $1 +- }' /proc/scsi/$DEV_NAME ++ grep -wF "$SCSI_ID" /proc/scsi/$DEV_NAME | ++ cut -d ' ' -f 1 + ) + if [ "$IBM_IDX" != "" ]; then + TAPE_DEV=$DEV_NAME$IBM_IDX + fi +- fi + fi + + printf "$SCSIFORMAT" \ +@@ -313,9 +353,16 @@ function SysfsCreateListSCSI() + $STATE + + if $VERBOSE; then ++ if [ -r $SCSI_DEV/hba_id ]; then ++ HBA_ID=$(cat $SCSI_DEV/hba_id) ++ else ++ HBA_ID=$(SCSISearchCCWBusid $SCSI_DEV) ++ fi ++ WWPN="N/A" ++ [ -r $SCSI_DEV/wwpn ] && WWPN=$(cat $SCSI_DEV/wwpn) + printf "$SCSIVFORMAT" \ +- $(cat $SCSI_DEV/hba_id) \ +- $(cat $SCSI_DEV/wwpn) \ ++ "$HBA_ID" \ ++ "$WWPN" \ + $TAPE_SERIAL + fi + done +diff --git a/zconf/lstape.8 b/zconf/lstape.8 +index 544cb08..019b6e5 100644 +--- a/zconf/lstape.8 ++++ b/zconf/lstape.8 +@@ -1,8 +1,8 @@ +-.\" Copyright 2017 IBM Corp. ++.\" Copyright 2017, 2018 IBM Corp. + .\" 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 LSTAPE 8 "Jul 2007" "s390-tools" ++.TH LSTAPE 8 "Jun 2018" "s390-tools" + + .SH NAME + lstape \- list tape devices. +@@ -20,6 +20,8 @@ lstape \- list tape devices. + .br + .RB [ -t + .IR [, ] "" ...] ++.br ++.RI [ ...] + + .SH DESCRIPTION + The lstape command lists all available tape devices on the current host. For +@@ -27,19 +29,8 @@ channel attached tape devices this output is the same as the contents of + /proc/tapedevices (which is obsolete) but also includes offline devices. By + default all tape devices are displayed. + +-Since SCSI tape devices are accessed differently to channel attached tape +-devices they are only visible if they are known to the SCSI layer. There +-are at least two possible drivers that can claim a SCSI tape device and the +-lstape command tries to find out which one this is. For the generic tape +-and changer driver the device names start with "st" or "sch", while for the +-IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown, +-the correct driver could not be obtained. +-This happens for example if there is no sg_inq command installed which is +-required to read the drive's serial number which in turn is used to find out +-the device number of the IBM tape driver. +- +-The serial number of a SCSI tape can be displayed with the --verbose option. If +-there is no sg_inq command available "NO/INQ" is shown as the tape's serial. ++The lstape command without the --ccw-only option causes extra SAN traffic ++for each SCSI tape or changer device by invoking the sg_inq command. + + .SH OPTIONS + .TP 8 +@@ -75,12 +66,64 @@ on the output of SCSI devices. + + .TP + .BR -t | --type " \fI\fR" +-Limit output to given device types (currently only applies to channel attached ++Limit output to given device types, for example 3490 ++(currently only applies to channel-attached + tape devices). + + .TP +-\fB\fR = +-Device type of devices that should be displayed (e.g. 3490). ++.I ++Limits the output to information about the specified tape device or ++devices only. For CCW-attached devices only. ++ ++.SH OUTPUT FIELDS FOR SCSI TAPE/CHANGER DEVICES ++.TP ++.B Generic ++SCSI generic device file for the tape drive, for example /dev/sg0. ++"N/A" if the SCSI generic (sg) kernel functionality is not available. ++.TP ++.B Device ++Main character device node file for accessing the tape drive or medium changer. ++SCSI tape devices are only visible if they are known to the SCSI layer. There ++are at least two possible drivers that can claim a SCSI tape device. The ++lstape command tries to determine the device driver. For the generic tape ++and changer driver the device names start with "st" or "sch", while for the ++IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown, ++the device driver could not be determined. ++.TP ++.B Target ++Linux SCSI device name in H:C:T:L format. ++.TP ++.B Vendor ++The vendor field from the SCSI device. ++.TP ++.B Model ++The model field from the SCSI device. ++.TP ++.B Type ++"tapedrv" for a tape drive or "changer" for a medium changer. ++.TP ++.B State ++The state of the SCSI device object in the kernel. ++Any state other than "running" can indicate problems. ++ ++.PP ++ ++For SCSI devices, the --verbose option additionally displays: ++.TP ++.B HBA ++The device bus-ID of the FCP device ++or of the virtio-scsi-ccw virtual HBA ++through which the tape drive is attached. ++"N/A" if the device does not have a sysfs ancestor with subsystem ccw. ++.TP ++.B WWPN ++The WWPN (worldwide port name) of the tape drive in the SAN. ++"N/A" if device is not attached through zfcp. ++.TP ++.B Serial ++The serial number. ++"NO/INQ" if there is no sg_inq command available. ++"NO/SG" if no SCSI generic (sg) kernel support is available. + + .SH EXAMPLES + \fBlstape\fR +@@ -92,3 +135,8 @@ List all tape devices that are available + .RS + Show all 3490 CCW devices that are online. + .RE ++ ++\fBlstape --scsi-only --verbose\fR ++.RS ++Show all SCSI tape or changer devices with maximum information. ++.RE +-- +2.21.3 + + +From 164d283c73cc17b8a5927a449ed59671545f7066 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 1 Oct 2019 15:33:59 +0200 +Subject: [PATCH 38/44] zipl: fix the scanned tokens array size calculation + (#1751587) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The zipl config file (zipl.conf) and the BootLoaderSpec (BLS) fragments +in /boot/loader/entries define a set of tokens that are parsed by zipl. + +These are stored in an array of tokens whose size is calculated to make +sure that there is enough memory allocated for all the scanned tokens. + +But the size calculation logic was wrong, since it was checking if the +current size was enough to store a single token per BLS fragment, while +up to 4 tokens can be defined in a BLS file: a section heading and the +image, ramdisk and parameter keywords. + +This led to zipl being killed by a SIGABRT signal when trying to parse +more tokens than the ones that could fit in the scanned tokens array: + +Using config file '/etc/zipl.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-9.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-8.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-7.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-6.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-5.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-4.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-3.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-2.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-1.conf' +Using BLS config file '/boot/loader/entries/vmlinuz-0.conf' +double free or corruption (out) +Aborted (core dumped) + +Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/68 +Closes: https://github.com/ibm-s390-tools/s390-tools/pull/73 +Signed-off-by: Javier Martinez Canillas +Signed-off-by: Stefan Haberland +Signed-off-by: Jan Höppner + +(cherry picked from commit 8bfe18e674e17a20682dbd1fa0e4813c789e22e5) +--- + zipl/src/scan.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index e57361e..ee04251 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -751,8 +751,14 @@ scan_bls(const char* blsdir, struct scan_token** token, int scan_size) + + remaining = scan_size - count; + +- if (remaining < n) { +- size = scan_size - remaining + n; ++ /* The array of scanned tokens is allocated when the zipl config file is ++ * parsed. Its size is a multiple of INITIAL_ARRAY_LENGTH so it may have ++ * enough space to scan all the tokens that are defined in the BLS files. ++ * Calculate if is enough assuming that a BLS fragment can contain up to ++ * 4 tokens: a section heading and 3 keywords (image, ramdisk, parameter). ++ */ ++ if (remaining < n * 4) { ++ size = scan_size - remaining + (n * 4); + buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token)); + if (!buffer) + goto err; +-- +2.21.3 + + +From ba8021cb17f74ea5130d7c5acdc267b6fb0a433c Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Wed, 12 Sep 2018 09:40:22 -0500 +Subject: [PATCH 39/44] zipl: use FIEMAP mapping ioctl if it exists (moved from + Patch101) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +zipl currently uses the FIBMAP ioctl to map blocks for the bootloader; +on XFS, if FIBMAP is requested on a reflinked file, it will fail - +and FIBMAP returns 0 in this case, which is indistinguishable from a +hole. This causes boot to fail because the file is not mapped. + +We can use the FIEMAP ioctl instead, which is able to map reflinked +files. While FIEMAP is able to map entire extents at once, here we +simply use it to obtain the mapping block-by-block so that it fits +in with the current FIBMAP calls. + +Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/34 +Closes: https://github.com/ibm-s390-tools/s390-tools/pull/36 +Signed-off-by: Eric Sandeen +Reviewed-by: Peter Oberparleiter +Reviewed-by: Stefan Haberland +Tested-by: Stefan Haberland +Signed-off-by: Jan Höppner + +(cherry picked from commit 07e30951f8f31cdb5ea3cbd020254239be522e6a) +--- + zipl/src/disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 53 insertions(+), 4 deletions(-) + +diff --git a/zipl/src/disk.c b/zipl/src/disk.c +index 0d8e779..43092bf 100644 +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -21,6 +21,8 @@ + #include + #include + #include ++#include ++#include + + #include "lib/util_proc.h" + +@@ -550,8 +552,12 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, + { + struct statfs buf; + blocknum_t phy_per_fs; +- int mapped; ++ blocknum_t mapped; ++ int block; + int subblock; ++ int fiemap_size; ++ int map_offset; ++ struct fiemap *fiemap; + + /* No file system: partition or raw disk */ + if (info->fs_block_size == -1) { +@@ -576,12 +582,55 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, + } + /* Get mapping in file system blocks */ + phy_per_fs = info->fs_block_size / info->phy_block_size; +- mapped = logical / phy_per_fs; + subblock = logical % phy_per_fs; +- if (ioctl(fd, FIBMAP, &mapped)) { +- error_reason("Could not get file mapping"); ++ ++ /* First try FIEMAP, more complicated to set up */ ++ fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent); ++ ++ fiemap = misc_malloc(fiemap_size); ++ if (!fiemap) + return -1; ++ memset(fiemap, 0, fiemap_size); ++ ++ fiemap->fm_extent_count = 1; ++ fiemap->fm_flags = FIEMAP_FLAG_SYNC; ++ /* fm_start, fm_length in bytes; logical is in physical block units */ ++ fiemap->fm_start = logical * info->phy_block_size; ++ fiemap->fm_length = info->phy_block_size; ++ ++ if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) { ++ /* FIEMAP failed, fall back to FIBMAP */ ++ block = logical / phy_per_fs; ++ if (ioctl(fd, FIBMAP, &block)) { ++ error_reason("Could not get file mapping"); ++ free(fiemap); ++ return -1; ++ } ++ mapped = block; ++ } else { ++ if (fiemap->fm_mapped_extents) { ++ if (fiemap->fm_extents[0].fe_flags & ++ FIEMAP_EXTENT_ENCODED) { ++ error_reason("File mapping is encoded"); ++ free(fiemap); ++ return -1; ++ } ++ /* ++ * returned extent may start prior to our request ++ */ ++ map_offset = fiemap->fm_start - ++ fiemap->fm_extents[0].fe_logical; ++ mapped = fiemap->fm_extents[0].fe_physical + ++ map_offset; ++ /* set mapped to fs block units */ ++ mapped = mapped / info->fs_block_size; ++ } else { ++ mapped = 0; ++ } + } ++ ++ free(fiemap); ++ + if (mapped == 0) { + /* This is a hole in the file */ + *physical = 0; +-- +2.21.3 + + +From 658eef5bc3ea1ec9b917c35a36dcbe1219bd0b08 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 6 Nov 2018 13:29:32 +0100 +Subject: [PATCH 40/44] zipl: use the BLS "title" field as the IPL section name + (moved from Patch103) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Most bootloaders use the BootLoaderSpec "title" field to name the entries +in their boot menu. The zipl bootloader used the "version" field instead, +since it was wrongly assumed that the zipl boot menu didn't support names +that contained spaces, which are usually present in a BLS "title" field. + +But this is not the case, names with space characters are supported by the +IPL and is just a constraint of the section heading in the zipl.conf file. + +So to be consistent with all the other bootloaders, use the "title" field +also on zipl when populating the boot menu entries from BLS files. + +Closes: https://github.com/ibm-s390-tools/s390-tools/pull/47 +Signed-off-by: Javier Martinez Canillas +Reviewed-by: Peter Oberparleiter +Reviewed-by: Stefan Haberland sth@linux.ibm.com +Signed-off-by: Jan Höppner + +(cherry picked from commit 49510442133e63bfd650a7b7e27e388d38baba81) +--- + scripts/zipl-switch-to-blscfg | 1 + + zipl/src/scan.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/scripts/zipl-switch-to-blscfg b/scripts/zipl-switch-to-blscfg +index 5272033..5141b1c 100755 +--- a/scripts/zipl-switch-to-blscfg ++++ b/scripts/zipl-switch-to-blscfg +@@ -150,6 +150,7 @@ while IFS='= ' read key val; do + if [ -f "${OUTPUT}" ]; then + print_error "BLS file ${OUTPUT} already exists" + fi ++ echo "title $section" >> ${OUTPUT} + fi + elif [[ $val ]]; then + val="$(echo $val | sed -e 's/^[ \t"]*//;s/[ \t"]*$//')" +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index ee04251..b8fea13 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -702,7 +702,7 @@ scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan, + file->buffer[key_end] = '\0'; + file->buffer[val_end] = '\0'; + +- if (strncmp("version", &file->buffer[key_start], key_end - key_start) == 0) { ++ if (strncmp("title", &file->buffer[key_start], key_end - key_start) == 0) { + scan_append_section_heading(scan, index, &file->buffer[val_start]); + } + +-- +2.21.3 + + +From 2515287113812346ef8a3bb00a80632b698ab542 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 7 Nov 2019 14:37:36 +0100 +Subject: [PATCH 41/44] zipl: config file handling improvements for CoreOS + (#1764706) + +Use TOOLS_SYSCONFDIR for the default zipl.conf path - c01bcfa73a6d795f33df639ba36304a704d13561 +zipl: ship a minimal zipl.conf - 19259aeb656df5cbc7bda89599442dd51d80a7a8 +zipl: allow check for other locations of zipl.conf - 275105fe3d64d214f982a5c0eb113d806853919b +zipl: add value of target= as search path for BLS case - d71628326d80e623fc9f008fe4ea93edb5592b2e +--- + zipl/Makefile | 1 + + zipl/doc/zipl.conf.minimal | 10 ++++ + zipl/include/scan.h | 1 + + zipl/include/zipl.h | 4 +- + zipl/man/Makefile | 10 +++- + zipl/man/{zipl.8 => zipl.8.in} | 2 +- + zipl/man/{zipl.conf.5 => zipl.conf.5.in} | 17 +++++-- + zipl/src/job.c | 23 +++++++++- + zipl/src/scan.c | 58 ++++++++++++++++++++++++ + 9 files changed, 117 insertions(+), 9 deletions(-) + create mode 100644 zipl/doc/zipl.conf.minimal + rename zipl/man/{zipl.8 => zipl.8.in} (99%) + rename zipl/man/{zipl.conf.5 => zipl.conf.5.in} (96%) + +diff --git a/zipl/Makefile b/zipl/Makefile +index a02e997..931b113 100644 +--- a/zipl/Makefile ++++ b/zipl/Makefile +@@ -8,6 +8,7 @@ all: + install: all + $(MAKE) -C src install + $(MAKE) -C man install ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 doc/zipl.conf.minimal $(DESTDIR)$(TOOLS_LIBDIR)/zipl.conf + + clean: + $(MAKE) -C src clean +diff --git a/zipl/doc/zipl.conf.minimal b/zipl/doc/zipl.conf.minimal +new file mode 100644 +index 0000000..9d6fa5e +--- /dev/null ++++ b/zipl/doc/zipl.conf.minimal +@@ -0,0 +1,10 @@ ++# This is an example of a minimal zipl.conf file that can be used when the ++# sections are defined in BootLoaderSpec fragments files. ++# ++# See the zipl and zipl.conf man page for more details. ++[defaultboot] ++defaultauto ++prompt=1 ++timeout=5 ++secure=auto ++target=/boot +diff --git a/zipl/include/scan.h b/zipl/include/scan.h +index 4dcee0b..f39e96e 100644 +--- a/zipl/include/scan.h ++++ b/zipl/include/scan.h +@@ -126,6 +126,7 @@ char* scan_keyword_name(enum scan_keyword_id id); + int scan_check_defaultboot(struct scan_token* scan); + struct scan_token* scan_build_automenu(struct scan_token* scan); + int scan_check(struct scan_token* scan); ++int scan_check_bls(struct scan_token *scan); + int scan_find_section(struct scan_token* scan, char* name, enum scan_id type, + int offset); + int scan_check_section_data(char* keyword[], int* line, char* name, +diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h +index 6f2d115..0ef06e4 100644 +--- a/zipl/include/zipl.h ++++ b/zipl/include/zipl.h +@@ -47,7 +47,9 @@ + #define DEFAULTBOOT_SECTION "defaultboot" + + #define ZIPL_CONF_VAR "ZIPLCONF" +-#define ZIPL_DEFAULT_CONF "/etc/zipl.conf" ++#define ZIPL_RUNTIME_CONF "/run/zipl/zipl.conf" ++#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf" ++#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf" + #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" + #define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" + #define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure" +diff --git a/zipl/man/Makefile b/zipl/man/Makefile +index 6439205..1d8dbe3 100644 +--- a/zipl/man/Makefile ++++ b/zipl/man/Makefile +@@ -6,7 +6,13 @@ all: + install: + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man5 + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 +- $(INSTALL) -m 644 -c zipl.8 $(DESTDIR)$(MANDIR)/man8 +- $(INSTALL) -m 644 -c zipl.conf.5 $(DESTDIR)$(MANDIR)/man5 ++ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \ ++ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \ ++ > $(DESTDIR)$(MANDIR)/man8/zipl.8 ++ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \ ++ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \ ++ > $(DESTDIR)$(MANDIR)/man5/zipl.conf.5 ++ chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \ ++ $(DESTDIR)$(MANDIR)/man5/zipl.conf.5 + + clean: +diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8.in +similarity index 99% +rename from zipl/man/zipl.8 +rename to zipl/man/zipl.8.in +index 2f873f3..f12e27b 100644 +--- a/zipl/man/zipl.8 ++++ b/zipl/man/zipl.8.in +@@ -120,7 +120,7 @@ Print version information, then exit. + .TP + .BR "\-c " " or " "\-\-config=" + Use the specified . If none is supplied, the environment +-variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used. ++variable ZIPLCONF is evaluated if set, otherwise %SYSCONFDIR%/zipl.conf is used. + + .TP + .BR "\-b " " or " "\-\-blsdir=" +diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5.in +similarity index 96% +rename from zipl/man/zipl.conf.5 +rename to zipl/man/zipl.conf.5.in +index 71d1252..ef40287 100644 +--- a/zipl/man/zipl.conf.5 ++++ b/zipl/man/zipl.conf.5.in +@@ -17,8 +17,13 @@ boot loader tool + .BR zipl (8)). + .br + +-By default this configuration file is located at /etc/zipl.conf. A different +-location may be specified either using the '\-\-config' option of ++By default ++.B zipl ++checks for ++.I zipl.conf ++at /run/zipl/zipl.conf, %SYSCONFDIR%/zipl.conf, %TOOLS_LIBDIR%/zipl.conf in that ++order - whichever is found first will be used. Users can specifically choose a ++location using the '\-\-config' option of + .B zipl + or by setting the ZIPLCONF shell environment variable. + .br +@@ -138,7 +143,13 @@ initrd /initramfs-4.15.9 + options root=/dev/dasda1 console=ttyS0 + .PP + +-The location of the linux and initrd has to be specified relative to the boot partition. The BLS config files are only used to specify the IPL sections, a zipl.conf configuration files is still needed for global parameters. ++The location of the linux and initrd has to be specified relative to the boot ++partition. The BLS config files are only used to specify the IPL sections, a ++zipl.conf configuration file is still needed for global parameters. For this ++purpose, a minimal zipl.conf configuration file is shipped at ++%TOOLS_LIBDIR%/zipl.conf which would help when used with BLS config files, by ++not requiring users to create the traditional configuration file at ++%SYSCONFDIR%/zipl.conf. + + .B Boot menu + +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 1178a7d..7d61c84 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -59,6 +59,14 @@ static struct option options[] = { + /* Command line option abbreviations */ + static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:"; + ++/* Locations of zipl.conf configuration file */ ++static const char *zipl_conf[] = { ++ ZIPL_RUNTIME_CONF, ++ ZIPL_DEFAULT_CONF, ++ ZIPL_MINIMAL_CONF, ++ NULL ++}; ++ + struct command_line { + char* data[SCAN_KEYWORD_NUM]; + char* config; +@@ -1783,7 +1791,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) + char* filename; + char *blsdir; + char* source; +- int rc, scan_size; ++ int i, rc, scan_size; + + /* Read configuration file */ + if (cmdline->config != NULL) { +@@ -1797,7 +1805,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) + ZIPL_CONF_VAR ")"; + } else { + /* Use default config file */ +- filename = ZIPL_DEFAULT_CONF; ++ for (i = 0; zipl_conf[i]; i++) { ++ if (misc_check_readable_file(zipl_conf[i]) == 0) { ++ filename = zipl_conf[i]; ++ break; ++ } ++ } + source = ""; + } + printf("Using config file '%s'%s\n", filename, source); +@@ -1840,6 +1853,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) + scan_free(scan); + return rc; + } ++ rc = scan_check_bls(scan); ++ if (rc) { ++ error_text("BLS parsing '%s'", blsdir); ++ scan_free(scan); ++ return rc; ++ } + /* Get job from config file data */ + if (cmdline->menu != NULL) + rc = get_menu_job(scan, cmdline->menu, job); +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index b8fea13..f4228d3 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -1559,6 +1559,64 @@ scan_check(struct scan_token* scan) + return 0; + } + ++/* ++ * Check if kernel and initrd image paths provided by BLS files are readable. ++ * If not, add value of 'scan_keyword_target' into search path and silently ++ * update scan list. ++ */ ++int ++scan_check_bls(struct scan_token *scan) ++{ ++ int i, rc; ++ char *target_value = NULL; ++ char *img_value = NULL; ++ char *buffer = NULL; ++ /* ++ * In the BLS case, each BLS section heading inherits a keyword ++ * assignment target= from zipl.conf, and they are all the same. ++ * ++ */ ++ for (i = 0 ; scan[i].id != scan_id_empty; i++) { ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == scan_keyword_target) { ++ target_value = scan[i].content.keyword.value; ++ break; ++ } ++ } ++ if (!target_value) ++ return -1; ++ for (i = 0 ; scan[i].id != scan_id_empty; i++) { ++ if (scan[i].id != scan_id_keyword_assignment) ++ continue; ++ if (scan[i].content.keyword.keyword == scan_keyword_image || ++ scan[i].content.keyword.keyword == scan_keyword_ramdisk) { ++ ++ rc = misc_check_readable_file( ++ scan[i].content.keyword.value); ++ if (rc) { ++ misc_asprintf(&img_value, "%s%s", ++ target_value, ++ scan[i].content.keyword.value); ++ rc = misc_check_readable_file(img_value); ++ if (rc) { ++ error_text( ++ "Image file '%s' is not accessible", ++ scan[i].content.keyword.value); ++ return rc; ++ } ++ buffer = (char *) ++ misc_malloc(strlen(img_value) + 1); ++ if (buffer == NULL) ++ return -1; ++ memcpy(buffer, img_value, strlen(img_value)); ++ buffer[strlen(img_value)] = 0; ++ free(scan[i].content.keyword.value); ++ scan[i].content.keyword.value = buffer; ++ } ++ } ++ } ++ return 0; ++} + + static int + scan_get_defaultboot_type(char* keyword[], int line[], int section_line, +-- +2.21.3 + + +From 1ca71226d392c59855432ee992e263be680644d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 27 Jan 2020 11:22:42 +0100 +Subject: [PATCH 42/44] zkey: Fix display of clear key size for XTS keys + (#1794375) + +Description: zkey: Fix display of clear key size for XTS keys +Symptom: The 'zkey list' command shows bogus values for the + keys 'Clear key size' for XTS keys of type CCA-AESDATA + or CCA-AESCIPHER. +Problem: XTS keys consist of 2 keys concatenated to each other. + To calculate the clear key size, the clear key size of + both keys must be added. The code does not address the + second key correctly, and thus reads the clear key size + of the second key from an invalid memory location. This + results in bogus values reported as clear key size. + This bug has been introduced with feature SEC1717 "Cipher + key support" with commit 298fab68fee8 "zkey: Preparations + for introducing a new key type". +Solution: Correct the addressing of the second key. +Reproduction: Generate an XTS key of type CCA-AESDATA or CCA-AESCIPHER + and then run 'zkey list'. +Upstream-ID: e7f446432b92b293e758099842843cfb1f18fa97 +--- + zkey/pkey.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/zkey/pkey.c b/zkey/pkey.c +index 462f9fe..640ff86 100644 +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -1591,8 +1591,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) + if (is_cca_aes_data_key(key, key_size)) { + *bitsize = datakey->bitsize; + if (key_size == 2 * AESDATA_KEY_SIZE) { +- datakey = (struct aesdatakeytoken *)key + +- AESDATA_KEY_SIZE; ++ datakey = (struct aesdatakeytoken *)(key + ++ AESDATA_KEY_SIZE); + *bitsize += datakey->bitsize; + } + } else if (is_cca_aes_cipher_key(key, key_size)) { +@@ -1601,8 +1601,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) + else + *bitsize = 0; /* Unknown */ + if (key_size > cipherkey->length) { +- cipherkey = (struct aescipherkeytoken *)key + +- cipherkey->length; ++ cipherkey = (struct aescipherkeytoken *)(key + ++ cipherkey->length); + if (cipherkey->pfv == 0x00) /* V0 payload */ + *bitsize += cipherkey->pl - 384; + } +-- +2.21.3 + + +From 2beeedb4f5eca1cf2faf34d1a0db176ea887854f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 27 Jan 2020 11:39:24 +0100 +Subject: [PATCH 43/44] zkey: Fix listing of keys on file systems reporting + DT_UNKNOWN (#1792957) + +Description: zkey: Fix listing of keys on file systems reporting DT_UNKNOWN. +Symptom: When the zkey key repository is located in a file system that + does not have full support for report the file type, such as + XFS, the 'zkey list' command does not show any keys, although + keys exist in the repository. +Problem: The zkey list function uses scandir() to look for files in the + zkey key repository directory. It checks the dirent.d_type field + to consider only regular files, but skips all others. File + systems that do not have full support for returning the file + type in d_type will return DT_UNKNOWN instead. zkey skips + those directory entries and thus does not show any keys. +Solution: Also consider directory entries with d_type = DT_UNKNOWN. +Reproduction: Use zkey with a zkey repository directory located in a file + system that does not have full support for returning the file + type, such as XFS. Generate a key in the repository and then + list the key s with 'zkey list'. + Note: Newly created XFS file systems usually support returning + the file type, but existing XFS file systems might not. To + create an XFS file system that does not support returning the + file type, use 'mkfs.xfs -f -m crc=0 -n ftype=0' to create + the file system. +Upstream-ID: 0de533aef9def920fed751c6025e4f19c4cba763 +--- + zkey/keystore.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zkey/keystore.c b/zkey/keystore.c +index af67721..e6be3a4 100644 +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -906,7 +906,7 @@ static int _keystore_info_file_filter(const struct dirent *dirent) + { + size_t len; + +- if (dirent->d_type != DT_REG) ++ if (dirent->d_type != DT_REG && dirent->d_type != DT_UNKNOWN) + return 0; + + len = strlen(dirent->d_name); +-- +2.21.3 + + +From 4d0488330d69aa634d3c5bc68c8036b2ddc97144 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 27 May 2020 10:29:10 +0200 +Subject: [PATCH 44/44] zipl/libc: Fix potential buffer overflow in printf + (#1847536) + +Description: zipl/libc: Fix potential buffer overflow in printf +Symptom: Crash of the zipl boot loader during boot. +Problem: The zipl boot loaders have their own minimalistic libc + implementation. In it printf and sprintf use vsprintf for string + formatting. Per definition vsprintf assumes that the buffer it + writes to is large enough to contain the formatted string and + performs no size checks. This is problematic for the boot + loaders because the buffer they use are often allocated on the + stack. Thus even small changes to the string format can + potentially cause buffer overflows on the stack. +Solution: Implement vsnprintf and make use of it. +Reproduction: Use printf to print a string with >81 characters (exact number + depends on the stack layout/compiler used). +Upstream-ID: 6fe9e6c55c69c14971dca55551009f5060418aae +Upstream-ID: 8874b908254c47c8a6fd7a1aca2c7371c11035c4 +Upstream-ID: f7430027b41d5ad6220e962a179c2a5213330a44 +Upstream-ID: 36fed0e6c6590631c4ce1707c8fe3c3397bcce4d +--- + zipl/boot/libc.c | 360 ++++++++++++++++++++++++++++-------------- + zipl/boot/libc.h | 4 +- + zipl/boot/menu.c | 2 +- + zipl/boot/menu.h | 1 - + zipl/boot/tape2dump.c | 2 +- + 5 files changed, 248 insertions(+), 121 deletions(-) + +diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c +index 5944c4e..bd88d15 100644 +--- a/zipl/boot/libc.c ++++ b/zipl/boot/libc.c +@@ -125,81 +125,6 @@ int strncmp(const char *s1, const char *s2, unsigned long count) + return 0; + } + +-/* +- * Convert number to string +- * +- * Parameters: +- * +- * - buf: Output buffer +- * - base: Base used for formatting (e.g. 10 or 16) +- * - val: Number to format +- * - zero: If > 0, fill with leading zeros, otherwise use blanks +- * - count: Minimum number of characters used for output string +- */ +-static int num_to_str(char *buf, int base, unsigned long val, int zero, +- unsigned long count) +-{ +- static const char conv_vec[] = {'0', '1', '2', '3', '4', '5', '6', '7', +- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +- unsigned long num = 0, val_work = val, in_number = 1; +- int i; +- +- /* Count number of characters needed for number */ +- do { +- num++; +- val_work /= base; +- } while (val_work); +- /* Real character number overwrites count */ +- if (count < num) +- count = num; +- /* Format number */ +- for (i = count - 1; i >= 0; i--) { +- if (in_number) { +- buf[i] = conv_vec[val % base]; +- val /= base; +- in_number = val ? 1 : 0; +- } else { +- buf[i] = zero ? '0' : ' '; +- } +- } +- buf[count] = 0; +- return count; +-} +- +-/* +- * Convert string to string with indentation +- */ +-static int str_to_str(char *buf, const char *str, unsigned long count) +-{ +- unsigned long size; +- +- size = strlen(str); +- if (count < size) +- count = size; +- else +- memset(buf, ' ', count - size); +- strcpy(buf + (count - size), str); +- return count; +-} +- +-/* +- * Convert string to number with given base +- */ +-unsigned long strtoul(const char *nptr, char **endptr, int base) +-{ +- unsigned long val = 0; +- +- while (isdigit(*nptr)) { +- if (val != 0) +- val *= base; +- val += *nptr - '0'; +- nptr++; +- } +- if (endptr) +- *endptr = (char *) nptr; +- return val; +-} +- + /* + * Convert ebcdic string to number with given base + */ +@@ -218,79 +143,282 @@ unsigned long ebcstrtoul(char *nptr, char **endptr, int base) + return val; + } + +-/* +- * Convert string to number with given base +- */ +-static int sprintf_fmt(char type, char *buf, unsigned long val, int zero, +- int count) ++static int skip_atoi(const char **c) + { +- switch (type) { ++ int i = 0; ++ ++ do { ++ i = i*10 + *((*c)++) - '0'; ++ } while (isdigit(**c)); ++ ++ return i; ++} ++ ++enum format_type { ++ FORMAT_TYPE_NONE, ++ FORMAT_TYPE_STR, ++ FORMAT_TYPE_ULONG, ++}; ++ ++struct printf_spec { ++ unsigned int type:8; /* format_type enum */ ++ signed int field_width:24; /* width of output field */ ++ unsigned int zeropad:1; /* pad numbers with zero */ ++ unsigned int base:8; /* number base, 8, 10 or 16 only */ ++ signed int precision:16; /* # of digits/chars */ ++}; ++ ++#define FIELD_WIDTH_MAX ((1 << 23) - 1) ++ ++static int format_decode(const char *fmt, struct printf_spec *spec) ++{ ++ const char *start = fmt; ++ ++ spec->type = FORMAT_TYPE_NONE; ++ while (*fmt) { ++ if (*fmt == '%') ++ break; ++ fmt++; ++ } ++ ++ /* return current non-format string */ ++ if (fmt != start || !*fmt) ++ return fmt - start; ++ ++ /* first char is '%', skip it */ ++ fmt++; ++ if (*fmt == '0') { ++ spec->zeropad = 1; ++ fmt++; ++ } ++ ++ spec->field_width = -1; ++ if (isdigit(*fmt)) ++ spec->field_width = skip_atoi(&fmt); ++ ++ spec->precision = -1; ++ if (*fmt == '.') { ++ fmt++; ++ if (isdigit(*fmt)) ++ spec->precision = skip_atoi(&fmt); ++ } ++ ++ /* always use long form, i.e. ignore long qualifier */ ++ if (*fmt == 'l') ++ fmt++; ++ ++ switch (*fmt) { + case 's': +- return str_to_str(buf, (const char *) val, count); +- case 'x': +- return num_to_str(buf, 16, val, zero, count); ++ spec->type = FORMAT_TYPE_STR; ++ break; ++ ++ case 'o': ++ spec->base = 8; ++ spec->type = FORMAT_TYPE_ULONG; ++ break; ++ + case 'u': +- return num_to_str(buf, 10, val, zero, count); ++ spec->base = 10; ++ spec->type = FORMAT_TYPE_ULONG; ++ break; ++ ++ case 'x': ++ spec->base = 16; ++ spec->type = FORMAT_TYPE_ULONG; ++ break; ++ + default: + libc_stop(EINTERNAL); + } +- return 0; ++ ++ return ++fmt - start; ++} ++ ++static char *string(char *buf, char *end, const char *s, ++ struct printf_spec *spec) ++{ ++ int limit = spec->precision; ++ int len = 0; ++ int spaces; ++ ++ /* Copy string to buffer */ ++ while (limit--) { ++ char c = *s++; ++ if (!c) ++ break; ++ if (buf < end) ++ *buf = c; ++ buf++; ++ len++; ++ } ++ ++ /* right align if necessary */ ++ if (len < spec->field_width && buf < end) { ++ spaces = spec->field_width - len; ++ if (spaces >= end - buf) ++ spaces = end - buf; ++ memmove(buf + spaces, buf, len); ++ memset(buf, ' ', spaces); ++ buf += spaces; ++ } ++ ++ return buf; ++} ++ ++static char *number(char *buf, char *end, unsigned long val, ++ struct printf_spec *spec) ++{ ++ /* temporary buffer to prepare the string. ++ * Worst case: base = 8 -> 3 bits per char -> 2.67 chars per byte */ ++ char tmp[3 * sizeof(val)]; ++ static const char vec[] = {'0', '1', '2', '3', '4', '5', '6', '7', ++ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; ++ int field_width = spec->field_width; ++ int precision = spec->precision; ++ int len; ++ ++ /* prepare string in reverse order */ ++ len = 0; ++ while (val) { ++ tmp[len++] = vec[val % spec->base]; ++ val /= spec->base; ++ } ++ ++ if (len > precision) ++ precision = len; ++ ++ field_width -= precision; ++ while (field_width-- > 0) { ++ char c = spec->zeropad ? '0' : ' '; ++ if (buf < end) ++ *buf = c; ++ buf++; ++ } ++ ++ /* needed if no field width but a precision is given */ ++ while (len < precision--) { ++ if (buf < end) ++ *buf = '0'; ++ buf++; ++ } ++ ++ while (len-- > 0) { ++ if (buf < end) ++ *buf = tmp[len]; ++ buf++; ++ } ++ ++ return buf; + } + + /* +- * Print formated string (va version) ++ * vsnprintf - Format string and place in a buffer ++ * ++ * This funcion only supports a subset of format options defined in the ++ * C standard, i.e. ++ * specifiers: ++ * * %s (strings) ++ * * %o (unsigned int octal) ++ * * %u (unsigned int decimal) ++ * * %x (unsigned int hexadecimal) ++ * ++ * length modifier: ++ * * 'l' (ignored, see below) ++ * ++ * flag: ++ * * '0' (zero padding for integers) ++ * ++ * precision and field width as integers, i.e. _not_ by asterix '*'. ++ * ++ * The integer specifiers (o, u and, x) always use the long form, i.e. ++ * assume the argument to be of type 'unsigned long int'. ++ * ++ * Returns the number of characters the function would have generated for ++ * the given input (excluding the trailing '\0'. If the return value is ++ * greater than or equal @size the resulting string is trunctuated. + */ +-static void vsprintf(char *str, const char *fmt, va_list va) ++static int vsnprintf(char *buf, unsigned long size, const char *fmt, ++ va_list args) + { +- unsigned long val, zero, count; +- char *fmt_next; ++ struct printf_spec spec = {0}; ++ char *str, *end; + +- do { +- if (*fmt == '%') { +- fmt++; +- if (*fmt == '0') { +- zero = 1; +- fmt++; +- } else { +- zero = 0; ++ str = buf; ++ end = buf + size; ++ ++ /* use negative (large positive) buffer sizes as indication for ++ * unknown/unlimited buffer sizes. */ ++ if (end < buf) { ++ end = ((void *)-1); ++ size = end - buf; ++ } ++ ++ while (*fmt) { ++ const char *old_fmt = fmt; ++ int read = format_decode(fmt, &spec); ++ int copy; ++ ++ fmt += read; ++ ++ switch (spec.type) { ++ case FORMAT_TYPE_NONE: ++ copy = read; ++ if (str < end) { ++ if (copy > end - str) ++ copy = end - str; ++ memcpy(str, old_fmt, copy); + } +- /* No number found by strtoul: count=0 fmt_next=fmt */ +- count = strtoul(fmt, &fmt_next, 10); +- fmt = fmt_next; +- if (*fmt == 'l') +- fmt++; +- val = va_arg(va, unsigned long); +- str += sprintf_fmt(*fmt, str, val, zero, count); +- fmt++; +- } else { +- *str++ = *fmt++; ++ str += read; ++ break; ++ ++ case FORMAT_TYPE_STR: ++ str = string(str, end, va_arg(args, char *), &spec); ++ break; ++ ++ case FORMAT_TYPE_ULONG: ++ str = number(str, end, va_arg(args, unsigned long), ++ &spec); ++ break; + } +- } while (*fmt); +- *str = 0; ++ } ++ ++ if (size) { ++ if (str < end) ++ *str = '\0'; ++ else ++ end[-1] = '\0'; ++ } ++ return str - buf; + } + + /* +- * Write formated string to string ++ * Write formatted string to buffer + */ +-void sprintf(char *str, const char *fmt, ...) ++void snprintf(char *buf, unsigned long size, const char *fmt, ...) + { + va_list va; + + va_start(va, fmt); +- vsprintf(str, fmt, va); ++ vsnprintf(buf, size, fmt, va); + va_end(va); + } + + /* +- * Print formated string ++ * Print formatted string to console + */ + void printf(const char *fmt, ...) + { +- char buf[81]; ++ char buf[LINE_LENGTH + 1]; ++ int len; + va_list va; + + va_start(va, fmt); +- vsprintf(buf, fmt, va); ++ len = vsnprintf(buf, sizeof(buf), fmt, va); ++ if (len > LINE_LENGTH) { ++ buf[LINE_LENGTH - 1] = '.'; ++ buf[LINE_LENGTH - 2] = '.'; ++ buf[LINE_LENGTH - 3] = '.'; ++ } + sclp_print(buf); + va_end(va); + } +diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h +index 44097fa..68ecd00 100644 +--- a/zipl/boot/libc.h ++++ b/zipl/boot/libc.h +@@ -40,6 +40,7 @@ + #define ENOTTY 25 /* Not a typewriter */ + + #define MIB (1024ULL * 1024) ++#define LINE_LENGTH 80 /* max line length printed by printf */ + + typedef unsigned long long uint64_t; + typedef unsigned int uint32_t; +@@ -47,13 +48,12 @@ typedef unsigned short uint16_t; + typedef unsigned char uint8_t; + + void printf(const char *, ...); +-void sprintf(char *, const char *, ...); ++void snprintf(char *buf, unsigned long size, const char *fmt, ...); + void *memcpy(void *, const void *, unsigned long); + void *memmove(void *, const void *, unsigned long); + void *memset(void *, int c, unsigned long); + char *strcat(char *, const char *); + int strncmp(const char *, const char *, unsigned long); +-unsigned long strtoul(const char *, char **, int); + unsigned long ebcstrtoul(char *, char **, int); + int strlen(const char *); + char *strcpy(char *, const char *); +diff --git a/zipl/boot/menu.c b/zipl/boot/menu.c +index 3f68620..35ac4de 100644 +--- a/zipl/boot/menu.c ++++ b/zipl/boot/menu.c +@@ -186,7 +186,7 @@ boot: + (void *)&__stage2_params + TEXT_OFFSET)); + + /* append 'BOOT_IMAGE=' to parmline */ +- sprintf(endstring, " BOOT_IMAGE=%u", value); ++ snprintf(endstring, sizeof(endstring), " BOOT_IMAGE=%u", value); + if ((strlen(cmd_line_extra) + strlen(endstring)) < COMMAND_LINE_SIZE) + strcat(cmd_line_extra, endstring); + +diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h +index 1b103a8..ee0f2c5 100644 +--- a/zipl/boot/menu.h ++++ b/zipl/boot/menu.h +@@ -20,7 +20,6 @@ + /* max command line length */ + #define COMMAND_LINE_SIZE 896 + #define BOOT_MENU_ENTRIES 63 +-#define LINE_LENGTH 80 + #define PARAM_SIZE 8 + #define TEXT_OFFSET 4 + +diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c +index 1da3ce1..800b943 100644 +--- a/zipl/boot/tape2dump.c ++++ b/zipl/boot/tape2dump.c +@@ -186,7 +186,7 @@ static void progress_print_disp(unsigned long addr) + + if (addr % (1024 * 1024 * 16) != 0) + return; +- sprintf(msg, "%08u", addr >> 20); ++ snprintf(msg, sizeof(msg), "%08u", addr >> 20); + ccw_load_display(msg); + } + +-- +2.21.3 + diff --git a/SOURCES/s390-tools-zipl-bls-loadaddr.patch b/SOURCES/s390-tools-zipl-bls-loadaddr.patch new file mode 100644 index 0000000..032d84e --- /dev/null +++ b/SOURCES/s390-tools-zipl-bls-loadaddr.patch @@ -0,0 +1,146 @@ +From ecf5a4ecb909bfd91306678d0c460ab2f2837a33 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 18 Nov 2019 04:10:06 -0500 +Subject: [PATCH 1/3] zipl: drop redundant string duplication +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Dan Horák +--- + zipl/src/scan.c | 9 +-------- + 1 file changed, 1 insertion(+), 8 deletions(-) + +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index 38fa5454..0ea37efa 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -1575,7 +1575,6 @@ scan_check_bls(struct scan_token *scan) + int i, rc; + char *target_value = NULL; + char *img_value = NULL; +- char *buffer = NULL; + /* + * In the BLS case, each BLS section heading inherits a keyword + * assignment target= from zipl.conf, and they are all the same. +@@ -1609,14 +1608,8 @@ scan_check_bls(struct scan_token *scan) + scan[i].content.keyword.value); + return rc; + } +- buffer = (char *) +- misc_malloc(strlen(img_value) + 1); +- if (buffer == NULL) +- return -1; +- memcpy(buffer, img_value, strlen(img_value)); +- buffer[strlen(img_value)] = 0; + free(scan[i].content.keyword.value); +- scan[i].content.keyword.value = buffer; ++ scan[i].content.keyword.value = img_value; + } + } + } + +From 05f83569960e2774e819fe0942da1f92d0cce35b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 18 Nov 2019 04:29:04 -0500 +Subject: [PATCH 2/3] zipl: set reason not text for failed check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Dan Horák +--- + zipl/src/scan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index 0ea37efa..0f01cac9 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -1603,7 +1603,7 @@ scan_check_bls(struct scan_token *scan) + scan[i].content.keyword.value); + rc = misc_check_readable_file(img_value); + if (rc) { +- error_text( ++ error_reason( + "Image file '%s' is not accessible", + scan[i].content.keyword.value); + return rc; + +From 8ab552b430f109d80966d0c56bed0d204d917d30 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 18 Nov 2019 11:45:50 -0500 +Subject: [PATCH 3/3] zipl: fix handling of values with load address in BLS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Various keywords (like image or ramdisk) allow specifying a load address +as an optional argument. Adapt the logic for checking the presence of +the files to take this into the account. + +Fixes: https://github.com/ibm-s390-tools/s390-tools/commit/d71628326d80e623fc9f008fe4ea93edb5592b2e +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1772054 + +Signed-off-by: Dan Horák +--- + zipl/src/scan.c | 32 ++++++++++++++++++++++++++------ + 1 file changed, 26 insertions(+), 6 deletions(-) + +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index 0f01cac9..a34edf62 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -1575,6 +1575,8 @@ scan_check_bls(struct scan_token *scan) + int i, rc; + char *target_value = NULL; + char *img_value = NULL; ++ char *file = NULL; ++ char *tmp, *value; + /* + * In the BLS case, each BLS section heading inherits a keyword + * assignment target= from zipl.conf, and they are all the same. +@@ -1595,19 +1597,37 @@ scan_check_bls(struct scan_token *scan) + if (scan[i].content.keyword.keyword == scan_keyword_image || + scan[i].content.keyword.keyword == scan_keyword_ramdisk) { + +- rc = misc_check_readable_file( +- scan[i].content.keyword.value); ++ value = scan[i].content.keyword.value; ++ /* ++ * put the filename only into the file var before ++ * checking its presence ++ */ ++ if (contains_address(value)) { ++ tmp = strrchr(value, ','); ++ file = strndup(value, tmp - value); ++ } else { ++ file = value; ++ } ++ rc = misc_check_readable_file(file); + if (rc) { + misc_asprintf(&img_value, "%s%s", +- target_value, +- scan[i].content.keyword.value); ++ target_value, file); + rc = misc_check_readable_file(img_value); + if (rc) { + error_reason( +- "Image file '%s' is not accessible", +- scan[i].content.keyword.value); ++ "File '%s' not accessible", file); + return rc; + } ++ /* ++ * when file has stripped the load address part, ++ * do generate a prefixed value ++ */ ++ if (file != value) { ++ free(file); ++ free(img_value); ++ misc_asprintf(&img_value, "%s%s", ++ target_value, value); ++ } + free(scan[i].content.keyword.value); + scan[i].content.keyword.value = img_value; + } diff --git a/SOURCES/s390-tools-zipl-invert-script-options.patch b/SOURCES/s390-tools-zipl-invert-script-options.patch new file mode 100644 index 0000000..93d5cc7 --- /dev/null +++ b/SOURCES/s390-tools-zipl-invert-script-options.patch @@ -0,0 +1,84 @@ +From 2faae5cf51c49e3f166b8526eee276dab2fe7308 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Wed, 30 May 2018 14:33:25 +0200 +Subject: [PATCH] zipl-switch-to-blscfg: invert ignore-default and + use-version-name options + +These options were added because the zipl maintainers wanted a different +default behaviour for the migration script than the one we use. Instead +of requiring to always use these options, just invert the logic for us. + +Signed-off-by: Javier Martinez Canillas +--- + scripts/zipl-switch-to-blscfg | 16 +++++++++------- + scripts/zipl-switch-to-blscfg.1 | 8 ++++---- + 2 files changed, 13 insertions(+), 11 deletions(-) + +diff --git a/scripts/zipl-switch-to-blscfg b/scripts/zipl-switch-to-blscfg +index 871935c783f..d8d5eca5867 100755 +--- a/scripts/zipl-switch-to-blscfg ++++ b/scripts/zipl-switch-to-blscfg +@@ -57,14 +57,14 @@ Options: + --backup-suffix=SUFFIX suffix used for backup files, defaults to .bak + --bls-directory=DIR path to generate BLS files, defaults to /boot/loader/entries + --config-file=FILE path to zipl configuration file, defaults to /etc/zipl.conf +- --ignore-default ignore the default option from the zipl configuration file +- --use-version-name use the section kernel version as the BLS file name ++ --leave-default leave the default option from the zipl configuration file ++ --use-section-name use the section name as the BLS file name + + EOF + } + + OPTS="$(getopt -o hv --long help,version,backup-suffix:,bls-directory:,config-file:,\ +-ignore-default,use-version-name -n \'$SCRIPTNAME\' -- "$@")" ++leave-default,use-section-name -n \'$SCRIPTNAME\' -- "$@")" + eval set -- "$OPTS" + + BACKUP_SUFFIX=.bak +@@ -73,6 +73,8 @@ CMDLINE_LINUX_DEBUG=" systemd.log_level=debug systemd.log_target=kmsg" + LINUX_DEBUG_VERSION_POSTFIX="_with_debugging" + LINUX_DEBUG_TITLE_POSTFIX=" with debugging" + CONFIG="/etc/zipl.conf" ++ignore_default=true ++version_name=true + + while [ ${#} -gt 0 ]; do + case "$1" in +@@ -96,11 +98,11 @@ while [ ${#} -gt 0 ]; do + CONFIG=${2} + shift + ;; +- --ignore-default) +- ignore_default=true ++ --leave-default) ++ ignore_default=false + ;; +- --use-version-name) +- version_name=true ++ --use-section-name) ++ version_name=false + ;; + --) + shift +diff --git a/scripts/zipl-switch-to-blscfg.1 b/scripts/zipl-switch-to-blscfg.1 +index 6bd14d00d14..71b904ffd1c 100644 +--- a/scripts/zipl-switch-to-blscfg.1 ++++ b/scripts/zipl-switch-to-blscfg.1 +@@ -37,9 +37,9 @@ The DIRECTORY where the BLS fragments will be generated. The directory is create + The FILE used for zipl configuration file, defaults to /etc/zipl.conf. + + .TP +-\fB\-\-ignore-default\fP +-Ignore the default option from the zipl configuration file ++\fB\-\-leave-default\fP ++Leave the default option from the zipl configuration file + + .TP +-\fB\-\-use-version-name\fP +-Use the section kernel version as the BLS file name ++\fB\-\-use-section-name\fP ++Use the section name as the BLS file name +-- +2.17.0 + diff --git a/SOURCES/s390-tools-zipl-sort-like-rpm.patch b/SOURCES/s390-tools-zipl-sort-like-rpm.patch new file mode 100644 index 0000000..4595255 --- /dev/null +++ b/SOURCES/s390-tools-zipl-sort-like-rpm.patch @@ -0,0 +1,147 @@ +From 8ec7b75204f3c7bf691e14b89c73c5dd28d2a824 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 15 Oct 2018 13:54:16 -0400 +Subject: [PATCH] blscfg: sort like rpm nvr, not like a single version + +Signed-off-by: Peter Jones +--- + zipl/src/scan.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++- + zipl/src/Makefile | 3 +- + 2 files changed, 96 insertions(+), 3 deletions(-) + +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index fe72e9ab13d..63186222783 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -33,6 +33,8 @@ + + #include "lib/util_base.h" + ++#include ++ + #include "boot.h" + #include "error.h" + #include "misc.h" +@@ -652,14 +654,104 @@ bls_filter(const struct dirent *ent) + return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0; + } + ++/* returns name/version/release */ ++/* NULL string pointer returned if nothing found */ ++static void ++split_package_string (char *package_string, char **name, ++ char **version, char **release) ++{ ++ char *package_version, *package_release; ++ ++ /* Release */ ++ package_release = strrchr (package_string, '-'); ++ ++ if (package_release != NULL) ++ *package_release++ = '\0'; ++ ++ *release = package_release; ++ ++ /* Version */ ++ package_version = strrchr(package_string, '-'); ++ ++ if (package_version != NULL) ++ *package_version++ = '\0'; ++ ++ *version = package_version; ++ /* Name */ ++ *name = package_string; ++ ++ /* Bubble up non-null values from release to name */ ++ if (name != NULL && *name == NULL) { ++ *name = (*version == NULL ? *release : *version); ++ *version = *release; ++ *release = NULL; ++ } ++ if (*version == NULL) { ++ *version = *release; ++ *release = NULL; ++ } ++} ++ ++static int ++split_cmp(char *nvr0, char *nvr1, int has_name) ++{ ++ int ret = 0; ++ char *name0, *version0, *release0; ++ char *name1, *version1, *release1; ++ ++ split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0); ++ split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1); ++ ++ if (has_name) { ++ ret = rpmvercmp(name0 == NULL ? "" : name0, ++ name1 == NULL ? "" : name1); ++ if (ret != 0) ++ return ret; ++ } ++ ++ ret = rpmvercmp(version0 == NULL ? "" : version0, ++ version1 == NULL ? "" : version1); ++ if (ret != 0) ++ return ret; ++ ++ ret = rpmvercmp(release0 == NULL ? "" : release0, ++ release1 == NULL ? "" : release1); ++ return ret; ++} ++ ++/* return 1: filename0 is newer than filename1 */ ++/* 0: filename0 and filename1 are the same version */ ++/* -1: filename1 is newer than filename0 */ ++static int bls_cmp(const char *filename0, const char *filename1) ++{ ++ char *id0, *id1; ++ int l, r; ++ ++ id0 = strdup(filename0); ++ id1 = strdup(filename1); ++ ++ l = strlen(id0); ++ if (l > 5 && strcmp(id0 + l - 5, ".conf")) ++ id0[l-5] = '\0'; ++ ++ l = strlen(id1); ++ if (l > 5 && strcmp(id1 + l - 5, ".conf")) ++ id1[l-5] = '\0'; ++ ++ r = split_cmp(id0, id1, 1); ++ ++ free(id0); ++ free(id1); ++ ++ return r; ++} + + static int + bls_sort(const struct dirent **ent_a, const struct dirent **ent_b) + { +- return strverscmp((*ent_a)->d_name, (*ent_b)->d_name); ++ return bls_cmp((*ent_a)->d_name, (*ent_b)->d_name); + } + +- + static int + scan_append_section_heading(struct scan_token* scan, int* index, char* name); + static int +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index 1634c0d5121..bc797990652 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -7,7 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \ + -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) + ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) + +-libs = $(rootdir)/libutil/libutil.a ++libs = $(rootdir)/libutil/libutil.a -lrpm + + objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ + install.o zipl.o $(rootdir)/zipl/boot/data.o +-- +2.17.1 + diff --git a/SOURCES/zfcp.udev b/SOURCES/zfcp.udev new file mode 100644 index 0000000..5558f8b --- /dev/null +++ b/SOURCES/zfcp.udev @@ -0,0 +1 @@ +KERNEL=="zfcp", RUN+="/sbin/zfcpconf.sh" diff --git a/SOURCES/zfcpconf.sh b/SOURCES/zfcpconf.sh new file mode 100644 index 0000000..45d10a1 --- /dev/null +++ b/SOURCES/zfcpconf.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# config file syntax: +# deviceno WWPN FCPLUN +# +# Example: +# 0.0.4000 0x5005076300C213e9 0x5022000000000000 +# 0.0.4001 0x5005076300c213e9 0x5023000000000000 +# +# +# manual setup: +# modprobe zfcp +# echo 1 > /sys/bus/ccw/drivers/zfcp/0.0.4000/online +# echo LUN > /sys/bus/ccw/drivers/zfcp/0.0.4000/WWPN/unit_add +# +# Example: +# modprobe zfcp +# echo 1 > /sys/bus/ccw/drivers/zfcp/0.0.4000/online +# echo 0x5022000000000000 > /sys/bus/ccw/drivers/zfcp/0.0.4000/0x5005076300c213e9/unit_add + +CONFIG=/etc/zfcp.conf +PATH=/bin:/sbin + +if [ -f "$CONFIG" ]; then + if [ ! -d /sys/bus/ccw/drivers/zfcp ]; then + modprobe zfcp + fi + if [ ! -d /sys/bus/ccw/drivers/zfcp ]; then + return + fi + sed 'y/ABCDEF/abcdef/' < $CONFIG | while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + set $line + if [ $# -eq 5 ]; then + DEVICE=$1 + SCSIID=$2 + WWPN=$3 + SCSILUN=$4 + FCPLUN=$5 + echo "Warning: Deprecated values in /etc/zfcp.conf, ignoring SCSI ID $SCSIID and SCSI LUN $SCSILUN" + elif [ $# -eq 3 ]; then + DEVICE=${1##*0x} + WWPN=$2 + FCPLUN=$3 + fi + [ `cat /sys/bus/ccw/drivers/zfcp/${DEVICE}/online` = "0" ] \ + && echo 1 > /sys/bus/ccw/drivers/zfcp/${DEVICE}/online + [ ! -d /sys/bus/ccw/drivers/zfcp/${DEVICE}/${WWPN}/${FCPLUN} ] \ + && echo $FCPLUN > /sys/bus/ccw/drivers/zfcp/${DEVICE}/${WWPN}/unit_add + ;; + esac + done +fi diff --git a/SPECS/s390utils.spec b/SPECS/s390utils.spec new file mode 100644 index 0000000..5ba2cbe --- /dev/null +++ b/SPECS/s390utils.spec @@ -0,0 +1,1840 @@ +%define cmsfsver 1.1.8c +%define vipaver 2.1.0 + +%if 0%{?rhel} >= 8 +%global signzipl 1 +%endif + +Name: s390utils +Summary: Utilities and daemons for IBM z Systems +Group: System Environment/Base +Version: 2.6.0 +Release: 28%{?dist}.2 +Epoch: 2 +License: MIT +ExclusiveArch: s390 s390x +#URL: http://www.ibm.com/developerworks/linux/linux390/s390-tools.html +URL: https://github.com/ibm-s390-tools/s390-tools +Source0: https://github.com/ibm-s390-tools/s390-tools/archive/v%{version}.tar.gz#/s390-tools-%{version}.tar.gz +Source4: http://www.linuxvm.org/Patches/S390/cmsfs-%{cmsfsver}.tar.gz +Source5: zfcpconf.sh +# http://www.ibm.com/developerworks/linux/linux390/src_vipa-%%{vipaver}.html +Source6: http://download.boulder.ibm.com/ibmdl/pub/software/dw/linux390/ht_src/src_vipa-%{vipaver}.tar.gz +Source7: zfcp.udev +# files for DASD initialization +Source12: dasd.udev +Source13: dasdconf.sh +Source14: device_cio_free +Source15: device_cio_free.service +Source16: ccw_init +Source17: ccw.udev +Source21: normalize_dasd_arg +Source22: 00-zipl-prepare.install +Source23: 20-zipl-kernel.install +Source24: 52-zipl-rescue.install +Source25: 91-zipl.install + +# for secure boot +%if 0%{?signzipl} +%define pesign_name redhatsecureboot302 +%endif + +# backported stuff for RHEL +Patch0: s390-tools-rhel.patch +# BLS support in zipl +# change the defaults to match Fedora environment +Patch100: s390-tools-zipl-invert-script-options.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1640968 +Patch102: s390-tools-zipl-sort-like-rpm.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1772054 +Patch103: s390-tools-zipl-bls-loadaddr.patch + +Patch1000: cmsfs-1.1.8-warnings.patch +Patch1001: cmsfs-1.1.8-kernel26.patch +Patch1002: cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch + +Requires: s390utils-base = %{epoch}:%{version}-%{release} +Requires: s390utils-osasnmpd = %{epoch}:%{version}-%{release} +Requires: s390utils-cpuplugd = %{epoch}:%{version}-%{release} +Requires: s390utils-mon_statd = %{epoch}:%{version}-%{release} +Requires: s390utils-iucvterm = %{epoch}:%{version}-%{release} +Requires: s390utils-ziomon = %{epoch}:%{version}-%{release} +Requires: s390utils-cmsfs = %{epoch}:%{version}-%{release} + +BuildRequires: gcc-c++ +BuildRequires: rpm-devel + +%description +This is a meta package for installing the default s390-tools sub packages. +If you do not need all default sub packages, it is recommended to install the +required sub packages separately. + +The s390utils packages contain a set of user space utilities that should to +be used together with the zSeries (s390) Linux kernel and device drivers. + +%prep +%setup -q -n s390-tools-%{version} -a 4 -a 6 + +# Backported stuff for RHEL +%patch0 -p1 + +# BLS support in zipl +%patch100 -p1 -b .zipl-invert-script-options +%patch102 -p1 -b .zipl-sort-like-rpm +%patch103 -p1 -b .zipl-bls-loadaddr + +# +# cmsfs +# +pushd cmsfs-%{cmsfsver} +# Patch to fix a couple of code bugs +%patch1000 -p1 -b .warnings + +# build on kernel-2.6, too +%patch1001 -p1 -b .cmsfs26 + +# use detected filesystem block size (#651012) +%patch1002 -p1 -b .use-detected-block-size +popd + + +# remove --strip from install +find . -name Makefile | xargs sed -i 's/$(INSTALL) -s/$(INSTALL)/g' + +pushd cmsfs-%{cmsfsver} +# cmdfs: fix encoding +iconv -f ISO8859-1 -t UTF-8 -o README.new README +touch -r README README.new +mv README.new README +# prepare docs +mv README README.cmsfs +mv CREDITS CREDITS.cmsfs +popd + + +%build +make \ + CFLAGS="%{build_cflags}" CXXFLAGS="%{build_cflags}" LDFLAGS="%{build_ldflags}" \ + BINDIR=/usr/sbin \ + DISTRELEASE=%{release} \ + V=1 + +pushd cmsfs-%{cmsfsver} +./configure +make CC="gcc %{build_cflags} -fno-strict-aliasing %{build_ldflags}" +popd + +pushd src_vipa-%{vipaver} +make CC_FLAGS="%{build_cflags} -fPIC" LD_FLAGS="%{build_ldflags} -shared" LIBDIR=%{_libdir} +popd + + +%install +make install \ + HAVE_DRACUT=1 \ + DESTDIR=$RPM_BUILD_ROOT \ + BINDIR=/usr/sbin \ + SYSTEMDSYSTEMUNITDIR=%{_unitdir} \ + DISTRELEASE=%{release} \ + V=1 + +# sign the stage3 bootloader +%if 0%{?signzipl} +if [ -x /usr/bin/rpm-sign ]; then + pushd %{buildroot}/lib/s390-tools/ + rpm-sign --key "%{pesign_name}" --lkmsign stage3.bin --output stage3.signed + mv stage3.signed stage3.bin + popd +else + echo "rpm-sign not available, stage3 won't be signed" +fi +%endif + +mkdir -p $RPM_BUILD_ROOT{/boot,%{_udevrulesdir},%{_sysconfdir}/{profile.d,sysconfig},%{_prefix}/lib/modules-load.d} +install -p -m 644 zipl/boot/tape0.bin $RPM_BUILD_ROOT/boot/tape0 +install -p -m 755 %{SOURCE5} $RPM_BUILD_ROOT%{_sbindir} +install -p -m 755 %{SOURCE13} $RPM_BUILD_ROOT%{_sbindir} +install -p -m 755 %{SOURCE21} $RPM_BUILD_ROOT%{_sbindir} +install -p -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_udevrulesdir}/56-zfcp.rules +install -p -m 644 %{SOURCE12} $RPM_BUILD_ROOT%{_udevrulesdir}/56-dasd.rules + +touch $RPM_BUILD_ROOT%{_sysconfdir}/{zfcp.conf,dasd.conf} + +# upstream udev rules +install -Dp -m 644 etc/udev/rules.d/*.rules $RPM_BUILD_ROOT%{_udevrulesdir} + +# upstream modules config +install -Dp -m 644 etc/modules-load.d/*.conf $RPM_BUILD_ROOT%{_prefix}/lib/modules-load.d + +# Install kernel-install scripts +install -d -m 0755 %{buildroot}%{_prefix}/lib/kernel/install.d/ +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE22} +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ zfcpdump/10-zfcpdump.install +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE23} +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE24} +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE25} +install -d -m 0755 %{buildroot}%{_sysconfdir}/kernel/install.d/ +install -m 0644 /dev/null %{buildroot}%{_sysconfdir}/kernel/install.d/20-grubby.install + +# cmsfs tools must be available in /sbin +for f in cat lst vol cp ck; do + install -p -m 755 cmsfs-%{cmsfsver}/cmsfs${f} $RPM_BUILD_ROOT%{_sbindir} + install -p -m 644 cmsfs-%{cmsfsver}/cmsfs${f}.8 $RPM_BUILD_ROOT%{_mandir}/man8 +done + +# src_vipa +pushd src_vipa-%{vipaver} +make install LIBDIR=%{_libdir} SBINDIR=%{_bindir} INSTROOT=$RPM_BUILD_ROOT LDCONFIG=/bin/true +popd + +# install usefull headers for devel subpackage +mkdir -p $RPM_BUILD_ROOT%{_includedir}/%{name} +install -p -m 644 include/lib/vtoc.h $RPM_BUILD_ROOT%{_includedir}/%{name} + +# device_cio_free +install -p -m 755 %{SOURCE14} $RPM_BUILD_ROOT%{_sbindir} +pushd $RPM_BUILD_ROOT%{_sbindir} +for lnk in dasd zfcp znet; do + ln -sf device_cio_free ${lnk}_cio_free +done +popd +install -p -m 644 %{SOURCE15} $RPM_BUILD_ROOT%{_unitdir} + +# ccw +install -p -m 755 %{SOURCE16} $RPM_BUILD_ROOT/usr/lib/udev/ccw_init +install -p -m 644 %{SOURCE17} $RPM_BUILD_ROOT%{_udevrulesdir}/81-ccw.rules + +# zipl.conf to be ghosted +touch $RPM_BUILD_ROOT%{_sysconfdir}/zipl.conf + + +%files +%doc README.md + +# +# ************************* s390-tools base package ************************* +# +%package base +# src_vipa is CPL +License: MIT and CPL +Summary: S390 base tools +Group: System Environment/Base +Requires: gawk sed coreutils +Requires: sysfsutils +Requires: sg3_utils +Requires: ethtool +%{?systemd_requires} +BuildRequires: perl-generators +BuildRequires: ncurses-devel +BuildRequires: libpfm-devel +BuildRequires: glibc-static +BuildRequires: cryptsetup-devel >= 2.0.3 +BuildRequires: json-c-devel + + +%description base +s390 base tools. This collection provides the following utilities: + * dasdfmt: + Low-level format ECKD DASDs with the classical linux disk layout or the + new z/OS compatible disk layout. + + * fdasd: + Create or modify partitions on ECKD DASDs formatted with the z/OS + compatible disk layout. + + * dasdview: + Display DASD and VTOC information or dump the contents of a DASD to the + console. + + * dasdinfo: + Display unique DASD ID, either UID or volser. + + * udev rules: + - 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/. + + * zipl: + Make DASDs or tapes bootable for system IPL or system dump. + + * zgetdump: + Retrieve system dumps from either tapes or DASDs. + + * qetharp: + Read and flush the ARP cache on OSA Express network cards. + + * tape390_display: + Display information on the message display facility of a zSeries tape + device. + + * tape390_crypt: + Control and query crypto settings for 3592 zSeries tape devices. + + * qethconf: + bash shell script simplifying the usage of qeth IPA (IP address + takeover), VIPA (Virtual IP address) and Proxy ARP. + + * dbginfo.sh: + Shell script collecting useful information about the current system for + debugging purposes. + + * zfcpdump: + Dump tool to create system dumps on fibre channel attached SCSI disks. + It is installed using the zipl command. + + * zfcpdump_v2: + Version 2 of the zfcpdump tool. Now based on the upstream 2.6.26 Linux + kernel. + + * ip_watcher: + Provides HiperSockets Network Concentrator functionality. + It looks for addresses in the HiperSockets and sets them as Proxy ARP + on the OSA cards. It also adds routing entries for all IP addresses + configured on active HiperSockets devices. + Use start_hsnc.sh to start HiperSockets Network Concentrator. + + * tunedasd: + Adjust tunable parameters on DASD devices. + + * vmconvert: + Convert system dumps created by the z/VM VMDUMP command into dumps with + LKCD format. These LKCD dumps can then be analyzed with the dump analysis + tool lcrash. + + * vmcp: + Allows Linux users to send commands to the z/VM control program (CP). + The normal usage is to invoke vmcp with the command you want to + execute. The response of z/VM is written to the standard output. + + * vmur: + Allows to work with z/VM spool file queues (reader, punch, printer). + + * zfcpdbf: + Display debug data of zfcp. zfcp provides traces via the s390 debug + feature. Those traces are filtered with the zfcpdbf script, i.e. merge + several traces, make it more readable etc. + + * scsi_logging_level: + Create, get or set the logging level for the SCSI logging facility. + + * zconf: + Set of scripts to configure and list status information of Linux for + zSeries IO devices. + - chccwdev: Modify generic attributes of channel attached devices. + - lscss: List channel subsystem devices. + - lsdasd: List channel attached direct access storage devices (DASD). + - lsqeth: List all qeth-based network devices with their corresponding + settings. + - lstape: List tape devices, both channel and FCP attached. + - lszfcp: Shows information contained in sysfs about zfcp adapters, + ports and units that are online. + - lschp: List information about available channel-paths. + - chchp: Modify channel-path state. + - lsluns: List available SCSI LUNs depending on adapter or port. + - lszcrypt: Show Information about zcrypt devices and configuration. + - chzcrypt: Modify zcrypt configuration. + - znetconf: List and configure network devices for System z network + adapters. + - cio_ignore: Query and modify the contents of the CIO device driver + blacklist. + + * dumpconf: + Allows to configure the dump device used for system dump in case a kernel + panic occurs. This tool can also be used as an init script for etc/init.d. + Prerequisite for dumpconf is a Linux kernel with the "dump on panic" + feature. + + * ipl_tools: + Tools set to configure and list reipl and shutdown actions. + - lsreipl: List information of reipl device. + - chreipl: Change reipl device settings. + - lsshut: List actions which will be done in case of halt, poff, reboot + or panic. + - chshut: Change actions which should be done in case of halt, poff, + reboot or panic. + + * cpi: + Allows to set the system and sysplex names from the Linux guest to + the HMC/SE using the Control Program Identification feature. + +For more information refer to the following publications: + * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" + * "Using the dump tools" + +%pre base +# check for zkeyadm group and create it +getent group zkeyadm > /dev/null || groupadd -r zkeyadm + +%post base +%systemd_post cpi.service +%if 0 +# enable in F-31 +%systemd_post device_cio_free.service +%else +# explicit enable for upgrade patch from s390utils-base < 2.6.0-4 +systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : +%endif +%systemd_post dumpconf.service + +%preun base +%systemd_preun cpi.service +%systemd_preun device_cio_free.service +%systemd_preun dumpconf.service + +%postun base +%systemd_postun_with_restart cpi.service +%systemd_postun_with_restart dumpconf.service + +%files base +%doc README.md zdev/src/*.txt +%doc LICENSE +%{_sbindir}/chccwdev +%{_sbindir}/chchp +%{_sbindir}/chcpumf +%{_sbindir}/chreipl +%{_sbindir}/chshut +%{_sbindir}/chzcrypt +%{_sbindir}/chzdev +%{_sbindir}/cio_ignore +%{_sbindir}/dasdfmt +%{_sbindir}/dasdinfo +%{_sbindir}/dasdstat +%{_sbindir}/dasdview +%{_sbindir}/dbginfo.sh +%{_sbindir}/fdasd +%{_sbindir}/hyptop +%{_sbindir}/ip_watcher.pl +%{_sbindir}/lschp +%{_sbindir}/lscss +%{_sbindir}/lsdasd +%{_sbindir}/lsqeth +%{_sbindir}/lsluns +%{_sbindir}/lsreipl +%{_sbindir}/lsscm +%{_sbindir}/lsshut +%{_sbindir}/lstape +%{_sbindir}/lszcrypt +%{_sbindir}/lszdev +%{_sbindir}/lszfcp +%{_sbindir}/qetharp +%{_sbindir}/qethconf +%{_sbindir}/qethqoat +%{_sbindir}/scsi_logging_level +%{_sbindir}/start_hsnc.sh +%{_sbindir}/tape390_crypt +%{_sbindir}/tape390_display +%{_sbindir}/ttyrun +%{_sbindir}/tunedasd +%{_sbindir}/vmcp +%{_sbindir}/vmur +%{_sbindir}/xcec-bridge +%{_sbindir}/zcryptctl +%{_sbindir}/zcryptstats +%{_sbindir}/zfcpdbf +%{_sbindir}/zgetdump +%{_sbindir}/zipl +%{_sbindir}/zipl-switch-to-blscfg +%{_sbindir}/znetconf +%{_sbindir}/zpcictl +%{_bindir}/lscpumf +%{_bindir}/dump2tar +%{_bindir}/vmconvert +%{_bindir}/zkey +%{_bindir}/zkey-cryptsetup +%{_unitdir}/cpi.service +%{_unitdir}/dumpconf.service +%ghost %config(noreplace) %{_sysconfdir}/zipl.conf +%config(noreplace) %{_sysconfdir}/sysconfig/cpi +%config(noreplace) %{_sysconfdir}/sysconfig/dumpconf +/lib/s390-tools/ +/usr/lib/dracut/modules.d/95zdev/ +%{_mandir}/man1/dbginfo.sh.1* +%{_mandir}/man1/dump2tar.1* +%{_mandir}/man1/lscpumf.1* +%{_mandir}/man1/vmconvert.1* +%{_mandir}/man1/zfcpdbf.1* +%{_mandir}/man1/zipl-switch-to-blscfg.1* +%{_mandir}/man1/zkey.1* +%{_mandir}/man1/zkey-cryptsetup.1* +%{_mandir}/man4/prandom.4* +%{_mandir}/man5/zipl.conf.5* +%{_mandir}/man8/chccwdev.8* +%{_mandir}/man8/chchp.8* +%{_mandir}/man8/chcpumf.8* +%{_mandir}/man8/chreipl.8* +%{_mandir}/man8/chshut.8* +%{_mandir}/man8/chzcrypt.8* +%{_mandir}/man8/chzdev.8* +%{_mandir}/man8/cio_ignore.8* +%{_mandir}/man8/dasdfmt.8* +%{_mandir}/man8/dasdinfo.8* +%{_mandir}/man8/dasdstat.8* +%{_mandir}/man8/dasdview.8* +%{_mandir}/man8/dumpconf.8* +%{_mandir}/man8/fdasd.8* +%{_mandir}/man8/hyptop.8* +%{_mandir}/man8/lschp.8* +%{_mandir}/man8/lscss.8* +%{_mandir}/man8/lsdasd.8* +%{_mandir}/man8/lsluns.8* +%{_mandir}/man8/lsqeth.8* +%{_mandir}/man8/lsreipl.8* +%{_mandir}/man8/lsscm.8* +%{_mandir}/man8/lsshut.8* +%{_mandir}/man8/lstape.8* +%{_mandir}/man8/lszcrypt.8* +%{_mandir}/man8/lszdev.8* +%{_mandir}/man8/lszfcp.8* +%{_mandir}/man8/qetharp.8* +%{_mandir}/man8/qethconf.8* +%{_mandir}/man8/qethqoat.8* +%{_mandir}/man8/tape390_crypt.8* +%{_mandir}/man8/tape390_display.8* +%{_mandir}/man8/ttyrun.8* +%{_mandir}/man8/tunedasd.8* +%{_mandir}/man8/vmcp.8* +%{_mandir}/man8/vmur.8* +%{_mandir}/man8/zcryptctl.8* +%{_mandir}/man8/zcryptstats.8* +%{_mandir}/man8/zgetdump.8* +%{_mandir}/man8/zipl.8* +%{_mandir}/man8/znetconf.8* +%{_mandir}/man8/zpcictl.8* +%dir %{_datadir}/s390-tools/ +%{_datadir}/s390-tools/cpumf/ +%{_datadir}/s390-tools/netboot/ +%dir %attr(0770,root,zkeyadm) %{_sysconfdir}/zkey +%dir %attr(0770,root,zkeyadm) %{_sysconfdir}/zkey/repository + +# Additional Redhat specific stuff +/boot/tape0 +%ghost %config(noreplace) %{_sysconfdir}/dasd.conf +%ghost %config(noreplace) %{_sysconfdir}/zfcp.conf +%{_sbindir}/dasdconf.sh +%{_sbindir}/zfcpconf.sh +%{_sbindir}/dasd_cio_free +%{_sbindir}/device_cio_free +%{_sbindir}/zfcp_cio_free +%{_sbindir}/znet_cio_free +%{_sbindir}/normalize_dasd_arg +%{_unitdir}/device_cio_free.service +/usr/lib/udev/ccw_init +%{_udevrulesdir}/40-z90crypt.rules +%{_udevrulesdir}/56-zfcp.rules +%{_udevrulesdir}/56-dasd.rules +%{_udevrulesdir}/59-dasd.rules +%{_udevrulesdir}/60-readahead.rules +%{_udevrulesdir}/81-ccw.rules +%{_udevrulesdir}/90-cpi.rules +%{_sysconfdir}/kernel/install.d/20-grubby.install +%{_prefix}/lib/kernel/install.d/00-zipl-prepare.install +%{_prefix}/lib/kernel/install.d/10-zfcpdump.install +%{_prefix}/lib/kernel/install.d/20-zipl-kernel.install +%{_prefix}/lib/kernel/install.d/52-zipl-rescue.install +%{_prefix}/lib/kernel/install.d/91-zipl.install +%{_prefix}/lib/modules-load.d/s390-pkey.conf + +# src_vipa +%{_bindir}/src_vipa.sh +%{_libdir}/src_vipa.so +%{_mandir}/man8/src_vipa.8* + +# +# *********************** s390-tools osasnmpd package *********************** +# +%package osasnmpd +Summary: SNMP sub-agent for OSA-Express cards +Group: System Environment/Daemons +Requires: net-snmp +Requires: psmisc +BuildRequires: net-snmp-devel + +%description osasnmpd +UCD-SNMP/NET-SNMP sub-agent implementing MIBs provided by OSA-Express +features Fast Ethernet, Gigabit Ethernet, High Speed Token Ring and +ATM Ethernet LAN Emulation in QDIO mode. + +%files osasnmpd +%{_sbindir}/osasnmpd +%{_udevrulesdir}/57-osasnmpd.rules +%{_mandir}/man8/osasnmpd.8* + +# +# *********************** s390-tools mon_statd package ********************** +# +%package mon_statd +Summary: Monitoring daemons for Linux in z/VM +Group: System Environment/Daemons +Requires: coreutils +%{?systemd_requires} + +%description mon_statd +Monitoring daemons for Linux in z/VM: + + - mon_fsstatd: Daemon that writes file system utilization data to the + z/VM monitor stream. + + - mon_procd: Daemon that writes process information data to the z/VM + monitor stream. + +%post mon_statd +%systemd_post mon_fsstatd.service +%systemd_post mon_procd.service + +%preun mon_statd +%systemd_preun mon_fsstatd.service +%systemd_preun mon_procd.service + +%postun mon_statd +%systemd_postun_with_restart mon_fsstatd.service +%systemd_postun_with_restart mon_procd.service + +%files mon_statd +%{_sbindir}/mon_fsstatd +%{_sbindir}/mon_procd +%config(noreplace) %{_sysconfdir}/sysconfig/mon_fsstatd +%config(noreplace) %{_sysconfdir}/sysconfig/mon_procd +%{_unitdir}/mon_fsstatd.service +%{_unitdir}/mon_procd.service +%{_mandir}/man8/mon_fsstatd.8* +%{_mandir}/man8/mon_procd.8* + +# +# *********************** s390-tools cpuplugd package *********************** +# +%package cpuplugd +Summary: Daemon that manages CPU and memory resources +Group: System Environment/Daemons +%{?systemd_requires} +BuildRequires: systemd + +%description cpuplugd +Daemon that manages CPU and memory resources based on a set of rules. +Depending on the workload CPUs can be enabled or disabled. The amount of +memory can be increased or decreased exploiting the CMM1 feature. + +%post cpuplugd +%systemd_post cpuplugd.service + +%preun cpuplugd +%systemd_preun cpuplugd.service + +%postun cpuplugd +%systemd_postun_with_restart cpuplugd.service + +%files cpuplugd +%config(noreplace) %{_sysconfdir}/cpuplugd.conf +%{_sbindir}/cpuplugd +%{_mandir}/man5/cpuplugd.conf.5* +%{_mandir}/man8/cpuplugd.8* +%{_unitdir}/cpuplugd.service + +# +# *********************** s390-tools ziomon package ************************* +# +%package ziomon +Summary: S390 ziomon tools +Group: Applications/System +Requires: blktrace +Requires: coreutils +Requires: device-mapper-multipath +Requires: gawk +Requires: grep +Requires: lsscsi +Requires: procps-ng +Requires: rsync +Requires: sed +Requires: tar +Requires: util-linux + +%description ziomon +Tool set to collect data for zfcp performance analysis and report. + +%files ziomon +%{_sbindir}/ziomon +%{_sbindir}/ziomon_fcpconf +%{_sbindir}/ziomon_mgr +%{_sbindir}/ziomon_util +%{_sbindir}/ziomon_zfcpdd +%{_sbindir}/ziorep_config +%{_sbindir}/ziorep_traffic +%{_sbindir}/ziorep_utilization +%{_mandir}/man8/ziomon.8* +%{_mandir}/man8/ziomon_fcpconf.8* +%{_mandir}/man8/ziomon_mgr.8* +%{_mandir}/man8/ziomon_util.8* +%{_mandir}/man8/ziomon_zfcpdd.8* +%{_mandir}/man8/ziorep_config.8* +%{_mandir}/man8/ziorep_traffic.8* +%{_mandir}/man8/ziorep_utilization.8* + +# +# *********************** s390-tools iucvterm package ************************* +# +%package iucvterm +Summary: z/VM IUCV terminal applications +Group: Applications/System +Requires(pre): shadow-utils +Requires(post): grep +Requires(postun): grep +BuildRequires: gettext +BuildRequires: systemd + +%description iucvterm +A set of applications to provide terminal access via the z/VM Inter-User +Communication Vehicle (IUCV). The terminal access does not require an +active TCP/IP connection between two Linux guest operating systems. + +- iucvconn: Application to establish a terminal connection via z/VM IUCV. +- iucvtty: Application to provide terminal access via z/VM IUCV. +- ts-shell: Terminal server shell to authorize and control IUCV terminal + connections for individual Linux users. + +%pre iucvterm +# check for ts-shell group and create it +getent group ts-shell > /dev/null || groupadd -r ts-shell + +%post iucvterm +# /etc/shells is provided by "setup" +grep -q '^/usr/bin/ts-shell$' /etc/shells \ + || echo "/usr/bin/ts-shell" >> /etc/shells + +%postun iucvterm +if [ $1 = 0 ] +then + # remove ts-shell from /etc/shells on uninstall + grep -v '^/usr/bin/ts-shell$' /etc/shells > /etc/shells.ts-new + mv /etc/shells.ts-new /etc/shells + chmod 0644 /etc/shells +fi + +%files iucvterm +%dir %{_sysconfdir}/iucvterm +%config(noreplace) %attr(0640,root,ts-shell) %{_sysconfdir}/iucvterm/ts-audit-systems.conf +%config(noreplace) %attr(0640,root,ts-shell) %{_sysconfdir}/iucvterm/ts-authorization.conf +%config(noreplace) %attr(0640,root,ts-shell) %{_sysconfdir}/iucvterm/ts-shell.conf +%config(noreplace) %attr(0640,root,ts-shell) %{_sysconfdir}/iucvterm/unrestricted.conf +%{_bindir}/iucvconn +%{_bindir}/iucvtty +%{_bindir}/ts-shell +%{_sbindir}/chiucvallow +%{_sbindir}/lsiucvallow +%dir %attr(2770,root,ts-shell) /var/log/ts-shell +%doc iucvterm/doc/ts-shell +%{_mandir}/man1/iucvconn.1* +%{_mandir}/man1/iucvtty.1* +%{_mandir}/man1/ts-shell.1* +%{_mandir}/man7/af_iucv.7* +%{_mandir}/man8/chiucvallow.8* +%{_mandir}/man9/hvc_iucv.9* +%{_unitdir}/iucvtty-login@.service +%{_unitdir}/ttyrun-getty@.service + +# +# *********************** cmsfs package *********************** +# +%package cmsfs +License: GPLv2 +Summary: CMS file system tools +Group: System Environment/Base +URL: http://www.casita.net/pub/cmsfs/cmsfs.html +# Requires: + +%description cmsfs +This package contains the CMS file system tools. + +%files cmsfs +%{_sbindir}/cmsfscat +%{_sbindir}/cmsfsck +%{_sbindir}/cmsfscp +%{_sbindir}/cmsfslst +%{_sbindir}/cmsfsvol +%{_mandir}/man8/cmsfscat.8* +%{_mandir}/man8/cmsfsck.8* +%{_mandir}/man8/cmsfscp.8* +%{_mandir}/man8/cmsfslst.8* +%{_mandir}/man8/cmsfsvol.8* + +# +# *********************** cmsfs-fuse package *********************** +# +%package cmsfs-fuse +Summary: CMS file system based on FUSE +Group: System Environment/Base +BuildRequires: fuse-devel +Requires: fuse + +%description cmsfs-fuse +This package contains the CMS file system based on FUSE. + +%files cmsfs-fuse +%dir %{_sysconfdir}/cmsfs-fuse +%config(noreplace) %{_sysconfdir}/cmsfs-fuse/filetypes.conf +%{_bindir}/cmsfs-fuse +%{_mandir}/man1/cmsfs-fuse.1* + +# +# *********************** zdsfs package *********************** +# +%package zdsfs +Summary: z/OS data set access based on FUSE +Group: System Environment/Base +BuildRequires: fuse-devel +Requires: fuse + +%description zdsfs +This package contains the z/OS data set access based on FUSE. + +%files zdsfs +%{_bindir}/zdsfs +%{_mandir}/man1/zdsfs.1* + +# +# *********************** hmcdrvfs package *********************** +# +%package hmcdrvfs +Summary: HMC drive file system based on FUSE +Group: System Environment/Base +BuildRequires: fuse-devel +Requires: fuse + +%description hmcdrvfs +This package contains a HMC drive file system based on FUSE and a tool +to list files and directories. + +%files hmcdrvfs +%{_bindir}/hmcdrvfs +%{_sbindir}/lshmc +%{_mandir}/man1/hmcdrvfs.1* +%{_mandir}/man8/lshmc.8* + +# +# *********************** cpacfstatsd package *********************** +# +%package cpacfstatsd +Summary: Monitor and maintain CPACF activity counters +Group: System Environment/Base +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +Requires(pre): shadow-utils +BuildRequires: systemd + +%description cpacfstatsd +The cpacfstats tools provide a client/server application set to monitor +and maintain CPACF activity counters. + +%post cpacfstatsd +%systemd_post cpacfstatsd.service + +%preun cpacfstatsd +%systemd_preun cpacfstatsd.service + +%postun cpacfstatsd +%systemd_postun_with_restart cpacfstatsd.service + +%pre cpacfstatsd +getent group cpacfstats >/dev/null || groupadd -r cpacfstats + +%files cpacfstatsd +%{_bindir}/cpacfstats +%{_sbindir}/cpacfstatsd +%{_mandir}/man1/cpacfstats.1* +%{_mandir}/man8/cpacfstatsd.8* +%{_unitdir}/cpacfstatsd.service + +# +# *********************** devel package *********************** +# +%package devel +Summary: Development files +Group: Development/Libraries + +%description devel +User-space development files for the s390/s390x architecture. + +%files devel +%{_includedir}/%{name} + + +%changelog +* Tue Jul 07 2020 Dan Horák - 2:2.6.0-28.2 +- avoid dependency on network-scripts (#1852525) +- Resolves: #1852525 + +* Thu Jun 25 2020 Dan Horák - 2:2.6.0-28.1 +- zipl/libc: Fix potential buffer overflow in printf (#1847536) +- Resolves: #1847536 + +* Mon Jan 27 2020 Dan Horák - 2:2.6.0-28 +- zkey: Fix listing of keys on file systems reporting DT_UNKNOWN (#1792957) +- Resolves: #1792957 + +* Mon Jan 27 2020 Dan Horák - 2:2.6.0-27 +- zkey: Fix display of clear key size for XTS keys (#1794375) +- Resolves: #1794375 + +* Wed Jan 15 2020 Dan Horák - 2:2.6.0-26 +- fix service order after switching to real root file system (#1790790) +- Resolves: #1790790 + +* Fri Dec 13 2019 Dan Horák - 2:2.6.0-25 +- kernel-install: skip BOOT_IMAGE param when copying the cmdline to BLS snippets (#1782321) +- Resolves: #1782321 + +* Wed Dec 11 2019 Dan Horák - 2:2.6.0-24.1 +- rebuild + +* Tue Dec 10 2019 Dan Horák - 2:2.6.0-24 +- zipl: fix handling of values with load address in BLS (#1772054) +- kernel-install: fix /tmp being deleted when kernel-core is installed in a container (#1778771) +- kernel-install: fix BLS-related decision logic (#1778243) +- Resolves: #1772054 #1778771 #1778243 + +* Thu Nov 07 2019 Dan Horák - 2:2.6.0-23 +- zipl: config file handling improvements for CoreOS (#1764706) +- zipl: fix the scanned tokens array size calculation (#1751587) +- lstape, lsluns: handle non-zfcp; lin_tape multiple paths (#1766569) +- zcrypt: CEX7S exploitation support (#1723837) +- zkey: Add support for CCA AES CIPHER keys (#1719623) +- zkey: check master key consistency (#1753153) +- zkey: various enhancements (#1725881) +- zipl: set correct secure IPL default value (#1750326) +- zipl: Fix error message printed with --dumptofs (#1750307) +- zdev: add zfcp dix parameter handling (#1723852) +- zdsfs: add online vtoc refresh (#1685536) +- dasdfmt/lsdasd: Add Thin provisioning base support (#1651733) +- Resolves: #1651733 #1685536 #1723852 #1750307 #1750326 #1725881 #1753153 +- Resolves: #1719623 #1723837 #1766569 #1751587 #1764706 + +* Thu Sep 26 2019 Javier Martinez Canillas - 2:2.6.0-22 +- force a BLS config if /boot/loader/entries directory exists (#1755899) +- Resolves: #1755899 + +* Thu Sep 12 2019 Dan Horák - 2:2.6.0-21.1 +- rebuild + +* Wed Sep 04 2019 Dan Horák - 2:2.6.0-21 +- sign bootloader stage3 (#1739496) +- Resolves: #1739496 + +* Tue Aug 13 2019 Dan Horák - 2:2.6.0-20 +- zipl: fix zfcp dump image location (#1730707) +- Resolves: #1730707 + +* Wed Aug 07 2019 Dan Horák - 2:2.6.0-19 +- fdasd: Fix exit status in error cases (#1734816) +- zipl: do not overwrite BOOT_IMAGE entry (#1728677) +- Resolves: #1734816 #1728677 + +* Wed Jul 24 2019 Dan Horák - 2:2.6.0-18 +- zdev: Do not export inacceptable attribute values (#1731960) +- Resolves: #1731960 + +* Fri Jul 19 2019 Dan Horák - 2:2.6.0-17 +- ziomon: fix utilization recording with multi-digit scsi hosts (#1731203) +- Resolves: #1731203 + +* Thu Jul 18 2019 Dan Horák - 2:2.6.0-16 +- cpumf: Add support for CPU-Measurement Facility counters SVN 6 (#1683276) +- Resolves: #1683276 + +* Tue May 21 2019 Dan Horák - 2:2.6.0-15 +- zpcictl: Check for regular directory (#1695001) +- s390-tools: Add zcryptstats tool (#1658756) +- zipl: Secure Boot support for SCSI IPL (#1659401) +- Resolves: #1659401 #1658756 #1695001 + +* Wed Feb 27 2019 Dan Horák - 2:2.6.0-14 +- pkey: Support autoloading kernel pkey module (#1664632) +- Related: #1664632 + +* Fri Feb 01 2019 Dan Horák - 2:2.6.0-13 +- create cpacfstats group needed by cpacfstatsd (#1670076) +- Resolves: #1670076 + +* Tue Jan 29 2019 Dan Horák - 2:2.6.0-12 +- zfcpdump: add install script for zfcpdump kernel (#1600480) +- pkey: Support autoloading kernel pkey module (#1664632) +- Make kernel-install to update default if present in zipl.conf (#1665060) +- Resolves: #1600480 #1664632 #1665060 + +* Tue Dec 11 2018 Dan Horák - 2:2.6.0-11 +- zkey: Fails to run commands generated by 'zkey cryptsetup' (#1650628) +- zkey: Enhance error message about missing CCA library (#1655134) +- Resolves: #1650628 #1655134 + +* Mon Nov 19 2018 Dan Horák - 2:2.6.0-10 +- lszcrypt: support for alternate zcrypt device drivers (#1646355) +- zcryptctl: add zcryptctl to manage multiple zcrypt nodes (#1646354) +- qethqoat: add OSA-Express7S support (#1644384) +- zdev: qeth BridgePort and VNICC attribute conflict (#1643452) +- zpcictl: Change wording of man-page and help output (#1643451) +- zpcictl: Read device link to obtain device address (#1639220) +- zpcictl: Add tool to manage PCI devices (#1525409) +- Resolves: #1525409 #1639220 #1643451 #1643452 #1644384 #1646354 #1646355 + +* Tue Nov 06 2018 Javier Martinez Canillas - 2.6.0-9 +- Make zipl to use the BLS title field as the IPL section name +- Resolves: #1645200 + +* Mon Oct 22 2018 Dan Horák - 2:2.6.0-8 +- don't relink the zkey tools +- Resolves: #1624169 + +* Mon Oct 15 2018 Peter Jones - 2.6.0-7 +- Make the blscfg sort order match what grub2 and grubby do. (pjones) +- Add a ~debug suffix instead of -debug to sort it correctly. (javierm) +- Resolves: #1640968 + +* Mon Oct 01 2018 Dan Horák - 2:2.6.0-6 +- Fix kernel-install scripts issues (#1634803) +- Resolves: #1634803 + +* Fri Sep 21 2018 Dan Horák - 2:2.6.0-5 +- Makefile cleanups (#1624169) +- Resolves: #1624169 + +* Mon Sep 17 2018 Dan Horák - 2:2.6.0-4 +- drop redundant systemd services installation (#1631682) +- Resolves #1631682 + +* Fri Sep 14 2018 Dan Horák - 2:2.6.0-3 +- add FIEMAP support into zipl (#1623163) +- Resolves: #1623163 + +* Tue Aug 14 2018 Dan Horák - 2:2.6.0-2 +- fix R:/BR: perl + +* Fri Aug 10 2018 Dan Horák - 2:2.6.0-1 +- rebased to 2.6.0 (#1584283) +- include zdev dracut module +- Resolves: #1584283 + +* Fri Aug 10 2018 Josh Boyer - 2:2.5.0-6 +- Rebuild against net-snmp 5.8 +- Resolves: #1584510 + +* Tue Jul 31 2018 Dan Horák - 2:2.5.0-5 +- add missing zkey infrastructure (#1610242) + +* Fri Jul 27 2018 Dan Horák - 2:2.5.0-4 +- don't override TERM for console + +* Thu Jul 26 2018 Dan Horák - 2:2.5.0-3 +- network-scripts are required for network device initialization + +* Sat Jul 14 2018 Fedora Release Engineering - 2:2.5.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Mon Jun 11 2018 Dan Horák - 2:2.5.0-1 +- rebased to 2.5.0 + +* Thu May 24 2018 Javier Martinez Canillas - 2:2.4.0-2 +- zipl: Add BootLoaderSpec support +- Add kernel-install scripts to create BLS fragment files + +* Wed May 09 2018 Dan Horák - 2:2.4.0-1 +- rebased to 2.4.0 + +* Fri Apr 13 2018 Dan Horák - 2:2.3.0-3 +- fix building zipl with PIE (#1566140) + +* Mon Mar 12 2018 Dan Horák - 2:2.3.0-2 +- fix LDFLAGS injection (#1552661) + +* Wed Feb 21 2018 Rafael Santos - 2:2.3.0-1 +- rebased to 2.3.0 + +* Fri Feb 09 2018 Fedora Release Engineering - 2:2.2.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Jan 22 2018 Dan Horák - 2:2.2.0-2 +- fix build with non-standard %%dist + +* Thu Dec 07 2017 Dan Horák - 2:2.2.0-1 +- rebased to 2.2.0 + +* Mon Sep 25 2017 Dan Horák - 2:2.1.0-1 +- rebased to 2.1.0 + +* Wed Aug 23 2017 Dan Horák - 2:2.0.0-1 +- rebased to first public release on github, functionally same as 1.39.0 +- relicensed to MIT + +* Wed Aug 23 2017 Dan Horák - 2:1.39.0-1 +- rebased to 1.39.0 +- completed switch to systemd +- further cleanups and consolidation + +* Wed Aug 16 2017 Dan Horák - 2:1.37.1-4 +- rebuild for librpm soname bump in rpm 4.13.90 + +* Thu Aug 03 2017 Fedora Release Engineering - 2:1.37.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 2:1.37.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri May 19 2017 Dan Horák - 2:1.37.1-1 +- rebased to 1.37.1 +- removed chmem/lsmem as they are now provided by util-linux >= 2.30 (#1452792) + +* Sat Feb 11 2017 Fedora Release Engineering - 2:1.36.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Dec 01 2016 Dan Horák - 2:1.36.1-1 +- rebased to 1.36.1 + +* Wed Sep 07 2016 Dan Horák - 2:1.36.0-1 +- rebased to 1.36.0 +- switch cpuplugd to systemd service + +* Fri Apr 22 2016 Dan Horák - 2:1.34.0-1 +- rebased to 1.34.0 + +* Thu Feb 04 2016 Fedora Release Engineering - 2:1.30.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Oct 01 2015 Dan Horák - 2:1.30.0-2 +- rebuild for librpm soname bump + +* Fri Jul 17 2015 Dan Horák - 2:1.30.0-1 +- rebased to 1.30.0 + +* Tue Jun 23 2015 Dan Horák - 2:1.29.0-1 +- rebased to 1.29.0 +- dropped daemon hardening patch as hardening is enabled globally +- added hmcdrvfs and cpacfstatsd subpackages +- install systemd units where available + +* Fri Jun 19 2015 Fedora Release Engineering - 2:1.23.0-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Tue Apr 07 2015 Dan Horák - 2:1.23.0-15 +- remove bashism from zfcpconf.sh + +* Wed Jan 28 2015 Dan Horák - 2:1.23.0-14 +- refresh from RHEL-7 + - update patches + - add zdsfs subpackage + - rebase src_vipa to 2.1.0 + +* Thu Oct 09 2014 Dan Horák - 2:1.23.0-13 +- update device_cio_free script +- udpate Requires for ziomon subpackage + +* Wed Jun 11 2014 Dan Horák - 2:1.23.0-12 +- update for -Werror=format-security + +* Sun Jun 08 2014 Fedora Release Engineering - 2:1.23.0-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Mar 04 2014 Dan Horák - 2:1.23.0-10 +- fix zFCP device discovery in anaconda GUI (#1054691) + +* Mon Feb 10 2014 Dan Horák - 2:1.23.0-9 +- znetconf: Allow for 16-char network interface names (#1062285) +- qetharp: Allow for 16-char network interface names (#1062250) + +* Mon Feb 03 2014 Dan Horák - 2:1.23.0-8 +- znetconf,lsqeth: Allow for 16-char network interface name (#1060303) + +* Wed Jan 29 2014 Dan Horák - 2:1.23.0-7 +- zipl: Fix zfcpdump "struct job_ipl_data" initialization (#1058856) + +* Wed Jan 15 2014 Dan Horák - 2:1.23.0-6 +- zipl: fix segmentation fault in automenu array (#1017541) +- zfcpconf.sh: check current online state before setting zfcp device online (#1042496) + +* Tue Nov 19 2013 Dan Horák - 2:1.23.0-5 +- dbginfo.sh: enhancements for script execution and man page (#1031144) +- dbginfo.sh: avoid double data collection (#1032068) + +* Wed Nov 06 2013 Dan Horák - 2:1.23.0-4 +- build daemons hardened (#881250) +- zipl: Use "possible_cpus" kernel parameter (#1016180) + +* Wed Aug 21 2013 Dan Horák - 2:1.23.0-3 +- dbginfo.sh: Avoiding exclusion list for pipes in sysfs (#996732) +- zipl: Fix zipl "--force" option for DASD multi-volume dump (#997361) + +* Sun Aug 04 2013 Fedora Release Engineering - 2:1.23.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Wed Jul 24 2013 Dan Horák - 2:1.23.0-1 +- rebased to 1.23 (#804774) + +* Wed Jun 05 2013 Dan Horák - 2:1.20.0-5 +- update with patches from RHEL-6 +- rebase zIPL to 1.21 to fix booting from FBA DASD (#970859) + +* Tue May 21 2013 Dan Horák - 2:1.20.0-4 +- drop the libzfcphbaapi subpackage as it is moved to its own package (#963670) +- update the zfcp udev rules (#958197) +- fix runtime dependencies for osasnmpd (#965413) + +* Wed Mar 27 2013 Dan Horák - 2:1.20.0-3 +- disable libzfcphbaapi subpackage, fails to build with recent kernels + +* Thu Feb 14 2013 Fedora Release Engineering - 2:1.20.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Dec 19 2012 Dan Horák - 2:1.20.0-1 +- updated to 1.20.0 (#804774) + +* Thu Nov 22 2012 Dan Horák - 2:1.19.0-4 +- clean BuildRequires a bit + +* Mon Sep 17 2012 Dan Horák - 2:1.19.0-3 +- zipl: Flush disk buffers before installing IPL record (#857814) + +* Mon Aug 27 2012 Dan Horák 2:1.19.0-2 +- add support for CEX4 devices to chzcrypt/lszcrypt (#847092) + +* Mon Aug 27 2012 Dan Horák 2:1.19.0-1 +- updated to 1.19.0 (#804774) +- fixed syntax in s390.sh script (#851096) +- spec cleanup + +* Tue Aug 21 2012 Dan Horák 2:1.17.0-1 +- updated to 1.17.0 +- add support for new storage device on System z (#847086) + +* Thu Aug 16 2012 Dan Horák 2:1.16.0-11 +- fix libzfcphbaapi for recent kernels + +* Sat Jul 21 2012 Fedora Release Engineering - 2:1.16.0-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Fri May 25 2012 Dan Horák 2:1.16.0-9 +- improve DASD parameters handling in normalize_dasd_arg (#824807) + +* Wed May 23 2012 Dan Horák 2:1.16.0-8 +- add normalize_dasd_arg script (#823078) + +* Mon May 14 2012 Dan Horák 2:1.16.0-7 +- ethtool is required by lsqeth (#821421) + +* Fri May 11 2012 Dan Horák 2:1.16.0-6 +- updated the Fedora patch set - no vol_id tool in udev (#819530) + +* Fri May 4 2012 Dan Horák 2:1.16.0-5 +- zipl.conf must be owned by s390utils-base (#818877) + +* Tue Apr 17 2012 Dan Horák 2:1.16.0-4 +- install the z90crypt udev rule (moved here from the udev package) + +* Tue Apr 10 2012 Dan Horák 2:1.16.0-3 +- include fixed ccw_init and updated device_cio_free + +* Sat Jan 14 2012 Fedora Release Engineering - 2:1.16.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Dec 15 2011 Dan Horák 2:1.16.0-1 +- rebased to 1.16.0 + +* Tue Aug 16 2011 Dan Horák 2:1.14.0-1 +- rebased to 1.14.0 + +* Wed Apr 27 2011 Dan Horák 2:1.8.2-32 +- updated ccw udev rules +- converted cio_free_device from an upstart job to systemd unit (jstodola) +- mon_statd: switch to using udevadm settle (#688140) +- cpuplugd: Fix incorrect multiplication in rules evaluation (#693365) +- cmsfs-fuse: Delete old file if renaming to an existing file (#690505) +- cmsfs-fuse: Enlarge fsname string (#690506) +- cmsfs-fuse: Unable to use cmsfs-fuse if $HOME is not set (#690514) +- hyptop: Prevent interactive mode on s390 line mode terminals (#690810) + +* Fri Mar 18 2011 Dan Horák 2:1.8.2-31 +- mon_statd: switch to using udevadm settle (#688140) +- hyptop: Fix man page typo for "current weight" (#684244) +- fdasd: buffer overflow when writing to read-only device (#688340) +- cmsfs-fuse: fix read and write errors in text mode (#680465) +- cmsfs-fuse needs fuse (#631546) +- dumpconf: Add DELAY_MINUTES description to man page (#676706) +- iucvterm scriptlet need shadow-utils (#677247) +- use lower-case in udev rules (#597360) +- add support for the 1731/02 OSM/OSX network device (#636849) +- xcec-bridge: fix multicast forwarding (#619504) +- ziomon: wrong return codes (#623250) +- qethconf: process devices with non-zero subchannel (#627692) +- wait for completion of any pending actions affecting device (#631527) +- add infrastructure code for new features (#631541) +- hyptop: Show hypervisor performance data on System z (#631541) +- cmsfs-fuse: support for CMS EDF filesystems via fuse (#631546) +- lsmem/chmem: Tools to manage memory hotplug (#631561) +- dumpconf: Prevent re-IPL loop for dump on panic (#633411) +- ttyrun: run a program if a terminal device is available (#633420) +- zgetdump/zipl: Add ELF dump support (needed for makedumpfile) (#633437) +- znetconf: support for OSA CHPID types OSX and OSM (#633534) +- iucvtty: do not specify z/VM user ID as argument to login -h (#636204) +- tunedasd: add new option -Q / --query_reserve (#644935) +- fdasd/dasdfmt: fix format 7 label (#649787) +- cpuplugd: cmm_pages not set and restored correctly (#658517) +- lsluns: Fix LUN reporting for SAN volume controller (SVC) (#659828) +- lsluns: Accept uppercase and lowercase hex digits (#660361) +- cmsfs: use detected filesystem block size (#651012) +- device_cio_free: use the /proc/cio_settle interface when waiting for devices +- libzfcphbaapi library needs kernel-devel during build and thus is limited to s390x +- libzfcphbaapi library rebased to 2.1 (#633414) +- new zfcp tools added (#633409) + +* Wed Feb 09 2011 Fedora Release Engineering - 2:1.8.2-30 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jul 13 2010 Dan Horák 2:1.8.2-29 +- lsluns: uninitialized value on adapter offline (#611795) +- zfcpdbf: Fix 'Use of uninitialized value' and output issues (#612622) + +* Wed Jul 7 2010 Dan Horák 2:1.8.2-28 +- fix linking with --no-add-needed + +* Tue Jun 29 2010 Dan Horák 2:1.8.2-27 +- make znet_cio_free work also when no interface config files exists (#609073) +- fix --dates option in zfcpdbf (#609092) + +* Mon Jun 28 2010 Dan Horák 2:1.8.2-26 +- follow symlinks in ziorep (#598574) +- do not restrict group names to be alphanumeric in ts-shell (#598641) +- znetconf --drive|-d option returning 'unknown driver' for qeth (#601846) +- fix stack overwrite in cpuplugd (#601847) +- fix cmm_min/max limit checks in cpuplugd (#606366) +- set cpu_min to 1 by default in cpuplugd (#606416) +- build with -fno-strict-aliasing (#599396) +- remove reference to z/VM from the cpi initscript (#601753) +- fix return values for the mon_statd initscript (#606805) +- ignore backup and similar config files in device_cio_free (#533494) + +* Fri May 28 2010 Dan Horák 2:1.8.2-25 +- fixed device_cio_free command line handling (#595569) + +* Thu May 20 2010 Dan Horák 2:1.8.2-24 +- added a check for the length of the parameters line (#594031) + +* Wed May 19 2010 Dan Horák 2:1.8.2-23 +- make ccw_init compatible with posix shell (#546615) + +* Wed May 5 2010 Dan Horák 2:1.8.2-22 +- scripts can't depend on stuff from /usr (#587364) + +* Mon May 3 2010 Dan Horák 2:1.8.2-21 +- updated patch for the "reinitialize array in lsqeth" issue (#587757) + +* Fri Apr 30 2010 Dan Horák 2:1.8.2-20 +- updated lsdasd man page (#587044) +- reinitialize array in lsqeth (#587599) + +* Wed Apr 28 2010 Dan Horák 2:1.8.2-19 +- fixed mismatch between man and -h in chshut (#563625) +- use the merged ccw_init script (#533494, #561814) + +* Thu Apr 22 2010 Dan Horák 2:1.8.2-18 +- lsluns utility from the base subpackage requires sg3_utils + +* Wed Apr 21 2010 Dan Horák 2:1.8.2-17 +- updated device_cio_free script (#576015) + +* Wed Mar 31 2010 Dan Horák 2:1.8.2-16 +- updated device_cio_free upstart config file (#578260) +- fix multipathing in ziomon (#577318) + +* Mon Mar 29 2010 Dan Horák 2:1.8.2-15 +- remove check for ziorep_config availability (#576579) +- install upstart event file into /etc/init (#561339) +- device_cio_free updates + - don't use basename/dirname + - correctly parse /etc/ccw.conf (#533494) + +* Mon Mar 22 2010 Dan Horák 2:1.8.2-14 +- don't use memory cgroups in zfcpdump kernel (#575183) +- fix df usage in ziomon (#575833) + +* Thu Mar 11 2010 Dan Horák 2:1.8.2-13 +- dropped dependency on redhat-lsb (#542702) + +* Wed Mar 10 2010 Dan Horák 2:1.8.2-12 +- run device_cio_free on startup (#561339) +- use hex index for chpidtype table in znetconf (#561056) +- handle status during IPL SSCH (#559250) +- don't show garbage in vmconvert's progress bar (#567681) +- don't print enviroment when there are no devices to wait for (#570763) +- fix zfcp dump partition error (#572313) +- switched to new initscripts for cpuplugd and fsstatd/procd (#524218, #524477) + +* Tue Feb 16 2010 Dan Horák 2:1.8.2-11 +- moved ccw udev stuff from initscripts to s390utils +- updated ccw_init with delay loops and layer2 handling (#561926) + +* Fri Jan 22 2010 Dan Horák 2:1.8.2-10.1 +- really update zfcpconf.sh script from dracut + +* Wed Jan 20 2010 Dan Horák 2:1.8.2-10 +- fixed return codes in ziorep (#556849) +- fixed return code in lstape (#556910) +- fixed reading the size of /proc/sys/vm/cmm_pages in cpuplugd (#556911) +- support new attributes in lsqeth (#556915) + +* Wed Jan 13 2010 Dan Horák 2:1.8.2-9 +- updated device_cio_free script (#533494) +- fixed uppercase conversion in lscss (#554768) + +* Fri Jan 8 2010 Dan Horák 2:1.8.2-8 +- updated device_cio_free script (#533494) + +* Fri Jan 8 2010 Dan Horák 2:1.8.2-7 +- updated device_cio_free script (#533494) + +* Tue Dec 22 2009 Dan Horák 2:1.8.2-6.1 +- fixed return value in cpi initscript (#541389) + +* Tue Dec 22 2009 Dan Horák 2:1.8.2-6 +- fixed return value in cpi initscript (#541389) +- updated zfcpconf.sh script from dracut +- added device-mapper support into zipl (#546280) +- added missing check and print NSS name in case an NSS has been IPLed (#546297) +- added device_cio_free script and its symlinks (#533494) +- added qualified return codes and further error handling in znetconf (#548487) + +* Fri Nov 13 2009 Dan Horák 2:1.8.2-5 +- added multiple fixes from IBM (#533955, #537142, #537144) + +* Thu Nov 12 2009 Dan Horák 2:1.8.2-4 +- added udev rules and script for dasd initialization (#536966) +- added ghosted zfcp and dasd config files, fixes their ownership on the system +- fixed upgrade path for libzfcphbaapi-devel subpackage + +* Mon Nov 9 2009 Dan Horák 2:1.8.2-3 +- added files for the CPI feature (#463282) +- built lib-zfcp-hbaabi library as vendor lib, switched from -devel (no devel content now) to -docs subpackage (#532707) + +* Fri Oct 30 2009 Dan Horák 2:1.8.2-2 +- install dasd udev rules provided by the s390-tools +- added patch for setting readahead value + +* Thu Oct 8 2009 Dan Horák 2:1.8.2-1 +- added patch for improving mon_statd behaviour +- rebased to 1.8.2 + +* Fri Oct 2 2009 Dan Horák 2:1.8.1-8 +- really changed ramdisk load address (#526339) +- change the required and optional subpackages for the meta package + +* Wed Sep 30 2009 Dan Horák 2:1.8.1-7 +- changed ramdisk load address (#526339) +- updated zfcpconf.sh script to new sysfs interface (#526324) +- added 1.8.1 fixes from IBM (#525495) + +* Fri Sep 25 2009 Dan Horák 2:1.8.1-6 +- fix issues in lib-zfcp-hbaapi with a patch + +* Thu Sep 24 2009 Dan Horák 2:1.8.1-5 +- drop support for Fedora < 10 + +* Thu Sep 24 2009 Dan Horák 2:1.8.1-4 +- fixed string overflow in vtoc_volume_label_init (#525318) + +* Thu Sep 3 2009 Dan Horák 2:1.8.1-3 +- create devel subpackage with some useful headers +- preserving timestamps on installed files + +* Wed Aug 26 2009 Dan Horák 2:1.8.1-2 +- Fix byte check for disk encryption check in lsluns (#510032) +- Fix cmm configuration file value initialization parser in cpuplugd (#511379) +- Check only ZFCP devices in lszfcp (#518669) + +* Mon Jun 29 2009 Dan Horák 2:1.8.1-1 +- update to 1.8.1 +- drop upstreamed patches +- create iucvterm subpackage +- update src_vipa locations patch +- install cmsfs tools into /sbin +- add post 1.8.1 fixes from IBM + +* Fri Apr 17 2009 Dan Horák 2:1.8.0-6 +- fix build with newer kernels + +* Wed Mar 25 2009 Dan Horák 2:1.8.0-5 +- reword the summaries a bit +- add downloadable URLs for Sources +- fix CFLAGS usage + +* Fri Mar 13 2009 Dan Horák 2:1.8.0-4 +- next round of clean-up for compliance with Fedora + +* Sun Mar 8 2009 Dan Horák 2:1.8.0-3 +- little clean-up for compliance with Fedora + +* Fri Dec 12 2008 Hans-Joachim Picht 2:1.8.0-2 +- Adapted package for F9 + +* Tue Dec 9 2008 Michael Holzheu 2:1.8.0-1 +- Changed spec file to create sub packages +- Updated to zfcphbaapi version 2.0 + +* Tue Oct 28 2008 Dan Horák 2:1.7.0-4 +- disable build-id feature in zipl (#468017) + +* Wed Sep 24 2008 Dan Horák 2:1.7.0-3 +- drop the mon_tools patch (mon_statd service starts both mon_procd and mon_fsstatd since 1.7.0) + +* Thu Aug 28 2008 Dan Horák 2:1.7.0-2 +- preserve timestamps on installed files +- add proper handling of initscripts +- fix permissions for some files + +* Tue Aug 12 2008 Dan Horák 2:1.7.0-1 +- update to s390-tools 1.7.0, src_vipa 2.0.4 and cmsfs 1.1.8c +- rebase or drop RHEL5 patches + +* Fri Jul 25 2008 Dan Horák 2:1.5.3-19.el5 +- fix use "vmconvert" directly on the vmur device node (#439389) +- fix the Linux Raid partition type is not retained when changed through fdasd (#445271) +- include missing files into the package (#442584) +- Resolves: #439389, #445271, #442584 + +* Fri Jul 25 2008 Dan Horák 2:1.5.3-18.el5 +- split the warnings patch into s390-tools and cmsfs parts +- mismatch between installed /etc/zfcp.conf and zfcpconf.sh expected format (#236016) +- dbginfo.sh exits before running all tests and drops errors (#243299) +- updates for cleanup SCSI dumper code for upstream integration - tool (#253118) +- fix segfault when using LD_PRELOAD=/usr/lib64/src_vipa.so (#282751) +- add support for timeout parameter in /etc/zipl.conf (#323651) +- fixes not listing all the dasds passed as arguments to lsdasd command (#369891) +- fix for zipl fail when section is specified and target is not repeated for all sections (#381201) +- fix for dasdview -s option fails to ignore the garbage value passed (#412951) +- update documentation for zfcpdump (#437477) +- update documentation for lsqeth (#455908) +- Resolves: #236016, #243299, #253118, #282751, #323651, #369891, #381201, #412951, #437477, #455908 + +* Fri Mar 28 2008 Phil Knirsch 2:1.5.3-17.el5 +- Fix error messages and proc/0 entry are not handled correctly (#438819) + +* Wed Feb 06 2008 Phil Knirsch 2:1.5.3-16.el5 +- Fixed a build problem with the mon_tools patch (#253029) + +* Mon Feb 04 2008 Phil Knirsch 2:1.5.3-14.el5 +- Added zfcpdump kernel symlink to dumpconf init script (#430550) + +* Fri Jan 18 2008 Phil Knirsch 2:1.5.3-13.el5 +- Fix tape390_crypt query shows wrong msg 'Kernel does not support tape encryption' (#269181) + +* Wed Jan 16 2008 Phil Knirsch 2:1.5.3-12.el5 +- Add System z guest file system size in Monitor APPLDATA (#253029) +- Add Dynamic CHPID reconfiguration via SCLP - tools (#253076) +- Add z/VM unit-record device driver - tools (#253078) +- Cleanup SCSI dumper code for upstream integration - tool (#253118) + +* Tue Jan 08 2008 Phil Knirsch 2:1.5.3-11.el5 +- Fix installer LVM partitions that show up as "unknown" in fdasd (#250176) +- Fixed zfcpconf.sh failure if / and /usr are separated (#279201) + +* Mon Sep 24 2007 Phil Knirsch 2:1.5.3-10.el5.14 +- Added missing openssl-devel buildrequires (#281361) + +* Thu Aug 23 2007 Phil Knirsch 2:1.5.3-10.el5.13 +- Last updage for -t parameter patch (#202086) + +* Tue Aug 14 2007 Phil Knirsch 2:1.5.3-10.el5.12 +- Fix handling of external timer interrupts (#250352) + +* Tue Jul 31 2007 Phil Knirsch 2:1.5.3-10.el5.11 +- Update fix for -t parameter for image operations (#202086) + +* Fri Jul 27 2007 Phil Knirsch 2:1.5.3-10.el5.10 +- Fixed udev regression from RHEL 4 with /dev/dasd/ (#208189) +- Fixed missing -d option for zgetdump (#228094) + +* Thu Jun 28 2007 Phil Knirsch 2:1.5.3-10.el5.9 +- Fix optional -t parameter for image operations (#202086) + +* Wed Jun 27 2007 Phil Knirsch 2:1.5.3-10.el5.8 +- Fix wrong manpage (#202250) +- Fix zfcp devices not showing up after boot (#223569) +- Fix help menu of lsqeth showing wrong file (#225159) +- Add tape encryption userspace tool (#228080) +- Add dump on panic initscript and sysconf (#228094) +- Fix a off-by-one error in zfcpdbf (#230527) +- Fix zipl aborting with floating point exception if the target specified is a logical volume (#231240) +- Fix boot menu use wrong conversion table for input on LPAR (#240399) + +* Mon Jan 22 2007 Phil Knirsch 2:1.5.3-10.el5.6 +- Fixed problem with invisible zfcp devices after boot (#223569) + +* Mon Jan 15 2007 Phil Knirsch 2:1.5.3-10.el5.5 +- Extended fix for automenu bug (#202086) + +* Thu Jan 11 2007 Phil Knirsch 2:1.5.3-10.el5.4 +- Updated dbginfo.sh patch to final fix from IBM (#214805) + +* Wed Nov 29 2006 Phil Knirsch 2:1.5.3-10.el5.3 +- Fixed problem with missing debugfs for dbginfo.sh (#214805) + +* Thu Nov 09 2006 Phil Knirsch 2:1.5.3-10.el5.2 +- Fixed lszfcp bug related to sysfsutils (#210515) + +* Tue Nov 07 2006 Phil Knirsch 2:1.5.3-10.el5.1 +- Removed wrong additional $ in src_vipa.sh (#213395) +- Release and Buildroot specfile fixes + +* Wed Sep 13 2006 Phil Knirsch 2:1.5.3-10 +- Needed to bump release + +* Tue Sep 12 2006 Phil Knirsch 2:1.5.3-9 +- Added libsysfs requirement (#201863) +- Fixed zipl problem with missing default target for automenus (#202086) + +* Thu Aug 10 2006 Phil Knirsch 2:1.5.3-8 +- Added missing sysfsutils requirement for lszfcp (#201863) + +* Tue Jul 25 2006 Phil Knirsch 2:1.5.3-7 +- Included zfcpdbf, dbginfo.sh and the man1 manpages to package (#184812) + +* Tue Jul 18 2006 Phil Knirsch 2:1.5.3-6 +- Disabled sysfs support due to API changes in sysfs-2.0.0 + +* Fri Jul 14 2006 Karsten Hopp 2:1.5.3-5 +- buildrequire net-snmp-devel + +* Fri Jul 14 2006 Jesse Keating - 2:1.5.3-4 +- rebuild +- Add missing br libsysfs-devel, indent, zlib-devel + +* Wed May 17 2006 Phil Knirsch 2:1.5.3-1 +- Made src_vipa build on current toolchain again + +* Tue May 16 2006 Phil Knirsch +- Update to s390-tools-1.5.3 from IBM +- Included vmconvert +- Dropped obsolete asm patch + +* Tue Feb 07 2006 Jesse Keating - 2:1.5.0-2.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Mon Jan 30 2006 Phil Knirsch 2:1.5.0-2 +- Fixed problem with s390-tools-1.5.0-fdasd-raid.patch +- Don't try to remove the non empty _bindir +- Some more install cleanups + +* Thu Jan 26 2006 Phil Knirsch +- Fixed some .macro errors in zipl/boot + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Thu Oct 20 2005 Phil Knirsch 2:1.5.0-1 +- Large update from 1.3.2 to 1.5.0 +- Include osasnmpd and vmcp now by default + +* Tue Sep 06 2005 Phil Knirsch 2:1.3.2-7 +- Fixed a couple of code bugs (#143808) + +* Fri Jul 29 2005 Phil Knirsch 2:1.3.2-6 +- Corrected filelist for libdir to only include *.so files + +* Tue Jun 21 2005 Phil Knirsch 2:1.3.2-5 +- Added src_vipa to s390utils + +* Wed Mar 02 2005 Phil Knirsch 2:1.3.2-4 +- bump release and rebuild with gcc 4 + +* Tue Oct 26 2004 Phil Knirsch 2:1.3.2-3 +- Put binaries for system recovery in /sbin again. + +* Fri Oct 15 2004 Phil Knirsch 2:1.3.2-1 +- Update to s390-tools-1.3.2 +- Added qetharp, qethconf, ip_watcher, tunedasd and various other tools to + improve functionality on s390(x). + +* Wed Oct 06 2004 Phil Knirsch 2:1.3.1-7 +- Made the raid patch less verbose (#129656) + +* Thu Sep 16 2004 Phil Knirsch 2:1.3.1-6 +- Added prompt=1 and timeout=15 to automatically generated menu + +* Tue Aug 31 2004 Karsten Hopp 2:1.3.1-5 +- install zfcpconf.sh into /sbin + +* Tue Aug 24 2004 Karsten Hopp 2:1.3.1-4 +- add zfcpconf.sh to read /etc/zfcp.conf and configure the zfcp + devices + +* Thu Jun 24 2004 Phil Knirsch 2:1.3.1-3 +- Fixed another automenu bug with dumpto and dumptofs (#113204). + +* Thu Jun 17 2004 Phil Knirsch 2:1.3.1-2 +- Fixed automenu patch. +- Fixed problem with installation from tape (#121788). + +* Wed Jun 16 2004 Phil Knirsch 2:1.3.1-1 +- Updated to latest upstream version s390-tools-1.3.1 + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 07 2004 Karsten Hopp +- add cmsfs utils + +* Tue Mar 02 2004 Elliot Lee +- rebuilt + +* Thu Feb 19 2004 Phil Knirsch 2:1.2.4-4 +- Fixed rebuilt on fc2. + +* Thu Feb 19 2004 Phil Knirsch 2:1.2.4-3 +- Fixed automenu patch, was allocating 1 line to little. + +* Mon Feb 16 2004 Phil Knirsch 2:1.2.4-2 +- rebuilt + +* Mon Feb 16 2004 Phil Knirsch 2:1.2.4-1 +- Updated to latest developerworks release 1.2.4 +- Disabled zfcpdump build until i find a way to build it as none-root. + +* Fri Feb 13 2004 Elliot Lee 2:1.2.3-3 +- rebuilt + +* Thu Dec 04 2003 Phil Knirsch 2:1.2.3-2 +- Fixed zfcpdump build. + +* Fri Nov 28 2003 Phil Knirsch 2:1.2.3-1 +- New bugfix release 1.2.3 came out today, updated again. + +* Wed Nov 26 2003 Phil Knirsch 2:1.2.2-1 +- Updated to latest Developerworks version 1.2.2 +- Cleaned up specfile and patches a little. + +* Wed Nov 12 2003 Phil Knirsch 2:1.2.1-4.1 +- rebuilt + +* Wed Nov 12 2003 Phil Knirsch 2:1.2.1-4 +- Another fix for the new automenu patch. Target was an optional parameter in + old s390utils, provided compatibility behaviour. + +* Mon Oct 20 2003 Phil Knirsch 2:1.2.1-3.1 +- rebuilt + +* Mon Oct 20 2003 Phil Knirsch 2:1.2.1-3 +- Small fix for the new automenu patch, default section didn't work correctly + +* Mon Oct 20 2003 Phil Knirsch 2:1.2.1-2.1 +- rebuilt + +* Fri Oct 17 2003 Phil Knirsch 2:1.2.1-2 +- Patched new zipl to be backwards compatible to old multiboot feature. + +* Thu Oct 9 2003 Harald Hoyer 2:1.2.1-1 +- second round at updating to 1.2.1 + +* Thu Oct 09 2003 Florian La Roche +- first round at updating to 1.2.1 + +* Sat Sep 27 2003 Florian La Roche +- add /boot/tape0 for .tdf tape boots + +* Fri Jul 25 2003 Florian La Roche +- apply dasdfmt patch from 1.2.1 + +* Fri Jun 20 2003 Phil Knirsch 1.1.7-1 +- Updated to latest upstream version 1.1.7 + +* Fri May 02 2003 Pete Zaitcev 1.1.6-7 +- Fix usage of initialized permissions for bootmap. + +* Tue Apr 29 2003 Florian La Roche +- add extra tape loader from Pete Zaitcev + +* Mon Apr 14 2003 Karsten Hopp 2:1.1.6-5 +- drop cpint support + +* Mon Mar 24 2003 Karsten Hopp 1.1.6-4 +- use multiboot as default +- add option to disable multiboot + +* Sat Mar 22 2003 Karsten Hopp 1.1.6-3 +- add multiboot patch + +* Mon Mar 10 2003 Karsten Hopp 1.1.6-2 +- added percentage patch (used by anaconda to display progress bars) + +* Thu Feb 27 2003 Phil Knirsch 1.1.6-1 +- Updated to newest upstream version 1.1.6 + +* Tue Feb 04 2003 Phil Knirsch 1.1.5-1 +- Updated to newest upstream version 1.1.5 + +* Tue Feb 04 2003 Karsten Hopp 1.1.4-3 +- install libraries in /lib*, not /usr/lib*, they are required + by some tools in /sbin + +* Sun Feb 02 2003 Florian La Roche +- fix filelist to not include debug files + +* Fri Jan 24 2003 Phil Knirsch 1.1.4-1 +- Updated to latest upstream version of IBM. +- Removed all unecessary patches and updated still needed patches. +- Fixed version number. Needed to introduce epoch though. +- A little specfile cleanup. +- Dropped oco-setver and oco-convert as we don't need them anymore. + +* Wed Jan 22 2003 Phil Knirsch 20020226-4 +- Added ExclusiveArch tag. + +* Mon Oct 21 2002 Phil Knirsch 20020226-3 +- Removed fdisk -> fdasd symlink. Is now provided by util-linux. +- Disabled f5 patch for s390x for now. Enable it later for newer kernels again. + +* Mon May 27 2002 Phil Knirsch +- Fixed dasdview to build on kernels > 2.4.18. + +* Wed Apr 24 2002 Karsten Hopp +- add IBM 5 patch + +* Tue Jan 29 2002 Karsten Hopp +- add IBM 4 patch +- add profile.d scripts to set correct TERM in 3270 console + +* Tue Dec 18 2001 Karsten Hopp +- add cpint programs + +* Mon Nov 26 2001 Harald Hoyer 20011012-6 +- fix for #56720 + +* Thu Nov 15 2001 Karsten Hopp +- add fdisk - > fdasd symlink + +* Mon Nov 12 2001 Karsten Hopp +- add IBM patch (11/09/2001) and redo percentage patch + +* Thu Nov 08 2001 Karsten Hopp +- re-enable DASD if dasdfmt is interrupted with Ctrl-C + +* Mon Nov 05 2001 Harald Hoyer 20011012-4 +- added s390-tools-dasdfmt-percentage.patch + +* Mon Oct 22 2001 Karsten Hopp +- remove postinstall script + +* Mon Oct 15 2001 Karsten Hopp +- add IBM's s390-utils-2.patch +- add console to securetty + +* Mon Oct 01 2001 Karsten Hopp +- added oco-setkver and oco-convert + +* Fri Aug 31 2001 Karsten Hopp +- don't write error message in silent mode + +* Thu Aug 23 2001 Harald Hoyer +- added s390-tools-dasdfmt-status.patch + +* Tue Aug 21 2001 Karsten Hopp +- update to the version from Aug 20 + +* Tue Aug 14 2001 Karsten Hopp +- fix permissions + +* Mon Aug 13 2001 Karsten Hopp +- rename package to s390utils. s390-tools is no longer needed. + +* Thu Aug 02 2001 Karsten Hopp +- initial build