s390utils/SOURCES/s390-tools-rhel.patch

8407 lines
233 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From ab2402c95f031ad97544a86e4418cb765313eee8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:07:35 +0200
Subject: [PATCH 01/21] 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 <dan@danny.cz>
---
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.0
From ed7cf76fd149a9fed3ce9f728e072bccc44997cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:13:38 +0200
Subject: [PATCH 02/21] 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 <dan@danny.cz>
---
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.0
From 3a354d9d8e83a36edb9ce68fb85b3cc4afd19991 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:17:07 +0200
Subject: [PATCH 03/21] 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 <dan@danny.cz>
---
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 <libcryptsetup.h>" > 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.0
From 913721b06be3b4662593c3f1a344256c46ce841a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 04:29:39 -0400
Subject: [PATCH 04/21] 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 <dan@danny.cz>
---
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.0
From 6a3da6ec34a0947bd14cd0a36ab08b607236a414 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Wed, 17 Oct 2018 13:52:48 +0200
Subject: [PATCH 05/21] 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 <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
---
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.0
From e7b59a9f7cc5d049ef68331855a881beb85a1347 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 25 Oct 2018 12:57:29 +0200
Subject: [PATCH 06/21] 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 <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
---
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 <libcryptsetup.h>" > 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 <libcryptsetup.h>" > 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.0
From dc92c4ce7963dcf5018c3ef3672b6d217f29842f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:26:40 +0100
Subject: [PATCH 07/21] 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 <long> [<short>] [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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <time.h>
+
+#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 <linux/types.h>
+#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.0
From c17b203ee85a63c812654e937f0c5cb3da6229e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:35:09 +0100
Subject: [PATCH 08/21] 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.0
From 8d8e0a970dbde0ca4486192aed806d837f080370 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:36:36 +0100
Subject: [PATCH 09/21] 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.0
From 916022e17d4cb189c0d83028ed09d3f4600046dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:37:34 +0100
Subject: [PATCH 10/21] 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.0
From e2916bdb493e9565d74b0334fa45f17c6f0f730c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:38:30 +0100
Subject: [PATCH 11/21] 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.0
From ba7eb5cd9d858f2ff8bd29e0b610337896587b94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:39:35 +0100
Subject: [PATCH 12/21] 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=<node-name>"
+.IP "aps=<list of ap numbers separated by space, tab or ','>"
+.IP "doms=<list of domain numbers separated by space, tab or ','>"
+.IP "ioctls=<list of ioctl as numeric or symbolic number separated by space, tab or ','>"
+.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 <freude@linux.ibm.com>
+ * 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 <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#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_<x>\n"
+ "with <x> 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 <nodename>",
+ .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 <adapter>",
+ .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 <adapter>",
+ .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 <domain>",
+ .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 <adapter>",
+ .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 <ioctlexp>",
+ .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 <ioctlexp>",
+ .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 <configfile>",
+ .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=<node_name>\n"
+ " aps=<list of ap numbers separated by space, tab or ','>\n"
+ " doms=<list of domains separated by space, tab or ','>\n"
+ " ioctls=<list of ioctl as number or symbolic number\n"
+ " separated by space, tab or ','>\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.0
From d5aab30ab6dd79a47d821b39e9f803d4afe8ed7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:40:35 +0100
Subject: [PATCH 13/21] 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 <device-id>
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.0
From 47e15616b0c81f85918794a0af2073aac6455826 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 11 Dec 2018 09:46:40 +0100
Subject: [PATCH 14/21] 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.0
From 3328fadb3147a24bae10495259fa7fd00b973eec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 11 Dec 2018 10:14:05 +0100
Subject: [PATCH 15/21] 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.0
From ba7d612c055c887d5cbb596a369d32f00c47711d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 29 Jan 2019 13:06:21 +0100
Subject: [PATCH 16/21] 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/<machine-id>/<kernel-version>
+# 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 <original-name>-<kernel-version>.
+#
+# 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.0
From eef1d70b8bd0ec96aa90f5dad9795196c85382ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Wed, 9 Jan 2019 13:58:10 +0100
Subject: [PATCH 17/21] 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 <dan@danny.cz>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(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.0
From 7a3ba6fb39f015da0ee10e1dc0e14ae9fcf6347f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 21 May 2019 13:39:08 +0200
Subject: [PATCH 18/21] 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 <SWITCH>" " or " "\-\-secure <SWITCH>"
+Control the zIPL secure boot support.
+<SWITCH> 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#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, &params, sizeof(struct boot_stage3_params));
+ memcpy(data, &params, 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 <sys/types.h>
#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.0
From 100c89a273fb4bcc1deb93aaf77ac9264d9ac3ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 2 May 2019 15:46:39 +0200
Subject: [PATCH 19/21] 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 <dan@danny.cz>
---
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.0
From 78f0479055e025ba849766aa2d60c22a7bcdbfba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 21 May 2019 13:49:09 +0200
Subject: [PATCH 20/21] 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<card-id>\fP) or a queue device (APQN) ID (\fI<card-id>.<domain-id>\fP).
+To filter all devices by domain, provide \fI.<domain-id>\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 <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <signal.h>
+#include <time.h>
+#include <asm/chsc.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#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 "
+ "('<card-id>') or a queue device ID (<card-id>."
+ "<domain-id>'). To filter all devices by domain, "
+ "provide '.<domain-id>'. 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.0
From c0670b62ccd7a9f1f1b3a8a49114c2e3c673ecd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20H=C3=B6ppner?= <hoeppner@linux.ibm.com>
Date: Mon, 1 Apr 2019 09:53:06 +0200
Subject: [PATCH 21/21] 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 <hoeppner@linux.ibm.com>
(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.0