8407 lines
233 KiB
Diff
8407 lines
233 KiB
Diff
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, ¶ms, sizeof(struct boot_stage3_params));
|
||
+ memcpy(data, ¶ms, sizeof(params));
|
||
*buffer = data;
|
||
- *bytecount = DATA_SIZE(stage3);
|
||
+ *bytecount = sizeof(params);
|
||
return 0;
|
||
}
|
||
|
||
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
|
||
index 1b71b03..456c2ef 100644
|
||
--- a/zipl/src/bootmap.c
|
||
+++ b/zipl/src/bootmap.c
|
||
@@ -19,6 +19,7 @@
|
||
#include <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
|
||
|