diff --git a/.gitignore b/.gitignore index 9776127..f4293eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ SOURCES/cmsfs-1.1.8c.tar.gz -SOURCES/s390-tools-2.6.0.tar.gz +SOURCES/s390-tools-2.15.1.tar.gz SOURCES/src_vipa-2.1.0.tar.gz diff --git a/.s390utils.metadata b/.s390utils.metadata index cba2a76..73b210e 100644 --- a/.s390utils.metadata +++ b/.s390utils.metadata @@ -1,3 +1,3 @@ 9c9a4e89bddb2b4e6e09ef6fc7c2e6f2ad6316de SOURCES/cmsfs-1.1.8c.tar.gz -46a09493030c3c80987b7710e34a33462e4b90f4 SOURCES/s390-tools-2.6.0.tar.gz +9a169518bf1074f3c087f97a32fd85c35ac216ee SOURCES/s390-tools-2.15.1.tar.gz 8ed8592a0a9370ce8422df9231ccb17f6cf49bed SOURCES/src_vipa-2.1.0.tar.gz diff --git a/SOURCES/20-zipl-kernel.install b/SOURCES/20-zipl-kernel.install index 99bfcb9..3487aa2 100755 --- a/SOURCES/20-zipl-kernel.install +++ b/SOURCES/20-zipl-kernel.install @@ -132,8 +132,12 @@ case "$COMMAND" in fi fi - if [ -n "$NEWDEFAULT" ]; then - sed -i -e "s,^default=.*,default=${NEWDEFAULT}," "${ZIPLCFG}" + if [ -n "$NEWDEFAULT" ] && [ -f "${ZIPLCFG}" ]; then + if grep -q "^default=" "${ZIPLCFG}"; then + sed -i -e "s,^default=.*,default=${NEWDEFAULT}," "${ZIPLCFG}" + else + echo "default=${NEWDEFAULT}" >> "${ZIPLCFG}" + fi fi exit 0 @@ -151,8 +155,10 @@ case "$COMMAND" in BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf" BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" - TITLE="$(grep '^title[ \t]' "${BLS_TARGET}" | sed -e 's/^title[ \t]*//')" - sed -i -e "/^default=${TITLE}/d" "${ZIPLCFG}" + if [ -f "${BLS_TARGET}" ] && [ -f "${ZIPLCFG}" ]; then + TITLE="$(grep '^title[ \t]' "${BLS_TARGET}" | sed -e 's/^title[ \t]*//')" + sed -i -e "/^default=${TITLE}/d" "${ZIPLCFG}" + fi if [[ -f "${BLS_DEBUG}" ]]; then TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')" diff --git a/SOURCES/cmsfs-1.1.8-args.patch b/SOURCES/cmsfs-1.1.8-args.patch new file mode 100644 index 0000000..8aca1e4 --- /dev/null +++ b/SOURCES/cmsfs-1.1.8-args.patch @@ -0,0 +1,12 @@ +diff -up cmsfs-1.1.8c/cmsfslst.c.orig cmsfs-1.1.8c/cmsfslst.c +--- cmsfs-1.1.8c/cmsfslst.c.orig 2020-08-19 09:47:36.459063820 +0000 ++++ cmsfs-1.1.8c/cmsfslst.c 2020-08-19 09:47:45.619063820 +0000 +@@ -49,7 +49,7 @@ int main(int argc,unsigned char *argv[]) + } + + /* sanity check */ +- if (*devname == 0x00) ++ if ((devname == NULL) || (*devname == 0x00)) + { + (void) fprintf(stderr,"Please specify a CMS volume.\n"); + (void) fprintf(stderr,USAGE,argv[0]); diff --git a/SOURCES/s390-tools-rhel.patch b/SOURCES/s390-tools-rhel.patch deleted file mode 100644 index 972a0f5..0000000 --- a/SOURCES/s390-tools-rhel.patch +++ /dev/null @@ -1,41795 +0,0 @@ -From ab2402c95f031ad97544a86e4418cb765313eee8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 31 Aug 2018 10:07:35 +0200 -Subject: [PATCH 01/57] drop LOADLIBES variable -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Remove depreciated LOADLIBES variable from the Makefile rules, LDLIBS serves -the same purpose these days. - -Signed-off-by: Dan Horák ---- - common.mak | 2 +- - cpuplugd/Makefile | 2 +- - ipl_tools/Makefile | 2 +- - vmconvert/Makefile | 2 +- - vmur/Makefile | 2 +- - ziomon/Makefile | 10 +++++----- - 6 files changed, 10 insertions(+), 10 deletions(-) - -diff --git a/common.mak b/common.mak -index 571fb30..6f0990e 100644 ---- a/common.mak -+++ b/common.mak -@@ -239,7 +239,7 @@ endif - $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@ - - %: %.o -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - %.a: - $(AR) rcs $@ $^ -diff --git a/cpuplugd/Makefile b/cpuplugd/Makefile -index a9a49ab..916638d 100644 ---- a/cpuplugd/Makefile -+++ b/cpuplugd/Makefile -@@ -7,7 +7,7 @@ LDLIBS += -lm - OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o - - cpuplugd: $(OBJECTS) -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - clean: - rm -f cpuplugd $(OBJECTS) -diff --git a/ipl_tools/Makefile b/ipl_tools/Makefile -index 128ec3e..506d5cd 100644 ---- a/ipl_tools/Makefile -+++ b/ipl_tools/Makefile -@@ -6,7 +6,7 @@ objects = main.o ccw.o fcp.o system.o shutdown.o \ - cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o - - chreipl: $(objects) -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - lsreipl: - ln -sf chreipl lsreipl -diff --git a/vmconvert/Makefile b/vmconvert/Makefile -index 4d0216c..380eb19 100644 ---- a/vmconvert/Makefile -+++ b/vmconvert/Makefile -@@ -9,7 +9,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a - objects = vmconvert.o - - vmconvert: $(objects) $(libs) -- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install: all - $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(DESTDIR)$(MANDIR)/man1 -diff --git a/vmur/Makefile b/vmur/Makefile -index 1a6bddc..2c1c2d5 100644 ---- a/vmur/Makefile -+++ b/vmur/Makefile -@@ -11,7 +11,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a \ - objects = vmur.o - - vmur: $(objects) $(libs) -- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install: all - $(INSTALL) -d -m 755 $(DESTDIR)$(USRSBINDIR) $(DESTDIR)$(MANDIR)/man8 -diff --git a/ziomon/Makefile b/ziomon/Makefile -index 778401b..61c2399 100644 ---- a/ziomon/Makefile -+++ b/ziomon/Makefile -@@ -12,33 +12,33 @@ ziomon_mgr_main.o: ziomon_mgr.c - ziomon_mgr: LDLIBS += -lm - ziomon_mgr: ziomon_dacc.o ziomon_util.o ziomon_mgr_main.o ziomon_tools.o \ - ziomon_zfcpdd.o ziomon_msg_tools.o -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - ziomon_util_main.o: ziomon_util.c ziomon_util.h - $(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@ - ziomon_util: LDLIBS += -lm - ziomon_util: ziomon_util_main.o ziomon_tools.o -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - ziomon_zfcpdd_main.o: ziomon_zfcpdd.c ziomon_zfcpdd.h - $(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@ - ziomon_zfcpdd: LDLIBS += -lm -lrt -lpthread - ziomon_zfcpdd: ziomon_zfcpdd_main.o ziomon_tools.o -- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - ziorep_traffic: ziorep_traffic.o ziorep_framer.o ziorep_frameset.o \ - ziorep_printers.o ziomon_dacc.o ziomon_util.o \ - ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \ - ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \ - ziorep_filters.o -- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - ziorep_utilization: ziorep_utilization.o ziorep_framer.o ziorep_frameset.o \ - ziorep_printers.o ziomon_dacc.o ziomon_util.o \ - ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \ - ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \ - ziorep_filters.o -- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install: all - $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ --- -2.21.3 - - -From ed7cf76fd149a9fed3ce9f728e072bccc44997cb Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 31 Aug 2018 10:13:38 +0200 -Subject: [PATCH 02/57] zkey: Drop redundant include -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Dan Horák ---- - zkey/Makefile | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/zkey/Makefile b/zkey/Makefile -index 68f35cf..725cb3b 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -22,7 +22,6 @@ else - INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2 - endif - --CPPFLAGS += -I../include - LIBS = $(rootdir)/libutil/libutil.a - - detect-libcryptsetup.h: --- -2.21.3 - - -From 3a354d9d8e83a36edb9ce68fb85b3cc4afd19991 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 31 Aug 2018 10:17:07 +0200 -Subject: [PATCH 03/57] zkey: Be consistent when refering to libutil.a -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Dan Horák ---- - zkey/Makefile | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/zkey/Makefile b/zkey/Makefile -index 725cb3b..7e2047a 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -22,7 +22,7 @@ else - INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2 - endif - --LIBS = $(rootdir)/libutil/libutil.a -+libs = $(rootdir)/libutil/libutil.a - - detect-libcryptsetup.h: - echo "#include " > detect-libcryptsetup.h -@@ -69,10 +69,10 @@ keystore.o: keystore.c keystore.h properties.h - zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h - - zkey: LDLIBS = -ldl -lcrypto --zkey: zkey.o pkey.o properties.o keystore.o $(LIBS) -+zkey: zkey.o pkey.o properties.o keystore.o $(libs) - - zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c --zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(LIBS) -+zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) - - install-common: - $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) --- -2.21.3 - - -From 913721b06be3b4662593c3f1a344256c46ce841a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 31 Aug 2018 04:29:39 -0400 -Subject: [PATCH 04/57] zkey: Be explicit about linking the tools -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -I've met cases when the make's default rule for linking was used instead omitting -the ALL_LDFLAGS variable. The linking rule from common.mak is defined for linking -*.o files only, here we have libutil.a too. - -Signed-off-by: Dan Horák ---- - zkey/Makefile | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/zkey/Makefile b/zkey/Makefile -index 7e2047a..901ddd4 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -70,9 +70,11 @@ zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h - - zkey: LDLIBS = -ldl -lcrypto - zkey: zkey.o pkey.o properties.o keystore.o $(libs) -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c - zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) -+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install-common: - $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) --- -2.21.3 - - -From 6a3da6ec34a0947bd14cd0a36ab08b607236a414 Mon Sep 17 00:00:00 2001 -From: Ingo Franzki -Date: Wed, 17 Oct 2018 13:52:48 +0200 -Subject: [PATCH 05/57] zkey: Makefile: Avoid relink of modules during 'make - install' -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Because targets check-dep-zkey and check-dep-zkey-cryptsetup -do not produce any file, any targets that have a pre-req on those -targets are rebuilt during 'make install'. - -Also correct .PHONY targets. - -Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/46 -Signed-off-by: Ingo Franzki -Signed-off-by: Jan Höppner ---- - zkey/Makefile | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/zkey/Makefile b/zkey/Makefile -index 901ddd4..bc7bc33 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -37,6 +37,7 @@ check-dep-zkey: - "openssl/evp.h", \ - "openssl-devel", \ - "HAVE_OPENSSL=0") -+ touch check-dep-zkey - - check-dep-zkey-cryptsetup: detect-libcryptsetup.h - $(call check_dep, \ -@@ -50,6 +51,7 @@ check-dep-zkey-cryptsetup: detect-libcryptsetup.h - "json-c/json.h", \ - "json-c-devel", \ - "HAVE_JSONC=0") -+ touch check-dep-zkey-cryptsetup - - zkey-skip: - echo " SKIP zkey due to HAVE_OPENSSL=0" -@@ -93,6 +95,9 @@ install-zkey-cryptsetup: - install: all install-common $(INSTALL_TARGETS) - - clean: -- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h -+ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \ -+ check-dep-zkey check-dep-zkey-cryptsetup - --.PHONY: all install clean -+.PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \ -+ zkey-cryptsetup-skip-jsonc install-common install-zkey \ -+ install-zkey-cryptsetup --- -2.21.3 - - -From e7b59a9f7cc5d049ef68331855a881beb85a1347 Mon Sep 17 00:00:00 2001 -From: Ingo Franzki -Date: Thu, 25 Oct 2018 12:57:29 +0200 -Subject: [PATCH 06/57] zkey: Makefile: Don't rebuild .o.d files on 'make - install' -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The .o.d make targets in common.mak do not expect that -header files are generated by a make target. When a new header -file is generated, the .o.d targets will be rebuilt on the -next make invocation, because that new header file is then -detected, and is then treated as a new dependency of all .o.d -targets. - -Signed-off-by: Ingo Franzki -Signed-off-by: Jan Höppner ---- - zkey/Makefile | 18 +++++++++--------- - 1 file changed, 9 insertions(+), 9 deletions(-) - -diff --git a/zkey/Makefile b/zkey/Makefile -index bc7bc33..a44b14b 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -24,12 +24,12 @@ endif - - libs = $(rootdir)/libutil/libutil.a - --detect-libcryptsetup.h: -- echo "#include " > detect-libcryptsetup.h -- echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.h -- echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.h -- echo "#endif" >> detect-libcryptsetup.h -- echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.h -+detect-libcryptsetup.dep: -+ echo "#include " > detect-libcryptsetup.dep -+ echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.dep -+ echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.dep -+ echo "#endif" >> detect-libcryptsetup.dep -+ echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.dep - - check-dep-zkey: - $(call check_dep, \ -@@ -39,10 +39,10 @@ check-dep-zkey: - "HAVE_OPENSSL=0") - touch check-dep-zkey - --check-dep-zkey-cryptsetup: detect-libcryptsetup.h -+check-dep-zkey-cryptsetup: detect-libcryptsetup.dep - $(call check_dep, \ - "zkey-cryptsetup", \ -- "detect-libcryptsetup.h", \ -+ "detect-libcryptsetup.dep", \ - "cryptsetup-devel version 2.0.3", \ - "HAVE_CRYPTSETUP2=0", \ - "-I.") -@@ -95,7 +95,7 @@ install-zkey-cryptsetup: - install: all install-common $(INSTALL_TARGETS) - - clean: -- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \ -+ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.dep \ - check-dep-zkey check-dep-zkey-cryptsetup - - .PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \ --- -2.21.3 - - -From dc92c4ce7963dcf5018c3ef3672b6d217f29842f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:26:40 +0100 -Subject: [PATCH 07/57] zpcictl: Add tool to manage PCI devices (#1525409) - -Summary: zpcictl: Add tool to manage PCI devices -Description: Use the zpcictl tool to manage PCI devices on the IBM Z - platform. Initial functions include generating firmware - error logs, resetting PCI devices, and preparing a device - for further repair actions. ---- - .gitignore | 1 + - Makefile | 2 +- - zpcictl/Makefile | 18 +++ - zpcictl/zpcictl.8 | 80 ++++++++++ - zpcictl/zpcictl.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++ - zpcictl/zpcictl.h | 60 ++++++++ - 6 files changed, 539 insertions(+), 1 deletion(-) - create mode 100644 zpcictl/Makefile - create mode 100644 zpcictl/zpcictl.8 - create mode 100644 zpcictl/zpcictl.c - create mode 100644 zpcictl/zpcictl.h - -diff --git a/.gitignore b/.gitignore -index 41feaf6..042233f 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -89,3 +89,4 @@ zipl/src/chreipl_helper.device-mapper - zipl/src/zipl - zipl/src/zipl_helper.device-mapper - zkey/zkey -+zpcictl/zpcictl -diff --git a/Makefile b/Makefile -index adc92b6..bb2b900 100644 ---- a/Makefile -+++ b/Makefile -@@ -8,7 +8,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ - tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ - ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ -- systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc -+ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl - SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) - - all: $(TOOL_DIRS) -diff --git a/zpcictl/Makefile b/zpcictl/Makefile -new file mode 100644 -index 0000000..2b315c5 ---- /dev/null -+++ b/zpcictl/Makefile -@@ -0,0 +1,18 @@ -+include ../common.mak -+ -+all: zpcictl -+ -+libs = $(rootdir)/libutil/libutil.a -+ -+zpcictl: zpcictl.o $(libs) -+ -+install: all -+ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zpcictl $(DESTDIR)$(BINDIR) -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 zpcictl.8 \ -+ $(DESTDIR)$(MANDIR)/man8 -+ -+clean: -+ rm -f *.o *~ zpcictl core -+ -+.PHONY: all install clean -diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 -new file mode 100644 -index 0000000..4f8fcd8 ---- /dev/null -+++ b/zpcictl/zpcictl.8 -@@ -0,0 +1,80 @@ -+.\" Copyright 2017 IBM Corp. -+.\" s390-tools is free software; you can redistribute it and/or modify -+.\" it under the terms of the MIT license. See LICENSE for details. -+.\" -+.\" Macro for inserting an option description prologue. -+.\" .OD [] [args] -+.de OD -+. ds args " -+. if !'\\$3'' .as args \fI\\$3\fP -+. if !'\\$4'' .as args \\$4 -+. if !'\\$5'' .as args \fI\\$5\fP -+. if !'\\$6'' .as args \\$6 -+. if !'\\$7'' .as args \fI\\$7\fP -+. PD 0 -+. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4 -+. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4 -+. PD -+.. -+. -+.TH zpcictl 8 "Oct 2018" s390-tools zpcictl -+. -+.SH NAME -+zpcictl - Manage PCI devices on z Systems -+. -+. -+.SH SYNOPSIS -+.B "zpcictl" -+.I "OPTIONS" -+.I "DEVICE" -+. -+. -+.SH DESCRIPTION -+.B zpcictl -+is a tool for managing PCI devices on the IBM z Systems platform. It is -+especially used for reporting errorneous PCI devices to the service element. -+ -+.B Note: -+For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent -+with any error handling action. The smartmontools are required to be installed -+for this to work. -+.PP -+. -+. -+.SH DEVICE -+.B DEVICE -+can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node -+of an NVMe device (e.g. /dev/nvme0). -+. -+. -+.SH OPTIONS -+.SS Error Handling -+.OD reset "" "DEVICE" -+Reset -+.I DEVICE -+and initiate a re-initialisation of the adapter. -+.PP -+. -+.OD deconfigure "" "DEVICE" -+De-configure -+.I DEVICE -+and prepare for any repair action. This action will move the -+PCI device from a configured to a reserved state. -+.PP -+. -+.OD report-error "" "DEVICE" -+Report any device error for -+.IR DEVICE . -+The -+.I DEVICE -+is marked as erroneous and no further action is initiated on it. -+.PP -+. -+.SS Misc -+.OD help "h" "" -+Print usage information, then exit. -+.PP -+. -+.OD version "v" "" -+Print version information, then exit. -+.PP -diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c -new file mode 100644 -index 0000000..5f63b17 ---- /dev/null -+++ b/zpcictl/zpcictl.c -@@ -0,0 +1,379 @@ -+/* -+ * zpcictl - Manage PCI devices on z Systems -+ * -+ * Copyright IBM Corp. 2018 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_base.h" -+#include "lib/util_libc.h" -+#include "lib/util_opt.h" -+#include "lib/util_path.h" -+#include "lib/util_prg.h" -+#include "lib/util_proc.h" -+#include "lib/util_rec.h" -+#include "lib/util_scandir.h" -+ -+#include "zpcictl.h" -+ -+#define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" -+ -+static const struct util_prg prg = { -+ .desc = "Use zpcictl to manage PCI devices on s390\n" -+ "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", -+ .args = "DEVICE", -+ .copyright_vec = { -+ { -+ .owner = "IBM Corp.", -+ .pub_first = 2018, -+ .pub_last = 2018, -+ }, -+ UTIL_PRG_COPYRIGHT_END -+ } -+}; -+ -+/* Defines for options with no short command */ -+#define OPT_RESET 128 -+#define OPT_DECONF 129 -+#define OPT_REPORT_ERR 130 -+ -+static struct util_opt opt_vec[] = { -+ UTIL_OPT_SECTION("ERROR HANDLING"), -+ { -+ .option = { "reset", no_argument, NULL, OPT_RESET }, -+ .desc = "Reset device", -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, -+ .desc = "De-configure device and prepare for any repair action", -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, -+ .desc = "Report device error to service element (SE)", -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ UTIL_OPT_SECTION("MISC"), -+ UTIL_OPT_HELP, -+ UTIL_OPT_VERSION, -+ UTIL_OPT_END -+}; -+ -+static int is_char_dev(const char *dev) -+{ -+ struct stat s; -+ -+ if (stat(dev, &s)) -+ return 0; -+ -+ return S_ISCHR(s.st_mode); -+} -+ -+static int is_blk_dev(const char *dev) -+{ -+ struct stat s; -+ -+ if (stat(dev, &s)) -+ return 0; -+ -+ return S_ISBLK(s.st_mode); -+} -+ -+static void fopen_err(char *path) -+{ -+ warnx("Could not open file %s: %s", path, strerror(errno)); -+ free(path); -+ exit(EXIT_FAILURE); -+} -+ -+#define READ_CHUNK_SIZE 512 -+ -+static char *collect_smart_data(struct zpci_device *pdev) -+{ -+ char *buffer = NULL; -+ size_t count = 0; -+ char *cmd; -+ FILE *fd; -+ -+ util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); -+ fd = popen(cmd, "r"); -+ if (!fd) -+ goto out; -+ -+ while (!feof(fd)) { -+ buffer = realloc(buffer, count + READ_CHUNK_SIZE); -+ if (!buffer) { -+ warnx("Could not collect S.M.A.R.T. data"); -+ goto out; -+ } -+ count += fread(&buffer[count], 1, READ_CHUNK_SIZE, fd); -+ if (ferror(fd)) { -+ free(buffer); -+ buffer = NULL; -+ goto out; -+ } -+ } -+ -+ buffer = realloc(buffer, count); -+ if (!buffer && count > 0) -+ warnx("Could not collect S.M.A.R.T. data"); -+ if (buffer) -+ buffer[count] = '\0'; -+ -+out: -+ pclose(fd); -+ free(cmd); -+ -+ return buffer; -+} -+ -+static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) -+{ -+ unsigned int val; -+ char *path; -+ FILE *fp; -+ -+ path = util_path_sysfs("bus/pci/devices/%s/%s", pdev->slot, attr); -+ fp = fopen(path, "r"); -+ if (!fp) -+ fopen_err(path); -+ fscanf(fp, "%x", &val); -+ fclose(fp); -+ free(path); -+ -+ return val; -+} -+ -+static void sysfs_write_data(struct zpci_report_error *report, char *slot) -+{ -+ char *path; -+ int fd, rc; -+ -+ path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); -+ fd = open(path, O_WRONLY); -+ if (!fd) -+ fopen_err(path); -+ rc = write(fd, report, sizeof(*report)); -+ if (rc == -1) -+ warnx("Could not write to file: %s: %s", path, strerror(errno)); -+ if (close(fd)) -+ warnx("Could not close file: %s: %s", path, strerror(errno)); -+ free(path); -+} -+ -+static void sysfs_get_slot_addr(const char *dev, char *slot) -+{ -+ unsigned int major, minor; -+ struct stat dev_stat; -+ char addr[13]; -+ char *path; -+ FILE *fp; -+ -+ if (stat(dev, &dev_stat) != 0) { -+ errx(EXIT_FAILURE, "Could not get stat information for %s: %s", -+ dev, strerror(errno)); -+ } -+ major = major(dev_stat.st_rdev); -+ minor = minor(dev_stat.st_rdev); -+ -+ path = util_path_sysfs("dev/char/%u:%u/address", major, minor); -+ fp = fopen(path, "r"); -+ if (!fp) -+ fopen_err(path); -+ fscanf(fp, "%s", addr); -+ fclose(fp); -+ free(path); -+ -+ strcpy(slot, addr); -+} -+ -+static void get_device_node(struct zpci_device *pdev) -+{ -+ struct dirent **de_vec; -+ char *path, *dev; -+ char slot[13]; -+ int count, i; -+ -+ path = util_path_sysfs("bus/pci/devices/%s/nvme", pdev->slot); -+ count = util_scandir(&de_vec, alphasort, path, "nvme*"); -+ if (count == -1) { -+ warnx("Could not read directory %s: %s", path, strerror(errno)); -+ free(path); -+ exit(EXIT_FAILURE); -+ } -+ -+ for (i = 0; i < count; i++) { -+ util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); -+ sysfs_get_slot_addr(dev, slot); -+ if (strcmp(slot, pdev->slot) == 0) { -+ pdev->device = dev; -+ break; -+ } -+ } -+ -+ util_scandir_free(de_vec, count); -+ free(path); -+} -+ -+static int device_exists(char *dev) -+{ -+ char *path; -+ int rc = 0; -+ -+ path = util_path_sysfs("bus/pci/devices/%s", dev); -+ if (util_path_exists(path) || util_path_exists(dev)) -+ rc = 1; -+ free(path); -+ -+ return rc; -+} -+ -+static void get_device_info(struct zpci_device *pdev, char *dev) -+{ -+ if (!device_exists(dev)) -+ errx(EXIT_FAILURE, "Device %s not found", dev); -+ if (is_blk_dev(dev)) -+ errx(EXIT_FAILURE, "Unsupported device type %s", dev); -+ if (is_char_dev(dev)) { -+ sysfs_get_slot_addr(dev, pdev->slot); -+ pdev->device = dev; -+ } else { -+ strcpy(pdev->slot, dev); -+ } -+ -+ pdev->class = sysfs_read_value(pdev, "class"); -+ pdev->fid = sysfs_read_value(pdev, "function_id"); -+ pdev->pchid = sysfs_read_value(pdev, "pchid"); -+ -+ /* In case a slot address was specified, we still need to figure out -+ * the device node for NVMe devices. Otherwise we won't be able to -+ * collect S.M.A.R.T. data at a later point. -+ */ -+ if (!pdev->device && pdev->class == PCI_CLASS_NVME) -+ get_device_node(pdev); -+} -+ -+/* -+ * Issue an SCLP Adapter Error Notification event with a specific action -+ * qualifier. -+ * -+ * Collect additional information when possible (e.g. S.M.A.R.T. data for NVMe -+ * devices). -+ */ -+static void sclp_issue_action(struct zpci_device *pdev, int action) -+{ -+ struct zpci_report_error report = { -+ .header = { 0 }, -+ .data = { 0 } -+ }; -+ char *sdata = NULL; -+ -+ report.header.version = 1; -+ report.header.action = action; -+ report.header.length = sizeof(report.data); -+ report.data.timestamp = (__u64)time(NULL); -+ report.data.err_log_id = 0x4713; -+ -+ if (pdev->class == PCI_CLASS_NVME) -+ sdata = collect_smart_data(pdev); -+ if (sdata) { -+ strncpy(report.data.log_data, sdata, sizeof(report.data.log_data)); -+ free(sdata); -+ } -+ sysfs_write_data(&report, pdev->slot); -+} -+ -+/* -+ * Reset the PCI device and initiate a re-initialization. -+ */ -+static void sclp_reset_device(struct zpci_device *pdev) -+{ -+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET); -+} -+ -+/* -+ * De-Configure/repair PCI device. Moves the device from configured -+ * to reserved state. -+ */ -+static void sclp_deconfigure(struct zpci_device *pdev) -+{ -+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_DECONF); -+} -+ -+/* -+ * Report an error to the SE. -+ */ -+static void sclp_report_error(struct zpci_device *pdev) -+{ -+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_REPORT_ERR); -+} -+ -+static void parse_cmdline(int argc, char *argv[], struct options *opts) -+{ -+ int cmd; -+ -+ util_prg_init(&prg); -+ util_opt_init(opt_vec, NULL); -+ -+ do { -+ cmd = util_opt_getopt_long(argc, argv); -+ -+ switch (cmd) { -+ case OPT_RESET: -+ opts->reset = 1; -+ break; -+ case OPT_DECONF: -+ opts->deconfigure = 1; -+ break; -+ case OPT_REPORT_ERR: -+ opts->report = 1; -+ break; -+ case 'h': -+ util_prg_print_help(); -+ util_opt_print_help(); -+ exit(EXIT_SUCCESS); -+ case 'v': -+ util_prg_print_version(); -+ exit(EXIT_SUCCESS); -+ case -1: -+ /* End of options string */ -+ if (argc == 1) { -+ errx(EXIT_FAILURE, -+ "Use '%s --help' for more information", -+ argv[0]); -+ } -+ break; -+ } -+ } while (cmd != -1); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ struct zpci_device pdev = { 0 }; -+ struct options opts = { 0 }; -+ -+ parse_cmdline(argc, argv, &opts); -+ -+ if (optind >= argc) -+ errx(EXIT_FAILURE, "No device specified"); -+ -+ get_device_info(&pdev, argv[optind]); -+ -+ if (opts.reset) -+ sclp_reset_device(&pdev); -+ else if (opts.deconfigure) -+ sclp_deconfigure(&pdev); -+ else if (opts.report) -+ sclp_report_error(&pdev); -+ -+ return 0; -+} -diff --git a/zpcictl/zpcictl.h b/zpcictl/zpcictl.h -new file mode 100644 -index 0000000..5187e7c ---- /dev/null -+++ b/zpcictl/zpcictl.h -@@ -0,0 +1,60 @@ -+/* -+ * zpcictl - Manage PCI devices on z Systems -+ * -+ * Copyright IBM Corp. 2018 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef ZPCICTL_H -+#define ZPCICTL_H -+ -+#include -+#include "lib/zt_common.h" -+ -+#define SCLP_ERRNOTIFY_AQ_RESET 0 -+#define SCLP_ERRNOTIFY_AQ_DECONF 1 -+#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 -+ -+#define PCI_CLASS_UNCLASSIFIED 0x000000U -+#define PCI_CLASS_NVME 0x010802U -+#define PCI_CLASS_NETWORK 0x020000U -+ -+struct options { -+ unsigned int reset; -+ unsigned int deconfigure; -+ unsigned int report; -+}; -+ -+struct zpci_device { -+ u16 fid; -+ u16 pchid; -+ u32 class; -+ char slot[13]; -+ char *device; -+}; -+ -+struct zpci_report_error_header { -+ __u8 version; /* Interface version byte */ -+ __u8 action; /* Action qualifier byte -+ * 0: Adapter Reset Request -+ * 1: Deconfigure and repair action requested -+ * 2: Informational Report -+ */ -+ __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ -+ __u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */ -+}; -+ -+struct zpci_report_error_data { -+ __u64 timestamp; -+ __u64 err_log_id; -+ char log_data[4054]; /* We cannot exceed a total of 4074 bytes (header + data) */ -+}; -+ -+struct zpci_report_error { -+ struct zpci_report_error_header header; -+ struct zpci_report_error_data data; -+} __packed; -+ -+#endif /* ZPCICTL_H */ --- -2.21.3 - - -From c17b203ee85a63c812654e937f0c5cb3da6229e3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:35:09 +0100 -Subject: [PATCH 08/57] zpcictl: Read device link to obtain device address - (#1639220) - -Description: zpcictl: Read device link to obtain device address -Symptom: Issuing zpcictl --report-error /dev/nvme0 leads to: - zpcictl: Could not open file /sys/dev/char/249:0/address: - No such file or directory -Problem: The sysfs attribute 'address' in /sys/dev/char/x:x/ is not - present on all kernel versions. -Solution: Read the device link using readlink() to obtain the device - address instead. -Reproduction: For example, run zpcictl --report-error /dev/nvme0 before - kernel 4.8. ---- - zpcictl/zpcictl.8 | 13 +++++---- - zpcictl/zpcictl.c | 72 +++++++++++++++++++++++++++++++---------------- - 2 files changed, 55 insertions(+), 30 deletions(-) - -diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 -index 4f8fcd8..41fab9a 100644 ---- a/zpcictl/zpcictl.8 -+++ b/zpcictl/zpcictl.8 -@@ -1,4 +1,4 @@ --.\" Copyright 2017 IBM Corp. -+.\" Copyright IBM Corp. 2018 - .\" s390-tools is free software; you can redistribute it and/or modify - .\" it under the terms of the MIT license. See LICENSE for details. - .\" -@@ -30,9 +30,10 @@ zpcictl - Manage PCI devices on z Systems - . - . - .SH DESCRIPTION -+With - .B zpcictl --is a tool for managing PCI devices on the IBM z Systems platform. It is --especially used for reporting errorneous PCI devices to the service element. -+, you can manage PCI devices on the IBM z Systems platform. It is especially -+used for reporting erroneous PCI devices to the service element. - - .B Note: - For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent -@@ -44,7 +45,9 @@ for this to work. - .SH DEVICE - .B DEVICE - can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node --of an NVMe device (e.g. /dev/nvme0). -+of an NVMe device (e.g. -+.I /dev/nvme0 -+). - . - . - .SH OPTIONS -@@ -52,7 +55,7 @@ of an NVMe device (e.g. /dev/nvme0). - .OD reset "" "DEVICE" - Reset - .I DEVICE --and initiate a re-initialisation of the adapter. -+and initiate a re-initialization of the PCI device. - .PP - . - .OD deconfigure "" "DEVICE" -diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c -index 5f63b17..a9e38fb 100644 ---- a/zpcictl/zpcictl.c -+++ b/zpcictl/zpcictl.c -@@ -104,6 +104,9 @@ static char *collect_smart_data(struct zpci_device *pdev) - char *cmd; - FILE *fd; - -+ if (!pdev->device) -+ return NULL; -+ - util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); - fd = popen(cmd, "r"); - if (!fd) -@@ -155,45 +158,60 @@ static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) - - static void sysfs_write_data(struct zpci_report_error *report, char *slot) - { -+ size_t r_size; - char *path; -- int fd, rc; -+ FILE *fp; -+ -+ r_size = sizeof(*report); - - path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); -- fd = open(path, O_WRONLY); -- if (!fd) -+ fp = fopen(path, "w"); -+ if (!fp) - fopen_err(path); -- rc = write(fd, report, sizeof(*report)); -- if (rc == -1) -+ if (fwrite(report, 1, r_size, fp) != r_size) - warnx("Could not write to file: %s: %s", path, strerror(errno)); -- if (close(fd)) -+ if (fclose(fp)) - warnx("Could not close file: %s: %s", path, strerror(errno)); - free(path); - } - --static void sysfs_get_slot_addr(const char *dev, char *slot) -+/* lstat() doesn't work for sysfs files, so we have to work with a fixed size */ -+#define READLINK_SIZE 256 -+ -+static int sysfs_get_slot_addr(const char *dev, char *slot) - { -+ char device[READLINK_SIZE], *result; - unsigned int major, minor; - struct stat dev_stat; -- char addr[13]; -+ ssize_t len; - char *path; -- FILE *fp; - - if (stat(dev, &dev_stat) != 0) { -- errx(EXIT_FAILURE, "Could not get stat information for %s: %s", -- dev, strerror(errno)); -+ warnx("Could not get stat information for %s: %s", -+ dev, strerror(errno)); -+ return 0; - } - major = major(dev_stat.st_rdev); - minor = minor(dev_stat.st_rdev); - -- path = util_path_sysfs("dev/char/%u:%u/address", major, minor); -- fp = fopen(path, "r"); -- if (!fp) -- fopen_err(path); -- fscanf(fp, "%s", addr); -- fclose(fp); -+ path = util_path_sysfs("dev/char/%u:%u/device", major, minor); -+ len = readlink(path, device, READLINK_SIZE - 1); - free(path); -+ if (len != -1) { -+ device[len] = '\0'; -+ } else { -+ warnx("Could not read device link for %s", dev); -+ return 0; -+ } -+ -+ result = strrchr(device, '/'); -+ if (result) -+ result++; -+ else -+ result = device; -+ strcpy(slot, result); - -- strcpy(slot, addr); -+ return 1; - } - - static void get_device_node(struct zpci_device *pdev) -@@ -208,12 +226,13 @@ static void get_device_node(struct zpci_device *pdev) - if (count == -1) { - warnx("Could not read directory %s: %s", path, strerror(errno)); - free(path); -- exit(EXIT_FAILURE); -+ return; - } - - for (i = 0; i < count; i++) { - util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); -- sysfs_get_slot_addr(dev, slot); -+ if (!sysfs_get_slot_addr(dev, slot)) -+ continue; - if (strcmp(slot, pdev->slot) == 0) { - pdev->device = dev; - break; -@@ -240,11 +259,13 @@ static int device_exists(char *dev) - static void get_device_info(struct zpci_device *pdev, char *dev) - { - if (!device_exists(dev)) -- errx(EXIT_FAILURE, "Device %s not found", dev); -+ errx(EXIT_FAILURE, "Could not find device %s", dev); - if (is_blk_dev(dev)) - errx(EXIT_FAILURE, "Unsupported device type %s", dev); - if (is_char_dev(dev)) { -- sysfs_get_slot_addr(dev, pdev->slot); -+ if (!sysfs_get_slot_addr(dev, pdev->slot)) -+ errx(EXIT_FAILURE, -+ "Could not determine slot address for %s", dev); - pdev->device = dev; - } else { - strcpy(pdev->slot, dev); -@@ -254,9 +275,10 @@ static void get_device_info(struct zpci_device *pdev, char *dev) - pdev->fid = sysfs_read_value(pdev, "function_id"); - pdev->pchid = sysfs_read_value(pdev, "pchid"); - -- /* In case a slot address was specified, we still need to figure out -- * the device node for NVMe devices. Otherwise we won't be able to -- * collect S.M.A.R.T. data at a later point. -+ /* -+ * In case a slot address was specified, the device node for NVMe -+ * devices is still needed. Otherwise it won't be possible to collect -+ * S.M.A.R.T. data at a later point. - */ - if (!pdev->device && pdev->class == PCI_CLASS_NVME) - get_device_node(pdev); --- -2.21.3 - - -From 8d8e0a970dbde0ca4486192aed806d837f080370 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:36:36 +0100 -Subject: [PATCH 09/57] zpcictl: Change wording of man-page and help output - (#1643451) - -Description: zpcictl: Change wording of man-page and help output -Symptom: - -Problem: Wording not clear enough and platform description is wrong. -Solution: Change wording and use the correct platform description. -Reproduction: - -Upstream-ID: aaaebb2030c80151ecac528f22cb9a52752b868c ---- - zpcictl/zpcictl.8 | 38 +++++++++++++++----------------------- - zpcictl/zpcictl.c | 15 ++++++++------- - 2 files changed, 23 insertions(+), 30 deletions(-) - -diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8 -index 41fab9a..38aa344 100644 ---- a/zpcictl/zpcictl.8 -+++ b/zpcictl/zpcictl.8 -@@ -20,7 +20,7 @@ - .TH zpcictl 8 "Oct 2018" s390-tools zpcictl - . - .SH NAME --zpcictl - Manage PCI devices on z Systems -+zpcictl - Manage PCI devices on IBM Z - . - . - .SH SYNOPSIS -@@ -30,50 +30,42 @@ zpcictl - Manage PCI devices on z Systems - . - . - .SH DESCRIPTION --With -+Use - .B zpcictl --, you can manage PCI devices on the IBM z Systems platform. It is especially --used for reporting erroneous PCI devices to the service element. -+to manage PCI devices on the IBM Z platform. In particular, -+use this command to report defective PCI devices to the service element. - - .B Note: - For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent --with any error handling action. The smartmontools are required to be installed --for this to work. -+with any error handling action. For this extendend data collection, the -+smartmontools must be installed. - .PP - . - . - .SH DEVICE --.B DEVICE --can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node --of an NVMe device (e.g. -+A PCI slot address (e.g. 0000:00:00.0) or the main device node of an NVMe -+device (e.g. - .I /dev/nvme0 - ). - . - . - .SH OPTIONS --.SS Error Handling -+.SS Error Handling Options - .OD reset "" "DEVICE" --Reset --.I DEVICE --and initiate a re-initialization of the PCI device. -+Reset and re-initialize the PCI device. - .PP - . - .OD deconfigure "" "DEVICE" --De-configure --.I DEVICE --and prepare for any repair action. This action will move the --PCI device from a configured to a reserved state. -+Deconfigure the PCI device and prepare for any repair action. This action -+changes the status of the PCI device from configured to reserved. - .PP - . - .OD report-error "" "DEVICE" --Report any device error for --.IR DEVICE . --The --.I DEVICE --is marked as erroneous and no further action is initiated on it. -+Report any device error for the PCI device. -+The device is marked as defective but no further action is taken. - .PP - . --.SS Misc -+.SS General Options - .OD help "h" "" - Print usage information, then exit. - .PP -diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c -index a9e38fb..7cfa0d3 100644 ---- a/zpcictl/zpcictl.c -+++ b/zpcictl/zpcictl.c -@@ -27,8 +27,9 @@ - #define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" - - static const struct util_prg prg = { -- .desc = "Use zpcictl to manage PCI devices on s390\n" -- "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", -+ .desc = "Use zpcictl to manage PCI devices on IBM Z\n" -+ "DEVICE is the slot ID or node of the device " -+ "(e.g. 0000:00:00.0 or /dev/nvme0)", - .args = "DEVICE", - .copyright_vec = { - { -@@ -46,23 +47,23 @@ static const struct util_prg prg = { - #define OPT_REPORT_ERR 130 - - static struct util_opt opt_vec[] = { -- UTIL_OPT_SECTION("ERROR HANDLING"), -+ UTIL_OPT_SECTION("ERROR HANDLING OPTIONS"), - { - .option = { "reset", no_argument, NULL, OPT_RESET }, -- .desc = "Reset device", -+ .desc = "Reset the device", - .flags = UTIL_OPT_FLAG_NOSHORT, - }, - { - .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, -- .desc = "De-configure device and prepare for any repair action", -+ .desc = "Deconfigure the device to prepare for any repair action", - .flags = UTIL_OPT_FLAG_NOSHORT, - }, - { - .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, -- .desc = "Report device error to service element (SE)", -+ .desc = "Report a device error to the service element (SE)", - .flags = UTIL_OPT_FLAG_NOSHORT, - }, -- UTIL_OPT_SECTION("MISC"), -+ UTIL_OPT_SECTION("GENERAL OPTIONS"), - UTIL_OPT_HELP, - UTIL_OPT_VERSION, - UTIL_OPT_END --- -2.21.3 - - -From 916022e17d4cb189c0d83028ed09d3f4600046dd Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:37:34 +0100 -Subject: [PATCH 10/57] zdev: qeth BridgePort and VNICC attribute conflict - (#1643452) - -Description: zdev: qeth BridgePort and VNICC attribute conflict -Symptom: chzdev cannot set VNICC attributes due to a conflict with - BridgePort attributes. -Problem: Existing conflict checking always assumes a BridgePort and a - VNICC attribute are active. -Solution: Introduce a function that determines if BridgePort or VNICC - attributes are active and use only active attributes for conflict - checking. -Reproduction: Set VNICC attribute with chzdev w/o active BridgePort attributes. -Upstream-ID: ffe91d1b3082730905caee75b6ec8b05e3cf46a3 ---- - zdev/src/qeth.c | 33 +++++++++++++++++++++++++++++++++ - 1 file changed, 33 insertions(+) - -diff --git a/zdev/src/qeth.c b/zdev/src/qeth.c -index 46bc23d..6191ad1 100644 ---- a/zdev/src/qeth.c -+++ b/zdev/src/qeth.c -@@ -1171,6 +1171,37 @@ static exit_code_t check_ineffective_settings(struct setting_list *list, - return rc; - } - -+/* Check if a possibly conflicting setting is active in the configuration */ -+static bool conflict_setting_active(struct setting *s) -+{ -+ enum qeth_attr_group_type t; -+ -+ t = get_attr_group_type(s); -+ if (t != group_bridge && t != group_vnicc) { -+ /* Check BridgePort and VNICC attributes only */ -+ return false; -+ } -+ if (s->specified) { -+ /* Specified on the command line: We are strict here and do not -+ * allow to specify VNICC and BridgePort attributes in the same -+ * command to avoid issues when attributes are enabled/disabled -+ * in the wrong order. Example: disable VNICC and enable -+ * BridgePort in the same command would result in an error -+ * because BridgePort attributes are set first. -+ */ -+ return true; -+ } -+ if (attrib_match_default(s->attrib, s->value)) { -+ /* Not active if set to default value */ -+ return false; -+ } -+ if (s->actual_value && strncmp(s->actual_value, "n/a", 3) == 0) { -+ /* Not active if in n/a state (conflicting attribute set) */ -+ return false; -+ } -+ return true; -+} -+ - /* Check if there are conflicting attribute settings */ - static exit_code_t check_conflicting_settings(struct setting_list *list) - { -@@ -1182,6 +1213,8 @@ static exit_code_t check_conflicting_settings(struct setting_list *list) - util_list_iterate(&list->list, s) { - if (s->removed) - continue; -+ if (!conflict_setting_active(s)) -+ continue; - t = get_attr_group_type(s); - if (t == group_bridge && (!bridge || !bridge->specified)) - bridge = s; --- -2.21.3 - - -From e2916bdb493e9565d74b0334fa45f17c6f0f730c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:38:30 +0100 -Subject: [PATCH 11/57] qethqoat: add OSA-Express7S support (#1644384) - -Description: qethqoat: add OSA-Express7S support -Symptom: qethqoat fails to report HW generation and link speed for - OSA-Express7S network card. -Problem: Missing identifiers for new values in the QUERY OAT data. -Solution: Add identifiers for card generation and link speed. -Reproduction: Run qethqoat on an OSA-Express7S, check the output for - 'OSA Generation' and 'Port speed/mode'. -Upstream-ID: 20145b6d06debd47944bff0a471d17e5eba07010 ---- - qethqoat/qethqoat.c | 6 ++++++ - qethqoat/qethqoat.h | 2 ++ - 2 files changed, 8 insertions(+) - -diff --git a/qethqoat/qethqoat.c b/qethqoat/qethqoat.c -index 6a30d1d..7110c28 100644 ---- a/qethqoat/qethqoat.c -+++ b/qethqoat/qethqoat.c -@@ -208,6 +208,9 @@ static void print_physical(struct qeth_qoat_physical *phdr) - case OAT_OSA_GEN_OSAE6S: - osagen = "OSA-Express6S"; - break; -+ case OAT_OSA_GEN_OSAE7S: -+ osagen = "OSA-Express7S"; -+ break; - default: - sprintf(tmp, "unknown (0x%x)", phdr->osa_gen); - osagen = tmp; -@@ -239,6 +242,9 @@ static void print_physical(struct qeth_qoat_physical *phdr) - case OAT_PORT_SPEED_10gbs_full: - speed = "10 Gb/s / full duplex"; - break; -+ case OAT_PORT_SPEED_25gbs_full: -+ speed = "25 Gb/s / full duplex"; -+ break; - case OAT_PORT_SPEED_UNKNOWN: - speed = "unknown / unknown"; - break; -diff --git a/qethqoat/qethqoat.h b/qethqoat/qethqoat.h -index dd7e992..e692937 100644 ---- a/qethqoat/qethqoat.h -+++ b/qethqoat/qethqoat.h -@@ -58,6 +58,7 @@ struct qeth_qoat_physical { - #define OAT_OSA_GEN_OSAE4S 0x02 - #define OAT_OSA_GEN_OSAE5S 0x03 - #define OAT_OSA_GEN_OSAE6S 0x04 -+#define OAT_OSA_GEN_OSAE7S 0x05 - __u8 osa_gen; - #define OAT_PORT_SPEED_UNKNOWN 0x00 - #define OAT_PORT_SPEED_10mbs_half 0x01 -@@ -68,6 +69,7 @@ struct qeth_qoat_physical { - #define OAT_PORT_SPEED_1000mbs_full 0x06 - #define OAT_PORT_SPEED_NA 0x07 - #define OAT_PORT_SPEED_10gbs_full 0x08 -+#define OAT_PORT_SPEED_25gbs_full 0x0A - __u8 port_speed; - #define OAT_PORT_MEDIA_COPPER 0x01 - #define OAT_PORT_MEDIA_MULTI_MODE 0x02 --- -2.21.3 - - -From ba7eb5cd9d858f2ff8bd29e0b610337896587b94 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:39:35 +0100 -Subject: [PATCH 12/57] zcryptctl: add zcryptctl to manage multiple zcrypt - nodes (#1646354) - -Summary: zcryptctl: add zcryptctl to manage multiple zcrypt nodes -Description: There is a new zcrypt kernel feature which provides - multiple customizable device nodes for the zcrypt - device driver. Here is the userspace part of this - which adds a new application zcryptctl for user - friendly management of this feature. -Upstream-ID: f05f7d656b13c3904f0c55e86ebe9e9b19fcd222 ---- - zconf/zcrypt/Makefile | 7 +- - zconf/zcrypt/zcryptctl.8 | 147 ++++++ - zconf/zcrypt/zcryptctl.c | 1030 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 1182 insertions(+), 2 deletions(-) - create mode 100644 zconf/zcrypt/zcryptctl.8 - create mode 100644 zconf/zcrypt/zcryptctl.c - -diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile -index 698e148..d075f34 100644 ---- a/zconf/zcrypt/Makefile -+++ b/zconf/zcrypt/Makefile -@@ -1,21 +1,24 @@ - include ../../common.mak - --all: chzcrypt lszcrypt -+all: chzcrypt lszcrypt zcryptctl - - libs = $(rootdir)/libutil/libutil.a - - chzcrypt: chzcrypt.o misc.o $(libs) - lszcrypt: lszcrypt.o misc.o $(libs) -+zcryptctl: zcryptctl.o misc.o $(libs) - - install: all - $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR) -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR) - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8 -+ $(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8 - - clean: -- rm -f *.o chzcrypt lszcrypt -+ rm -f *.o chzcrypt lszcrypt zcryptctl - - .PHONY: all install clean -diff --git a/zconf/zcrypt/zcryptctl.8 b/zconf/zcrypt/zcryptctl.8 -new file mode 100644 -index 0000000..5d098da ---- /dev/null -+++ b/zconf/zcrypt/zcryptctl.8 -@@ -0,0 +1,147 @@ -+.\" zcryptctl.8 -+.\" -+.\" Copyright 2018 IBM Corp. -+.\" s390-tools is free software; you can redistribute it and/or modify -+.\" it under the terms of the MIT license. See LICENSE for details. -+.\" -+.\" use -+.\" groff -man -Tutf8 zcryptctl.8 -+.\" or -+.\" nroff -man zcryptctl.8 -+.\" to process this source -+.\" -+.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools" -+.SH NAME -+zcryptctl \- display information and administrate zcrypt multiple device nodes -+.SH SYNOPSIS -+.TP 8 -+.B zcryptctl list -+.TP -+.B zcryptctl create -+.R [ -+.I node-name -+.R ] -+.TP -+.B zcryptctl destroy -+.I node-name -+.TP -+.B zcryptctl addap -+.R | -+.B delap -+.I node-name adapter-nr -+.TP -+.B zcryptctl adddom -+.R | -+.B deldom -+.I node-name domain-nr -+.TP -+.B zcryptctl addioctl -+.R | -+.B delioctl -+.I node-name ioctl-term -+.TP -+.B zcryptctl config -+.I config-file -+.TP -+.B zcryptctl listconfig -+.SH DESCRIPTION -+The -+.B zcryptctl -+command displays information and maintains the multi device node -+extension for the zcrypt device driver. -+.P -+With the multi device node extension you can create and configure -+additional zcrypt device nodes which can be used as alternate device -+nodes to access the crypto hardware provided by the zcrypt device -+driver. Each zcrypt device node can be restricted in terms of crypto -+cards, domains, and available ioctls. Such a device node can be used -+as a base for container solutions like Docker to control and restrict -+the access to crypto resources. -+.SH COMMANDS -+.TP 8 -+.B zcryptctl list -+Show all the additional device nodes that are currently active. -+.TP -+.B zcryptctl create -+.R [ -+.I node-name -+.R ] -+Create a new zcrypt device node. The \fInode-name\fP might be given -+and needs to be unique and not in use. If there is no node name -+provided, the zcrypt device driver will create a new one with pattern -+zcrypt_\fIx\fP, with \fIx\fP being the next free number. Up to 256 -+additional device nodes can be created. The newly created additional -+device node appears in /dev and has read and write permissions enabled -+only for root. By default all adapters, domains and ioctls are -+initially disabled on this new device node. -+.TP -+.B zcryptctl destroy -+.I node-name -+Destroy an additional zcrypt device node. The device node is only -+marked for disposal and destroyed when it is no longer used. -+.TP -+.B zcryptctl addap -+.R | -+.B delap -+.I node-name adapter-nr -+Update the filter for the specified zcrypt device node and add or -+delete a crypto adapter to be accessible via this node. The symbol -+\fBALL\fP can be used to enable or disable all adapters. -+.TP -+.B zcryptctl adddom -+.R | -+.B deldom -+.I node-name domain-nr -+Update the filter for the specified zcrypt device node and add or -+delete a domain to be accessible through this node. The symbol -+\fBALL\fP can be used to enable or disable all domains. -+.TP -+.B zcryptctl addioctl -+.R | -+.B delioctl -+.I node-name ioctl-term -+Update the filter for the specified zcrypt device node and add or -+delete an ioctl. The ioctl might be specified as symbolic string (one -+of \fBICARSAMODEXPO\fP, \fBICARSACRT\fP, \fBZSECSENDCPRB\fP, -+\fBZSENDEP11CPRB\fP, \fBZCRYPT_DEVICE_STATUS\fP, -+\fBZCRYPT_STATUS_MASK\fP, \fBZCRYPT_QDEPTH_MASK\fP, -+\fBZCRYPT_PERDEV_REQCNT\fP) or numeric value in the range 0-255 and -+the symbol \fBALL\fP can be used to include all ioctls. -+.TP -+.B zcryptctl config -+.I config-file -+Process a config file. The given configuration file is read line by -+line and the settings are applied. Syntax is simple: -+.RS -+.IP "node=" -+.IP "aps=" -+.IP "doms=" -+.IP "ioctls=" -+.LP -+Empty lines are ignored and the '#' marks the rest of the -+line as comment. -+.LP -+The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP, -+\fBdoms=\fP and \fBioctls=\fP lines customize the previously created -+node. The symbol \fBALL\fP is also recognized for aps, doms, and -+ioctls. -+.LP -+Each action must fit into one line, spreading over multiple lines is -+not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP -+and \fBioctls=\fP lines to customize the very same node. -+.LP -+Processing stops when a line cannot be parsed or the current action -+fails. In this case the exit status is non zero but the successful -+actions until the failure occurs are not rolled back. -+.RE -+.TP -+.B zcryptctl listconfig -+List the current configuration in a form suitable for input to the -+\fBzcryptctl config\fP command. -+.LP -+.SH EXIT STATUS -+On successful completion of the command the exit status is 0. A non -+zero return code (and some kind of failure message) is emitted if the -+processing could not complete successful. -+.SH SEE ALSO -+\fBlszcrypt\fR(8) -diff --git a/zconf/zcrypt/zcryptctl.c b/zconf/zcrypt/zcryptctl.c -new file mode 100644 -index 0000000..8326a08 ---- /dev/null -+++ b/zconf/zcrypt/zcryptctl.c -@@ -0,0 +1,1030 @@ -+/* -+ * zcryptctl - Maintain zcrypt multi device nodes. -+ * -+ * by Harald Freudenberger -+ * Copyright IBM Corp. 2018 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_base.h" -+#include "lib/util_file.h" -+#include "lib/util_opt.h" -+#include "lib/util_panic.h" -+#include "lib/util_path.h" -+#include "lib/util_prg.h" -+#include "lib/util_proc.h" -+#include "lib/util_rec.h" -+#include "lib/util_scandir.h" -+#include "lib/zt_common.h" -+ -+#define MAX_ZDEV_IOCTLS 256 -+#define ZCRYPT_NAME "zcrypt" -+#define MAX_ZDEV_CARDIDS_EXT 256 -+#define MAX_ZDEV_DOMAINS_EXT 256 -+#define ZCRYPTDEVICE "/dev/z90crypt" -+#define _UNUSED_ __attribute__((unused)) -+ -+/* -+ * Currently known commands -+ */ -+#define CMD_LIST 0x0001 -+#define CMD_CREATE 0x0002 -+#define CMD_DESTROY 0x0003 -+#define CMD_ADD_AP 0x0004 -+#define CMD_DEL_AP 0x0005 -+#define CMD_ADD_DOM 0x0006 -+#define CMD_DEL_DOM 0x0007 -+#define CMD_ADD_IOCTL 0x0008 -+#define CMD_DEL_IOCTL 0x0009 -+#define CMD_CONFIG 0x000A -+#define CMD_LISTCONFIG 0x000B -+ -+/* -+ * Program configuration -+ */ -+static const struct util_prg prg = { -+ .args = "", -+ .command_args = "COMMAND [COMMAND-PARAMS]", -+ .desc = "Display and administrate zcrypt multiple device nodes.", -+ .copyright_vec = { -+ { -+ .owner = "IBM Corp.", -+ .pub_first = 2018, -+ .pub_last = 2018, -+ }, -+ UTIL_PRG_COPYRIGHT_END -+ } -+}; -+ -+static struct util_opt opt_vec[] = { -+ UTIL_OPT_HELP, -+ UTIL_OPT_VERSION, -+ UTIL_OPT_END -+}; -+ -+/* -+ * List of currently known and supported ioctls -+ */ -+static struct zcryptctl_ioctls_s { -+ int nr; -+ const char *name; -+} zcryptctl_ioctls[] = { -+ { -+ .name = "ICARSAMODEXPO", -+ .nr = 0x05, -+ }, -+ { -+ .name = "ICARSACRT", -+ .nr = 0x06, -+ }, -+ { -+ .name = "ZSECSENDCPRB", -+ .nr = 0x81, -+ }, -+ { -+ .name = "ZSENDEP11CPRB", -+ .nr = 0x04, -+ }, -+ { -+ .name = "ZCRYPT_DEVICE_STATUS", -+ .nr = 0x5f, -+ }, -+ { -+ .name = "ZCRYPT_STATUS_MASK", -+ .nr = 0x58, -+ }, -+ { -+ .name = "ZCRYPT_QDEPTH_MASK", -+ .nr = 0x59, -+ }, -+ { -+ .name = "ZCRYPT_PERDEV_REQCNT", -+ .nr = 0x5a, -+ }, -+ { -+ .name = NULL, -+ .nr = 0, -+ }, -+}; -+ -+static int ioctlstr2value(const char *str) -+{ -+ int i; -+ -+ for (i = 0; zcryptctl_ioctls[i].name; i++) -+ if (strcasecmp(str, zcryptctl_ioctls[i].name) == 0) -+ return zcryptctl_ioctls[i].nr; -+ -+ return -1; -+} -+ -+static const char *value2ioctlstr(int value) -+{ -+ int i; -+ -+ for (i = 0; zcryptctl_ioctls[i].name; i++) -+ if (value == zcryptctl_ioctls[i].nr) -+ return zcryptctl_ioctls[i].name; -+ -+ return NULL; -+} -+ -+static int check_nodename(const char *nodename) -+{ -+ struct stat sb; -+ const char *node; -+ char pathname[PATH_MAX]; -+ -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ snprintf(pathname, sizeof(pathname), "/dev/%s", node); -+ pathname[sizeof(pathname) - 1] = '\0'; -+ if (stat(pathname, &sb) != 0) -+ return -1; -+ if (!S_ISCHR(sb.st_mode)) -+ return -2; -+ -+ return 0; -+} -+ -+static int check_zcrypt_class_dir(void) -+{ -+ int rc = 0; -+ char *afile; -+ -+ afile = util_path_sysfs("class/%s", ZCRYPT_NAME); -+ if (!util_path_is_dir(afile)) -+ rc = -1; -+ -+ free(afile); -+ return rc; -+} -+ -+static int fetch_major_minor(const char *nodename, int *major, int *minor) -+{ -+ FILE *f; -+ int rc = 0; -+ char *afile; -+ const char *node; -+ -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ afile = util_path_sysfs("class/%s/%s/dev", ZCRYPT_NAME, node); -+ f = fopen(afile, "r"); -+ if (!f) { -+ rc = -1; -+ goto out; -+ } -+ if (fscanf(f, "%i:%i", major, minor) != 2) { -+ fclose(f); -+ rc = -2; -+ goto out; -+ } -+ fclose(f); -+ -+out: -+ free(afile); -+ return rc; -+} -+ -+static int write_dn_attr(const char *nodename, const char *attr, -+ const char *value) -+{ -+ FILE *f; -+ int rc = 0; -+ char *afile; -+ const char *node; -+ -+ if (nodename) { -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ afile = util_path_sysfs("class/%s/%s/%s", -+ ZCRYPT_NAME, node, attr); -+ } else -+ afile = util_path_sysfs("class/%s/%s", ZCRYPT_NAME, attr); -+ f = fopen(afile, "w"); -+ if (!f) { -+ rc = -1; -+ goto out; -+ } -+ if (fprintf(f, "%s\n", value) < 0) { -+ fclose(f); -+ rc = -2; -+ goto out; -+ } -+ fflush(f); -+ if (ferror(f)) { -+ fclose(f); -+ rc = -2; -+ goto out; -+ } -+ -+ fclose(f); -+ -+out: -+ free(afile); -+ return rc; -+} -+ -+static int read_dn_attr(const char *nodename, const char *attr, -+ char *value, int valuelen) -+{ -+ int rc; -+ FILE *f; -+ char *afile; -+ const char *node; -+ -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ afile = util_path_sysfs("class/%s/%s/%s", ZCRYPT_NAME, node, attr); -+ f = fopen(afile, "r"); -+ if (!f) { -+ rc = -1; -+ goto out; -+ } -+ value = fgets(value, valuelen, f); -+ fclose(f); -+ rc = value ? 0 : -2; -+ -+out: -+ free(afile); -+ return rc; -+} -+ -+static int test_bit(int n, const char *hexbytestr) -+{ -+ char c; -+ int v, i = 0; -+ -+ if (strncmp(hexbytestr, "0x", 2) == 0) -+ i += 2; -+ c = hexbytestr[i + n / 4]; -+ if (c >= '0' && c <= '9') -+ v = c - '0'; -+ else if (c >= 'a' && c <= 'f') -+ v = 10 + c - 'a'; -+ else if (c >= 'A' && c <= 'F') -+ v = 10 + c - 'A'; -+ else -+ errx(EXIT_FAILURE, -+ "Could not parse hex digit '%c'", c); -+ -+ return v & (1 << (3 - (n % 4))); -+} -+ -+static int cmd_list(int cmd, -+ const char *node _UNUSED_, -+ const char *arg _UNUSED_) -+{ -+ DIR *dir; -+ char *dirname; -+ const char *p; -+ struct dirent *de; -+ int i, n, major, minor, count = 0; -+ char buf[80], tab = (cmd == CMD_LISTCONFIG ? ' ' : '\t'); -+ -+ dirname = util_path_sysfs("class/%s", ZCRYPT_NAME); -+ dir = opendir(dirname); -+ if (!dir) -+ errx(EXIT_FAILURE, -+ "Could not read directory '%s' errno=%d (%s)", -+ dirname, errno, strerror(errno)); -+ while ((de = readdir(dir)) != NULL) { -+ if (de->d_name[0] == '.' || de->d_type == DT_REG) -+ continue; -+ if (fetch_major_minor(de->d_name, &major, &minor) != 0) -+ errx(EXIT_FAILURE, -+ "Could not fetch major/minor from sysfs for zcrypt node '%s'", -+ de->d_name); -+ if (cmd == CMD_LISTCONFIG) { -+ printf("node = %s\n", de->d_name); -+ printf(" aps ="); -+ } else { -+ printf("zcrypt node name:\t%s\n", de->d_name); -+ printf(" device node:\t/dev/%s\n", de->d_name); -+ printf(" major:minor:\t%d:%d\n", major, minor); -+ printf(" adapter:"); -+ } -+ if (read_dn_attr(de->d_name, "apmask", buf, sizeof(buf)) != 0) -+ errx(EXIT_FAILURE, -+ "Could not fetch apmask attribute from sysfs for zcrypt node '%s'", -+ de->d_name); -+ for (i = n = 0; i < MAX_ZDEV_CARDIDS_EXT; i++) -+ if (test_bit(i, buf)) -+ printf("%c%d", n++ == 0 ? tab : ',', i); -+ putchar('\n'); -+ if (cmd == CMD_LISTCONFIG) -+ printf(" doms ="); -+ else -+ printf(" domains:"); -+ if (read_dn_attr(de->d_name, "aqmask", buf, sizeof(buf)) != 0) -+ errx(EXIT_FAILURE, -+ "Could not fetch aqmask attribute from sysfs for zcrypt node '%s'", -+ de->d_name); -+ for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++) -+ if (test_bit(i, buf)) -+ printf("%c%d", n++ == 0 ? tab : ',', i); -+ putchar('\n'); -+ if (cmd == CMD_LISTCONFIG) -+ printf(" ioctls ="); -+ else -+ printf(" ioctls:"); -+ if (read_dn_attr(de->d_name, "ioctlmask", -+ buf, sizeof(buf)) != 0) -+ errx(EXIT_FAILURE, -+ "Could not fetch ioctlmask attribute from sysfs for zcrypt node '%s'", -+ de->d_name); -+ for (i = n = 0; i < MAX_ZDEV_IOCTLS; i++) { -+ if (test_bit(i, buf)) { -+ p = value2ioctlstr(i); -+ if (p) -+ printf("%c%s", -+ n++ == 0 ? tab : ',', p); -+ else -+ printf("%c%d", -+ n++ == 0 ? tab : ',', i); -+ } -+ } -+ putchar('\n'); -+ count++; -+ } -+ closedir(dir); -+ -+ if (count == 0) -+ printf("No additional zcrypt device nodes defined\n"); -+ -+ return 0; -+} -+ -+static int cmd_create(int cmd _UNUSED_, -+ const char *nodename, -+ const char *arg _UNUSED_) -+{ -+ int rc; -+ const char *node; -+ char buf[PATH_MAX]; -+ -+ if (nodename) { -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ strncpy(buf, node, sizeof(buf) - 1); -+ } else -+ strncpy(buf, "\n", sizeof(buf) - 1); -+ buf[sizeof(buf) - 1] = 0; -+ -+ rc = write_dn_attr(NULL, "create", buf); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not write into sysfs entry to create zdev node"); -+ -+ printf("Device node created\n"); -+ -+ return 0; -+} -+ -+static int cmd_destroy(int cmd _UNUSED_, -+ const char *nodename, -+ const char *arg _UNUSED_) -+{ -+ int rc; -+ struct stat sb; -+ const char *node; -+ char pathname[PATH_MAX]; -+ -+ node = strrchr(nodename, '/'); -+ node = node ? node + 1 : nodename; -+ snprintf(pathname, sizeof(pathname), "/dev/%s", node); -+ pathname[sizeof(pathname) - 1] = '\0'; -+ rc = stat(pathname, &sb); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not check status for '%s'", pathname); -+ if (!S_ISCHR(sb.st_mode)) -+ errx(EXIT_FAILURE, -+ "File '%s' is not a character device node", pathname); -+ -+ rc = write_dn_attr(NULL, "destroy", node); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not write into sysfs entry to destroy zdev node '%s'", -+ node); -+ -+ printf("Device node '%s' marked for destruction\n", node); -+ -+ return 0; -+} -+ -+static void add_del_ap(int cmd, const char *node, int ap) -+{ -+ int rc; -+ char buf[PATH_MAX]; -+ -+ if (cmd == CMD_ADD_AP) -+ sprintf(buf, "+%d", ap); -+ else -+ sprintf(buf, "-%d", ap); -+ rc = write_dn_attr(node, "apmask", buf); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not write into sysfs entry to %s adapter %d for zdev node '%s'", -+ cmd == CMD_ADD_AP ? "add" : "remove", ap, node); -+} -+ -+static int cmd_add_del_ap(int cmd, const char *node, const char *arg) -+{ -+ int ap, all = 0; -+ -+ if (strcasecmp(arg, "ALL") == 0) { -+ all = 1; -+ } else { -+ if (sscanf(arg, "%i", &ap) != 1) -+ errx(EXIT_FAILURE, -+ "Invalid adapter argument '%s'", arg); -+ if (ap < 0 || ap >= MAX_ZDEV_CARDIDS_EXT) -+ errx(EXIT_FAILURE, -+ "Adapter argument '%s' out of range [0..%d]", -+ arg, MAX_ZDEV_CARDIDS_EXT - 1); -+ } -+ -+ if (!all) { -+ add_del_ap(cmd, node, ap); -+ printf("Adapter %d %s\n", ap, -+ (cmd == CMD_ADD_AP ? "added" : "removed")); -+ } else { -+ for (ap = 0; ap < MAX_ZDEV_CARDIDS_EXT; ap++) -+ add_del_ap(cmd, node, ap); -+ printf("All adapters %s\n", -+ (cmd == CMD_ADD_AP ? "added" : "removed")); -+ } -+ -+ return 0; -+} -+ -+static void add_del_dom(int cmd, const char *node, int dom) -+{ -+ int rc; -+ char buf[PATH_MAX]; -+ -+ if (cmd == CMD_ADD_DOM) -+ sprintf(buf, "+%d", dom); -+ else -+ sprintf(buf, "-%d", dom); -+ rc = write_dn_attr(node, "aqmask", buf); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not write into sysfs entry to %s domain %d for zdev node '%s'", -+ cmd == CMD_ADD_DOM ? "add" : "remove", dom, node); -+} -+ -+static int cmd_add_del_dom(int cmd, const char *node, const char *arg) -+{ -+ int dom, all = 0; -+ -+ if (strcasecmp(arg, "ALL") == 0) { -+ all = 1; -+ } else { -+ if (sscanf(arg, "%i", &dom) != 1) -+ errx(EXIT_FAILURE, -+ "Invalid domain argument '%s'", arg); -+ if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT) -+ errx(EXIT_FAILURE, -+ "Domain argument '%s' out of range [0..%d]", -+ arg, MAX_ZDEV_DOMAINS_EXT - 1); -+ } -+ -+ if (!all) { -+ add_del_dom(cmd, node, dom); -+ printf("Domain %d %s\n", dom, -+ (cmd == CMD_ADD_DOM ? "added" : "removed")); -+ } else { -+ for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++) -+ add_del_dom(cmd, node, dom); -+ printf("All domains %s\n", -+ (cmd == CMD_ADD_DOM ? "added" : "removed")); -+ } -+ -+ return 0; -+} -+ -+static void add_del_ioctl(int cmd, const char *node, int ioctlnr) -+{ -+ int rc; -+ char buf[PATH_MAX]; -+ -+ if (cmd == CMD_ADD_IOCTL) -+ sprintf(buf, "+%d", ioctlnr); -+ else -+ sprintf(buf, "-%d", ioctlnr); -+ rc = write_dn_attr(node, "ioctlmask", buf); -+ if (rc != 0) -+ errx(EXIT_FAILURE, -+ "Could not write into sysfs entry to %s ioctl %d for zdev node '%s'", -+ cmd == CMD_ADD_IOCTL ? "add" : "remove", ioctlnr, node); -+} -+ -+static int cmd_add_del_ioctl(int cmd, const char *node, const char *arg) -+{ -+ int ioctlnr, all = 0; -+ -+ if (strcasecmp(arg, "ALL") == 0) { -+ all = 1; -+ } else { -+ ioctlnr = ioctlstr2value(arg); -+ if (ioctlnr < 0) -+ if (sscanf(arg, "%i", &ioctlnr) != 1) -+ errx(EXIT_FAILURE, -+ "Invalid ioctl argument '%s'", arg); -+ if (ioctlnr < 0 || ioctlnr >= MAX_ZDEV_IOCTLS) -+ errx(EXIT_FAILURE, -+ "Ioctl argument '%s' out of range [0..%d]", -+ arg, MAX_ZDEV_IOCTLS - 1); -+ } -+ -+ if (!all) { -+ add_del_ioctl(cmd, node, ioctlnr); -+ printf("Ioctl %s %s\n", arg, -+ (cmd == CMD_ADD_IOCTL ? "added" : "removed")); -+ } else { -+ for (ioctlnr = 0; ioctlnr < MAX_ZDEV_IOCTLS; ioctlnr++) -+ add_del_ioctl(cmd, node, ioctlnr); -+ printf("All Ioctls %s\n", -+ (cmd == CMD_ADD_IOCTL ? "added" : "removed")); -+ } -+ -+ return 0; -+} -+ -+static int _match_keyword(char **p, const char *keyword) -+{ -+ int n = strlen(keyword); -+ -+ if (strncmp(*p, keyword, n) == 0) { -+ *p += n; -+ return n; -+ } -+ -+ return 0; -+} -+ -+static int _match_character(char **p, char c) -+{ -+ char *q = *p; -+ -+ while (isblank(*q)) -+ q++; -+ if (*q != c) -+ return 0; -+ q++; -+ while (isblank(*q)) -+ q++; -+ *p = q; -+ -+ return 1; -+} -+ -+static int _match_string(char **p, char *buf) -+{ -+ int n = 0; -+ char *q = *p; -+ -+ while (isblank(*q)) -+ q++; -+ while (*q && *q != ',' && !isspace(*q)) { -+ buf[n++] = *q; -+ q++; -+ } -+ while (isblank(*q)) -+ q++; -+ -+ if (n > 0) { -+ buf[n] = '\0'; -+ *p = q; -+ } -+ -+ return n; -+} -+ -+static int cmd_config(int cmd _UNUSED_, -+ const char *nodename _UNUSED_, -+ const char *arg) -+{ -+ ssize_t n; -+ size_t linesize = 0; -+ int nr = 0, havenode = 0; -+ FILE *f = fopen(arg, "r"); -+ char *p, *line = NULL, node[128], buf[128]; -+ -+ if (!f) -+ errx(EXIT_FAILURE, -+ "Could not open file '%s'", arg); -+ -+ while ((n = getline(&line, &linesize, f)) != -1) { -+ nr++; -+ p = line; -+ while (isspace(*p)) -+ p++; -+ if (*p == '\0' || *p == '#') -+ continue; -+ if (_match_keyword(&p, "node")) { -+ if (!_match_character(&p, '=')) -+ errx(EXIT_FAILURE, -+ "Missing '=' at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ if (!_match_string(&p, node)) -+ errx(EXIT_FAILURE, -+ "Missing node name at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ cmd_create(CMD_CREATE, node, NULL); -+ havenode = 1; -+ } else if (_match_keyword(&p, "aps")) { -+ if (!havenode) -+ errx(EXIT_FAILURE, -+ "Missing node=... before processing any aps=... statements in line %d '%s'", -+ nr, line); -+ if (!_match_character(&p, '=')) -+ errx(EXIT_FAILURE, -+ "Missing '=' at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ while (1) { -+ while (isspace(*p)) -+ p++; -+ if (*p == '\0' || *p == '#') -+ break; -+ if (!_match_string(&p, buf)) -+ errx(EXIT_FAILURE, -+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ cmd_add_del_ap(CMD_ADD_AP, node, buf); -+ while (isblank(*p) || *p == ',') -+ p++; -+ } -+ } else if (_match_keyword(&p, "doms")) { -+ if (!havenode) -+ errx(EXIT_FAILURE, -+ "Missing node=... before processing any doms=... statements in line %d '%s'", -+ nr, line); -+ if (!_match_character(&p, '=')) -+ errx(EXIT_FAILURE, -+ "Missing '=' at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ while (1) { -+ while (isspace(*p)) -+ p++; -+ if (*p == '\0' || *p == '#') -+ break; -+ if (!_match_string(&p, buf)) -+ errx(EXIT_FAILURE, -+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ cmd_add_del_dom(CMD_ADD_DOM, node, buf); -+ while (isblank(*p) || *p == ',') -+ p++; -+ } -+ } else if (_match_keyword(&p, "ioctls")) { -+ if (!havenode) -+ errx(EXIT_FAILURE, -+ "Missing node=... before processing any ioctls=... statements in line %d '%s'", -+ nr, line); -+ if (!_match_character(&p, '=')) -+ errx(EXIT_FAILURE, -+ "Missing '=' at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ while (1) { -+ while (isspace(*p)) -+ p++; -+ if (*p == '\0' || *p == '#') -+ break; -+ if (!_match_string(&p, buf)) -+ errx(EXIT_FAILURE, -+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ cmd_add_del_ioctl(CMD_ADD_IOCTL, node, buf); -+ while (isblank(*p) || *p == ',') -+ p++; -+ } -+ } else -+ errx(EXIT_FAILURE, -+ "Unknown keyword '%-8.8s...' in line %d '%s'", -+ p, nr, line); -+ } -+ -+ free(line); -+ fclose(f); -+ -+ return 0; -+} -+ -+static struct zcryptctl_cmds_s { -+ int cmd; -+ const char *usage; -+ const char *command; -+ const char *description; -+ int (*function)(int cmd, const char *node, const char *arg); -+} zcryptctl_cmds[] = { -+ { -+ .cmd = CMD_LIST, -+ .command = "list", -+ .function = cmd_list, -+ .usage = "zcryptctl list", -+ .description = -+ "List all currently known additional zcrypt device nodes.", -+ }, -+ { -+ .cmd = CMD_CREATE, -+ .command = "create", -+ .function = cmd_create, -+ .usage = "zcryptctl create [nodename]", -+ .description = -+ "Create a new zcrypt device node.\n" -+ "The node-name might be given and needs to be unique and not\n" -+ "in use. If there is no node name provided, the zcrypt device\n" -+ "driver will create a new one with pattern zcrypt_\n" -+ "with being the next free number. By default all\n" -+ "adapters, domains and ioctls are initially disabled on this\n" -+ "new device node." -+ }, -+ { -+ .cmd = CMD_DESTROY, -+ .command = "destroy", -+ .function = cmd_destroy, -+ .usage = "zcryptctl destroy ", -+ .description = -+ "Destroy an additional zcrypt device node.\n" -+ "Mark the given zcrypt device node as disposable. The removal\n" -+ "will take place when it is no longer used.", -+ }, -+ { -+ .cmd = CMD_ADD_AP, -+ .command = "addap", -+ .function = cmd_add_del_ap, -+ .usage = "zcryptctl addap ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "add an crypto adapter to be accessible via this node. The\n" -+ "adapter argument may be a number in the range 0-255 or the\n" -+ "symbol ALL.", -+ }, -+ { -+ .cmd = CMD_DEL_AP, -+ .command = "delap", -+ .function = cmd_add_del_ap, -+ .usage = "zcryptctl delap ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "remove a crypto adapter from the allowed adapters list. The\n" -+ "adapter argument may be a number in the range 0-255 or the\n" -+ "symbol ALL.", -+ }, -+ { -+ .cmd = CMD_ADD_DOM, -+ .command = "adddom", -+ .function = cmd_add_del_dom, -+ .usage = "zcryptctl adddom ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "add a crypto domain to be accessible via this node. The\n" -+ "domain argument may be a number in the range 0-255 or the\n" -+ "symbol ALL.", -+ }, -+ { -+ .cmd = CMD_DEL_DOM, -+ .command = "deldom", -+ .function = cmd_add_del_dom, -+ .usage = "zcryptctl deldom ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "remove a crypto domain from the allowed domains list. The\n" -+ "domain argument may be a number in the range 0-255 or the\n" -+ "symbol ALL.", -+ }, -+ { -+ .cmd = CMD_ADD_IOCTL, -+ .command = "addioctl", -+ .function = cmd_add_del_ioctl, -+ .usage = "zcryptctl addioctl ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "add an ioctl number to be accessible via this node. The\n" -+ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n" -+ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n" -+ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n" -+ "or a number in the range 0-255 or the symbol ALL.", -+ }, -+ { -+ .cmd = CMD_DEL_IOCTL, -+ .command = "delioctl", -+ .function = cmd_add_del_ioctl, -+ .usage = "zcryptctl delioctl ", -+ .description = -+ "Update the filter for the specified zcrypt device node and\n" -+ "remove an ioctl number from the allowed ioctls list. The\n" -+ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n" -+ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n" -+ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n" -+ "or a number in the range 0-255 or the symbol ALL.", -+ }, -+ { -+ .cmd = CMD_CONFIG, -+ .command = "config", -+ .function = cmd_config, -+ .usage = "zcryptctl config ", -+ .description = -+ "Process a config file. The given config file is read line by\n" -+ "line and the settings are applied. Syntax is simple:\n" -+ " node=\n" -+ " aps=\n" -+ " doms=\n" -+ " ioctls=\n" -+ "Empty lines are ignored and the '#' marks the rest of the\n" -+ "line as comment.\n" -+ "The node= line creates a new zcrypt device node, the\n" -+ "aps=, doms= and ioctls= lines customize the previously\n" -+ "created node. The symbol ALL is also recognized for aps,\n" -+ "doms, and ioctls.\n" -+ "Each action must fit into one line, spreading over multiple\n" -+ "lines is not supported. But you can use more than one\n" -+ "aps=, doms= and ioctls= lines to customize the very same\n" -+ "node.\n" -+ "Processing stops when a line cannot be parsed or the\n" -+ "current action fails. When the config file has been\n" -+ "processed successful, the zcryptctl return code is 0. A non\n" -+ "zero return code (and some kind of failure message) is\n" -+ "emitted on partial completion.", -+ }, -+ { -+ .cmd = CMD_LISTCONFIG, -+ .command = "listconfig", -+ .function = cmd_list, -+ .usage = "zcryptctl listconfig", -+ .description = -+ "List all currently known additional zcrypt device nodes\n" -+ "in a format suitable for the 'config' command.", -+ }, -+ { -+ .command = NULL, -+ .cmd = 0, -+ } -+}; -+ -+static int get_command_index(const char *cmdstr) -+{ -+ int i; -+ -+ for (i = 0; zcryptctl_cmds[i].command; i++) -+ if (!strcmp(zcryptctl_cmds[i].command, cmdstr)) -+ return i; -+ -+ return -1; -+} -+ -+static void commands_print_help(void) -+{ -+ int i; -+ -+ for (i = 0; zcryptctl_cmds[i].command; i++) -+ if (zcryptctl_cmds[i].usage) -+ printf(" %s\n", zcryptctl_cmds[i].usage); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ int c, cmdindex = -1; -+ int rc = EXIT_SUCCESS; -+ -+ util_prg_init(&prg); -+ util_opt_init(opt_vec, NULL); -+ -+ for (c = 1; c < argc; c++) { -+ cmdindex = get_command_index(argv[c]); -+ if (cmdindex >= 0) -+ break; -+ } -+ -+ while (1) { -+ c = util_opt_getopt_long(argc, argv); -+ if (c == -1) -+ break; -+ switch (c) { -+ case 'h': -+ if (cmdindex < 0) { -+ util_prg_print_help(); -+ commands_print_help(); -+ util_opt_print_help(); -+ } else { -+ printf("Usage: %s\n", -+ zcryptctl_cmds[cmdindex].usage); -+ printf("%s\n", -+ zcryptctl_cmds[cmdindex].description); -+ } -+ return EXIT_SUCCESS; -+ case 'v': -+ util_prg_print_version(); -+ return EXIT_SUCCESS; -+ default: -+ util_opt_print_parse_error(c, argv); -+ return EXIT_FAILURE; -+ } -+ } -+ -+ if (cmdindex < 0) -+ errx(EXIT_FAILURE, "Missing or invalid command argument"); -+ -+ if (check_zcrypt_class_dir() != 0) -+ errx(EXIT_FAILURE, -+ "Directory class/%s is missing in sysfs.\n" -+ "Multiple zcrypt node support is not available", -+ ZCRYPT_NAME); -+ -+ c = zcryptctl_cmds[cmdindex].cmd; -+ switch (c) { -+ case CMD_LIST: -+ case CMD_LISTCONFIG: -+ rc = zcryptctl_cmds[cmdindex].function(c, NULL, NULL); -+ break; -+ case CMD_CREATE: -+ rc = zcryptctl_cmds[cmdindex].function(c, -+ optind + 1 < argc ? -+ argv[optind + 1] : NULL, -+ NULL); -+ break; -+ case CMD_DESTROY: -+ if (optind + 1 >= argc) -+ errx(EXIT_FAILURE, "Missing node name argument"); -+ if (check_nodename(argv[optind + 1]) != 0) -+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", -+ argv[optind + 1]); -+ rc = zcryptctl_cmds[cmdindex].function(c, -+ argv[optind + 1], NULL); -+ break; -+ case CMD_ADD_AP: -+ case CMD_DEL_AP: -+ if (optind + 1 >= argc) -+ errx(EXIT_FAILURE, "Missing node name argument"); -+ if (optind + 2 >= argc) -+ errx(EXIT_FAILURE, "Missing adapter argument"); -+ if (check_nodename(argv[optind + 1]) != 0) -+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", -+ argv[optind + 1]); -+ rc = zcryptctl_cmds[cmdindex].function(c, -+ argv[optind + 1], -+ argv[optind + 2]); -+ break; -+ case CMD_ADD_DOM: -+ case CMD_DEL_DOM: -+ if (optind + 1 >= argc) -+ errx(EXIT_FAILURE, "Missing node name argument"); -+ if (optind + 2 >= argc) -+ errx(EXIT_FAILURE, "Missing domain argument"); -+ if (check_nodename(argv[optind + 1]) != 0) -+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", -+ argv[optind + 1]); -+ rc = zcryptctl_cmds[cmdindex].function(c, -+ argv[optind + 1], -+ argv[optind + 2]); -+ break; -+ case CMD_ADD_IOCTL: -+ case CMD_DEL_IOCTL: -+ if (optind + 1 >= argc) -+ errx(EXIT_FAILURE, "Missing node name argument"); -+ if (optind + 2 >= argc) -+ errx(EXIT_FAILURE, "Missing ioctl argument"); -+ if (check_nodename(argv[optind + 1]) != 0) -+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'", -+ argv[optind + 1]); -+ rc = zcryptctl_cmds[cmdindex].function(c, -+ argv[optind + 1], -+ argv[optind + 2]); -+ break; -+ case CMD_CONFIG: -+ if (optind + 1 >= argc) -+ errx(EXIT_FAILURE, "Missing filename argument"); -+ rc = zcryptctl_cmds[cmdindex].function(c, NULL, -+ argv[optind + 1]); -+ break; -+ default: -+ errx(EXIT_FAILURE, "Unknown command %d", c); -+ } -+ -+ return rc; -+} --- -2.21.3 - - -From d5aab30ab6dd79a47d821b39e9f803d4afe8ed7b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 19 Nov 2018 11:40:35 +0100 -Subject: [PATCH 13/57] lszcrypt: support for alternate zcrypt device drivers - (#1646355) - -Summary: lszcrypt: support for alternate zcrypt device drivers -Description: With kernel 4.19 there comes an extension to the - existing AP bus which supports alternate zcrypt - drivers. For details about this see kernel patch - "s390/zcrypt: AP bus support for alternate - driver(s)". So now lszcrypt displays the driver name - in verbose mode. As some of the information - displayed by lszcrypt was based on sysfs attributes, - which are only available when the default zcrypt - driver is bound to the device, this also needed some - rework. If a sysfs attribute is not available - because of an alternate driver binding (or no - driver) a question mark is printed into the field. -Upstream-ID: 0a0b4c382693cded5652404e8fa2c0e483aa33df ---- - zconf/zcrypt/lszcrypt.8 | 4 +- - zconf/zcrypt/lszcrypt.c | 163 +++++++++++++++++++++++++++------------- - 2 files changed, 112 insertions(+), 55 deletions(-) - -diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8 -index 7196806..826e109 100644 ---- a/zconf/zcrypt/lszcrypt.8 -+++ b/zconf/zcrypt/lszcrypt.8 -@@ -54,8 +54,8 @@ status. - .B -V, --verbose - The verbose level for cryptographic device information. - With this verbose level additional information like hardware card type, --hardware queue depth, pending request queue count, outstanding --request queue count, and installed function facilities are displayed. -+hardware queue depth, pending requests count, installed function -+facilities and driver binding is displayed. - .TP 8 - .B - Specifies a cryptographic device to display. A cryptographic device can be -diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c -index eb3cd6e..580407f 100644 ---- a/zconf/zcrypt/lszcrypt.c -+++ b/zconf/zcrypt/lszcrypt.c -@@ -1,7 +1,7 @@ - /** - * lszcrypt - Display zcrypt devices and configuration settings - * -- * Copyright IBM Corp. 2008, 2017 -+ * Copyright IBM Corp. 2008, 2018 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. -@@ -56,6 +56,25 @@ struct lszcrypt_l *lszcrypt_l = &l; - #define MASK_CLASS_STATELESS 0x00400000 - #define CLASS_STATELESS "restricted function set" - -+/* -+ * facility bits -+ */ -+#define MAX_FAC_BITS 9 -+static struct fac_bits_s { -+ int mask; -+ char c; -+} fac_bits[MAX_FAC_BITS] = { -+ { 0x80000000, 'S' }, -+ { 0x40000000, 'M' }, -+ { 0x20000000, 'C' }, -+ { 0x10000000, 'D' }, -+ { 0x08000000, 'A' }, -+ { 0x04000000, 'X' }, -+ { 0x02000000, 'N' }, -+ { 0x00800000, 'F' }, -+ { 0x00400000, 'R' }, -+}; -+ - /* - * Program configuration - */ -@@ -66,7 +85,7 @@ const struct util_prg prg = { - { - .owner = "IBM Corp.", - .pub_first = 2008, -- .pub_last = 2017, -+ .pub_last = 2018, - }, - UTIL_PRG_COPYRIGHT_END - } -@@ -255,7 +274,8 @@ static void show_capability(const char *id_str) - /* Skip devices, which are not supported by zcrypt layer */ - if (!util_path_is_readable("%s/type", dev) || - !util_path_is_readable("%s/online", dev)) { -- printf("Detailed capability information for %s (hardware type %ld) is not available.\n", card, hwtype); -+ printf("Detailed capability information for %s (hardware type %ld) is not available.\n", -+ card, hwtype); - return; - } - cbuf[0] = '\0'; -@@ -299,11 +319,13 @@ static void show_capability(const char *id_str) - } else if (func_val & MASK_EP11) { - printf("%s", CAP_EP11); - } else { -- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype); -+ printf("Detailed capability information for %s (hardware type %ld) is not available.", -+ card, hwtype); - } - break; - default: -- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype); -+ printf("Detailed capability information for %s (hardware type %ld) is not available.", -+ card, hwtype); - break; - } - printf("\n"); -@@ -315,17 +337,22 @@ static void show_capability(const char *id_str) - static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, - const char *sub_dev) - { -- unsigned long facility; - char buf[256]; -+ unsigned long facility; - -- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev); -- util_rec_set(rec, "type", buf); -+ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev)) -+ util_rec_set(rec, "type", "-"); -+ else -+ util_rec_set(rec, "type", buf); - -- util_file_read_line(buf, sizeof(buf), "%s/%s/online", grp_dev, sub_dev); -- if (strcmp(buf, "0") == 0) -- util_rec_set(rec, "online", "offline"); -+ if (util_file_read_line(buf, sizeof(buf), "%s/%s/online", -+ grp_dev, sub_dev)) -+ util_rec_set(rec, "online", "-"); - else -- util_rec_set(rec, "online", "online"); -+ if (strcmp(buf, "0") == 0) -+ util_rec_set(rec, "online", "offline"); -+ else -+ util_rec_set(rec, "online", "online"); - - util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); - if (facility & MASK_COPRO) -@@ -339,7 +366,7 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, - - util_file_read_line(buf, sizeof(buf), "%s/%s/request_count", - grp_dev, sub_dev); -- util_rec_set(rec, "request_count", buf); -+ util_rec_set(rec, "requests", buf); - } - - /* -@@ -348,20 +375,19 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev, - static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev, - const char *sub_dev) - { -+ int i; - unsigned long facility; -- char buf[256]; -- long depth; -+ char buf[256], afile[PATH_MAX]; -+ long depth, pending1, pending2; - - if (l.verbose == 0) - return; - -- util_file_read_line(buf, sizeof(buf), "%s/%s/pendingq_count", -- grp_dev, sub_dev); -- util_rec_set(rec, "pendingq_count", buf); -- -- util_file_read_line(buf, sizeof(buf), "%s/%s/requestq_count", -- grp_dev, sub_dev); -- util_rec_set(rec, "requestq_count", buf); -+ util_file_read_l(&pending1, 10, "%s/%s/pendingq_count", -+ grp_dev, sub_dev); -+ util_file_read_l(&pending2, 10, "%s/%s/requestq_count", -+ grp_dev, sub_dev); -+ util_rec_set(rec, "pending", "%ld", pending1 + pending2); - - util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev); - util_rec_set(rec, "hwtype", buf); -@@ -370,7 +396,18 @@ static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev, - util_rec_set(rec, "depth", "%02d", depth + 1); - - util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); -- util_rec_set(rec, "facility", "0x%08x", facility); -+ for (i = 0; i < MAX_FAC_BITS; i++) -+ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-'; -+ buf[i] = '\0'; -+ util_rec_set(rec, "facility", buf); -+ -+ snprintf(afile, sizeof(afile), "%s/%s/driver", grp_dev, sub_dev); -+ afile[sizeof(afile) - 1] = '\0'; -+ memset(buf, 0, sizeof(buf)); -+ if (readlink(afile, buf, sizeof(buf)) > 0) -+ util_rec_set(rec, "driver", strrchr(buf, '/') + 1); -+ else -+ util_rec_set(rec, "driver", "-no-driver-"); - } - - /* -@@ -382,9 +419,13 @@ static void show_subdevice(struct util_rec *rec, const char *grp_dev, - if (!util_path_is_dir("%s/%s", grp_dev, sub_dev)) - errx(EXIT_FAILURE, "Error - cryptographic device %s/%s does not exist.", grp_dev, sub_dev); - -- /* Skip devices, which are not supported by zcrypt layer */ -- if (!util_path_is_readable("%s/type", grp_dev) || -- !util_path_is_readable("%s/%s/online", grp_dev, sub_dev)) -+ /* -+ * If not verbose mode, skip devices which are not supported -+ * by the zcrypt layer. -+ */ -+ if (l.verbose == 0 && -+ (!util_path_is_readable("%s/type", grp_dev) || -+ !util_path_is_readable("%s/%s/online", grp_dev, sub_dev))) - return; - - util_rec_set(rec, "card", sub_dev); -@@ -414,11 +455,13 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev) - */ - static void read_rec_default(struct util_rec *rec, const char *grp_dev) - { -- unsigned long facility; - char buf[256]; -+ unsigned long facility; - -- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev); -- util_rec_set(rec, "type", buf); -+ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev)) -+ util_rec_set(rec, "type", "-"); -+ else -+ util_rec_set(rec, "type", buf); - - util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); - if (facility & MASK_COPRO) -@@ -430,14 +473,16 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev) - else - util_rec_set(rec, "mode", "Unknown"); - -- util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev); -- if (strcmp(buf, "0") == 0) -- util_rec_set(rec, "online", "offline"); -+ if (util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev)) -+ util_rec_set(rec, "online", "-"); - else -- util_rec_set(rec, "online", "online"); -+ if (strcmp(buf, "0") == 0) -+ util_rec_set(rec, "online", "offline"); -+ else -+ util_rec_set(rec, "online", "online"); - - util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev); -- util_rec_set(rec, "request_count", buf); -+ util_rec_set(rec, "requests", buf); - } - - /* -@@ -445,18 +490,17 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev) - */ - static void read_rec_verbose(struct util_rec *rec, const char *grp_dev) - { -+ int i; - unsigned long facility; -- char buf[256]; -- long depth; -+ char buf[256], afile[PATH_MAX]; -+ long depth, pending1, pending2; - - if (l.verbose == 0) - return; - -- util_file_read_line(buf, sizeof(buf), "%s/pendingq_count", grp_dev); -- util_rec_set(rec, "pendingq_count", buf); -- -- util_file_read_line(buf, sizeof(buf), "%s/requestq_count", grp_dev); -- util_rec_set(rec, "requestq_count", buf); -+ util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev); -+ util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev); -+ util_rec_set(rec, "pending", "%ld", pending1 + pending2); - - util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev); - util_rec_set(rec, "hwtype", buf); -@@ -465,7 +509,18 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev) - util_rec_set(rec, "depth", "%02d", depth + 1); - - util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev); -- util_rec_set(rec, "facility", "0x%08x", facility); -+ for (i = 0; i < MAX_FAC_BITS; i++) -+ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-'; -+ buf[i] = '\0'; -+ util_rec_set(rec, "facility", buf); -+ -+ snprintf(afile, sizeof(afile), "%s/driver", grp_dev); -+ afile[sizeof(afile) - 1] = '\0'; -+ memset(buf, 0, sizeof(buf)); -+ if (readlink(afile, buf, sizeof(buf)) > 0) -+ util_rec_set(rec, "driver", strrchr(buf, '/') + 1); -+ else -+ util_rec_set(rec, "driver", "-no-driver-"); - } - - /* -@@ -481,9 +536,14 @@ static void show_device(struct util_rec *rec, const char *device) - grp_dev = util_path_sysfs("devices/ap/%s", device); - if (!util_path_is_dir(grp_dev)) - errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device); -- /* Skip devices, which are not supported by zcrypt layer */ -- if (!util_path_is_readable("%s/type", grp_dev) || -- !util_path_is_readable("%s/online", grp_dev)) { -+ -+ /* -+ * If not verbose mode, skip devices which are not supported -+ * by the zcrypt layer. -+ */ -+ if (l.verbose == 0 && -+ (!util_path_is_readable("%s/type", grp_dev) || -+ !util_path_is_readable("%s/online", grp_dev))) { - goto out_free; - } - util_rec_set(rec, "card", card); -@@ -506,8 +566,7 @@ static void define_rec_default(struct util_rec *rec) - util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE"); - util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE"); - util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 7, "STATUS"); -- util_rec_def(rec, "request_count", UTIL_REC_ALIGN_RIGHT, 11, -- "REQUEST_CNT"); -+ util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS"); - } - - /* -@@ -517,13 +576,11 @@ static void define_rec_verbose(struct util_rec *rec) - { - if (l.verbose == 0) - return; -- util_rec_def(rec, "pendingq_count", UTIL_REC_ALIGN_RIGHT, 12, -- "PENDINGQ_CNT"); -- util_rec_def(rec, "requestq_count", UTIL_REC_ALIGN_RIGHT, 12, -- "REQUESTQ_CNT"); -- util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 7, "HW_TYPE"); -- util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 7, "Q_DEPTH"); -+ util_rec_def(rec, "pending", UTIL_REC_ALIGN_RIGHT, 8, "PENDING"); -+ util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 6, "HWTYPE"); -+ util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 6, "QDEPTH"); - util_rec_def(rec, "facility", UTIL_REC_ALIGN_LEFT, 10, "FUNCTIONS"); -+ util_rec_def(rec, "driver", UTIL_REC_ALIGN_LEFT, 11, "DRIVER"); - } - - /* --- -2.21.3 - - -From 47e15616b0c81f85918794a0af2073aac6455826 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 11 Dec 2018 09:46:40 +0100 -Subject: [PATCH 14/57] zkey: Fails to run commands generated by 'zkey - cryptsetup' (#1650628) - -Description: zkey: Fails to run commands generated by 'zkey cryptsetup' -Symptom: Fails to run commands generated by 'zkey cryptsetup'. -Problem: When using 'zkey cryptsetup' with --run option the - execution of the generated commands may fail, when - the executable to be run is located in '/sbin'. -Solution: Include /sbin into PATH when executing commands. -Reproduction: Use 'zkey cryptsetup' with option --run on a distribution - where 'cryptsetup' is located in '/sbin'. -Upstream-ID: 9100327092c470c2e5b5819087c8094822a1c751 ---- - zkey/keystore.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zkey/keystore.c b/zkey/keystore.c -index 11f555c..a4ad634 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -3235,7 +3235,7 @@ static int _keystore_execute_cmd(const char *cmd, - { - int rc; - -- rc = setenv("PATH", "/bin:/usr/bin:/usr/sbin", 1); -+ rc = setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1); - if (rc < 0) - return rc; - --- -2.21.3 - - -From 3328fadb3147a24bae10495259fa7fd00b973eec Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 11 Dec 2018 10:14:05 +0100 -Subject: [PATCH 15/57] zkey: Enhance error message about missing CCA library - (#1655134) - -Description: zkey: Enhance error message about missing CCA library. -Symptom: "zkey-cryptsetup reencipher" fails with missing - library and confusing error message. -Problem: The "zkey reencipher" command as well as the "zkey-cryptsetup - reencipher" command requires the IBM CCA Host Libraries and - Tools package to be installed. This is a closed source - library that is not distributed by the distributions, but - must be downloaded separately from an IBM web page. -Solution: Enhance the error message to point to the web page where - the package can be downloaded. -Reproduction: Run the "zkey-cryptsetup reencipher" or "zkey reencipher" - command without having installed the IBM CCA Host Libraries - and Tools package. ---- - zkey/pkey.c | 13 +++++++++---- - zkey/zkey-cryptsetup.1 | 3 ++- - zkey/zkey.1 | 3 ++- - 3 files changed, 13 insertions(+), 6 deletions(-) - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index fe43d02..15e606a 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -48,6 +48,7 @@ - * Definitions for the CCA library - */ - #define CCA_LIBRARY_NAME "libcsulcca.so" -+#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" - - #define DEFAULT_KEYBITS 256 - -@@ -71,16 +72,20 @@ int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose) - /* Load the CCA library */ - *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); - if (*lib_csulcca == NULL) { -- warnx("%s\nEnsure that the IBM CCA Host Libraries and " -- "Tools are installed properly", dlerror()); -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM CCA Host Libraries and " -+ "Tools.\nFor the supported environments and downloads, " -+ "see:\n%s", CCA_WEB_PAGE); - return -ELIBACC; - } - - /* Get the Key Token Change function */ - *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC"); - if (*dll_CSNBKTC == NULL) { -- warnx("%s\nEnsure that the IBM CCA Host Libraries and " -- "Tools are installed properly", dlerror()); -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM CCA Host Libraries and " -+ "Tools.\nFor the supported environments and downloads, " -+ "see:\n%s", CCA_WEB_PAGE); - dlclose(*lib_csulcca); - *lib_csulcca = NULL; - return -ELIBACC; -diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 -index bf92e21..988ef76 100644 ---- a/zkey/zkey-cryptsetup.1 -+++ b/zkey/zkey-cryptsetup.1 -@@ -182,7 +182,8 @@ behave in the same way as with \fBcryptsetup\fP. - .PP - .B Note: - The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) --to be installed. -+to be installed. For the supported environments and downloads, see: -+\fIhttp://www.ibm.com/security/cryptocards\fP - . - . - . -diff --git a/zkey/zkey.1 b/zkey/zkey.1 -index c9f7906..0837b27 100644 ---- a/zkey/zkey.1 -+++ b/zkey/zkey.1 -@@ -282,7 +282,8 @@ a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case. - .PP - .B Note: - The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) --to be installed. -+to be installed. For the supported environments and downloads, see: -+\fIhttp://www.ibm.com/security/cryptocards\fP - . - .SS "Import existing AES secure keys into the secure key repository" - . --- -2.21.3 - - -From ba7d612c055c887d5cbb596a369d32f00c47711d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Jan 2019 13:06:21 +0100 -Subject: [PATCH 16/57] zfcpdump: add install script for zfcpdump kernel - (#1600480) - -Summary: zfcpdump: add install script for zfcpdump kernel -Description: zipl expects to find the zfcpdump kernel at a specific location - (usually /usr/lib/s390-tools/zfcpdump/zfcpdump-image). During - kernel installation however, the kernels are installed to /boot. - In order for zipl to find the image add a script that manages a - link to the latest installed zfcpdump kernel at said location. ---- - .gitignore | 3 +- - common.mak | 7 +- - zfcpdump/10-zfcpdump.install.in | 114 ++++++++++++++++++++++++++++++++ - zfcpdump/Makefile | 22 ++++-- - zipl/include/zipl.h | 7 -- - zipl/src/Makefile | 7 +- - zipl/src/job.c | 10 +-- - 7 files changed, 143 insertions(+), 27 deletions(-) - create mode 100644 zfcpdump/10-zfcpdump.install.in - -diff --git a/.gitignore b/.gitignore -index 042233f..6cbec8c 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -76,7 +76,8 @@ zdsfs/zdsfs - zdump/zgetdump - zfcpdump/cpioinit - zfcpdump/zfcpdump_part --zfcpdump/zfcpdump_part.rd -+zfcpdump/zfcpdump-initrd -+zfcpdump/10-zfcpdump.install - ziomon/ziomon_mgr - ziomon/ziomon_util - ziomon/ziomon_zfcpdd -diff --git a/common.mak b/common.mak -index 6f0990e..6029687 100644 ---- a/common.mak -+++ b/common.mak -@@ -182,9 +182,10 @@ GROUP = $(shell id -gn) - export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP - - # Special defines for zfcpdump --ZFCPDUMP_PART_IMAGE = zfcpdump_part.image --ZFCPDUMP_PART_RD = zfcpdump_part.rd --export ZFCPDUMP_DIR ZFCPDUMP_PART_IMAGE ZFCPDUMP_PART_RD -+ZFCPDUMP_IMAGE = zfcpdump-image -+ZFCPDUMP_INITRD = zfcpdump-initrd -+ZFCPDUMP_FLAVOR = zfcpdump -+export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_INITRD ZFCPDUMP_FLAVOR - - CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) - HOSTCFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) -diff --git a/zfcpdump/10-zfcpdump.install.in b/zfcpdump/10-zfcpdump.install.in -new file mode 100644 -index 0000000..37fde3b ---- /dev/null -+++ b/zfcpdump/10-zfcpdump.install.in -@@ -0,0 +1,114 @@ -+#!/bin/bash -+# -+# 10-zfcpdump.install - Installation script to handle zfcpdump kernels -+# -+# Copyright IBM Corp. 2018 -+# -+# s390-tools is free software; you can redistribute it and/or modify -+# it under the terms of the MIT license. See LICENSE for details. -+# -+# -+# This script supports two modes: -+# -+# 1) Installing the images to /boot// -+# subdirectories, i.e. BOOT_DIR_ABS, as recommended by the BLS. -+# In this case file names are taken over from the original files. -+# -+# 2) Installing the images directly to /boot. In this case the files are -+# renamed to -. -+# -+# The existence of BOOT_DIR_ABS is taken as trigger to switch between both -+# modes. -+# -+# The KERNEL_VERSION is assumed to contain '@flavor@' to identify the image -+# as a zfcpdump kernel. -+ -+COMMAND="$1" -+KERNEL_VERSION="$2" -+BOOT_DIR_ABS="$3" -+KERNEL_IMAGE="$4" -+ -+# Location zipl looks for the zfcpdump kernel -+ZFCPDUMP_IMAGE='@zfcpdump_image@' -+ -+# Only handle zfcpdump kernels -+echo "$KERNEL_VERSION" | grep -q '@flavor@' || exit 0 -+ -+case "$COMMAND" in -+ add) -+ KERNEL_DIR="$(dirname $KERNEL_IMAGE)" -+ KERNEL_NAME="$(basename $KERNEL_IMAGE)" -+ -+ for f in \ -+ "$KERNEL_IMAGE" \ -+ "$KERNEL_DIR"/System.map \ -+ "$KERNEL_DIR"/config \ -+ "$KERNEL_DIR"/zImage.stub -+ do -+ test -e "$f" || continue -+ test -d "$BOOT_DIR_ABS" \ -+ && DEST="$BOOT_DIR_ABS/$(basename $f)" \ -+ || DEST="/boot/$(basename $f)-$KERNEL_VERSION" -+ -+ cp -aT "$f" "$DEST" -+ test $(command -v restorecon) && restorecon -R "$DEST" -+ done -+ -+ # hmac file need special treatment -+ f="$KERNEL_DIR/.$KERNEL_NAME.hmac" -+ if [ -e "$f" ]; then -+ test -d "$BOOT_DIR_ABS" \ -+ && DEST="$BOOT_DIR_ABS/$(basename $f)" \ -+ || DEST="/boot/.$KERNEL_NAME-$KERNEL_VERSION.hmac" -+ -+ cp -aT "$f" "$DEST" -+ test $(command -v restorecon) && restorecon -R "$DEST" -+ fi -+ -+ # Set link so zipl finds the kernel -+ test -d "$BOOT_DIR_ABS" \ -+ && TARGET="$BOOT_DIR_ABS/$KERNEL_NAME" \ -+ || TARGET="/boot/$KERNEL_NAME-$KERNEL_VERSION" -+ ln -sf "$TARGET" "$ZFCPDUMP_IMAGE" -+ ;; -+ -+ remove) -+ # On removal -+ # $KERNEL_IMAGE is empty -> $KERNEL_NAME is empty -> rebuild it -+ KERNEL_NAME="$(basename $(readlink $ZFCPDUMP_IMAGE))" -+ if [ -d "$BOOT_DIR_ABS" ]; then -+ INSTALL_DIR="$(dirname $BOOT_DIR_ABS)" -+ else -+ INSTALL_DIR="/boot/" -+ KERNEL_NAME="$(echo $KERNEL_NAME \ -+ | sed -e "s#\(.*\)-$KERNEL_VERSION#\1#")" -+ fi -+ -+ for f in $(find "$INSTALL_DIR" -name "*$KERNEL_VERSION*"); do -+ rm -rf "$f" -+ done -+ -+ # Update link to latest remaining zfcpdump kernel. -+ if [ $(readlink "$ZFCPDUMP_IMAGE" | grep "$KERNEL_VERSION") ] -+ then -+ NEXT_IMAGE=$( \ -+ find "$INSTALL_DIR" -type f \ -+ | grep '@flavor@' \ -+ | grep "$KERNEL_NAME" \ -+ | grep -v "hmac" \ -+ | sort -V \ -+ | tail -n1 ) -+ -+ test $NEXT_IMAGE \ -+ && ln -sf "$NEXT_IMAGE" "$ZFCPDUMP_IMAGE" \ -+ || rm -f "$ZFCPDUMP_IMAGE" -+ fi -+ ;; -+ *) -+ ;; -+esac -+ -+# Prevent execution of all other scripts. -+# The zfcpdump kernel is stripped down to the bare minimum needed for -+# dumping. It is not supposed to be used for any other purpose. -+exit 77 -diff --git a/zfcpdump/Makefile b/zfcpdump/Makefile -index 5fcb073..cb2fd41 100644 ---- a/zfcpdump/Makefile -+++ b/zfcpdump/Makefile -@@ -1,6 +1,7 @@ - include ../common.mak - - CPIOINIT = $(call echocmd," CPIOINI ",/$@)./cpioinit -+INSTALL_SCRIPTS = 10-zfcpdump.install - - ifeq (${HAVE_LIBC_STATIC},0) - -@@ -20,7 +21,7 @@ check_dep: - "HAVE_LIBC_STATIC=0", \ - "-static") - --all: check_dep $(ZFCPDUMP_PART_RD) -+all: check_dep $(ZFCPDUMP_INITRD) scripts - - cpioinit: cpioinit.c - $(HOSTCC) $(HOSTCFLAGS) -o $@ $^ -@@ -29,17 +30,26 @@ zfcpdump_part: zfcpdump.o zfcpdump_part.o - $(LINK) $(ALL_LDFLAGS) $^ -static -o $@ - $(STRIP) -s $@ - --$(ZFCPDUMP_PART_RD): cpioinit zfcpdump_part -+$(ZFCPDUMP_INITRD): cpioinit zfcpdump_part - $(CPIOINIT) zfcpdump_part > $@.tmp - $(GZIP) -f $@.tmp -- $(MV) $@.tmp.gz $(ZFCPDUMP_PART_RD) -+ $(MV) $@.tmp.gz $(ZFCPDUMP_INITRD) -+ -+scripts: $(INSTALL_SCRIPTS) -+ chmod +x $(INSTALL_SCRIPTS) - - install: all -- $(INSTALL) -m 611 $(ZFCPDUMP_PART_RD) $(DESTDIR)$(ZFCPDUMP_DIR) -+ $(INSTALL) -m 611 $(ZFCPDUMP_INITRD) $(DESTDIR)$(ZFCPDUMP_DIR) - -+%: %.in -+ zfcpdump_image=$(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE); \ -+ $(SED) -e "s#@zfcpdump_image@#$$zfcpdump_image#g" \ -+ -e "s#@flavor@#$(ZFCPDUMP_FLAVOR)#g" \ -+ < $< > $@ - endif - - clean: -- rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_PART_RD) -+ rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_INITRD) \ -+ $(INSTALL_SCRIPTS) - --.PHONY: all clean install check_dep -+.PHONY: all clean install check_dep scripts -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index 2aed54f..770801e 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -48,13 +48,6 @@ - #define MENU_DEFAULT_PROMPT 0 - #define MENU_DEFAULT_TIMEOUT 0 - --#define FSDUMP_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_IMAGE) --#define FSDUMP_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_RD) --#define FSDUMP_PART_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" \ -- STRINGIFY(ZFCPDUMP_PART_IMAGE) --#define FSDUMP_PART_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" \ -- STRINGIFY(ZFCPDUMP_PART_RD) -- - #define MAX_DUMP_VOLUMES 32 - - /* Internal component load address type */ -diff --git a/zipl/src/Makefile b/zipl/src/Makefile -index 1634c0d..be14fce 100644 ---- a/zipl/src/Makefile -+++ b/zipl/src/Makefile -@@ -2,11 +2,8 @@ - include ../../common.mak - - ALL_CPPFLAGS += -I../include -I../boot \ -- -DZFCPDUMP_DIR=$(ZFCPDUMP_DIR) \ -- -DZFCPDUMP_FS_IMAGE=$(ZFCPDUMP_FS_IMAGE) \ -- -DZFCPDUMP_FS_RD=$(ZFCPDUMP_FS_RD) \ -- -DZFCPDUMP_PART_IMAGE=$(ZFCPDUMP_PART_IMAGE) \ -- -DZFCPDUMP_PART_RD=$(ZFCPDUMP_PART_RD) \ -+ -DZFCPDUMP_IMAGE="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE))" \ -+ -DZFCPDUMP_INITRD="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_INITRD))" \ - -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) - ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index e6d2981..22d2549 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -874,22 +874,22 @@ check_job_dump_images(struct job_dump_data* dump, char* name) - { - int rc; - /* Add data needed to convert fs dump job to IPL job */ -- rc = misc_check_readable_file(FSDUMP_PART_IMAGE); -+ rc = misc_check_readable_file(ZFCPDUMP_IMAGE); - if (rc) { - error_text("Need external file '%s' for partition dump", -- FSDUMP_PART_IMAGE); -+ ZFCPDUMP_IMAGE); - return rc; - } -- dump->image = misc_strdup(FSDUMP_PART_IMAGE); -+ dump->image = misc_strdup(ZFCPDUMP_IMAGE); - if (dump->image == NULL) - return -1; - dump->image_addr = DEFAULT_IMAGE_ADDRESS; - - /* Ramdisk is no longer required with new initramfs dump system */ -- if (misc_check_readable_file(FSDUMP_PART_RAMDISK)) -+ if (misc_check_readable_file(ZFCPDUMP_INITRD)) - dump->ramdisk = NULL; - else { -- dump->ramdisk = misc_strdup(FSDUMP_PART_RAMDISK); -+ dump->ramdisk = misc_strdup(ZFCPDUMP_INITRD); - if (dump->ramdisk == NULL) - return -1; - dump->ramdisk_addr = UNSPECIFIED_ADDRESS; --- -2.21.3 - - -From eef1d70b8bd0ec96aa90f5dad9795196c85382ce Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 9 Jan 2019 13:58:10 +0100 -Subject: [PATCH 17/57] pkey: Support autoloading kernel pkey module (#1664632) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The module is loaded automatically based on CPU features, but it's still -too late in some use cases. Thus allow distros to use explicit loading. - -See also: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=63c19be095d0f8eb8992674991e44b4228bd4179 - -Closes: https://github.com/ibm-s390-tools/s390-tools/pull/51 -Signed-off-by: Dan Horák -Reviewed-by: Ingo Franzki -Signed-off-by: Jan Höppner -(cherry picked from commit dffd41943e5c01be2f343da7726edabf9d2ec05e) ---- - etc/modules-load.d/s390-pkey.conf | 2 ++ - 1 file changed, 2 insertions(+) - create mode 100644 etc/modules-load.d/s390-pkey.conf - -diff --git a/etc/modules-load.d/s390-pkey.conf b/etc/modules-load.d/s390-pkey.conf -new file mode 100644 -index 0000000..972a099 ---- /dev/null -+++ b/etc/modules-load.d/s390-pkey.conf -@@ -0,0 +1,2 @@ -+# Load protected key support module on s390 early at boot -+pkey --- -2.21.3 - - -From 7a3ba6fb39f015da0ee10e1dc0e14ae9fcf6347f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 21 May 2019 13:39:08 +0200 -Subject: [PATCH 18/57] zipl: Secure Boot support for SCSI IPL (#1659401) - -Description: The Secure Boot firmware feature ensures that only signed - and verified code is executed during IPL. - This feature adds support for secure boot to the zipl - tool and internal loader. - -Upstream-ID: e764f460c457ab2a6000acb5f2eb7169866ce192 -Upstream-ID: 6825645a21a3221629e7d5aa9d5cfbf6e3bc4188 -Upstream-ID: 0c1a63ce5ec6059374d9f8dd55bffbb42ef9bd45 -Upstream-ID: 7c7e10ed8fb048efc4e0cd91b0f6fa704fba128e -Upstream-ID: 58a7462f65d85726168303bd58e7472bd4c82731 -Upstream-ID: 75d4317f208da1e25afd520fe43a1786333388ad -Upstream-ID: dc2e4395506956a99819484d8938e877c4c2cd88 -Upstream-ID: 7e7a77675d2b88b3d516d60aa64f2b1264b25117 -Upstream-ID: 331b54d573c38c7eed6617eef69cf296c2e86d6d -Upstream-ID: 6ea645b3453264a0f1a60955c4476dab54035f88 ---- - zipl/boot/Makefile | 26 ++- - zipl/boot/error.h | 3 + - zipl/boot/libc.c | 20 ++ - zipl/boot/libc.h | 1 + - zipl/boot/s390.h | 5 + - zipl/boot/stage2.c | 9 +- - zipl/boot/stage2.h | 3 +- - zipl/boot/stage2.lds | 5 +- - zipl/boot/stage3.c | 114 ++++++++--- - zipl/boot/stage3.h | 142 ++++++++++++++ - zipl/boot/stage3.lds | 38 ++-- - zipl/include/boot.h | 11 +- - zipl/include/bootmap.h | 31 +++ - zipl/include/job.h | 1 + - zipl/include/misc.h | 1 + - zipl/include/scan.h | 3 +- - zipl/include/zipl.h | 15 +- - zipl/man/zipl.8 | 15 ++ - zipl/man/zipl.conf.5 | 27 +++ - zipl/src/Makefile | 7 +- - zipl/src/boot.c | 33 ++-- - zipl/src/bootmap.c | 417 +++++++++++++++++++++++++++++++++-------- - zipl/src/job.c | 44 ++++- - zipl/src/misc.c | 1 - - zipl/src/scan.c | 34 ++-- - 25 files changed, 837 insertions(+), 169 deletions(-) - -diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile -index 52b3a23..da7e95f 100644 ---- a/zipl/boot/Makefile -+++ b/zipl/boot/Makefile -@@ -13,10 +13,9 @@ FILES = fba0.bin fba1b.bin fba2.bin \ - eckd0_ldl.bin eckd0_cdl.bin \ - eckd1.bin eckd1b.bin eckd2.bin \ - tape0.bin \ -- eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin \ -- stage3.bin -+ eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin - --all: data.o data.h tape0.bin -+all: data.o data.h tape0.bin stage3.bin - - # Prevent make from using some default rules... - %: %.S -@@ -44,7 +43,7 @@ eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \ - fba2.exec: head.o stage2.o cio.o fba2.o libc.o menu.o sclp.o \ - kdump2.o kdump.o entry.o - stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ -- kdump.o entry.o -+ kdump.o entry.o stage3.lds - - %.exec: %.o - @STAGE=$$( \ -@@ -77,6 +76,22 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ - --only-section=.fixup \ - $< $@ - -+stage3.bin: stage3.exec -+ $(OBJCOPY) -O binary \ -+ --only-section=.stage2.head \ -+ --only-section=.text.dummy \ -+ --only-section=.text.start \ -+ --only-section=.text \ -+ --only-section=.ex_table \ -+ --only-section=.data \ -+ --only-section=.rodata.str1.2 \ -+ --only-section=.rodata \ -+ --only-section=.stage2dump.tail \ -+ --only-section=.eckd2dump_mv.tail \ -+ --only-section=.fixup \ -+ --pad-to=0xf000 \ -+ $< $@ -+ - data.o: $(FILES) - $(LD) $(NO_PIE_LDFLAGS) -r -b binary -o data.o $(FILES) - -@@ -86,6 +101,7 @@ data.h: data.o - echo "extern char $$SYMBOL;" >>data.h; done - - clean: -- rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy -+ rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \ -+ stage3.bin - - .PHONY: all clean -diff --git a/zipl/boot/error.h b/zipl/boot/error.h -index 93fd748..715973e 100644 ---- a/zipl/boot/error.h -+++ b/zipl/boot/error.h -@@ -31,6 +31,9 @@ - /* Internal error */ - #define EINTERNAL 0x00004511 - -+/* Secure IPL error */ -+#define ESECUREBOOT 0x00004512 -+ - /* kdump: No operating system information was found */ - #define EOS_INFO_MISSING 0x00004520 - -diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c -index f86f62a..5944c4e 100644 ---- a/zipl/boot/libc.c -+++ b/zipl/boot/libc.c -@@ -58,6 +58,26 @@ void *memcpy(void *dest, const void *src, unsigned long n) - return dest; - } - -+/* -+ * Move @n bytes of memory from @src to @dest. The memory regions may overlap. -+ */ -+void *memmove(void *dest, const void *src, unsigned long n) -+{ -+ const char *s = src; -+ char *d = dest; -+ -+ if (s < d) { -+ d += n; -+ s += n; -+ while (n--) -+ *--d = *--s; -+ } else { -+ while (n--) -+ *d++ = *s++; -+ } -+ return dest; -+} -+ - /* - * Copy string - */ -diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h -index b05a116..44097fa 100644 ---- a/zipl/boot/libc.h -+++ b/zipl/boot/libc.h -@@ -49,6 +49,7 @@ typedef unsigned char uint8_t; - void printf(const char *, ...); - void sprintf(char *, const char *, ...); - void *memcpy(void *, const void *, unsigned long); -+void *memmove(void *, const void *, unsigned long); - void *memset(void *, int c, unsigned long); - char *strcat(char *, const char *); - int strncmp(const char *, const char *, unsigned long); -diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h -index 279d16b..6e5a7ae 100644 ---- a/zipl/boot/s390.h -+++ b/zipl/boot/s390.h -@@ -35,6 +35,11 @@ struct psw_t { - uint64_t addr; - } __aligned(8); - -+struct psw32_t { -+ uint32_t mask; -+ uint32_t addr; -+} __aligned(8); -+ - void load_wait_psw(uint64_t, struct psw_t *); - - struct _lowcore { -diff --git a/zipl/boot/stage2.c b/zipl/boot/stage2.c -index 02d644e..99e1bd1 100644 ---- a/zipl/boot/stage2.c -+++ b/zipl/boot/stage2.c -@@ -115,8 +115,13 @@ void start(void) - /* skip header */ - entry = (struct component_entry *) - (load_address + sizeof(struct component_header)); -- -- while (entry->type == COMPONENT_LOAD) { -+ while (entry->type == COMPONENT_LOAD || -+ entry->type == COMPONENT_SIGNATURE) { -+ if (entry->type == COMPONENT_SIGNATURE) { -+ /* Skip unhandled signature components */ -+ entry++; -+ continue; -+ } - load_address = (void *)(unsigned long) - entry->address.load_address[1]; - load_blocklist(entry, subchannel_id, load_address); -diff --git a/zipl/boot/stage2.h b/zipl/boot/stage2.h -index b79c798..8fd5bd3 100644 ---- a/zipl/boot/stage2.h -+++ b/zipl/boot/stage2.h -@@ -61,7 +61,8 @@ struct component_entry { - - typedef enum { - COMPONENT_EXECUTE = 0x01, -- COMPONENT_LOAD = 0x02 -+ COMPONENT_LOAD = 0x02, -+ COMPONENT_SIGNATURE = 0x03 - } component_type; - - struct stage2_descr { -diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds -index befe8e2..41efa79 100644 ---- a/zipl/boot/stage2.lds -+++ b/zipl/boot/stage2.lds -@@ -9,7 +9,8 @@ - * 0x2000-0x4fff Sections (load): head, text, data, rodata, rodata.str - * 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes) - * 0x5200-0x5fff Sections: bss -- * 0x6000-0x9fff Memory allocation (heap) -+ * 0x6000-0x8fff Memory allocation (heap) -+ * 0x9000-0x9fff Memory to load stage3 parameter to - * 0xa000-0xdfff Memory to load stage3 to - * 0xe000-0xffff Stack - * -@@ -52,7 +53,7 @@ SECTIONS - - . = 0x6000; - __heap_start = .; -- . = 0xa000; -+ . = 0x9000; - __heap_stop = .; - - . = 0xf000; -diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c -index eb02627..f7a9597 100644 ---- a/zipl/boot/stage3.c -+++ b/zipl/boot/stage3.c -@@ -12,35 +12,15 @@ - #include "libc.h" - #include "s390.h" - #include "stage3.h" -+#include "error.h" - --/* -- * 48 Byte dummy space for external symbols -- * _parm_addr; address of parmline -- * _initrd_addr; address of initrd -- * _initrd_len; length of initrd -- * _load_psw; load psw of kernel -- * _extra_parm; use extra parm line mechanism? -- * stage3_flags; flags (e.g. STAGE3_FLAG_KDUMP) -- * -- * needed to blow up the binary and leave room -- */ --__attribute__ ((section(".text.dummy"))) void _dummy(void) --{ -- asm volatile( -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ".long 0x00000000\n" -- ); --} -+#define for_each_rb_entry(entry, rb) \ -+ for (entry = rb->entries; \ -+ (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \ -+ entry++) -+ -+static const char *msg_sipl_inval = "Secure boot failure: invalid load address"; -+static const char *msg_sipl_unverified = "Secure boot failure: unverified load address"; - - static unsigned char ebc_037[256] = { - /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ -@@ -221,6 +201,64 @@ start_kernel(void) - : [psw] "a" (psw) ); - } - -+unsigned int -+is_verified_address(unsigned long image_addr) -+{ -+ struct ipl_rb_component_entry *comp; -+ struct ipl_rb_components *comps; -+ struct ipl_pl_hdr *pl_hdr; -+ struct ipl_rl_hdr *rl_hdr; -+ struct ipl_rb_hdr *rb_hdr; -+ unsigned long tmp; -+ void *rl_end; -+ -+ /* -+ * There is an IPL report, to find it load the pointer to the -+ * IPL parameter information block from lowcore and skip past -+ * the IPL parameter list, then align the address to a double -+ * word boundary. -+ */ -+ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; -+ pl_hdr = (struct ipl_pl_hdr *) tmp; -+ tmp = (tmp + pl_hdr->len + 7) & -8UL; -+ rl_hdr = (struct ipl_rl_hdr *) tmp; -+ /* Walk through the IPL report blocks in the IPL Report list */ -+ comps = NULL; -+ rl_end = (void *) rl_hdr + rl_hdr->len; -+ rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr); -+ while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end && -+ (void *) rb_hdr + rb_hdr->len <= rl_end) { -+ switch (rb_hdr->rbt) { -+ case IPL_RBT_COMPONENTS: -+ comps = (struct ipl_rb_components *) rb_hdr; -+ break; -+ default: -+ break; -+ } -+ -+ rb_hdr = (void *) rb_hdr + rb_hdr->len; -+ } -+ for_each_rb_entry(comp, comps) { -+ if (image_addr == comp->addr && -+ comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED && -+ comp->flags & IPL_RB_COMPONENT_FLAG_VERIFIED) -+ return 1; -+ } -+ return 0; -+} -+ -+unsigned int -+secure_boot_enabled() -+{ -+ struct ipl_pl_hdr *pl_hdr; -+ unsigned long tmp; -+ -+ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; -+ pl_hdr = (struct ipl_pl_hdr *) tmp; -+ -+ return pl_hdr->flags & IPL_FLAG_SECURE; -+} -+ - void start(void) - { - unsigned int subchannel_id; -@@ -228,6 +266,26 @@ void start(void) - unsigned char *command_line = (unsigned char *)COMMAND_LINE; - unsigned int begin = 0, end = 0, length = 0; - -+ /* -+ * IPL process is secure we have to use default IPL values and -+ * check if the psw jump address is within at the start of a -+ * verified component. If it is not IPL is aborted. -+ */ -+ if (secure_boot_enabled()) { -+ if (_image_addr != DEFAULT_IMAGE_ADDR || -+ _load_psw != DEFAULT_PSW_LOAD) -+ panic(ESECUREBOOT, "%s", msg_sipl_inval); -+ -+ if (!is_verified_address(_load_psw & PSW_ADDR_MASK)) -+ panic(ESECUREBOOT, "%s", msg_sipl_unverified); -+ } -+ /* -+ * cut the kernel header -+ */ -+ memmove((void *)_image_addr, -+ (void *)_image_addr + KERNEL_HEADER_SIZE, -+ _image_len - KERNEL_HEADER_SIZE); -+ - /* store subchannel ID into low core and into new kernel space */ - subchannel_id = S390_lowcore.subchannel_id; - *(unsigned int *)__LC_IPLDEV = subchannel_id; -diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h -index cdc0d77..91477b1 100644 ---- a/zipl/boot/stage3.h -+++ b/zipl/boot/stage3.h -@@ -27,14 +27,156 @@ - #define STAGE3_FLAG_SCSI 0x0001000000000000ULL - #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL - -+#define IPL_FLAG_SECURE 0x40 -+ -+#define DEFAULT_IMAGE_ADDR 0x10000 -+#define DEFAULT_PSW_LOAD 0x0008000080010000L -+#define PSW_ADDR_MASK 0x000000007FFFFFFFL -+#define KERNEL_HEADER_SIZE 65536 -+ - #define UNSPECIFIED_ADDRESS -1ULL - -+ -+/* IPL Parameter List header */ -+struct ipl_pl_hdr { -+ uint32_t len; -+ uint8_t flags; -+ uint8_t reserved1[2]; -+ uint8_t version; -+} __packed; -+ -+/* IPL Parameter Block header */ -+struct ipl_pb_hdr { -+ uint32_t len; -+ uint8_t pbt; -+} __packed; -+ -+/* IPL Parameter Block 0 with common fields */ -+struct ipl_pb0_common { -+ uint32_t len; -+ uint8_t pbt; -+ uint8_t flags; -+ uint8_t reserved1[2]; -+ uint8_t loadparm[8]; -+ uint8_t reserved2[84]; -+} __packed; -+ -+/* IPL Parameter Block 0 for FCP */ -+struct ipl_pb0_fcp { -+ uint32_t len; -+ uint8_t pbt; -+ uint8_t reserved1[3]; -+ uint8_t loadparm[8]; -+ uint8_t reserved2[304]; -+ uint8_t opt; -+ uint8_t reserved3[3]; -+ uint8_t cssid; -+ uint8_t reserved4[1]; -+ uint8_t devno; -+ uint8_t reserved5[4]; -+ uint64_t wwpn; -+ uint64_t lun; -+ uint32_t bootprog; -+ uint8_t reserved6[12]; -+ uint64_t br_lba; -+ uint32_t scp_data_len; -+ uint8_t reserved7[260]; -+ uint8_t scp_data[]; -+} __packed; -+ -+/* IPL Parameter Block 0 for CCW */ -+struct ipl_pb0_ccw { -+ uint32_t len; -+ uint8_t pbt; -+ uint8_t flags; -+ uint8_t reserved1[2]; -+ uint8_t loadparm[8]; -+ uint8_t reserved2[84]; -+ uint16_t reserved3 : 13; -+ uint8_t ssid : 3; -+ uint16_t devno; -+ uint8_t vm_flags; -+ uint8_t reserved4[3]; -+ uint32_t vm_parm_len; -+ uint8_t nss_name[8]; -+ uint8_t vm_parm[64]; -+ uint8_t reserved5[8]; -+} __packed; -+ -+struct ipl_parameter_block { -+ struct ipl_pl_hdr hdr; -+ union { -+ struct ipl_pb_hdr pb0_hdr; -+ struct ipl_pb0_common common; -+ struct ipl_pb0_fcp fcp; -+ struct ipl_pb0_ccw ccw; -+ char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)]; -+ }; -+} __packed __aligned(PAGE_SIZE); -+ -+/* IPL Report List header */ -+struct ipl_rl_hdr { -+ uint32_t len; -+ uint8_t flags; -+ uint8_t reserved1[2]; -+ uint8_t version; -+ uint8_t reserved2[8]; -+} __packed; -+ -+/* IPL Report Block header */ -+struct ipl_rb_hdr { -+ uint32_t len; -+ uint8_t rbt; -+ uint8_t reserved1[11]; -+} __packed; -+ -+/* IPL Report Block types */ -+enum ipl_rbt { -+ IPL_RBT_CERTIFICATES = 1, -+ IPL_RBT_COMPONENTS = 2, -+}; -+ -+/* IPL Report Block for the certificate list */ -+struct ipl_rb_certificate_entry { -+ uint64_t addr; -+ uint64_t len; -+} __packed; -+ -+struct ipl_rb_certificates { -+ uint32_t len; -+ uint8_t rbt; -+ uint8_t reserved1[11]; -+ struct ipl_rb_certificate_entry entries[]; -+} __packed; -+ -+/* IPL Report Block for the component list */ -+struct ipl_rb_component_entry { -+ uint64_t addr; -+ uint64_t len; -+ uint8_t flags; -+ uint8_t reserved1[5]; -+ uint16_t certificate_index; -+ uint8_t reserved2[8]; -+}; -+ -+#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80 -+#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40 -+ -+struct ipl_rb_components { -+ uint32_t len; -+ uint8_t rbt; -+ uint8_t reserved1[11]; -+ struct ipl_rb_component_entry entries[]; -+} __packed; -+ - extern unsigned long long _parm_addr; /* address of parmline */ - extern unsigned long long _initrd_addr; /* address of initrd */ - extern unsigned long long _initrd_len; /* length of initrd */ - extern unsigned long long _load_psw; /* load psw of kernel */ - extern unsigned long long _extra_parm; /* use extra parm line mechanism? */ - extern unsigned long long stage3_flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */ -+extern unsigned long long _image_len; /* length of kernel */ -+extern unsigned long long _image_addr; /* target address of kernel */ - extern void kdump_stage3(); - - #endif /* STAGE3_H */ -diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds -index 638fb25..c67bc32 100644 ---- a/zipl/boot/stage3.lds -+++ b/zipl/boot/stage3.lds -@@ -1,33 +1,47 @@ - /* - * Memory layout for stage 3 -+ * ========================= -+ * -+ * General memory layout -+ * --------------------- -+ * -+ * 0x0000-0x1fff Lowcore -+ * 0x2000-0x5fff Memory allocation (heap) -+ * 0x6000-0x8fff free -+ * 0x9000-0x9fff Stage3 parameter -+ * 0xa000-0xdfff Stage3 code + data -+ * 0xe000-0xffff Stack - */ - - SECTIONS - { - . = 0x0; - -- . = 0x7000; -+ . = 0x2000; - __heap_start = .; -- . = 0xa000; -+ . = 0x6000; - __heap_stop = .; - -- . = 0xa000; -+ -+ /* stage 3 parameter */ -+ . = 0x9000; - _parm_addr = .; -- . = 0xa008; -+ . = 0x9008; - _initrd_addr = .; -- . = 0xa010; -+ . = 0x9010; - _initrd_len = .; -- . = 0xa018; -+ . = 0x9018; - _load_psw = .; -- . = 0xa020; -+ . = 0x9020; - _extra_parm = .; -- . = 0xa028; -+ . = 0x9028; - stage3_flags =.; -+ . = 0x9030; -+ _image_len = .; -+ . = 0x9038; -+ _image_addr = .; - - . = 0xa000; -- .text.dummy : { *(.text.dummy) } -- -- . = 0xa050; - .text.start : { *(.text.start) } - .text : { *(.text) } - __ex_table_start = .; -@@ -35,11 +49,9 @@ SECTIONS - __ex_table_stop = .; - .eh_frame : { *(.eh_frame) } - -- . = 0xc000; - __bss_start = .; - .bss : { *(.bss) } - __bss_stop = .; - .rodata : {*(.rodata) } - .data : { *(.data) } -- - } -diff --git a/zipl/include/boot.h b/zipl/include/boot.h -index f21ab59..6bd2a9b 100644 ---- a/zipl/include/boot.h -+++ b/zipl/include/boot.h -@@ -248,6 +248,9 @@ struct boot_stage3_params { - uint64_t load_psw; - uint64_t extra_parm; - uint16_t flags; -+ uint16_t reserved[3]; -+ uint64_t image_len; -+ uint64_t image_addr; - } __attribute__ ((packed)); - - #define STAGE3_FLAG_SCSI 0x0001 -@@ -305,10 +308,10 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, - disk_blockptr_t *stage2_list, - blocknum_t stage2_count); - int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job); --size_t get_stage3_size(); --int boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, -- address_t initrd_addr, size_t initrd_len, -- address_t image_addr, int extra_parm, uint16_t flags); -+int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, -+ address_t initrd_addr, size_t initrd_len, -+ address_t load_addr, int extra_parm, uint16_t flags, -+ size_t image_len); - int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr, - address_t initrd_addr, address_t image_addr); - int boot_get_tape_dump(void** data, size_t* size, uint64_t mem); -diff --git a/zipl/include/bootmap.h b/zipl/include/bootmap.h -index c03c041..4a0bd04 100644 ---- a/zipl/include/bootmap.h -+++ b/zipl/include/bootmap.h -@@ -16,6 +16,37 @@ - #include "job.h" - #include "zipl.h" - -+#define SIGNATURE_MAGIC "~Module signature appended~\n" -+#define PKCS7_FORMAT 0x01 -+ -+struct signature_header { -+ uint8_t format; -+ uint8_t reserved[3]; -+ uint32_t length; -+} __attribute((packed)); -+ -+typedef union { -+ uint64_t load_address; -+ uint64_t load_psw; -+ struct signature_header sig_head; -+} component_data; -+ -+/* -+ * The file_signature structure and the PKEY_ID definition -+ * are based on linux/scripts/sign-file.c -+ */ -+struct file_signature { -+ u8 algorithm; -+ u8 hash; -+ u8 id_type; -+ u8 signer_len; -+ u8 key_id_len; -+ u8 __pad[3]; -+ u32 sig_len; -+ char magic[28]; -+}; -+ -+#define PKEY_ID_PKCS7 0x02 - - int bootmap_create(struct job_data* job, disk_blockptr_t* program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, -diff --git a/zipl/include/job.h b/zipl/include/job.h -index 4c9253f..fce811c 100644 ---- a/zipl/include/job.h -+++ b/zipl/include/job.h -@@ -121,6 +121,7 @@ struct job_data { - int add_files; - int dry_run; - int command_line; -+ int is_secure; - }; - - -diff --git a/zipl/include/misc.h b/zipl/include/misc.h -index 37a8a87..5a349a7 100644 ---- a/zipl/include/misc.h -+++ b/zipl/include/misc.h -@@ -51,6 +51,7 @@ int misc_check_readable_file(const char* filename); - int misc_check_writable_device(const char* devno, int blockdev, int chardev); - void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to); - void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to); -+unsigned int misc_check_secure_boot(void); - - #define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) - #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -diff --git a/zipl/include/scan.h b/zipl/include/scan.h -index 0fe415c..4dcee0b 100644 ---- a/zipl/include/scan.h -+++ b/zipl/include/scan.h -@@ -16,7 +16,7 @@ - - - #define SCAN_SECTION_NUM 9 --#define SCAN_KEYWORD_NUM 21 -+#define SCAN_KEYWORD_NUM 22 - #define SCAN_KEYWORD_ONLY_NUM 1 - #define SCAN_AUTOMENU_NAME "zipl-automatic-menu" - -@@ -51,6 +51,7 @@ enum scan_keyword_id { - scan_keyword_targetoffset = 18, - scan_keyword_defaultauto = 19, - scan_keyword_kdump = 20, -+ scan_keyword_secure = 21, - }; - - enum scan_section_type { -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index 770801e..6f2d115 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -20,10 +20,11 @@ - #define DISK_LAYOUT_ID 0x00000001 - - #define ZIPL_STAGE2_LOAD_ADDRESS 0x2000 --#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa050LL -+#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa000LL - #define DEFAULT_IMAGE_ADDRESS 0x10000LL - #define KDUMP_IMAGE_ADDRESS 0x10010LL - #define DEFAULT_STAGE3_ADDRESS 0xa000LL -+#define DEFAULT_STAGE3_PARAMS_ADDRESS 0x9000LL - #define MINIMUM_ADDRESS 0x10000LL - #define ADDRESS_LIMIT 0x80000000LL - #define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */ -@@ -31,11 +32,15 @@ - #define MAXIMUM_PARMLINE_SIZE 0x380 - #define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000 - -+#define STAGE3_HEAP_SIZE 0x4000 -+#define STAGE3_HEAP_ADDRESS 0x2000 -+#define STAGE3_STACK_SIZE 0x1000 -+#define STAGE3_STACK_ADDRESS 0xF000 -+ - #define PSW_ADDRESS_MASK 0x000000007fffffffLL - #define PSW_LOAD 0x0008000080000000LL - #define PSW_DISABLED_WAIT 0x000a000000000000LL - --#define KERNEL_HEADER_SIZE 65536 - #define BOOTMAP_FILENAME "bootmap" - #define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX" - -@@ -44,12 +49,18 @@ - #define ZIPL_CONF_VAR "ZIPLCONF" - #define ZIPL_DEFAULT_CONF "/etc/zipl.conf" - #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" -+#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" -+#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure" - - #define MENU_DEFAULT_PROMPT 0 - #define MENU_DEFAULT_TIMEOUT 0 - - #define MAX_DUMP_VOLUMES 32 - -+#define SECURE_BOOT_DISABLED 0 -+#define SECURE_BOOT_ENABLED 1 -+#define SECURE_BOOT_AUTO 2 -+ - /* Internal component load address type */ - typedef uint64_t address_t; - -diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 -index 9891975..2f873f3 100644 ---- a/zipl/man/zipl.8 -+++ b/zipl/man/zipl.8 -@@ -352,6 +352,21 @@ whether they contain a dump signature or not. - This option can only be used together with - .BR \-\-mvdump . - -+.TP -+.BR "\-S " " or " "\-\-secure " -+Control the zIPL secure boot support. -+ can take one of three values: -+ -+ auto (default) -+ Write signatures if available and supported by the system. -+ 1 -+ Signatures are written independent of support indicated by the local -+ system. Also missing signatures for stage 3 and kernel IPL files -+ will result in an error. -+ 0 -+ No signatures will be written. -+ -+ - .SH EXAMPLE - 1. Scenario: prepare disk for booting a Linux kernel image using the - following parameters: -diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5 -index d4877d8..71d1252 100644 ---- a/zipl/man/zipl.conf.5 -+++ b/zipl/man/zipl.conf.5 -@@ -82,6 +82,8 @@ below). - .br - defaultmenu = menu1 - .br -+secure = auto -+.br - - [linux] - .br -@@ -517,6 +519,31 @@ An optional hexadecimal address may be provided to load the kernel to a - non-default memory location. - .PP - -+.B secure -+= -+.IR auto / 1 / 0 -+(configuration only) -+.IP -+.B Configuration section: -+.br -+Control the zIPL secure boot support. -+Set this option to one of the following: -+.IP " - " 12 -+.BR auto: -+Write signatures if available and supported by the system. -+.IP " - " 12 -+.BR 1: -+Signatures are written independent of support indicated by the local system. -+Also missing signatures for stage 3 and kernel IPL files will result in an error. -+.IP " - " 12 -+.BR 0: -+No signatures will be written. -+ -+The default value for -+.B 'secure' -+is auto. -+.PP -+ - .B segment - = - .IR segment\-file , address -diff --git a/zipl/src/Makefile b/zipl/src/Makefile -index be14fce..bed970c 100644 ---- a/zipl/src/Makefile -+++ b/zipl/src/Makefile -@@ -15,8 +15,9 @@ objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ - - zipl_helpers = $(basename $(wildcard zipl_helper.*.c)) - chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers)) -+zipl_stage3 = ../boot/stage3.bin - --all: zipl $(chreipl_helpers) -+all: zipl $(chreipl_helpers) $(zipl_stage3) - - zipl: $(objects) $(libs) - -@@ -33,6 +34,7 @@ install: all - $(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \ - $(DESTDIR)$(TOOLS_LIBDIR) - $(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR) -+ $(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR) - - clean: - rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl -@@ -46,3 +48,6 @@ clean: - - ../boot/data.o: - make -C ../boot data.o -+ -+../boot/stage3.bin: -+ make -C ../boot stage3.bin -diff --git a/zipl/src/boot.c b/zipl/src/boot.c -index 279d246..b13bcb7 100644 ---- a/zipl/src/boot.c -+++ b/zipl/src/boot.c -@@ -14,6 +14,8 @@ - #include - #include - #include -+#include -+#include - - #include "../boot/data.h" - #include "boot.h" -@@ -68,20 +70,17 @@ boot_check_data(void) - return 0; - } - --/* Export stage 3 size for partition dump with dump kernel */ --size_t --get_stage3_size() --{ -- return DATA_SIZE(stage3); --} -- --/* Create a stage 3 loader in memory. -+/* -+ * Create a stage 3 parameter block in memory. - * Upon success, return 0 and set BUFFER to point to the data buffer and set -- * BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */ -+ * BYTECOUNT to contain the parameter block size in bytes. -+ * Return non-zero otherwise. -+ */ - int --boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, -- address_t initrd_addr, size_t initrd_len, address_t image_addr, -- int extra_parm, uint16_t flags) -+boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, -+ address_t initrd_addr, size_t initrd_len, -+ address_t image_addr, int extra_parm, uint16_t flags, -+ size_t image_len) - { - struct boot_stage3_params params; - void* data; -@@ -92,9 +91,10 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, - return -1; - } - /* Get memory */ -- data = misc_malloc(DATA_SIZE(stage3)); -+ data = misc_malloc(sizeof(params)); - if (data == NULL) - return -1; -+ memset(data, 0, sizeof(params)); - /* Prepare params section */ - params.parm_addr = (uint64_t) parm_addr; - params.initrd_addr = (uint64_t) initrd_addr; -@@ -102,11 +102,12 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, - params.load_psw = (uint64_t)(image_addr | PSW_LOAD); - params.extra_parm = (uint64_t) extra_parm; - params.flags = flags; -+ params.image_len = (uint64_t) image_len; -+ params.image_addr = (uint64_t) image_addr; - /* Initialize buffer */ -- memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3)); -- memcpy(data, ¶ms, sizeof(struct boot_stage3_params)); -+ memcpy(data, ¶ms, sizeof(params)); - *buffer = data; -- *bytecount = DATA_SIZE(stage3); -+ *bytecount = sizeof(params); - return 0; - } - -diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c -index 1b71b03..456c2ef 100644 ---- a/zipl/src/bootmap.c -+++ b/zipl/src/bootmap.c -@@ -19,6 +19,7 @@ - #include - - #include "lib/util_part.h" -+#include "lib/util_path.h" - - #include "boot.h" - #include "bootmap.h" -@@ -117,6 +118,22 @@ check_menu_positions(struct job_menu_data* menu, char* name, - return 0; - } - -+static bool -+check_secure_boot_support(void) -+{ -+ unsigned int val; -+ FILE *fp; -+ -+ fp = fopen(ZIPL_SIPL_PATH, "r"); -+ if (!fp) -+ return false; -+ -+ fscanf(fp, "%d", &val); -+ fclose(fp); -+ -+ return val ? true : false; -+} -+ - - /* Write COUNT elements of the blocklist specified by LIST as a linked list - * of segment table blocks to the file identified by file descriptor FD. Upon -@@ -199,24 +216,21 @@ add_program_table(int fd, disk_blockptr_t* table, int entries, - return rc; - } - -- - struct component_entry { - uint8_t data[23]; - uint8_t type; -- union { -- uint64_t load_address; -- uint64_t load_psw; -- } address; -+ component_data compdat; - } __attribute((packed)); - - typedef enum { - component_execute = 0x01, -- component_load = 0x02 -+ component_load = 0x02, -+ component_signature = 0x03 - } component_type; - - static void - create_component_entry(void* buffer, disk_blockptr_t* pointer, -- component_type type, uint64_t address, -+ component_type type, component_data data, - struct disk_info* info) - { - struct component_entry* entry; -@@ -228,10 +242,15 @@ create_component_entry(void* buffer, disk_blockptr_t* pointer, - case component_load: - bootmap_store_blockptr(&entry->data, pointer, - info); -- entry->address.load_address = address; -+ entry->compdat.load_address = data.load_address; - break; - case component_execute: -- entry->address.load_psw = address; -+ entry->compdat.load_psw = data.load_psw; -+ break; -+ case component_signature: -+ bootmap_store_blockptr(&entry->data, pointer, -+ info); -+ entry->compdat.sig_head = data.sig_head; - break; - } - } -@@ -267,7 +286,7 @@ struct component_loc { - - static int - add_component_file(int fd, const char* filename, address_t load_address, -- off_t offset, void* component, int add_files, -+ size_t trailer, void *component, int add_files, - struct disk_info* info, struct job_target_data* target, - struct component_loc *location) - { -@@ -279,8 +298,6 @@ add_component_file(int fd, const char* filename, address_t load_address, - size_t size; - blocknum_t count; - int rc; -- int from; -- unsigned int to; - - if (add_files) { - /* Read file to buffer */ -@@ -289,17 +306,10 @@ add_component_file(int fd, const char* filename, address_t load_address, - error_text("Could not read file '%s'", filename); - return rc; - } -- /* Ensure minimum size */ -- if (size <= (size_t) offset) { -- error_reason("File '%s' is too small (has to be " -- "greater than %ld bytes)", filename, -- (long) offset); -- free(buffer); -- return -1; -- } -+ size -= trailer; - /* Write buffer */ -- count = disk_write_block_buffer(fd, 0, buffer + offset, -- size - offset, &list, info); -+ count = disk_write_block_buffer(fd, 0, buffer, -+ size, &list, info); - free(buffer); - if (count == 0) { - error_text("Could not write to bootmap file"); -@@ -321,20 +331,7 @@ add_component_file(int fd, const char* filename, address_t load_address, - disk_free_info(file_info); - if (count == 0) - return -1; -- if (count * info->phy_block_size <= (size_t) offset) { -- error_reason("File '%s' is too small (has to be " -- "greater than %ld bytes)", filename, -- (long) offset); -- free(list); -- return -1; -- } -- if (offset > 0) { -- /* Shorten list by offset */ -- from = offset / info->phy_block_size; -- count -= from; -- for (to=0; to < count; to++, from++) -- list[to] = list[from]; -- } -+ count -= DIV_ROUND_UP(trailer, info->phy_block_size); - } - /* Fill in component location */ - loc.addr = load_address; -@@ -346,7 +343,7 @@ add_component_file(int fd, const char* filename, address_t load_address, - free(list); - if (rc == 0) { - create_component_entry(component, &segment, component_load, -- load_address, info); -+ (component_data) load_address, info); - /* Return location if requested */ - if (location != NULL) - *location = loc; -@@ -354,11 +351,10 @@ add_component_file(int fd, const char* filename, address_t load_address, - return rc; - } - -- - static int --add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, -+add_component_buffer(int fd, void* buffer, size_t size, component_data data, - void* component, struct disk_info* info, -- struct component_loc *location) -+ struct component_loc *location, int type) - { - struct component_loc loc; - disk_blockptr_t segment; -@@ -372,17 +368,21 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, - error_text("Could not write to bootmap file"); - return -1; - } -- /* Fill in component location */ -- loc.addr = load_address; -- loc.size = count * info->phy_block_size; -+ if (type == component_load) { -+ /* Fill in component location */ -+ loc.addr = data.load_address; -+ loc.size = count * info->phy_block_size; -+ } else { -+ loc.addr = 0; -+ loc.size = 0; -+ } - /* Try to compact list */ - count = disk_compact_blocklist(list, count, info); - /* Write segment table */ - rc = add_segment_table(fd, list, count, &segment, info); - free(list); - if (rc == 0) { -- create_component_entry(component, &segment, component_load, -- load_address, info); -+ create_component_entry(component, &segment, type, data, info); - /* Return location if requested */ - if (location != NULL) - *location = loc; -@@ -391,6 +391,30 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address, - } - - -+static int -+add_dummy_buffer(int fd, size_t size, address_t addr, void *component, -+ struct disk_info *info, struct component_loc *comp_loc) -+{ -+ char *buffer; -+ int rc; -+ -+ buffer = misc_malloc(size); -+ if (buffer == NULL) -+ return -1; -+ -+ memset(buffer, 0, size); -+ rc = add_component_buffer(fd, buffer, size, -+ (component_data) (uint64_t) addr, -+ component, info, comp_loc, component_load); -+ if (rc) { -+ free(buffer); -+ return rc; -+ } -+ free(buffer); -+ return 0; -+} -+ -+ - static void - print_components(const char *name[], struct component_loc *loc, int num) - { -@@ -409,23 +433,86 @@ print_components(const char *name[], struct component_loc *loc, int num) - } - } - -+static int -+extract_signature(char *filename, void **ret_signature, -+ struct signature_header *sig_head) -+{ -+ struct file_signature *file_sig; -+ size_t signature_size = 0; -+ void *signature; -+ char *buffer; -+ size_t size; -+ -+ if (misc_read_file(filename, &buffer, &size, 0)) -+ return 0; -+ -+ file_sig = (void *) buffer + size - sizeof(*file_sig); -+ if (memcmp(file_sig->magic, SIGNATURE_MAGIC, sizeof(file_sig->magic)) -+ != 0) -+ goto out; -+ -+ signature = misc_malloc(file_sig->sig_len); -+ if (signature == NULL) -+ goto out; -+ signature_size = file_sig->sig_len; -+ -+ memcpy(signature, buffer + size - signature_size - sizeof(*file_sig), -+ signature_size); -+ -+ *ret_signature = signature; -+ sig_head->length = signature_size; -+ -+ switch (file_sig->id_type) { -+ case PKEY_ID_PKCS7: -+ sig_head->format = PKCS7_FORMAT; -+ break; -+ default: -+ error_text("Unsupported signature type %02x", -+ file_sig->id_type); -+ signature_size = 0; -+ goto out; -+ } -+ /* return size of signature and corresponding header */ -+ signature_size += sizeof(*file_sig); -+out: -+ free(buffer); -+ return signature_size; -+} -+ -+static void -+check_remaining_filesize(size_t filesize, size_t signature_size, -+ struct disk_info *info, char *filename) -+{ -+ if ((filesize - signature_size) % info->phy_block_size) { -+ fprintf(stderr, -+ "Warning: Size of signed file %s is not a multiple of the disk block size\n", -+ filename); -+ } -+} - - static int - add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - int verbose, int add_files, component_header_type type, -- struct disk_info* info, struct job_target_data* target) -+ struct disk_info* info, struct job_target_data* target, -+ int is_secure) - { -+ struct component_loc comp_loc[10]; -+ struct signature_header sig_head; -+ size_t ramdisk_size, image_size; -+ bool secure_boot_supported; -+ size_t stage3_params_size; -+ const char *comp_name[10]; -+ size_t signature_size; -+ int offset, flags = 0; -+ void *stage3_params; - struct stat stats; -- void* table; -- void* stage3; -- size_t stage3_size; -- const char *comp_name[4] = {"kernel image", "parmline", -- "initial ramdisk", "internal loader"}; -- struct component_loc comp_loc[4]; -+ void *signature; -+ int comp_nr = 0; -+ void *table; - int rc; -- int offset, flags = 0; - - memset(comp_loc, 0, sizeof(comp_loc)); -+ memset(&sig_head, 0, sizeof(sig_head)); - table = misc_malloc(info->phy_block_size); - if (table == NULL) - return -1; -@@ -456,55 +543,189 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - return -1; - } - } -- if (info->type == disk_type_scsi) -+ ramdisk_size = stats.st_size; -+ if (info->type == disk_type_scsi) { - flags |= STAGE3_FLAG_SCSI; -+ /* -+ * Add dummy components for stage 3 heap and stack to block the -+ * associated memory areas against firmware use. -+ */ -+ rc = add_dummy_buffer(fd, STAGE3_HEAP_SIZE, STAGE3_HEAP_ADDRESS, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr]); -+ if (rc) { -+ error_text("Could not add stage3 HEAP dummy"); -+ free(table); -+ return rc; -+ } -+ comp_name[comp_nr] = "heap area"; -+ offset += sizeof(struct component_entry); -+ comp_nr++; -+ rc = add_dummy_buffer(fd, STAGE3_STACK_SIZE, -+ STAGE3_STACK_ADDRESS, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr]); -+ if (rc) { -+ error_text("Could not add stage3 STACK dummy"); -+ free(table); -+ return rc; -+ } -+ comp_name[comp_nr] = "stack area"; -+ offset += sizeof(struct component_entry); -+ comp_nr++; -+ } - if (ipl->is_kdump) - flags |= STAGE3_FLAG_KDUMP; - -+ /* Get kernel file size */ -+ if (stat(ipl->image, &stats)) { -+ error_reason(strerror(errno)); -+ error_text("Could not get information for file '%s'", -+ ipl->image); -+ free(table); -+ return -1; -+ } -+ image_size = stats.st_size; -+ secure_boot_supported = check_secure_boot_support(); -+ signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature, -+ &sig_head); -+ if (signature_size && -+ (is_secure == SECURE_BOOT_ENABLED || -+ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { -+ if (verbose) -+ printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH); -+ -+ rc = add_component_buffer(fd, signature, sig_head.length, -+ (component_data)sig_head, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr], -+ component_signature); -+ if (rc) { -+ error_text("Could not add stage3 signature"); -+ free(table); -+ return rc; -+ } -+ comp_name[comp_nr] = "loader signature"; -+ offset += sizeof(struct component_entry); -+ comp_nr++; -+ free(signature); -+ } else if (is_secure == SECURE_BOOT_ENABLED) { -+ /* -+ * If secure boot is forced and we have failed to extract a -+ * signature for the stage 3 loader zipl will abort with an -+ * error message -+ */ -+ error_text("Could not install Secure Boot IPL records"); -+ error_reason("Missing signature in internal loader file %s", -+ ZIPL_STAGE3_PATH); -+ free(table); -+ return -1; -+ } -+ - /* Add stage 3 loader to bootmap */ -- rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr, -- ipl->ramdisk_addr, (size_t) stats.st_size, -- ipl->is_kdump ? ipl->image_addr + 0x10 : -- ipl->image_addr, -- (info->type == disk_type_scsi) ? 0 : 1, -- flags); -+ rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS, -+ signature_size, VOID_ADD(table, offset), 1, -+ info, target, &comp_loc[comp_nr]); - if (rc) { -+ error_text("Could not add internal loader file '%s'", -+ ZIPL_STAGE3_PATH); - free(table); - return rc; - } -- rc = add_component_buffer(fd, stage3, stage3_size, -- DEFAULT_STAGE3_ADDRESS, -- VOID_ADD(table, offset), info, &comp_loc[3]); -- free(stage3); -+ offset += sizeof(struct component_entry); -+ comp_name[comp_nr] = "internal loader"; -+ comp_nr++; -+ -+ /* Add stage 3 parameter to bootmap */ -+ rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size, -+ ipl->parm_addr, ipl->ramdisk_addr, -+ ramdisk_size, -+ ipl->is_kdump ? ipl->image_addr + 0x10 : -+ ipl->image_addr, -+ (info->type == disk_type_scsi) ? 0 : 1, -+ flags, image_size); - if (rc) { -- error_text("Could not add stage 3 boot loader"); -+ free(table); -+ return rc; -+ } -+ rc = add_component_buffer(fd, stage3_params, stage3_params_size, -+ (component_data) (uint64_t) -+ DEFAULT_STAGE3_PARAMS_ADDRESS, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr], component_load); -+ free(stage3_params); -+ if (rc) { -+ error_text("Could not add parameters"); - free(table); - return -1; - } - offset += sizeof(struct component_entry); -+ comp_name[comp_nr] = "parameters"; -+ comp_nr++; -+ - /* Add kernel image */ - if (verbose) { - printf(" kernel image......: %s\n", ipl->image); - } -+ signature_size = extract_signature(ipl->image, &signature, &sig_head); -+ if (signature_size && -+ (is_secure == SECURE_BOOT_ENABLED || -+ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { -+ if (verbose) -+ printf(" signature for.....: %s\n", ipl->image); -+ -+ rc = add_component_buffer(fd, signature, sig_head.length, -+ (component_data)sig_head, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr], -+ component_signature); -+ if (rc) { -+ error_text("Could not add image signature"); -+ free(table); -+ return rc; -+ } -+ comp_name[comp_nr] = "image signature"; -+ offset += sizeof(struct component_entry); -+ comp_nr++; -+ free(signature); -+ check_remaining_filesize(image_size, signature_size, info, -+ ipl->image); -+ } else if (is_secure == SECURE_BOOT_ENABLED) { -+ /* -+ * If secure boot is forced and we have failed to extract a -+ * signature for the kernel image zipl will abort with an -+ * error message -+ */ -+ error_text("Could not install Secure Boot IPL records"); -+ error_reason("Missing signature in image file %s", -+ ipl->image); -+ free(table); -+ return -1; -+ } -+ - rc = add_component_file(fd, ipl->image, ipl->image_addr, -- KERNEL_HEADER_SIZE, VOID_ADD(table, offset), -- add_files, info, target, &comp_loc[0]); -+ signature_size, VOID_ADD(table, offset), -+ add_files, info, target, &comp_loc[comp_nr]); - if (rc) { - error_text("Could not add image file '%s'", ipl->image); - free(table); - return rc; - } - offset += sizeof(struct component_entry); -+ comp_name[comp_nr] = "kernel image"; -+ comp_nr++; -+ -+ /* Add kernel parmline */ - if (ipl->parmline != NULL) { -- /* Add kernel parmline */ - if (verbose) { - printf(" kernel parmline...: '%s'\n", ipl->parmline); - } - rc = add_component_buffer(fd, ipl->parmline, - strlen(ipl->parmline) + 1, -- ipl->parm_addr, -+ (component_data) ipl->parm_addr, - VOID_ADD(table, offset), -- info, &comp_loc[1]); -+ info, &comp_loc[comp_nr], -+ component_load); - if (rc) { - error_text("Could not add parmline '%s'", - ipl->parmline); -@@ -512,14 +733,45 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - return -1; - } - offset += sizeof(struct component_entry); -+ comp_name[comp_nr] = "parmline"; -+ comp_nr++; - } - - /* finally add ramdisk */ - if (ipl->ramdisk != NULL) { -+ signature_size = extract_signature(ipl->ramdisk, &signature, -+ &sig_head); -+ if (signature_size && -+ (is_secure == SECURE_BOOT_ENABLED || -+ (is_secure == SECURE_BOOT_AUTO && -+ secure_boot_supported))) { -+ if (verbose) { -+ printf(" signature for.....: %s\n", -+ ipl->ramdisk); -+ } -+ rc = add_component_buffer(fd, signature, -+ sig_head.length, -+ (component_data)sig_head, -+ VOID_ADD(table, offset), info, -+ &comp_loc[comp_nr], -+ component_signature); -+ if (rc) { -+ error_text("Could not add ramdisk signature"); -+ free(table); -+ return rc; -+ } -+ comp_name[comp_nr] = "ramdisk signature"; -+ offset += sizeof(struct component_entry); -+ comp_nr++; -+ free(signature); -+ check_remaining_filesize(ramdisk_size, signature_size, -+ info, ipl->ramdisk); -+ } - rc = add_component_file(fd, ipl->ramdisk, -- ipl->ramdisk_addr, 0, -+ ipl->ramdisk_addr, signature_size, - VOID_ADD(table, offset), -- add_files, info, target, &comp_loc[2]); -+ add_files, info, target, -+ &comp_loc[comp_nr]); - if (rc) { - error_text("Could not add ramdisk '%s'", - ipl->ramdisk); -@@ -527,13 +779,16 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - return -1; - } - offset += sizeof(struct component_entry); -+ comp_name[comp_nr] = "initial ramdisk"; -+ comp_nr++; - } - if (verbose) -- print_components(comp_name, comp_loc, 4); -+ print_components(comp_name, comp_loc, comp_nr); - /* Terminate component table */ - create_component_entry(VOID_ADD(table, offset), NULL, - component_execute, -- ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD, -+ (component_data) (uint64_t) -+ (ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD), - info); - /* Write component table */ - rc = disk_write_block_aligned(fd, table, info->phy_block_size, -@@ -584,7 +839,8 @@ if (rc) { - print_components(comp_name, comp_loc, 1); - /* Terminate component table */ - create_component_entry(VOID_ADD(table, offset), NULL, -- component_execute, PSW_DISABLED_WAIT, info); -+ component_execute, (component_data) (uint64_t) -+ PSW_DISABLED_WAIT, info); - /* Write component table */ - rc = disk_write_block_aligned(fd, table, info->phy_block_size, - program, info); -@@ -674,7 +930,7 @@ add_dump_program(int fd, struct job_dump_data* dump, - return rc; - ipl.parm_addr = dump->parm_addr; - return add_ipl_program(fd, &ipl, program, verbose, 1, -- type, info, target); -+ type, info, target, SECURE_BOOT_DISABLED); - } - - -@@ -711,7 +967,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, - rc = add_ipl_program(fd, &job->data.ipl, &table[0], - verbose || job->command_line, - job->add_files, component_header, -- info, &job->target); -+ info, &job->target, job->is_secure); - break; - case job_segment: - if (job->command_line) -@@ -762,7 +1018,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, - &table[job->data.menu.entry[i].pos], - verbose || job->command_line, - job->add_files, component_header, -- info, &job->target); -+ info, &job->target, -+ job->is_secure); - break; - case job_print_usage: - case job_print_version: -@@ -913,7 +1170,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - ulong size; - ulong unused_size; - -- size = DIV_ROUND_UP(get_stage3_size(), info->phy_block_size); -+ /* Use approximated stage 3 size as starting point */ -+ size = MINIMUM_ADDRESS; -+ - /* Ramdisk */ - if (job->data.dump.ramdisk != NULL) { - if (stat(job->data.dump.ramdisk, &st)) -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 22d2549..2d8de8f 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -52,11 +52,12 @@ static struct option options[] = { - { "dry-run", no_argument, NULL, '0'}, - { "force", no_argument, NULL, 'f'}, - { "kdump", required_argument, NULL, 'k'}, -+ { "secure", required_argument, NULL, 'S'}, - { NULL, 0, NULL, 0 } - }; - - /* Command line option abbreviations */ --static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:"; -+static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:"; - - struct command_line { - char* data[SCAN_KEYWORD_NUM]; -@@ -215,6 +216,11 @@ get_command_line(int argc, char* argv[], struct command_line* line) - } else - cmdline.menu = optarg; - break; -+ case 'S': -+ is_keyword = 1; -+ rc = store_option(&cmdline, scan_keyword_secure, -+ optarg); -+ break; - case 'h': - cmdline.help = 1; - break; -@@ -511,7 +517,7 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp, - /* Fill in component data */ - num = 0; - rc = set_cl_element(&cl[num++], "kernel image", ipl->image, -- &ipl->image_addr, 0, 0x10000, -+ &ipl->image_addr, 0, 0, - MAXIMUM_PHYSICAL_BLOCKSIZE); - if (rc) - goto error; -@@ -1263,6 +1269,26 @@ type_from_target(char *target, disk_type_t *type) - } - } - -+static int -+set_secure_ipl(char *keyword, struct job_data *job) -+{ -+ if (strcmp(keyword, "auto") == 0) { -+ job->is_secure = SECURE_BOOT_AUTO; -+ } else if (strcmp(keyword, "0") == 0) { -+ job->is_secure = SECURE_BOOT_DISABLED; -+ } else if (strcmp(keyword, "1") == 0) { -+ if (job->target.targettype != disk_type_scsi) { -+ error_reason("Secure boot forced for non-SCSI disk type"); -+ return -1; -+ } -+ job->is_secure = SECURE_BOOT_ENABLED; -+ } else { -+ error_reason("Invalid secure boot setting '%s'", -+ keyword); -+ return -1; -+ } -+ return 0; -+} - - static int - get_job_from_section_data(char* data[], struct job_data* job, char* section) -@@ -1345,6 +1371,13 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) - return -1; - } - } -+ /* Fill in secure boot */ -+ if (data[(int) scan_keyword_secure] != NULL) { -+ rc = set_secure_ipl(data[(int) scan_keyword_secure], -+ job); -+ if (rc) -+ return rc; -+ } - break; - case section_ipl_tape: - /* Tape IPL job */ -@@ -1502,6 +1535,13 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - job->data.menu.timeout = - atol(scan[i].content.keyword.value); - break; -+ case scan_keyword_secure: -+ rc = set_secure_ipl( -+ scan[i].content.keyword.value, -+ job); -+ if (rc) -+ return rc; -+ break; - case scan_keyword_target: - job->target.bootmap_dir = misc_strdup( - scan[i].content.keyword.value); -diff --git a/zipl/src/misc.c b/zipl/src/misc.c -index fd16810..057c9a0 100644 ---- a/zipl/src/misc.c -+++ b/zipl/src/misc.c -@@ -22,7 +22,6 @@ - #include "error.h" - #include "misc.h" - -- - /* Allocate SIZE bytes of memory. Upon success, return pointer to memory. - * Return NULL otherwise. */ - void * -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index fe72e9a..e57361e 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -45,45 +45,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { - * ult to tofs e mete file isk ent et pt out ultm dump - * rs enu - * -- * targ targ targ targ targ defa kdum -- * etba etty etge etbl etof ulta p -+ * targ targ targ targ targ defa kdum secu -+ * etba etty etge etbl etof ulta p re - * se pe omet ocks fset uto - * ry ize - */ - /* default auto */ - {opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, -- opt, opt, opt, opt, opt, opt, inv}, -+ opt, opt, opt, opt, opt, opt, inv, opt}, - /* default menu */ - {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, opt}, - /* default section */ - {req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, opt}, - /* ipl */ - {inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv, -- opt, opt, opt, opt, opt, inv, opt}, -+ opt, opt, opt, opt, opt, inv, opt, opt}, - /* segment load */ - {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, inv}, - /* part dump */ - {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, inv}, - /* fs dump */ - {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, inv}, - /* ipl tape */ - {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv, -- inv, inv, inv, inv, inv, inv, inv}, -+ inv, inv, inv, inv, inv, inv, inv, inv}, - /* multi volume dump */ - {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, -- inv, inv, inv, inv, inv, inv, inv} -+ inv, inv, inv, inv, inv, inv, inv, inv} - }; - - /* Determines which keyword may be present in a menu section */ - enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = { - /* menu section */ - opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, -- opt, opt, opt, opt, opt, inv, inv -+ opt, opt, opt, opt, opt, inv, inv, opt - }; - - /* Mapping of keyword IDs to strings */ -@@ -111,6 +111,7 @@ static const struct { - { "timeout", scan_keyword_timeout}, - { "tape", scan_keyword_tape}, - { "kdump", scan_keyword_kdump}, -+ { "secure", scan_keyword_secure}, - }; - - /* List of keywords that are used without an assignment */ -@@ -1863,6 +1864,7 @@ scan_build_automenu(struct scan_token* scan) - /* defaultmenu */ 1 + - /* menu heading */ 1 + - /* keyword default,prompt,timeout */ 3 + -+ /* keyword secure */ 1 + - /* target keywords*/ num_targets + - /* missing target definitions */ num_sections * num_targets + - /* number assigment */ num_sections; -@@ -1978,6 +1980,14 @@ scan_build_automenu(struct scan_token* scan) - db_keyword[i])) - goto err; - } -+ /* secure= */ -+ i = (int) scan_keyword_secure; -+ if (db_keyword[i]) { -+ if (scan_append_keyword_assignment(new_scan, &i_new, -+ scan_keyword_secure, -+ db_keyword[i])) -+ goto err; -+ } - /* target= */ - /* targetbase= */ - /* targetgeometry= */ --- -2.21.3 - - -From 100c89a273fb4bcc1deb93aaf77ac9264d9ac3ee Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 2 May 2019 15:46:39 +0200 -Subject: [PATCH 19/57] zipl: update stage3 objcopy command for gcc9 (#1659401) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The objcopy command for stage3.bin needs to take the new rodata section into account. -See also PR #60. - -Signed-off-by: Dan Horák ---- - zipl/boot/Makefile | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile -index da7e95f..a049797 100644 ---- a/zipl/boot/Makefile -+++ b/zipl/boot/Makefile -@@ -85,6 +85,7 @@ stage3.bin: stage3.exec - --only-section=.ex_table \ - --only-section=.data \ - --only-section=.rodata.str1.2 \ -+ --only-section=.rodata.cst8 \ - --only-section=.rodata \ - --only-section=.stage2dump.tail \ - --only-section=.eckd2dump_mv.tail \ --- -2.21.3 - - -From 78f0479055e025ba849766aa2d60c22a7bcdbfba Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 21 May 2019 13:49:09 +0200 -Subject: [PATCH 20/57] s390-tools: Add zcryptstats tool (#1658756) - -Description: The zcryptstats tool displays usage statistics of - IBM Crypto Express adapters. It obtains cryptographic - performance measurement data periodically and displays - the data for each cryptographic device for each - interval. A cryptographic device can be either a card - device or queue device (APQN). - -Upstream-ID: 5f2ddad6a83e04f6a64ea39619148c80e56c0814 -Upstream-ID: b165500b6987f3fcbbb6daee6d84d66af7fbd41f -Upstream-ID: 58189b878697fef3bf405fdd864bf40f2149ccc0 -Upstream-ID: 7b4a05e5a36fd36ba1866047f6b4814f127ec846 -Upstream-ID: 8be43a1f16923da037d5f5bffca73c433ca1231b -Upstream-ID: 43fcb694bfd2e4515ce131ea3aba2f81e8437757 ---- - README.md | 1 + - include/lib/util_rec.h | 3 + - libutil/util_rec.c | 89 +- - libutil/util_rec_example.c | 7 + - zconf/zcrypt/Makefile | 7 +- - zconf/zcrypt/zcryptstats.8 | 247 ++++ - zconf/zcrypt/zcryptstats.c | 2418 ++++++++++++++++++++++++++++++++++++ - 7 files changed, 2768 insertions(+), 4 deletions(-) - create mode 100644 zconf/zcrypt/zcryptstats.8 - create mode 100644 zconf/zcrypt/zcryptstats.c - -diff --git a/README.md b/README.md -index 08c27d7..af1024e 100644 ---- a/README.md -+++ b/README.md -@@ -129,6 +129,7 @@ Package contents - or show encryption state of attached LUNs. - - lszcrypt: Show Information about zcrypt devices and configuration. - - chzcrypt: Modify the zcrypt configuration. -+ - zcryptstats: Display usage statistics of IBM Crypto Express adapters. - - znetconf: List and configure network devices for s390 network adapters. - - cio_ignore: Query and modify the contents of the CIO device driver - blacklist. -diff --git a/include/lib/util_rec.h b/include/lib/util_rec.h -index a44461a..7a0c2f6 100644 ---- a/include/lib/util_rec.h -+++ b/include/lib/util_rec.h -@@ -68,5 +68,8 @@ const char *util_rec_get(struct util_rec *rec, const char *key); - - void util_rec_print_hdr(struct util_rec *rec); - void util_rec_print(struct util_rec *rec); -+void util_rec_print_separator(struct util_rec *rec); -+ -+void util_rec_set_indent(struct util_rec *rec, int indent); - - #endif /** LIB_UTIL_REC_H @} */ -diff --git a/libutil/util_rec.c b/libutil/util_rec.c -index 621bebc..c216cf9 100644 ---- a/libutil/util_rec.c -+++ b/libutil/util_rec.c -@@ -64,6 +64,7 @@ struct rec_fmt { - int argz_sep; - } csv_p; - } d; -+ int indent; - }; - - /* -@@ -122,9 +123,50 @@ struct util_rec *util_rec_new_wide(const char *hdr_sep) - rec->fmt.type = REC_FMT_WIDE; - rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep); - rec->fmt.d.wide_p.argz_sep = ','; -+ rec->fmt.indent = 0; - return rec; - } - -+/* -+ * Print the indentation characters -+ */ -+static inline void rec_print_indention(int indent) -+{ -+ if (indent <= 0) -+ return; -+ -+ printf("%*s", indent, ""); -+} -+ -+/* -+ * Print record separator in "wide" output format -+ */ -+static void rec_print_wide_separator(struct util_rec *rec) -+{ -+ const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep; -+ int size = 0, field_count = 0; -+ struct util_rec_fld *fld; -+ char *buf; -+ -+ if (!hdr_sep) -+ return; -+ -+ util_list_iterate(rec->list, fld) { -+ if (fld->hdr) { -+ size += fld->width; -+ field_count++; -+ } -+ } -+ -+ size += field_count - 1; -+ buf = util_malloc(size + 1); -+ memset(buf, (int)hdr_sep[0], size); -+ buf[size] = 0; -+ rec_print_indention(rec->fmt.indent); -+ printf("%s\n", buf); -+ free(buf); -+} -+ - /* - * Print record header in "wide" output format - */ -@@ -135,6 +177,7 @@ static void rec_print_wide_hdr(struct util_rec *rec) - struct util_rec_fld *fld; - char *buf; - -+ rec_print_indention(rec->fmt.indent); - util_list_iterate(rec->list, fld) { - if (col_nr) - printf(" "); -@@ -156,6 +199,7 @@ static void rec_print_wide_hdr(struct util_rec *rec) - buf = util_malloc(size + 1); - memset(buf, (int)hdr_sep[0], size); - buf[size] = 0; -+ rec_print_indention(rec->fmt.indent); - printf("%s\n", buf); - free(buf); - } -@@ -172,6 +216,7 @@ void rec_print_wide(struct util_rec *rec) - int fld_count = 0; - char *entry; - -+ rec_print_indention(rec->fmt.indent); - util_list_iterate(rec->list, fld) { - if (!fld->hdr) - continue; -@@ -225,6 +270,7 @@ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep, - rec->fmt.d.long_p.key_size = key_size; - rec->fmt.d.long_p.val_size = val_size; - rec->fmt.d.long_p.argz_sep = ' '; -+ rec->fmt.indent = 0; - return rec; - } - -@@ -241,6 +287,7 @@ static void rec_print_long_hdr(struct util_rec *rec) - fld = rec_get_fld(rec, p->key); - util_assert(fld != NULL, "Record not found\n"); - util_assert(fld->hdr != NULL, "Header for field not found\n"); -+ rec_print_indention(rec->fmt.indent); - if (p->col_sep) { - printf("%-*s %s %-*s\n", p->key_size, fld->hdr, - p->col_sep, fld->width, fld->val); -@@ -255,6 +302,7 @@ static void rec_print_long_hdr(struct util_rec *rec) - buf = util_malloc(len + 1); - memset(buf, p->hdr_sep[0], len); - buf[len] = 0; -+ rec_print_indention(rec->fmt.indent); - printf("%s\n", buf); - free(buf); - } -@@ -277,19 +325,24 @@ static void rec_print_long(struct util_rec *rec) - continue; - if (!fld->val) - continue; -+ rec_print_indention(rec->fmt.indent); - item = argz_next(fld->val, fld->len, item); - if (p->col_sep) { - printf(" %-*s %s %s\n", - p->key_size - 8, fld->hdr, p->col_sep, item); -- while ((item = argz_next(fld->val, fld->len, item))) -+ while ((item = argz_next(fld->val, fld->len, item))) { -+ rec_print_indention(rec->fmt.indent); - printf(" %-*s %c %s\n", - p->key_size - 8, "", p->argz_sep, item); -+ } - } else { - printf(" %-*s %s\n", - p->key_size - 8, fld->hdr, fld->val); -- while ((item = argz_next(fld->val, fld->len, item))) -+ while ((item = argz_next(fld->val, fld->len, item))) { -+ rec_print_indention(rec->fmt.indent); - printf(" %-*s %s\n", - p->key_size - 8, "", item); -+ } - } - } - printf("\n"); -@@ -320,6 +373,7 @@ struct util_rec *util_rec_new_csv(const char *col_sep) - rec->fmt.type = REC_FMT_CSV; - rec->fmt.d.csv_p.col_sep = util_strdup(col_sep); - rec->fmt.d.csv_p.argz_sep = ' '; -+ rec->fmt.indent = 0; - return rec; - } - -@@ -332,6 +386,7 @@ void rec_print_csv_hdr(struct util_rec *rec) - struct util_rec_fld *fld; - int fld_count = 0; - -+ rec_print_indention(rec->fmt.indent); - util_list_iterate(rec->list, fld) { - if (fld_count) - printf("%c", *col_sep); -@@ -354,6 +409,7 @@ void rec_print_csv(struct util_rec *rec) - int fld_count = 0; - char *item = NULL; - -+ rec_print_indention(rec->fmt.indent); - util_list_iterate(rec->list, fld) { - item = argz_next(fld->val, fld->len, item); - if (fld_count) -@@ -470,6 +526,24 @@ void util_rec_print_hdr(struct util_rec *rec) - } - } - -+/** -+ * Print record separator according to output format -+ * -+ * @param[in] rec Record pointer -+ */ -+void util_rec_print_separator(struct util_rec *rec) -+{ -+ switch (rec->fmt.type) { -+ case REC_FMT_WIDE: -+ rec_print_wide_separator(rec); -+ break; -+ case REC_FMT_LONG: -+ break; -+ case REC_FMT_CSV: -+ break; -+ } -+} -+ - /** - * Set a field value to an argz vector - * -@@ -537,3 +611,14 @@ const char *util_rec_get(struct util_rec *rec, const char *key) - - return (fld != NULL) ? fld->val : NULL; - } -+ -+/** -+ * Sets the indentation of the record -+ * -+ * @param[in] rec Record pointer -+ * @param[in] indent Number of characters to indent -+ */ -+void util_rec_set_indent(struct util_rec *rec, int indent) -+{ -+ rec->fmt.indent = indent; -+} -diff --git a/libutil/util_rec_example.c b/libutil/util_rec_example.c -index bc38bb4..801f909 100644 ---- a/libutil/util_rec_example.c -+++ b/libutil/util_rec_example.c -@@ -42,6 +42,8 @@ static void print_records(const char *format, struct util_rec *rec) - /* Print the record */ - util_rec_print(rec); - } -+ /* Print a separator line (is a nop for long and csv format) */ -+ util_rec_print_separator(rec); - printf("\n"); - } - -@@ -72,6 +74,11 @@ int main(void) - print_records("Wide format", rec); - util_rec_free(rec); - -+ rec = util_rec_new_wide("-"); -+ util_rec_set_indent(rec, 4); -+ print_records("Wide format with indentation", rec); -+ util_rec_free(rec); -+ - rec = util_rec_new_long("-", ":", "number", 30, 20); - print_records("Long format", rec); - util_rec_free(rec); -diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile -index d075f34..f679775 100644 ---- a/zconf/zcrypt/Makefile -+++ b/zconf/zcrypt/Makefile -@@ -1,24 +1,27 @@ - include ../../common.mak - --all: chzcrypt lszcrypt zcryptctl -+all: chzcrypt lszcrypt zcryptctl zcryptstats - - libs = $(rootdir)/libutil/libutil.a - - chzcrypt: chzcrypt.o misc.o $(libs) - lszcrypt: lszcrypt.o misc.o $(libs) - zcryptctl: zcryptctl.o misc.o $(libs) -+zcryptstats: zcryptstats.o $(libs) - - install: all - $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR) -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptstats $(DESTDIR)$(BINDIR) - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8 -+ $(INSTALL) -m 644 -c zcryptstats.8 $(DESTDIR)$(MANDIR)/man8 - - clean: -- rm -f *.o chzcrypt lszcrypt zcryptctl -+ rm -f *.o chzcrypt lszcrypt zcryptctl zcryptstats - - .PHONY: all install clean -diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8 -new file mode 100644 -index 0000000..8c31c69 ---- /dev/null -+++ b/zconf/zcrypt/zcryptstats.8 -@@ -0,0 +1,247 @@ -+.\" Copyright IBM Corp. 2019 -+.\" s390-tools is free software; you can redistribute it and/or modify -+.\" it under the terms of the MIT license. See LICENSE for details. -+.\" -+.TH ZCRYPTSTATS 1 "January 2019" "s390-tools" -+.SH NAME -+zcryptstats \- Display usage statistics of IBM Crypto Express adapters -+. -+. -+.SH SYNOPSIS -+.B zcryptstats -+.RI [ OPTIONS ] -+.RI [ DEVICE_ID -+[...] ] -+. -+.PP -+.B zcryptstats -+.BR \-\-help | \-h -+.br -+.B zcryptstats -+.BR \-\-version | \-v -+. -+. -+. -+.SH DESCRIPTION -+. -+Use \fBzcryptstats\fP to display usage statistics of IBM Crypto Express -+adapters. -+.PP -+\fBzcryptstats\fP obtains cryptographic performance measurement data -+periodically and displays the data for each cryptographic device for each -+interval. -+A cryptographic device can be either a card device or a queue device (APQN). -+\fBzcryptstats\fP runs forever unless you limit the number of intervals with -+the \fB\-\-count\fP option. The default interval time is 10 seconds. -+Use the \fB\-\-interval\fP option to specify a different interval time. -+.PP -+By default, all available cryptographic devices are monitored. -+You can optionally specify the device IDs of the devices to be monitored. -+The card device representation and the queue device are both in hexadecimal -+notation. -+.PP -+Use the \fB\-\-no-apqn\fP option to omit the performance measurement data of -+the queues. If the system does not support obtaining cryptographic -+performance measurement data on the queue devices, only the card devices -+are monitored. -+.PP -+For each device, a set of counters is displayed. The amount and meaning of the -+counters are dependent on the device type and mode, see the COUNTERS section. -+For each counter and interval, the following values are displayed: -+.RS 2 -+.IP "\(bu" 2 -+Number of measured operations. -+.IP "\(bu" 2 -+Rate of the measured operation in operations per second. -+.IP "\(bu" 2 -+Utilization of the device in percent. -+.IP "\(bu" 2 -+Average duration of the operations. -+.RE -+.PP -+The sum of all operations is displayed in a separate \fBtotals\fP line. -+Use the \fB\-\-only-totals\fP option to omit the individual counters and -+display the totals only. Use the \fB\-\-no\-totals\fP option to omit the -+totals. -+.PP -+ -+.B Note: -+The utilization value of a counter can exceed 100%. This value is caused by -+the parallel execution of cryptographic operations. -+.PP -+Cryptographic performance measurement data might not be available when Linux -+is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an -+error message and exits. -+. -+. -+. -+.SH OPTIONS -+. -+.TP -+.BR DEVICE_ID -+Specifies a cryptographic device for which statistics are displayed. -+A device ID can either be a card device ID -+(\fI\fP) or a queue device (APQN) ID (\fI.\fP). -+To filter all devices by domain, provide \fI.\fP. -+If no IDs are given, statistics are displayed for all available devices. -+. -+.TP -+.BR \-i ", " \-\-interval\~\fIINTERVAL\fP -+Specifies the interval time in seconds. If this option is omitted, then the -+default interval time of 10 seconds is used. -+. -+.TP -+.BR \-c ", " \-\-count\~\fICOUNT\fP -+Specifies the number of reports that are generated at \fIINTERVAL\fP seconds -+apart. If this option is omitted, the \fBzcryptstats\fP command generates -+reports continuously, until it is stopped with control-C. -+. -+.TP -+.BR \-o ", " \-\-output\~\fIJSON\fP|\fITABLE\fP|\fICSV\fP -+Displays the statistics in the specified format. If this option is omitted, a -+comprehensive report is displayed. Supported output formats are: -+.RS 8 -+.IP "\(bu" 2 -+\fBJSON:\fP Displays the statistics in Javascript Object Notation (JSON) format. -+JSON output field order is undefined, and new fields might be added in the -+future. -+.IP "\(bu" 2 -+\fBTABLE:\fP Displays the statistics in a human readable simple table format. -+The individual counters are omitted, and only the totals are displayed. -+This output format implies option \fB\-\-only-totals\fP. -+.IP "\(bu" 2 -+\fBCSV:\fP Displays the statistics in comma-separated values format. The values -+are separated with a semicolon. The individual counters are omitted, and only -+the totals are displayed. This output format implies option -+\fB\-\-only-totals\fP. -+.RE -+. -+.TP -+.BR \-t ", " \-\-no\-totals -+Excludes the totals of all counters of a card device or queue device -+(APQN). This option cannot be specified together with option -+\fB\-\-only\-totals\fP or option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP. -+. -+.TP -+.BR \-T ", " \-\-only\-totals -+Displays only the totals of all counters of a card device or a queue device -+(APQN), but not the individual counters. This option is implied with -+option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP. -+. -+.TP -+.BR \-a ", " \-\-no\-apqn -+Displays only the counters of the card device, but omits the counters of the -+queue device (APQN). If the system does not support obtaining cryptographic -+performance measurement data on the queue devices, this option is implied. -+. -+.TP -+.BR \-M ", " \-\-map\-type\~\fIMAPPING\fP -+Maps unknown cryptographic device types and modes to known types and modes. -+This option should only be used when new, so far unknown cryptographic devices -+are found. You can then map them to known devices and modes, provided that the -+new cryptographic devices report the same counters as the known cryptographic -+device to which it is mapped. -+The mapping specification consists of a comma-separated list of -+\fIFROM\-TYPE\fP:\fIFROM\-MODE\fP=\fITO\-TYPE\fP:\fITO\-MODE\fP specifications. -+The type and mode values must be specified in decimal notation. -+. -+.TP -+.BR \-A ", " \-\-all -+Displays all cards devices and queue devices (APQNs), not only those that are -+available to the Linux instance. Using this option additional cryptographic -+devices that are available in the CEC, but not available to the Linux system -+are also monitored. -+This option cannot be specified together with option \fB\-\-only-online\fP. -+. -+.TP -+.BR \-O ", " \-\-only\-online -+Displays only online cards devices and queue devices (APQNs). This option -+cannot be specified together with option \fB\-\-all\fP. -+. -+.TP -+.BR \-V ", " \-\-verbose -+Displays additional information messages during processing. -+.TP -+.BR \-h ", " \-\-help -+Displays help text and exits. -+.TP -+.BR \-v ", " \-\-version -+Displays version information and exits. -+. -+. -+. -+.SH COUNTERS -+. -+.PP -+.B IBM Crypto Express adapter in accelerator mode: -+.RS 4 -+.TP -+.B All -+All operations on the adapter -+.TP -+.B RSA Key-gen -+RSA-key-generation operations (also included in \fBAll\fP). -+.RE -+.PP -+.B IBM Crypto Express adapter in CCA co-processor mode: -+.RS 4 -+.TP -+.B RSA 1024 ME -+1024-bit ME-format RSA operations. -+.TP -+.B RSA 2048 ME -+2048-bit ME-format RSA operations. -+.TP -+.B RSA 1024 CRT -+1024-bit CRT-format RSA operations. -+.TP -+.B RSA 2048 CRT -+2048-bit CRT-format RSA operations. -+.TP -+.B RSA 4096 ME -+4096-bit ME-format RSA operations. -+.TP -+.B RSA 4096 CTR -+4096-bit CRT-format RSA operations. -+.RE -+.PP -+.B IBM Crypto Express adapter in EP11 co-processor mode: -+.RS 4 -+.TP -+.B Asym. Slow -+Slow asymmetric-key functions. -+.TP -+.B Asym. Fast -+Fast asymmetric-key functions. -+.TP -+.B Symm. Partial -+Symmetric-key functions that return partial or incremental results. -+.TP -+.B Symm. Complete -+Symmetric-key functions that return a complete or final result. -+.TP -+.B Asym. Key-gen -+asymmetric-key generation function. -+.RE -+.PP -+. -+. -+. -+.SH EXAMPLES -+.TP -+.B zcryptstats 02 -+Display statistics for all cryptographic devices with card ID \fB02\fP. -+.TP -+.B zcryptstats 02.0005 --interval 5 -+Display statistics for cryptographic devices with card ID \fB02\fP and domain -+ID \fB0005\fP in a 5 second interval. -+.TP -+.B zcryptstats .0005 --count 10 -+Display statistics for cryptographic devices with domain ID \fB0005\fP with the -+default interval time of 10 seconds, for 10 intervals. -+.TP -+.B zcryptstats 02 --output JSON -+Display statistics for all cryptographic devices with card ID \fB02\fP in -+\fBJSON\fP output format. -+.TP -+ -diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c -new file mode 100644 -index 0000000..136d5ba ---- /dev/null -+++ b/zconf/zcrypt/zcryptstats.c -@@ -0,0 +1,2418 @@ -+/* -+ * zcryptstats - Show usage statistics of IBM Crypto Express adapters -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_base.h" -+#include "lib/util_file.h" -+#include "lib/util_libc.h" -+#include "lib/util_opt.h" -+#include "lib/util_path.h" -+#include "lib/util_prg.h" -+#include "lib/util_rec.h" -+#include "lib/util_scandir.h" -+#include "lib/zt_common.h" -+ -+#ifndef offsetof -+ #define offsetof(type, member) ((size_t) &((type *)0)->member) -+#endif -+#ifndef offsetofend -+ #define offsetofend(type, member) \ -+ (offsetof(type, member) + sizeof(((type *)0)->member)) -+#endif -+ -+#define SYSFS_DEVICES_AP_PATH "devices/ap" -+#define SYSFS_DEVICES_CARD "devices/ap/card%02x" -+#define SYSFS_DEVICES_APQN "devices/ap/card%02x/%02x.%04x" -+#define SYSFS_DEVICES_CARD_ONLINE "devices/ap/card%02x/online" -+#define SYSFS_DEVICES_APQN_ONLINE "devices/ap/card%02x/%02x.%04x/online" -+#define CHSC_DEVICE "/dev/chsc" -+#define NUM_CARDS_OLD 64 -+#define NUM_CARDS 256 -+#define NUM_DOMAINS 256 -+ -+#define MASK_WORD_BITS (sizeof(uint32_t) * 8) -+#define MASK_WORD_NO(n) ((n) / MASK_WORD_BITS) -+#define MASK_BIT_IN_WORD(n) ((n) % MASK_WORD_BITS) -+#define MASK_BIT(n) (0x80000000 >> MASK_BIT_IN_WORD(n)) -+ -+struct chsc_apdn { -+ uint8_t ap_index; -+ uint8_t domain_index; -+} __packed; -+ -+struct chsc_scdmd_request { -+ struct chsc_header header; -+ struct chsc_apdn first_drid; -+ struct chsc_apdn last_drid; -+ uint32_t s:1; -+ uint32_t reserved1:31; -+ uint32_t reserved2; -+ uint32_t apsm[8]; -+ uint32_t dsm[8]; -+} __packed; -+ -+struct chsc_scdmd_response { -+ struct chsc_header header; -+ uint32_t reserved1; -+ uint16_t p:1; -+ uint16_t reserved2:15; -+ struct chsc_apdn crid; -+ uint32_t reserved3; -+} __packed; -+ -+struct chsc_scdmd_area { -+ struct chsc_scdmd_request request; -+ struct chsc_scdmd_response response; -+ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scdmd_request) - -+ sizeof(struct chsc_scdmd_response)]; -+} __packed; -+ -+struct chsc_scmd_request { -+ struct chsc_header header; -+ uint8_t reserved1; -+ uint8_t zeros1:6; -+ uint8_t one:1; -+ uint8_t zero:1; -+ uint8_t fcs; -+ uint8_t lcs; -+ uint32_t reserved2; -+ uint32_t reserved3; -+} __packed; -+ -+struct chsc_scmd_response { -+ struct chsc_header header; -+ uint32_t reserved1; -+ uint32_t p:1; -+ uint32_t reserved2:31; -+ uint32_t reserved3; -+} __packed; -+ -+struct chsc_scmd_area { -+ struct chsc_scmd_request request; -+ struct chsc_scmd_response response; -+ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scmd_request) - -+ sizeof(struct chsc_scmd_response)]; -+} __packed; -+ -+struct chsc_cmb_header { -+ uint8_t reserved1; -+ uint8_t ct; /* AP_DEVICE_TYPE_xxx values */ -+ uint8_t format; -+ uint8_t ax; -+ float s; -+ uint32_t v; -+ uint8_t dx; -+ uint8_t mt; -+ uint16_t l4; -+} __packed; -+ -+struct chsc_cmb_entry { -+ u64 t; -+ u64 c; -+} __packed; -+ -+struct chsc_cmb_area { -+ struct chsc_cmb_header header; -+ struct chsc_cmb_entry entries[32]; -+} __packed; -+ -+#define CRYPTO_TYPE_PCICC 3 -+#define CRYPTO_TYPE_PCICA 4 -+#define CRYPTO_TYPE_PCIXCC 5 -+#define CRYPTO_TYPE_CEX2A 6 -+#define CRYPTO_TYPE_CEX2C 7 -+#define CRYPTO_TYPE_CEX3A 8 -+#define CRYPTO_TYPE_CEX3C 9 -+#define CRYPTO_TYPE_CEX4S 10 -+#define CRYPTO_TYPE_CEX5S 11 -+#define CRYPTO_TYPE_CEX6S 12 -+ -+#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S -+ -+struct crypto_counter { -+ const char *name; -+ bool is_totals; -+}; -+ -+struct crypto_mode { -+ const char *name; -+ char indicatior_char; -+ unsigned int num_counters; -+ const struct crypto_counter *counters; -+}; -+ -+struct crypto_type { -+ const char *name; -+ unsigned int num_modes; -+ const struct crypto_mode *modes; -+}; -+ -+#define NUM_COPROC_COUNTERS 2 -+const struct crypto_counter counter_coproc[NUM_COPROC_COUNTERS] = { -+ { .name = "All", .is_totals = true }, -+ { .name = "RSA Key-gen" }, -+}; -+ -+#define NUM_ACCEL_COUNTERS 6 -+const struct crypto_counter counter_accel[NUM_ACCEL_COUNTERS] = { -+ { .name = "RSA 1024 ME" }, -+ { .name = "RSA 2048 ME" }, -+ { .name = "RSA 1024 CRT" }, -+ { .name = "RSA 2048 CRT" }, -+ { .name = "RSA 4096 ME" }, -+ { .name = "RSA 4096 CTR" }, -+}; -+ -+#define NUM_EP11_COUNTERS 5 -+const struct crypto_counter counter_ep11[NUM_EP11_COUNTERS] = { -+ { .name = "Asym. Slow" }, -+ { .name = "Asym. Fast" }, -+ { .name = "Symm. Partial" }, -+ { .name = "Symm. Complete" }, -+ { .name = "Asym. Key-gen" }, -+}; -+ -+#define NUM_PCICA_COUNTERS 20 -+const struct crypto_counter counter_pcica[NUM_PCICA_COUNTERS] = { -+ { .name = "RSA 1024 ME (E0)" }, -+ { .name = "RSA 2048 ME (E0)" }, -+ { .name = "RSA 1024 CRT (E0)" }, -+ { .name = "RSA 2048 CRT (E0)" }, -+ { .name = "RSA 1024 ME (E1)" }, -+ { .name = "RSA 2048 ME (E1)" }, -+ { .name = "RSA 1024 CRT (E1)" }, -+ { .name = "RSA 2048 CRT (E1)" }, -+ { .name = "RSA 1024 ME (E2)" }, -+ { .name = "RSA 2048 ME (E2)" }, -+ { .name = "RSA 1024 CRT (E2)" }, -+ { .name = "RSA 2048 CRT (E2)" }, -+ { .name = "RSA 1024 ME (E3)" }, -+ { .name = "RSA 2048 ME (E3)" }, -+ { .name = "RSA 1024 CRT (E3)" }, -+ { .name = "RSA 2048 CRT (E3)" }, -+ { .name = "RSA 1024 ME (E4)" }, -+ { .name = "RSA 2048 ME (E4)" }, -+ { .name = "RSA 1024 CRT (E4)" }, -+ { .name = "RSA 2048 CRT (E4)" }, -+}; -+ -+#define NUM_COPROC_MODES 1 -+const struct crypto_mode mode_coproc[1] = { -+ { .num_counters = NUM_COPROC_COUNTERS, -+ .counters = counter_coproc}, -+}; -+ -+#define NUM_ACCEL_MODES 1 -+const struct crypto_mode mode_accel[1] = { -+ { .num_counters = NUM_ACCEL_COUNTERS, -+ .counters = counter_accel }, -+}; -+ -+#define NUM_PCICA_MODES 1 -+const struct crypto_mode mode_pcica[1] = { -+ { .num_counters = NUM_PCICA_COUNTERS, -+ .counters = counter_pcica }, -+}; -+ -+#define NUM_CEX456_MODES 11 -+const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { .name = "Accelerator", .indicatior_char = 'A', -+ .num_counters = NUM_ACCEL_COUNTERS, -+ .counters = counter_accel }, -+ { .name = "CCA co-processor", .indicatior_char = 'C', -+ .num_counters = NUM_COPROC_COUNTERS, -+ .counters = counter_coproc}, -+ { .name = "EP11 co-processor", .indicatior_char = 'P', -+ .num_counters = NUM_EP11_COUNTERS, -+ .counters = counter_ep11 }, -+}; -+ -+#define NUM_CRYPTO_TYPES 13 -+const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { -+ { 0 }, -+ { 0 }, -+ { 0 }, -+ { .name = "PCICC", .num_modes = NUM_COPROC_MODES, -+ .modes = mode_coproc }, -+ { .name = "PCICA", .num_modes = NUM_PCICA_MODES, -+ .modes = mode_pcica }, -+ { .name = "PCIXCC", .num_modes = NUM_COPROC_MODES, -+ .modes = mode_coproc}, -+ { .name = "CEX2A", .num_modes = NUM_ACCEL_MODES, -+ .modes = mode_accel }, -+ { .name = "CEX2C", .num_modes = NUM_COPROC_MODES, -+ .modes = mode_coproc }, -+ { .name = "CEX3A", .num_modes = NUM_ACCEL_MODES, -+ .modes = mode_accel }, -+ { .name = "CEX3C", .num_modes = NUM_COPROC_MODES, -+ .modes = mode_coproc }, -+ { .name = "CEX4", .num_modes = NUM_CEX456_MODES, -+ .modes = mode_cex456 }, -+ { .name = "CEX5", .num_modes = NUM_CEX456_MODES, -+ .modes = mode_cex456 }, -+ { .name = "CEX6", .num_modes = NUM_CEX456_MODES, -+ .modes = mode_cex456 }, -+}; -+ -+ -+struct type_mapping { -+ uint8_t from_type; -+ uint8_t from_mode; -+ uint8_t to_type; -+ uint8_t to_mode; -+ struct type_mapping *next; -+}; -+ -+struct device_selection { -+ int card; -+ int domain; /* -1 if not specified */ -+ struct device_selection *next; -+}; -+ -+struct interval_data { -+ bool current_valid; -+ bool previous_valid; -+ struct chsc_cmb_area current; -+ struct chsc_cmb_area previous; -+}; -+ -+struct card_data { -+ struct interval_data data; -+ struct interval_data *domains[NUM_DOMAINS]; -+}; -+ -+struct interval_values { -+ u64 count; -+ double rate; -+ double utilization; -+ double duration; -+}; -+ -+struct print_func { -+ int (*print_initialize)(void); -+ int (*print_terminate)(void); -+ int (*print_header)(void); -+ int (*print_footer)(void); -+ int (*print_interval_header)(unsigned long interval_count, -+ const char *timestamp); -+ int (*print_interval_footer)(void); -+ int (*print_device_header)(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *type, const char *timestamp); -+ int (*print_device_footer)(void); -+ int (*print_counter_data)(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *type, const char *timestamp, -+ const char *name, -+ struct interval_values *vals); -+ int (*print_counter_separator)(void); -+}; -+ -+#define pr_call(func) g.print_funcs->func == NULL ? 0 : g.print_funcs->func -+ -+static int default_print_initialize(void); -+static int default_print_terminate(void); -+static int default_print_header(void); -+static int default_print_footer(void); -+static int default_print_interval_header(unsigned long interval_count, -+ const char *timestamp); -+static int default_print_device_header(bool is_apqn, uint8_t card, -+ uint8_t domain, const char *type, -+ const char *timestamp); -+static int default_print_device_footer(void); -+static int default_print_counter_data(bool is_apqn, uint8_t card, -+ uint8_t domain, const char *type, -+ const char *timestamp, const char *name, -+ struct interval_values *vals); -+static int default_print_counter_separator(void); -+ -+static const struct print_func default_print = { -+ .print_initialize = default_print_initialize, -+ .print_terminate = default_print_terminate, -+ .print_header = default_print_header, -+ .print_footer = default_print_footer, -+ .print_interval_header = default_print_interval_header, -+ .print_device_header = default_print_device_header, -+ .print_device_footer = default_print_device_footer, -+ .print_counter_data = default_print_counter_data, -+ .print_counter_separator = default_print_counter_separator, -+}; -+ -+static int json_print_initialize(void); -+static int json_print_header(void); -+static int json_print_footer(void); -+static int json_print_interval_header(unsigned long interval_count, -+ const char *timestamp); -+static int json_print_interval_footer(void); -+static int json_print_device_header(bool is_apqn, uint8_t card, -+ uint8_t domain, const char *type, -+ const char *timestamp); -+static int json_print_device_footer(void); -+static int json_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *type, const char *timestamp, -+ const char *name, -+ struct interval_values *vals); -+ -+static const struct print_func json_print = { -+ .print_initialize = json_print_initialize, -+ .print_header = json_print_header, -+ .print_footer = json_print_footer, -+ .print_interval_header = json_print_interval_header, -+ .print_interval_footer = json_print_interval_footer, -+ .print_device_header = json_print_device_header, -+ .print_device_footer = json_print_device_footer, -+ .print_counter_data = json_print_counter_data, -+}; -+ -+ -+static int table_print_initialize(void); -+static int table_print_terminate(void); -+static int table_print_header(void); -+static int table_print_interval_footer(void); -+static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *type, const char *timestamp, -+ const char *name, -+ struct interval_values *vals); -+ -+static const struct print_func table_print = { -+ .print_initialize = table_print_initialize, -+ .print_terminate = table_print_terminate, -+ .print_header = table_print_header, -+ .print_interval_footer = table_print_interval_footer, -+ .print_counter_data = table_print_counter_data, -+}; -+ -+static int csv_print_initialize(void); -+static int csv_print_terminate(void); -+static int csv_print_header(void); -+static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *type, const char *timestamp, -+ const char *name, -+ struct interval_values *vals); -+ -+static const struct print_func csv_print = { -+ .print_initialize = csv_print_initialize, -+ .print_terminate = csv_print_terminate, -+ .print_header = csv_print_header, -+ .print_counter_data = csv_print_counter_data, -+}; -+ -+/* -+ * Program configuration -+ */ -+const struct util_prg prg = { -+ .desc = "Display usage statistics of IBM Crypto Express adapters", -+ .args = "[DEVICE_IDS]", -+ .copyright_vec = { -+ { -+ .owner = "IBM Corp.", -+ .pub_first = 2019, -+ .pub_last = 2019, -+ }, -+ UTIL_PRG_COPYRIGHT_END -+ } -+}; -+ -+/* -+ * Global variables for program options -+ */ -+static struct zcryptstats_globals { -+ long interval; -+ unsigned long count; -+ bool no_totals; -+ bool only_totals; -+ bool no_apqn; -+ char *map_type; -+ char **device_ids; -+ bool all; -+ bool only_online; -+ bool verbose; -+ int chsc_fd; -+ uint8_t max_card_used; -+ uint32_t card_mask[8]; -+ uint8_t min_card; -+ uint8_t max_card; -+ uint32_t domain_mask[8]; -+ uint8_t min_domain; -+ uint8_t max_domain; -+ struct device_selection *dev_selection; -+ struct type_mapping *type_mapping; -+ struct card_data *cards[NUM_CARDS]; -+ const struct print_func *print_funcs; -+ struct util_rec *device_rec; -+ struct util_rec *counter_rec; -+ bool first_device; -+ bool first_counter; -+} g = { -+ .interval = 10, -+ .chsc_fd = -1, -+ .print_funcs = &default_print, -+}; -+ -+ -+static volatile bool quit; -+ -+/* -+ * Configuration of command line options -+ */ -+static struct util_opt opt_vec[] = { -+ /***********************************************************/ -+ { -+ .flags = UTIL_OPT_FLAG_SECTION, -+ .desc = "OPTIONS", -+ }, -+ { -+ .option = {"interval", required_argument, NULL, 'i'}, -+ .argument = "INTERVAL", -+ .desc = "Specifies the interval time in seconds. If omitted, a " -+ "default interval of 10 seconds is used", -+ }, -+ { -+ .option = {"count", required_argument, NULL, 'c'}, -+ .argument = "COUNT", -+ .desc = "Specifies the number of reports that are generated " -+ "at INTERVAL seconds apart. If omitted, reports are " -+ "generated continuously, until stopped with control-C", -+ }, -+ { -+ .option = {"output", required_argument, NULL, 'o'}, -+ .argument = "JSON|TABLE|CSV", -+ .desc = "Displays the statistics in the specified format. If " -+ "this option is omitted, a comprehensive report is " -+ "displayed. Supported output formats are: JSON, TABLE, " -+ "CSV. With TABLE and CSV the display of the individual " -+ "counters are omitted, and only the totals are " -+ "displayed. CSV and TABLE output formats imply option " -+ "--only-totals", -+ }, -+ { -+ .option = {"no-totals", 0, NULL, 't'}, -+ .desc = "Excludes the totals of all counters of a card " -+ "device or queue device (APQN). It can not be " -+ "specified together with option --only-totals or " -+ "option --output TABLE|CSV", -+ }, -+ { -+ .option = {"only-totals", 0, NULL, 'T'}, -+ .desc = "Displays only the totals of all counters of a card " -+ "device or a queue device (APQN), but not the " -+ "individual counters. This option is implied with " -+ "option --output TABLE|CSV", -+ }, -+ { -+ .option = {"no-apqn", 0, NULL, 'a'}, -+ .desc = "Displays only the counters of the card device, but " -+ "omits the counters of the queue device (APQN). If the " -+ "system does not support obtaining cryptographic " -+ "performance measurement data on the queue devices, " -+ "then this option is implied", -+ }, -+ { -+ .option = {"map-type", required_argument, NULL, 'M'}, -+ .argument = "MAPPING", -+ .desc = "Maps unknown cryptographic device types and modes to " -+ "known types and modes. This option should only be " -+ "used when new, so far unknown cryptographic devices " -+ "are found. You can then map them to known devices and " -+ "modes, provided that the new cryptographic devices " -+ "report the same counters as the known cryptographic " -+ "device to which it is mapped. The mapping " -+ "specification consists of a comma-separated list of " -+ "FROM-TYPE:FROM-MODE=TO-TYPE:TO-MODE specifications. " -+ "The type and mode values must be specified in decimal " -+ "notation", -+ }, -+ { -+ .option = {"all", 0, NULL, 'A'}, -+ .desc = "Displays all cards devices and queue devices (APQNs), " -+ "not only those that are available to the Linux " -+ "system. Using this option additional cryptographic " -+ "devices that are available in the CEC, but not " -+ "available to the Linux system are also monitored. " -+ "This option can not be specified together with option " -+ "--only-online", -+ }, -+ { -+ .option = {"only-online", 0, NULL, 'O'}, -+ .desc = "Displays only online cards devices and queue devices " -+ "(APQNs). This option can not be specified together " -+ "with option --all" -+ }, -+ { -+ .option = {"verbose", 0, NULL, 'V'}, -+ .desc = "Prints additional information messages during " -+ "processing", -+ }, -+ UTIL_OPT_HELP, -+ UTIL_OPT_VERSION, -+ UTIL_OPT_END -+}; -+ -+#define pr_verbose(fmt...) do { \ -+ if (g.verbose) \ -+ warnx(fmt); \ -+ } while (0) -+ -+/* -+ * Describe adapter ids -+ */ -+static void print_adapter_id_help(void) -+{ -+ printf("\n"); -+ printf("DEVICE_IDS\n"); -+ util_print_indented(" List of cryptographic device IDs separated by " -+ "blanks for which statistics are displayed. " -+ "DEVICE_ID can either be a card device ID " -+ "('') or a queue device ID (." -+ "'). To filter all devices by domain, " -+ "provide '.'. If no IDs are given, " -+ "statistics are displayed for all available " -+ "devices.", 2); -+ printf("\n"); -+ printf("EXAMPLE:\n"); -+ util_print_indented(" Display statistics for all cryptographic " -+ "devices with card ID '02.", 2); -+ printf(" # zcryptstats 02\n"); -+ printf("\n"); -+ util_print_indented(" Display statistics for cryptographic devices " -+ "with card ID '02' and domain ID '0005'.", 2); -+ printf(" # zcryptstats 02.0005\n"); -+ printf("\n"); -+} -+ -+static struct type_mapping *find_type_mapping(uint8_t from_type, -+ uint8_t from_mode) -+{ -+ struct type_mapping *map = g.type_mapping; -+ -+ while (map != NULL) { -+ if (map->from_type == from_type && map->from_mode == from_mode) -+ return map; -+ map = map->next; -+ } -+ return NULL; -+} -+ -+/* -+ * Get the name of the card for a crypto type and mode. -+ * Note: This function might return the address of a static string variable. -+ * It is only valid until this function is called again. -+ */ -+static const char *get_card_name(uint8_t type, uint8_t mode) -+{ -+ const struct crypto_type *ct; -+ const struct crypto_mode *m; -+ static char temp_name[250]; -+ struct type_mapping *map; -+ -+ map = find_type_mapping(type, mode); -+ if (map != NULL) { -+ type = map->to_type; -+ mode = map->to_mode; -+ } else if (type >= NUM_CRYPTO_TYPES) { -+ type = CRYPTO_TYPE_TOLERATION; -+ } -+ -+ if (type >= NUM_CRYPTO_TYPES) -+ return "UNKNOWN ADAPTER TYPE"; -+ -+ ct = &crypto_types[type]; -+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) -+ return "UNKNOWN ADAPTER TYPE"; -+ -+ if (mode >= ct->num_modes) -+ return ct->name; -+ -+ m = &ct->modes[mode]; -+ snprintf(temp_name, sizeof(temp_name) - 1, "%s%c (%s)", ct->name, -+ m->indicatior_char, m->name != NULL ? m->name : ""); -+ return temp_name; -+} -+ -+/* -+ * Get the name of a counter for a crypto type, mode and index. -+ * Note: This function might return the address of a static string variable. -+ * It is only valid until this function is called again. -+ */ -+static const char *get_counter_name(uint8_t type, uint8_t mode, uint8_t index) -+{ -+ const struct crypto_type *ct; -+ const struct crypto_mode *m; -+ static char temp_name[250]; -+ struct type_mapping *map; -+ -+ map = find_type_mapping(type, mode); -+ if (map != NULL) { -+ type = map->to_type; -+ mode = map->to_mode; -+ } else if (type >= NUM_CRYPTO_TYPES) { -+ type = CRYPTO_TYPE_TOLERATION; -+ } -+ -+ if (type >= NUM_CRYPTO_TYPES) -+ goto generic; -+ -+ ct = &crypto_types[type]; -+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) -+ goto generic; -+ -+ if (mode >= ct->num_modes) -+ goto generic; -+ -+ m = &ct->modes[mode]; -+ if (m->counters == NULL || m->num_counters == 0) -+ goto generic; -+ -+ if (index >= m->num_counters) -+ goto generic; -+ -+ return m->counters[index].name; -+ -+generic: -+ snprintf(temp_name, sizeof(temp_name) - 1, "COUNTER %u", index); -+ return temp_name; -+} -+ -+/* -+ * Returns true if a counter for a crypto type, mode and index represents the -+ * total number of operations. -+ */ -+static bool is_counter_totals(uint8_t type, uint8_t mode, uint8_t index) -+{ -+ const struct crypto_type *ct; -+ const struct crypto_mode *m; -+ struct type_mapping *map; -+ -+ map = find_type_mapping(type, mode); -+ if (map != NULL) { -+ type = map->to_type; -+ mode = map->to_mode; -+ } else if (type >= NUM_CRYPTO_TYPES) { -+ type = CRYPTO_TYPE_TOLERATION; -+ } -+ -+ if (type >= NUM_CRYPTO_TYPES) -+ return false; -+ -+ ct = &crypto_types[type]; -+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0) -+ return false; -+ -+ if (mode >= ct->num_modes) -+ return false; -+ -+ m = &ct->modes[mode]; -+ if (m->counters == NULL || m->num_counters == 0) -+ return false; -+ -+ if (index >= m->num_counters) -+ return false; -+ -+ return m->counters[index].is_totals; -+} -+ -+/* -+ * Returns true if the card is available -+ */ -+static bool is_card_available(uint8_t card) -+{ -+ char *path; -+ bool ret; -+ -+ path = util_path_sysfs(SYSFS_DEVICES_CARD, card); -+ ret = util_path_is_dir(path); -+ free(path); -+ -+ return ret; -+} -+ -+/* -+ * Returns true if the APQN is available -+ */ -+static bool is_apqn_available(uint8_t card, uint8_t domain) -+{ -+ char *path; -+ bool ret; -+ -+ path = util_path_sysfs(SYSFS_DEVICES_APQN, card, card, domain); -+ ret = util_path_is_dir(path); -+ free(path); -+ -+ return ret; -+} -+ -+/* -+ * Returns true if the card is online -+ */ -+static bool is_card_online(uint8_t card) -+{ -+ unsigned long online; -+ char *path; -+ int rc; -+ -+ path = util_path_sysfs(SYSFS_DEVICES_CARD_ONLINE, card); -+ rc = util_file_read_ul(&online, 10, path); -+ free(path); -+ -+ return rc == 0 && online != 0; -+} -+ -+/* -+ * Returns true if the APQN is online -+ */ -+static bool is_apqn_online(uint8_t card, uint8_t domain) -+{ -+ unsigned long online; -+ char *path; -+ int rc; -+ -+ path = util_path_sysfs(SYSFS_DEVICES_APQN_ONLINE, card, card, domain); -+ rc = util_file_read_ul(&online, 10, path); -+ free(path); -+ -+ if (rc != 0) -+ return false; -+ -+ return online != 0; -+} -+ -+/* -+ * Updates the APQNs data with data for the current interval. -+ */ -+static void update_apqn_data(uint8_t card, uint8_t domain, -+ struct chsc_cmb_area *cmb, size_t cmb_len) -+{ -+ struct card_data *cd = g.cards[card]; -+ struct interval_data *dd; -+ -+ if (cd == NULL) { -+ cd = util_malloc(sizeof(struct card_data)); -+ memset(cd, 0, sizeof(struct card_data)); -+ g.cards[card] = cd; -+ -+ pr_verbose("Card %02x added", card); -+ } -+ -+ dd = cd->domains[domain]; -+ if (dd == NULL) { -+ dd = util_malloc(sizeof(struct interval_data)); -+ memset(dd, 0, sizeof(struct interval_data)); -+ cd->domains[domain] = dd; -+ -+ pr_verbose("APQN %02x.%04x added", card, domain); -+ } else { -+ if (!dd->current_valid) { -+ dd->previous = dd->current; -+ dd->previous_valid = true; -+ } -+ } -+ -+ memset(&dd->current, 0, sizeof(struct chsc_cmb_area)); -+ memcpy(&dd->current, cmb, cmb_len); -+ dd->current_valid = true; -+} -+ -+/* -+ * Updates the card's data with data for the current interval. -+ */ -+static void update_card_data(uint8_t card, struct chsc_cmb_area *cmb, -+ size_t cmb_len) -+{ -+ struct card_data *cd = g.cards[card]; -+ -+ if (cd == NULL) { -+ cd = util_malloc(sizeof(struct card_data)); -+ memset(cd, 0, sizeof(struct card_data)); -+ g.cards[card] = cd; -+ -+ pr_verbose("Card %02x added", card); -+ } else { -+ if (!cd->data.current_valid) { -+ cd->data.previous = cd->data.current; -+ cd->data.previous_valid = true; -+ } -+ }; -+ -+ memset(&cd->data.current, 0, sizeof(struct chsc_cmb_area)); -+ memcpy(&cd->data.current, cmb, cmb_len); -+ cd->data.current_valid = true; -+} -+ -+/* -+ * Frees the interval data of a card -+ */ -+static void free_card_data(struct card_data *cd) -+{ -+ struct interval_data *dd; -+ int domain; -+ -+ if (cd == NULL) -+ return; -+ -+ for (domain = 0; domain < NUM_DOMAINS; domain++) { -+ dd = cd->domains[domain]; -+ if (dd == NULL) -+ continue; -+ -+ free(dd); -+ cd->domains[domain] = NULL; -+ } -+ -+ free(cd); -+} -+ -+/* -+ * Frees the interval data -+ */ -+static void free_interval_data(void) -+{ -+ struct card_data *cd; -+ int card; -+ -+ for (card = 0; card < NUM_CARDS; card++) { -+ cd = g.cards[card]; -+ if (cd == NULL) -+ continue; -+ -+ free_card_data(cd); -+ g.cards[card] = NULL; -+ } -+} -+ -+/* -+ * Returns the highest card index used by the system. -+ * If there is a card with an index > 0x3f, then the returned number is 0xff, -+ * else 0x3f is returned. -+ */ -+static int get_max_card_index(uint8_t *max_index) -+{ -+ struct dirent **dev_vec = NULL; -+ int i, count, card, rc = 0; -+ char *path; -+ -+ path = util_path_sysfs(SYSFS_DEVICES_AP_PATH); -+ if (!util_path_is_dir(path)) { -+ warnx("Crypto device driver is not available"); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ count = util_scandir(&dev_vec, NULL, path, "card[0-9a-fA-F]+"); -+ if (count < 1) { -+ warnx("No crypto card devices found"); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ *max_index = NUM_CARDS_OLD - 1; -+ for (i = 0; i < count; i++) { -+ if (sscanf(dev_vec[i]->d_name, "card%x", &card) != 1) -+ continue; -+ if (card >= NUM_CARDS_OLD) -+ *max_index = NUM_CARDS - 1; -+ } -+ -+ pr_verbose("Max card index used: %u", *max_index); -+ -+out: -+ free(path); -+ if (dev_vec != NULL) -+ free(dev_vec); -+ return rc; -+} -+ -+/* -+ * Returns the size of the CMB. The size is either contained in l4 field -+ * of the cmb, or a fix length dependent on the crypto type, if l4 is zero. -+ */ -+static size_t get_cmb_length(struct chsc_cmb_area *cmb) -+{ -+ size_t len = cmb->header.l4; -+ -+ if (len != 0) -+ return len; -+ -+ switch (cmb->header.ct) { -+ case CRYPTO_TYPE_PCICC: -+ case CRYPTO_TYPE_PCIXCC: -+ case CRYPTO_TYPE_CEX2C: -+ case CRYPTO_TYPE_CEX3C: -+ return 64; -+ case CRYPTO_TYPE_PCICA: -+ return 336; -+ case CRYPTO_TYPE_CEX2A: -+ case CRYPTO_TYPE_CEX3A: -+ return 80; -+ default: -+ warnx("Zero length value in CMB"); -+ return 0; -+ } -+} -+ -+/* -+ * Return true if the device is in the device selection list and mask -+ */ -+static bool filter_device(uint8_t card, uint8_t domain, bool is_apqn) -+{ -+ struct device_selection *dev; -+ bool found; -+ -+ /* Check for selection mask */ -+ if ((g.card_mask[MASK_WORD_NO(card)] & -+ MASK_BIT(card)) == 0) { -+ pr_verbose("Skipping card %02x (mask)", card); -+ return false; -+ } -+ if (is_apqn) { -+ if ((g.domain_mask[MASK_WORD_NO(domain)] & -+ MASK_BIT(domain)) == 0) { -+ pr_verbose("Skipping APQN %02x.%04x (mask)", card, -+ domain); -+ return false; -+ } -+ } -+ -+ /* Check for device selection list */ -+ if (g.dev_selection != NULL) { -+ dev = g.dev_selection; -+ found = false; -+ while (dev != NULL) { -+ if (is_apqn == false) { -+ /* Its a card */ -+ if (card == (dev->card >= 0 ? dev->card : -+ card)) { -+ found = true; -+ break; -+ } -+ } else { -+ /* Its an APQN */ -+ if (card == (dev->card >= 0 ? dev->card : -+ card) && -+ domain == (dev->domain >= 0 ? dev->domain : -+ domain)) { -+ found = true; -+ break; -+ } -+ } -+ -+ dev = dev->next; -+ } -+ -+ if (!found) { -+ if (is_apqn) -+ pr_verbose("Skipping APQN %02x.%04x " -+ "(selection)", card, domain); -+ else -+ pr_verbose("Skipping card %02x (selection)", -+ card); -+ return false; -+ } -+ } -+ -+ if (g.all) -+ return true; -+ -+ /* Check if card/APQN is available in the system (SYSFS) */ -+ if (!is_card_available(card)) { -+ pr_verbose("Skipping card %02x (not available)", card); -+ return false; -+ } -+ if (is_apqn && !is_apqn_available(card, domain)) { -+ pr_verbose("Skipping APQN %02x.%04x (not available)", -+ card, domain); -+ return false; -+ } -+ -+ if (g.only_online) { -+ /* Check if card/APQN is online */ -+ if (!is_card_online(card)) { -+ pr_verbose("Skipping card %02x (not online)", card); -+ return false; -+ } -+ if (is_apqn && !is_apqn_online(card, domain)) { -+ pr_verbose("Skipping APQN %02x.%04x (not online)", -+ card, domain); -+ return false; -+ } -+ } -+ -+ return true; -+} -+ -+/* -+ * Process a crypto measurement block. -+ * Passes back the actual length of the CMB processed and its card number. -+ * Returns -ENODEV when the CMB is skipped. -+ */ -+static int process_cmb(struct chsc_cmb_area *cmb, size_t size, size_t *length, -+ uint8_t *card) -+{ -+ size_t len; -+ -+ len = get_cmb_length(cmb); -+ if (len == 0) -+ return -EINVAL; -+ -+ if (len > size) { -+ warnx("Length value in CMB exceeds size of CMB"); -+ return -EINVAL; -+ } -+ -+ if (card != NULL) -+ *card = cmb->header.ax; -+ if (length != NULL) -+ *length = len; -+ -+ if (filter_device(cmb->header.ax, cmb->header.dx, -+ cmb->header.format == 1) == false) -+ return -ENODEV; -+ -+ if (cmb->header.format == 1) -+ update_apqn_data(cmb->header.ax, cmb->header.dx, cmb, len); -+ else -+ update_card_data(cmb->header.ax, cmb, len); -+ -+ return 0; -+} -+ -+/* -+ * Translate the CHSC response code to an error (0 or negative errno) -+ */ -+static int chsc_error_from_response(int response) -+{ -+ if (response != 0x0001) -+ pr_verbose("CHSC Response code: %04x", response); -+ -+ switch (response) { -+ case 0x0001: -+ return 0; -+ case 0x0002: -+ return -EOPNOTSUPP; -+ case 0x0003: -+ case 0x0006: -+ case 0x0007: -+ case 0x0008: -+ case 0x000a: -+ case 0x0103: -+ case 0x0104: -+ return -EINVAL; -+ case 0x0004: -+ return -EOPNOTSUPP; -+ case 0x000b: -+ return -EBUSY; -+ case 0x0102: -+ return -ENOMEM; -+ case 0x0105: -+ return -EACCES; -+ case 0x0100: -+ case 0x0107: -+ return -ENODEV; -+ default: -+ return -EIO; -+ } -+} -+ -+/* -+ * Process the APQN measurement data and extract the CMBs -+ */ -+static int process_apqn_measurement_data(struct chsc_scdmd_area *scdmd_area) -+{ -+ size_t size = scdmd_area->response.header.length - -+ sizeof(struct chsc_scdmd_response); -+ size_t len, ofs = 0; -+ int rc; -+ -+ while (ofs < size) { -+ rc = process_cmb((struct chsc_cmb_area *) -+ &scdmd_area->response_data[ofs], -+ size - ofs, &len, NULL); -+ if (rc != 0 && rc != -ENODEV) -+ return rc; -+ ofs += len; -+ -+ } -+ -+ return 0; -+} -+ -+/* -+ * Get Crypto Measurement data on the APQN level -+ */ -+static int get_apqn_measurement_data(uint8_t card) -+{ -+ struct chsc_scdmd_area scdmd_area; -+ int rc; -+ -+ memset(&scdmd_area, 0, sizeof(scdmd_area)); -+ do { -+ scdmd_area.request.header.code = 0x102d; -+ scdmd_area.request.header.length = -+ sizeof(struct chsc_scdmd_request); -+ scdmd_area.request.first_drid.ap_index = card; -+ scdmd_area.request.first_drid.domain_index = g.min_domain; -+ scdmd_area.request.last_drid.ap_index = card; -+ scdmd_area.request.last_drid.domain_index = g.max_domain; -+ scdmd_area.request.s = 1; -+ scdmd_area.request.apsm[MASK_WORD_NO(card)] |= MASK_BIT(card); -+ memcpy(scdmd_area.request.dsm, g.domain_mask, -+ sizeof(scdmd_area.request.dsm)); -+ -+ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scdmd_area); -+ if (rc != 0) { -+ rc = -errno; -+ warnx("Failed to get APQN measurement data for card " -+ "%02x: %s", card, strerror(errno)); -+ break; -+ } -+ -+ rc = chsc_error_from_response(scdmd_area.response.header.code); -+ if (rc != 0) { -+ if (rc != -EOPNOTSUPP && rc != -ENODEV) { -+ warnx("Failed to get APQN crypto measurement " -+ "data for card %02x: %s", card, -+ strerror(-rc)); -+ } else { -+ pr_verbose("Failed to get APQN crypto " -+ "measurement data for card %02x: %s", -+ card, strerror(-rc)); -+ /* -+ * ignore return code other than -EOPNOTSUPP -+ * and -ENODEV -+ */ -+ rc = 0; -+ } -+ break; -+ } -+ -+ rc = process_apqn_measurement_data(&scdmd_area); -+ if (rc != 0) -+ break; -+ -+ if (scdmd_area.response.p) -+ scdmd_area.request.first_drid = -+ scdmd_area.response.crid; -+ } while (scdmd_area.response.p); -+ -+ return rc; -+} -+ -+/* -+ * Process the card measurement data and extract the CMBs -+ */ -+static int process_card_measurement_data(struct chsc_scmd_area *scmd_area, -+ uint8_t *last_card) -+{ -+ size_t size = scmd_area->response.header.length - -+ sizeof(struct chsc_scmd_response); -+ size_t len, ofs = 0; -+ int rc; -+ -+ while (ofs < size) { -+ rc = process_cmb((struct chsc_cmb_area *) -+ &scmd_area->response_data[ofs], -+ size - ofs, &len, last_card); -+ if (rc != 0 && rc != -ENODEV) -+ return rc; -+ ofs += len; -+ -+ if (rc == -ENODEV) -+ continue; -+ -+ if (!g.no_apqn) { -+ rc = get_apqn_measurement_data(*last_card); -+ if (rc != 0) -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Get Crypto Measurement data on the card level -+ */ -+static int get_card_measurement_data(void) -+{ -+ struct chsc_scmd_area scmd_area; -+ uint8_t last_card = 0; -+ int rc; -+ -+ memset(&scmd_area, 0, sizeof(scmd_area)); -+ do { -+ scmd_area.request.header.code = 0x102e; -+ scmd_area.request.header.length = -+ sizeof(struct chsc_scmd_request); -+ scmd_area.request.one = 1; -+ scmd_area.request.fcs = g.min_card; -+ scmd_area.request.lcs = g.max_card; -+ -+ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scmd_area); -+ if (rc != 0) { -+ rc = -errno; -+ warnx("Failed to get card measurement data: %s", -+ strerror(errno)); -+ break; -+ } -+ -+ rc = chsc_error_from_response(scmd_area.response.header.code); -+ if (rc != 0) { -+ warnx("Failed to get card crypto measurement data: %s", -+ strerror(-rc)); -+ break; -+ } -+ -+ rc = process_card_measurement_data(&scmd_area, &last_card); -+ if (rc != 0) -+ break; -+ -+ if (scmd_area.response.p) -+ scmd_area.request.fcs = last_card + 1; -+ } while (scmd_area.response.p && last_card < g.max_card); -+ -+ return rc; -+} -+ -+/* -+ * Signal handler for SIGALRM -+ */ -+static void alarm_handler(int UNUSED(sig)) -+{ -+ if (!quit) -+ alarm(g.interval); -+} -+ -+/* -+ * Signal handler for SIGINT and SIGTERM -+ */ -+static void int_handler(int UNUSED(sig)) -+{ -+ quit = true; -+ raise(SIGALRM); -+} -+ -+/* -+ * Calculates the time difference between tv1 and tv2 in seconds -+ */ -+static float time_diff(struct timeval *tv1, struct timeval *tv2) -+{ -+ struct timeval tv_diff; -+ -+ tv_diff.tv_sec = tv2->tv_sec - tv1->tv_sec; -+ tv_diff.tv_usec = tv2->tv_usec - tv1->tv_usec; -+ if (tv_diff.tv_usec < 0) { -+ tv_diff.tv_sec--; -+ tv_diff.tv_usec += 1000000; -+ } -+ -+ return (float)tv_diff.tv_sec + (float)(0.000001 * tv_diff.tv_usec); -+} -+ -+/* -+ * Initialize the default print format -+ */ -+static int default_print_initialize(void) -+{ -+ g.device_rec = util_rec_new_wide("-"); -+ util_rec_def(g.device_rec, "device", UTIL_REC_ALIGN_LEFT, 7, -+ "DEVICE"); -+ util_rec_def(g.device_rec, "kind", UTIL_REC_ALIGN_LEFT, 5, ""); -+ util_rec_def(g.device_rec, "type", UTIL_REC_ALIGN_LEFT, 33, -+ "TYPE"); -+ util_rec_def(g.device_rec, "time", UTIL_REC_ALIGN_LEFT, 20, -+ "TIMESTAMP"); -+ -+ g.counter_rec = util_rec_new_wide("-"); -+ util_rec_def(g.counter_rec, "name", UTIL_REC_ALIGN_LEFT, 18, -+ "COUNTER"); -+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, -+ "OPS"); -+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, -+ "RATE"); -+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, -+ 12, "UTILIZATION"); -+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, -+ 15, "AVG.DURATION"); -+ -+ return 0; -+} -+ -+/* -+ * Terminate the default print format -+ */ -+static int default_print_terminate(void) -+{ -+ util_rec_free(g.counter_rec); -+ util_rec_free(g.device_rec); -+ -+ return 0; -+} -+ -+/* -+ * Print the header lines for the default print format -+ */ -+static int default_print_header(void) -+{ -+ char timestamp[64]; -+ struct utsname un; -+ struct tm *tm; -+ time_t t; -+ -+ time(&t); -+ tm = localtime(&t); -+ strftime(timestamp, sizeof(timestamp), "%x", tm); -+ -+ if (uname(&un) != 0) -+ return -errno; -+ -+ printf("%s %s (%s) \t%s\t%s\n\n", un.sysname, un.release, -+ un.nodename, timestamp, un.machine); -+ -+ return 0; -+} -+ -+/* -+ * Print the footer lines for the default print format -+ */ -+static int default_print_footer(void) -+{ -+ printf("\n"); -+ return 0; -+} -+ -+/* -+ * Print the interval header lines for the default print format -+ */ -+static int default_print_interval_header(unsigned long interval_count, -+ const char *timestamp) -+{ -+ -+ printf("*****************************************************" -+ "***************\n"); -+ printf("TIME: %s\t\tINTERVAL: %lu\n\n", timestamp, interval_count); -+ return 0; -+} -+ -+/* -+ * Prints the separator lines in front of a device for the default print format -+ */ -+static int default_print_device_header(bool is_apqn, uint8_t card, -+ uint8_t domain, const char *type, -+ const char *timestamp) -+{ -+ if (is_apqn) -+ util_rec_set(g.device_rec, "device", "%02x.%04x", card, -+ domain); -+ else -+ util_rec_set(g.device_rec, "device", "%02x", card); -+ util_rec_set(g.device_rec, "kind", "%s", -+ is_apqn ? "APQN" : "CARD"); -+ util_rec_set(g.device_rec, "type", "%s", type); -+ util_rec_set(g.device_rec, "time", "%s", timestamp); -+ -+ util_rec_print_hdr(g.device_rec); -+ util_rec_print(g.device_rec); -+ printf("\n"); -+ -+ util_rec_set_indent(g.counter_rec, is_apqn ? 8 : 4); -+ util_rec_print_hdr(g.counter_rec); -+ -+ return 0; -+} -+ -+/* -+ * Prints the separator lines after a device for the default print format -+ */ -+static int default_print_device_footer(void) -+{ -+ -+ printf("\n"); -+ return 0; -+} -+ -+ -+/** -+ * Prints the counter data for the default print format -+ */ -+static int default_print_counter_data(bool UNUSED(is_apqn), -+ uint8_t UNUSED(card), -+ uint8_t UNUSED(domain), -+ const char *UNUSED(type), -+ const char *UNUSED(timestamp), -+ const char *name, -+ struct interval_values *vals) -+{ -+ util_rec_set(g.counter_rec, "name", "%s", name); -+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); -+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); -+ util_rec_set(g.counter_rec, "utilization", "%.2f %%", -+ vals->utilization * 100); -+ if (vals->duration >= 1) -+ util_rec_set(g.counter_rec, "duration", "%.3f sec ", -+ vals->duration); -+ else if (vals->duration >= 0.001) -+ util_rec_set(g.counter_rec, "duration", "%.3f msec", -+ vals->duration * 1000); -+ else -+ util_rec_set(g.counter_rec, "duration", "%.3f usec", -+ vals->duration * 1000000); -+ util_rec_print(g.counter_rec); -+ -+ return 0; -+} -+ -+/* -+ * Prints a separator between the counter lines and the totals line for the -+ * default print format -+ */ -+static int default_print_counter_separator(void) -+{ -+ util_rec_print_separator(g.counter_rec); -+ return 0; -+} -+ -+/* -+ * Initialize the JSON print format -+ */ -+static int json_print_initialize(void) -+{ -+ /* Use a decimal point to make JSON code compliant with RFC7159 */ -+ setlocale(LC_NUMERIC, "C"); -+ return 0; -+} -+ -+/* -+ * Print the header lines for the JSON print format -+ */ -+static int json_print_header(void) -+{ -+ char timestamp[64]; -+ struct utsname un; -+ struct tm *tm; -+ time_t t; -+ -+ time(&t); -+ tm = localtime(&t); -+ strftime(timestamp, sizeof(timestamp), "%x", tm); -+ -+ if (uname(&un) != 0) -+ return -errno; -+ -+ printf("{\"zcryptstats\": {\n"); -+ printf("\t\"host\": {\n"); -+ printf("\t\t\"nodename\": \"%s\",\n", un.nodename); -+ printf("\t\t\"sysname\": \"%s\",\n", un.sysname); -+ printf("\t\t\"release\": \"%s\",\n", un.release); -+ printf("\t\t\"machine\": \"%s\",\n", un.machine); -+ printf("\t\t\"date\": \"%s\",\n", timestamp); -+ printf("\t\t\"statistics\": [\n"); -+ -+ return 0; -+} -+ -+/* -+ * Print the footer lines for the JSON print format -+ */ -+static int json_print_footer(void) -+{ -+ printf("\n\t\t]\n"); -+ printf("\t}\n"); -+ printf("}}\n"); -+ -+ return 0; -+} -+ -+/* -+ * Print the interval header lines for the JSON print format -+ */ -+static int json_print_interval_header(unsigned long interval_count, -+ const char *timestamp) -+{ -+ if (interval_count > 1) -+ printf(",\n"); -+ printf("\t\t\t{\n"); -+ printf("\t\t\t\t\"interval\": %lu, \"timestamp\": \"%s\"," -+ " \"devices\": [\n", interval_count, timestamp); -+ -+ return 0; -+} -+ -+/* -+ * Print the interval footer lines for the JSON print format -+ */ -+static int json_print_interval_footer(void) -+{ -+ if (!g.first_device) -+ printf("\n"); -+ printf("\t\t\t\t]\n"); -+ printf("\t\t\t}"); -+ -+ return 0; -+} -+ -+/* -+ * Prints the separator lines in front of a device for the JSON print format -+ */ -+static int json_print_device_header(bool is_apqn, uint8_t card, -+ uint8_t domain, const char *type, -+ const char *UNUSED(timestamp)) -+{ -+ if (!g.first_device) -+ printf(",\n"); -+ printf("\t\t\t\t\t{"); -+ if (is_apqn) -+ printf("\"device\": \"%02x.%04x\"", card, -+ domain); -+ else -+ printf("\"device\": \"%02x\"", card); -+ printf(", \"type\": \"%s\",\n", type); -+ printf("\t\t\t\t\t \"counters\": [\n"); -+ -+ return 0; -+} -+ -+/* -+ * Prints the separator lines after a device for the JSON print format -+ */ -+static int json_print_device_footer(void) -+{ -+ if (!g.first_counter) -+ printf("\n"); -+ printf("\t\t\t\t\t ]}"); -+ -+ return 0; -+} -+ -+ -+/** -+ * Prints the counter data for the JSON print format -+ */ -+static int json_print_counter_data(bool UNUSED(is_apqn), -+ uint8_t UNUSED(card), -+ uint8_t UNUSED(domain), -+ const char *UNUSED(type), -+ const char *UNUSED(timestamp), -+ const char *name, -+ struct interval_values *vals) -+{ -+ if (!g.first_counter) -+ printf(",\n"); -+ printf("\t\t\t\t\t\t{\"counter\": \"%s\", \"ops\": %llu, " -+ "\"rate\": %.2f, \"utilization\": %.2f, \"duration\": %.9f}", -+ name, vals->count, vals->rate, vals->utilization * 100, -+ vals->duration); -+ -+ return 0; -+} -+ -+static int table_print_initialize(void) -+{ -+ g.counter_rec = util_rec_new_wide("-"); -+ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20, -+ "TIMESTAMP"); -+ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7, -+ "DEVICE"); -+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, -+ "OPS"); -+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, -+ "RATE"); -+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, -+ 12, "UTILIZATION"); -+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, -+ 15, "AVG.DURATION"); -+ return 0; -+} -+ -+static int table_print_terminate(void) -+{ -+ util_rec_free(g.counter_rec); -+ return 0; -+} -+ -+static int table_print_header(void) -+{ -+ int rc; -+ -+ rc = default_print_header(); -+ if (rc != 0) -+ return rc; -+ -+ util_rec_print_hdr(g.counter_rec); -+ return 0; -+} -+ -+static int table_print_interval_footer(void) -+{ -+ util_rec_print_separator(g.counter_rec); -+ return 0; -+} -+ -+static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *UNUSED(type), -+ const char *timestamp, -+ const char *UNUSED(name), -+ struct interval_values *vals) -+{ -+ if (is_apqn) -+ util_rec_set(g.counter_rec, "device", "%02x.%04x", card, -+ domain); -+ else -+ util_rec_set(g.counter_rec, "device", "%02x", card); -+ util_rec_set(g.counter_rec, "time", "%s", timestamp); -+ -+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); -+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); -+ util_rec_set(g.counter_rec, "utilization", "%.2f %%", -+ vals->utilization * 100); -+ if (vals->duration >= 1) -+ util_rec_set(g.counter_rec, "duration", "%.3f sec ", -+ vals->duration); -+ else if (vals->duration >= 0.001) -+ util_rec_set(g.counter_rec, "duration", "%.3f msec", -+ vals->duration * 1000); -+ else -+ util_rec_set(g.counter_rec, "duration", "%.3f usec", -+ vals->duration * 1000000); -+ -+ util_rec_print(g.counter_rec); -+ -+ return 0; -+} -+ -+static int csv_print_initialize(void) -+{ -+ /* Use a decimal point to not conflict with the colon separator char */ -+ setlocale(LC_NUMERIC, "C"); -+ -+ g.counter_rec = util_rec_new_csv(","); -+ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20, -+ "TIMESTAMP"); -+ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7, -+ "DEVICE"); -+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10, -+ "OPS"); -+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12, -+ "RATE"); -+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT, -+ 12, "UTILIZATION"); -+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT, -+ 15, "AVG.DURATION"); -+ return 0; -+} -+ -+static int csv_print_terminate(void) -+{ -+ util_rec_free(g.counter_rec); -+ return 0; -+} -+ -+static int csv_print_header(void) -+{ -+ util_rec_print_hdr(g.counter_rec); -+ return 0; -+} -+ -+ -+static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain, -+ const char *UNUSED(type), -+ const char *timestamp, -+ const char *UNUSED(name), -+ struct interval_values *vals) -+{ -+ if (is_apqn) -+ util_rec_set(g.counter_rec, "device", "%02x.%04x", card, -+ domain); -+ else -+ util_rec_set(g.counter_rec, "device", "%02x", card); -+ util_rec_set(g.counter_rec, "time", "%s", timestamp); -+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count); -+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate); -+ util_rec_set(g.counter_rec, "utilization", "%.2f %%", -+ vals->utilization * 100); -+ util_rec_set(g.counter_rec, "duration", "%.9f", vals->duration); -+ -+ util_rec_print(g.counter_rec); -+ -+ return 0; -+} -+ -+/* -+ * Calculates number of ops, utilization, duration and rate of an -+ * interval from the timer values, scale and interval time. -+ */ -+static void calc_interval_values(struct chsc_cmb_entry *current, -+ struct chsc_cmb_entry *previous, -+ float scale, -+ float interval_time, -+ struct interval_values *result) -+{ -+ u64 tdiff; -+ -+ tdiff = current->t - previous->t; -+ result->count = current->c - previous->c; -+ result->utilization = (double)(tdiff) * scale / interval_time; -+ if (result->count > 0) -+ result->duration = (double)(tdiff) * scale / result->count; -+ else -+ result->duration = 0; -+ result->rate = (double)result->count / interval_time; -+} -+ -+/* -+ * Print the measurement data of an interval -+ */ -+static int print_interval_data(struct interval_data *data, -+ const char *timestamp, float interval_time) -+{ -+ struct chsc_cmb_entry total_current; -+ struct chsc_cmb_entry total_previous; -+ struct interval_values vals; -+ const char *type, *counter; -+ uint32_t mask = 0x80000000; -+ bool totals_found = false; -+ size_t len; -+ int i, rc; -+ -+ len = get_cmb_length(&data->current); -+ type = get_card_name(data->current.header.ct, data->current.header.mt); -+ -+ rc = pr_call(print_device_header)(data->current.header.format == 1, -+ data->current.header.ax, -+ data->current.header.dx, type, -+ timestamp); -+ if (rc != 0) -+ return rc; -+ -+ memset(&total_current, 0, sizeof(total_current)); -+ memset(&total_previous, 0, sizeof(total_previous)); -+ -+ g.first_counter = true; -+ -+ for (i = 0; i < 32 && -+ offsetofend(struct chsc_cmb_area, entries[i]) <= len; i++) { -+ if (data->current.header.v & mask) { -+ if (is_counter_totals(data->current.header.ct, -+ data->current.header.mt, i)) { -+ total_current.t = data->current.entries[i].t; -+ total_current.c = data->current.entries[i].c; -+ total_previous.t = data->previous.entries[i].t; -+ total_previous.c = data->previous.entries[i].c; -+ totals_found = true; -+ } else if (!totals_found) { -+ total_current.t += data->current.entries[i].t; -+ total_current.c += data->current.entries[i].c; -+ total_previous.t += data->previous.entries[i].t; -+ total_previous.c += data->previous.entries[i].c; -+ } -+ -+ if (g.only_totals) -+ continue; -+ -+ calc_interval_values(&data->current.entries[i], -+ &data->previous.entries[i], -+ data->current.header.s, -+ interval_time, -+ &vals); -+ -+ counter = get_counter_name(data->current.header.ct, -+ data->current.header.mt, i); -+ -+ rc = pr_call(print_counter_data)( -+ data->current.header.format == 1, -+ data->current.header.ax, -+ data->current.header.dx, type, -+ timestamp, counter, &vals); -+ if (rc != 0) -+ break; -+ -+ g.first_counter = false; -+ } -+ mask >>= 1; -+ } -+ -+ if (!g.no_totals) { -+ rc = pr_call(print_counter_separator)(); -+ if (rc != 0) -+ return rc; -+ -+ calc_interval_values(&total_current, &total_previous, -+ data->current.header.s, interval_time, -+ &vals); -+ -+ rc = pr_call(print_counter_data)( -+ data->current.header.format == 1, -+ data->current.header.ax, -+ data->current.header.dx, type, -+ timestamp, "Total", &vals); -+ if (rc != 0) -+ return rc; -+ } -+ -+ rc = pr_call(print_device_footer)(); -+ if (rc != 0) -+ return rc; -+ -+ return 0; -+} -+ -+/* -+ * Print the measured data -+ */ -+static int print_measurement_data(unsigned long interval_count, -+ float interval_time, const char *timestamp) -+{ -+ bool header_printed = false; -+ struct interval_data *dd; -+ struct card_data *cd; -+ int card, domain, rc; -+ -+ g.first_device = true; -+ for (card = 0; card < NUM_CARDS; card++) { -+ cd = g.cards[card]; -+ if (cd == NULL) -+ continue; -+ -+ if (!cd->data.current_valid) { -+ /* Not update in last interval -> free it */ -+ free_card_data(cd); -+ g.cards[card] = NULL; -+ -+ pr_verbose("Card %02x removed", card); -+ continue; -+ } -+ -+ if (cd->data.previous_valid) { -+ if (memcmp(&cd->data.current.header, -+ &cd->data.previous.header, -+ sizeof(struct chsc_cmb_header)) != 0) { -+ free_card_data(cd); -+ g.cards[card] = NULL; -+ -+ pr_verbose("CMB header mismatch, card %02x " -+ "removed", card); -+ continue; -+ } -+ -+ if (!header_printed) { -+ rc = pr_call(print_interval_header)( -+ interval_count, timestamp); -+ if (rc != 0) -+ return rc; -+ header_printed = true; -+ } -+ -+ rc = print_interval_data(&cd->data, timestamp, -+ interval_time); -+ if (rc != 0) -+ return rc; -+ -+ g.first_device = false; -+ } -+ -+ cd->data.current_valid = false; -+ -+ for (domain = 0; domain < NUM_DOMAINS; domain++) { -+ dd = cd->domains[domain]; -+ if (dd == NULL) -+ continue; -+ -+ if (!dd->current_valid) { -+ /* Not update in last interval -> free it */ -+ free(dd); -+ cd->domains[domain] = NULL; -+ -+ pr_verbose("APQN %02x.%04x removed", card, -+ domain); -+ } -+ -+ if (dd->previous_valid) { -+ if (memcmp(&dd->current.header, -+ &dd->previous.header, -+ sizeof(struct chsc_cmb_header))) { -+ free(dd); -+ cd->domains[domain] = NULL; -+ -+ pr_verbose("CMB header mismatch, APQN " -+ "%02x.%04x removed", card, -+ domain); -+ continue; -+ } -+ -+ rc = print_interval_data(dd, timestamp, -+ interval_time); -+ if (rc != 0) -+ return rc; -+ } -+ -+ dd->current_valid = false; -+ } -+ } -+ -+ if (header_printed) { -+ rc = pr_call(print_interval_footer)(); -+ if (rc != 0) -+ return rc; -+ } else if (interval_count > 0) { -+ pr_verbose("No data was reported in this interval"); -+ warnx("Failed to get card crypto measurement data: %s", -+ strerror(ENODEV)); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Perform the measurement in intervals -+ */ -+static int perform_measurement(void) -+{ -+ struct timeval tv_current, tv_previous; -+ struct sigaction alrm_act, int_act; -+ unsigned long interval_count = 0; -+ float interval_time; -+ char timestamp[64]; -+ struct tm *tm; -+ int rc; -+ -+ /* Set a handler for SIGINT/SIGTERM */ -+ memset(&int_act, 0, sizeof(int_act)); -+ int_act.sa_handler = int_handler; -+ sigaction(SIGINT, &int_act, NULL); -+ sigaction(SIGTERM, &int_act, NULL); -+ -+ /* Set a handler for SIGALRM */ -+ memset(&alrm_act, 0, sizeof(alrm_act)); -+ alrm_act.sa_handler = alarm_handler; -+ sigaction(SIGALRM, &alrm_act, NULL); -+ -+ rc = pr_call(print_initialize)(); -+ if (rc != 0) -+ return rc; -+ -+ rc = pr_call(print_header)(); -+ if (rc != 0) -+ return 0; -+ -+ alarm(g.interval); -+ -+ memset(&tv_current, 0, sizeof(tv_current)); -+ while (!quit) { -+ pr_verbose("Interval %lu", interval_count); -+ -+ tv_previous = tv_current; -+ rc = gettimeofday(&tv_current, NULL); -+ if (rc != 0) -+ break; -+ -+ tm = localtime(&tv_current.tv_sec); -+ if (tm == NULL) -+ break; -+ strftime(timestamp, sizeof(timestamp), "%x %X", tm); -+ interval_time = time_diff(&tv_previous, &tv_current); -+ -+ rc = get_card_measurement_data(); -+ if (rc != 0) -+ break; -+ -+ rc = print_measurement_data(interval_count, interval_time, -+ timestamp); -+ if (rc != 0) -+ break; -+ -+ if (g.count > 0 && interval_count >= g.count) { -+ pr_verbose("Interval limit reached"); -+ break; -+ } -+ interval_count++; -+ -+ if (quit) -+ break; -+ -+ pause(); -+ } -+ -+ if (quit) -+ pr_verbose("Measurement stopped by user"); -+ -+ alarm(0); -+ memset(&alrm_act, 0, sizeof(alrm_act)); -+ alrm_act.sa_handler = SIG_DFL; -+ sigaction(SIGALRM, &alrm_act, NULL); -+ -+ rc = pr_call(print_footer)(); -+ if (rc != 0) -+ return 0; -+ -+ rc = pr_call(print_terminate)(); -+ if (rc != 0) -+ return rc; -+ -+ return 0; -+} -+ -+/* -+ * Parse the type mapping specification: -+ * TYPE:MODE=TYPE:MODE[,TYPE:MODE=TYPE:MODE[,...]] -+ */ -+static int parse_type_mapping(char *mapping) -+{ -+ unsigned int from_type, to_type, from_mode, to_mode; -+ struct type_mapping *map; -+ char *tok; -+ -+ tok = strtok(mapping, ","); -+ while (tok != NULL) { -+ if (sscanf(tok, "%u:%u=%u:%u", &from_type, &from_mode, -+ &to_type, &to_mode) != 4) { -+ warnx("Invalid type mapping: %s", tok); -+ return -EINVAL; -+ } -+ -+ pr_verbose("from_type: %u from_mode: %u to_type: %u " -+ "to_mode: %u", from_type, from_mode, to_type, -+ to_mode); -+ -+ if (from_type < NUM_CRYPTO_TYPES && -+ crypto_types[from_type].name != NULL && -+ from_mode < crypto_types[from_type].num_modes && -+ crypto_types[from_type].modes[from_mode].counters != NULL) { -+ warnx("Cannot map a known type/mode to another " -+ "type/mode: %s", tok); -+ return -EINVAL; -+ } -+ -+ if (to_type >= NUM_CRYPTO_TYPES || -+ crypto_types[to_type].name == NULL || -+ to_mode >= crypto_types[to_type].num_modes || -+ crypto_types[to_type].modes[to_mode].counters == NULL) { -+ warnx("Cannot map a type/mode to an unknown " -+ "type/mode: %s", tok); -+ return -EINVAL; -+ } -+ -+ map = util_malloc(sizeof(struct type_mapping)); -+ map->from_type = from_type; -+ map->from_mode = from_mode; -+ map->to_type = to_type; -+ map->to_mode = to_mode; -+ -+ map->next = g.type_mapping; -+ g.type_mapping = map; -+ -+ tok = strtok(NULL, ","); -+ } -+ -+ return 0; -+} -+ -+static void free_type_mapping(void) -+{ -+ struct type_mapping *map = g.type_mapping; -+ struct type_mapping *next; -+ -+ while (map != NULL) { -+ next = map->next; -+ free(map); -+ map = next; -+ } -+} -+ -+static int add_device_selection(const char *device_id) -+{ -+ int card = -1, domain = -1; -+ struct device_selection *dev; -+ -+ pr_verbose("device_id: '%s'", device_id); -+ -+ /* check for 'card[.domain]' specification */ -+ if (sscanf(device_id, "%x.%x", &card, &domain) >= 1) { -+ pr_verbose("card: %d domain: %d", card, domain); -+ if (card < 0 || card > g.max_card_used) { -+ warnx("Invalid card specified: %s", device_id); -+ return -EINVAL; -+ } -+ g.card_mask[MASK_WORD_NO(card)] |= MASK_BIT(card); -+ g.min_card = MIN(g.min_card, card); -+ g.max_card = MAX(g.max_card, card); -+ -+ if (domain >= 0) { -+ if (domain > NUM_DOMAINS) { -+ warnx("Invalid domain specified: %s", -+ device_id); -+ return -EINVAL; -+ } -+ g.domain_mask[MASK_WORD_NO(domain)] |= -+ MASK_BIT(domain); -+ g.min_domain = MIN(g.max_domain, domain); -+ g.max_domain = MAX(g.max_domain, domain); -+ } else { -+ memset(g.domain_mask, 0xff, sizeof(g.domain_mask)); -+ g.min_domain = 0; -+ g.max_domain = NUM_DOMAINS - 1; -+ } -+ -+ dev = util_malloc(sizeof(struct device_selection)); -+ dev->card = card; -+ dev->domain = domain; -+ dev->next = g.dev_selection; -+ g.dev_selection = dev; -+ -+ return 0; -+ } -+ /* check for '.domain' specification */ -+ if (device_id[0] == '.' && -+ sscanf(device_id + 1, "%x", &domain) == 1) { -+ pr_verbose("domain: %d", domain); -+ if (domain < 0 || domain > NUM_DOMAINS) { -+ warnx("Invalid domain specified: %s", device_id); -+ return -EINVAL; -+ } -+ -+ g.domain_mask[MASK_WORD_NO(domain)] |= MASK_BIT(domain); -+ g.min_domain = MIN(g.max_domain, domain); -+ g.max_domain = MAX(g.max_domain, domain); -+ memset(g.card_mask, 0xff, sizeof(g.card_mask)); -+ g.min_card = 0; -+ g.max_card = g.max_card_used; -+ -+ dev = util_malloc(sizeof(struct device_selection)); -+ dev->card = -1; -+ dev->domain = domain; -+ dev->next = g.dev_selection; -+ g.dev_selection = dev; -+ -+ return 0; -+ } -+ -+ warnx("Invalid device ID specified: %s", device_id); -+ return -EINVAL; -+} -+ -+/* -+ * Frees the device selection list -+ */ -+static void free_device_selection(void) -+{ -+ struct device_selection *dev = g.dev_selection; -+ struct device_selection *next; -+ -+ while (dev != NULL) { -+ next = dev->next; -+ free(dev); -+ -+ dev = next; -+ } -+} -+ -+/* -+ * Build the AP and domain selection mask from the specified device ID's. -+ */ -+static int parse_device_selection(void) -+{ -+ int i, rc; -+ -+ g.min_card = NUM_CARDS - 1; -+ g.max_card = 0; -+ g.min_domain = NUM_DOMAINS - 1; -+ g.max_domain = 0; -+ -+ for (i = 0; g.device_ids[i] != NULL; i++) { -+ rc = add_device_selection(g.device_ids[i]); -+ if (rc != 0) -+ return rc; -+ } -+ -+ if (i == 0) { -+ /* No device-IDs specified */ -+ memset(g.card_mask, 0xff, sizeof(g.card_mask)); -+ g.min_card = 0; -+ g.max_card = g.max_card_used; -+ memset(g.domain_mask, 0xff, sizeof(g.domain_mask)); -+ g.min_domain = 0; -+ g.max_domain = NUM_DOMAINS - 1; -+ } -+ -+ pr_verbose("Min card: %u, max card: %u", g.min_card, g.max_card); -+ pr_verbose("Min domain: %u, max domain: %u", g.min_domain, -+ g.max_domain); -+ -+ return 0; -+} -+ -+/* -+ * Entry point -+ */ -+int main(int argc, char *argv[]) -+{ -+ char *endp; -+ int c, rc; -+ -+ util_prg_init(&prg); -+ util_opt_init(opt_vec, NULL); -+ -+ while (1) { -+ c = util_opt_getopt_long(argc, argv); -+ if (c == -1) -+ break; -+ switch (c) { -+ case 'i': -+ g.interval = strtoll(optarg, &endp, 0); -+ if (*optarg == '\0' || *endp != '\0' || -+ g.interval <= 0 || -+ (g.interval == LLONG_MAX && errno == ERANGE)) { -+ warnx("Invalid value for '--interval'|" -+ "'-i': '%s'", optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+ case 'c': -+ g.count = strtoull(optarg, &endp, 0); -+ if (*optarg == '\0' || *endp != '\0' || g.count == 0 || -+ (g.count == LLONG_MAX && errno == ERANGE)) { -+ warnx("Invalid value for '--count'|" -+ "'-c': '%s'", optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+ case 'o': -+ if (strcasecmp(optarg, "JSON") == 0) { -+ g.print_funcs = &json_print; -+ } else if (strcasecmp(optarg, "TABLE") == 0) { -+ g.only_totals = true; -+ g.print_funcs = &table_print; -+ } else if (strcasecmp(optarg, "CSV") == 0) { -+ g.only_totals = true; -+ g.print_funcs = &csv_print; -+ } else { -+ warnx("Invalid value for '--output'|" -+ "'-o': '%s'", optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+ case 't': -+ g.no_totals = true; -+ break; -+ case 'T': -+ g.only_totals = true; -+ break; -+ case 'a': -+ g.no_apqn = true; -+ break; -+ case 'M': -+ g.map_type = optarg; -+ break; -+ case 'A': -+ g.all = true; -+ break; -+ case 'O': -+ g.only_online = true; -+ break; -+ case 'V': -+ g.verbose = true; -+ break; -+ case 'h': -+ util_prg_print_help(); -+ util_opt_print_help(); -+ print_adapter_id_help(); -+ return EXIT_SUCCESS; -+ case 'v': -+ util_prg_print_version(); -+ return EXIT_SUCCESS; -+ default: -+ util_opt_print_parse_error(c, argv); -+ return EXIT_FAILURE; -+ } -+ } -+ -+ /* remaining positional args are device IDs */ -+ g.device_ids = &argv[optind]; -+ -+ if (g.only_totals && g.no_totals) { -+ warnx("Either --no-totals or --only-totals can be specified, " -+ "but not both"); -+ return EXIT_FAILURE; -+ } -+ -+ if (g.only_online && g.all) { -+ warnx("Either --only-online or --all can be specified, " -+ "but not both"); -+ return EXIT_FAILURE; -+ } -+ -+ pr_verbose("Interval: %ld Count: %ld", g.interval, g.count); -+ -+ rc = get_max_card_index(&g.max_card_used); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = parse_device_selection(); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ if (g.map_type != NULL) { -+ rc = parse_type_mapping(g.map_type); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ } -+ -+ g.chsc_fd = open(CHSC_DEVICE, O_RDWR); -+ if (g.chsc_fd < 0) { -+ warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno)); -+ return EXIT_FAILURE; -+ } -+ pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE); -+ -+ /* Don't buffer data if redirected to a pipe */ -+ setbuf(stdout, NULL); -+ -+ rc = perform_measurement(); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+out: -+ if (g.chsc_fd >= 0) -+ close(g.chsc_fd); -+ free_device_selection(); -+ free_type_mapping(); -+ free_interval_data(); -+ -+ return rc; -+} -+ --- -2.21.3 - - -From c0670b62ccd7a9f1f1b3a8a49114c2e3c673ecd9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jan=20H=C3=B6ppner?= -Date: Mon, 1 Apr 2019 09:53:06 +0200 -Subject: [PATCH 21/57] zpcictl: Check for regular directory (#1695001) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In case a regular directory was specified, rather than a device node, -the check if the device exists will pass. The following code paths then -assume a slot id was specified. This in turn may lead to a buffer -overflow when the device data is copied to to the zpci_device struct. - -Check if the specified path is a regular directory and prevent a -possible later buffer overflow and copying wrong data respectively. - -Signed-off-by: Jan Höppner -(cherry picked from commit c7a255fcd9433bba6dd516f6b28a139bbc5351d3) ---- - zpcictl/zpcictl.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c -index 7cfa0d3..f2a55ad 100644 ---- a/zpcictl/zpcictl.c -+++ b/zpcictl/zpcictl.c -@@ -249,8 +249,12 @@ static int device_exists(char *dev) - char *path; - int rc = 0; - -+ /* In case a device node is specified, this will be sufficiant */ -+ if (util_path_exists(dev) && !util_path_is_dir(dev)) -+ return 1; -+ - path = util_path_sysfs("bus/pci/devices/%s", dev); -- if (util_path_exists(path) || util_path_exists(dev)) -+ if (util_path_exists(path)) - rc = 1; - free(path); - --- -2.21.3 - - -From 9f0fe2e3d5bcc079577700d778e3f1a58fced510 Mon Sep 17 00:00:00 2001 -From: Thomas Richter -Date: Wed, 10 Jul 2019 13:01:09 +0200 -Subject: [PATCH 22/57] cpumf: Add support for CPU-Measurement Facility - counters SVN 6 (#1683276) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add support for CPU-Measurement facility counter second version -number 6. This adds some more counters to the crypto counter set. -Extended counter set is the same as for z14. - -Signed-off-by: Thomas Richter -Signed-off-by: Jan Höppner -(cherry picked from commit f110a77b68d46f10cab8847cf73fe92686ae7d11) ---- - cpumf/Makefile | 2 +- - cpumf/bin/cpumf_helper.in | 7 +++- - ...svn-generic.ctr => cpum-cf-csvn-12345.ctr} | 0 - cpumf/data/cpum-cf-csvn-6.ctr | 33 +++++++++++++++++++ - cpumf/data/cpum-cf-hw-counter.map | 6 +++- - 5 files changed, 45 insertions(+), 3 deletions(-) - rename cpumf/data/{cpum-cf-csvn-generic.ctr => cpum-cf-csvn-12345.ctr} (100%) - create mode 100644 cpumf/data/cpum-cf-csvn-6.ctr - -diff --git a/cpumf/Makefile b/cpumf/Makefile -index 3fc4c0f..2e11810 100644 ---- a/cpumf/Makefile -+++ b/cpumf/Makefile -@@ -6,7 +6,7 @@ include ../common.mak - CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf - DATA_FILES = cpum-cf-hw-counter.map \ - cpum-cf-cfvn-1.ctr cpum-cf-cfvn-3.ctr \ -- cpum-cf-csvn-generic.ctr \ -+ cpum-cf-csvn-12345.ctr cpum-cf-csvn-6.ctr \ - cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \ - cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \ - cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr -diff --git a/cpumf/bin/cpumf_helper.in b/cpumf/bin/cpumf_helper.in -index 4757725..e1da017 100755 ---- a/cpumf/bin/cpumf_helper.in -+++ b/cpumf/bin/cpumf_helper.in -@@ -266,7 +266,12 @@ sub cpumf_load_ctrdef($;$) - # List of "generic" counter sets - my @def = (); - push @def, "cfvn-" . $version->{cfvn}; -- push @def, "csvn-generic"; -+ if ($version->{csvn} >= 1 && $version->{csvn} <= 6) { -+ push @def, "csvn-12345"; -+ } -+ if ($version->{csvn} == 6) { -+ push @def, "csvn-6"; -+ } - - my $h = {}; - # Load counter set definition -diff --git a/cpumf/data/cpum-cf-csvn-generic.ctr b/cpumf/data/cpum-cf-csvn-12345.ctr -similarity index 100% -rename from cpumf/data/cpum-cf-csvn-generic.ctr -rename to cpumf/data/cpum-cf-csvn-12345.ctr -diff --git a/cpumf/data/cpum-cf-csvn-6.ctr b/cpumf/data/cpum-cf-csvn-6.ctr -new file mode 100644 -index 0000000..4ea3b44 ---- /dev/null -+++ b/cpumf/data/cpum-cf-csvn-6.ctr -@@ -0,0 +1,33 @@ -+Counter: 80 Name:ECC_FUNCTION_COUNT -+Short-Description:ECC Function Count -+Description: -+This counter counts the -+total number of the elliptic-curve cryptography (ECC) -+functions issued by the CPU. -+. -+Counter: 81 Name:ECC_CYCLES_COUNT -+Short-Description:ECC Cycles Count -+Description: -+This counter counts the total -+number of CPU cycles when the ECC coprocessor is -+busy performing the elliptic-curve cryptography -+(ECC) functions issued by the CPU. -+. -+Counter: 82 Name:ECC_BLOCKED_FUNCTION_COUNT -+Short-Description:Ecc Blocked Function Count -+Description: -+This counter -+counts the total number of the elliptic-curve -+cryptography (ECC) functions that are issued by the CPU -+and are blocked because the ECC coprocessor is -+busy performing a function issued by another CPU. -+. -+Counter: 83 Name:ECC_BLOCKED_CYCLES_COUNT -+Short-Description:ECC Blocked Cycles Count -+Description: -+This counter counts -+the total number of CPU cycles blocked for the elliptic-curve -+cryptography (ECC) functions issued by the -+CPU because the ECC coprocessor is busy perform- -+ing a function issued by another CPU. -+. -diff --git a/cpumf/data/cpum-cf-hw-counter.map b/cpumf/data/cpum-cf-hw-counter.map -index a8a2846..5729b95 100644 ---- a/cpumf/data/cpum-cf-hw-counter.map -+++ b/cpumf/data/cpum-cf-hw-counter.map -@@ -14,7 +14,8 @@ - 'cfvn-3' => 'cpum-cf-cfvn-3.ctr', - - # CSVN -- 'csvn-generic' => 'cpum-cf-csvn-generic.ctr', -+ 'csvn-12345' => 'cpum-cf-csvn-12345.ctr', -+ 'csvn-6' => 'cpum-cf-csvn-6.ctr', - - # Extended counters - 2097 => 'cpum-cf-extended-z10.ctr', -@@ -27,4 +28,7 @@ - 2965 => 'cpum-cf-extended-z13.ctr', - 3906 => 'cpum-cf-extended-z14.ctr', - 3907 => 'cpum-cf-extended-z14.ctr', -+ # Identical with z14 -+ 8561 => 'cpum-cf-extended-z14.ctr', -+ 8562 => 'cpum-cf-extended-z14.ctr', - }; --- -2.21.3 - - -From f58ee5580cd6e357dce488d9cd3d999e7ad61f16 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 19 Jul 2019 09:41:08 +0200 -Subject: [PATCH 23/57] ziomon: fix utilization recording with multi-digit scsi - hosts (#1731203) - -Description: ziomon: fix utilization recording with multi-digit scsi hosts -Symptom: During running of ziomon script, user receives ziomon - error: "ziomon: Number of LUNs does not match number of - devices: 2 devices and 1 LUNs". Also user receives - ziomon_util error: "ziomon_util: Path does not exist: - /sys/class/scsi_host/host0/utilization - correct kernel - version?". After collection of records, user receives - error during using of ziorep_utilization or - ziorep_traffic: "ziorep_traffic: Could not retrieve - initial data - data files corrupted or broken, or the - .agg file is missing." -Problem: s390-tools-1.9.0 introduced a new way of recognizing of - multipath device paths with using of sed command - invocation in ziomon script. With this new way of - recognizing, if there are paths, related to multipath - device, with SCSI host ID longer, than one digit, - ziomon incorrectly parses the multipath -l command - output. It erroneously cuts off all but the least - significant digit of the SCSI host ID (H) of paths in - H:B:T:L format (Host:Bus:Target:Lun). This leads to - passing of hosts (-a) and paths (-l) with wrong - SCSI host ID to ziomon_util. In turn ziomon_util cannot - recognize hosts with non-existing SCSI host ID and - issues an error. Also, wrong sed command invocation - could lead to receiving of duplicate LUNs by ziomon - after parsing of multipath -l command output. Then - ziomon excludes duplicates from WRP_LUNS, which leads - to mismatch between number of LUNs and number of - detected block devices and issues ziomon script error, - without starting ziomon_util and without writing to - specified log file. -Solution: The regular expression to match a path in H:B:T:L - format started with a greedy ".*", which erroneously - consumed parts of the SCSI host ID (H). The solution - is to replace the greedy ".*" by "[^0-9]*", so that sed - command does not consume parts of the SCSI host ID any - more. -Reproduction: Create such multipath device, that its related path - contains LUN with SCSI host ID longer, than one digit. - Example for wrong SCSI host ID: - $ multipath -l - mpathc (36005076307ffc5e300000000000083f5) dm-2 IBM ... - size=20G features='1 queue_if_no_path' hwhandler='0'... - `-+- policy='service-time 0' prio=0 status=active - |- 10:0:0:1089814659 sdb 8:16 active undef running - `- 11:0:0:1089814659 sdf 8:80 active undef running - Example for duplicate SCSI host ID: - $ multipath -l - mpathd (36005076307ffc5e300000000000083f4) dm-3 IBM ... - size=20G features='1 queue_if_no_path' hwhandler='0'... - `-+- policy='service-time 0' prio=0 status=active - |- 10:0:0:1089749123 sda 8:0 active undef running - `- 0:0:0:1089749123 sdd 8:48 active undef running - Use ziomon tool with one of the described multipath - device as input. -Upstream-ID: f2dee9f542439cf07e00df4296b05a47b81e2469 ---- - ziomon/ziomon | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ziomon/ziomon b/ziomon/ziomon -index 87c4bc4..26fa269 100755 ---- a/ziomon/ziomon -+++ b/ziomon/ziomon -@@ -514,7 +514,7 @@ function check_for_multipath_devices() { - (( i+=2 )); - while [[ `echo "${mp_arr[$i]:0:1}" | grep -ve "[0-9a-zA-Z]"` ]] && [ $i -lt ${#mp_arr[@]} ]; do - if [ `echo ${mp_arr[$i]} | grep -e "[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}" | wc -l` -ne 0 ]; then -- line="`echo ${mp_arr[$i]} | sed 's/.*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`"; -+ line="`echo ${mp_arr[$i]} | sed 's/[^0-9]*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`"; - checked_devs[${#checked_devs[@]}]=`echo $line | awk '{print "/dev/"$2}'`; - ddebug " adding ${checked_devs[${#checked_devs[@]}-1]}"; - WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}"; --- -2.21.3 - - -From ef93298c3f0a8d318c0568f3bec10b76279bb15b Mon Sep 17 00:00:00 2001 -From: Peter Oberparleiter -Date: Tue, 12 Mar 2019 14:05:17 +0100 -Subject: [PATCH 24/57] zdev: Do not export inacceptable attribute values - (#1731960) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -For some device driver SysFS attributes, values read may not be -acceptable input values for that attribute. - -An example would be the group of qeth VNICC attributes that return "n/a" -when VNICC setup is not supported, but only accept "0" and "1" as valid -values that can be written to it. - -This leads to errors such as the following when data for such attributes -is imported: - - # chzdev f500 --import test.conf - Importing configuration data from test.conf - QETH device 0.0.f500:0.0.f501:0.0.f502 configure failed - Error: Invalid value for qeth attribute: vnicc/flooding=n/a (*) - Acceptable values: - - Integers in the range 0 - 1 - Use 'chzdev qeth --help-attribute vnicc/flooding' for more information - Note: You can use --force to override safety checks (*) - -To fix this, change chzdev's --export function to skip any attribute -value that is not acceptable for that attribute. - -Fixes: e831269e7433 ("zdev: Add support for VNIC Characteristics") -Signed-off-by: Peter Oberparleiter -Signed-off-by: Jan Höppner -(cherry picked from commit 4ff6519961965deb278093c83ea0f46fb7c7c205) ---- - zdev/src/export.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/zdev/src/export.c b/zdev/src/export.c -index f1acb8a..a66db0b 100644 ---- a/zdev/src/export.c -+++ b/zdev/src/export.c -@@ -78,6 +78,10 @@ static bool is_exportable(struct setting *s, config_t config) - /* Skip values that cannot be determined. */ - return false; - } -+ if (!attrib_check_value(a, s->value)) { -+ /* Skip values that are not acceptable input values. */ -+ return false; -+ } - if (!attrib_match_default(s->attrib, s->value)) { - /* All non-default values should be exported. */ - return true; --- -2.21.3 - - -From de7f4178f2178268ed65292bf5e1f42894832d9a Mon Sep 17 00:00:00 2001 -From: Stefan Haberland -Date: Wed, 17 Jul 2019 17:26:40 +0200 -Subject: [PATCH 25/57] zipl: do not overwrite BOOT_IMAGE entry (#1728677) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The zipl internal loader adds a variable BOOT_IMAGE= to the commandline -so that it is visible in the operating system which menu entry has been -chosen. - -This entry was overwritten by the stage3 parameter page. - -Fix by re-arranging the internal memory layout and putting the command -line extra param, which contains the BOOT_IMAGE entry, at 0xe000. -This location is available because less than one page is used for the -stack. - -Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/67 -Signed-off-by: Stefan Haberland -Signed-off-by: Jan Höppner -(cherry picked from commit 8bf5f8d0e293b294c9b7aaebea80e92634c7564d) ---- - zipl/boot/Makefile | 4 ++-- - zipl/boot/menu.h | 2 +- - zipl/boot/stage2.lds | 3 ++- - zipl/boot/stage3.h | 2 +- - 4 files changed, 6 insertions(+), 5 deletions(-) - -diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile -index a049797..0faf3ec 100644 ---- a/zipl/boot/Makefile -+++ b/zipl/boot/Makefile -@@ -6,7 +6,7 @@ CFLAGS_BOOT = $(NO_PIE_CFLAGS) -Os -g -I../include -D__ASSEMBLY__ \ - -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ - -fno-delete-null-pointer-checks \ - -fexec-charset=IBM1047 -m64 -mpacked-stack \ -- -mstack-size=8192 -mstack-guard=128 -msoft-float \ -+ -mstack-size=4096 -mstack-guard=128 -msoft-float \ - -W -Wall -Wformat-security - - FILES = fba0.bin fba1b.bin fba2.bin \ -@@ -90,7 +90,7 @@ stage3.bin: stage3.exec - --only-section=.stage2dump.tail \ - --only-section=.eckd2dump_mv.tail \ - --only-section=.fixup \ -- --pad-to=0xf000 \ -+ --pad-to=0xe000 \ - $< $@ - - data.o: $(FILES) -diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h -index 83338a8..1b103a8 100644 ---- a/zipl/boot/menu.h -+++ b/zipl/boot/menu.h -@@ -15,7 +15,7 @@ - #include "stage2.h" - - /* address of extra command line */ --#define COMMAND_LINE_EXTRA (0xA000-0x400) -+#define COMMAND_LINE_EXTRA 0xE000 - - /* max command line length */ - #define COMMAND_LINE_SIZE 896 -diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds -index 41efa79..34ed014 100644 ---- a/zipl/boot/stage2.lds -+++ b/zipl/boot/stage2.lds -@@ -12,7 +12,8 @@ - * 0x6000-0x8fff Memory allocation (heap) - * 0x9000-0x9fff Memory to load stage3 parameter to - * 0xa000-0xdfff Memory to load stage3 to -- * 0xe000-0xffff Stack -+ * 0xe000-0xe3ff command line extra -+ * 0xe400-0xffff Stack - * - * Special memory locations - * ------------------------ -diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h -index 91477b1..3a02001 100644 ---- a/zipl/boot/stage3.h -+++ b/zipl/boot/stage3.h -@@ -22,7 +22,7 @@ - #define OLDMEM_SIZE 0x10420 - #define COMMAND_LINE 0x10480 - #define COMMAND_LINE_SIZE 896 --#define COMMAND_LINE_EXTRA (0xA000-0x400) -+#define COMMAND_LINE_EXTRA 0xE000 - - #define STAGE3_FLAG_SCSI 0x0001000000000000ULL - #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL --- -2.21.3 - - -From d25f635ebdb0f2faa8ca018859c3223c3dac4d0f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jan=20H=C3=B6ppner?= -Date: Wed, 31 Jul 2019 15:25:00 +0200 -Subject: [PATCH 26/57] fdasd: Fix exit status in error cases (#1734816) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In error cases, fdasd returns -1 which results in the return value 255. -This is due to the fact that only the low-order 8 bits are used for the -status value. See 2.13 Status Information [1] in the POSIX standard and -the exit() POSIX man page [2] for more details. - -Instead of returning -1, use the EXIT_FAILURE constant to indicate -unsuccessful termination properly. - -[1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html -[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exit.html - -Signed-off-by: Jan Höppner -(cherry picked from commit 80443ab629a234e2af55dd9bcceb6fe150c6c79e) ---- - fdasd/fdasd.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c -index 085d2be..5de7955 100644 ---- a/fdasd/fdasd.c -+++ b/fdasd/fdasd.c -@@ -397,7 +397,7 @@ static void fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str) - fputc('\n', stderr); - fputs(err_str, stderr); - -- fdasd_exit(anc, -1); -+ fdasd_exit(anc, EXIT_FAILURE); - } - - /* -@@ -3040,5 +3040,5 @@ int main(int argc, char *argv[]) - } - } - -- return -1; -+ return EXIT_FAILURE; - } --- -2.21.3 - - -From 89ed5688c2b351f487e75e3035948aadcab73a8f Mon Sep 17 00:00:00 2001 -From: Stefan Haberland -Date: Wed, 7 Aug 2019 16:59:20 +0200 -Subject: [PATCH 27/57] zipl: fix zfcp dump image location (#1730707) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The zfcp dumper fails with the following error: - -MLOPDM003I: Machine loader finished, moving data to final storage - location. - - uncompression error ---- System halted - -HCPGIR450W CP entered; disabled wait -PSW 00020001 80000000 00000000 DEADBEEF - -The zipl command shows overlapping components when installing the dumper -to a zfcp disk: - -zipl -d /dev/sda1 -Building bootmap directly on partition '/dev/sda1' -Adding dump section - kernel image......: /lib/s390-tools/zfcpdump/zfcpdump-image - kernel parmline...: 'root=/dev/ram0 dump_mem=1 possible_cpus=1 - cgroup_disable=memory ' - component address: - heap area.......: 0x00002000-0x00005fff - stack area......: 0x0000f000-0x0000ffff - internal loader.: 0x0000a000-0x0000dfff - parameters......: 0x00009000-0x000091ff - kernel image....: 0x00010000-0x005761ff - ^^^^^^ - parmline........: 0x00567000-0x005671ff - ^^^^^^ -Preparing boot device: sda. -Done. - -With the secure IPL patchset the offset of the kernel image has been -removed for the normal IPL case but it has not been removed for the dump -image which leads to the overlap of 0x10000. - -Fix by removing the offset for the dump case. - -Signed-off-by: Stefan Haberland -Reviewd-by: Mikhail Zaslonko -Tested-by: Mikhail Zaslonko -Signed-off-by: Jan Höppner -(cherry picked from commit a9f4ae2db3fa513941244956e323cb6ce214ca5f) ---- - zipl/src/job.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 2d8de8f..e68d1b7 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -559,7 +559,7 @@ get_dump_components(struct job_dump_data *dump, - /* Fill in component data */ - num = 0; - rc = set_cl_element(&cl[num++], "kernel image", dump->image, -- &dump->image_addr, 0, 0x10000, -+ &dump->image_addr, 0, 0x0, - MAXIMUM_PHYSICAL_BLOCKSIZE); - if (rc) - goto error; --- -2.21.3 - - -From 9e5da1e34622725dd9fb7a681f99552bcdabe414 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 12:23:26 +0100 -Subject: [PATCH 28/57] dasdfmt/lsdasd: Add Thin provisioning base support - (#1651733) - -Summary: dasdfmt/lsdasd: Add Thin provisioning base support -Description: Make dasdfmt and lsdasd aware of thinly provisioned (Extent Space - Efficient (ESE)) DASD volumes. - - If an ESE volume is recognised a QUICK format is performed, - formatting only the first two tracks. The mode can always be - overwritten by --mode. - Previously allocated space is always released before formatting, - if not specified otherwise. The option --no-discard (-D) is - provided to omit the space release. - - The ESE information is added to the discipline output of lsdasd - and the extended output additionally lists information about - logical capacity, allocated space, and the extent size. ---- - Makefile | 2 +- - common.mak | 4 - - dasdfmt/Makefile | 1 - - dasdfmt/dasdfmt.8 | 6 + - dasdfmt/dasdfmt.c | 60 +++++++- - dasdfmt/dasdfmt.h | 48 +----- - dasdinfo/Makefile | 3 +- - dasdview/Makefile | 1 - - dasdview/dasdview.c | 4 +- - dasdview/dasdview.h | 3 +- - fdasd/Makefile | 1 - - fdasd/fdasd.c | 2 +- - include/lib/dasd_base.h | 36 +++++ - include/lib/dasd_sys.h | 3 +- - include/lib/u2s.h | 21 --- - include/lib/util_sys.h | 17 ++ - libdasd/dasd_ioctl.c | 20 +++ - libdasd/dasd_sys.c | 114 ++++++++++++-- - libu2s/Makefile | 14 -- - libu2s/misc.c | 27 ---- - libu2s/misc.h | 15 -- - libu2s/u2s.c | 334 ---------------------------------------- - libutil/Makefile | 3 +- - libutil/util_sys.c | 78 ++++++++++ - libzds/libzds.c | 4 +- - tunedasd/src/Makefile | 1 - - zconf/lsdasd | 30 +++- - zdsfs/Makefile | 5 +- - zipl/src/Makefile | 3 +- - zipl/src/install.c | 4 +- - 30 files changed, 354 insertions(+), 510 deletions(-) - delete mode 100644 include/lib/u2s.h - create mode 100644 include/lib/util_sys.h - delete mode 100644 libu2s/Makefile - delete mode 100644 libu2s/misc.c - delete mode 100644 libu2s/misc.h - delete mode 100644 libu2s/u2s.c - create mode 100644 libutil/util_sys.c - -diff --git a/Makefile b/Makefile -index bb2b900..8a07a4d 100644 ---- a/Makefile -+++ b/Makefile -@@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar - # Include common definitions - include common.mak - --LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw libvmcp -+LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp - TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ - tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ -diff --git a/common.mak b/common.mak -index 6029687..6f6acbb 100644 ---- a/common.mak -+++ b/common.mak -@@ -331,10 +331,6 @@ $(rootdir)/libzds/libzds.a: $(rootdir)/libzds - $(MAKE) -C $(rootdir)/libzds/ libzds.a - .PHONY: $(rootdir)/libzds - --$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s -- $(MAKE) -C $(rootdir)/libu2s/ libu2s.a --.PHONY: $(rootdir)/libu2s -- - $(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump - $(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a - .PHONY: $(rootdir)/libvmdump -diff --git a/dasdfmt/Makefile b/dasdfmt/Makefile -index 03877b3..98d9d3e 100644 ---- a/dasdfmt/Makefile -+++ b/dasdfmt/Makefile -@@ -4,7 +4,6 @@ all: dasdfmt - - libs = $(rootdir)/libdasd/libdasd.a \ - $(rootdir)/libvtoc/libvtoc.a \ -- $(rootdir)/libu2s/libu2s.a \ - $(rootdir)/libutil/libutil.a - - dasdfmt: dasdfmt.o $(libs) -diff --git a/dasdfmt/dasdfmt.8 b/dasdfmt/dasdfmt.8 -index 99da9ed..c3e98fa 100644 ---- a/dasdfmt/dasdfmt.8 -+++ b/dasdfmt/dasdfmt.8 -@@ -122,6 +122,8 @@ Format the first two tracks and write label and partition information. Only use - this option if you are sure that the target DASD already contains a regular - format with the specified blocksize. A blocksize can optionally be specified - using \fB-b\fR (\fB--blocksize\fR). -+.br -+For thin-provisioned DASD ESE volumes this is the default mode. - .IP expand - Format all unformatted tracks at the end of the target DASD. This mode assumes - that tracks at the beginning of the DASD volume have already been correctly -@@ -136,6 +138,10 @@ using \fB-b\fR (\fB--blocksize\fR). - Perform a complete format check on a DASD volume. A blocksize can be specified - with \fB-b\fR (\fB--blocksize\fR). - -+.TP -+\fB--no-discard\fR -+Omit a full space release when formatting a thin-provisioned DASD ESE volume. -+ - .TP - \fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR - Number of cylinders to be processed in one formatting step. -diff --git a/dasdfmt/dasdfmt.c b/dasdfmt/dasdfmt.c -index a34d79b..c78080c 100644 ---- a/dasdfmt/dasdfmt.c -+++ b/dasdfmt/dasdfmt.c -@@ -53,6 +53,7 @@ static const struct util_prg prg = { - /* Defines for options with no short command */ - #define OPT_CHECK 128 - #define OPT_NOZERO 129 -+#define OPT_NODISCARD 130 - - static struct util_opt opt_vec[] = { - UTIL_OPT_SECTION("FORMAT ACTIONS"), -@@ -105,6 +106,11 @@ static struct util_opt opt_vec[] = { - .desc = "Prevent storage server from modifying record 0", - .flags = UTIL_OPT_FLAG_NOSHORT, - }, -+ { -+ .option = { "no-discard", no_argument, NULL, OPT_NODISCARD }, -+ .desc = "Do not discard space before formatting", -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - { - .option = { NULL, no_argument, NULL, 'y' }, - .desc = "Start formatting without further user-confirmation", -@@ -921,6 +927,8 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname, - printf("Drive Geometry: %d Cylinders * %d Heads = %d Tracks\n", - cylinders, heads, (cylinders * heads)); - -+ printf("Device Type: %s Provisioned\n", -+ info->ese ? "Thinly" : "Fully"); - printf("\nI am going to format the device "); - printf("%s in the following way:\n", devname); - printf(" Device number of device : 0x%x\n", info->dasd_info.devno); -@@ -939,7 +947,10 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname, - (p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no"); - printf(" Blocksize : %d\n", p->blksize); - printf(" Mode : %s\n", mode_str[mode]); -- -+ if (info->ese) { -+ printf(" Full Space Release : %s\n", -+ (info->no_discard || mode == FULL) ? "no" : "yes"); -+ } - if (info->testmode) - printf("Test mode active, omitting ioctl.\n"); - } -@@ -1234,6 +1245,26 @@ static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders, - process_tracks(info, cylinders, heads, format_params); - } - -+static void dasdfmt_release_space(dasdfmt_info_t *info) -+{ -+ format_data_t r = { -+ .start_unit = 0, -+ .stop_unit = 0, -+ .intensity = DASD_FMT_INT_ESE_FULL, -+ }; -+ int err = 0; -+ -+ if (!info->ese || info->no_discard) -+ return; -+ -+ printf("Releasing space for the entire device...\n"); -+ err = dasd_release_space(dev_filename, &r); -+ if (err) { -+ ERRMSG_EXIT(EXIT_FAILURE, "%s: Could not release space (%s)\n", -+ prog_name, strerror(err)); -+ } -+} -+ - static void dasdfmt_prepare_and_format(dasdfmt_info_t *info, - unsigned int cylinders, - unsigned int heads, format_data_t *p) -@@ -1329,6 +1360,8 @@ static void dasdfmt_quick_format(dasdfmt_info_t *info, unsigned int cylinders, - - if (info->force) { - printf("Skipping format check due to --force.\n"); -+ } else if (info->ese) { -+ printf("Skipping format check due to thin-provisioned device.\n"); - } else { - check_blocksize(info, p->blksize); - -@@ -1394,7 +1427,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, - if ((info->verbosity > 0) || !info->withoutprompt || info->testmode) - dasdfmt_print_info(info, devname, vlabel, cylinders, heads, p); - -- count = u2s_get_host_access_count(devname); -+ count = dasd_get_host_access_count(devname); - if (info->force_host) { - if (count > 1) { - ERRMSG_EXIT(EXIT_FAILURE, -@@ -1438,6 +1471,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, - dasdfmt_prepare_and_format(info, cylinders, heads, p); - break; - case QUICK: -+ dasdfmt_release_space(info); - dasdfmt_quick_format(info, cylinders, heads, p); - break; - case EXPAND: -@@ -1461,6 +1495,19 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname, - } - } - -+static void eval_format_mode(dasdfmt_info_t *info) -+{ -+ if (!info->force && info->mode_specified && info->ese && mode == EXPAND) { -+ ERRMSG_EXIT(EXIT_FAILURE, -+ "WARNING: The specified device is thin-provisioned\n" -+ "Format mode 'expand' is not feasible.\n" -+ "Use --mode=full or --mode=quick to perform a clean format\n"); -+ } -+ -+ if (!info->mode_specified) -+ mode = info->ese ? QUICK : FULL; -+} -+ - int main(int argc, char *argv[]) - { - dasdfmt_info_t info = { -@@ -1496,8 +1543,6 @@ int main(int argc, char *argv[]) - format_params.blksize = DEFAULT_BLOCKSIZE; - format_params.intensity = DASD_FMT_INT_COMPAT; - -- mode = FULL; -- - /*************** parse parameters **********************/ - - while (1) { -@@ -1601,6 +1646,10 @@ int main(int argc, char *argv[]) - "invalid. Consult the man page for " - "more information.\n", - prog_name, optarg); -+ info.mode_specified = 1; -+ break; -+ case OPT_NODISCARD: -+ info.no_discard = 1; - break; - case OPT_CHECK: - info.check = 1; -@@ -1645,6 +1694,9 @@ int main(int argc, char *argv[]) - "device information failed (%s).\n", - prog_name, strerror(rc)); - -+ info.ese = dasd_sys_ese(dev_filename); -+ eval_format_mode(&info); -+ - /* Either let the user specify the blksize or get it from the kernel */ - if (!info.blksize_specified) { - if (!(mode == FULL || -diff --git a/dasdfmt/dasdfmt.h b/dasdfmt/dasdfmt.h -index 5470c90..2f53a0d 100644 ---- a/dasdfmt/dasdfmt.h -+++ b/dasdfmt/dasdfmt.h -@@ -27,10 +27,6 @@ - #include - #include - --/**************************************************************************** -- * SECTION: Definition needed for DASD-API (see dasd.h) * -- ****************************************************************************/ -- - /* - * Represents possible format modes that can be specified when formatting - * a DASD. -@@ -45,57 +41,16 @@ static const char mode_str[3][10] = { - "Full", "Quick", "Expand" - }; - --/* -- * values to be used for format_data_t.intensity -- * 0/8: normal format -- * 1/9: also write record zero -- * 3/11: also write home address -- * 4/12: invalidate track -- */ --#define DASD_FMT_INT_FMT_R0 1 /* write record zero */ --#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */ --#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ --#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ --#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ -- --/* -- * values to be used in format_check_t for indicating -- * possible format errors -- */ --#define DASD_FMT_ERR_TOO_FEW_RECORDS 1 --#define DASD_FMT_ERR_TOO_MANY_RECORDS 2 --#define DASD_FMT_ERR_BLKSIZE 3 --#define DASD_FMT_ERR_RECORD_ID 4 --#define DASD_FMT_ERR_KEY_LENGTH 5 -- --/* -- * values to be used for dasd_information2_t.format -- * 0x00: NOT formatted -- * 0x01: Linux disc layout -- * 0x02: Common disc layout -- */ --#define DASD_FORMAT_NONE 0 --#define DASD_FORMAT_LDL 1 --#define DASD_FORMAT_CDL 2 -- --/**************************************************************************** -- * SECTION: DASDFMT internal types * -- ****************************************************************************/ -- - #define DASD_PARTN_BITS 2 - #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) - - #define EXIT_MISUSE 1 - #define EXIT_BUSY 2 --#define LABEL_LENGTH 14 --#define VLABEL_CHARS 84 --#define LINE_LENGTH 80 - #define ERR_LENGTH 90 - - #define DEFAULT_BLOCKSIZE 4096 - /* requestsize - number of cylinders in one format step */ - #define DEFAULT_REQUESTSIZE 10 --#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1) - - #define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} - #define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} -@@ -137,6 +92,9 @@ typedef struct dasdfmt_info { - int force_host; - int layout_specified; - int check; -+ int mode_specified; -+ int ese; -+ int no_discard; - } dasdfmt_info_t; - - -diff --git a/dasdinfo/Makefile b/dasdinfo/Makefile -index ea45bf1..ed81c3f 100644 ---- a/dasdinfo/Makefile -+++ b/dasdinfo/Makefile -@@ -1,7 +1,6 @@ - include ../common.mak - --libs = $(rootdir)/libu2s/libu2s.a \ -- $(rootdir)/libutil/libutil.a \ -+libs = $(rootdir)/libutil/libutil.a \ - $(rootdir)/libdasd/libdasd.a - - all: dasdinfo -diff --git a/dasdview/Makefile b/dasdview/Makefile -index b7bdba3..59fda17 100644 ---- a/dasdview/Makefile -+++ b/dasdview/Makefile -@@ -7,7 +7,6 @@ all: dasdview - libs = $(rootdir)/libdasd/libdasd.a \ - $(rootdir)/libzds/libzds.a \ - $(rootdir)/libvtoc/libvtoc.a \ -- $(rootdir)/libu2s/libu2s.a \ - $(rootdir)/libutil/libutil.a - - dasdview: dasdview.o $(libs) -diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c -index d773b02..3cf5629 100644 ---- a/dasdview/dasdview.c -+++ b/dasdview/dasdview.c -@@ -28,10 +28,10 @@ - #include "lib/dasd_base.h" - #include "lib/dasd_sys.h" - #include "lib/libzds.h" --#include "lib/u2s.h" - #include "lib/util_base.h" - #include "lib/util_opt.h" - #include "lib/util_prg.h" -+#include "lib/util_sys.h" - #include "lib/vtoc.h" - #include "lib/zt_common.h" - -@@ -183,7 +183,7 @@ dasdview_get_info(dasdview_info_t *info) - else - info->hw_cylinders = characteristics->no_cyl; - -- if (u2s_getbusid(info->device, info->busid) == -1) -+ if (util_sys_get_dev_addr(info->device, info->busid) != 0) - info->busid_valid = 0; - else - info->busid_valid = 1; -diff --git a/dasdview/dasdview.h b/dasdview/dasdview.h -index 50dd3fe..35f2c54 100644 ---- a/dasdview/dasdview.h -+++ b/dasdview/dasdview.h -@@ -11,7 +11,6 @@ - #define DASDVIEW_H - - #include --#include "lib/u2s.h" - - /******************************************************************************** - * SECTION: Definitions needed for DASD-API (see dasd.h) -@@ -109,7 +108,7 @@ typedef struct dasdview_info - int f8c; - int f9c; - -- char busid[U2S_BUS_ID_SIZE]; -+ char busid[DASD_BUS_ID_SIZE]; - int busid_valid; - int raw_track_access; - struct zdsroot *zdsroot; -diff --git a/fdasd/Makefile b/fdasd/Makefile -index 360d81f..facd89e 100644 ---- a/fdasd/Makefile -+++ b/fdasd/Makefile -@@ -3,7 +3,6 @@ include ../common.mak - libs = $(rootdir)/libvtoc/libvtoc.a \ - $(rootdir)/libzds/libzds.a \ - $(rootdir)/libdasd/libdasd.a \ -- $(rootdir)/libu2s/libu2s.a \ - $(rootdir)/libutil/libutil.a - - all: fdasd -diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c -index 5de7955..569cc78 100644 ---- a/fdasd/fdasd.c -+++ b/fdasd/fdasd.c -@@ -935,7 +935,7 @@ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name) - fdasd_error(anc, device_verification_failed, err_str); - } - -- count = u2s_get_host_access_count(name); -+ count = dasd_get_host_access_count(name); - if (anc->force_host) { - if (count > 1) { - snprintf(err_str, ERROR_STRING_SIZE, -diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h -index cf7be2e..abd17f5 100644 ---- a/include/lib/dasd_base.h -+++ b/include/lib/dasd_base.h -@@ -20,6 +20,9 @@ - #include - #include - -+/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */ -+#define DASD_BUS_ID_SIZE 9 -+ - typedef struct dasd_information2_t { - unsigned int devno; /* S/390 devno */ - unsigned int real_devno; /* for aliases */ -@@ -51,6 +54,16 @@ typedef struct dasd_information2_t { - unsigned int reserved7; /* reserved for further use ,... */ - } dasd_information2_t; - -+/* -+ * values to be used for dasd_information2_t.format -+ * 0x00: NOT formatted -+ * 0x01: Linux disc layout -+ * 0x02: Common disc layout -+ */ -+#define DASD_FORMAT_NONE 0 -+#define DASD_FORMAT_LDL 1 -+#define DASD_FORMAT_CDL 2 -+ - struct dasd_eckd_characteristics { - unsigned short cu_type; - struct { -@@ -136,6 +149,16 @@ typedef struct format_data_t { - unsigned int intensity; - } format_data_t; - -+/* -+ * values to be used for format_data_t.intensity -+ */ -+#define DASD_FMT_INT_FMT_R0 1 /* write record zero */ -+#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */ -+#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ -+#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ -+#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ -+#define DASD_FMT_INT_ESE_FULL 32 /* release space for entire volume */ -+ - /* - * struct format_check_t - * represents all data necessary to evaluate the format of -@@ -154,6 +177,16 @@ typedef struct format_check_t { - unsigned int key_length; /* Key length of first record in error */ - } format_check_t; - -+/* -+ * values to be used in format_check_t for indicating -+ * possible format errors -+ */ -+#define DASD_FMT_ERR_TOO_FEW_RECORDS 1 -+#define DASD_FMT_ERR_TOO_MANY_RECORDS 2 -+#define DASD_FMT_ERR_BLKSIZE 3 -+#define DASD_FMT_ERR_RECORD_ID 4 -+#define DASD_FMT_ERR_KEY_LENGTH 5 -+ - #ifndef __linux__ - /* definition from hdreg.h */ - struct hd_geometry { -@@ -174,6 +207,8 @@ struct hd_geometry { - #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t) - /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ - #define BIODASDFMT _IOW(DASD_IOCTL_LETTER, 1, format_data_t) -+/* Release Allocated Space */ -+#define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) - /* Check device format according to format_data_t */ - #define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t) - -@@ -197,6 +232,7 @@ int dasd_check_format(const char *device, format_check_t *p); - int dasd_format_disk(int fd, format_data_t *p); - int dasd_disk_disable(const char *device, int *fd); - int dasd_disk_enable(int fd); -+int dasd_release_space(const char *device, format_data_t *r); - int dasd_get_blocksize(const char *device, unsigned int *blksize); - int dasd_get_blocksize_in_bytes(const char *device, unsigned long long *blksize); - int dasd_get_geo(const char *device, struct hd_geometry *geo); -diff --git a/include/lib/dasd_sys.h b/include/lib/dasd_sys.h -index 5438172..75b186e 100644 ---- a/include/lib/dasd_sys.h -+++ b/include/lib/dasd_sys.h -@@ -13,9 +13,10 @@ - #define LIB_DASD_SYS_H - - #include --#include "u2s.h" - - int dasd_sys_raw_track_access(char *); -+int dasd_sys_ese(char *); - int dasd_reset_chpid(char *, char *); -+int dasd_get_host_access_count(char *device); - - #endif /* LIB_DASD_SYS_H */ -diff --git a/include/lib/u2s.h b/include/lib/u2s.h -deleted file mode 100644 -index fe5d673..0000000 ---- a/include/lib/u2s.h -+++ /dev/null -@@ -1,21 +0,0 @@ --/* -- * -- * Copyright IBM Corp. 2004, 2017 -- * -- * s390-tools is free software; you can redistribute it and/or modify -- * it under the terms of the MIT license. See LICENSE for details. -- * -- * History of changes (starts July 2004) -- * 2004-07-02 initial -- */ -- --#ifndef LIB_U2S_H --#define LIB_U2S_H -- --#define U2S_BUS_ID_SIZE 32 -- --int u2s_getbusid(char *, char *); --int u2s_read_attribute(char *, char *, char *, size_t); --int u2s_get_host_access_count(char *); -- --#endif /* LIB_U2S_H */ -diff --git a/include/lib/util_sys.h b/include/lib/util_sys.h -new file mode 100644 -index 0000000..e0f078b ---- /dev/null -+++ b/include/lib/util_sys.h -@@ -0,0 +1,17 @@ -+/* -+ * @defgroup util_sys_h util_sys: SysFS interface -+ * @{ -+ * @brief Work with SysFS -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef LIB_UTIL_SYS_H -+#define LIB_UTIL_SYS_H -+ -+int util_sys_get_dev_addr(const char *dev, char *addr); -+ -+#endif /** LIB_UTIL_SYS_H @} */ -diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c -index bbfb384..ef1605e 100644 ---- a/libdasd/dasd_ioctl.c -+++ b/libdasd/dasd_ioctl.c -@@ -228,6 +228,26 @@ int dasd_check_format(const char *device, format_check_t *p) - return 0; - } - -+/* -+ * Release Allocated Space -+ * -+ * @param[in] fd device node's file descriptor -+ * @param[in] r format options -+ * -+ * @retval 0 in case of success -+ * @retval errno in case of failure -+ */ -+int dasd_release_space(const char *device, format_data_t *r) -+{ -+ int fd; -+ -+ fd = dasd_open_device(device, O_RDONLY); -+ RUN_IOCTL(fd, BIODASDRAS, r); -+ dasd_close_device(fd); -+ -+ return 0; -+} -+ - /* - * Reread partition table - * -diff --git a/libdasd/dasd_sys.c b/libdasd/dasd_sys.c -index f2fd826..94c2d12 100644 ---- a/libdasd/dasd_sys.c -+++ b/libdasd/dasd_sys.c -@@ -12,7 +12,11 @@ - #include - #include - -+#include "lib/dasd_base.h" - #include "lib/dasd_sys.h" -+#include "lib/util_file.h" -+#include "lib/util_path.h" -+#include "lib/util_sys.h" - - /** - * Get raw-track access mode status -@@ -31,39 +35,80 @@ - */ - int dasd_sys_raw_track_access(char *devnode) - { -- char busid[9]; -- char path[47]; -+ char busid[DASD_BUS_ID_SIZE]; -+ char *path; - FILE *fp; - int rc; - -- if (u2s_getbusid(devnode, busid)) -+ if (util_sys_get_dev_addr(devnode, busid) != 0) - return 0; - -- sprintf(path, "/sys/bus/ccw/devices/%s/raw_track_access", busid); -- -+ path = util_path_sysfs("bus/ccw/devices/%s/raw_track_access", busid); - fp = fopen(path, "r"); -- if (!fp) -+ if (!fp) { -+ free(path); - return 0; -+ } - - rc = fgetc(fp) - '0'; - fclose(fp); -+ free(path); - - return (rc == 1) ? 1 : 0; - } - -+/** -+ * Is volume extent space efficient a.k.a. thin-provisioned -+ * -+ * The "devnode" parameter can be any valid relative or absolute path to -+ * a DASD device node, for example: -+ * -+ * - /dev/dasda -+ * - /dev/disk/by-path/ccw-0.0.bf20 -+ * -+ * @param[in] devnode Device node of interest -+ * -+ * @retval 1 Volume is extent space efficient -+ * @retval 0 Volume is not extent space efficient or -+ * cannot be determined -+ */ -+int dasd_sys_ese(char *devnode) -+{ -+ char busid[DASD_BUS_ID_SIZE]; -+ char *path; -+ FILE *fp; -+ int rc; -+ -+ if (util_sys_get_dev_addr(devnode, busid) != 0) -+ return 0; -+ -+ path = util_path_sysfs("bus/ccw/devices/%s/ese", busid); -+ fp = fopen(path, "r"); -+ if (!fp) { -+ free(path); -+ return 0; -+ } -+ -+ rc = fgetc(fp) - '0'; -+ fclose(fp); -+ -+ return (rc == 1) ? 1 : 0; -+} - - int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) - { - unsigned int val; -- char path[40]; - int count, i; -+ char *path; - FILE *fp; - -- sprintf(path, "/sys/bus/ccw/devices/%s/../chpids", busid); -+ path = util_path_sysfs("bus/ccw/devices/%s/../chpids", busid); - *mask = 0; - fp = fopen(path, "r"); -- if (!fp) -+ if (!fp) { -+ free(path); - return ENODEV; -+ } - - for (i = 0; i < 8; i++) { - count = fscanf(fp, " %x", &val); -@@ -75,6 +120,7 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) - *mask = 0x80 >> i; - } - fclose(fp); -+ free(path); - - return 0; - } -@@ -102,22 +148,25 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) - int dasd_reset_chpid(char *devnode, char *chpid_char) - { - unsigned int chpid; -- char path[41]; -- char busid[9]; -+ char busid[DASD_BUS_ID_SIZE]; - int mask, rc; - char *endptr; -+ char *path; - FILE *fp; - -- if (u2s_getbusid(devnode, busid)) -+ if (util_sys_get_dev_addr(devnode, busid) != 0) - return ENODEV; - - if (!chpid_char) { -- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); -+ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); - fp = fopen(path, "w"); -- if (!fp) -+ if (!fp) { -+ free(path); - return ENODEV; -+ } - fprintf(fp, "%s", "all\n"); - fclose(fp); -+ free(path); - return 0; - } - -@@ -132,12 +181,45 @@ int dasd_reset_chpid(char *devnode, char *chpid_char) - if (!mask) - return ENOENT; - -- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); -+ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); - fp = fopen(path, "w"); -- if (!fp) -+ if (!fp) { -+ free(path); - return ENODEV; -+ } - fprintf(fp, "%02x", mask); - fclose(fp); -+ free(path); - - return 0; - } -+ -+/** -+ * Read amount of host with access to \p device -+ * -+ * The \p device can be any valid relative or absolute path to a DASD device -+ * node, for example: -+ * -+ * - /dev/dasda -+ * - /dev/disk/by-path/ccw-0.0.bf20 -+ * -+ * @param[in] device Device node of interest -+ * -+ * @retval n Number of hosts with access to \p device -+ * @retval 0 Value could not be determined -+ */ -+int dasd_get_host_access_count(char *device) -+{ -+ char busid[9]; -+ char *path; -+ long value; -+ -+ if (!util_sys_get_dev_addr(device, busid)) -+ return 0; -+ -+ path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid); -+ util_file_read_l(&value, 10, path); -+ free(path); -+ -+ return value; -+} -diff --git a/libu2s/Makefile b/libu2s/Makefile -deleted file mode 100644 -index 523282b..0000000 ---- a/libu2s/Makefile -+++ /dev/null -@@ -1,14 +0,0 @@ --include ../common.mak -- --lib = libu2s.a -- --all: $(lib) -- --objects = u2s.o misc.o -- --$(lib): $(objects) -- --install: all -- --clean: -- rm -f *.o $(lib) -diff --git a/libu2s/misc.c b/libu2s/misc.c -deleted file mode 100644 -index d29784c..0000000 ---- a/libu2s/misc.c -+++ /dev/null -@@ -1,27 +0,0 @@ --/* -- * Misc - Local helper functions -- * -- * Copyright IBM Corp. 2016, 2017 -- * -- * s390-tools is free software; you can redistribute it and/or modify -- * it under the terms of the MIT license. See LICENSE for details. -- */ -- --#include --#include "lib/util_base.h" -- --/* -- * Helper function that copies a string safely -- */ --size_t misc_strlcpy(char *dest, const char *src, size_t size) --{ -- size_t str_len = strlen(src); -- size_t len; -- -- if (size) { -- len = MIN(size - 1, str_len); -- memcpy(dest, src, len); -- dest[len] = '\0'; -- } -- return str_len; --} -diff --git a/libu2s/misc.h b/libu2s/misc.h -deleted file mode 100644 -index 53b7356..0000000 ---- a/libu2s/misc.h -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * Misc - Local helper functions -- * -- * Copyright IBM Corp. 2016, 2017 -- * -- * s390-tools is free software; you can redistribute it and/or modify -- * it under the terms of the MIT license. See LICENSE for details. -- */ -- --#ifndef MISC_H --#define MISC_H -- --size_t misc_strlcpy(char *, const char *, size_t); -- --#endif /* MISC_H */ -diff --git a/libu2s/u2s.c b/libu2s/u2s.c -deleted file mode 100644 -index 9ffafa7..0000000 ---- a/libu2s/u2s.c -+++ /dev/null -@@ -1,334 +0,0 @@ --/* -- * -- * Copyright IBM Corp. 2004, 2017 -- * -- * s390-tools is free software; you can redistribute it and/or modify -- * it under the terms of the MIT license. See LICENSE for details. -- * -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "lib/u2s.h" --#include "misc.h" -- --#define DEV_BUFFER_LENGTH 20 --#define PATH_BUFFER_LENGTH 256 --#define BUSIDSIZE 9 -- --#define BLOCKPATH "/sys/block/" --#define DEVICE_LINK "device" --#define DEV_ATTRIBUTE "dev" -- -- --/* -- * Helper function that expects a file name and returns 1 if this -- * is a directory or 0 otherwise. -- */ --static int isdir(char *name) { -- -- struct stat statbuf; -- -- if (stat(name, &statbuf) < 0) -- return 0; -- return S_ISDIR(statbuf.st_mode); --} -- --/* -- * Helper function that expects a directory name in sysfs of the form -- * /sys/block// or /sys/block///. -- * It will try to read the file "dev" in this directory and compare -- * it's contents with the given dev string of the form :. -- * Trailing white space (newline) is ignored. -- * The buffer name is expected to be long enough to hold the additional "dev". -- * Returns 1 if the directory matches dev, 0 otherwise. -- */ --static int check_directory(char *name, char *dev) { -- -- char buffer[DEV_BUFFER_LENGTH]; -- char *end; -- int fd; -- ssize_t count; -- int dev_attr_len, dev_parm_len; -- unsigned int namelen; -- -- namelen = strlen(name); -- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEV_ATTRIBUTE)) -- return 0; -- end = name + namelen; -- strcpy(end, DEV_ATTRIBUTE); -- fd = open(name, O_RDONLY); -- *end = 0; -- if (fd < 0) -- return 0; -- count = read(fd, buffer, DEV_BUFFER_LENGTH); -- close(fd); -- if (count < 0) -- return 0; -- dev_attr_len = strspn(buffer, "1234567890:"); -- dev_parm_len = strlen(dev); -- if (dev_attr_len != dev_parm_len ) -- return 0; -- return (strncmp(dev, buffer, dev_parm_len) == 0); --} -- --/* -- * Helper function that expects a directory name in sysfs of the form -- * /sys/block//. It will try to read a link "device" -- * in this directory and extract the busid, which is the last part -- * of that link. The buffer name is expected to be long enough -- * to hold the additional "device". -- * name: block device path in sysfs. -- * busid: buffer in which the busid string will be returned -- * returns 0 for successful operation and -1 in case of an error. -- */ --static int extract_busid(char *name, char *busid) { -- -- int count; -- unsigned int namelen; -- char linkbuffer[PATH_BUFFER_LENGTH]; -- char *start, *end; -- size_t len; -- -- namelen = strlen(name); -- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEVICE_LINK)) -- return 0; -- end = name + namelen; -- strcpy(end, DEVICE_LINK); -- count = readlink(name, linkbuffer, PATH_BUFFER_LENGTH - 1); -- if (count < 0) -- return -1; -- linkbuffer[count] = 0; -- start = strrchr(linkbuffer, '/'); -- if (!start) -- return -1; -- start++; -- len = misc_strlcpy(busid, start, BUSIDSIZE); -- if (len >= BUSIDSIZE) -- return -1; -- -- return 0; --}; -- --/* -- * Helper function that makes some basic checks on a directory entry. -- * The function checks if there is still enough space left in the buffer -- * for the new string, excludes '.' and '..', and verifies that the entry -- * is actually a directory. -- * buffer: the beginning of the name buffer -- * oldend: the current end of the string in the name buffer -- * dir: the dirent in question -- * returns: a pointer to the new end of the string in buffer or NULL if -- * one of the checks failed -- */ -- --static char *append_if_directory(char *buffer, char *oldend, struct dirent *dir) { -- -- char *newend; -- int oldlength, dirlength; -- -- if (strcmp(dir->d_name, ".") == 0 || -- strcmp(dir->d_name, "..") == 0) -- return NULL; -- oldlength = strlen(buffer); -- dirlength = strlen(dir->d_name); -- if (PATH_BUFFER_LENGTH < oldlength + dirlength + 2) -- return NULL; -- strcpy(oldend, dir->d_name); -- if (!isdir(buffer)) { -- *oldend = 0; -- return NULL; -- } -- newend = oldend + dirlength; -- strcpy(newend, "/"); -- newend++; -- -- return newend; --} -- --/* -- * helper function that searches for a specific block device and returns -- * it's busid -- * dev: : of the device -- * busid: buffer in which the busid string will be returned -- * returns 0 for successful operation and -1 in case of an error. -- */ --static int find_busid_in_sysfs(char *dev, char *busid) { -- -- DIR *blockdir, *diskdir; -- struct dirent *blockde, *diskde; -- int found = 0; -- char namebuffer[PATH_BUFFER_LENGTH]; -- char *blockend, *diskend = NULL, *partend; -- -- /* everything, including the other helper functions, works on the -- * same buffer area 'namebuffer'. The pointers blockend, diskend -- * and partend point to the end of the various names. -- * Example: -- * "/sys/block/dasda/dasda1/" -- * ^ blockend -- * ^ diskend -- * ^ partend -- */ -- -- strcpy(namebuffer,BLOCKPATH); -- blockdir = opendir(namebuffer); -- if (!blockdir) -- return -1; -- blockend = namebuffer + strlen(namebuffer); -- /* check each entry in /sys/block */ -- while ((blockde = readdir(blockdir))) { -- diskend = append_if_directory(namebuffer, blockend, blockde); -- if (!diskend) -- continue; -- found = check_directory(namebuffer, dev); -- if (found) -- break; -- diskdir = opendir(namebuffer); -- if (!diskdir) -- continue; -- /* check each entry in /sys/block/ */ -- while ((diskde = readdir(diskdir))) { -- partend = append_if_directory( -- namebuffer, diskend, diskde); -- if (!partend) -- continue; -- found = check_directory(namebuffer, dev); -- if (found) -- break; -- } -- closedir(diskdir); -- if (found) -- break; -- } -- closedir(blockdir); -- if (found) { -- *diskend = 0; /* remove partition directory from name */ -- return extract_busid(namebuffer, busid); -- } else -- return -1; --} -- --/* -- * helper function that searches for a specific block device in -- * /proc/dasd/devices and returns it's bus-ID -- * maja, mina: , of the device -- * busid: buffer in which the bus-ID string will be returned -- * returns 0 for successful operation and -1 in case of an error -- * e.g. /proc/dasd/devices does not exist. -- * -- * An entry looks like: -- * 0.0.XXXX(DISCIPLINE) at ( MAJ: MIN) is dasdX : -- * active at blocksize: BLOCKSIZE, BLOCKS blocks, SIZE MB -- */ --static int find_busid_in_proc(int maja, int mina, char *busid) --{ -- FILE *filp; -- char bus[BUSIDSIZE]; -- int majb, minb, rc; -- size_t len; -- -- rc = -1; -- -- filp = fopen("/proc/dasd/devices", "r"); -- if (!filp) -- return rc; -- while (fscanf(filp, "%[^(] %*[^)] ) at ( %d : %d %*[^\n]\n", -- bus, &majb, &minb) != EOF) { -- if ((maja == majb) && (mina == minb)) { -- len = misc_strlcpy(busid, bus, BUSIDSIZE); -- if (len < BUSIDSIZE) -- rc = 0; -- break; -- } -- } -- -- fclose(filp); -- return rc; --} -- --/* -- * Return the busid of a given device node. -- * Works only for block devices. -- * devicenode: path to the device node -- * busid: buffer in which the busid string will be returned -- * returns 0 for successful operation and -1 in case of an error. -- */ --int u2s_getbusid(char *devicenode, char *busid) --{ -- int maj, min, rc; -- struct stat stat_buf; -- char dev_string[DEV_BUFFER_LENGTH]; -- -- /* -- * Get major and minor information of the device special file -- * and combine them to a : string, as returned by -- * the dev attributes in sysfs -- */ -- if (stat(devicenode, &stat_buf)) -- return -1; -- if (!S_ISBLK(stat_buf.st_mode)) -- return -1; -- maj = major(stat_buf.st_rdev); -- min = minor(stat_buf.st_rdev); -- -- rc = find_busid_in_proc(maj, min, busid); -- if (rc) { -- snprintf(dev_string, DEV_BUFFER_LENGTH, "%u:%u", maj, min); -- rc = find_busid_in_sysfs(dev_string, busid); -- } -- -- return rc; --} -- --/* -- * Attempts to find the sysfs entry for the given busid and reads -- * the contents of a specified attribute to the buffer -- */ --int u2s_read_attribute(char *busid, char *attribute, char *buffer, -- size_t count) --{ -- char path[100]; -- int rc, fd; -- ssize_t rcount; -- -- rc = 0; -- snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s", -- busid, attribute); -- fd = open(path, O_RDONLY); -- if (fd < 0) -- return errno; -- rcount = read(fd, buffer, count); -- if (rcount < 0) -- rc = errno; -- close(fd); -- return rc; --} -- --int u2s_get_host_access_count(char *devicenode) --{ -- char busid[BUSIDSIZE]; -- unsigned long value; -- char buffer[10]; -- char *endp; -- -- u2s_getbusid(devicenode, busid); -- u2s_read_attribute(busid, "host_access_count", buffer, sizeof(buffer)); -- -- value = strtoul(buffer, &endp, 0); -- -- if (endp == buffer) -- return -EINVAL; -- -- return value; --} -diff --git a/libutil/Makefile b/libutil/Makefile -index fd5e213..8a11db2 100644 ---- a/libutil/Makefile -+++ b/libutil/Makefile -@@ -26,7 +26,8 @@ objects = util_base.o \ - util_part.o \ - util_prg.o \ - util_proc.o \ -- util_rec.o -+ util_rec.o \ -+ util_sys.o - - util_base_example: util_base_example.o $(lib) - util_panic_example: util_panic_example.o $(lib) -diff --git a/libutil/util_sys.c b/libutil/util_sys.c -new file mode 100644 -index 0000000..05e3653 ---- /dev/null -+++ b/libutil/util_sys.c -@@ -0,0 +1,78 @@ -+/* -+ * util - Utility function library -+ * -+ * SysFS helper functions -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_path.h" -+#include "lib/util_sys.h" -+ -+/* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */ -+#define READLINK_SIZE 256 -+ -+/** -+ * Identify device address -+ * -+ * Identifying the device address with this function works for almost any -+ * character and block device (e.g. NVMe, SCSI, DASD, etc). -+ * The user must provide a buffer that is large enough for the desired device -+ * address to be read into \p addr. -+ * -+ * @param[in] dev Device node of interest -+ * @param[out] addr Identified device address -+ * -+ * @retval 0 Success -+ * @retval -1 Error while reading device information or -+ * constructed path -+ */ -+int util_sys_get_dev_addr(const char *dev, char *addr) -+{ -+ char device[READLINK_SIZE], *result; -+ unsigned int maj, min; -+ struct stat s; -+ ssize_t len; -+ char *path; -+ -+ if (stat(dev, &s) != 0) -+ return -1; -+ -+ maj = major(s.st_rdev); -+ min = minor(s.st_rdev); -+ -+ if (S_ISBLK(s.st_mode)) -+ path = util_path_sysfs("dev/block/%u:%u/device", maj, min); -+ else if (S_ISCHR(s.st_mode)) -+ path = util_path_sysfs("dev/char/%u:%u/device", maj, min); -+ else -+ return -1; -+ -+ len = readlink(path, device, READLINK_SIZE - 1); -+ free(path); -+ if (len != -1) -+ device[len] = '\0'; -+ else -+ return -1; -+ -+ result = strrchr(device, '/'); -+ if (result) -+ result++; -+ else -+ result = device; -+ strcpy(addr, result); -+ -+ return 0; -+} -diff --git a/libzds/libzds.c b/libzds/libzds.c -index 2430962..affb30c 100644 ---- a/libzds/libzds.c -+++ b/libzds/libzds.c -@@ -20,8 +20,8 @@ - #include - - #include "lib/dasd_base.h" -+#include "lib/dasd_sys.h" - #include "lib/libzds.h" --#include "lib/u2s.h" - #include "lib/util_base.h" - #include "lib/util_list.h" - #include "lib/vtoc.h" -@@ -3727,7 +3727,7 @@ int lzds_analyse_open_count(struct zdsroot *root, int warn) - int rc = 0; - - util_list_iterate(root->dasdlist, dasd) { -- value = u2s_get_host_access_count(dasd->device); -+ value = dasd_get_host_access_count(dasd->device); - - if (value < 0) { - fprintf(stderr, -diff --git a/tunedasd/src/Makefile b/tunedasd/src/Makefile -index 959b8eb..c79a235 100644 ---- a/tunedasd/src/Makefile -+++ b/tunedasd/src/Makefile -@@ -3,7 +3,6 @@ include ../../common.mak - ALL_CPPFLAGS += -I../include -I../boot - - libs = $(rootdir)/libdasd/libdasd.a \ -- $(rootdir)/libu2s/libu2s.a \ - $(rootdir)/libutil/libutil.a - - all: tunedasd -diff --git a/zconf/lsdasd b/zconf/lsdasd -index a0af6ea..792efc0 100755 ---- a/zconf/lsdasd -+++ b/zconf/lsdasd -@@ -164,6 +164,7 @@ function gatherDeviceData() { - read DEV_UID 2> /dev/null < $DEVPATH/uid || continue - read READONLY 2> /dev/null < $DEVPATH/readonly || continue - read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue -+ read ESE 2> /dev/null < $DEVPATH/ese - - # Block device specific information is only available for - # devices that are online and not a PAV alias -@@ -244,7 +245,7 @@ function newoutput() - #-------------------------------------------# - - if [[ "$ONLINE" == 0 ]]; then -- printf "%s:%s:%-8s offline\n" \ -+ printf "%s:%s:%-8s offline\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" ; - return -@@ -252,7 +253,7 @@ function newoutput() - - if [[ "$ALIAS" == 1 ]]; then - if [[ "$BASEONLY" == "false" ]]; then -- printf "%s:%s:%-8s alias %28s\n" \ -+ printf "%s:%s:%-8s alias %26s\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" \ - "$DISCIPLINE" -@@ -286,7 +287,11 @@ function newoutput() - ACTIVE="active" - fi - -- printf "%s:%s:%-8s %-6s%-4s %-8s %-2s:%-2s %-4s %-4s %-8s %s\n" \ -+ if [[ "$ESE" == 1 ]]; then -+ DISCIPLINE="${DISCIPLINE} (ESE)" -+ fi -+ -+ printf "%s:%s:%-8s %-6s%-2s %-8s %-2s:%-2s %-11s %-4s %-8s %s\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" \ - "$ACTIVE" \ -@@ -378,6 +383,10 @@ function extended() - read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks - read -a C 2> /dev/null < $DEVPATH/../chpids - read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom -+ read ESE 2> /dev/null < $DEVPATH/ese -+ read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size -+ read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity -+ read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated - - # convert to hexadecimal values - PIM=0x$PIM -@@ -550,7 +559,11 @@ function extended() - COLON=":" - fi - -- printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ -+ if [[ "$ESE" == 1 ]]; then -+ DISCIPLINE="${DISCIPLINE} (ESE)" -+ fi -+ -+ printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" \ - "$BLOCKNAME" \ -@@ -562,6 +575,9 @@ function extended() - "$SSIZE" \ - "$MBSIZE" \ - "$BLOCKCOUNT" \ -+ "$EXTSZ" \ -+ "$CAPACITY" \ -+ "$ALLOCATED" \ - "$DIAG" \ - "$READONLY" \ - "$EER" \ -@@ -787,10 +803,10 @@ fi - - if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then - printf "Bus-ID Name UID\n" -- printf "==============================================================================\n" -+ printf "================================================================================\n" - elif [[ "$OUTPUT" == "new" ]]; then -- printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n" -- printf "==============================================================================\n" -+ printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n" -+ printf "================================================================================\n" - elif [[ "$OUTPUT" == "extended" ]]; then - PROCESSING=" $PROCESSING | sed 's/#/\n/g' " - fi -diff --git a/zdsfs/Makefile b/zdsfs/Makefile -index 579ce4b..07d7658 100644 ---- a/zdsfs/Makefile -+++ b/zdsfs/Makefile -@@ -2,9 +2,8 @@ include ../common.mak - - libs = $(rootdir)/libzds/libzds.a \ - $(rootdir)/libvtoc/libvtoc.a \ -- $(rootdir)/libu2s/libu2s.a \ -- $(rootdir)/libutil/libutil.a \ -- $(rootdir)/libdasd/libdasd.a -+ $(rootdir)/libdasd/libdasd.a \ -+ $(rootdir)/libutil/libutil.a - - ifeq (${HAVE_FUSE},0) - -diff --git a/zipl/src/Makefile b/zipl/src/Makefile -index bed970c..fd77670 100644 ---- a/zipl/src/Makefile -+++ b/zipl/src/Makefile -@@ -7,8 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \ - -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) - ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) - --libs = $(rootdir)/libutil/libutil.a \ -- $(rootdir)/libu2s/libu2s.a -+libs = $(rootdir)/libutil/libutil.a - - objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ - install.o zipl.o $(rootdir)/zipl/boot/data.o -diff --git a/zipl/src/install.c b/zipl/src/install.c -index a6c4333..20b53f5 100644 ---- a/zipl/src/install.c -+++ b/zipl/src/install.c -@@ -23,7 +23,7 @@ - #include - #include - --#include "lib/u2s.h" -+#include "lib/util_sys.h" - - #include "boot.h" - #include "bootmap.h" -@@ -1132,7 +1132,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count, - parm.param[i].num_heads = info[i]->geo.heads; - parm.param[i].blocksize = info[i]->phy_block_size >> 8; - parm.param[i].devno = info[i]->devno; -- if (u2s_getbusid(device[i], busid)) { -+ if (util_sys_get_dev_addr(device[i], busid) != 0) { - error_text("Could not find bus-ID for '%s'", device[i]); - rc = -1; - goto out; --- -2.21.3 - - -From 836d4b9f0f4387a3e35125be8ee50563501d6ab7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 12:29:42 +0100 -Subject: [PATCH 29/57] zdsfs: add online vtoc refresh (#1685536) - -Description: Enable zdsfs to access datasets that were created after zdsfs was - mounted without the need to remount zdsfs. - This is done by re-reading the VTOC with every readdir system - call. - To ensure a consistent VTOC state the DASD device is reserved for - every VTOC read and released afterwards. -Upstream-ID: bc053a975af6bd8eecd31269656f305d4c744fbe ---- - dasdview/dasdview.c | 2 +- - include/lib/dasd_base.h | 6 ++ - include/lib/libzds.h | 83 ++++++++++++++++-- - libdasd/dasd_ioctl.c | 39 +++++++++ - libzds/libzds.c | 184 +++++++++++++++++----------------------- - zdsfs/zdsfs.c | 97 +++++++++++++++++---- - 6 files changed, 280 insertions(+), 131 deletions(-) - -diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c -index 3cf5629..3dc5890 100644 ---- a/dasdview/dasdview.c -+++ b/dasdview/dasdview.c -@@ -1741,7 +1741,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info) - " rc=%d\n", rc); - exit(-1); - } -- rc = lzds_dasd_read_rawvtoc(info->dasd); -+ rc = lzds_dasd_alloc_rawvtoc(info->dasd); - if (rc == EINVAL) { - zt_error_print("dasdview: Cannot read VTOC because disk does" - " not contain valid VOL1 label.\n", -diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h -index abd17f5..b12e1f9 100644 ---- a/include/lib/dasd_base.h -+++ b/include/lib/dasd_base.h -@@ -203,6 +203,10 @@ struct hd_geometry { - #define BIODASDDISABLE _IO(DASD_IOCTL_LETTER, 0) - /* Enable the volume (for Linux) */ - #define BIODASDENABLE _IO(DASD_IOCTL_LETTER, 1) -+/* Reserve the device for the current LPAR */ -+#define BIODASDRSRV _IO(DASD_IOCTL_LETTER, 2) -+/* Release the device for the current LPAR */ -+#define BIODASDRLSE _IO(DASD_IOCTL_LETTER, 3) - /* Get information on a dasd device (enhanced) */ - #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t) - /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ -@@ -239,5 +243,7 @@ int dasd_get_geo(const char *device, struct hd_geometry *geo); - int dasd_get_info(const char *device, dasd_information2_t *info); - int dasd_is_ro(const char *device, bool *ro); - int dasd_reread_partition_table(const char *device, int ntries); -+int dasd_disk_reserve(const char *device); -+int dasd_disk_release(const char *device); - - #endif /* LIB_DASD_BASE_H */ -diff --git a/include/lib/libzds.h b/include/lib/libzds.h -index 355bf77..6ae767b 100644 ---- a/include/lib/libzds.h -+++ b/include/lib/libzds.h -@@ -112,6 +112,8 @@ - */ - #define LIB_LIBZDS_H - -+#include "lib/util_base.h" -+#include "lib/util_list.h" - #include "vtoc.h" - - -@@ -328,11 +330,74 @@ struct pds_member_entry { - */ - struct zdsroot; - -+/** -+ * @struct raw_vtoc -+ * @brief The VTOC is a directory of data sets on one DASD -+ * -+ * As the VTOC is the data area on the DASD that describes all data sets, -+ * this library will often have to refer to the various records in the VTOC. -+ * To make this more efficient, we will read the whole VTOC once and identify -+ * all elements (DSCBs). The raw data of the VTOC tracks and the index to the -+ * DSCBs is stored. -+ */ -+struct raw_vtoc { -+ /** @brief The raw track data */ -+ char *rawdata; -+ /** @brief This size of the raw track data in bytes */ -+ unsigned long long rawdatasize; -+ /** @brief An array with pointers to the various DSCBs in the rawdata */ -+ char **vtocindex; -+ /** @brief Number of entries in the index */ -+ unsigned int vtocindexcount; -+ /** @brief Number of records per VTOC track -+ * -+ * @note While the DS4DEVDT field in the format 4 DSCB names the number -+ * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1 -+ * for record 0. -+ */ -+ unsigned int vtoc_rec_per_track; -+ /** @brief The track number at which the vtoc begins on the DASD */ -+ unsigned int vtoctrackoffset; -+ /** @brief Start record of VTOC. -+ * -+ * The rawdata contains full tracks. This is the number of the first -+ * record that actually belongs to the VTOC -+ */ -+ unsigned int vtocrecno; -+ /** @brief The DASD this vtoc was read from */ -+ struct dasd *dasd; -+ /** @brief Detailed error messages in case of a problem */ -+ struct errorlog *log; -+}; -+ - /** - * @struct dasd - * @brief Represents one physical device, may have a vtoc - */ --struct dasd; -+struct dasd { -+ /** @brief List head used to store a list of DASDs in struct zdsroot */ -+ struct util_list_node list; -+ /** @brief Name of the block device, e.g. /dev/dasde */ -+ char *device; -+ /** @brief File descriptor for the block device. -+ * -+ * The device is kept open for as along as the library uses it. -+ * This lets the system know that the device is still in use. -+ */ -+ int inusefd; -+ /* @brief where to find the volume label */ -+ unsigned int label_block; -+ /** @brief Device geometry. How many cylinders does the DASD have. */ -+ unsigned int cylinders; -+ /** @brief Device geometry. How many heads does the DASD have. */ -+ unsigned int heads; -+ /** @brief The VTOC data that has been read from this device */ -+ struct raw_vtoc *rawvtoc; -+ /** @brief The volume label that has been read from this device */ -+ volume_label_t *vlabel; -+ /** @brief Detailed error messages in case of a problem */ -+ struct errorlog *log; -+}; - - /** - * @struct dasditerator -@@ -350,12 +415,6 @@ struct dasditerator; - */ - struct dasdhandle; - --/** -- * @struct raw_vtoc -- * @brief The VTOC is a directory of data sets on one dasd -- */ --struct raw_vtoc; -- - /** - * @struct dscbiterator - * @brief allows to iterate over all DSCBs in a vtoc -@@ -572,7 +631,13 @@ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel); - * @brief Read the vtoc data from device. The data as stored as part - * of the struct dasd. - */ --int lzds_dasd_read_rawvtoc(struct dasd *dasd); -+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc); -+ -+/** -+ * @brief Read the vtoc data from device. The data as stored as part -+ * of the struct dasd. -+ */ -+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd); - - /** - * @brief Get the previously read raw_vtoc data. -@@ -787,6 +852,8 @@ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, - struct dasd *dasd); - - -+void lzds_dslist_free(struct zdsroot *root); -+ - - /** @} */ /* end of group libzds_functions_high */ - -diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c -index ef1605e..22e6d21 100644 ---- a/libdasd/dasd_ioctl.c -+++ b/libdasd/dasd_ioctl.c -@@ -281,3 +281,42 @@ int dasd_reread_partition_table(const char *device, int ntries) - - return err; - } -+ -+/* -+ * Reserve DASD disk. -+ * -+ * @param[in] device node device node's name -+ * -+ * @retval 0 in case of success -+ * @retval errno in case of failure -+ * -+ */ -+int dasd_disk_reserve(const char *device) -+{ -+ int fd; -+ -+ fd = dasd_open_device(device, O_RDONLY); -+ RUN_IOCTL(fd, BIODASDRSRV, NULL); -+ dasd_close_device(fd); -+ -+ return 0; -+} -+ -+/* -+ * Release DASD disk -+ * -+ * @param[in] device node device node's name -+ * -+ * @retval 0 in case of success -+ * @retval errno in case of failure -+ */ -+int dasd_disk_release(const char *device) -+{ -+ int fd; -+ -+ fd = dasd_open_device(device, O_RDONLY); -+ RUN_IOCTL(fd, BIODASDRLSE, NULL); -+ dasd_close_device(fd); -+ -+ return 0; -+} -diff --git a/libzds/libzds.c b/libzds/libzds.c -index affb30c..c444769 100644 ---- a/libzds/libzds.c -+++ b/libzds/libzds.c -@@ -22,8 +22,6 @@ - #include "lib/dasd_base.h" - #include "lib/dasd_sys.h" - #include "lib/libzds.h" --#include "lib/util_base.h" --#include "lib/util_list.h" - #include "lib/vtoc.h" - - /** @cond PRIVATE */ -@@ -70,42 +68,6 @@ struct errormsg { - char text[ERRORMSG]; - }; - --/** -- * As the VTOC is the data area on the DASD that describes all data sets, -- * this library will often have to refer to the various records in the VTOC. -- * To make this more efficiant, we will read the whole VTOC once and identify -- * all elements (DSCBs). The raw data of the VTOC tracks and the index to the -- * DSCBs is stored. -- */ --struct raw_vtoc { -- /** @brief The raw track data */ -- char *rawdata; -- /** @brief This size of the raw track data in bytes */ -- unsigned long long rawdatasize; -- /** @brief An array with pointers to the various DSCBs in the rawdata */ -- char **vtocindex; -- /** @brief Number of entries in the index */ -- unsigned int vtocindexcount; -- /** @brief Number of records per VTOC track -- * -- * @note While the DS4DEVDT field in the format 4 DSCB names the number -- * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1 -- * for record 0. -- */ -- unsigned int vtoc_rec_per_track; -- /** @brief The track number in which the vtoc begins on the DASD */ -- unsigned int vtoctrackoffset; -- /** @brief Start record of VTOC. -- * -- * The rawdata contains full tracks. This is the number of the first -- * record that actually belongs to the VTOC */ -- unsigned int vtocrecno; -- /** @brief The DASD this vtoc was read from */ -- struct dasd *dasd; -- /** @brief Detailed error messages in case of a problem */ -- struct errorlog *log; --}; -- - struct dscbiterator { - /** @brief The raw_vtoc this iterator refers to */ - struct raw_vtoc *rawvtoc; -@@ -113,31 +75,6 @@ struct dscbiterator { - unsigned int i; - }; - --struct dasd { -- /** @brief List head used to store a list of DASDs in struct zdsroot */ -- struct util_list_node list; -- /** @brief Name of the block device, e.g. /dev/dasde */ -- char *device; -- /** @brief File descriptor for the block device. -- * -- * The device is kept open for as along as the library uses it. -- * This lets the system know that the device is still in use. -- */ -- int inusefd; -- /* @brief where to find the volume label */ -- unsigned int label_block; -- /** @brief Device geometry. How many cylinders does the DASD have. */ -- unsigned int cylinders; -- /** @brief Device geometry. How many heads does the DASD have. */ -- unsigned int heads; -- /** @brief The VTOC data that has been read from this device */ -- struct raw_vtoc *rawvtoc; -- /** @brief The volume label that has been read from this device */ -- volume_label_t *vlabel; -- /** @brief Detailed error messages in case of a problem */ -- struct errorlog *log; --}; -- - struct dasdhandle { - /** @brief The struct dasd this context relates to */ - struct dasd *dasd; -@@ -412,6 +349,7 @@ int lzds_zdsroot_alloc(struct zdsroot **root) - return 0; - } - -+ - /** - * It should be noted that this frees all structures that are owned by the - * root structure as well. For example, a pointer to a struct dasd that -@@ -419,21 +357,11 @@ int lzds_zdsroot_alloc(struct zdsroot **root) - * - * @param[in] root Reference to the zdsroot structure that is to be freed. - */ --void lzds_zdsroot_free(struct zdsroot *root) -+void lzds_dslist_free(struct zdsroot *root) - { -- struct dasd *dasd, *nextdasd; - struct dataset *ds, *nextds; - int i; - -- if (!root) -- return; -- -- util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { -- util_list_remove(root->dasdlist, dasd); -- dasd_free(dasd); -- } -- util_list_free(root->dasdlist); -- - util_list_iterate_safe(root->datasetlist, ds, nextds) { - util_list_remove(root->datasetlist, ds); - dataset_free_memberlist(ds); -@@ -442,6 +370,28 @@ void lzds_zdsroot_free(struct zdsroot *root) - errorlog_free(ds->log); - free(ds); - } -+} -+ -+/** -+ * It should be noted that this frees all structures that are owned by the -+ * root structure as well. For example, a pointer to a struct dasd that -+ * has been returned by lzds_zdsroot_add_device is not valid anymore. -+ * -+ * @param[in] root Reference to the zdsroot structure that is to be freed. -+ */ -+void lzds_zdsroot_free(struct zdsroot *root) -+{ -+ struct dasd *dasd, *nextdasd; -+ -+ if (!root) -+ return; -+ -+ util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { -+ util_list_remove(root->dasdlist, dasd); -+ dasd_free(dasd); -+ } -+ util_list_free(root->dasdlist); -+ lzds_dslist_free(root); - util_list_free(root->datasetlist); - errorlog_free(root->log); - free(root); -@@ -1443,7 +1393,7 @@ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, - * - EPROTO The VTOC data is not in a valid format. - * - EIO Other I/O error - */ --int lzds_dasd_read_rawvtoc(struct dasd *dasd) -+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc) - { - unsigned long long vtoctrckno, vtocrecno; - unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize; -@@ -1455,26 +1405,11 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd) - format4_label_t *f4; - unsigned long long rawvtocsize; - -- struct raw_vtoc *rawvtoc = NULL; - volume_label_t *vlabel = NULL; - char *trackdata = NULL; - char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */ - - errorlog_clear(dasd->log); -- /* cleanup the old rawvtoc structures before we read new ones */ -- rawvtoc = dasd->rawvtoc; -- dasd->rawvtoc = NULL; -- if (rawvtoc) { -- free(rawvtoc->rawdata); -- free(rawvtoc->vtocindex); -- free(rawvtoc); -- } -- -- rawvtoc = malloc(sizeof(*rawvtoc)); -- if (!rawvtoc) -- return ENOMEM; -- memset(rawvtoc, 0, sizeof(*rawvtoc)); -- rawvtoc->dasd = dasd; - - rc = lzds_dasd_get_vlabel(dasd, &vlabel); - if (rc) { -@@ -1611,13 +1546,49 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd) - ++i; - } - -- dasd->rawvtoc = rawvtoc; - return 0; - - cleanup: -- free(rawvtoc->vtocindex); - free(trackdata); -- free(rawvtoc); -+ return rc; -+} -+ -+/** -+ * @param[in] dasd The struct dasd that represents the device we want to read -+ * the VTOC from. -+ * @return 0 on success, otherwise one of the following error codes: -+ * - ENOMEM Could not allocate internal structure due to lack of memory. -+ * - EINVAL The volume label has not yet been read or it is not valid. -+ * - EPROTO The VTOC data is not in a valid format. -+ * - EIO Other I/O error -+ */ -+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd) -+{ -+ struct raw_vtoc *rawvtoc = NULL; -+ int rc; -+ -+ /* cleanup the old rawvtoc structures before we read new ones */ -+ rawvtoc = dasd->rawvtoc; -+ dasd->rawvtoc = NULL; -+ if (rawvtoc) { -+ free(rawvtoc->rawdata); -+ free(rawvtoc->vtocindex); -+ free(rawvtoc); -+ } -+ -+ rawvtoc = malloc(sizeof(*rawvtoc)); -+ if (!rawvtoc) -+ return ENOMEM; -+ memset(rawvtoc, 0, sizeof(*rawvtoc)); -+ rawvtoc->dasd = dasd; -+ -+ rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc); -+ if (rc) { -+ free(rawvtoc->vtocindex); -+ free(rawvtoc); -+ } else { -+ dasd->rawvtoc = rawvtoc; -+ } - return rc; - } - -@@ -2261,6 +2232,7 @@ out1: - static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds) - { - int k, l, dspcount; -+ - for (k = 0; k < MAXVOLUMESPERDS; ++k) { - /* if both datasets have a part in position k, - * then something is wrong */ -@@ -2280,18 +2252,20 @@ static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds) - * Since dsp[0] may not be set yet, we loop over the - * base dsp array until we find an entry. - */ -- for (l = 0; l < MAXVOLUMESPERDS; ++l) -- if (baseds->dsp[l]) { -- if (memcmp(baseds->dsp[l]->f1->DS1DSSN, -- newds->dsp[k]->f1->DS1DSSN, -- MAXVOLSER)) -- return errorlog_add_message( -- &baseds->log, NULL, EPROTO, -- "merge dataset: part %d has incompatible" -- " base volume serial\n", k); -- else -- break; -- } -+ for (l = 0; l < MAXVOLUMESPERDS; ++l) { -+ if (!baseds->dsp[l]) -+ continue; -+ if (memcmp(baseds->dsp[l]->f1->DS1DSSN, -+ newds->dsp[k]->f1->DS1DSSN, -+ MAXVOLSER)) -+ return errorlog_add_message( -+ &baseds->log, NULL, EPROTO, -+ "merge dataset: part %d has incompatible base volume serial\n", -+ k); -+ else -+ break; -+ } -+ - baseds->dsp[k] = newds->dsp[k]; - baseds->dspcount++; - -diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c -index 462fda7..d7a83ee 100644 ---- a/zdsfs/zdsfs.c -+++ b/zdsfs/zdsfs.c -@@ -52,6 +52,8 @@ struct zdsfs_info { - }; - - static struct zdsfs_info zdsfsinfo; -+static int zdsfs_create_meta_data_buffer(struct zdsfs_info *); -+static int zdsfs_verify_datasets(void); - - struct zdsfs_file_info { - struct dshandle *dsh; -@@ -194,6 +196,48 @@ static int zdsfs_getattr(const char *path, struct stat *stbuf) - return 0; - } - -+static void zdsfs_read_device(struct dasd *newdasd, const char *device) -+{ -+ struct errorlog *log; -+ int rc; -+ -+ rc = dasd_disk_reserve(device); -+ if (rc) { -+ fprintf(stderr, "error when reserving device %s: %s\n", -+ device, strerror(rc)); -+ lzds_dasd_get_errorlog(newdasd, &log); -+ lzds_errorlog_fprint(log, stderr); -+ exit(1); -+ } -+ rc = lzds_dasd_alloc_rawvtoc(newdasd); -+ if (rc) { -+ fprintf(stderr, "error when reading VTOC from device %s: %s\n", -+ device, strerror(rc)); -+ lzds_dasd_get_errorlog(newdasd, &log); -+ lzds_errorlog_fprint(log, stderr); -+ exit(1); -+ } -+ rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot, -+ newdasd); -+ if (rc) { -+ fprintf(stderr, -+ "error when extracting data sets from dasd %s: %s\n", -+ device, strerror(rc)); -+ lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log); -+ lzds_errorlog_fprint(log, stderr); -+ exit(1); -+ } -+ rc = dasd_disk_release(device); -+ if (rc) { -+ fprintf(stderr, "error when releasing device %s: %s\n", -+ device, strerror(rc)); -+ lzds_dasd_get_errorlog(newdasd, &log); -+ lzds_errorlog_fprint(log, stderr); -+ exit(1); -+ } -+} -+ -+ - static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs) - { - struct dasditerator *dasdit; -@@ -240,6 +284,33 @@ static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs) - return 0; - } - -+ -+static int zdsfs_update_vtoc(void) -+{ -+ struct dasditerator *dasdit; -+ struct dasd *dasd; -+ int rc; -+ -+ lzds_dslist_free(zdsfsinfo.zdsroot); -+ rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit); -+ if (rc) -+ return -ENOMEM; -+ -+ while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd)) -+ zdsfs_read_device(dasd, dasd->device); -+ -+ lzds_dasditerator_free(dasdit); -+ rc = zdsfs_verify_datasets(); -+ if (rc) -+ return rc; -+ -+ rc = zdsfs_create_meta_data_buffer(&zdsfsinfo); -+ if (rc) -+ return rc; -+ -+ return 0; -+} -+ - static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi)) - { -@@ -253,6 +324,10 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - int rc; - int ispds, issupported; - -+ rc = zdsfs_update_vtoc(); -+ if (rc) -+ return rc; -+ - /* we have two type of directories - * type one: the root directory contains all data sets - */ -@@ -329,6 +404,9 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi) - goto error1; - - if (strcmp(path, "/"METADATAFILE) == 0) { -+ rc = zdsfs_update_vtoc(); -+ if (rc) -+ return rc; - zfi->dsh = NULL; - zfi->is_metadata_file = 1; - zfi->metaread = 0; -@@ -596,6 +674,7 @@ static int zdsfs_verify_datasets(void) - if (rc) - return ENOMEM; - while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { -+ lzds_dataset_get_name(ds, &dsname); - lzds_dataset_get_is_complete(ds, &iscomplete); - if (!iscomplete) { - lzds_dataset_get_name(ds, &dsname); -@@ -822,23 +901,7 @@ static void zdsfs_process_device(const char *device) - lzds_errorlog_fprint(log, stderr); - exit(1); - } -- rc = lzds_dasd_read_rawvtoc(newdasd); -- if (rc) { -- fprintf(stderr, "error when reading VTOC from device %s:" -- " %s\n", device, strerror(rc)); -- lzds_dasd_get_errorlog(newdasd, &log); -- lzds_errorlog_fprint(log, stderr); -- exit(1); -- } -- rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot, -- newdasd); -- if (rc) { -- fprintf(stderr, "error when extracting data sets from dasd %s:" -- " %s\n", device, strerror(rc)); -- lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log); -- lzds_errorlog_fprint(log, stderr); -- exit(1); -- } -+ zdsfs_read_device(newdasd, device); - } - - static void zdsfs_process_device_file(const char *devfile) --- -2.21.3 - - -From db8ece16f1c2b4e514d39505397bc4d70052617b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 12:31:54 +0100 -Subject: [PATCH 30/57] zdev: add zfcp dix parameter handling (#1723852) - -Description: The zfcp kernel module was changed to introduce separate - parameters for selecting DIF and DIF&DIX. This patch - implements the corresponding changes in chzdev and - lszdev. -Upstream-ID: 2c4c210ca8c641ad6e051eea65493202a16a5f4a ---- - zdev/src/zfcp.c | 20 +++++++++++++++++--- - 1 file changed, 17 insertions(+), 3 deletions(-) - -diff --git a/zdev/src/zfcp.c b/zdev/src/zfcp.c -index 03aa252..82dac57 100644 ---- a/zdev/src/zfcp.c -+++ b/zdev/src/zfcp.c -@@ -75,16 +75,28 @@ static struct attrib zfcp_tattr_allow_lun_scan = { - - static struct attrib zfcp_tattr_dif = { - .name = "dif", -- .title = "Enable DIF/DIX data consistency checking", -+ .title = "Enable DIF data consistency checking", - .desc = -- "Control the use of the end-to-end data consistency checking\n" -- "mechanism (DIF/DIX):\n" -+ "Control the use of the DIF data consistency checking\n" -+ "mechanism:\n" - " 0: DIF is disabled\n" - " 1: DIF is enabled when supported by the FCP device hardware\n", - .defval = "0", - .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), - }; - -+static struct attrib zfcp_tattr_dix = { -+ .name = "dix", -+ .title = "Enable DIF&DIX data consistency checking", -+ .desc = -+ "Control the use of the end-to-end data consistency checking\n" -+ "mechanism (DIF&DIX):\n" -+ " 0: DIF&DIX is disabled\n" -+ " 1: DIF&DIX is enabled when supported by the FCP device hardware\n", -+ .defval = "0", -+ .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), -+}; -+ - static struct attrib zfcp_tattr_datarouter = { - .name = "datarouter", - .title = "Enable hardware data routing", -@@ -160,6 +172,7 @@ static void all_bools_to_num(struct setting_list *list) - util_list_iterate(&list->list, s) { - if (s->attrib == &zfcp_tattr_allow_lun_scan || - s->attrib == &zfcp_tattr_dif || -+ s->attrib == &zfcp_tattr_dix || - s->attrib == &zfcp_tattr_datarouter || - s->attrib == &zfcp_tattr_no_auto_port_rescan) { - /* Convert Y to 1 and N to 0. */ -@@ -295,6 +308,7 @@ struct devtype zfcp_devtype = { - &zfcp_tattr_queue_depth, - &zfcp_tattr_allow_lun_scan, - &zfcp_tattr_dif, -+ &zfcp_tattr_dix, - &zfcp_tattr_datarouter, - &zfcp_tattr_no_auto_port_rescan, - &zfcp_tattr_port_scan_ratelimit, --- -2.21.3 - - -From 41bb1d87abfe5f4b856ba885109749d42959a812 Mon Sep 17 00:00:00 2001 -From: Philipp Rudo -Date: Wed, 21 Aug 2019 12:34:43 +0200 -Subject: [PATCH 31/57] zipl: Fix error message printed with --dumptofs - (#1750307) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The option --dump does not exist. Adjust it to --dumpto. - -Signed-off-by: Philipp Rudo -Signed-off-by: Jan Höppner ---- - zipl/src/job.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index e68d1b7..5dcebaf 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -114,7 +114,7 @@ get_command_line(int argc, char* argv[], struct command_line* line) - break; - case 'D': - error_reason("dumptofs has been deprecated, use " -- "--dump instead"); -+ "--dumpto instead"); - rc = -1; - break; - case 'M': --- -2.21.3 - - -From 47f3a82078811bd4dfad2726ed3ba38cc0fe2e3f Mon Sep 17 00:00:00 2001 -From: Stefan Haberland -Date: Wed, 4 Sep 2019 13:01:53 +0200 -Subject: [PATCH 32/57] zipl: set correct secure IPL default value (#1750326) - -Set secure IPL to auto as default value to match documented behavior. - -(cherry picked from commit 367598b18738d26e92302812deb5fdbdc2617ba6) ---- - zipl/src/job.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 5dcebaf..1178a7d 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -1880,6 +1880,7 @@ job_get(int argc, char* argv[], struct job_data** data) - job->add_files = cmdline.add_files; - job->data.mvdump.force = cmdline.force; - job->dry_run = cmdline.dry_run; -+ job->is_secure = SECURE_BOOT_AUTO; - /* Get job data from user input */ - if (cmdline.help) { - job->command_line = 1; --- -2.21.3 - - -From d64cb3431d7cd51a4b4c24ed3e0a2e50a024b50b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 12:49:06 +0100 -Subject: [PATCH 33/57] zkey: various enhancements (#1725881) - -Description: Enhancements to zkey in response to first (customer) experiences - -Upstream-ID: 9561a0b98385180441b2f5aa338fb583a94d4c40 -Upstream-ID: a648dbb0149513885eba4ced84ca9e1e65c6caca -Upstream-ID: d95dc6d698b5a666920201e39f3ccd0b1afb918c -Upstream-ID: b26dbfe8320c9ddb72b86efbe532b616243e0e3f -Upstream-ID: a69470d7e02af2d7d6a4c781eef57727c0672fc0 -Upstream-ID: 79ce12055369009be3d5d0a170e8d6edde0e96f2 -Upstream-ID: ed6e3b727059e0533b5ccf7bb697a7fe1a5f151b -Upstream-ID: 951bd1ae54a074cf59a4719909d6f6122f5c0448 -Upstream-ID: ec9c67189e1b841a922a627a9066d17222b51a01 -Upstream-ID: 90dc65659fa868ab446db07cac82235fa8a184a5 -Upstream-ID: b0c7965234c80af83b0419dc3781aff323cd04d4 -Upstream-ID: 5a0c93443c6a1d31d238444c798272979f0c5bf3 -Upstream-ID: 3ed8ab4e2a071180cfdce1a673d5101b7a09e171 -Upstream-ID: 11bfa1d3c8bdec60c5d0b04e93431e55c264d5ef -Upstream-ID: e693173eef75b9c67ad5516c2e1bc3d5ded5bbee -Upstream-ID: e4cd42900f1922b25d99af1134e2695799d14248 -Upstream-ID: f97d048643ef105e2c4dcdcf656d4dee5b2d7a15 ---- - zkey/keystore.c | 315 ++++++++++++++++++++++++++++++----------- - zkey/keystore.h | 24 ++-- - zkey/pkey.c | 10 +- - zkey/zkey-cryptsetup.1 | 36 +++++ - zkey/zkey-cryptsetup.c | 81 ++++++++++- - zkey/zkey.1 | 244 +++++++++++++++++++++++++++++-- - zkey/zkey.c | 282 ++++++++++++++++++++++++++++++++++-- - 7 files changed, 866 insertions(+), 126 deletions(-) - -diff --git a/zkey/keystore.c b/zkey/keystore.c -index a4ad634..33c8f93 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -259,13 +259,13 @@ static int _keystore_set_file_permission(struct keystore *keystore, - - if (chmod(filename, keystore->mode) != 0) { - rc = -errno; -- warnx("chmod faild on file '%s': %s", filename, strerror(-rc)); -+ warnx("chmod failed on file '%s': %s", filename, strerror(-rc)); - return rc; - } - - if (chown(filename, geteuid(), keystore->owner) != 0) { - rc = -errno; -- warnx("chown faild on file '%s': %s", filename, strerror(-rc)); -+ warnx("chown failed on file '%s': %s", filename, strerror(-rc)); - return rc; - } - -@@ -369,7 +369,7 @@ out: - return 0; - } - --typedef int (*check_association_t)(const char *value, bool remove, -+typedef int (*check_association_t)(const char *value, bool remove, bool set, - char **normalized, void *private); - - /** -@@ -407,7 +407,7 @@ static int _keystore_set_association(struct properties *key_props, - - for (i = 0; newvals[i] != NULL; i++) { - if (check_func != NULL) { -- rc = check_func(newvals[i], 0, &normalized, -+ rc = check_func(newvals[i], 0, 1, &normalized, - check_private); - if (rc != 0) - goto out; -@@ -488,7 +488,7 @@ static int _keystore_add_association(struct properties *key_props, - - for (i = 0; newvals[i] != NULL; i++) { - if (check_func != NULL) { -- rc = check_func(newvals[i], 0, &normalized, -+ rc = check_func(newvals[i], 0, 0, &normalized, - check_private); - if (rc != 0) - goto out; -@@ -567,7 +567,7 @@ static int _keystore_remove_association(struct properties *key_props, - - for (i = 0; delvals[i] != NULL; i++) { - if (check_func != NULL) { -- rc = check_func(delvals[i], 1, &normalized, -+ rc = check_func(delvals[i], 1, 0, &normalized, - check_private); - if (rc != 0) - goto out; -@@ -1085,20 +1085,27 @@ out: - return rc; - } - -+struct apqn_check { -+ bool noonlinecheck; -+ bool nomsg; -+}; -+ - /** - * Checks an APQN value for its syntax. This is a callback function for - * function _keystore_change_association(). - * - * @param[in] apqn the APQN value to check - * @param[in] remove if true the apqn is removed -+ * @param[in] set if true the apqn is set (not used here) - * @param[out] normalized normalized value on return or NULL if no change -- * @param[in] private private data (not used here) -+ * @param[in] private private data (struct apqn_check) - * - * @returns 0 if successful, a negative errno value otherwise - */ --static int _keystore_apqn_check(const char *apqn, bool remove, -- char **normalized, void *UNUSED(private)) -+static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), -+ char **normalized, void *private) - { -+ struct apqn_check *info = (struct apqn_check *)private; - int rc, card, domain; - regmatch_t pmatch[1]; - regex_t reg_buf; -@@ -1124,15 +1131,16 @@ static int _keystore_apqn_check(const char *apqn, bool remove, - - util_asprintf(normalized, "%02x.%04x", card, domain); - -- if (remove) { -+ if (remove || info->noonlinecheck) { - rc = 0; - goto out; - } - - rc = _keystore_is_apqn_online(card, domain); - if (rc != 1) { -- warnx("The APQN %02x.%04x is %s", card, domain, -- rc == -1 ? "not a CCA card" : "not online"); -+ if (info->nomsg == 0) -+ warnx("The APQN %02x.%04x is %s", card, domain, -+ rc == -1 ? "not a CCA card" : "not online"); - rc = -EIO; - goto out; - } else { -@@ -1149,6 +1157,7 @@ struct volume_check { - struct keystore *keystore; - const char *name; - const char *volume; -+ bool set; - }; - - /** -@@ -1173,6 +1182,11 @@ static int _keystore_volume_check_process(struct keystore *UNUSED(keystore), - { - struct volume_check *info = (struct volume_check *)private; - -+ if (info->set) { -+ if (strcmp(name, info->name) == 0) -+ return 0; -+ } -+ - warnx("Key '%s' is already associated with volume '%s'", name, - info->volume); - return -EINVAL; -@@ -1204,12 +1218,13 @@ static int _keystore_is_block_device(const char *volume) - * - * @param[in] volume the Volume value to check - * @param[in] remove if true the volume is removed -+ * @param[in] set if true the volume is set - * @param[out] normalized normalized value on return or NULL if no change - * @param[in] private private data: struct volume_check - * - * @returns 0 if successful, a negative errno value otherwise - */ --static int _keystore_volume_check(const char *volume, bool remove, -+static int _keystore_volume_check(const char *volume, bool remove, bool set, - char **normalized, void *private) - { - struct volume_check *info = (struct volume_check *)private; -@@ -1250,6 +1265,7 @@ static int _keystore_volume_check(const char *volume, bool remove, - goto out; - } - -+ info->set = set; - rc = _keystore_process_filtered(info->keystore, NULL, info->volume, - NULL, NULL, - _keystore_volume_check_process, info); -@@ -1359,7 +1375,7 @@ struct keystore *keystore_new(const char *directory, bool verbose) - util_assert(directory != NULL, "Internal error: directory is NULL"); - - if (stat(directory, &sb) != 0) { -- warnx("'%s' does not exist", directory); -+ warnx("Can not access '%s': %s", directory, strerror(errno)); - return NULL; - } - if (!(sb.st_mode & S_IFDIR)) { -@@ -1543,6 +1559,8 @@ static int _keystore_set_default_properties(struct properties *key_props) - * key (optional, can be NULL) - * @param[in] apqns a comma separated list of APQNs associated with this - * key (optional, can be NULL) -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. - * @param[in] sector_size the sector size to use with dm-crypt. It must be power - * of two and in range 512 - 4096 bytes. 0 means that - * the sector size is not specified and the system -@@ -1554,10 +1572,14 @@ static int _keystore_create_info_file(struct keystore *keystore, - const struct key_filenames *filenames, - const char *description, - const char *volumes, const char *apqns, -+ bool noapqncheck, - size_t sector_size, - const char *volume_type) - { -- struct volume_check vol_check = { .keystore = keystore, .name = name }; -+ struct volume_check vol_check = { .keystore = keystore, .name = name, -+ .set = 0 }; -+ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, -+ .nomsg = 0 }; - struct properties *key_props; - char temp[10]; - int rc; -@@ -1583,7 +1605,8 @@ static int _keystore_create_info_file(struct keystore *keystore, - - rc = _keystore_change_association(key_props, PROP_NAME_APQNS, - apqns != NULL ? apqns : "", -- "APQN", _keystore_apqn_check, NULL); -+ "APQN", _keystore_apqn_check, -+ &apqn_check); - if (rc != 0) - goto out; - -@@ -1642,15 +1665,18 @@ out: - } - - /** -- * Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT -- * if no APQNs are specified. -+ * Extracts an online card/domain pair from the specified APQns. If none of the -+ * specified APQNs are online, then -ENODEV is returned. -+ * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero. - */ - static int _keystore_get_card_domain(const char *apqns, unsigned int *card, - unsigned int *domain) - { -+ struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 }; - char **apqn_list; - char *normalized = NULL; - int rc = 0; -+ int i; - - *card = AUTOSELECT; - *domain = AUTOSELECT; -@@ -1662,17 +1688,23 @@ static int _keystore_get_card_domain(const char *apqns, unsigned int *card, - if (apqn_list[0] == NULL) - goto out; - -- rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL); -- if (normalized != NULL) -- free(normalized); -- if (rc != 0) -- goto out; -+ for (i = 0; apqn_list[i] != NULL; i++) { -+ rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized, -+ &apqn_check); -+ if (normalized != NULL) -+ free(normalized); -+ if (rc == -EINVAL) -+ goto out; -+ if (rc != 0) -+ continue; - -- if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) { -- rc = -EINVAL; -- goto out; -+ if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2) -+ goto found; - } - -+ warnx("None of the specified APQNs is online or of type CCA"); -+ rc = -ENODEV; -+found: - out: - str_list_free_string_array(apqn_list); - return rc; -@@ -1688,6 +1720,8 @@ out: - * key (optional, can be NULL) - * @param[in] apqns a comma separated list of APQNs associated with this - * key (optional, can be NULL) -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. - * @param[in] sector_size the sector size to use with dm-crypt. It must be power - * of two and in range 512 - 4096 bytes. 0 means that - * the sector size is not specified and the system -@@ -1704,9 +1738,10 @@ out: - */ - int keystore_generate_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, size_t sector_size, -- size_t keybits, bool xts, const char *clear_key_file, -- const char *volume_type, int pkey_fd) -+ const char *apqns, bool noapqncheck, -+ size_t sector_size, size_t keybits, bool xts, -+ const char *clear_key_file, const char *volume_type, -+ int pkey_fd) - { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; -@@ -1748,7 +1783,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - - rc = _keystore_create_info_file(keystore, name, &file_names, - description, volumes, apqns, -- sector_size, volume_type); -+ noapqncheck, sector_size, volume_type); - if (rc != 0) - goto out_free_props; - -@@ -1781,6 +1816,8 @@ out_free_key_filenames: - * key (optional, can be NULL) - * @param[in] apqns a comma separated list of APQNs associated with this - * key (optional, can be NULL) -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. - * @param[in] sector_size the sector size to use with dm-crypt. It must be power - * of two and in range 512 - 4096 bytes. 0 means that - * the sector size is not specified and the system -@@ -1792,7 +1829,7 @@ out_free_key_filenames: - */ - int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, size_t sector_size, -+ const char *apqns, bool noapqncheck, size_t sector_size, - const char *import_file, const char *volume_type) - { - struct key_filenames file_names = { NULL, NULL, NULL }; -@@ -1832,7 +1869,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, - - rc = _keystore_create_info_file(keystore, name, &file_names, - description, volumes, apqns, -- sector_size, volume_type); -+ noapqncheck, sector_size, volume_type); - if (rc != 0) - goto out_free_props; - -@@ -1870,6 +1907,8 @@ out_free_key_filenames: - * key, or an APQN prefixed with '+' or '-' to add or - * remove that APQN respectively. If NULL then the APQNs - * are not changed. -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. - * @param[in] sector_size the sector size to use with dm-crypt. It must be power - * of two and in range 512 - 4096 bytes. 0 means that - * the sector size is not specified and the system -@@ -1883,10 +1922,13 @@ out_free_key_filenames: - */ - int keystore_change_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, long int sector_size, -- const char *volume_type) -+ const char *apqns, bool noapqncheck, -+ long int sector_size, const char *volume_type) - { -- struct volume_check vol_check = { .keystore = keystore, .name = name }; -+ struct volume_check vol_check = { .keystore = keystore, .name = name, -+ .set = 0 }; -+ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, -+ .nomsg = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; - char temp[30]; -@@ -1931,7 +1973,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, - if (apqns != NULL) { - rc = _keystore_change_association(key_props, PROP_NAME_APQNS, - apqns, "APQN", -- _keystore_apqn_check, NULL); -+ _keystore_apqn_check, -+ &apqn_check); - if (rc != 0) - goto out; - } -@@ -1982,10 +2025,6 @@ int keystore_change_key(struct keystore *keystore, const char *name, - goto out; - } - -- rc = _keystore_set_file_permission(keystore, file_names.info_filename); -- if (rc != 0) -- goto out; -- - pr_verbose(keystore, "Successfully changed key '%s'", name); - - out: -@@ -2269,6 +2308,7 @@ static void _keystore_print_record(struct util_rec *rec, - struct validate_info { - struct util_rec *rec; - int pkey_fd; -+ bool noapqncheck; - unsigned long int num_valid; - unsigned long int num_invalid; - unsigned long int num_warnings; -@@ -2422,8 +2462,9 @@ static int _keystore_process_validate(struct keystore *keystore, - "master key\n", 0); - info->num_warnings++; - } -- if (_keystore_display_apqn_status(properties, name) != 0) -- info->num_warnings++; -+ if (info->noapqncheck == 0) -+ if (_keystore_display_apqn_status(properties, name) != 0) -+ info->num_warnings++; - if (_keystore_display_volume_status(properties, name) != 0) - info->num_warnings++; - -@@ -2439,11 +2480,16 @@ out: - * - * @param[in] keystore the key store - * @param[in] name_filter the name filter to select the key (can be NULL) -+ * @param[in] apqn_filter the APQN filter to select the key (can be NULL) -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. -+ * @param[in] pkey_fd the file descriptor of /dev/pkey - * - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_validate_key(struct keystore *keystore, const char *name_filter, -- const char *apqn_filter, int pkey_fd) -+ const char *apqn_filter, bool noapqncheck, -+ int pkey_fd) - { - struct validate_info info; - struct util_rec *rec; -@@ -2454,6 +2500,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter, - rec = _keystore_setup_record(1); - - info.pkey_fd = pkey_fd; -+ info.noapqncheck = noapqncheck; - info.rec = rec; - info.num_valid = 0; - info.num_invalid = 0; -@@ -2683,10 +2730,6 @@ static int _keystore_process_reencipher(struct keystore *keystore, - if (rc != 0) - goto out; - -- rc = _keystore_set_file_permission(keystore, out_file); -- if (rc != 0) -- goto out; -- - if (params.complete || params.inplace == 1) { - rc = _keystore_set_timestamp_property(properties, - PROP_NAME_REENC_TIME); -@@ -2712,11 +2755,6 @@ static int _keystore_process_reencipher(struct keystore *keystore, - goto out; - } - -- rc = _keystore_set_file_permission(keystore, -- file_names->info_filename); -- if (rc != 0) -- goto out; -- - util_asprintf(&temp, "The following LUKS2 volumes are " - "encrypted with key '%s'. You should also " - "re-encipher the volume key of those volumes " -@@ -2846,7 +2884,7 @@ int keystore_copy_key(struct keystore *keystore, const char *name, - const char *newname, const char *volumes) - { - struct volume_check vol_check = { .keystore = keystore, -- .name = newname }; -+ .name = newname, .set = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; - struct key_filenames new_names = { NULL, NULL, NULL }; - struct properties *key_prop = NULL; -@@ -3255,6 +3293,13 @@ static int _keystore_execute_cmd(const char *cmd, - - struct crypt_info { - bool execute; -+ bool batch_mode; -+ const char *keyfile; -+ size_t keyfile_offset; -+ size_t keyfile_size; -+ size_t tries; -+ bool open; -+ bool format; - char **volume_filter; - int (*process_func)(struct keystore *keystore, - const char *volume, -@@ -3293,16 +3338,46 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, - const char *volume_type, - struct crypt_info *info) - { -+ char *keyfile_opt = NULL, *offset_opt = NULL; -+ char *size_opt = NULL, *tries_opt = NULL; -+ char *common_passphrase_options; -+ size_t common_len; - char temp[100]; - int rc = 0; - char *cmd; - - sprintf(temp, "--sector-size %lu ", sector_size); - -+ if (info->keyfile) { -+ util_asprintf(&keyfile_opt, "--key-file '%s' ", info->keyfile); -+ if (info->keyfile_offset > 0) -+ util_asprintf(&offset_opt, "--keyfile-offset %lu ", -+ info->keyfile_offset); -+ if (info->keyfile_size > 0) -+ util_asprintf(&size_opt, "--keyfile-size %lu ", -+ info->keyfile_size); -+ } -+ if (info->tries > 0) -+ util_asprintf(&tries_opt, "--tries %lu ", info->tries); -+ util_asprintf(&common_passphrase_options, "%s%s%s%s", -+ keyfile_opt != NULL ? keyfile_opt : "", -+ offset_opt != NULL ? offset_opt : "", -+ size_opt != NULL ? size_opt : "", -+ tries_opt != NULL ? tries_opt : ""); -+ common_len = strlen(common_passphrase_options); -+ free(keyfile_opt); -+ free(offset_opt); -+ free(size_opt); -+ free(tries_opt); -+ - if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) { -+ if (info->format) -+ return 0; -+ - util_asprintf(&cmd, -- "cryptsetup plainOpen %s--key-file '%s' " -+ "cryptsetup plainOpen %s%s--key-file '%s' " - "--key-size %lu --cipher %s %s%s %s", -+ info->batch_mode ? "-q " : "", - keystore->verbose ? "-v " : "", key_file_name, - key_file_size * 8, cipher_spec, - sector_size > 0 ? temp : "", volume, dmname); -@@ -3314,39 +3389,69 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, - printf("%s\n", cmd); - } - } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { -- util_asprintf(&cmd, -- "cryptsetup luksFormat %s--type luks2 " -- "--master-key-file '%s' --key-size %lu " -- "--cipher %s %s%s", -- keystore->verbose ? "-v " : "", key_file_name, -- key_file_size * 8, cipher_spec, -- sector_size > 0 ? temp : "", volume); -- -- if (info->execute) { -- printf("Executing: %s\n", cmd); -- rc = _keystore_execute_cmd(cmd, "cryptsetup"); -+ if (info->open) { -+ util_asprintf(&cmd, -+ "cryptsetup luksOpen %s%s%s%s %s", -+ info->batch_mode ? "-q " : "", -+ keystore->verbose ? "-v " : "", -+ common_len > 0 ? -+ common_passphrase_options : "", -+ volume, dmname); -+ -+ if (info->execute) { -+ printf("Executing: %s\n", cmd); -+ rc = _keystore_execute_cmd(cmd, "cryptsetup"); -+ } else { -+ printf("%s\n", cmd); -+ } - } else { -- printf("%s\n", cmd); -- } -- -- free(cmd); -- if (rc != 0) -- return rc; -- -- util_asprintf(&cmd, -- "zkey-cryptsetup setvp %s%s", volume, -- keystore->verbose ? " -V " : ""); -+ /* -+ * Use PBKDF2 as key derivation function for LUKS2 -+ * volumes. LUKS2 uses Argon2i as default, but this -+ * might cause out-of-memory errors when multiple LUKS2 -+ * volumes are opened automatically via /etc/crypttab -+ */ -+ util_asprintf(&cmd, -+ "cryptsetup luksFormat %s%s--type luks2 " -+ "--master-key-file '%s' --key-size %lu " -+ "--cipher %s --pbkdf pbkdf2 %s%s%s", -+ info->batch_mode ? "-q " : "", -+ keystore->verbose ? "-v " : "", -+ key_file_name, key_file_size * 8, -+ cipher_spec, common_len > 0 ? -+ common_passphrase_options : "", -+ sector_size > 0 ? temp : "", volume); -+ -+ if (info->execute) { -+ printf("Executing: %s\n", cmd); -+ rc = _keystore_execute_cmd(cmd, "cryptsetup"); -+ } else { -+ printf("%s\n", cmd); -+ } - -- if (info->execute) { -- printf("Executing: %s\n", cmd); -- rc = _keystore_execute_cmd(cmd, "zkey-cryptsetup"); -- } else { -- printf("%s\n", cmd); -+ free(cmd); -+ if (rc != 0) -+ return rc; -+ -+ util_asprintf(&cmd, -+ "zkey-cryptsetup setvp %s %s%s", volume, -+ common_len > 0 ? -+ common_passphrase_options : "", -+ keystore->verbose ? "-V" : ""); -+ -+ if (info->execute) { -+ printf("Executing: %s\n", cmd); -+ rc = _keystore_execute_cmd(cmd, -+ "zkey-cryptsetup"); -+ } else { -+ printf("%s\n", cmd); -+ } - } - } else { - return -EINVAL; - } - -+ free(common_passphrase_options); - free(cmd); - return rc; - } -@@ -3376,7 +3481,7 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), - size_t key_file_size, - size_t sector_size, - const char *volume_type, -- struct crypt_info *UNUSED(info)) -+ struct crypt_info *info) - { - char temp[1000]; - -@@ -3395,11 +3500,22 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), - } - - sprintf(temp, ",sector-size=%lu", sector_size); -- printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n", -+ printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu%s\n", - dmname, volume, key_file_name, cipher_spec, - key_file_size * 8, sector_size > 0 ? temp : ""); - } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { -- printf("%s\t%s\n", dmname, volume); -+ printf("%s\t%s\t%s\tluks", dmname, volume, -+ info->keyfile != NULL ? info->keyfile : "none"); -+ if (info->keyfile != NULL) { -+ if (info->keyfile_offset > 0) -+ printf(",keyfile-offset=%lu", -+ info->keyfile_offset); -+ if (info->keyfile_size > 0) -+ printf(",keyfile-size=%lu", info->keyfile_size); -+ } -+ if (info->tries > 0) -+ printf(",tries=%lu", info->tries); -+ printf("\n"); - } else { - return -EINVAL; - } -@@ -3584,11 +3700,21 @@ out: - * @param[in] execute If TRUE the cryptsetup command is executed, - * otherwise it is printed to stdout - * @param[in] volume_type the type of volume to generate cryptsetup cmds for -- * * -+ * @param[in] keyfile If non-NULL, specifies the name of the file to -+ * read the passphrase from. -+ * @param[in] keyfile_offset the offset in bytes for reading from keyfile -+ * @param[in] keyfile_size the size in bytes for reading from keyfile -+ * @param[in] tries the number of tries for passphrase entry -+ * @param[in] batch_mode If TRUE, suppress cryptsetup confirmation questions -+ * @param[in] open If TRUE, generate luksOpen/plainOpen commands -+ * @param[in] format If TRUE, generate luksFormat commands - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, -- bool execute, const char *volume_type) -+ bool execute, const char *volume_type, -+ const char *keyfile, size_t keyfile_offset, -+ size_t keyfile_size, size_t tries, bool batch_mode, -+ bool open, bool format) - { - struct crypt_info info = { 0 }; - int rc; -@@ -3605,6 +3731,13 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, - } - - info.execute = execute; -+ info.open = open; -+ info.format = format; -+ info.batch_mode = batch_mode; -+ info.keyfile = keyfile; -+ info.keyfile_offset = keyfile_offset; -+ info.keyfile_size = keyfile_size; -+ info.tries = tries; - info.volume_filter = str_list_split(volume_filter); - info.process_func = _keystore_process_cryptsetup; - -@@ -3636,11 +3769,17 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, - * for the volume filter. If not specified, the filter - * checks the volume part only. - * @param[in] volume_type the type of volume to generate crypttab entries for -+ * @param[in] keyfile If non-NULL, specifies the name of the file to -+ * read the passphrase from. -+ * @param[in] keyfile_offset the offset in bytes for reading from keyfile -+ * @param[in] keyfile_size the size in bytes for reading from keyfile -+ * @param[in] tries the number of tries for passphrase entry - * - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_crypttab(struct keystore *keystore, const char *volume_filter, -- const char *volume_type) -+ const char *volume_type, const char *keyfile, -+ size_t keyfile_offset, size_t keyfile_size, size_t tries) - { - struct crypt_info info = { 0 }; - int rc; -@@ -3656,6 +3795,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - return -EINVAL; - } - -+ info.keyfile = keyfile; -+ info.keyfile_offset = keyfile_offset; -+ info.keyfile_size = keyfile_size; -+ info.tries = tries; - info.volume_filter = str_list_split(volume_filter); - info.process_func = _keystore_process_crypttab; - -diff --git a/zkey/keystore.h b/zkey/keystore.h -index 11a2103..16ecc62 100644 ---- a/zkey/keystore.h -+++ b/zkey/keystore.h -@@ -28,25 +28,27 @@ struct keystore *keystore_new(const char *directory, bool verbose); - - int keystore_generate_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, size_t sector_size, -- size_t keybits, bool xts, const char *clear_key_file, -- const char *volume_type, int pkey_fd); -+ const char *apqns, bool noapqncheck, -+ size_t sector_size, size_t keybits, bool xts, -+ const char *clear_key_file, const char *volume_type, -+ int pkey_fd); - - int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, size_t sector_size, -+ const char *apqns, bool noapqncheck, size_t sector_size, - const char *import_file, const char *volume_type); - - int keystore_change_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -- const char *apqns, long int sector_size, -- const char *volume_type); -+ const char *apqns, bool noapqncheck, -+ long int sector_size, const char *volume_type); - - int keystore_rename_key(struct keystore *keystore, const char *name, - const char *newname); - - int keystore_validate_key(struct keystore *keystore, const char *name_filter, -- const char *apqn_filter, int pkey_fd); -+ const char *apqn_filter, bool noapqncheck, -+ int pkey_fd); - - int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - const char *apqn_filter, -@@ -68,10 +70,14 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter, - const char *volume_type); - - int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, -- bool execute, const char *volume_type); -+ bool execute, const char *volume_type, -+ const char *keyfile, size_t keyfile_offset, -+ size_t keyfile_size, size_t tries, bool batch_mode, -+ bool open, bool format); - - int keystore_crypttab(struct keystore *keystore, const char *volume_filter, -- const char *volume_type); -+ const char *volume_type, const char *keyfile, -+ size_t keyfile_offset, size_t keyfile_size, size_t tries); - - void keystore_free(struct keystore *keystore); - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index 15e606a..a88c4e9 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -163,8 +163,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, - - buf = util_malloc(size); - count = fread(buf, 1, size, fp); -- if (count <= 0) { -- msg = feof(fp) ? "File is too small" : strerror(errno); -+ if (count != size) { -+ msg = ferror(fp) ? strerror(errno) : "File is too small"; - warnx("File '%s': %s", keyfile, msg); - free(buf); - buf = NULL; -@@ -211,7 +211,7 @@ int write_secure_key(const char *keyfile, const u8 *secure_key, - } - - count = fwrite(secure_key, 1, secure_key_size, fp); -- if (count <= 0) { -+ if (count != secure_key_size) { - warnx("File '%s': %s", keyfile, strerror(errno)); - fclose(fp); - return -EIO; -@@ -301,8 +301,8 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, - - buf = util_malloc(size); - count = fread(buf, 1, size, fp); -- if (count <= 0) { -- msg = feof(fp) ? "File is too small" : strerror(errno); -+ if (count != size) { -+ msg = ferror(fp) ? strerror(errno) : "File is too small"; - warnx("File '%s': %s", keyfile, msg); - free(buf); - buf = NULL; -diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 -index 988ef76..e93b5f0 100644 ---- a/zkey/zkey-cryptsetup.1 -+++ b/zkey/zkey-cryptsetup.1 -@@ -102,6 +102,7 @@ behave in the same way as with \fBcryptsetup\fP. - .IR bytes ] - .RB [ \-\-tries | \-T - .IR number ] -+.RB [ \-\-batch\-mode | \-q ] - .RB [ \-\-verbose | \-V ] - .RB [ \-\-debug | \-D ] - .PP -@@ -180,6 +181,20 @@ and - to control which part of the key file is used as passphrase. These options - behave in the same way as with \fBcryptsetup\fP. - .PP -+The -+.B reencipher -+command creates a new key slot with the re-enciphered secure AES volume key. -+The new key slot uses -+.B PBKDF2 -+as password based key derivation function. LUKS2 volumes typically default to -+.B Argon2i -+as password based key derivation function, -+but this might cause out-of-memory errors when multiple encrypted volumes are -+unlocked automatically at boot through /etc/crypttab. Because PAES -+uses secure AES keys as volume keys, the security of the key derivation -+function used to encrypt the volume key in the LUKS key slots is of less -+relevance. -+.PP - .B Note: - The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) - to be installed. For the supported environments and downloads, see: -@@ -247,6 +262,7 @@ behave in the same way as with \fBcryptsetup\fP. - .IR bytes ] - .RB [ \-\-tries | \-T - .IR number ] -+.RB [ \-\-batch\-mode | \-q ] - .RB [ \-\-verbose | \-V ] - .RB [ \-\-debug | \-D ] - .PP -@@ -288,6 +304,20 @@ and - .B \-\-keyfile\-size - to control which part of the key file is used as passphrase. These options - behave in the same way the same as with \fBcryptsetup\fP. -+.PP -+The -+.B setkey -+command creates a new key slot with the re-enciphered secure AES volume key. -+The new key slot uses -+.B PBKDF2 -+as password based key derivation function. LUKS2 volumes typically default to -+.B Argon2i -+as password based key derivation function, -+but this might cause out-of-memory errors when multiple encrypted volumes are -+unlocked automatically at boot through /etc/crypttab. Because PAES -+uses secure AES keys as volume keys, the security of the key derivation -+function used to encrypt the volume key in the LUKS key slots is of less -+relevance. - . - . - . -@@ -319,6 +349,9 @@ has been set (made active). When completing the staged re-enciphering, the - (unbound) key slot containing the re-enciphered secure volume key becomes - the active key slot and, optionally, all key slots containing the old secure - volume key are removed. -+.TP -+.BR \-q ", " \-\-batch\-mode -+Suppresses all confirmation questions. Use with care! - . - . - . -@@ -327,6 +360,9 @@ volume key are removed. - .BR \-m ", " \-\-master\-key\-file\~\fIfile\-name\fP - Specifies the name of a file containing the secure AES key that is set as the - new volume key. -+.TP -+.BR \-q ", " \-\-batch\-mode -+Suppresses all confirmation questions. Use with care! - . - . - . -diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c -index f7098f8..8180b4a 100644 ---- a/zkey/zkey-cryptsetup.c -+++ b/zkey/zkey-cryptsetup.c -@@ -35,6 +35,11 @@ - #include "misc.h" - #include "pkey.h" - -+/* Detect if cryptsetup 2.1 or later is available */ -+#ifdef CRYPT_LOG_DEBUG_JSON -+#define HAVE_CRYPT_KEYSLOT_GET_PBKDF -+#endif -+ - #define MAX_KEY_SIZE (8 * 1024 * 1024) - #define MAX_PASSWORD_SIZE 512 - #define KEYFILE_BUFLEN 4096 -@@ -93,6 +98,7 @@ static struct zkey_cryptsetup_globals { - bool inplace; - bool staged; - char *master_key_file; -+ bool batch_mode; - bool debug; - bool verbose; - void *lib_csulcca; -@@ -176,6 +182,11 @@ static struct util_opt opt_vec[] = { - .command = COMMAND_REENCIPHER, - }, - OPT_PASSPHRASE_ENTRY(COMMAND_REENCIPHER), -+ { -+ .option = {"batch-mode", 0, NULL, 'q'}, -+ .desc = "Suppresses all confirmation questions. Use with care!", -+ .command = COMMAND_REENCIPHER, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -204,6 +215,11 @@ static struct util_opt opt_vec[] = { - .command = COMMAND_SETKEY, - }, - OPT_PASSPHRASE_ENTRY(COMMAND_SETKEY), -+ { -+ .option = {"batch-mode", 0, NULL, 'q'}, -+ .desc = "Suppresses all confirmation questions. Use with care!", -+ .command = COMMAND_SETKEY, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -414,8 +430,13 @@ static void cryptsetup_log(int level, const char *msg, - fprintf(stderr, "%s: %s", program_invocation_short_name, msg); - break; - case CRYPT_LOG_DEBUG: -- fprintf(stderr, "%s: # %s", program_invocation_short_name, msg); -+ fprintf(stderr, "%s: # %s\n", program_invocation_short_name, msg); -+ break; -+#ifdef CRYPT_DEBUG_JSON -+ case CRYPT_DEBUG_JSON: -+ fprintf(stderr, "%s\n", msg); - break; -+#endif - default: - warnx("Internal error on logging class for msg: %s", msg); - break; -@@ -1096,7 +1117,11 @@ static int put_vp_token(struct crypt_device *cd, int token, - */ - static int open_device(const char *device, struct crypt_device **cd) - { -- const struct crypt_pbkdf_type *pbkdf; -+ const struct crypt_pbkdf_type pbkdf2 = { -+ .type = CRYPT_KDF_PBKDF2, -+ .hash = "sha256", -+ .time_ms = 2000, -+ }; - struct crypt_device *cdev = NULL; - int rc; - -@@ -1128,10 +1153,14 @@ static int open_device(const char *device, struct crypt_device **cd) - goto out; - } - -- pbkdf = crypt_get_pbkdf_type(cdev); -- rc = crypt_set_pbkdf_type(cdev, pbkdf); -+ /* -+ * Set PBKDF2 as default key derivation function. LUKS2 uses -+ * Argon2i as default, but this might cause out-of-memory errors when -+ * multiple LUKS2 volumes are opened automatically via /etc/crypttab -+ */ -+ rc = crypt_set_pbkdf_type(cdev, &pbkdf2); - if (rc != 0) { -- warnx("Failed to set the PBKDF-type for device '%s': %s", -+ warnx("Failed to set the PBKDF for device '%s': %s", - device, strerror(-rc)); - goto out; - } -@@ -1155,6 +1184,12 @@ static bool prompt_for_yes(void) - { - char str[20]; - -+ if (g.batch_mode) { -+ printf("(yes implied because '--batch-mode' | '-q' option is " -+ "specified)\n"); -+ return true; -+ } -+ - if (fgets(str, sizeof(str), stdin) == NULL) - return false; - -@@ -1311,6 +1346,9 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, - char **password, size_t *password_len, - const char *prompt) - { -+#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF -+ struct crypt_pbkdf_type pbkdf; -+#endif - char *vkey = NULL; - char *pw = NULL; - long long tries; -@@ -1362,6 +1400,30 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, - keyslot = rc; - pr_verbose("Volume key obtained from key slot %d", keyslot); - -+#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF -+ /* -+ * Get PBKDF of the key slot that was opened, and use its PBKDF for -+ * new key slots. -+ */ -+ memset(&pbkdf, 0, sizeof(pbkdf)); -+ rc = crypt_keyslot_get_pbkdf(g.cd, keyslot, &pbkdf); -+ if (rc != 0) { -+ warnx("Failed to get the PBKDF for key slot %d: %s", -+ keyslot, strerror(-rc)); -+ goto out; -+ } -+ -+ /* Reuse already benchmarked number of iterations */ -+ pbkdf.flags |= CRYPT_PBKDF_NO_BENCHMARK; -+ -+ rc = crypt_set_pbkdf_type(g.cd, &pbkdf); -+ if (rc != 0) { -+ warnx("Failed to set the PBKDF for new key slots: %s", -+ strerror(-rc)); -+ goto out; -+ } -+#endif -+ - if (key != NULL) - *key = vkey; - else -@@ -2191,6 +2253,9 @@ int main(int argc, char *argv[]) - case 'm': - g.master_key_file = optarg; - break; -+ case 'q': -+ g.batch_mode = true; -+ break; - case 'D': - g.debug = true; - g.verbose = true; -@@ -2238,7 +2303,11 @@ int main(int argc, char *argv[]) - - crypt_set_log_callback(NULL, cryptsetup_log, NULL); - if (g.debug) -- crypt_set_debug_level(-1); -+#ifdef CRYPT_DEBUG_JSON -+ crypt_set_debug_level(CRYPT_DEBUG_JSON); -+#else -+ crypt_set_debug_level(CRYPT_DEBUG_ALL); -+#endif - - if (command->open_device) { - if (g.pos_arg == NULL) { -diff --git a/zkey/zkey.1 b/zkey/zkey.1 -index 0837b27..c7cea83 100644 ---- a/zkey/zkey.1 -+++ b/zkey/zkey.1 -@@ -92,6 +92,7 @@ key repository. - .IR volume1:dmname1[,volume2:dmname2[,...]] ] - .RB [ \-\-apqns | \-a - .IR card1.domain1[,card2.domain2[,...]] ] -+.RB [ \-\-no\-apqn\-check ] - .RB [ \-\-sector-size | \-S - .IR bytes ] - .RB [ \-\-volume-type | \-t -@@ -141,6 +142,9 @@ options. - .BR validate | val - .RB [ \-\-name | \-N - .IR key-name ] -+.RB [ \-\-apqns | \-a -+.IR card1.domain1[,card2.domain2[,...]] ] -+.RB [ \-\-no\-apqn\-check ] - .RB [ \-\-verbose | \-V ] - .PP - Use the -@@ -160,8 +164,21 @@ contained in the secure key repository, specify the name of the key - or a pattern containing wildcards using the - .B \-\-name - option. When wildcards are used you must quote the value. --If neither option \fIsecure\-key\-file\fP nor option -+You can also specify the -+.B \-\-apqns -+option to validate those secure keys which are associated with the specified -+cryptographic adapters (APQNs). You can use wildcards for the APQN -+specification. When wildcards are used you must quote the value. -+If both option -+.B \-\-name -+and option -+.B \-\-apqns -+are specified then all secure keys contained in the key repository that match -+both patterns are validated. -+If neither option \fIsecure\-key\-file\fP nor options - .B \-\-name -+or -+.B \-\-apqns - are specified, then all secure keys contained in the key repository - are validated. - . -@@ -259,7 +276,7 @@ and option - .B \-\-apqns - are specified then all secure keys - contained in the key repository that match both patterns are re-enciphered. --If all both options are omitted, then all secure keys contained in the key -+If both options are omitted, then all secure keys contained in the key - repository are re-enciphered. - .PP - Re-enciphering a secure key contained in the secure key repository can be -@@ -298,6 +315,7 @@ to be installed. For the supported environments and downloads, see: - .IR volume1:dmname1[,volume2:dmname2[,...]] ] - .RB [ \-\-apqns | \-a - .IR card1.domain1[,card2.domain2[,...]] ] -+.RB [ \-\-no\-apqn\-check ] - .RB [ \-\-sector-size | \-S - .IR bytes ] - .RB [ \-\-volume-type | \-t -@@ -409,6 +427,7 @@ secure key. - .IR [+|-]volume1:dmname1[,volume2:dmname2[,...]] ] - .RB [ \-\-apqns | \-a - .IR [+|-]card1.domain1[,card2.domain2[,...]] ] -+.RB [ \-\-no\-apqn\-check ] - .RB [ \-\-sector-size | \-S - .IR bytes ] - .RB [ \-\-volume-type | \-t -@@ -477,8 +496,8 @@ option. You cannot use wildcards. - .B copy | co - .RB \-\-name | \-N - .IR key-name --.B \-\-new-key-name | \-w --.IR new-name -+.B \-\-new\-name | \-w -+.IR new-key-name - .RB [ \-\-volumes | \-l - .IR volume1:dmname1[,volume2:dmname2[,...]] ] - .RB [ \-\-verbose | \-V ] -@@ -509,6 +528,14 @@ volumes afterwards. - .IR volume1[:dmname1][,volume2[:dmname2][,...]] ] - .RB [ \-\-volume-type | \-t - .IR type ] -+.RB [ \-\-key\-file -+.IR file-name ] -+.RB [ \-\-keyfile\-offset -+.IR bytes ] -+.RB [ \-\-keyfile\-size -+.IR bytes ] -+.RB [ \-\-tries -+.IR number ] - .RB [ \-\-verbose | \-V ] - . - .PP -@@ -527,6 +554,23 @@ name are selected. - Specify the - .B \-\-volume-type - option to generate crypttab entries for the specified volume type only. -+.P -+For LUKS2 volumes, a passphrase is required. You are prompted for the -+passphrase during system startup when crypttab is evaluated, unless option -+.B \-\-key\-file -+is specified. Option -+.B \-\-tries -+specifies how often a passphrase can be re-entered. When option -+.B \-\-key\-file -+is specified, the passphrase is read from the specified file. You can specify -+options -+.B \-\-keyfile\-offset -+and -+.B \-\-keyfile\-size -+to control which part of the key file is used as passphrase. These options are -+passed to the generated crypttab entries and are only available if -+.B zkey -+has been compiled with LUKS2 support enabled. - . - .SS "Generate cryptsetup commands for volumes associated with secure AES keys" - . -@@ -537,14 +581,24 @@ option to generate crypttab entries for the specified volume type only. - .RB [ \-\-volume-type | \-t - .IR type ] - .RB [ \-\-run | \-r ] -+.RB [ \-\-open ] -+.RB [ \-\-format ] -+.RB [ \-\-key\-file -+.IR file-name ] -+.RB [ \-\-keyfile\-offset -+.IR bytes ] -+.RB [ \-\-keyfile\-size -+.IR bytes ] -+.RB [ \-\-tries -+.IR number ] - .RB [ \-\-verbose | \-V ] - . - .PP - Use the - .B cryptsetup --command to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksFormat\fP --commands for volumes that are associated with secure keys contained in the --secure key repository. Specify the -+command to generate \fBcryptsetup plainOpen\fP, \fBcryptsetup luksOpen\fP, or -+\fBcryptsetup luksFormat\fP commands for volumes that are associated with -+secure keys contained in the secure key repository. Specify the - .B \-\-volumes - option to limit the list - of volumes where cryptsetup commands are generated for. You can use wildcards. -@@ -556,7 +610,44 @@ name are selected. Specify the - option to generate cryptsetup commands for the specified volume type only. - Specify the - .B \-\-run --option to run the generated cryptsetup commands. -+option to run the generated cryptsetup commands. Specify the -+.B \-\-open -+to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksOpen\fP commands. -+For the plain volume type, this is the default. Specify the -+.B \-\-format -+option to generate \fBcryptsetup luksFormat\fP commands. For the LUKS2 volume -+type, this is the default. If specified for the plain volume type, then no -+command is generated. -+.P -+For LUKS2 volumes, the generated \fBcryptsetup luksFormat\fP contains -+option \fB\-\-pbkdf pbkdf2\fP to set \fBPBKDF2\fP as password based key -+derivation function. LUKS2 volumes typically default to \fBArgon2i\fP as -+password based key derivation function, but this might cause out-of-memory -+errors when multiple encrypted volumes are unlocked automatically at boot -+through /etc/crypttab. Because PAES uses secure AES keys as volume keys, the -+security of the key derivation function used to encrypt the volume key in the -+LUKS key slots is of less relevance. -+.P -+For LUKS2 volumes, a passphrase is required. You are prompted for the -+passphrase when running the generated commands, unless option -+.B \-\-key\-file -+is specified. Option -+.B \-\-tries -+specifies how often a passphrase can be re-entered. When option -+.B \-\-key\-file -+is specified, the passphrase is read from the specified file. You can specify -+options -+.B \-\-keyfile\-offset -+and -+.B \-\-keyfile\-size -+to control which part of the key file is used as passphrase. These options are -+only available if -+.B zkey -+has been compiled with LUKS2 support enabled. To avoid cryptsetup confirmation -+questions, you can specify the -+.B \-\-batch\-mode -+option. These options are passed to the generated command(s) and behave in the -+same way as with \fBcryptsetup\fP. - . - . - . -@@ -603,8 +694,14 @@ Specifies a comma-separated list of cryptographic adapters in CCA - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Each APQN association specifies a card and domain number separated - by a period (like lszcrypt displays it). When at least one APQN is specified, --then the first one is used to generate the key. If no APQNs are specified, --then an APQN is selected automatically. All specified APQNs must be online. -+then the first online APQN is used to generate the key. If no APQNs are -+specified, then an APQN is selected automatically. All specified APQNs must be -+online, unless the \fB\-\-no\-apqn\-check\fP option is specified. -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-no\-apqn\-check -+Do not check if the specified APQNs are available. Use this option to -+associate APQNs with a secure AES key that are currently not available. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-S ", " \-\-sector-size\~\fIbytes\fP -@@ -631,6 +728,19 @@ Specifies the name of the secure key in the secure key repository. You can - use wildcards to select multiple secure keys in the secure key repository. - When wildcards are used you must quote the value. - This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP -+Specifies a comma-separated list of cryptographic adapters in CCA -+coprocessor mode (APQNs). You can use wildcards in the APQN specification. -+All secure keys contained in the secure key repository -+which are associated with the specified APQNs are validated. -+Each APQN specifies a card and domain number separated by a period (like -+lszcrypt displays it). -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-no\-apqn\-check -+Do not check if the associated APQNs are available. -+This option is only used for secure keys contained in the secure key repository. - . - . - . -@@ -714,7 +824,13 @@ This option is only used for secure keys contained in the secure key repository. - Specifies a comma-separated list of cryptographic adapters in CCA - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Each APQN association specifies a card and domain number separated --by a period (like lszcrypt displays it). All specified APQNs must be online. -+by a period (like lszcrypt displays it). All specified APQNs must be online, -+unless option \fB\-\-no\-apqn\-check\fP is specified. -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-no\-apqn\-check -+Do not check if the specified APQNs are available. Use this option to -+associate APQNs with a secure AES key that are currently not available. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-S ", " \-\-sector-size\~\fIbytes\fP -@@ -833,7 +949,13 @@ To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP. - To set (replace) the APQN association do not specify a prefix. - You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or - remove (or set) the associations with one command. --All APQNs being added or set (replaced) must be online. -+All APQNs being added or set (replaced) must be online, unless option -+\fB\-\-no\-apqn\-check\fP is specified. -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-no\-apqn\-check -+Do not check if the specified APQNs are available. Use this option to -+associate APQNs with a secure AES key that are currently not available. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-S ", " \-\-sector-size\~\fIbytes\fP -@@ -908,6 +1030,46 @@ This option is only available if - .B zkey - has been compiled with LUKS2 support enabled. - This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-key\-file\~\fIfile\-name\fP -+Reads the passphrase from the specified file. If this option is omitted, then -+you are prompted to enter the passphrase interactively during system startup. -+This option is passed to the generated crypttab entries for LUKS2 volumes, and -+is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-keyfile\-offset\~\fIbytes\fP -+Specifies the number of bytes to skip before starting to read in the file -+specified with option \fB\-\-key\-file\fP. If omitted, the file is read -+from the beginning. When option \fB\-\-key\-file\fP is not specified, this -+option is ignored. This option is passed to the generated crypttab entries -+for LUKS2 volumes, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. Not all distributions support the -+.B keyfile-offset -+option in crypttab entries. -+.TP -+.BR \-\-keyfile\-size\~\fIbytes\fP -+Specifies the number of bytes to be read from the beginning of the file -+specified with option \fB\-\-key\-file\fP. If omitted, the file is read -+until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts -+at the offset. When option \fB\-\-key\-file\fP is not specified, this option is -+ignored. This option is passed to the generated crypttab entries for LUKS2 -+volumes, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. Not all distributions support the -+.B keyfile-size -+option in crypttab entries. -+.TP -+.BR \-\-tries\~\fInumber\fP -+Specifies how often the interactive input of the passphrase can be re-entered -+during system startup. The default is 3 times. When option \fB\-\-key\-file\fP -+is specified, this option is ignored, and the passphrase is read only once from -+the file. This option is passed to the generated crypttab entries for LUKS2 -+volumes, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. - . - . - . -@@ -937,6 +1099,64 @@ This option is only used for secure keys contained in the secure key repository. - Runs the generated cryptsetup commands. When one of the cryptsetup command fail, - no further cryptsetup commands are run, and zkey ends with an error. - This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-\-open -+Generates \fBcryptsetup luksOpen\fP or \fBcryptsetup plainOpen\fP commands. -+For a plain volume type, this is the default. This option can not be specified -+together with the -+.BR \-\-format -+option, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-format -+Generates \fBcryptsetup luksFormat\fP commands. For a LUKS2 volume type, this -+is the default. If specified for a plain volume type, then no command is -+generated. This option can not be specified together with the -+.BR \-\-open -+option, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-key\-file\~\fIfile\-name\fP -+Reads the passphrase from the specified file. If this option is omitted, -+or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the -+passphrase interactively. This option is passed to the generated command(s) -+for LUKS2 volumes, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-keyfile\-offset\~\fIbytes\fP -+Specifies the number of bytes to skip before starting to read in the file -+specified with option \fB\-\-key\-file\fP. If omitted, the file is read -+from the beginning. When option \fB\-\-key\-file\fP is not specified, this -+option is ignored. This option is passed to the generated command(s) -+for LUKS2 volumes, and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-keyfile\-size\~\fIbytes\fP -+Specifies the number of bytes to be read from the beginning of the file -+specified with option \fB\-\-key\-file\fP. If omitted, the file is read -+until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts -+at the offset. When option \fB\-\-key\-file\fP is not specified, this option is -+ignored. This option is passed to the generated command(s) for LUKS2 volumes, -+and is only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-\-tries\~\fInumber\fP -+Specifies how often the interactive input of the passphrase can be re-entered. -+The default is 3 times. When option \fB\-\-key\-file\fP is specified, this -+option is ignored, and the passphrase is read only once from the file. -+This option is passed to the generated command(s) for LUKS2 volumes, and is -+only available if -+.B zkey -+has been compiled with LUKS2 support enabled. -+.TP -+.BR \-q ", " \-\-batch\-mode -+Suppress cryptsetup confirmation questions. This option is passed to the generated -+cryptsetup command(s). - . - . - . -diff --git a/zkey/zkey.c b/zkey/zkey.c -index 3a8909a..2ecbb90 100644 ---- a/zkey/zkey.c -+++ b/zkey/zkey.c -@@ -67,11 +67,19 @@ static struct zkey_globals { - char *description; - char *volumes; - char *apqns; -+ bool noapqncheck; - long int sector_size; - char *volume_type; - char *newname; - bool run; -+ bool batch_mode; -+ char *keyfile; -+ long long keyfile_offset; -+ long long keyfile_size; -+ long long tries; - bool force; -+ bool open; -+ bool format; - void *lib_csulcca; - t_CSNBKTC dll_CSNBKTC; - int pkey_fd; -@@ -102,6 +110,14 @@ static struct zkey_globals { - #define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY" - #define DEFAULT_KEYSTORE "/etc/zkey/repository" - -+#define OPT_CRYPTSETUP_KEYFILE 256 -+#define OPT_CRYPTSETUP_KEYFILE_OFFSET 257 -+#define OPT_CRYPTSETUP_KEYFILE_SIZE 258 -+#define OPT_CRYPTSETUP_TRIES 259 -+#define OPT_CRYPTSETUP_OPEN 260 -+#define OPT_CRYPTSETUP_FORMAT 261 -+#define OPT_NO_APQN_CHECK 262 -+ - /* - * Configuration of command line options - */ -@@ -172,6 +188,14 @@ static struct util_opt opt_vec[] = { - "repository", - .command = COMMAND_GENERATE, - }, -+ { -+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, -+ .desc = "Do not check if the specified APQN(s) are available. " -+ "Use this option to associate APQN(s) with a secure " -+ "AES key that are currently not available.", -+ .command = COMMAND_GENERATE, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - { - .option = { "sector-size", required_argument, NULL, 'S'}, - .argument = "bytes", -@@ -281,6 +305,12 @@ static struct util_opt opt_vec[] = { - "associated with specific crypto cards", - .command = COMMAND_VALIDATE, - }, -+ { -+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, -+ .desc = "Do not check if the associated APQN(s) are available", -+ .command = COMMAND_VALIDATE, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -316,6 +346,14 @@ static struct util_opt opt_vec[] = { - "repository", - .command = COMMAND_IMPORT, - }, -+ { -+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, -+ .desc = "Do not check if the specified APQN(s) are available. " -+ "Use this option to associate APQN(s) with a secure " -+ "AES key that are currently not available.", -+ .command = COMMAND_IMPORT, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - { - .option = { "sector-size", required_argument, NULL, 'S'}, - .argument = "512|4096", -@@ -453,6 +491,14 @@ static struct util_opt opt_vec[] = { - "specify '-CARD.DOMAIN[,...]'", - .command = COMMAND_CHANGE, - }, -+ { -+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, -+ .desc = "Do not check if the specified APQN(s) are available. " -+ "Use this option to associate APQN(s) with a secure " -+ "AES key that are currently not available.", -+ .command = COMMAND_CHANGE, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - { - .option = { "sector-size", required_argument, NULL, 'S'}, - .argument = "0|512|4096", -@@ -547,6 +593,53 @@ static struct util_opt opt_vec[] = { - "entry is to be generated", - .command = COMMAND_CRYPTTAB, - }, -+ { -+ .option = {"key-file", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE}, -+ .argument = "FILE-NAME", -+ .desc = "Read the passphrase from the specified file. " -+ "The specified file is passed to the generated " -+ "crypttab entry for LUKS2 volumes", -+ .command = COMMAND_CRYPTTAB, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"keyfile-offset", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE_OFFSET}, -+ .argument = "BYTES", -+ .desc = "Specifies the number of bytes to skip in the file " -+ "specified with option '--key-file'. " -+ "The specified offset is passed to the generated " -+ "crypttab entry for LUKS2 volumes. Not all " -+ "distributions support the 'keyfile-offset' option in " -+ "crypttab entries", -+ .command = COMMAND_CRYPTTAB, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"keyfile-size", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE_SIZE}, -+ .argument = "BYTES", -+ .desc = "Specifies the number of bytes to read from the file " -+ "specified with option '--key-file'. " -+ "The specified size is passed to the generated " -+ "crypttab entry for LUKS2 volumes. Not all " -+ "distributions support the 'keyfile-size' option in " -+ "crypttab entries", -+ .command = COMMAND_CRYPTTAB, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"tries", required_argument, NULL, -+ OPT_CRYPTSETUP_TRIES}, -+ .argument = "NUMBER", -+ .desc = "Specifies how often the interactive input of the " -+ "passphrase can be retried. " -+ "The specified number is passed to the generated " -+ "crypttab entry for LUKS2 volumes", -+ .command = COMMAND_CRYPTTAB, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, - #endif - /***********************************************************/ - { -@@ -582,6 +675,75 @@ static struct util_opt opt_vec[] = { - .desc = "Runs the generated cryptsetup command", - .command = COMMAND_CRYPTSETUP, - }, -+#ifdef HAVE_LUKS2_SUPPORT -+ { -+ .option = {"key-file", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE}, -+ .argument = "FILE-NAME", -+ .desc = "Read the passphrase from the specified file. " -+ "This option is passed to the generated command(s) for " -+ "LUKS2 volumes", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"keyfile-offset", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE_OFFSET}, -+ .argument = "BYTES", -+ .desc = "Specifies the number of bytes to skip in the file " -+ "specified with option '--key-file'. " -+ "This option is passed to the generated command(s) for " -+ "LUKS2 volumes", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"keyfile-size", required_argument, NULL, -+ OPT_CRYPTSETUP_KEYFILE_SIZE}, -+ .argument = "BYTES", -+ .desc = "Specifies the number of bytes to read from the file " -+ "specified with option '--key-file'. " -+ "This option is passed to the generated command(s) for " -+ "LUKS2 volumes", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"tries", required_argument, NULL, -+ OPT_CRYPTSETUP_TRIES}, -+ .argument = "NUMBER", -+ .desc = "Specifies how often the interactive input of the " -+ "passphrase can be retried. " -+ "This option is passed to the generated command(s) for " -+ "LUKS2 volumes", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+#endif -+ { -+ .option = {"batch-mode", 0, NULL, 'q'}, -+ .desc = "Suppresses cryptsetup confirmation questions. " -+ "This option is passed to the generated cryptsetup " -+ "command(s)", -+ .command = COMMAND_CRYPTSETUP, -+ }, -+#ifdef HAVE_LUKS2_SUPPORT -+ { -+ .option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN}, -+ .desc = "Generates luksOpen or plainOpen commands. For the " -+ "plain volume type, this is the default", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"format", 0, NULL, OPT_CRYPTSETUP_FORMAT}, -+ .desc = "Generates luksFormat commands. For the LUKS2 volume " -+ "type, this is the default. If specified for the " -+ "plain volume type, then no command is generated", -+ .command = COMMAND_CRYPTSETUP, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+#endif - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -883,8 +1045,9 @@ static int command_generate_repository(void) - g.sector_size = 0; - - rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, -- g.apqns, g.sector_size, g.keybits, g.xts, -- g.clearkeyfile, g.volume_type, g.pkey_fd); -+ g.apqns, g.noapqncheck, g.sector_size, -+ g.keybits, g.xts, g.clearkeyfile, -+ g.volume_type, g.pkey_fd); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -903,6 +1066,12 @@ static int command_generate(void) - util_prg_print_parse_error(); - return EXIT_FAILURE; - } -+ if (g.apqns == NULL && g.noapqncheck) { -+ warnx("Option '--noapqncheck' is only valid together with " -+ "the '--apqns|-a' option"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } - if (g.name != NULL) - return command_generate_repository(); - if (g.pos_arg != NULL) { -@@ -918,6 +1087,12 @@ static int command_generate(void) - util_prg_print_parse_error(); - return EXIT_FAILURE; - } -+ if (g.noapqncheck) { -+ warnx("Option '--noapqncheck' is not valid for " -+ "generating a key outside of the repository"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } - if (g.description != NULL) { - warnx("Option '--description|-d' is not valid for " - "generating a key outside of the repository"); -@@ -1141,6 +1316,12 @@ static int command_validate_file(void) - util_prg_print_parse_error(); - return EXIT_FAILURE; - } -+ if (g.noapqncheck) { -+ warnx("Option '--noapqncheck' is not valid for " -+ "validating a key outside of the repository"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } - - /* Read the secure key to be re-enciphered */ - secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); -@@ -1194,7 +1375,15 @@ static int command_validate_repository(void) - { - int rc; - -- rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.pkey_fd); -+ if (g.apqns == NULL && g.noapqncheck) { -+ warnx("Option '--noapqncheck' is only valid together with " -+ "the '--apqns|-a' option"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ -+ rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck, -+ g.pkey_fd); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1231,9 +1420,16 @@ static int command_import(void) - if (g.sector_size < 0) - g.sector_size = 0; - -+ if (g.apqns == NULL && g.noapqncheck) { -+ warnx("Option '--noapqncheck' is only valid together with " -+ "the '--apqns|-a' option"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ - rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, -- g.apqns, g.sector_size, g.pos_arg, -- g.volume_type); -+ g.apqns, g.noapqncheck, g.sector_size, -+ g.pos_arg, g.volume_type); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1304,9 +1500,16 @@ static int command_change(void) - misc_print_required_parm("--name/-N"); - return EXIT_FAILURE; - } -+ if (g.apqns == NULL && g.noapqncheck) { -+ warnx("Option '--noapqncheck' is only valid together with " -+ "the '--apqns|-a' option"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } - - rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, -- g.apqns, g.sector_size, g.volume_type); -+ g.apqns, g.noapqncheck, g.sector_size, -+ g.volume_type); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1366,7 +1569,8 @@ static int command_crypttab(void) - { - int rc; - -- rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type); -+ rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type, g.keyfile, -+ g.keyfile_offset, g.keyfile_size, g.tries); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1380,7 +1584,16 @@ static int command_cryptsetup(void) - { - int rc; - -- rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type); -+ if (g.open && g.format) { -+ warnx("Either '--open' or '--format' can be specified, but " -+ "not both"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ -+ rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type, -+ g.keyfile, g.keyfile_offset, g.keyfile_size, -+ g.tries, g.batch_mode, g.open, g.format); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1546,6 +1759,9 @@ int main(int argc, char *argv[]) - case 'a': - g.apqns = optarg; - break; -+ case OPT_NO_APQN_CHECK: -+ g.noapqncheck = 1; -+ break; - case 'S': - g.sector_size = strtol(optarg, &endp, 0); - if (*optarg == '\0' || *endp != '\0' || -@@ -1574,6 +1790,56 @@ int main(int argc, char *argv[]) - case 'V': - g.verbose = 1; - break; -+#ifdef HAVE_LUKS2_SUPPORT -+ case OPT_CRYPTSETUP_KEYFILE: -+ g.keyfile = optarg; -+ break; -+ case OPT_CRYPTSETUP_KEYFILE_OFFSET: -+ g.keyfile_offset = strtoll(optarg, &endp, 0); -+ if (*optarg == '\0' || *endp != '\0' || -+ g.keyfile_offset < 0 || -+ (g.keyfile_offset == LLONG_MAX && -+ errno == ERANGE)) { -+ warnx("Invalid value for '--keyfile-offset': " -+ "'%s'", optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+ case OPT_CRYPTSETUP_KEYFILE_SIZE: -+ g.keyfile_size = strtoll(optarg, &endp, 0); -+ if (*optarg == '\0' || *endp != '\0' || -+ g.keyfile_size <= 0 || -+ (g.keyfile_size == LLONG_MAX && errno == ERANGE)) { -+ warnx("Invalid value for '--keyfile-size': " -+ "'%s'", optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+ case OPT_CRYPTSETUP_TRIES: -+ g.tries = strtoll(optarg, &endp, 0); -+ if (*optarg == '\0' || *endp != '\0' || -+ g.tries <= 0 || -+ (g.tries == LLONG_MAX && errno == ERANGE)) { -+ warnx("Invalid value for '--tries': '%s'", -+ optarg); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ break; -+#endif -+ case 'q': -+ g.batch_mode = 1; -+ break; -+#ifdef HAVE_LUKS2_SUPPORT -+ case OPT_CRYPTSETUP_OPEN: -+ g.open = 1; -+ break; -+ case OPT_CRYPTSETUP_FORMAT: -+ g.format = 1; -+ break; -+#endif - case 'h': - print_help(command); - return EXIT_SUCCESS; --- -2.21.3 - - -From 86c3031204f6e52455bba1dfc89d619ce26debe9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 13:16:36 +0100 -Subject: [PATCH 34/57] zkey: check master key consistency (#1753153) - -Description: Enhances the zkey tool to perform a cross check whether the - APQNs associated with a secure key have the same master key. - Display the master key verification pattern of a secure key - during the zkey validate command. This helps to better identify - which master key is the correct one, in case of master key - inconsistencies. - Select an appropriate APQN when re-enciphering a secure key. - Re-enciphering is done using the CCA host library. Special - handling is required to select an appropriate APQN for use with - the CCA host library. - -Upstream-ID: a6507b330d05a3033837768a54ce9959c2a84870 -Upstream-ID: 8d1a7fecd04e620138a3828d58900d35a396c33a -Upstream-ID: 7e6d782699adf681eaaf63fd43d65e6c0627ee6e -Upstream-ID: 819513ab385fa37306cff3f9ac32e3be7f225793 -Upstream-ID: 5dbe73403f455cb880aa256598b9527a465beb39 -Upstream-ID: 99ab2db6ad3f79c4e3a7287f4c724d1c635ebd2f -Upstream-ID: 7d4c8c27813c792237f92db5038b9dc4bea2fa7c -Upstream-ID: 94227b44d5e5538b221b7f33ae6e627b86d07593 -Upstream-ID: 139dff3525e7140a1d2e6aefdc1d35930d05c6d2 -Upstream-ID: 95c7258ea783c5bd6aa12fc0e3d5fbe65647af03 -Upstream-ID: 696e8458f0c117e3a084e1a083de89ec19baaff9 -Upstream-ID: a84d1c5d58fa4a0c9e087357eec009803ea06ef2 -Upstream-ID: bf8872e94a2dc4810df388d1539560b00b1acf6e -Upstream-ID: 625b81130ab2c9d184aaede2749f1fd776f51062 -Upstream-ID: bfc3dd018c4f0cc17f8463d8bd6be16aab8de4a4 -Upstream-ID: b32c0314746ddee69e59f892f105acd720d06452 -Upstream-ID: ea7cc9ea606dd879e4cdfae06a6f13d8fa3afff4 -Upstream-ID: c2244a57950f4eb35e3209151dcf48de66828df1 -Upstream-ID: a5b58038a0dbf1c3eb202a6933265f0d2e57e130 -Upstream-ID: 7f8e31e8619b32297b432a4882d78af79de37a58 -Upstream-ID: d854aed4b8154e7420def8749db2106a049dd80a -Upstream-ID: 0b4cbf00412f27456d28ff7f86ec5335a39e3416 -Upstream-ID: 016a0a56fcb3dd0bf8bed693e5d64873f6288995 -Upstream-ID: 1091b0bf65328aff94055a2e333aff2c737b6744 -Upstream-ID: 552a915465301b768268cddc7ccb65a6d167e432 -Upstream-ID: a0ed6709cf3c62b1fc9dfa28358e70215c1da55a ---- - zkey/Makefile | 12 +- - zkey/cca.c | 628 +++++++++++++++++++++++++++++++++++ - zkey/cca.h | 95 ++++++ - zkey/keystore.c | 306 +++++++++-------- - zkey/keystore.h | 3 +- - zkey/pkey.c | 165 ++-------- - zkey/pkey.h | 21 +- - zkey/properties.c | 28 +- - zkey/properties.h | 3 + - zkey/utils.c | 725 +++++++++++++++++++++++++++++++++++++++++ - zkey/utils.h | 54 +++ - zkey/zkey-cryptsetup.1 | 49 ++- - zkey/zkey-cryptsetup.c | 198 +++++++++-- - zkey/zkey.c | 122 +++++-- - 14 files changed, 2034 insertions(+), 375 deletions(-) - create mode 100644 zkey/cca.c - create mode 100644 zkey/cca.h - create mode 100644 zkey/utils.c - create mode 100644 zkey/utils.h - -diff --git a/zkey/Makefile b/zkey/Makefile -index a44b14b..f92de4f 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -64,18 +64,20 @@ zkey-cryptsetup-skip-jsonc: - - all: $(BUILD_TARGETS) - --zkey.o: zkey.c pkey.h misc.h -+zkey.o: zkey.c pkey.h cca.h misc.h - pkey.o: pkey.c pkey.h -+cca.o: cca.c cca.h pkey.h utils.h -+utils.o: utils.h - properties.o: check-dep-zkey properties.c properties.h --keystore.o: keystore.c keystore.h properties.h --zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h -+keystore.o: keystore.c keystore.h properties.h pkey.h cca.h utils.h -+zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h misc.h - - zkey: LDLIBS = -ldl -lcrypto --zkey: zkey.o pkey.o properties.o keystore.o $(libs) -+zkey: zkey.o pkey.o cca.o properties.o keystore.o utils.o $(libs) - $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c --zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) -+zkey-cryptsetup: zkey-cryptsetup.o pkey.o cca.o utils.o $(libs) - $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install-common: -diff --git a/zkey/cca.c b/zkey/cca.c -new file mode 100644 -index 0000000..2669c75 ---- /dev/null -+++ b/zkey/cca.c -@@ -0,0 +1,628 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_base.h" -+#include "lib/util_libc.h" -+#include "lib/util_panic.h" -+ -+#include "cca.h" -+#include "pkey.h" -+#include "utils.h" -+ -+#define pr_verbose(verbose, fmt...) do { \ -+ if (verbose) \ -+ warnx(fmt); \ -+ } while (0) -+ -+/* -+ * Definitions for the CCA library -+ */ -+#define CCA_LIBRARY_NAME "libcsulcca.so" -+#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" -+#define CCA_DOMAIN_ENVAR "CSU_DEFAULT_DOMAIN" -+#define CCA_ADAPTER_ENVAR "CSU_DEFAULT_ADAPTER" -+ -+/** -+ * Prints CCA return and reason code information for certain known CCA -+ * error situations. -+ * -+ * @param return_code the CCA return code -+ * @param reason_code the CCA reason code -+ */ -+static void print_CCA_error(int return_code, int reason_code) -+{ -+ switch (return_code) { -+ case 8: -+ switch (reason_code) { -+ case 48: -+ warnx("The secure key has a CCA master key " -+ "verification pattern that is not valid"); -+ break; -+ } -+ break; -+ case 12: -+ switch (reason_code) { -+ case 764: -+ warnx("The CCA master key is not loaded and " -+ "therefore a secure key cannot be enciphered"); -+ break; -+ } -+ break; -+ } -+} -+ -+/** -+ * Returns the version, release and modification number of the used CCA library. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int get_cca_version(struct cca_lib *cca, bool verbose) -+{ -+ unsigned char exit_data[4] = { 0, }; -+ unsigned char version_data[20]; -+ long return_code, reason_code; -+ long version_data_length; -+ long exit_data_len = 0; -+ char date[20]; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ memset(version_data, 0, sizeof(version_data)); -+ version_data_length = sizeof(version_data); -+ cca->dll_CSUACFV(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &version_data_length, version_data); -+ pr_verbose(verbose, "CSUACFV (Cryptographic Facility Version) " -+ "returned: return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ version_data[sizeof(version_data) - 1] = '\0'; -+ pr_verbose(verbose, "CCA Version string: %s", version_data); -+ -+ if (sscanf((char *)version_data, "%u.%u.%uz%s", &cca->version.ver, -+ &cca->version.rel, &cca->version.mod, date) != 4) { -+ warnx("CCA library version is invalid: %s", version_data); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * Loads the CCA library and provides the entry point of the CSNBKTC function. -+ * -+ * @param[out] cca on return this contains the address of the CCA -+ * library and certain CCA symbols. dlclose() should -+ * be used to free the library when no longer needed. -+ * @param verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, -ELIBACC in case of library load errors -+ */ -+int load_cca_library(struct cca_lib *cca, bool verbose) -+{ -+ util_assert(cca != NULL, "Internal error: caa is NULL"); -+ -+ /* Load the CCA library */ -+ cca->lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); -+ if (cca->lib_csulcca == NULL) { -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM CCA Host Libraries and " -+ "Tools.\nFor the supported environments and downloads, " -+ "see:\n%s", CCA_WEB_PAGE); -+ return -ELIBACC; -+ } -+ -+ /* Get the Cryptographic Facility Version function */ -+ cca->dll_CSUACFV = (t_CSUACFV)dlsym(cca->lib_csulcca, "CSUACFV"); -+ -+ /* Get the Key Token Change function */ -+ cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC"); -+ -+ /* Get the Cryptographic Facility Query function */ -+ cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ"); -+ -+ /* Get the Cryptographic Resource Allocate function */ -+ cca->dll_CSUACRA = (t_CSUACRA)dlsym(cca->lib_csulcca, "CSUACRA"); -+ -+ /* Cryptographic Resource Deallocate function */ -+ cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD"); -+ -+ if (cca->dll_CSUACFV == NULL || -+ cca->dll_CSNBKTC == NULL || -+ cca->dll_CSUACFQ == NULL || -+ cca->dll_CSUACRA == NULL || -+ cca->dll_CSUACRD == NULL) { -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM CCA Host Libraries and " -+ "Tools.\nFor the supported environments and downloads, " -+ "see:\n%s", CCA_WEB_PAGE); -+ dlclose(cca->lib_csulcca); -+ cca->lib_csulcca = NULL; -+ return -ELIBACC; -+ } -+ -+ pr_verbose(verbose, "CCA library '%s' has been loaded successfully", -+ CCA_LIBRARY_NAME); -+ -+ return get_cca_version(cca, verbose); -+} -+ -+/** -+ * Re-enciphers a secure key. -+ * -+ * @param[in] cca the CCA libraray structure -+ * @param[in] secure_key a buffer containing the secure key -+ * @param[in] secure_key_size the size of the secure key -+ * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT -+ * or METHOD_CURRENT_TO_NEW. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, -EIO in case of an error -+ */ -+int key_token_change(struct cca_lib *cca, -+ u8 *secure_key, unsigned int secure_key_size, -+ char *method, bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count; -+ unsigned char rule_array[2 * 8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ util_assert(secure_key_size > 0, -+ "Internal error: secure_key_size is 0"); -+ util_assert(method != NULL, "Internal error: method is NULL"); -+ -+ memcpy(rule_array, method, 8); -+ memcpy(rule_array + 8, "AES ", 8); -+ rule_array_count = 2; -+ -+ cca->dll_CSNBKTC(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ secure_key); -+ -+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " -+ "return_code: %ld, reason_code: %ld", method, return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ if (secure_key_size == 2 * SECURE_KEY_SIZE) { -+ cca->dll_CSNBKTC(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ secure_key + SECURE_KEY_SIZE); -+ -+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " -+ "returned: return_code: %ld, reason_code: %ld", -+ method, return_code, reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * Queries the number of adapters known by the CCA host library -+ * -+ * @param[in] cca the CCA library structure -+ * @param[out] adapters the number of adapters -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ */ -+static int get_number_of_cca_adapters(struct cca_lib *cca, -+ unsigned int *adapters, bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count, verb_data_length = 0; -+ unsigned char rule_array[16 * 8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ util_assert(adapters != NULL, "Internal error: adapters is NULL"); -+ -+ memset(rule_array, 0, sizeof(rule_array)); -+ memcpy(rule_array, "STATCRD2", 8); -+ rule_array_count = 1; -+ -+ cca->dll_CSUACFQ(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &verb_data_length, NULL); -+ -+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " -+ "return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ rule_array[8] = '\0'; -+ if (sscanf((char *)rule_array, "%u", adapters) != 1) { -+ pr_verbose(verbose, "Unparsable output: %s", rule_array); -+ return -EIO; -+ } -+ -+ pr_verbose(verbose, "Number of CCA adapters: %u", *adapters); -+ return 0; -+} -+ -+/** -+ * Allocate a specific CCA adapter. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] adapter the adapter number, starting at 1. If 0 is -+ * specified, then the AUTOSELECT option is -+ * enabled. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -ENODEV is -+ * returned if the adapter is not available. -+ */ -+static int allocate_cca_adapter(struct cca_lib *cca, unsigned int adapter, -+ bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count; -+ unsigned char rule_array[8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ char res_name[9]; -+ long res_name_len; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ if (adapter > 0) -+ memcpy(rule_array, "DEVICE ", 8); -+ else -+ memcpy(rule_array, "DEV-ANY ", 8); -+ rule_array_count = 1; -+ -+ sprintf(res_name, "CRP%02d", adapter); -+ res_name_len = strlen(res_name); -+ -+ cca->dll_CSUACRA(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &res_name_len, (unsigned char *)res_name); -+ -+ pr_verbose(verbose, "CSUACRA (Cryptographic Resource Allocate) " -+ "returned: return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -ENODEV; -+ } -+ -+ pr_verbose(verbose, "Adapter %u (%s) allocated", adapter, res_name); -+ return 0; -+} -+ -+/** -+ * Deallocate a specific CCA adapter. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] adapter the adapter number, starting at 1. If 0 is -+ * specified, then the AUTOSELECT option is -+ * disabled. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -ENODEV is -+ * returned if the adapter is not available. -+ */ -+static int deallocate_cca_adapter(struct cca_lib *cca, unsigned int adapter, -+ bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count; -+ unsigned char rule_array[8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ char res_name[9]; -+ long res_name_len; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ if (adapter > 0) -+ memcpy(rule_array, "DEVICE ", 8); -+ else -+ memcpy(rule_array, "DEV-ANY ", 8); -+ rule_array_count = 1; -+ -+ sprintf(res_name, "CRP%02d", adapter); -+ res_name_len = strlen(res_name); -+ -+ cca->dll_CSUACRD(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &res_name_len, (unsigned char *)res_name); -+ -+ pr_verbose(verbose, "CSUACRD (Cryptographic Resource Deallocate) " -+ "returned: return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -ENODEV; -+ } -+ -+ pr_verbose(verbose, "Adapter %u (%s) deallocated", adapter, res_name); -+ return 0; -+} -+ -+/** -+ * Queries the serial number of the current CCA adapter -+ * -+ * @param[in] cca the CCA library structure -+ * @param[out] serialnr the buffer where the serial number is returned -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ */ -+static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9], -+ bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count, verb_data_length = 0; -+ unsigned char rule_array[16 * 8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ memset(rule_array, 0, sizeof(rule_array)); -+ memcpy(rule_array, "STATCRD2", 8); -+ rule_array_count = 1; -+ -+ cca->dll_CSUACFQ(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &verb_data_length, NULL); -+ -+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " -+ "return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ memcpy(serialnr, rule_array+14*8, 8); -+ serialnr[8] = '\0'; -+ -+ pr_verbose(verbose, "Serial number of CCA adapter: %s", serialnr); -+ return 0; -+} -+ -+/** -+ * Selects the specified APQN to be used for the CCA host library. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] card the card number -+ * @param[in] domain the domain number -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is -+ * returned when the serialnr sysfs attribute is not available, -+ * because the zcrypt kernel module is on an older level. -ENODEV is -+ * returned if the APQN is not available. -+ */ -+int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose) -+{ -+ unsigned int adapters, adapter; -+ char adapter_serialnr[9]; -+ char apqn_serialnr[9]; -+ char temp[10]; -+ int rc, found = 0; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ pr_verbose(verbose, "Select %02x.%04x for the CCA host library", card, -+ domain); -+ -+ rc = sysfs_get_serialnr(card, apqn_serialnr, verbose); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to get the serial number: %s", -+ strerror(-rc)); -+ return rc; -+ } -+ -+ sprintf(temp, "%u", domain); -+ if (setenv(CCA_DOMAIN_ENVAR, temp, 1) != 0) { -+ rc = -errno; -+ pr_verbose(verbose, "Failed to set the %s environment variable:" -+ " %s", CCA_DOMAIN_ENVAR, strerror(-rc)); -+ return rc; -+ } -+ unsetenv(CCA_ADAPTER_ENVAR); -+ -+ /* -+ * Unload and reload the CCA host library so that it recognizes the -+ * changed CSU_DEFAULT_DOMAIN environment variable value. -+ */ -+ if (cca->lib_csulcca != NULL) -+ dlclose(cca->lib_csulcca); -+ memset(cca, 0, sizeof(struct cca_lib)); -+ -+ rc = load_cca_library(cca, verbose); -+ if (rc != 0) -+ return rc; -+ -+ rc = get_number_of_cca_adapters(cca, &adapters, verbose); -+ if (rc != 0) -+ return rc; -+ -+ /* Disable the AUTOSELECT option */ -+ rc = deallocate_cca_adapter(cca, 0, verbose); -+ if (rc != 0) -+ return rc; -+ -+ for (adapter = 1; adapter <= adapters; adapter++) { -+ rc = allocate_cca_adapter(cca, adapter, verbose); -+ if (rc != 0) -+ return rc; -+ -+ rc = get_cca_adapter_serialnr(cca, adapter_serialnr, verbose); -+ if (rc == 0) { -+ if (memcmp(apqn_serialnr, adapter_serialnr, 8) == 0) { -+ found = 1; -+ break; -+ } -+ } -+ -+ rc = deallocate_cca_adapter(cca, adapter, verbose); -+ if (rc != 0) -+ return rc; -+ } -+ -+ if (!found) -+ return -ENODEV; -+ -+ pr_verbose(verbose, "Selected adapter %u (CRP%02d)", adapter, adapter); -+ return 0; -+} -+ -+struct find_mkvp_info { -+ u64 mkvp; -+ unsigned int flags; -+ bool found; -+ int card; -+ int domain; -+ bool verbose; -+}; -+ -+static int find_mkvp(int card, int domain, void *handler_data) -+{ -+ struct find_mkvp_info *info = (struct find_mkvp_info *)handler_data; -+ struct mk_info mk_info; -+ bool found = false; -+ int rc; -+ -+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); -+ if (rc == -ENODEV) -+ return 0; -+ if (rc != 0) -+ return rc; -+ -+ if (info->flags & FLAG_SEL_CCA_MATCH_CUR_MKVP) -+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID && -+ mk_info.cur_mk.mkvp == info->mkvp) -+ found = true; -+ -+ if (info->flags & FLAG_SEL_CCA_MATCH_OLD_MKVP) -+ if (mk_info.old_mk.mk_state == MK_STATE_VALID && -+ mk_info.old_mk.mkvp == info->mkvp) -+ found = true; -+ -+ if (info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET) -+ if (mk_info.new_mk.mk_state != MK_STATE_FULL) -+ found = false; -+ -+ -+ if (found) { -+ info->card = card; -+ info->domain = domain; -+ info->found = true; -+ -+ pr_verbose(info->verbose, "%02x.%04x has the desired mkvp%s", -+ card, domain, -+ info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET ? -+ " and NEW MK set" : ""); -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/** -+ * Selects an APQN to be used for the CCA host library that has the specified -+ * master key verification pattern -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] mkvp the master key verification pattern to search for -+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -+ * or an empty string, then all online CCA APQNs are -+ * checked. -+ * @param[in] flags Flags that control the MKVM matching and NEW register -+ * checking. Multiple flags can be combined. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is -+ * returned when the serialnr sysfs attribute is not available, -+ * because the zcrypt kernel module is on an older level. -ENODEV is -+ * returned if no APQN is available with the desired mkvp. -+ */ -+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, -+ unsigned int flags, bool verbose) -+{ -+ struct find_mkvp_info info; -+ int rc; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ pr_verbose(verbose, "Select mkvp %016llx in APQNs %s for the CCA host " -+ "library", mkvp, apqns == 0 ? "ANY" : apqns); -+ -+ info.mkvp = mkvp; -+ info.flags = flags; -+ info.found = false; -+ info.card = 0; -+ info.domain = 0; -+ info.verbose = verbose; -+ -+ rc = handle_apqns(apqns, find_mkvp, &info, verbose); -+ if (rc < 0) -+ return rc; -+ -+ if (!info.found) -+ return -ENODEV; -+ -+ rc = select_cca_adapter(cca, info.card, info.domain, verbose); -+ return rc; -+} -+ -+void print_msg_for_cca_envvars(const char *key_name) -+{ -+ char *msg; -+ -+ util_asprintf(&msg, "WARNING: You must set environment variables " -+ "%s and %s to the desired card and domain that is " -+ "set up with the AES master key used by this %s. " -+ "%s specifies the domain as decimal number. %s " -+ "specifies the adapter number as 'CRPnn', where " -+ "'nn' is the adapter number. See the CCA " -+ "documentation for more details.\n", -+ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR, key_name, -+ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR); -+ util_print_indented(msg, 0); -+ free(msg); -+} -diff --git a/zkey/cca.h b/zkey/cca.h -new file mode 100644 -index 0000000..a79e2ee ---- /dev/null -+++ b/zkey/cca.h -@@ -0,0 +1,95 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * This header file defines the interface to the CCA host library. -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef CCA_H -+#define CCA_H -+ -+#include "lib/zt_common.h" -+ -+#define METHOD_OLD_TO_CURRENT "RTCMK " -+#define METHOD_CURRENT_TO_NEW "RTNMK " -+ -+typedef void (*t_CSNBKTC)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ unsigned char *key_identifier); -+ -+typedef void (*t_CSUACFV)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *version_data_length, -+ unsigned char *version_data); -+ -+typedef void (*t_CSUACFQ)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *verb_data_length, -+ unsigned char *verb_data); -+ -+typedef void (*t_CSUACRA)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *ressource_name_length, -+ unsigned char *ressource_name); -+ -+typedef void (*t_CSUACRD)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *ressource_name_length, -+ unsigned char *ressource_name); -+ -+struct cca_version { -+ unsigned int ver; -+ unsigned int rel; -+ unsigned int mod; -+}; -+ -+struct cca_lib { -+ void *lib_csulcca; -+ t_CSNBKTC dll_CSNBKTC; -+ t_CSUACFV dll_CSUACFV; -+ t_CSUACFQ dll_CSUACFQ; -+ t_CSUACRA dll_CSUACRA; -+ t_CSUACRD dll_CSUACRD; -+ struct cca_version version; -+}; -+ -+int load_cca_library(struct cca_lib *cca, bool verbose); -+ -+int key_token_change(struct cca_lib *cca, -+ u8 *secure_key, unsigned int secure_key_size, -+ char *method, bool verbose); -+ -+int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose); -+ -+#define FLAG_SEL_CCA_MATCH_CUR_MKVP 0x01 -+#define FLAG_SEL_CCA_MATCH_OLD_MKVP 0x02 -+#define FLAG_SEL_CCA_NEW_MUST_BE_SET 0x80 -+ -+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, -+ unsigned int flags, bool verbose); -+ -+void print_msg_for_cca_envvars(const char *key_name); -+ -+#endif -diff --git a/zkey/keystore.c b/zkey/keystore.c -index 33c8f93..957ebb0 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -3,7 +3,7 @@ - * - * Keystore handling functions - * -- * Copyright IBM Corp. 2018 -+ * Copyright IBM Corp. 2018, 2019 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. -@@ -25,7 +25,6 @@ - #include - - #include "lib/util_base.h" --#include "lib/util_file.h" - #include "lib/util_libc.h" - #include "lib/util_panic.h" - #include "lib/util_path.h" -@@ -33,7 +32,9 @@ - - #include "keystore.h" - #include "pkey.h" -+#include "cca.h" - #include "properties.h" -+#include "utils.h" - - struct key_filenames { - char *skey_filename; -@@ -133,14 +134,7 @@ static int _keystore_get_key_filenames(struct keystore *keystore, - */ - static int _keystore_reencipher_key_exists(struct key_filenames *file_names) - { -- struct stat sb; -- int rc; -- -- rc = stat(file_names->renc_filename, &sb); -- if (rc == 0 && !S_ISREG(sb.st_mode)) -- rc = 1; -- -- return !rc; -+ return util_path_is_reg_file("%s", file_names->renc_filename); - } - - /** -@@ -153,20 +147,14 @@ static int _keystore_reencipher_key_exists(struct key_filenames *file_names) - */ - static int _keystore_exists_keyfiles(struct key_filenames *file_names) - { -- struct stat sb_skey, sb_info; -- int rc_skey, rc_info; -- -- rc_skey = stat(file_names->skey_filename, &sb_skey); -- if (rc_skey == 0 && !S_ISREG(sb_skey.st_mode)) -- rc_skey = 1; -+ bool rc_skey, rc_info; - -- rc_info = stat(file_names->info_filename, &sb_info); -- if (rc_info == 0 && !S_ISREG(sb_info.st_mode)) -- rc_info = 1; -+ rc_skey = util_path_is_reg_file("%s", file_names->skey_filename); -+ rc_info = util_path_is_reg_file("%s", file_names->info_filename); - -- if (rc_skey == 0 && rc_info == 0) -+ if (rc_skey && rc_info) - return 1; -- if (rc_skey != 0 && rc_info != 0 && -+ if (!rc_skey && !rc_info && - _keystore_reencipher_key_exists(file_names) == 0) - return 0; - return -1; -@@ -1022,69 +1010,6 @@ free: - return rc; - } - --/** -- * Checks if the specified APQN is of type CCA and is online -- * -- * @param[in] card card number -- * @param[in] domain the domain -- * -- * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its -- * not a CCA card. -- */ --static int _keystore_is_apqn_online(int card, int domain) --{ -- long int online; -- char *dev_path; -- char type[20]; -- int rc = 1; -- -- dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -- if (!util_path_is_dir(dev_path)) { -- rc = 0; -- goto out; -- } -- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { -- rc = 0; -- goto out; -- } -- if (online == 0) { -- rc = 0; -- goto out; -- } -- if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { -- rc = 0; -- goto out; -- } -- if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { -- rc = 0; -- goto out; -- } -- if (type[4] != 'C') { -- rc = -1; -- goto out; -- } -- free(dev_path); -- -- dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card, -- card, domain); -- if (!util_path_is_dir(dev_path)) { -- rc = 0; -- goto out; -- } -- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { -- rc = 0; -- goto out; -- } -- if (online == 0) { -- rc = 0; -- goto out; -- } -- --out: -- free(dev_path); -- return rc; --} -- - struct apqn_check { - bool noonlinecheck; - bool nomsg; -@@ -1136,7 +1061,7 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), - goto out; - } - -- rc = _keystore_is_apqn_online(card, domain); -+ rc = sysfs_is_apqn_online(card, domain); - if (rc != 1) { - if (info->nomsg == 0) - warnx("The APQN %02x.%04x is %s", card, domain, -@@ -1378,7 +1303,7 @@ struct keystore *keystore_new(const char *directory, bool verbose) - warnx("Can not access '%s': %s", directory, strerror(errno)); - return NULL; - } -- if (!(sb.st_mode & S_IFDIR)) { -+ if (!S_ISDIR(sb.st_mode)) { - warnx("'%s' is not a directory", directory); - return NULL; - } -@@ -1630,7 +1555,8 @@ static int _keystore_create_info_file(struct keystore *keystore, - rc = -EINVAL; - goto out; - } -- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, volume_type); -+ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, volume_type, -+ true); - if (rc != 0) { - warnx("Invalid characters in volume-type"); - goto out; -@@ -1759,6 +1685,14 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - if (rc != 0) - goto out_free_key_filenames; - -+ rc = cross_check_apqns(apqns, 0, true, keystore->verbose); -+ if (rc == -EINVAL) -+ goto out_free_key_filenames; -+ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -+ warnx("Your master key setup is improper"); -+ goto out_free_key_filenames; -+ } -+ - rc = _keystore_get_card_domain(apqns, &card, &domain); - if (rc != 0) - goto out_free_key_filenames; -@@ -1836,6 +1770,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, - struct properties *key_props = NULL; - size_t secure_key_size; - u8 *secure_key; -+ u64 mkvp; - int rc; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); -@@ -1857,9 +1792,26 @@ int keystore_import_key(struct keystore *keystore, const char *name, - goto out_free_key_filenames; - } - -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, keystore->verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ goto out_free_key; -+ } -+ -+ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); -+ if (rc == -EINVAL) -+ goto out_free_key; -+ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -+ warnx("Your master key setup is improper"); -+ goto out_free_key; -+ } -+ - rc = write_secure_key(file_names.skey_filename, secure_key, - secure_key_size, keystore->verbose); - free(secure_key); -+ secure_key = NULL; - if (rc != 0) - goto out_free_props; - -@@ -1877,6 +1829,9 @@ int keystore_import_key(struct keystore *keystore, const char *name, - "Successfully imported a secure key in '%s' and key info in '%s'", - file_names.skey_filename, file_names.info_filename); - -+out_free_key: -+ if (secure_key != NULL) -+ free(secure_key); - out_free_props: - if (key_props != NULL) - properties_free(key_props); -@@ -1931,7 +1886,11 @@ int keystore_change_key(struct keystore *keystore, const char *name, - .nomsg = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; -+ size_t secure_key_size; -+ char *apqns_prop; -+ u8 *secure_key; - char temp[30]; -+ u64 mkvp; - int rc; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); -@@ -1977,6 +1936,33 @@ int keystore_change_key(struct keystore *keystore, const char *name, - &apqn_check); - if (rc != 0) - goto out; -+ -+ secure_key = read_secure_key(file_names.skey_filename, -+ &secure_key_size, -+ keystore->verbose); -+ if (secure_key == NULL) { -+ rc = -ENOENT; -+ goto out; -+ } -+ -+ rc = get_master_key_verification_pattern(secure_key, -+ secure_key_size, -+ &mkvp, -+ keystore->verbose); -+ free(secure_key); -+ if (rc) -+ goto out; -+ -+ apqns_prop = properties_get(key_props, PROP_NAME_APQNS); -+ rc = cross_check_apqns(apqns_prop, mkvp, true, -+ keystore->verbose); -+ free(apqns_prop); -+ if (rc == -ENOTSUP) -+ rc = 0; -+ if (rc != 0 && noapqncheck == 0) { -+ warnx("Your master key setup is improper"); -+ goto out; -+ } - } - - if (sector_size >= 0) { -@@ -2002,8 +1988,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, - goto out; - } - -- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, -- volume_type); -+ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, -+ volume_type, true); - if (rc != 0) { - warnx("Invalid characters in volume-type"); - goto out; -@@ -2181,7 +2167,7 @@ static void _keystore_print_record(struct util_rec *rec, - bool validation, const char *skey_filename, - size_t secure_key_size, - size_t clear_key_bitsize, bool valid, -- bool is_old_mk, bool reenc_pending) -+ bool is_old_mk, bool reenc_pending, u64 mkvp) - { - char temp_vp[VERIFICATION_PATTERN_LEN + 2]; - char *volumes_argz = NULL; -@@ -2243,10 +2229,11 @@ static void _keystore_print_record(struct util_rec *rec, - if (validation) { - if (valid) - util_rec_set(rec, REC_MASTERKEY, -- is_old_mk ? "OLD CCA master key" : -- "CURRENT CCA master key"); -+ "%s CCA master key (MKVP: %016llx)", -+ is_old_mk ? "OLD" : "CURRENT", mkvp); - else -- util_rec_set(rec, REC_MASTERKEY, "(unknown)"); -+ util_rec_set(rec, REC_MASTERKEY, -+ "(unknown, MKVP: %016llx)", mkvp); - } - if (volumes_argz != NULL) - util_rec_set_argz(rec, REC_VOLUMES, volumes_argz, -@@ -2317,43 +2304,32 @@ struct validate_info { - /** - * Displays the status of the associated APQNs. - * -+ * @param[in] keystore the key store - * @param[in] properties the properties of the key -- * @param[in] name the name of the key -+ * @param[in] mkvp the master key verification pattern of the key - * - * @returns 0 in case of success, 1 if at least one of the APQNs is not -- * available -+ * available or has a master key mismatch - */ --static int _keystore_display_apqn_status(struct properties *properties, -- const char *name) -+static int _keystore_display_apqn_status(struct keystore *keystore, -+ struct properties *properties, -+ u64 mkvp) - { -- int i, rc, card, domain, warning = 0; -- char **apqn_list; -+ int rc, warning = 0; - char *apqns; - - apqns = properties_get(properties, PROP_NAME_APQNS); - if (apqns == NULL) - return 0; -- apqn_list = str_list_split(apqns); - -- for (i = 0; apqn_list[i] != NULL; i++) { -- -- if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) -- continue; -- -- rc = _keystore_is_apqn_online(card, domain); -- if (rc != 1) { -- printf("WARNING: The APQN %02x.%04x associated with " -- "key '%s' is %s\n", card, domain, name, -- rc == -1 ? "not a CCA card" : "not online"); -- warning = 1; -- } -- } -+ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); -+ if (rc != 0 && rc != -ENOTSUP) -+ warning = 1; - - if (warning) - printf("\n"); - - free(apqns); -- str_list_free_string_array(apqn_list); - return warning; - } - /** -@@ -2424,6 +2400,7 @@ static int _keystore_process_validate(struct keystore *keystore, - u8 *secure_key; - int is_old_mk; - int rc, valid; -+ u64 mkvp; - - rc = _keystore_ensure_keyfiles_exist(file_names, name); - if (rc != 0) -@@ -2447,12 +2424,18 @@ static int _keystore_process_validate(struct keystore *keystore, - info->num_valid++; - valid = 1; - } -+ -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, keystore->verbose); - free(secure_key); -+ if (rc) -+ goto out; - - _keystore_print_record(info->rec, name, properties, 1, - file_names->skey_filename, secure_key_size, - clear_key_bitsize, valid, is_old_mk, -- _keystore_reencipher_key_exists(file_names)); -+ _keystore_reencipher_key_exists(file_names), -+ mkvp); - - if (valid && is_old_mk) { - util_print_indented("WARNING: The secure key is currently " -@@ -2463,7 +2446,8 @@ static int _keystore_process_validate(struct keystore *keystore, - info->num_warnings++; - } - if (info->noapqncheck == 0) -- if (_keystore_display_apqn_status(properties, name) != 0) -+ if (_keystore_display_apqn_status(keystore, properties, -+ mkvp) != 0) - info->num_warnings++; - if (_keystore_display_volume_status(properties, name) != 0) - info->num_warnings++; -@@ -2534,7 +2518,7 @@ struct reencipher_params { - struct reencipher_info { - struct reencipher_params params; - int pkey_fd; -- t_CSNBKTC dll_CSNBKTC; -+ struct cca_lib *cca; - unsigned long num_reenciphered; - unsigned long num_failed; - unsigned long num_skipped; -@@ -2545,23 +2529,33 @@ struct reencipher_info { - * - * @param[in] keystore the keystore - * @param[in] name the name of the key -- * @param[in] dll_CSNBKTC the CCA key token change function -+ * @param[in] cca the CCA library struct - * @param[in] params reenciphering parameters - * @param[in] secure_key a buffer containing the secure key - * @param[in] secure_key_size the size of the secure key - * @param[in] is_old_mk if true the key is currently re-enciphered with the - * OLD master key -+ * @param[in] apqns the associated APQNs (or NULL if none) - * @returns 0 if the re-enciphering is successful, a negative errno value - * otherwise, 1 if it was skipped - */ - static int _keystore_perform_reencipher(struct keystore *keystore, - const char *name, -- t_CSNBKTC dll_CSNBKTC, -+ struct cca_lib *cca, - struct reencipher_params *params, - u8 *secure_key, size_t secure_key_size, -- bool is_old_mk) -+ bool is_old_mk, const char *apqns) - { -- int rc; -+ int rc, selected = 1; -+ u64 mkvp; -+ -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, keystore->verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ return rc; -+ } - - if (!params->from_old && !params->to_new) { - /* Autodetect reencipher mode */ -@@ -2583,12 +2577,6 @@ static int _keystore_perform_reencipher(struct keystore *keystore, - } - - if (params->from_old) { -- if (!is_old_mk) { -- printf("The secure key '%s' is already enciphered " -- "with the CURRENT CCA master key\n", name); -- return 1; -- } -- - if (params->inplace == -1) - params->inplace = 1; - -@@ -2596,13 +2584,27 @@ static int _keystore_perform_reencipher(struct keystore *keystore, - "Secure key '%s' will be re-enciphered from OLD " - "to the CURRENT CCA master key", name); - -- rc = key_token_change(dll_CSNBKTC, -- secure_key, secure_key_size, -+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -+ FLAG_SEL_CCA_MATCH_OLD_MKVP, -+ keystore->verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering this secure AES key"); -+ return rc; -+ } -+ -+ rc = key_token_change(cca, secure_key, secure_key_size, - METHOD_OLD_TO_CURRENT, - keystore->verbose); - if (rc != 0) { - warnx("Failed to re-encipher '%s' from OLD to " - "CURRENT CCA master key", name); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); - return rc; - } - } -@@ -2614,13 +2616,30 @@ static int _keystore_perform_reencipher(struct keystore *keystore, - if (params->inplace == -1) - params->inplace = 0; - -- rc = key_token_change(dll_CSNBKTC, -- secure_key, secure_key_size, -+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP | -+ FLAG_SEL_CCA_NEW_MUST_BE_SET, -+ keystore->verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ util_print_indented("No APQN found that is suitable " -+ "for re-enciphering this secure " -+ "AES key and has the NEW master " -+ "key loaded", 0); -+ return rc; -+ } -+ -+ rc = key_token_change(cca, secure_key, secure_key_size, - METHOD_CURRENT_TO_NEW, - keystore->verbose); - if (rc != 0) { - warnx("Failed to re-encipher '%s' from CURRENT to " - "NEW CCA master key", name); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); - return rc; - } - } -@@ -2708,10 +2727,11 @@ static int _keystore_process_reencipher(struct keystore *keystore, - if (!params.complete) { - printf("Re-enciphering key '%s'\n", name); - -- rc = _keystore_perform_reencipher(keystore, name, -- info->dll_CSNBKTC, ¶ms, -- secure_key, secure_key_size, -- is_old_mk); -+ rc = _keystore_perform_reencipher(keystore, name, info->cca, -+ ¶ms, secure_key, -+ secure_key_size, is_old_mk, -+ properties_get(properties, -+ PROP_NAME_APQNS)); - if (rc < 0) - goto out; - if (rc > 0) { -@@ -2814,6 +2834,8 @@ out: - * @param[in] inplace if true, the key will be re-enciphere in-place - * @param[in] staged if true, the key will be re-enciphere not in-place - * @param[in] complete if true, a pending re-encipherment is completed -+ * @param[in] pkey_fd the file descriptor of /dev/pkey -+ * @param[in] cca the CCA library struct - * Note: if both from Old and toNew are FALSE, then the reencipherement mode is - * detected automatically. If both are TRUE then the key is reenciphered - * from the OLD to the NEW CCA master key. -@@ -2826,7 +2848,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - const char *apqn_filter, - bool from_old, bool to_new, bool inplace, - bool staged, bool complete, int pkey_fd, -- t_CSNBKTC dll_CSNBKTC) -+ struct cca_lib *cca) - { - struct reencipher_info info; - int rc; -@@ -2842,7 +2864,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - info.params.inplace = 0; - info.params.complete = complete; - info.pkey_fd = pkey_fd; -- info.dll_CSNBKTC = dll_CSNBKTC; -+ info.cca = cca; - info.num_failed = 0; - info.num_reenciphered = 0; - info.num_skipped = 0; -@@ -3063,7 +3085,7 @@ out: - * @returnd 0 if the user confirmed the deletion, a negative errno value - * otherwise - */ --static int _keystore_propmp_for_remove(struct keystore *keystore, -+static int _keystore_prompt_for_remove(struct keystore *keystore, - const char *name, - struct key_filenames *file_names) - { -@@ -3084,7 +3106,8 @@ static int _keystore_propmp_for_remove(struct keystore *keystore, - _keystore_msg_for_volumes(msg, key_prop, VOLUME_TYPE_PLAIN); - free(msg); - -- printf("%s: Remove key '%s'? ", program_invocation_short_name, name); -+ printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name, -+ name); - if (fgets(str, sizeof(str), stdin) == NULL) { - rc = -EIO; - goto out; -@@ -3093,6 +3116,7 @@ static int _keystore_propmp_for_remove(struct keystore *keystore, - str[strlen(str) - 1] = '\0'; - pr_verbose(keystore, "Prompt reply: '%s'", str); - if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) { -+ warnx("Operation aborted"); - rc = -ECANCELED; - goto out; - } -@@ -3129,7 +3153,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name, - goto out; - - if (!quiet) { -- if (_keystore_propmp_for_remove(keystore, name, -+ if (_keystore_prompt_for_remove(keystore, name, - &file_names) != 0) - goto out; - } -@@ -3204,7 +3228,7 @@ static int _keystore_display_key(struct keystore *keystore, - IS_XTS(secure_key_size) ? secure_key->bitsize * 2 - : secure_key->bitsize, - 0, 0, -- _keystore_reencipher_key_exists(file_names)); -+ _keystore_reencipher_key_exists(file_names), 0); - - out: - free(secure_key); -diff --git a/zkey/keystore.h b/zkey/keystore.h -index 16ecc62..8e888d1 100644 ---- a/zkey/keystore.h -+++ b/zkey/keystore.h -@@ -14,6 +14,7 @@ - - #include - -+#include "cca.h" - #include "pkey.h" - - struct keystore { -@@ -54,7 +55,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - const char *apqn_filter, - bool from_old, bool to_new, bool inplace, - bool staged, bool complete, int pkey_fd, -- t_CSNBKTC dll_CSNBKTC); -+ struct cca_lib *cca); - - int keystore_copy_key(struct keystore *keystore, const char *name, - const char *newname, const char *volumes); -diff --git a/zkey/pkey.c b/zkey/pkey.c -index a88c4e9..8471f3d 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -44,57 +44,7 @@ - - #define MAX_CIPHER_LEN 32 - --/* -- * Definitions for the CCA library -- */ --#define CCA_LIBRARY_NAME "libcsulcca.so" --#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards" -- --#define DEFAULT_KEYBITS 256 -- --/** -- * Loads the CCA library and provides the entry point of the CSNBKTC function. -- * -- * @param[out] lib_csulcca on return this contains the address of the CCA -- * library. dlclose() should be used to free this -- * when no longer needed. -- * @param[out] dll_CSNBKTC on return this contains the address of the -- * CSNBKTC function. -- * @param verbose if true, verbose messages are printed -- * -- * @returns 0 on success, -ELIBACC in case of library load errors -- */ --int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose) --{ -- util_assert(lib_csulcca != NULL, "Internal error: lib_csulcca is NULL"); -- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); -- -- /* Load the CCA library */ -- *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); -- if (*lib_csulcca == NULL) { -- pr_verbose(verbose, "%s", dlerror()); -- warnx("The command requires the IBM CCA Host Libraries and " -- "Tools.\nFor the supported environments and downloads, " -- "see:\n%s", CCA_WEB_PAGE); -- return -ELIBACC; -- } -- -- /* Get the Key Token Change function */ -- *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC"); -- if (*dll_CSNBKTC == NULL) { -- pr_verbose(verbose, "%s", dlerror()); -- warnx("The command requires the IBM CCA Host Libraries and " -- "Tools.\nFor the supported environments and downloads, " -- "see:\n%s", CCA_WEB_PAGE); -- dlclose(*lib_csulcca); -- *lib_csulcca = NULL; -- return -ELIBACC; -- } -- -- pr_verbose(verbose, "CCA library '%s' has been loaded successfully", -- CCA_LIBRARY_NAME); -- return 0; --} -+#define DEFAULT_KEYBITS 256 - - /** - * Opens the pkey device and returns its file descriptor. -@@ -267,7 +217,7 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, - return NULL; - } - } else { -- keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, xts); -+ keybits = HALF_KEYSIZE_FOR_XTS(size * 8, xts); - } - - switch (keybits) { -@@ -522,96 +472,6 @@ out: - return rc; - } - --/** -- * Prints CCA return and reason code information for certain known CCA -- * error situations. -- * -- * @param return_code the CCA return code -- * @param reason_code the CCA reason code -- */ --static void print_CCA_error(int return_code, int reason_code) --{ -- switch (return_code) { -- case 8: -- switch (reason_code) { -- case 48: -- warnx("The secure key has a CCA master key " -- "verification pattern that is not valid"); -- break; -- } -- break; -- case 12: -- switch (reason_code) { -- case 764: -- warnx("The CCA master key is not loaded and " -- "therefore a secure key cannot be enciphered"); -- break; -- } -- break; -- } --} -- --/** -- * Re-enciphers a secure key. -- * -- * @param[in] dll_CSNBKTC the address of the CCA CSNBKTC function -- * @param[in] secure_key a buffer containing the secure key -- * @param[in] secure_key_size the size of the secure key -- * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT -- * or METHOD_CURRENT_TO_NEW. -- * @param[in] verbose if true, verbose messages are printed -- * -- * @returns 0 on success, -EIO in case of an error -- */ --int key_token_change(t_CSNBKTC dll_CSNBKTC, -- u8 *secure_key, unsigned int secure_key_size, -- char *method, bool verbose) --{ -- long exit_data_len = 0, rule_array_count; -- unsigned char rule_array[2 * 80] = { 0, }; -- unsigned char exit_data[4] = { 0, }; -- long return_code, reason_code; -- -- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); -- util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -- util_assert(secure_key_size > 0, -- "Internal error: secure_key_size is 0"); -- util_assert(method != NULL, "Internal error: method is NULL"); -- -- memcpy(rule_array, method, 8); -- memcpy(rule_array + 8, "AES ", 8); -- rule_array_count = 2; -- -- dll_CSNBKTC(&return_code, &reason_code, -- &exit_data_len, exit_data, -- &rule_array_count, rule_array, -- secure_key); -- -- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " -- "return_code: %ld, reason_code: %ld", method, return_code, -- reason_code); -- if (return_code != 0) { -- print_CCA_error(return_code, reason_code); -- return -EIO; -- } -- -- if (secure_key_size == 2 * SECURE_KEY_SIZE) { -- dll_CSNBKTC(&return_code, &reason_code, -- &exit_data_len, exit_data, -- &rule_array_count, rule_array, -- secure_key + SECURE_KEY_SIZE); -- -- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " -- "returned: return_code: %ld, reason_code: %ld", -- method, return_code, reason_code); -- if (return_code != 0) { -- print_CCA_error(return_code, reason_code); -- return -EIO; -- } -- } -- return 0; --} -- - /** - * Validates an XTS secure key (the second part) - * -@@ -909,3 +769,24 @@ out: - - return rc; - } -+ -+int get_master_key_verification_pattern(const u8 *secure_key, -+ size_t secure_key_size, u64 *mkvp, -+ bool verbose) -+{ -+ struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; -+ -+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); -+ -+ if (secure_key_size < SECURE_KEY_SIZE) { -+ pr_verbose(verbose, "Size of secure key is too small: " -+ "%lu expected %lu", secure_key_size, -+ SECURE_KEY_SIZE); -+ return -EINVAL; -+ } -+ -+ *mkvp = token->mkvp; -+ -+ return 0; -+} -diff --git a/zkey/pkey.h b/zkey/pkey.h -index 3c524cd..c0aac2e 100644 ---- a/zkey/pkey.h -+++ b/zkey/pkey.h -@@ -82,23 +82,10 @@ struct pkey_verifykey { - - #define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey) - --#define METHOD_OLD_TO_CURRENT "RTCMK " --#define METHOD_CURRENT_TO_NEW "RTNMK " -- --typedef void (*t_CSNBKTC)(long *return_code, -- long *reason_code, -- long *exit_data_length, -- unsigned char *exit_data, -- long *rule_array_count, -- unsigned char *rule_array, -- unsigned char *key_identifier); -- - #define PAES_BLOCK_SIZE 16 - #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) - #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) - --int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose); -- - int open_pkey_device(bool verbose); - - int generate_secure_key_random(int pkey_fd, const char *keyfile, -@@ -122,11 +109,11 @@ int validate_secure_key(int pkey_fd, - size_t *clear_key_bitsize, int *is_old_mk, - bool verbose); - --int key_token_change(t_CSNBKTC dll_CSNBKTC, -- u8 *secure_key, unsigned int secure_key_size, -- char *method, bool verbose); -- - int generate_key_verification_pattern(const char *key, size_t key_size, - char *vp, size_t vp_len, bool verbose); - -+int get_master_key_verification_pattern(const u8 *secure_key, -+ size_t secure_key_size, u64 *mkvp, -+ bool verbose); -+ - #endif -diff --git a/zkey/properties.c b/zkey/properties.c -index c20e51b..147bcc5 100644 ---- a/zkey/properties.c -+++ b/zkey/properties.c -@@ -9,6 +9,7 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include - #include - #include - #include -@@ -183,14 +184,16 @@ static struct property *properties_find(struct properties *properties, - * @param[in] properties the properties object - * @param[in] name the name of the property - * @param[in] value the value of the property -+ * @param[in] uppercase if true the value is set all uppercase - * - * @returns 0 on success, - * -EINVAL if the name or value contains invalid characters - */ --int properties_set(struct properties *properties, -- const char *name, const char *value) -+int properties_set2(struct properties *properties, -+ const char *name, const char *value, bool uppercase) - { - struct property *property; -+ int i; - - util_assert(properties != NULL, "Internal error: properties is NULL"); - util_assert(name != NULL, "Internal error: name is NULL"); -@@ -211,9 +214,30 @@ int properties_set(struct properties *properties, - property->value = util_strdup(value); - util_list_add_tail(&properties->list, property); - } -+ if (uppercase) { -+ for (i = 0; property->value[i] != '\0'; i++) -+ property->value[i] = toupper(property->value[i]); -+ } -+ - return 0; - } - -+/** -+ * Adds or updates a property -+ * -+ * @param[in] properties the properties object -+ * @param[in] name the name of the property -+ * @param[in] value the value of the property -+ * -+ * @returns 0 on success, -+ * -EINVAL if the name or value contains invalid characters -+ */ -+int properties_set(struct properties *properties, -+ const char *name, const char *value) -+{ -+ return properties_set2(properties, name, value, false); -+} -+ - /** - * Gets a property - * -diff --git a/zkey/properties.h b/zkey/properties.h -index 234948e..f2c8a15 100644 ---- a/zkey/properties.h -+++ b/zkey/properties.h -@@ -23,6 +23,9 @@ void properties_free(struct properties *properties); - int properties_set(struct properties *properties, - const char *name, const char *value); - -+int properties_set2(struct properties *properties, -+ const char *name, const char *value, bool uppercase); -+ - char *properties_get(struct properties *properties, const char *name); - - int properties_remove(struct properties *properties, const char *name); -diff --git a/zkey/utils.c b/zkey/utils.c -new file mode 100644 -index 0000000..9dc4af7 ---- /dev/null -+++ b/zkey/utils.c -@@ -0,0 +1,725 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_path.h" -+#include "lib/util_file.h" -+#include "lib/util_scandir.h" -+#include "lib/util_libc.h" -+#include "lib/util_rec.h" -+#include "lib/util_base.h" -+ -+#include "utils.h" -+#include "properties.h" -+ -+#define pr_verbose(verbose, fmt...) do { \ -+ if (verbose) \ -+ warnx(fmt); \ -+ } while (0) -+ -+/** -+ * Checks if the specified card is of type CCA and is online -+ * -+ * @param[in] card card number -+ * -+ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its -+ * not a CCA card. -+ */ -+int sysfs_is_card_online(int card) -+{ -+ long int online; -+ char *dev_path; -+ char type[20]; -+ int rc = 1; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -+ if (!util_path_is_dir(dev_path)) { -+ rc = 0; -+ goto out; -+ } -+ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { -+ rc = 0; -+ goto out; -+ } -+ if (online == 0) { -+ rc = 0; -+ goto out; -+ } -+ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { -+ rc = 0; -+ goto out; -+ } -+ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { -+ rc = 0; -+ goto out; -+ } -+ if (type[4] != 'C') { -+ rc = -1; -+ goto out; -+ } -+ -+out: -+ free(dev_path); -+ return rc; -+} -+ -+/** -+ * Checks if the specified APQN is of type CCA and is online -+ * -+ * @param[in] card card number -+ * @param[in] domain the domain -+ * -+ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its -+ * not a CCA card. -+ */ -+int sysfs_is_apqn_online(int card, int domain) -+{ -+ long int online; -+ char *dev_path; -+ int rc = 1; -+ -+ rc = sysfs_is_card_online(card); -+ if (rc != 1) -+ return rc; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card, -+ card, domain); -+ if (!util_path_is_dir(dev_path)) { -+ rc = 0; -+ goto out; -+ } -+ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { -+ rc = 0; -+ goto out; -+ } -+ if (online == 0) { -+ rc = 0; -+ goto out; -+ } -+ -+out: -+ free(dev_path); -+ return rc; -+} -+ -+/** -+ * Gets the 8 character ASCII serial number string of an card from the sysfs. -+ * -+ * @param[in] card card number -+ * @param[out] serialnr Result buffer -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 if the serial number was returned. -ENODEV if the APQN is not -+ * available, or is not a CCA card. -ENOTSUP if the serialnr sysfs -+ * attribute is not available, because the zcrypt kernel module is -+ * on an older level. -+ */ -+int sysfs_get_serialnr(int card, char serialnr[9], bool verbose) -+{ -+ char *dev_path; -+ int rc = 0; -+ -+ if (serialnr == NULL) -+ return -EINVAL; -+ -+ if (sysfs_is_card_online(card) != 1) -+ return -ENODEV; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -+ if (!util_path_is_dir(dev_path)) { -+ rc = -ENODEV; -+ goto out; -+ } -+ if (util_file_read_line(serialnr, 9, "%s/serialnr", dev_path) != 0) { -+ rc = -ENOTSUP; -+ goto out; -+ } -+ -+ if (strlen(serialnr) == 0) { -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ pr_verbose(verbose, "Serial number of %02x: %s", card, serialnr); -+out: -+ if (rc != 0) -+ pr_verbose(verbose, "Failed to get serial number for " -+ "%02x: %s", card, strerror(-rc)); -+ -+ free(dev_path); -+ return rc; -+} -+ -+static int parse_mk_info(char *line, struct mk_info *mk_info) -+{ -+ struct mk_info_reg *mk_reg; -+ char *save; -+ char *tok; -+ -+ tok = strtok_r(line, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "AES") != 0) -+ return 0; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "NEW:") == 0) -+ mk_reg = &mk_info->new_mk; -+ else if (strcasecmp(tok, "CUR:") == 0) -+ mk_reg = &mk_info->cur_mk; -+ else if (strcasecmp(tok, "OLD:") == 0) -+ mk_reg = &mk_info->old_mk; -+ else -+ return -EIO; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "empty") == 0) -+ mk_reg->mk_state = MK_STATE_EMPTY; -+ else if (strcasecmp(tok, "partial") == 0) -+ mk_reg->mk_state = MK_STATE_PARTIAL; -+ else if (strcasecmp(tok, "full") == 0) -+ mk_reg->mk_state = MK_STATE_FULL; -+ else if (strcasecmp(tok, "valid") == 0) -+ mk_reg->mk_state = MK_STATE_VALID; -+ else if (strcasecmp(tok, "invalid") == 0) -+ mk_reg->mk_state = MK_STATE_INVALID; -+ else -+ mk_reg->mk_state = MK_STATE_UNKNOWN; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (sscanf(tok, "%llx", &mk_reg->mkvp) != 1) -+ return -EIO; -+ -+ return 0; -+} -+ -+/** -+ * Gets the master key states and verification patterns of an APQN from the -+ * sysfs. -+ * -+ * @param[in] card card number -+ * @param[in] domain the domain -+ * @param[out] mk_info structure is filled on return with master key infos -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 if the master key info was returned. -ENODEV if the APQN is not -+ * available, or is not a CCA card. -ENOTSUP if the mkvps sysfs -+ * attribute is not available, because the zcrypt kernel module is -+ * on an older level. -+ */ -+int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) -+{ -+ char *dev_path; -+ char *p, *end; -+ char buf[100]; -+ int rc = 0; -+ FILE *fp; -+ -+ if (mk_info == NULL) -+ return -EINVAL; -+ -+ memset(mk_info, 0, sizeof(struct mk_info)); -+ mk_info->new_mk.mk_state = MK_STATE_UNKNOWN; -+ mk_info->cur_mk.mk_state = MK_STATE_UNKNOWN; -+ mk_info->old_mk.mk_state = MK_STATE_UNKNOWN; -+ -+ if (sysfs_is_apqn_online(card, domain) != 1) -+ return -ENODEV; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/mkvps", -+ card, card, domain); -+ if (!util_path_is_reg_file(dev_path)) { -+ rc = -ENOTSUP; -+ goto out; -+ } -+ -+ fp = fopen(dev_path, "r"); -+ if (fp == NULL) { -+ rc = -ENOTSUP; -+ goto out; -+ } -+ -+ /* -+ * Expected contents: -+ * AES NEW: -+ * AES CUR: -+ * AES OLD: -+ * with -+ * : 'empty' or 'partial' or 'full' -+ * , : 'valid' or 'invalid' -+ * , , new_mk.mk_state == MK_STATE_UNKNOWN && -+ mk_info->cur_mk.mk_state == MK_STATE_UNKNOWN && -+ mk_info->old_mk.mk_state == MK_STATE_UNKNOWN) -+ rc = -EIO; -+out: -+ if (rc != 0) -+ pr_verbose(verbose, "Failed to get mkvps for %02x.%04x: %s", -+ card, domain, strerror(-rc)); -+ -+ free(dev_path); -+ return rc; -+} -+ -+static int scan_for_domains(int card, apqn_handler_t handler, -+ void *handler_data, bool verbose) -+{ -+ struct dirent **namelist; -+ char fname[290]; -+ int i, n, domain, rc = 0; -+ -+ sprintf(fname, "/sys/devices/ap/card%02x/", card); -+ n = util_scandir(&namelist, alphasort, fname, -+ "[0-9a-fA-F]+\\.[0-9a-fA-F]+"); -+ -+ if (n < 0) -+ return -EIO; -+ -+ for (i = 0; i < n; i++) { -+ if (sscanf(namelist[i]->d_name, "%x.%x", &card, &domain) != 2) -+ continue; -+ -+ pr_verbose(verbose, "Found %02x.%04x", card, domain); -+ -+ if (sysfs_is_apqn_online(card, domain) != 1) { -+ pr_verbose(verbose, "APQN %02x.%04x is offline or not " -+ "CCA", card, domain); -+ continue; -+ } -+ -+ rc = handler(card, domain, handler_data); -+ if (rc != 0) -+ break; -+ } -+ -+ util_scandir_free(namelist, n); -+ return rc; -+} -+ -+ -+static int scan_for_apqns(apqn_handler_t handler, void *handler_data, -+ bool verbose) -+{ -+ struct dirent **namelist; -+ int i, n, card, rc = 0; -+ -+ if (handler == NULL) -+ return -EINVAL; -+ -+ n = util_scandir(&namelist, alphasort, "/sys/devices/ap/", -+ "card[0-9a-fA-F]+"); -+ if (n < 0) -+ return -EIO; -+ -+ for (i = 0; i < n; i++) { -+ if (sscanf(namelist[i]->d_name, "card%x", &card) != 1) -+ continue; -+ -+ pr_verbose(verbose, "Found card %02x", card); -+ -+ if (sysfs_is_card_online(card) != 1) { -+ pr_verbose(verbose, "Card %02x is offline or not CCA", -+ card); -+ continue; -+ } -+ -+ rc = scan_for_domains(card, handler, handler_data, verbose); -+ if (rc != 0) -+ break; -+ } -+ -+ util_scandir_free(namelist, n); -+ return rc; -+} -+ -+/** -+ * Calls the handler for all APQNs specified in the apqns parameter, or of this -+ * is NULL, for all online CCA APQNs found in sysfs. In case sysfs is inspected, -+ * the cards and domains are processed in alphabetical order. -+ * -+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -+ * or an empty string, then all online CCA APQNs are -+ * handled. -+ * @param[in] handler a handler function that is called for each APQN -+ * @param[in] handler_data private data that is passed to the handler -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 for success or a negative errno in case of an error -+ */ -+int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, -+ bool verbose) -+{ -+ int card, domain; -+ char *copy, *tok; -+ char *save; -+ int rc = 0; -+ -+ if (apqns == NULL || (apqns != NULL && strlen(apqns) == 0)) { -+ rc = scan_for_apqns(handler, handler_data, verbose); -+ } else { -+ copy = util_strdup(apqns); -+ tok = strtok_r(copy, ",", &save); -+ while (tok != NULL) { -+ -+ if (sscanf(tok, "%x.%x", &card, &domain) != 2) { -+ warnx("the APQN '%s' is not valid", -+ tok); -+ rc = -EINVAL; -+ break; -+ } -+ -+ pr_verbose(verbose, "Specified: %02x.%04x", card, -+ domain); -+ rc = handler(card, domain, handler_data); -+ if (rc != 0) -+ break; -+ -+ tok = strtok_r(NULL, ",", &save); -+ } -+ free(copy); -+ } -+ -+ return rc; -+} -+ -+struct print_apqn_info { -+ struct util_rec *rec; -+ bool verbose; -+}; -+ -+static int print_apqn_mk_info(int card, int domain, void *handler_data) -+{ -+ struct print_apqn_info *info = (struct print_apqn_info *)handler_data; -+ struct mk_info mk_info; -+ int rc; -+ -+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); -+ if (rc == -ENOTSUP) -+ return rc; -+ -+ util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain); -+ -+ if (rc == 0) { -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL) -+ util_rec_set(info->rec, "NEW", "%016llx", -+ mk_info.new_mk.mkvp); -+ else if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) -+ util_rec_set(info->rec, "NEW", "partially loaded"); -+ else -+ util_rec_set(info->rec, "NEW", "-"); -+ -+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID) -+ util_rec_set(info->rec, "CUR", "%016llx", -+ mk_info.cur_mk.mkvp); -+ else -+ util_rec_set(info->rec, "CUR", "-"); -+ -+ if (mk_info.old_mk.mk_state == MK_STATE_VALID) -+ util_rec_set(info->rec, "OLD", "%016llx", -+ mk_info.old_mk.mkvp); -+ else -+ util_rec_set(info->rec, "OLD", "-"); -+ } else { -+ util_rec_set(info->rec, "NEW", "?"); -+ util_rec_set(info->rec, "CUR", "?"); -+ util_rec_set(info->rec, "OLD", "?"); -+ } -+ -+ util_rec_print(info->rec); -+ -+ return 0; -+} -+ -+/** -+ * Prints master key information for all specified APQNs -+ * -+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -+ * or an empty string, then all online CCA APQNs are -+ * printed. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 for success or a negative errno in case of an error. -ENOTSUP is -+ * returned when the mkvps sysfs attribute is not available, because -+ * the zcrypt kernel module is on an older level. -+ */ -+int print_mk_info(const char *apqns, bool verbose) -+{ -+ struct print_apqn_info info; -+ int rc; -+ -+ info.verbose = verbose; -+ info.rec = util_rec_new_wide("-"); -+ -+ util_rec_def(info.rec, "APQN", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN"); -+ util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK"); -+ util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK"); -+ util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK"); -+ util_rec_print_hdr(info.rec); -+ -+ rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose); -+ -+ util_rec_free(info.rec); -+ return rc; -+} -+ -+struct cross_check_info { -+ u64 mkvp; -+ u64 new_mkvp; -+ bool key_mkvp; -+ u32 num_cur_match; -+ u32 num_old_match; -+ u32 num_new_match; -+ bool mismatch; -+ bool print_mks; -+ int num_checked; -+ bool verbose; -+}; -+ -+static int cross_check_mk_info(int card, int domain, void *handler_data) -+{ -+ struct cross_check_info *info = (struct cross_check_info *)handler_data; -+ struct mk_info mk_info; -+ char temp[200]; -+ int rc; -+ -+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); -+ if (rc == -ENODEV) { -+ info->print_mks = 1; -+ printf("WARNING: APQN %02x.%04x: Not available or not of " -+ "type CCA\n", card, domain); -+ return 0; -+ } -+ if (rc != 0) -+ return rc; -+ -+ info->num_checked++; -+ -+ if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " -+ "register is only partially loaded.", card, domain); -+ util_print_indented(temp, 0); -+ } -+ -+ if (info->new_mkvp == 0 && -+ mk_info.new_mk.mk_state == MK_STATE_FULL) -+ info->new_mkvp = mk_info.new_mk.mkvp; -+ -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL && -+ mk_info.new_mk.mkvp != info->new_mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The NEW master key " -+ "register contains a different master key than " -+ "the NEW register of other APQNs.", card, -+ domain); -+ util_print_indented(temp, 0); -+ } -+ -+ if (mk_info.cur_mk.mk_state != MK_STATE_VALID) { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ printf("WARNING: APQN %02x.%04x: No master key is set.\n", card, -+ domain); -+ return 0; -+ } -+ -+ if (mk_info.old_mk.mk_state == MK_STATE_VALID && -+ mk_info.old_mk.mkvp == mk_info.cur_mk.mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The OLD master key " -+ "register contains the same master key as the CURRENT " -+ "master key register.", card, domain); -+ util_print_indented(temp, 0); -+ } -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL && -+ mk_info.new_mk.mkvp == mk_info.cur_mk.mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " -+ "register contains the same master key as the CURRENT " -+ "master key register.", card, domain); -+ util_print_indented(temp, 0); -+ } -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL && -+ mk_info.old_mk.mk_state == MK_STATE_VALID && -+ mk_info.new_mk.mkvp == mk_info.old_mk.mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " -+ "register contains the same master key as the OLD " -+ "master key register.", card, domain); -+ util_print_indented(temp, 0); -+ } -+ -+ if (info->mkvp == 0) -+ info->mkvp = mk_info.cur_mk.mkvp; -+ -+ if (info->key_mkvp) { -+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID && -+ mk_info.cur_mk.mkvp == info->mkvp) -+ info->num_cur_match++; -+ -+ if (mk_info.old_mk.mk_state == MK_STATE_VALID && -+ mk_info.old_mk.mkvp == info->mkvp) -+ info->num_old_match++; -+ -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL && -+ mk_info.new_mk.mkvp == info->mkvp) -+ info->num_new_match++; -+ } -+ -+ if (mk_info.cur_mk.mkvp != info->mkvp) { -+ -+ if (info->key_mkvp) { -+ if (mk_info.old_mk.mk_state == MK_STATE_VALID && -+ mk_info.old_mk.mkvp == info->mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The master" -+ " key has been changed to a new " -+ "master key, but the secure key has " -+ "not yet been re-enciphered.", card, -+ domain); -+ util_print_indented(temp, 0); -+ } else if (mk_info.new_mk.mk_state == MK_STATE_FULL && -+ mk_info.new_mk.mkvp == info->mkvp) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The master" -+ " key has been changed but is not " -+ "yet been set (made active).", card, -+ domain); -+ util_print_indented(temp, 0); -+ } else { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The " -+ "CURRENT master key register contains " -+ "a master key that is different from " -+ "the one used by the secure key.", card, -+ domain); -+ util_print_indented(temp, 0); -+ } -+ } else { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * Cross checks the master key information for all specified APQNs. It checks -+ * if all specified APQNs have the same current master key, and if it matches -+ * the master key specified by the mkvp parameter (optional). If not, it prints -+ * out an information message about the APQNs that have a different master key. -+ * -+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -+ * or an empty string, then all online CCA APQNs are -+ * checked. -+ * @param[in] mkvp The master key verification pattern of a secure key. -+ * If this is all zero, then the master keys are not -+ * matched against it. -+ * @param[in] print_mks if true, then a the full master key info of all -+ * specified APQns is printed, in case of a mismatch. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 for success or a negative errno in case of an error. -ENODEV is -+ * returned if at least one APQN has a mismatching master key. -+ * -ENOTSUP is returned when the mkvps sysfs attribute is not -+ * available, because the zcrypt kernel module is on an older level. -+ */ -+int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) -+{ -+ struct cross_check_info info; -+ char temp[200]; -+ int rc; -+ -+ memset(&info, 0, sizeof(info)); -+ info.key_mkvp = mkvp != 0; -+ info.mkvp = mkvp; -+ info.verbose = verbose; -+ -+ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s", -+ mkvp, apqns != NULL ? apqns : "ANY"); -+ -+ rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose); -+ if (rc != 0) -+ return rc; -+ -+ if (info.mismatch) { -+ if (info.key_mkvp) -+ printf("WARNING: Not all APQNs have the correct master " -+ "key (%016llx).\n", mkvp); -+ else -+ printf("WARNING: Not all APQNs have the same master " -+ "key.\n"); -+ -+ rc = -ENODEV; -+ } -+ if (info.num_checked == 0) { -+ printf("WARNING: None of the APQNs is available or of " -+ "type CCA\n"); -+ rc = -ENODEV; -+ } -+ if (info.num_old_match > 0 && info.num_new_match > 0) { -+ sprintf(temp, "WARNING: On %u APQNs the OLD master key " -+ "register contains the master key use by the secure " -+ "key, and on %u APQNs the NEW master key register " -+ "contains the master key use by the secure key.", -+ info.num_old_match, info.num_new_match); -+ util_print_indented(temp, 0); -+ info.print_mks = 1; -+ rc = -ENODEV; -+ } -+ -+ if (print_mks && info.print_mks) { -+ printf("\n"); -+ print_mk_info(apqns, verbose); -+ printf("\n"); -+ } -+ -+ return rc; -+} -diff --git a/zkey/utils.h b/zkey/utils.h -new file mode 100644 -index 0000000..c4dfb2b ---- /dev/null -+++ b/zkey/utils.h -@@ -0,0 +1,54 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * This header file defines the interface to the CCA host library. -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef UTILS_H -+#define UTILS_H -+ -+#include "lib/zt_common.h" -+ -+int sysfs_is_card_online(int card); -+ -+int sysfs_is_apqn_online(int card, int domain); -+ -+int sysfs_get_serialnr(int card, char serialnr[9], bool verbose); -+ -+#define MK_STATE_EMPTY 0 -+#define MK_STATE_PARTIAL 1 -+#define MK_STATE_FULL 2 -+#define MK_STATE_VALID 3 -+#define MK_STATE_INVALID 4 -+#define MK_STATE_UNKNOWN -1 -+ -+struct mk_info_reg { -+ int mk_state; -+ u64 mkvp; -+}; -+ -+struct mk_info { -+ struct mk_info_reg new_mk; -+ struct mk_info_reg cur_mk; -+ struct mk_info_reg old_mk; -+}; -+ -+int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, -+ bool verbose); -+ -+typedef int(*apqn_handler_t) (int card, int domain, void *handler_data); -+ -+int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, -+ bool verbose); -+ -+int print_mk_info(const char *apqns, bool verbose); -+ -+int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, -+ bool verbose); -+ -+#endif -diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 -index e93b5f0..35e3591 100644 ---- a/zkey/zkey-cryptsetup.1 -+++ b/zkey/zkey-cryptsetup.1 -@@ -91,6 +91,8 @@ behave in the same way as with \fBcryptsetup\fP. - .B zkey\-cryptsetup - .BR reencipher | re - .I device -+.RB [ \-\-to\-new | \-N ] -+.RB [ \-\-from\-old | \-O ] - .RB [ \-\-staged | \-s ] - .RB [ \-\-in\-place | \-i ] - .RB [ \-\-complete | \-c ] -@@ -128,17 +130,36 @@ register can still be used until the master key is changed again. - The \fBNEW\fP register contains the new master key to be set. - The master key in the \fBNEW\fP register cannot be used until it is made - the current master key. You can pro-actively re-encipher a secure key with the --\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. -+\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. Use the -+.B \-\-to-new -+option to do this. - .RE - .PP --\fBzkey\-cryptsetup\fP automatically detects whether the secure volume key --is currently enciphered with the master key in the \fBOLD\fP register or with --the master key in the \fBCURRENT\fP register. If currently enciphered with the --master key in the \fBOLD\fP register, it is re-enciphered with the master key --in the \fBCURRENT\fP register. If it is currently enciphered with the master --key in the \fBCURRENT\fP register, it is re-enciphered with the master key in --the \fBNEW\fP register. If for this case the \fBNEW\fP register does not --contain a valid master key, then the re-encipher operation fails. -+Use the -+.B \-\-from\-old -+option to re-encipher a secure volume key that is currently enciphered with -+the master key in the \fBOLD\fP register with the master key in the -+\fBCURRENT\fP register. -+.PP -+.PP -+If both the -+.B \-\-from-old -+and -+.B \-\-to-new -+options are specified, a secure volume key that is currently enciphered -+with the master key in the \fBOLD\fP register is re-enciphered with the -+master key in the \fBNEW\fP register. -+.RE -+.PP -+If both options are omitted, \fBzkey-cryptsetup\fP automatically detects whether -+the secure volume key is currently enciphered with the master key in the -+\fBOLD\fP register or with the master key in the \fBCURRENT\fP register. -+If currently enciphered with the master key in the \fBOLD\fP register, -+it is re-enciphered with the master key in the \fBCURRENT\fP register. -+If it is currently enciphered with the master key in the \fBCURRENT\fP -+register, it is re-enciphered with the master key in the \fBNEW\fP register. -+If for this case the \fBNEW\fP register does not contain a valid master key, -+then the re-encipher operation fails. - .PP - Re-enciphering a secure volume key of a volume encrypted with - \fBLUKS2\fP and the \fBpaes\fP cipher can be performed \fBin-place\fP, or in -@@ -326,6 +347,16 @@ relevance. - . - .SS "Options for the reencipher command" - .TP -+.BR \-N ", " \-\-to\-new -+Re-enciphers a secure volume key in the LUKS2 header that is currently -+enciphered with the master key in the CURRENT register with the master key in -+the NEW register. -+.TP -+.BR \-O ", " \-\-from\-old -+Re-enciphers a secure volume key in the LUKS2 header that is currently -+enciphered with the master key in the OLD register with the master key in the -+CURRENT register. -+.TP - .BR \-i ", " \-\-in-place - Forces an in-place re-enciphering of a secure volume key in the LUKS2 - header. This option immediately replaces the secure volume key in the LUKS2 -diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c -index 8180b4a..c683fd4 100644 ---- a/zkey/zkey-cryptsetup.c -+++ b/zkey/zkey-cryptsetup.c -@@ -34,6 +34,7 @@ - - #include "misc.h" - #include "pkey.h" -+#include "cca.h" - - /* Detect if cryptsetup 2.1 or later is available */ - #ifdef CRYPT_LOG_DEBUG_JSON -@@ -94,6 +95,8 @@ static struct zkey_cryptsetup_globals { - long long keyfile_offset; - long long keyfile_size; - long long tries; -+ bool tonew; -+ bool fromold; - bool complete; - bool inplace; - bool staged; -@@ -101,8 +104,7 @@ static struct zkey_cryptsetup_globals { - bool batch_mode; - bool debug; - bool verbose; -- void *lib_csulcca; -- t_CSNBKTC dll_CSNBKTC; -+ struct cca_lib cca; - int pkey_fd; - struct crypt_device *cd; - } g = { -@@ -162,6 +164,22 @@ static struct util_opt opt_vec[] = { - .desc = "OPTIONS", - .command = COMMAND_REENCIPHER, - }, -+ { -+ .option = {"to-new", 0, NULL, 'N'}, -+ .desc = "Re-enciphers a secure volume key in the LUKS2 header " -+ "that is currently enciphered with the master key in " -+ "the CURRENT register with the master key in the NEW " -+ "register", -+ .command = COMMAND_REENCIPHER, -+ }, -+ { -+ .option = {"from-old", 0, NULL, 'O'}, -+ .desc = "Re-enciphers a secure volume key in the LUKS2 header " -+ "that is currently enciphered with the master key in " -+ "the OLD register with the master key in the CURRENT " -+ "register", -+ .command = COMMAND_REENCIPHER, -+ }, - { - .option = {"staged", 0, NULL, 's'}, - .desc = "Forces that the re-enciphering of a secure volume " -@@ -1275,7 +1293,7 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key, - util_print_indented(complete_msg, 0); - util_print_indented("All key slots containing the old volume key are " - "now in unbound state. Do you want to remove " -- "these key slots?", 0); -+ "these key slots [y/N]?", 0); - - if (!prompt_for_yes()) - return 0; -@@ -1514,17 +1532,19 @@ static int reencipher_prepare(int token) - char *password = NULL; - size_t password_len; - char *key = NULL; -+ int selected = 1; - size_t keysize; - int is_old_mk; - char *prompt; - char *msg; -+ u64 mkvp; - int rc; - - if (token >= 0) { - util_asprintf(&msg, "Staged volume key re-enciphering is " - "already initiated for device '%s'. Do you want to " - "cancel the pending re-enciphering and start a " -- "new re-enciphering process?", g.pos_arg); -+ "new re-enciphering process [y/N]?", g.pos_arg); - util_print_indented(msg, 0); - free(msg); - -@@ -1570,25 +1590,97 @@ static int reencipher_prepare(int token) - if (rc < 0) - goto out; - -- util_asprintf(&msg, "The secure volume key of device '%s' is " -- "enciphered with the %s CCA master key and is being " -- "re-enciphered with the %s CCA master key.", -- g.pos_arg, is_old_mk ? "OLD" : "CURRENT", -- is_old_mk ? "CURRENT" : "NEW"); -- util_print_indented(msg, 0); -- free(msg); -+ if (!g.fromold && !g.tonew) { -+ /* Autodetect reencipher mode */ -+ if (is_old_mk) { -+ g.fromold = 1; -+ util_asprintf(&msg, "The secure volume key of device " -+ "'%s' is enciphered with the OLD CCA " -+ "master key and is being re-enciphered " -+ "with the CURRENT CCA master key.", -+ g.pos_arg); -+ util_print_indented(msg, 0); -+ free(msg); -+ } else { -+ g.tonew = 1; -+ util_asprintf(&msg, "The secure volume key of device " -+ "'%s' is enciphered with the CURRENT CCA " -+ "master key and is being re-enciphered " -+ "with the NEW CCA master key.", -+ g.pos_arg); -+ util_print_indented(msg, 0); -+ free(msg); -+ } -+ } - -- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, -- is_old_mk ? METHOD_OLD_TO_CURRENT : -- METHOD_CURRENT_TO_NEW, -- g.verbose); -+ rc = get_master_key_verification_pattern((u8 *)key, keysize, &mkvp, -+ g.verbose); - if (rc != 0) { -- warnx("Failed to re-encipher the secure volume key of device " -- "'%s'", g.pos_arg); -- rc = -EINVAL; -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); - goto out; - } - -+ if (g.fromold) { -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_OLD_MKVP, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ util_print_indented("No APQN found that is suitable " -+ "for re-enciphering the secure AES " -+ "volume key from the OLD to the " -+ "CURRENT CCA master key.", 0); -+ goto out; -+ } -+ -+ rc = key_token_change(&g.cca, (u8 *)key, keysize, -+ METHOD_OLD_TO_CURRENT, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to re-encipher the secure volume key of " -+ "device '%s'\n", g.pos_arg); -+ if (!selected) -+ print_msg_for_cca_envvars( -+ "secure AES volume key"); -+ rc = -EINVAL; -+ goto out; -+ } -+ } -+ -+ if (g.tonew) { -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP | -+ FLAG_SEL_CCA_NEW_MUST_BE_SET, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ util_print_indented("No APQN found that is suitable " -+ "for re-enciphering the secure AES " -+ "volume key from the CURRENT to " -+ "the NEW CCA master key.", 0); -+ goto out; -+ } -+ -+ rc = key_token_change(&g.cca, (u8 *)key, keysize, -+ METHOD_CURRENT_TO_NEW, -+ g.verbose); -+ if (rc != 0) { -+ warnx("Failed to re-encipher the secure volume key of " -+ "device '%s'\n", g.pos_arg); -+ if (!selected) -+ print_msg_for_cca_envvars( -+ "secure AES volume key"); -+ rc = -EINVAL; -+ goto out; -+ } -+ } -+ - rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, key, keysize, - password, password_len, - CRYPT_VOLUME_KEY_NO_SEGMENT); -@@ -1651,10 +1743,12 @@ static int reencipher_complete(int token) - char *password = NULL; - size_t password_len; - char *key = NULL; -+ int selected = 1; - size_t keysize; - int is_old_mk; - char *prompt; - char *msg; -+ u64 mkvp; - int rc; - - rc = get_reencipher_token(g.cd, token, &tok, true); -@@ -1690,7 +1784,7 @@ static int reencipher_complete(int token) - "was completed.\n" - "Do you want to re-encipher the secure key with " - "the CCA master key in the CURRENT master key " -- "register?", g.pos_arg); -+ "register [y/N]?", g.pos_arg); - util_print_indented(msg, 0); - free(msg); - -@@ -1700,11 +1794,38 @@ static int reencipher_complete(int token) - goto out; - } - -- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, -+ rc = get_master_key_verification_pattern((u8 *)key, keysize, -+ &mkvp, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification " -+ "pattern: %s", -+ strerror(-rc)); -+ goto out; -+ } -+ -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_OLD_MKVP, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ util_print_indented("No APQN found that is suitable " -+ "for re-enciphering the secure AES " -+ "volume key from the OLD to the " -+ "CURRENT CCA master key.", 0); -+ goto out; -+ } -+ -+ rc = key_token_change(&g.cca, (u8 *)key, keysize, - METHOD_OLD_TO_CURRENT, g.verbose); - if (rc != 0) { - warnx("Failed to re-encipher the secure volume key for " -- "device '%s'", g.pos_arg); -+ "device '%s'\n", g.pos_arg); -+ if (!selected) -+ print_msg_for_cca_envvars( -+ "secure AES volume key"); - rc = -EINVAL; - goto out; - } -@@ -1834,6 +1955,7 @@ static int command_validate(void) - char *prompt; - char *msg; - int token; -+ u64 mkvp; - int rc; - - util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); -@@ -1864,6 +1986,14 @@ static int command_validate(void) - vp_tok_avail = 1; - } - -+ rc = get_master_key_verification_pattern((u8 *)key, keysize, -+ &mkvp, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ goto out; -+ } -+ - printf("Validation of secure volume key of device '%s':\n", g.pos_arg); - printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); - printf(" Secure key size: %lu bytes\n", keysize); -@@ -1871,11 +2001,12 @@ static int command_validate(void) - keysize > SECURE_KEY_SIZE ? "Yes" : "No"); - if (is_valid) { - printf(" Clear key size: %lu bits\n", clear_keysize); -- printf(" Enciphered with: %s CCA master key\n", -- is_old_mk ? "OLD" : "CURRENT"); -+ printf(" Enciphered with: %s CCA master key (MKVP: " -+ "%016llx)\n", is_old_mk ? "OLD" : "CURRENT", mkvp); - } else { - printf(" Clear key size: (unknown)\n"); -- printf(" Enciphered with: (unknown)\n"); -+ printf(" Enciphered with: (unknown, MKVP: %016llx)\n", -+ mkvp); - } - if (vp_tok_avail) - print_verification_pattern(vp_tok.verification_pattern); -@@ -2014,12 +2145,13 @@ static int command_setkey(void) - util_asprintf(&msg, "The secure key in file '%s' is " - "enciphered with the CCA master key in the OLD " - "master key register. Do you want to set this " -- "key as the new volume key anyway?", -+ "key as the new volume key anyway [y/N]?", - g.master_key_file); - util_print_indented(msg, 0); - free(msg); - - if (!prompt_for_yes()) { -+ warnx("Device '%s' is left unchanged", g.pos_arg); - rc = -EINVAL; - goto out; - } -@@ -2078,12 +2210,13 @@ static int command_setkey(void) - "be correct. You will lose all data on the " - "volume if you set the wrong volume key!\n" - "Are you sure that the key in file '%s' is the " -- "correct volume key for volume '%s'?", -+ "correct volume key for volume '%s' [y/N]?", - g.master_key_file, g.pos_arg); - util_print_indented(msg, 0); - free(msg); - - if (!prompt_for_yes()) { -+ warnx("Device '%s' is left unchanged", g.pos_arg); - rc = -EINVAL; - goto out; - } -@@ -2204,6 +2337,12 @@ int main(int argc, char *argv[]) - if (c == -1) - break; - switch (c) { -+ case 'N': -+ g.tonew = 1; -+ break; -+ case 'O': -+ g.fromold = 1; -+ break; - case 'c': - g.complete = 1; - break; -@@ -2286,8 +2425,7 @@ int main(int argc, char *argv[]) - } - - if (command->need_cca_library) { -- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, -- g.verbose); -+ rc = load_cca_library(&g.cca, g.verbose); - if (rc != 0) { - rc = EXIT_FAILURE; - goto out; -@@ -2329,8 +2467,8 @@ int main(int argc, char *argv[]) - rc = command->function(); - - out: -- if (g.lib_csulcca) -- dlclose(g.lib_csulcca); -+ if (g.cca.lib_csulcca) -+ dlclose(g.cca.lib_csulcca); - if (g.pkey_fd >= 0) - close(g.pkey_fd); - if (g.cd) -diff --git a/zkey/zkey.c b/zkey/zkey.c -index 2ecbb90..a0dbc0b 100644 ---- a/zkey/zkey.c -+++ b/zkey/zkey.c -@@ -27,9 +27,11 @@ - #include "lib/util_prg.h" - #include "lib/zt_common.h" - -+#include "cca.h" - #include "keystore.h" - #include "misc.h" - #include "pkey.h" -+#include "utils.h" - - /* - * Program configuration -@@ -80,8 +82,7 @@ static struct zkey_globals { - bool force; - bool open; - bool format; -- void *lib_csulcca; -- t_CSNBKTC dll_CSNBKTC; -+ struct cca_lib cca; - int pkey_fd; - struct keystore *keystore; - } g = { -@@ -832,7 +833,7 @@ static struct zkey_command zkey_commands[] = { - .need_pkey_device = 1, - .short_desc = "Validate an existing secure AES key", - .long_desc = "Validate an existing secure AES key that is " -- "either contained in SECURE-KEY-FILE or is stored" -+ "either contained in SECURE-KEY-FILE or is stored " - "in the repository and print information about " - "the key", - .has_options = 1, -@@ -1060,6 +1061,8 @@ static int command_generate_repository(void) - */ - static int command_generate(void) - { -+ int rc; -+ - if (g.pos_arg != NULL && g.name != NULL) { - warnx(" Option '--name|-N' is not valid for generating a key " - "outside of the repository"); -@@ -1067,7 +1070,7 @@ static int command_generate(void) - return EXIT_FAILURE; - } - if (g.apqns == NULL && g.noapqncheck) { -- warnx("Option '--noapqncheck' is only valid together with " -+ warnx("Option '--no-apqn-check' is only valid together with " - "the '--apqns|-a' option"); - util_prg_print_parse_error(); - return EXIT_FAILURE; -@@ -1088,7 +1091,7 @@ static int command_generate(void) - return EXIT_FAILURE; - } - if (g.noapqncheck) { -- warnx("Option '--noapqncheck' is not valid for " -+ warnx("Option '--no-apqn-check' is not valid for " - "generating a key outside of the repository"); - util_prg_print_parse_error(); - return EXIT_FAILURE; -@@ -1100,6 +1103,14 @@ static int command_generate(void) - return EXIT_FAILURE; - } - -+ rc = cross_check_apqns(NULL, 0, true, g.verbose); -+ if (rc == -EINVAL) -+ return EXIT_FAILURE; -+ if (rc != 0 && rc != -ENOTSUP) { -+ warnx("Your master key setup is improper"); -+ return EXIT_FAILURE; -+ } -+ - return g.clearkeyfile ? command_generate_clear() - : command_generate_random(); - } -@@ -1117,7 +1128,9 @@ static int command_reencipher_file(void) - { - size_t secure_key_size; - int rc, is_old_mk; -+ int selected = 1; - u8 *secure_key; -+ u64 mkvp; - - if (g.name != NULL) { - warnx("Option '--name|-N' is not valid for " -@@ -1163,6 +1176,15 @@ static int command_reencipher_file(void) - goto out; - } - -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ - if (!g.fromold && !g.tonew) { - /* Autodetect reencipher option */ - if (is_old_mk) { -@@ -1194,13 +1216,28 @@ static int command_reencipher_file(void) - pr_verbose("Secure key will be re-enciphered from OLD to the " - "CURRENT CCA master key"); - -- rc = key_token_change(g.dll_CSNBKTC, -- secure_key, secure_key_size, -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_OLD_MKVP, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume key"); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = key_token_change(&g.cca, secure_key, secure_key_size, - METHOD_OLD_TO_CURRENT, - g.verbose); - if (rc != 0) { - warnx("Re-encipher from OLD to CURRENT CCA " -- "master key has failed"); -+ "master key has failed\n"); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); - rc = EXIT_FAILURE; - goto out; - } -@@ -1209,12 +1246,30 @@ static int command_reencipher_file(void) - pr_verbose("Secure key will be re-enciphered from CURRENT " - "to the NEW CCA master key"); - -- rc = key_token_change(g.dll_CSNBKTC, -- secure_key, secure_key_size, -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP | -+ FLAG_SEL_CCA_NEW_MUST_BE_SET, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ util_print_indented("No APQN found that is suitable " -+ "for re-enciphering this secure " -+ "AES key and has the NEW master " -+ "key loaded", 0); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = key_token_change(&g.cca, secure_key, secure_key_size, - METHOD_CURRENT_TO_NEW, g.verbose); - if (rc != 0) { - warnx("Re-encipher from CURRENT to NEW CCA " -- "master key has failed"); -+ "master key has failed\n"); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); - rc = EXIT_FAILURE; - goto out; - } -@@ -1270,7 +1325,7 @@ static int command_reencipher_repository(void) - - rc = keystore_reencipher_key(g.keystore, g.name, g.apqns, g.fromold, - g.tonew, g.inplace, g.staged, g.complete, -- g.pkey_fd, g.dll_CSNBKTC); -+ g.pkey_fd, &g.cca); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1302,6 +1357,7 @@ static int command_validate_file(void) - size_t clear_key_size; - u8 *secure_key; - int is_old_mk; -+ u64 mkvp; - int rc; - - if (g.name != NULL) { -@@ -1317,7 +1373,7 @@ static int command_validate_file(void) - return EXIT_FAILURE; - } - if (g.noapqncheck) { -- warnx("Option '--noapqncheck' is not valid for " -+ warnx("Option '--no-apqn-check' is not valid for " - "validating a key outside of the repository"); - util_prg_print_parse_error(); - return EXIT_FAILURE; -@@ -1348,19 +1404,37 @@ static int command_validate_file(void) - goto out; - } - -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ - printf("Validation of secure key in file '%s':\n", g.pos_arg); - printf(" Status: Valid\n"); - printf(" Secure key size: %lu bytes\n", secure_key_size); - printf(" Clear key size: %lu bits\n", clear_key_size); - printf(" XTS type key: %s\n", - secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No"); -- printf(" Enciphered with: %s CCA master key\n", -- is_old_mk ? "OLD" : "CURRENT"); -+ printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n", -+ is_old_mk ? "OLD" : "CURRENT", mkvp); - printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, - vp); - printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, - &vp[VERIFICATION_PATTERN_LEN / 2]); - -+ rc = cross_check_apqns(NULL, mkvp, true, g.verbose); -+ if (rc == -EINVAL) -+ return EXIT_FAILURE; -+ if (rc != 0 && rc != -ENOTSUP) { -+ warnx("Your master key setup is improper"); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ - out: - free(secure_key); - return rc; -@@ -1375,13 +1449,6 @@ static int command_validate_repository(void) - { - int rc; - -- if (g.apqns == NULL && g.noapqncheck) { -- warnx("Option '--noapqncheck' is only valid together with " -- "the '--apqns|-a' option"); -- util_prg_print_parse_error(); -- return EXIT_FAILURE; -- } -- - rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck, - g.pkey_fd); - -@@ -1421,7 +1488,7 @@ static int command_import(void) - g.sector_size = 0; - - if (g.apqns == NULL && g.noapqncheck) { -- warnx("Option '--noapqncheck' is only valid together with " -+ warnx("Option '--no-apqn-check' is only valid together with " - "the '--apqns|-a' option"); - util_prg_print_parse_error(); - return EXIT_FAILURE; -@@ -1501,7 +1568,7 @@ static int command_change(void) - return EXIT_FAILURE; - } - if (g.apqns == NULL && g.noapqncheck) { -- warnx("Option '--noapqncheck' is only valid together with " -+ warnx("Option '--no-apqn-check' is only valid together with " - "the '--apqns|-a' option"); - util_prg_print_parse_error(); - return EXIT_FAILURE; -@@ -1874,8 +1941,7 @@ int main(int argc, char *argv[]) - } - - if (command->need_cca_library) { -- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, -- g.verbose); -+ rc = load_cca_library(&g.cca, g.verbose); - if (rc != 0) { - rc = EXIT_FAILURE; - goto out; -@@ -1894,8 +1960,8 @@ int main(int argc, char *argv[]) - rc = command->function(); - - out: -- if (g.lib_csulcca) -- dlclose(g.lib_csulcca); -+ if (g.cca.lib_csulcca) -+ dlclose(g.cca.lib_csulcca); - if (g.pkey_fd >= 0) - close(g.pkey_fd); - if (g.keystore) --- -2.21.3 - - -From c5e94c1cf3b03d290752227d21540b8786df8131 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 13:23:55 +0100 -Subject: [PATCH 35/57] zkey: Add support for CCA AES CIPHER keys (#1719623) - -Description: With CCA 5 there is a new secure key type, the so called - variable length symmetric cipher key token. This token format - can hold AES keys with size 128, 192 and 256 bits together - with additional attributes cryptographic bound to the key - token. The attributes may limit the usage of the key, for - example restrict export or usability scope. So this key type - is considered to be even more secure than the traditional - secure key token. This key token type is also called "CCA - AES CIPHER key", where the formerly used key token is called - "CCA AES DATA key". - The zkey as well as the zkey-cryptsetup tools are enhanced - to support AES CIPHER keys. That is, zkey can manage AES DATA - keys, as well as AES CIPHER keys. The key type must be specified - at key generation time, the default is to generate AED DATA - keys. - -Upstream-ID: 9de85f42951e0b1a3d083363d7000b1950aebcd7 -Upstream-ID: 91c35543ca7fd25691487c61ec2e308f2903a6b8 -Upstream-ID: b47007b8ac8b446eb94b06e7ed3050b3df3e80e8 -Upstream-ID: 298fab68fee86cb9b1862d60ca274971d4c39638 -Upstream-ID: ddde3f354f3506521877a4e2a6082c4d597629cb -Upstream-ID: d4027e6506963fbf995992e32490d56a6f7ea587 -Upstream-ID: 663d362ff3b1036476bfce9e2563272bab087013 -Upstream-ID: b56c74fe7b6100b9a2ef17b8847cd850309cf487 -Upstream-ID: 0fab6bdf2aa01e093f8a4f3d86c9183889a587fe -Upstream-ID: 560b672bfad3c7bb6631ed4e1676a8b2c836e030 -Upstream-ID: b7bb90c552f9b62c0b4ddc1295e76769149ee6bb -Upstream-ID: b0cc0e47378de9cd82b0cd14228b26be4d615ffc -Upstream-ID: 7d4b1e18b6195f48414f42b4655f900872fed1e7 -Upstream-ID: e7d79d5c5c0928c1bdbd6b669a6e70b8fd3352a5 -Upstream-ID: 7fede7021ece58b9960532e17d963f844fe0de02 -Upstream-ID: 0d9e42264db9935e28f663802c5b95795af79160 -Upstream-ID: a86e41a51827b524c5f88db5e24282166df9b3c8 -Upstream-ID: bc987c8d18ddeb6fec46113a7fe7588555b592e7 -Upstream-ID: 9894e391ef26fd31e2995f19965b7ce5bf006caa ---- - zkey/cca.c | 385 ++++++++++- - zkey/cca.h | 51 ++ - zkey/keystore.c | 605 +++++++++++++----- - zkey/keystore.h | 11 +- - zkey/pkey.c | 1385 ++++++++++++++++++++++++++++++++++------ - zkey/pkey.h | 193 +++++- - zkey/utils.c | 104 ++- - zkey/utils.h | 8 +- - zkey/zkey-cryptsetup.c | 44 +- - zkey/zkey.1 | 142 +++- - zkey/zkey.c | 308 ++++++++- - 11 files changed, 2809 insertions(+), 427 deletions(-) - -diff --git a/zkey/cca.c b/zkey/cca.c -index 2669c75..01f7bfd 100644 ---- a/zkey/cca.c -+++ b/zkey/cca.c -@@ -55,6 +55,14 @@ static void print_CCA_error(int return_code, int reason_code) - warnx("The secure key has a CCA master key " - "verification pattern that is not valid"); - break; -+ case 90: -+ warnx("The operation has been rejected due to access " -+ "control checking"); -+ break; -+ case 2143: -+ warnx("The operation has been rejected due to key " -+ "export restrictions of the secure key"); -+ break; - } - break; - case 12: -@@ -142,6 +150,9 @@ int load_cca_library(struct cca_lib *cca, bool verbose) - /* Get the Key Token Change function */ - cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC"); - -+ /* Get the Key Token Change 2 function */ -+ cca->dll_CSNBKTC2 = (t_CSNBKTC2)dlsym(cca->lib_csulcca, "CSNBKTC2"); -+ - /* Get the Cryptographic Facility Query function */ - cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ"); - -@@ -151,11 +162,20 @@ int load_cca_library(struct cca_lib *cca, bool verbose) - /* Cryptographic Resource Deallocate function */ - cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD"); - -+ /* Get the Key Translate 2 function */ -+ cca->dll_CSNBKTR2 = (t_CSNBKTR2)dlsym(cca->lib_csulcca, "CSNBKTR2"); -+ -+ /* Get the Restrict Key Attribute function */ -+ cca->dll_CSNBRKA = (t_CSNBRKA)dlsym(cca->lib_csulcca, "CSNBRKA"); -+ - if (cca->dll_CSUACFV == NULL || - cca->dll_CSNBKTC == NULL || -+ cca->dll_CSNBKTC2 == NULL || - cca->dll_CSUACFQ == NULL || - cca->dll_CSUACRA == NULL || -- cca->dll_CSUACRD == NULL) { -+ cca->dll_CSUACRD == NULL || -+ cca->dll_CSNBKTR2 == NULL || -+ cca->dll_CSNBRKA == NULL) { - pr_verbose(verbose, "%s", dlerror()); - warnx("The command requires the IBM CCA Host Libraries and " - "Tools.\nFor the supported environments and downloads, " -@@ -187,10 +207,13 @@ int key_token_change(struct cca_lib *cca, - u8 *secure_key, unsigned int secure_key_size, - char *method, bool verbose) - { -+ struct aescipherkeytoken *cipherkey = -+ (struct aescipherkeytoken *)secure_key; - long exit_data_len = 0, rule_array_count; - unsigned char rule_array[2 * 8] = { 0, }; - unsigned char exit_data[4] = { 0, }; - long return_code, reason_code; -+ long key_token_length; - - util_assert(cca != NULL, "Internal error: cca is NULL"); - util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -@@ -202,33 +225,77 @@ int key_token_change(struct cca_lib *cca, - memcpy(rule_array + 8, "AES ", 8); - rule_array_count = 2; - -- cca->dll_CSNBKTC(&return_code, &reason_code, -- &exit_data_len, exit_data, -- &rule_array_count, rule_array, -- secure_key); -- -- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " -- "return_code: %ld, reason_code: %ld", method, return_code, -- reason_code); -- if (return_code != 0) { -- print_CCA_error(return_code, reason_code); -- return -EIO; -- } -- -- if (secure_key_size == 2 * SECURE_KEY_SIZE) { -+ if (is_cca_aes_data_key(secure_key, secure_key_size)) { - cca->dll_CSNBKTC(&return_code, &reason_code, - &exit_data_len, exit_data, - &rule_array_count, rule_array, -- secure_key + SECURE_KEY_SIZE); -+ secure_key); - - pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " - "returned: return_code: %ld, reason_code: %ld", - method, return_code, reason_code); -+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { -+ key_token_length = cipherkey->length; -+ cca->dll_CSNBKTC2(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &key_token_length, -+ (unsigned char *)cipherkey); -+ -+ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with '%s' " -+ "returned: return_code: %ld, reason_code: %ld", -+ method, return_code, reason_code); -+ -+ pr_verbose(verbose, "key_token_length: %lu", key_token_length); -+ } else { -+ warnx("Invalid key type specified"); -+ return -EINVAL; -+ } -+ -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ if (is_xts_key(secure_key, secure_key_size)) { -+ if (is_cca_aes_data_key(secure_key, secure_key_size)) { -+ cca->dll_CSNBKTC(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ secure_key + AESDATA_KEY_SIZE); -+ -+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with " -+ "'%s' returned: return_code: %ld, " -+ "reason_code: %ld", method, return_code, -+ reason_code); -+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { -+ cipherkey = (struct aescipherkeytoken *)(secure_key + -+ AESCIPHER_KEY_SIZE); -+ key_token_length = cipherkey->length; -+ cca->dll_CSNBKTC2(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &key_token_length, -+ (unsigned char *)cipherkey); -+ -+ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with " -+ "'%s' returned: return_code: %ld, " -+ "reason_code: %ld", method, return_code, -+ reason_code); -+ -+ pr_verbose(verbose, "key_token_length: %lu", -+ key_token_length); -+ } else { -+ warnx("Invalid key type specified"); -+ return -EINVAL; -+ } -+ - if (return_code != 0) { - print_CCA_error(return_code, reason_code); - return -EIO; - } - } -+ - return 0; - } - -@@ -422,6 +489,58 @@ static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9], - return 0; - } - -+/** -+ * Queries the firmware version of the current CCA adapter -+ * -+ * @param[in] cca the CCA library structure -+ * @param[out] version the struct where the version is returned -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ */ -+static int get_cca_adapter_version(struct cca_lib *cca, -+ struct cca_version *version, -+ bool verbose) -+{ -+ long exit_data_len = 0, rule_array_count, verb_data_length = 0; -+ unsigned char rule_array[6 * 8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ char version_data[9]; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ -+ memset(rule_array, 0, sizeof(rule_array)); -+ memcpy(rule_array, "STATCCA ", 8); -+ rule_array_count = 1; -+ -+ cca->dll_CSUACFQ(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &verb_data_length, NULL); -+ -+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: " -+ "return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ memcpy(version_data, rule_array+3*8, 8); -+ version_data[8] = '\0'; -+ -+ pr_verbose(verbose, "CCA firmware version string: %s", version_data); -+ -+ if (sscanf((char *)version_data, "%u.%u.%uz", &version->ver, -+ &version->rel, &version->mod) != 3) { -+ warnx("CCA formware version is invalid: %s", version_data); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - /** - * Selects the specified APQN to be used for the CCA host library. - * -@@ -626,3 +745,237 @@ void print_msg_for_cca_envvars(const char *key_name) - util_print_indented(msg, 0); - free(msg); - } -+ -+/* -+ * Convert a secure key of type CCA-AESDATA into a secure key of type -+ * CCA-AESCIPHER. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] input_key the secure key to convert -+ * @param[in] input_key_size the size of the secure key to convert -+ * @param[in] output_key buffer for the converted secure key -+ * @param[in/out] output_key_size on input: size of the output buffer. -+ * on exit: size of the converted secure key -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ */ -+int convert_aes_data_to_cipher_key(struct cca_lib *cca, -+ u8 *input_key, unsigned int input_key_size, -+ u8 *output_key, -+ unsigned int *output_key_size, -+ bool verbose) -+{ -+ long input_token_size, output_token_size, zero = 0; -+ long exit_data_len = 0, rule_array_count = 0; -+ unsigned char *input_token, *output_token; -+ unsigned char rule_array[8 * 2] = { 0, }; -+ unsigned char null_token[64] = { 0, }; -+ long null_token_len = sizeof(null_token); -+ unsigned char exit_data[4] = { 0, }; -+ struct aescipherkeytoken *cipherkey; -+ long return_code, reason_code; -+ struct cca_version version; -+ unsigned char buffer[800]; -+ int rc; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ util_assert(input_key != NULL, "Internal error: input_key is NULL"); -+ util_assert(output_key != NULL, "Internal error: output_key is NULL"); -+ util_assert(output_key_size != NULL, -+ "Internal error: output_key_size is NULL"); -+ -+ if (is_cca_aes_cipher_key(input_key, input_key_size)) { -+ warnx("Invalid key-type specified"); -+ return -EINVAL; -+ } -+ -+ if (*output_key_size < (is_xts_key(input_key, input_key_size) ? -+ 2 * AESCIPHER_KEY_SIZE : AESCIPHER_KEY_SIZE)) -+ return -EINVAL; -+ -+ /* -+ * We need a CCA firmware version 6.3.27 or later to support -+ * conversion of secure keys that are exportable to CPACF protected keys -+ */ -+ rc = get_cca_adapter_version(cca, &version, verbose); -+ if (rc != 0) -+ return rc; -+ if (version.ver < 6 || -+ (version.ver == 6 && version.rel < 3) || -+ (version.ver == 6 && version.rel < 3 && version.mod < 27)) { -+ util_print_indented("The used CCA firmware version does not " -+ "support converting a secure key that can " -+ "be used with the PAES cipher. The " -+ "required CCA firmware version is 6.3.27 " -+ "or later. For the supported environments " -+ "and updates, see: " CCA_WEB_PAGE, 0); -+ return -ENOTSUP; -+ } -+ -+ input_token = input_key; -+ input_token_size = AESDATA_KEY_SIZE; -+ output_token = buffer; -+ output_token_size = sizeof(buffer); -+ memset(buffer, 0, sizeof(buffer)); -+ -+ memcpy(rule_array, "AES ", 8); -+ memcpy(rule_array + 8, "REFORMAT", 8); -+ rule_array_count = 2; -+ -+ cca->dll_CSNBKTR2(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &input_token_size, input_token, -+ &null_token_len, null_token, -+ &zero, NULL, -+ &output_token_size, output_token); -+ -+ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) " -+ "returned: return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ pr_verbose(verbose, "output_token_size: %lu", output_token_size); -+ if (output_token_size > (long)AESCIPHER_KEY_SIZE) { -+ pr_verbose(verbose, "Output key token too large"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Check if the converted key allows export to CPACF protected key. -+ * If not, then the CCA host library or firmware code level is too low. -+ */ -+ cipherkey = (struct aescipherkeytoken *)buffer; -+ if ((cipherkey->kmf1 & 0x0800) == 0) { -+ util_print_indented("The used CCA firmware version does not " -+ "support converting a secure key that can " -+ "be used with the PAES cipher. The " -+ "required CCA firmware version is 6.3.27 " -+ "or later. For the supported environments " -+ "and updates, see: " CCA_WEB_PAGE, 0); -+ return -ENOTSUP; -+ } -+ -+ memset(output_key, 0, *output_key_size); -+ memcpy(output_key, buffer, output_token_size); -+ *output_key_size = AESCIPHER_KEY_SIZE; -+ -+ if (is_xts_key(input_key, input_key_size)) { -+ input_token = input_key + AESDATA_KEY_SIZE; -+ input_token_size = AESDATA_KEY_SIZE; -+ output_token = buffer; -+ output_token_size = sizeof(buffer); -+ memset(buffer, 0, sizeof(buffer)); -+ -+ cca->dll_CSNBKTR2(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &input_token_size, input_token, -+ &null_token_len, null_token, -+ &zero, NULL, -+ &output_token_size, output_token); -+ -+ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) " -+ "returned: return_code: %ld, reason_code: %ld", -+ return_code, reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ pr_verbose(verbose, "output_token_size: %lu", -+ output_token_size); -+ if (output_token_size > (long)AESCIPHER_KEY_SIZE) { -+ pr_verbose(verbose, "Output key token too large"); -+ return -EINVAL; -+ } -+ -+ memcpy(output_key + AESCIPHER_KEY_SIZE, buffer, -+ output_token_size); -+ *output_key_size += AESCIPHER_KEY_SIZE; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Restrict the exportability of an AES CIPHER key. It restricts export by means -+ * of NOEX-AES, NOEX-DES, NOEX-RSA, NOEX-SYM, NOEXUASY, NOEXAASY, NOEX-RAW -+ * keywords. -+ * When this function is called with an AES DATA key, it does nothing and -+ * returns 0. AES DATA keys can not be export restricted. -+ * -+ * @param[in] cca the CCA library structure -+ * @param[in] secure_key the secure key to restrict -+ * @param[in] secure_key_size the size of the secure key to restrict -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ */ -+int restrict_key_export(struct cca_lib *cca, u8 *secure_key, -+ unsigned int secure_key_size, bool verbose) -+{ -+ struct aescipherkeytoken *cipherkey = -+ (struct aescipherkeytoken *)secure_key; -+ long exit_data_len = 0, rule_array_count = 0; -+ unsigned char rule_array[8 * 8] = { 0, }; -+ unsigned char exit_data[4] = { 0, }; -+ long return_code, reason_code; -+ long token_length, zero = 0; -+ -+ util_assert(cca != NULL, "Internal error: cca is NULL"); -+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ -+ if (!is_cca_aes_cipher_key(secure_key, secure_key_size)) -+ return 0; -+ -+ memcpy(rule_array, "AES ", 8); -+ memcpy(rule_array + 8, "NOEX-AES", 8); -+ memcpy(rule_array + 16, "NOEX-DES", 8); -+ memcpy(rule_array + 24, "NOEX-RSA", 8); -+ memcpy(rule_array + 32, "NOEX-SYM", 8); -+ memcpy(rule_array + 40, "NOEXUASY", 8); -+ memcpy(rule_array + 48, "NOEXAASY", 8); -+ memcpy(rule_array + 56, "NOEX-RAW", 8); -+ rule_array_count = 8; -+ -+ token_length = cipherkey->length; -+ cca->dll_CSNBRKA(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &token_length, (unsigned char *)secure_key, -+ &zero, NULL, &zero, NULL, &zero, NULL); -+ -+ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) " -+ "returned: return_code: %ld, reason_code: %ld", return_code, -+ reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ -+ if (is_xts_key(secure_key, secure_key_size)) { -+ cipherkey = (struct aescipherkeytoken *)(secure_key + -+ AESCIPHER_KEY_SIZE); -+ token_length = cipherkey->length; -+ cca->dll_CSNBRKA(&return_code, &reason_code, -+ &exit_data_len, exit_data, -+ &rule_array_count, rule_array, -+ &token_length, (unsigned char *)cipherkey, -+ &zero, NULL, &zero, NULL, &zero, NULL); -+ -+ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) " -+ "returned: return_code: %ld, reason_code: %ld", -+ return_code, reason_code); -+ if (return_code != 0) { -+ print_CCA_error(return_code, reason_code); -+ return -EIO; -+ } -+ } -+ -+ return 0; -+} -diff --git a/zkey/cca.h b/zkey/cca.h -index a79e2ee..2b248ec 100644 ---- a/zkey/cca.h -+++ b/zkey/cca.h -@@ -25,6 +25,15 @@ typedef void (*t_CSNBKTC)(long *return_code, - unsigned char *rule_array, - unsigned char *key_identifier); - -+typedef void (*t_CSNBKTC2)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *key_identifier_length, -+ unsigned char *key_identifier); -+ - typedef void (*t_CSUACFV)(long *return_code, - long *reason_code, - long *exit_data_length, -@@ -59,6 +68,36 @@ typedef void (*t_CSUACRD)(long *return_code, - long *ressource_name_length, - unsigned char *ressource_name); - -+typedef void (*t_CSNBKTR2)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *input_key_token_length, -+ unsigned char *input_key_token, -+ long *input_KEK_key_identifier_length, -+ unsigned char *input_KEK_key_identifier, -+ long *output_KEK_key_identifier_length, -+ unsigned char *output_KEK_key_identifier, -+ long *output_key_token_length, -+ unsigned char *output_key_token); -+ -+typedef void (*t_CSNBRKA)(long *return_code, -+ long *reason_code, -+ long *exit_data_length, -+ unsigned char *exit_data, -+ long *rule_array_count, -+ unsigned char *rule_array, -+ long *key_identifier_length, -+ unsigned char *key_identifier, -+ long *ey_encrypting_key_identifier_length, -+ unsigned char *ey_encrypting_key_identifier, -+ long *opt_parameter1_length, -+ unsigned char *opt_parameter1, -+ long *opt_parameter2_length, -+ unsigned char *opt_parameter2); -+ - struct cca_version { - unsigned int ver; - unsigned int rel; -@@ -68,10 +107,13 @@ struct cca_version { - struct cca_lib { - void *lib_csulcca; - t_CSNBKTC dll_CSNBKTC; -+ t_CSNBKTC2 dll_CSNBKTC2; - t_CSUACFV dll_CSUACFV; - t_CSUACFQ dll_CSUACFQ; - t_CSUACRA dll_CSUACRA; - t_CSUACRD dll_CSUACRD; -+ t_CSNBKTR2 dll_CSNBKTR2; -+ t_CSNBRKA dll_CSNBRKA; - struct cca_version version; - }; - -@@ -92,4 +134,13 @@ int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, - - void print_msg_for_cca_envvars(const char *key_name); - -+int convert_aes_data_to_cipher_key(struct cca_lib *cca, -+ u8 *input_key, unsigned int input_key_size, -+ u8 *output_key, -+ unsigned int *output_key_size, -+ bool verbose); -+ -+int restrict_key_export(struct cca_lib *cca, u8 *secure_key, -+ unsigned int secure_key_size, bool verbose); -+ - #endif -diff --git a/zkey/keystore.c b/zkey/keystore.c -index 957ebb0..af67721 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -70,13 +70,12 @@ struct key_filenames { - #define DEFAULT_VOLUME_TYPE VOLUME_TYPE_PLAIN - #endif - --#define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0) -- - #define REC_KEY "Key" - #define REC_DESCRIPTION "Description" - #define REC_SEC_KEY_SIZE "Secure key size" - #define REC_CLR_KEY_SIZE "Clear key size" - #define REC_XTS "XTS type key" -+#define REC_KEY_TYPE "Key type" - #define REC_VOLUMES "Volumes" - #define REC_APQNS "APQNs" - #define REC_KEY_FILE "Key file name" -@@ -313,6 +312,41 @@ static char *_keystore_get_volume_type(struct properties *properties) - return type; - } - -+/** -+ * Returns the key type contained in the properties. If no key type -+ * property is contained, then 'CCA-AESDATA' is assumed (for backward -+ * compatibility). -+ * -+ * @returns a string containing the key type. Must be freed by the caller. -+ */ -+static char *_keystore_get_key_type(struct properties *properties) -+{ -+ char *type; -+ -+ type = properties_get(properties, PROP_NAME_KEY_TYPE); -+ if (type == NULL) -+ type = util_strdup(KEY_TYPE_CCA_AESDATA); -+ -+ return type; -+} -+ -+/** -+ * Checks if the key type is supported. -+ * -+ * @param[in] key_type the key type -+ * -+ * @returns 1 if the key type is valid, 0 otherwise -+ */ -+static int _keystore_valid_key_type(const char *key_type) -+{ -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) -+ return 1; -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) -+ return 1; -+ -+ return 0; -+} -+ - /** - * Prints a message followed by a list of associated volumes, if volumes are - * associated and the volume-type matches (if specified) -@@ -816,6 +850,33 @@ static int _keystore_match_volume_type_property(struct properties *properties, - return rc; - } - -+/** -+ * Checks if the key type property matches the specified key type. -+ * If the properties do not contain a key type property, then the default -+ * key type is assumed. -+ * -+ * @param[in] properties a properties object -+ * @param[in] key_type the key type to match. Can be NULL. In this case -+ * it always matches. -+ * -+ * @returns 1 for a match, 0 for not matched -+ */ -+static int _keystore_match_key_type_property(struct properties *properties, -+ const char *key_type) -+{ -+ char *type; -+ int rc = 0; -+ -+ if (key_type == NULL) -+ return 1; -+ -+ type = _keystore_get_key_type(properties); -+ if (strcasecmp(type, key_type) == 0) -+ rc = 1; -+ -+ free(type); -+ return rc; -+} - - /** - * Checks if a key name matches a name filter -@@ -881,6 +942,7 @@ typedef int (*process_key_t)(struct keystore *keystore, - * mutliple APQN filters separated by commas. - * NULL means no APQN filter. - * @param[in] volume_type If not NULL, specifies the volume type. -+ * @param[in] key_type The key type. NULL means no key type filter. - * @param[in] process_func the callback function called for a matching key - * @param[in/out] process_private private data passed to the process_func - * -@@ -893,6 +955,7 @@ static int _keystore_process_filtered(struct keystore *keystore, - const char *volume_filter, - const char *apqn_filter, - const char *volume_type, -+ const char *key_type, - process_key_t process_func, - void *process_private) - { -@@ -984,6 +1047,15 @@ static int _keystore_process_filtered(struct keystore *keystore, - goto free_prop; - } - -+ rc = _keystore_match_key_type_property(key_props, -+ key_type); -+ if (rc == 0) { -+ pr_verbose(keystore, -+ "Key '%s' filtered out due to key type", -+ name); -+ goto free_prop; -+ } -+ - rc = process_func(keystore, name, key_props, &file_names, - process_private); - if (rc != 0) { -@@ -1192,7 +1264,7 @@ static int _keystore_volume_check(const char *volume, bool remove, bool set, - - info->set = set; - rc = _keystore_process_filtered(info->keystore, NULL, info->volume, -- NULL, NULL, -+ NULL, NULL, NULL, - _keystore_volume_check_process, info); - out: - free((void *)info->volume); -@@ -1368,7 +1440,7 @@ static int _keystore_generate_verification_pattern(struct keystore *keystore, - if (key == NULL) - return -EIO; - -- rc = generate_key_verification_pattern((const char *)key, key_size, -+ rc = generate_key_verification_pattern(key, key_size, - vp, vp_len, keystore->verbose); - - free(key); -@@ -1453,10 +1525,6 @@ static int _keystore_set_default_properties(struct properties *key_props) - { - int rc; - -- rc = properties_set(key_props, PROP_NAME_KEY_TYPE, "CCA-AESDATA"); -- if (rc != 0) -- return rc; -- - rc = properties_set(key_props, PROP_NAME_CIPHER, "paes"); - if (rc != 0) - return rc; -@@ -1491,6 +1559,7 @@ static int _keystore_set_default_properties(struct properties *key_props) - * the sector size is not specified and the system - * default is used. - * @param[in] volume_type the type of volume -+ * @param[in] key_type the type of the key - */ - static int _keystore_create_info_file(struct keystore *keystore, - const char *name, -@@ -1499,7 +1568,8 @@ static int _keystore_create_info_file(struct keystore *keystore, - const char *volumes, const char *apqns, - bool noapqncheck, - size_t sector_size, -- const char *volume_type) -+ const char *volume_type, -+ const char *key_type) - { - struct volume_check vol_check = { .keystore = keystore, .name = name, - .set = 0 }; -@@ -1521,6 +1591,12 @@ static int _keystore_create_info_file(struct keystore *keystore, - goto out; - } - -+ rc = properties_set2(key_props, PROP_NAME_KEY_TYPE, key_type, true); -+ if (rc != 0) { -+ warnx("Invalid characters in key-type"); -+ goto out; -+ } -+ - rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, - volumes != NULL ? volumes : "", - "volume", _keystore_volume_check, -@@ -1590,52 +1666,6 @@ out: - return rc; - } - --/** -- * Extracts an online card/domain pair from the specified APQns. If none of the -- * specified APQNs are online, then -ENODEV is returned. -- * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero. -- */ --static int _keystore_get_card_domain(const char *apqns, unsigned int *card, -- unsigned int *domain) --{ -- struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 }; -- char **apqn_list; -- char *normalized = NULL; -- int rc = 0; -- int i; -- -- *card = AUTOSELECT; -- *domain = AUTOSELECT; -- -- if (apqns == NULL) -- return 0; -- -- apqn_list = str_list_split(apqns); -- if (apqn_list[0] == NULL) -- goto out; -- -- for (i = 0; apqn_list[i] != NULL; i++) { -- rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized, -- &apqn_check); -- if (normalized != NULL) -- free(normalized); -- if (rc == -EINVAL) -- goto out; -- if (rc != 0) -- continue; -- -- if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2) -- goto found; -- } -- -- warnx("None of the specified APQNs is online or of type CCA"); -- rc = -ENODEV; --found: --out: -- str_list_free_string_array(apqn_list); -- return rc; --} -- - /** - * Generates a secure key by random and adds it to the key store - * -@@ -1658,6 +1688,7 @@ out: - * clear key contained in the file denoted here. - * if NULL, the secure key is generated by random. - * @param[in] volume_type the type of volume -+ * @param[in] key_type the type of the key - * @param[in] pkey_fd the file descriptor of /dev/pkey - * - * @returns 0 for success or a negative errno in case of an error -@@ -1667,15 +1698,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - const char *apqns, bool noapqncheck, - size_t sector_size, size_t keybits, bool xts, - const char *clear_key_file, const char *volume_type, -- int pkey_fd) -+ const char *key_type, int pkey_fd) - { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; -- unsigned int card, domain; -+ char **apqn_list = NULL; - int rc; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); - util_assert(name != NULL, "Internal error: name is NULL"); -+ util_assert(key_type != NULL, "Internal error: key_type is NULL"); -+ -+ if (!_keystore_valid_key_type(key_type)) { -+ warnx("Invalid key-type specified"); -+ return -EINVAL; -+ } - - rc = _keystore_get_key_filenames(keystore, name, &file_names); - if (rc != 0) -@@ -1685,7 +1722,9 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - if (rc != 0) - goto out_free_key_filenames; - -- rc = cross_check_apqns(apqns, 0, true, keystore->verbose); -+ rc = cross_check_apqns(apqns, 0, -+ get_min_card_level_for_keytype(key_type), true, -+ keystore->verbose); - if (rc == -EINVAL) - goto out_free_key_filenames; - if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -@@ -1693,20 +1732,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - goto out_free_key_filenames; - } - -- rc = _keystore_get_card_domain(apqns, &card, &domain); -- if (rc != 0) -- goto out_free_key_filenames; -+ if (apqns != NULL) -+ apqn_list = str_list_split(apqns); - - if (clear_key_file == NULL) - rc = generate_secure_key_random(pkey_fd, - file_names.skey_filename, -- keybits, xts, card, domain, -+ keybits, xts, key_type, -+ (const char **)apqn_list, - keystore->verbose); - else - rc = generate_secure_key_clear(pkey_fd, - file_names.skey_filename, - keybits, xts, clear_key_file, -- card, domain, -+ key_type, -+ (const char **)apqn_list, - keystore->verbose); - if (rc != 0) - goto out_free_props; -@@ -1717,7 +1757,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - - rc = _keystore_create_info_file(keystore, name, &file_names, - description, volumes, apqns, -- noapqncheck, sector_size, volume_type); -+ noapqncheck, sector_size, volume_type, -+ key_type); - if (rc != 0) - goto out_free_props; - -@@ -1727,6 +1768,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - file_names.info_filename); - - out_free_props: -+ if (apqn_list != NULL) -+ str_list_free_string_array(apqn_list); - if (key_props != NULL) - properties_free(key_props); - if (rc != 0) -@@ -1758,17 +1801,21 @@ out_free_key_filenames: - * default is used. - * @param[in] import_file The name of a secure key containing the key to import - * @param[in] volume_type the type of volume -+ * @param[in] cca the CCA library struct - * - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, - const char *apqns, bool noapqncheck, size_t sector_size, -- const char *import_file, const char *volume_type) -+ const char *import_file, const char *volume_type, -+ struct cca_lib *cca) - { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; - size_t secure_key_size; -+ const char *key_type; -+ int selected = 1; - u8 *secure_key; - u64 mkvp; - int rc; -@@ -1792,6 +1839,14 @@ int keystore_import_key(struct keystore *keystore, const char *name, - goto out_free_key_filenames; - } - -+ key_type = get_key_type(secure_key, secure_key_size); -+ if (key_type == NULL) { -+ warnx("Key '%s' is not a valid secure key", name); -+ free(secure_key); -+ rc = -EINVAL; -+ goto out_free_key_filenames; -+ } -+ - rc = get_master_key_verification_pattern(secure_key, secure_key_size, - &mkvp, keystore->verbose); - if (rc != 0) { -@@ -1800,7 +1855,9 @@ int keystore_import_key(struct keystore *keystore, const char *name, - goto out_free_key; - } - -- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); -+ rc = cross_check_apqns(apqns, mkvp, -+ get_min_card_level_for_keytype(key_type), true, -+ keystore->verbose); - if (rc == -EINVAL) - goto out_free_key; - if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -@@ -1808,6 +1865,51 @@ int keystore_import_key(struct keystore *keystore, const char *name, - goto out_free_key; - } - -+ if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { -+ if (cca->lib_csulcca == NULL) { -+ rc = load_cca_library(cca, keystore->verbose); -+ if (rc != 0) -+ goto out_free_key; -+ } -+ -+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP | -+ FLAG_SEL_CCA_MATCH_OLD_MKVP, -+ keystore->verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ warnx("No APQN found that is suitable for " -+ "working with the secure AES key '%s'", name); -+ rc = 0; -+ goto out_free_key; -+ } -+ -+ rc = restrict_key_export(cca, secure_key, secure_key_size, -+ keystore->verbose); -+ if (rc != 0) { -+ warnx("Failed to export-restrict the imported secure " -+ "key: %s", strerror(-rc)); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); -+ goto out_free_key; -+ } -+ -+ rc = check_aes_cipher_key(secure_key, secure_key_size); -+ if (rc != 0) { -+ warnx("The secure key to import might not be secure"); -+ printf("%s: Do you want to import it anyway [y/N]? ", -+ program_invocation_short_name); -+ if (!prompt_for_yes(keystore->verbose)) { -+ warnx("Operation aborted"); -+ rc = -ECANCELED; -+ goto out_free_key; -+ } -+ } -+ } -+ - rc = write_secure_key(file_names.skey_filename, secure_key, - secure_key_size, keystore->verbose); - free(secure_key); -@@ -1821,7 +1923,8 @@ int keystore_import_key(struct keystore *keystore, const char *name, - - rc = _keystore_create_info_file(keystore, name, &file_names, - description, volumes, apqns, -- noapqncheck, sector_size, volume_type); -+ noapqncheck, sector_size, volume_type, -+ key_type); - if (rc != 0) - goto out_free_props; - -@@ -1886,8 +1989,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, - .nomsg = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; -+ char *apqns_prop, *key_type; - size_t secure_key_size; -- char *apqns_prop; - u8 *secure_key; - char temp[30]; - u64 mkvp; -@@ -1954,9 +2057,12 @@ int keystore_change_key(struct keystore *keystore, const char *name, - goto out; - - apqns_prop = properties_get(key_props, PROP_NAME_APQNS); -- rc = cross_check_apqns(apqns_prop, mkvp, true, -- keystore->verbose); -+ key_type = properties_get(key_props, PROP_NAME_KEY_TYPE); -+ rc = cross_check_apqns(apqns_prop, mkvp, -+ get_min_card_level_for_keytype(key_type), -+ true, keystore->verbose); - free(apqns_prop); -+ free(key_type); - if (rc == -ENOTSUP) - rc = 0; - if (rc != 0 && noapqncheck == 0) { -@@ -2140,6 +2246,7 @@ static struct util_rec *_keystore_setup_record(bool validation) - util_rec_def(rec, REC_CLR_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20, - REC_CLR_KEY_SIZE); - util_rec_def(rec, REC_XTS, UTIL_REC_ALIGN_LEFT, 3, REC_XTS); -+ util_rec_def(rec, REC_KEY_TYPE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_TYPE); - if (validation) - util_rec_def(rec, REC_MASTERKEY, UTIL_REC_ALIGN_LEFT, 54, - REC_MASTERKEY); -@@ -2165,7 +2272,7 @@ static void _keystore_print_record(struct util_rec *rec, - const char *name, - struct properties *properties, - bool validation, const char *skey_filename, -- size_t secure_key_size, -+ size_t secure_key_size, bool is_xts, - size_t clear_key_bitsize, bool valid, - bool is_old_mk, bool reenc_pending, u64 mkvp) - { -@@ -2178,6 +2285,7 @@ static void _keystore_print_record(struct util_rec *rec, - char *description; - char *volume_type; - char *reencipher; -+ char *key_type; - char *creation; - char *volumes; - char *change; -@@ -2212,6 +2320,7 @@ static void _keystore_print_record(struct util_rec *rec, - reencipher = properties_get(properties, PROP_NAME_REENC_TIME); - vp = properties_get(properties, PROP_NAME_KEY_VP); - volume_type = _keystore_get_volume_type(properties); -+ key_type = properties_get(properties, PROP_NAME_KEY_TYPE); - - util_rec_set(rec, REC_KEY, name); - if (validation) -@@ -2219,13 +2328,13 @@ static void _keystore_print_record(struct util_rec *rec, - util_rec_set(rec, REC_DESCRIPTION, - description != NULL ? description : ""); - util_rec_set(rec, REC_SEC_KEY_SIZE, "%lu bytes", secure_key_size); -- if (!validation || valid) -+ if ((!validation || valid) && clear_key_bitsize != 0) - util_rec_set(rec, REC_CLR_KEY_SIZE, "%lu bits", - clear_key_bitsize); - else - util_rec_set(rec, REC_CLR_KEY_SIZE, "(unknown)"); -- util_rec_set(rec, REC_XTS, -- IS_XTS(secure_key_size) ? "Yes" : "No"); -+ util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No"); -+ util_rec_set(rec, REC_KEY_TYPE, key_type); - if (validation) { - if (valid) - util_rec_set(rec, REC_MASTERKEY, -@@ -2290,6 +2399,8 @@ static void _keystore_print_record(struct util_rec *rec, - free(vp); - if (volume_type != NULL) - free(volume_type); -+ if (key_type != NULL) -+ free(key_type); - } - - struct validate_info { -@@ -2317,12 +2428,17 @@ static int _keystore_display_apqn_status(struct keystore *keystore, - { - int rc, warning = 0; - char *apqns; -+ char *key_type; - - apqns = properties_get(properties, PROP_NAME_APQNS); - if (apqns == NULL) - return 0; - -- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose); -+ apqns = properties_get(properties, PROP_NAME_APQNS); -+ key_type = properties_get(properties, PROP_NAME_KEY_TYPE); -+ rc = cross_check_apqns(apqns, mkvp, -+ get_min_card_level_for_keytype(key_type), true, -+ keystore->verbose); - if (rc != 0 && rc != -ENOTSUP) - warning = 1; - -@@ -2330,6 +2446,7 @@ static int _keystore_display_apqn_status(struct keystore *keystore, - printf("\n"); - - free(apqns); -+ free(key_type); - return warning; - } - /** -@@ -2395,8 +2512,10 @@ static int _keystore_process_validate(struct keystore *keystore, - void *private) - { - struct validate_info *info = (struct validate_info *)private; -+ char **apqn_list = NULL; - size_t clear_key_bitsize; - size_t secure_key_size; -+ char *apqns = NULL; - u8 *secure_key; - int is_old_mk; - int rc, valid; -@@ -2413,9 +2532,13 @@ static int _keystore_process_validate(struct keystore *keystore, - goto out; - } - -+ apqns = properties_get(properties, PROP_NAME_APQNS); -+ if (apqns != NULL) -+ apqn_list = str_list_split(apqns); -+ - rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, - &clear_key_bitsize, &is_old_mk, -- keystore->verbose); -+ (const char **)apqn_list, keystore->verbose); - if (rc != 0) { - valid = 0; - info->num_invalid++; -@@ -2433,6 +2556,7 @@ static int _keystore_process_validate(struct keystore *keystore, - - _keystore_print_record(info->rec, name, properties, 1, - file_names->skey_filename, secure_key_size, -+ is_xts_key(secure_key, secure_key_size), - clear_key_bitsize, valid, is_old_mk, - _keystore_reencipher_key_exists(file_names), - mkvp); -@@ -2453,6 +2577,10 @@ static int _keystore_process_validate(struct keystore *keystore, - info->num_warnings++; - - out: -+ if (apqns != NULL) -+ free(apqns); -+ if (apqn_list != NULL) -+ str_list_free_string_array(apqn_list); - if (rc != 0) - pr_verbose(keystore, "Failed to validate key '%s': %s", - name, strerror(-rc)); -@@ -2491,7 +2619,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter, - info.num_warnings = 0; - - rc = _keystore_process_filtered(keystore, name_filter, NULL, -- apqn_filter, NULL, -+ apqn_filter, NULL, NULL, - _keystore_process_validate, &info); - - util_rec_free(rec); -@@ -2669,7 +2797,9 @@ static int _keystore_process_reencipher(struct keystore *keystore, - struct reencipher_params params = info->params; - size_t clear_key_bitsize; - size_t secure_key_size; -+ char **apqn_list = NULL; - u8 *secure_key = NULL; -+ char *apqns = NULL; - char *out_file; - int is_old_mk; - char *temp; -@@ -2706,9 +2836,13 @@ static int _keystore_process_reencipher(struct keystore *keystore, - goto out; - } - -+ apqns = properties_get(properties, PROP_NAME_APQNS); -+ if (apqns != NULL) -+ apqn_list = str_list_split(apqns); -+ - rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, - &clear_key_bitsize, &is_old_mk, -- keystore->verbose); -+ (const char **)apqn_list, keystore->verbose); - if (rc != 0) { - if (params.complete) { - warnx("Key '%s' is not valid, re-enciphering is not " -@@ -2807,6 +2941,10 @@ static int _keystore_process_reencipher(struct keystore *keystore, - info->num_reenciphered++; - - out: -+ if (apqns != NULL) -+ free(apqns); -+ if (apqn_list != NULL) -+ str_list_free_string_array(apqn_list); - if (secure_key != NULL) - free(secure_key); - -@@ -2870,7 +3008,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - info.num_skipped = 0; - - rc = _keystore_process_filtered(keystore, name_filter, NULL, -- apqn_filter, NULL, -+ apqn_filter, NULL, NULL, - _keystore_process_reencipher, &info); - - if (rc != 0) { -@@ -3090,7 +3228,6 @@ static int _keystore_prompt_for_remove(struct keystore *keystore, - struct key_filenames *file_names) - { - struct properties *key_prop; -- char str[20]; - char *msg; - int rc; - -@@ -3108,14 +3245,7 @@ static int _keystore_prompt_for_remove(struct keystore *keystore, - - printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name, - name); -- if (fgets(str, sizeof(str), stdin) == NULL) { -- rc = -EIO; -- goto out; -- } -- if (str[strlen(str) - 1] == '\n') -- str[strlen(str) - 1] = '\0'; -- pr_verbose(keystore, "Prompt reply: '%s'", str); -- if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) { -+ if (!prompt_for_yes(keystore->verbose)) { - warnx("Operation aborted"); - rc = -ECANCELED; - goto out; -@@ -3205,29 +3335,29 @@ static int _keystore_display_key(struct keystore *keystore, - void *private) - { - struct util_rec *rec = (struct util_rec *)private; -- struct secaeskeytoken *secure_key; -- size_t secure_key_size; -+ u8 *secure_key; -+ size_t secure_key_size, clear_key_bitsize = 0; - int rc = 0; - -- secure_key = (struct secaeskeytoken *) -- read_secure_key(file_names->skey_filename, -+ secure_key = read_secure_key(file_names->skey_filename, - &secure_key_size, keystore->verbose); - if (secure_key == NULL) - return -EIO; - -- if (secure_key_size < SECURE_KEY_SIZE) { -+ if (secure_key_size < MIN_SECURE_KEY_SIZE) { - pr_verbose(keystore, - "Size of secure key is too small: %lu expected %lu", -- secure_key_size, SECURE_KEY_SIZE); -+ secure_key_size, MIN_SECURE_KEY_SIZE); - rc = -EIO; - goto out; - } - -+ get_key_bit_size(secure_key, secure_key_size, &clear_key_bitsize); -+ - _keystore_print_record(rec, name, properties, 0, - file_names->skey_filename, secure_key_size, -- IS_XTS(secure_key_size) ? secure_key->bitsize * 2 -- : secure_key->bitsize, -- 0, 0, -+ is_xts_key(secure_key, secure_key_size), -+ clear_key_bitsize, 0, 0, - _keystore_reencipher_key_exists(file_names), 0); - - out: -@@ -3251,12 +3381,13 @@ out: - * mutliple APQN filters separated by commas. - * NULL means no APQN filter. - * @param[in] volume_type The volume type. NULL means no volume type filter. -+ * @param[in] key_type The key type. NULL means no key type filter. - * - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_list_keys(struct keystore *keystore, const char *name_filter, - const char *volume_filter, const char *apqn_filter, -- const char *volume_type) -+ const char *volume_type, const char *key_type) - { - struct util_rec *rec; - int rc; -@@ -3269,10 +3400,16 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter, - return -EINVAL; - } - -+ if (key_type != NULL && -+ !_keystore_valid_key_type(key_type)) { -+ warnx("Invalid key-type specified"); -+ return -EINVAL; -+ } -+ - rec = _keystore_setup_record(0); - - rc = _keystore_process_filtered(keystore, name_filter, volume_filter, -- apqn_filter, volume_type, -+ apqn_filter, volume_type, key_type, - _keystore_display_key, rec); - util_rec_free(rec); - -@@ -3582,37 +3719,6 @@ out: - return cipher_spec; - } - --/** -- * Returns the size of the secure key file -- * -- * @param[in] keystore the keystore -- * @param[in] skey_filename the file name of the secure key -- * -- * @returns the size of the secure key, or -1 in case of an error -- */ --static size_t _keystore_get_key_file_size(struct keystore *keystore, -- const char *skey_filename) --{ -- size_t secure_key_size; -- struct stat sb; -- -- if (stat(skey_filename, &sb)) { -- pr_verbose(keystore, "Key file '%s': %s", -- skey_filename, strerror(errno)); -- return -1; -- } -- -- secure_key_size = sb.st_size; -- if (secure_key_size < SECURE_KEY_SIZE) { -- pr_verbose(keystore, -- "Size of secure key is too small: %lu expected %lu", -- secure_key_size, SECURE_KEY_SIZE); -- return -1; -- } -- -- return secure_key_size; --} -- - /** - * Processing function for the cryptsetup and crypttab functions. - * Extracts the required information and calls the secondary processing function -@@ -3639,6 +3745,7 @@ static int _keystore_process_crypt(struct keystore *keystore, - size_t secure_key_size; - size_t sector_size = 0; - char *volumes = NULL; -+ u8 *secure_key = NULL; - char *dmname; - char *temp; - int rc = 0; -@@ -3646,18 +3753,14 @@ static int _keystore_process_crypt(struct keystore *keystore, - char *ch; - int i; - -- secure_key_size = _keystore_get_key_file_size(keystore, -- file_names->skey_filename); -- if (secure_key_size < SECURE_KEY_SIZE) { -- pr_verbose(keystore, -- "Size of secure key is too small: %lu expected %lu", -- secure_key_size, SECURE_KEY_SIZE); -- rc = -EIO; -- goto out; -- } -+ secure_key = read_secure_key(file_names->skey_filename, -+ &secure_key_size, keystore->verbose); -+ if (secure_key == NULL) -+ return -EIO; - - cipher_spec = _keystore_build_cipher_spec(properties, -- IS_XTS(secure_key_size)); -+ is_xts_key(secure_key, -+ secure_key_size)); - if (cipher_spec == NULL) { - rc = -EINVAL; - goto out; -@@ -3709,6 +3812,8 @@ out: - free(cipher_spec); - if (volume_type != NULL) - free(volume_type); -+ if (secure_key != NULL) -+ free(secure_key); - return rc; - } - -@@ -3766,8 +3871,8 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, - info.process_func = _keystore_process_cryptsetup; - - rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, -- volume_type, _keystore_process_crypt, -- &info); -+ volume_type, NULL, -+ _keystore_process_crypt, &info); - - str_list_free_string_array(info.volume_filter); - -@@ -3827,8 +3932,8 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - info.process_func = _keystore_process_crypttab; - - rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, -- volume_type, _keystore_process_crypt, -- &info); -+ volume_type, NULL, -+ _keystore_process_crypt, &info); - - str_list_free_string_array(info.volume_filter); - -@@ -3841,6 +3946,218 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - return rc; - } - -+/** -+ * Converts a secure keys in the keystore -+ * -+ * @param[in] keystore the key store -+ * @param[in] name the name of the key to convert -+ * @param[in] key_type the type of the key to convert it to -+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for -+ * existence and type. -+ * @param[in] pkey_fd the file descriptor of /dev/pkey -+ * @param[in] cca the CCA library struct -+ * -+ * @returns 0 for success or a negative errno in case of an error -+ */ -+int keystore_convert_key(struct keystore *keystore, const char *name, -+ const char *key_type, bool noapqncheck, bool quiet, -+ int pkey_fd, struct cca_lib *cca) -+{ -+ struct key_filenames file_names = { NULL, NULL, NULL }; -+ u8 output_key[2 * MAX_SECURE_KEY_SIZE]; -+ struct properties *properties = NULL; -+ int rc, min_level, selected = 1; -+ unsigned int output_key_size; -+ char *cur_key_type = NULL; -+ char **apqn_list = NULL; -+ size_t secure_key_size; -+ u8 *secure_key = NULL; -+ char *apqns = NULL; -+ char *temp; -+ u64 mkvp; -+ -+ util_assert(keystore != NULL, "Internal error: keystore is NULL"); -+ util_assert(name != NULL, "Internal error: name is NULL"); -+ -+ rc = _keystore_get_key_filenames(keystore, name, &file_names); -+ if (rc != 0) -+ goto out; -+ -+ rc = _keystore_ensure_keyfiles_exist(&file_names, name); -+ if (rc != 0) -+ goto out; -+ -+ properties = properties_new(); -+ rc = properties_load(properties, file_names.info_filename, 1); -+ if (rc != 0) { -+ warnx("Key '%s' does not exist or is invalid", name); -+ goto out; -+ } -+ -+ cur_key_type = _keystore_get_key_type(properties); -+ if (strcasecmp(cur_key_type, key_type) == 0) { -+ warnx("The secure key '%s' is already of type %s", name, -+ cur_key_type); -+ rc = 0; -+ goto out; -+ } -+ if (strcasecmp(cur_key_type, KEY_TYPE_CCA_AESDATA) != 0) { -+ warnx("Only secure keys of type %s can " -+ "be converted. The secure key '%s' is of type %s", -+ KEY_TYPE_CCA_AESDATA, name, cur_key_type); -+ rc = 0; -+ goto out; -+ } -+ -+ secure_key = read_secure_key(file_names.skey_filename, -+ &secure_key_size, keystore->verbose); -+ if (secure_key == NULL) { -+ rc = -ENOENT; -+ goto out; -+ } -+ -+ min_level = get_min_card_level_for_keytype(key_type); -+ if (min_level < 0) { -+ warnx("Invalid key-type specified: %s", key_type); -+ rc = -EINVAL; -+ goto out; -+ } -+ -+ apqns = properties_get(properties, PROP_NAME_APQNS); -+ if (apqns != NULL) -+ apqn_list = str_list_split(apqns); -+ -+ rc = cross_check_apqns(apqns, 0, min_level, true, keystore->verbose); -+ if (rc == -EINVAL) -+ goto out; -+ if (rc != 0 && rc != -ENOTSUP && !noapqncheck) { -+ warnx("Your master key setup is improper for converting key " -+ "'%s'", name); -+ goto out; -+ } -+ -+ rc = validate_secure_key(pkey_fd, secure_key, secure_key_size, -+ NULL, NULL, (const char **)apqn_list, -+ keystore->verbose); -+ if (rc != 0) -+ goto out; -+ -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, keystore->verbose); -+ if (rc) -+ goto out; -+ -+ rc = select_cca_adapter_by_mkvp(cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP, -+ keystore->verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ warnx("No APQN found that is suitable for " -+ "converting the secure AES key '%s'", name); -+ goto out; -+ } -+ -+ if (!quiet) { -+ util_print_indented("ATTENTION: Converting a secure key is " -+ "irreversible, and might have an effect " -+ "on the volumes encrypted with it!", 0); -+ _keystore_msg_for_volumes("The following volumes are encrypted " -+ "with this key:", properties, NULL); -+ printf("%s: Convert key '%s [y/N]'? ", -+ program_invocation_short_name, name); -+ if (!prompt_for_yes(keystore->verbose)) { -+ warnx("Operation aborted"); -+ rc = -ECANCELED; -+ goto out; -+ } -+ } -+ -+ memset(output_key, 0, sizeof(output_key)); -+ output_key_size = sizeof(output_key); -+ rc = convert_aes_data_to_cipher_key(cca, secure_key, -+ secure_key_size, output_key, -+ &output_key_size, -+ keystore->verbose); -+ if (rc != 0) { -+ warnx("Converting the secure key '%s' from %s to %s has failed", -+ name, KEY_TYPE_CCA_AESDATA, key_type); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); -+ goto out; -+ } -+ -+ rc = restrict_key_export(cca, output_key, output_key_size, -+ keystore->verbose); -+ if (rc != 0) { -+ warnx("Export restricting the converted secure key '%s' has " -+ "failed", name); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); -+ goto out; -+ } -+ -+ rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true); -+ if (rc != 0) { -+ warnx("Invalid characters in key-type"); -+ goto out; -+ } -+ -+ rc = properties_save(properties, file_names.info_filename, 1); -+ if (rc != 0) { -+ pr_verbose(keystore, -+ "Failed to write key info file '%s': %s", -+ file_names.info_filename, strerror(-rc)); -+ goto out; -+ } -+ -+ rc = write_secure_key(file_names.skey_filename, output_key, -+ output_key_size, keystore->verbose); -+ if (rc != 0) -+ goto out; -+ -+ pr_verbose(keystore, "Secure key '%s' was converted successfully", -+ name); -+ -+ util_asprintf(&temp, "The following LUKS2 volumes are " -+ "encrypted with key '%s'. These volumes still contain " -+ "the secure AES volume key of type CCA-AESDATA. To " -+ "change the secure AES volume key in the LUKS2 header, " -+ "run command 'zkey-cryptsetup setkey " -+ "--master-key-file %s':", name, -+ file_names.skey_filename); -+ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_LUKS2); -+ free(temp); -+ util_asprintf(&temp, "The following plain mode volumes are " -+ "encrypted with key '%s'. You must adapt the crypttab " -+ "entries for this volumes and change the key size " -+ "parameter to 'size=%u' or run command 'zkey crypttab " -+ "--volumes ' for each volume to re-generate the " -+ "crypttab entries:", name, output_key_size * 8, name); -+ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_PLAIN); -+ free(temp); -+ -+out: -+ _keystore_free_key_filenames(&file_names); -+ if (properties != NULL) -+ properties_free(properties); -+ if (secure_key != NULL) -+ free(secure_key); -+ if (apqns != NULL) -+ free(apqns); -+ if (apqn_list != NULL) -+ str_list_free_string_array(apqn_list); -+ if (cur_key_type != NULL) -+ free(cur_key_type); -+ -+ if (rc != 0) -+ pr_verbose(keystore, "Failed to convert key '%s': %s", -+ name, strerror(-rc)); -+ return rc; -+} -+ - /** - * Frees a keystore object - * -diff --git a/zkey/keystore.h b/zkey/keystore.h -index 8e888d1..b17a575 100644 ---- a/zkey/keystore.h -+++ b/zkey/keystore.h -@@ -32,12 +32,13 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - const char *apqns, bool noapqncheck, - size_t sector_size, size_t keybits, bool xts, - const char *clear_key_file, const char *volume_type, -- int pkey_fd); -+ const char *key_type, int pkey_fd); - - int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, - const char *apqns, bool noapqncheck, size_t sector_size, -- const char *import_file, const char *volume_type); -+ const char *import_file, const char *volume_type, -+ struct cca_lib *cca); - - int keystore_change_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -@@ -68,7 +69,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name, - - int keystore_list_keys(struct keystore *keystore, const char *name_filter, - const char *volume_filter, const char *apqn_filter, -- const char *volume_type); -+ const char *volume_type, const char *key_type); - - int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, - bool execute, const char *volume_type, -@@ -80,6 +81,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - const char *volume_type, const char *keyfile, - size_t keyfile_offset, size_t keyfile_size, size_t tries); - -+int keystore_convert_key(struct keystore *keystore, const char *name, -+ const char *key_type, bool noapqncheck, bool quiet, -+ int pkey_fd, struct cca_lib *cca); -+ - void keystore_free(struct keystore *keystore); - - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index 8471f3d..462f9fe 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -46,6 +46,8 @@ - - #define DEFAULT_KEYBITS 256 - -+#define INITIAL_APQN_ENTRIES 16 -+ - /** - * Opens the pkey device and returns its file descriptor. - * -@@ -98,10 +100,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, - } - size = sb.st_size; - -- if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) { -- warnx("File '%s' has an invalid size, %lu or %lu bytes " -- "expected", keyfile, SECURE_KEY_SIZE, -- 2 * SECURE_KEY_SIZE); -+ if (size < MIN_SECURE_KEY_SIZE || size > 2 * MAX_SECURE_KEY_SIZE) { -+ warnx("File '%s' has an invalid size: %lu", keyfile, size); - return NULL; - } - -@@ -271,6 +271,615 @@ out: - return buf; - } - -+/** -+ * Returns the PKEY_KEYTYPE_xxx value for the specified key size. -+ * -+ * @param[in] keysize the key size in bits -+ * -+ * @returns the PKEY_KEYTYPE_xxx value or 0 for an unknown key size -+ */ -+static u32 keysize_to_keytype(enum pkey_key_size keysize) -+{ -+ switch (keysize) { -+ case PKEY_SIZE_AES_128: -+ return PKEY_KEYTYPE_AES_128; -+ case PKEY_SIZE_AES_192: -+ return PKEY_KEYTYPE_AES_192; -+ case PKEY_SIZE_AES_256: -+ return PKEY_KEYTYPE_AES_256; -+ default: -+ return 0; -+ } -+} -+ -+/** -+ * Returns the PKEY_SIZE_xxx value for the specified keybits. -+ * -+ * @param[in] keybits the key size in bits -+ * -+ * @returns thePKEY_SIZE_xxx value or 0 for an unknown key size -+ */ -+static enum pkey_key_size keybits_to_keysize(u32 keybits) -+{ -+ switch (keybits) { -+ case 128: -+ return PKEY_SIZE_AES_128; -+ case 192: -+ return PKEY_SIZE_AES_192; -+ case 256: -+ return PKEY_SIZE_AES_256; -+ default: -+ return PKEY_SIZE_UNKNOWN; -+ } -+} -+ -+/* -+ * Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure -+ * key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported -+ * by the pkey device, then it falls back to the older PKEY_GENSECK IOCTL -+ * -+ * @param[in] pkey_fd the pkey file descriptor -+ * @param[in/out] genseck info about key to generate -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int pkey_genseck2(int pkey_fd, struct pkey_genseck2 *genseck2, -+ bool verbose) -+{ -+ struct pkey_genseck genseck; -+ int rc; -+ u32 i; -+ -+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -+ util_assert(genseck2 != NULL, "Internal error: genseck2 is NULL"); -+ -+ rc = ioctl(pkey_fd, PKEY_GENSECK2, genseck2); -+ if (rc != 0 && errno != ENOTTY) -+ return -errno; -+ if (rc == 0) -+ return 0; -+ -+ /* New IOCTL is not available, fall back to old one */ -+ pr_verbose(verbose, "ioctl PKEY_GENSECK2 not supported, fall back to " -+ "PKEY_GENSECK"); -+ -+ if (genseck2->type != PKEY_TYPE_CCA_DATA) { -+ warnx("Key-type is not supported"); -+ return -ENOTSUP; -+ } -+ -+ if (genseck2->keylen < AESDATA_KEY_SIZE) -+ return -EINVAL; -+ -+ memset(&genseck, 0, sizeof(genseck)); -+ -+ genseck.keytype = keysize_to_keytype(genseck2->size); -+ if (genseck.keytype == 0) -+ return -EINVAL; -+ -+ for (i = 0; i < genseck2->apqn_entries; i++) { -+ genseck.cardnr = genseck2->apqns[i].card; -+ genseck.domain = genseck2->apqns[i].domain; -+ -+ rc = ioctl(pkey_fd, PKEY_GENSECK, &genseck); -+ if (rc != 0) -+ continue; -+ -+ memcpy(genseck2->key, &genseck.seckey.seckey, AESDATA_KEY_SIZE); -+ genseck2->keylen = AESDATA_KEY_SIZE; -+ return 0; -+ } -+ -+ return -errno; -+} -+ -+/* -+ * Wrapper for the PKEY_CLR2SECK/PKEY_CLR2SECK2 IOCTL to generate a secure -+ * key of any type from a clear key. If the newer PKEY_CLR2SECK2 IOCTL is not -+ * supported by the pkey device, then it falls back to the older PKEY_CLR2SECK -+ * IOCTL -+ * -+ * @param[in] pkey_fd the pkey file descriptor -+ * @param[in/out] clr2seck2 info about key to generate -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int pkey_clr2seck2(int pkey_fd, struct pkey_clr2seck2 *clr2seck2, -+ bool verbose) -+{ -+ struct pkey_clr2seck clr2seck; -+ int rc; -+ u32 i; -+ -+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -+ util_assert(clr2seck2 != NULL, "Internal error: clr2seck2 is NULL"); -+ -+ rc = ioctl(pkey_fd, PKEY_CLR2SECK2, clr2seck2); -+ if (rc != 0 && errno != ENOTTY) -+ return -errno; -+ if (rc == 0) -+ return 0; -+ -+ /* New IOCTL is not available, fall back to old one */ -+ pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 not supported, fall back to " -+ "PKEY_CLR2SECK"); -+ -+ if (clr2seck2->type != PKEY_TYPE_CCA_DATA) { -+ warnx("Key-type is not supported"); -+ return -ENOTSUP; -+ } -+ -+ if (clr2seck2->keylen < AESDATA_KEY_SIZE) -+ return -EINVAL; -+ -+ memset(&clr2seck, 0, sizeof(clr2seck)); -+ clr2seck.clrkey = clr2seck2->clrkey; -+ -+ clr2seck.keytype = keysize_to_keytype(clr2seck2->size); -+ if (clr2seck.keytype == 0) -+ return -EINVAL; -+ -+ for (i = 0; i < clr2seck2->apqn_entries; i++) { -+ clr2seck.cardnr = clr2seck2->apqns[i].card; -+ clr2seck.domain = clr2seck2->apqns[i].domain; -+ -+ rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2seck); -+ if (rc != 0) -+ continue; -+ -+ memcpy(clr2seck2->key, &clr2seck.seckey.seckey, -+ AESDATA_KEY_SIZE); -+ clr2seck2->keylen = AESDATA_KEY_SIZE; -+ return 0; -+ } -+ -+ return -errno; -+} -+ -+/* -+ * Wrapper for the PKEY_VERIFYKEY/PKEY_VERIFYKEY2 IOCTL to verify a secure -+ * key of any type. If the newer PKEY_VERIFYKEY2 IOCTL is not supported -+ * by the pkey device, then it falls back to the older PKEY_VERIFYKEY IOCTL -+ * -+ * @param[in] pkey_fd the pkey file descriptor -+ * @param[in/out] verifykey2 info about key to verify -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int pkey_verifyseck2(int pkey_fd, struct pkey_verifykey2 *verifykey2, -+ bool verbose) -+{ -+ struct pkey_verifykey verifykey; -+ int rc; -+ -+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -+ util_assert(verifykey2 != NULL, "Internal error: verifyseck2 is NULL"); -+ -+ rc = ioctl(pkey_fd, PKEY_VERIFYKEY2, verifykey2); -+ if (rc != 0 && errno != ENOTTY) -+ return -errno; -+ if (rc == 0) -+ return 0; -+ -+ /* New IOCTL is not available, fall back to old one */ -+ pr_verbose(verbose, "ioctl PKEY_VERIFYKEY2 not supported, fall back to " -+ "PKEY_VERIFYKEY"); -+ -+ if (!is_cca_aes_data_key(verifykey2->key, verifykey2->keylen)) -+ return -ENODEV; -+ -+ memset(&verifykey, 0, sizeof(verifykey)); -+ memcpy(&verifykey.seckey, verifykey2->key, sizeof(verifykey.seckey)); -+ -+ /* -+ * Note: the old IOCTL does not support to check a specific card and -+ * domain. If falling back to the old IOCTL, this input is silently -+ * ignored, and all APQNs currently available in the system are used. -+ */ -+ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); -+ if (rc != 0) -+ return -errno; -+ -+ if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) -+ return -ENODEV; -+ -+ verifykey2->type = PKEY_TYPE_CCA_DATA; -+ verifykey2->cardnr = verifykey.cardnr; -+ verifykey2->domain = verifykey.domain; -+ verifykey2->size = keybits_to_keysize(verifykey.keysize); -+ -+ if (verifykey.attributes & PKEY_VERIFY_ATTR_OLD_MKVP) -+ verifykey2->flags = PKEY_FLAGS_MATCH_ALT_MKVP; -+ else -+ verifykey2->flags = PKEY_FLAGS_MATCH_CUR_MKVP; -+ -+ return 0; -+} -+ -+/** -+ * Print a list of APQNs if verbose is set -+ */ -+static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num) -+{ -+ u32 i; -+ -+ if (!verbose) -+ return; -+ -+ for (i = 0; i < num ; i++) -+ warnx(" APQN: %02x.%04x", list[i].card, list[i].domain); -+} -+ -+/** -+ * Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings. -+ * -+ * @param[in] apqn_list a zero terminated array of pointers to C-strings -+ * @param[in/out] apqns A list of APQNs as array of struct pkey_apqn to -+ * filter. The list is modified during filtering. -+ * @param[in/out] apqn_entries Number of entries in the list of APQNs. The -+ * number is modified during filtering. -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns, -+ u32 *apqn_entries) -+{ -+ unsigned int count, i, k, card, domain; -+ struct pkey_apqn *list = *apqns; -+ bool found; -+ -+ if (apqn_list == NULL) -+ return 0; -+ -+ for (count = 0; apqn_list[count] != NULL; count++) -+ ; -+ if (count == 0) -+ return 0; -+ -+ for (i = 0; i < *apqn_entries; i++) { -+ found = false; -+ for (k = 0; apqn_list[k] != NULL; k++) { -+ if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2) -+ return -EINVAL; -+ -+ if (list[i].card == card && list[i].domain == domain) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) { -+ if (i < *apqn_entries - 1) -+ memmove(&list[i], &list[i+1], -+ (*apqn_entries - i - 1) * -+ sizeof(struct pkey_apqn)); -+ (*apqn_entries)--; -+ i--; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the -+ * List of APQNs as zero terminated array of pointers to C-strings that -+ * are usable for the CCA-AESDATA key type. -+ * -+ * @param[in] apqn_list a zero terminated array of pointers to C-strings -+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The -+ * list must be freed by the caller using free(). -+ * @param[out] apqn_entries Number of entries in the list of APQNs -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int build_apqn_list_for_aes_data(const char **apqn_list, -+ struct pkey_apqn **apqns, -+ u32 *apqn_entries, bool verbose) -+{ -+ unsigned int card, domain, count = 0; -+ struct pkey_apqn *list = NULL; -+ u32 list_entries = 0; -+ int i; -+ -+ pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA"); -+ -+ if (apqn_list != NULL) -+ for (count = 0; apqn_list[count] != NULL; count++) -+ ; -+ -+ if (count > 0) { -+ list = util_malloc(count * sizeof(struct pkey_apqn)); -+ list_entries = count; -+ -+ for (i = 0; apqn_list[i] != NULL; i++) { -+ if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) -+ return -EINVAL; -+ -+ list[i].card = card; -+ list[i].domain = domain; -+ } -+ -+ } else { -+ /* -+ * Although the new pkey IOCTLs do not support APQN entries -+ * with ANY indication, build an ANY-list here. If we get here, -+ * then the new IOCTLs are not available, and it will fall back -+ * to the old IOCTL which do support ANY specifications. -+ */ -+ list = util_malloc(sizeof(struct pkey_apqn)); -+ list_entries = 1; -+ -+ list[0].card = AUTOSELECT; -+ list[0].domain = AUTOSELECT; -+ } -+ -+ *apqns = list; -+ *apqn_entries = list_entries; -+ -+ pr_verbose(verbose, "%u APQNs found", list_entries); -+ pr_verbose_apqn_list(verbose, list, list_entries); -+ return 0; -+} -+ -+/** -+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the -+ * List of APQNs as zero terminated array of pointers to C-strings that -+ * are usable for the specified key type. -+ * -+ * @param[in] pkey_fd the pkey file descriptor -+ * @param[in] type the key type -+ * @param[in] apqn_list a zero terminated array of pointers to C-strings -+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The -+ * list must be freed by the caller using free(). -+ * @param[out] apqn_entries Number of entries in the list of APQNs -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type, -+ const char **apqn_list, -+ struct pkey_apqn **apqns, -+ u32 *apqn_entries, bool verbose) -+{ -+ struct pkey_apqns4keytype apqns4keytype; -+ int rc; -+ -+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -+ util_assert(apqns != NULL, "Internal error: apqns is NULL"); -+ util_assert(apqn_entries != NULL, -+ "Internal error: apqn_entries is NULL"); -+ -+ pr_verbose(verbose, "Build a list of APQNs for key type %d", type); -+ -+ memset(&apqns4keytype, 0, sizeof(apqns4keytype)); -+ apqns4keytype.type = type; -+ apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES; -+ apqns4keytype.apqns = (struct pkey_apqn *)util_malloc( -+ apqns4keytype.apqn_entries * sizeof(struct pkey_apqn)); -+ -+ do { -+ rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype); -+ if (rc == 0) -+ break; -+ rc = -errno; -+ pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s", -+ strerror(-rc)); -+ -+ switch (rc) { -+ case -ENOSPC: -+ free(apqns4keytype.apqns); -+ apqns4keytype.apqns = (struct pkey_apqn *) -+ util_malloc(apqns4keytype.apqn_entries * -+ sizeof(struct pkey_apqn)); -+ continue; -+ case -ENOTTY: -+ /* -+ * New IOCTL is not available: build the list -+ * manually (Key type CCA-AESDATA only) -+ */ -+ free(apqns4keytype.apqns); -+ -+ if (type != PKEY_TYPE_CCA_DATA) -+ return -ENOTSUP; -+ -+ rc = build_apqn_list_for_aes_data(apqn_list, apqns, -+ apqn_entries, -+ verbose); -+ return rc; -+ default: -+ goto out; -+ } -+ } while (rc != 0); -+ -+ if (apqns4keytype.apqn_entries == 0) { -+ pr_verbose(verbose, "No APQN available for key type %d", type); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns, -+ &apqns4keytype.apqn_entries); -+ if (rc != 0) -+ goto out; -+ -+ if (apqns4keytype.apqn_entries == 0) { -+ pr_verbose(verbose, "No APQN available for key type %d", type); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries); -+ pr_verbose_apqn_list(verbose, apqns4keytype.apqns, -+ apqns4keytype.apqn_entries); -+ -+out: -+ if (rc == 0) { -+ *apqns = apqns4keytype.apqns; -+ *apqn_entries = apqns4keytype.apqn_entries; -+ } else { -+ *apqns = NULL; -+ *apqn_entries = 0; -+ free(apqns4keytype.apqns); -+ } -+ -+ return rc; -+} -+ -+/** -+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the -+ * List of APQNs as zero terminated array of pointers to C-strings that are -+ * usable for the specufied key. -+ * -+ * @param[in] pkey_fd the pkey file descriptor -+ * @param[in] key the key -+ * @param[in] keylen the length of the key -+ * @param[in] flags PKEY_FLAGS_MATCH_xxx flags -+ * @param[in] apqn_list a zero terminated array of pointers to C-strings -+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The -+ * list must be freed by the caller using free(). -+ * @param[out] apqn_entries Number of entries in the list of APQNs -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags, -+ const char **apqn_list, -+ struct pkey_apqn **apqns, -+ u32 *apqn_entries, bool verbose) -+{ -+ struct pkey_apqns4key apqns4key; -+ u64 mkvp; -+ int rc; -+ -+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -+ util_assert(key != NULL, "Internal error: key is NULL"); -+ util_assert(apqns != NULL, "Internal error: apqns is NULL"); -+ util_assert(apqn_entries != NULL, -+ "Internal error: apqn_entries is NULL"); -+ -+ pr_verbose(verbose, "Build a list of APQNs for the key"); -+ -+ memset(&apqns4key, 0, sizeof(apqns4key)); -+ apqns4key.key = key; -+ apqns4key.keylen = keylen; -+ apqns4key.flags = flags; -+ apqns4key.apqn_entries = INITIAL_APQN_ENTRIES; -+ apqns4key.apqns = (struct pkey_apqn *)util_malloc( -+ apqns4key.apqn_entries * sizeof(struct pkey_apqn)); -+ -+ do { -+ rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key); -+ if (rc == 0) -+ break; -+ rc = -errno; -+ pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc)); -+ -+ switch (rc) { -+ case -ENOSPC: -+ free(apqns4key.apqns); -+ apqns4key.apqns = (struct pkey_apqn *) -+ util_malloc(apqns4key.apqn_entries * -+ sizeof(struct pkey_apqn)); -+ continue; -+ case -ENOTTY: -+ /* -+ * New IOCTL is not available: build the list manually -+ * (Key type CCA-AESDATA only) -+ */ -+ free(apqns4key.apqns); -+ -+ if (!is_cca_aes_data_key(key, keylen)) -+ return -ENOTSUP; -+ -+ rc = get_master_key_verification_pattern(key, keylen, -+ &mkvp, -+ verbose); -+ if (rc != 0) -+ return rc; -+ -+ rc = build_apqn_list_for_aes_data(apqn_list, apqns, -+ apqn_entries, -+ verbose); -+ return rc; -+ default: -+ goto out; -+ } -+ } while (rc != 0); -+ -+ if (apqns4key.apqn_entries == 0) { -+ pr_verbose(verbose, "No APQN available for the key"); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ rc = filter_apqn_list(apqn_list, &apqns4key.apqns, -+ &apqns4key.apqn_entries); -+ if (rc != 0) -+ goto out; -+ -+ if (apqns4key.apqn_entries == 0) { -+ pr_verbose(verbose, "No APQN available for the key"); -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries); -+ pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries); -+ -+out: -+ if (rc == 0) { -+ *apqns = apqns4key.apqns; -+ *apqn_entries = apqns4key.apqn_entries; -+ } else { -+ *apqns = NULL; -+ *apqn_entries = 0; -+ free(apqns4key.apqns); -+ } -+ -+ return rc; -+} -+ -+/** -+ * Convert the key type string into the pkey enumeration -+ * -+ * @param[in] key_type the type of the key -+ * -+ * @returns the pkey key type or 0 for an u known key type -+ */ -+static enum pkey_key_type key_type_to_pkey_type(const char *key_type) -+{ -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) -+ return PKEY_TYPE_CCA_DATA; -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) -+ return PKEY_TYPE_CCA_CIPHER; -+ -+ return 0; -+} -+ -+/** -+ * Return the size of a key blob for a specific type -+ * -+ * @param[in] type the type of the key -+ * -+ * @returns the size of the key or 0 for an invalid key type -+ */ -+static size_t key_size_for_type(enum pkey_key_type type) -+{ -+ switch (type) { -+ case PKEY_TYPE_CCA_DATA: -+ return AESDATA_KEY_SIZE; -+ case PKEY_TYPE_CCA_CIPHER: -+ return AESCIPHER_KEY_SIZE; -+ default: -+ return 0; -+ } -+} -+ - /** - * Generate a secure key by random - * -@@ -278,80 +887,110 @@ out: - * @param[in] keyfile the file name of the secure key to generate - * @param[in] keybits the cryptographic size of the key in bits - * @param[in] xts if true an XTS key is generated -- * @param[in] card the card number to use (or AUTOSELECT) -- * @param[in] domain the domain number to use (or AUTOSELECT) -+ * @param[in] key_type the type of the key -+ * @param[in] apqns a zero terminated array of pointers to APQN-strings, -+ * or NULL for AUTOSELECT - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 on success, a negative errno in case of an error - */ - int generate_secure_key_random(int pkey_fd, const char *keyfile, -- size_t keybits, bool xts, u16 card, u16 domain, -- bool verbose) -+ size_t keybits, bool xts, const char *key_type, -+ const char **apqns, bool verbose) - { -- struct pkey_genseck gensec; -- size_t secure_key_size; -- u8 *secure_key; -+ struct pkey_genseck2 genseck2; -+ size_t secure_key_size, size; -+ u8 *secure_key = NULL; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); - util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); -+ util_assert(key_type != NULL, "Internal error: key_type is NULL"); - - if (keybits == 0) - keybits = DEFAULT_KEYBITS; - -- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); -- secure_key = util_malloc(secure_key_size); -+ pr_verbose(verbose, "Generate secure key by random"); - -- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); -+ memset(&genseck2, 0, sizeof(genseck2)); - -- gensec.cardnr = card; -- gensec.domain = domain; -- switch (keybits) { -- case 128: -- gensec.keytype = PKEY_KEYTYPE_AES_128; -- break; -- case 192: -- if (xts) { -- warnx("Invalid value for '--keybits'|'-c' " -- "for XTS: '%lu'", keybits); -- rc = -EINVAL; -- goto out; -- } -- gensec.keytype = PKEY_KEYTYPE_AES_192; -- break; -- case 256: -- gensec.keytype = PKEY_KEYTYPE_AES_256; -- break; -- default: -+ genseck2.type = key_type_to_pkey_type(key_type); -+ if (genseck2.type == 0) { -+ warnx("Key-type not supported; %s", key_type); -+ return -ENOTSUP; -+ } -+ -+ genseck2.size = keybits_to_keysize(keybits); -+ if (genseck2.size == 0) { - warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits); -- rc = -EINVAL; -- goto out; -+ return -EINVAL; -+ } -+ if (keybits == 192 && xts) { -+ warnx("Invalid value for '--keybits'|'-c' " -+ "for XTS: '%lu'", keybits); -+ return -EINVAL; - } - -- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); -- if (rc < 0) { -- rc = -errno; -- warnx("Failed to generate a secure key: %s", strerror(errno)); -- warnx("Make sure that all available CCA crypto adapters are " -- "setup with the same master key"); -- goto out; -+ rc = build_apqn_list_for_key_type(pkey_fd, genseck2.type, apqns, -+ &genseck2.apqns, -+ &genseck2.apqn_entries, verbose); -+ if (rc != 0) { -+ if (rc == -ENODEV || rc == -ENOTSUP) -+ warnx("No APQN is available that can generate a secure " -+ "key of type %s", key_type); -+ else -+ warnx("Failed to build a list of APQNs that can " -+ "generate a secure key of type %s: %s", key_type, -+ strerror(-rc)); -+ return rc; - } - -- memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE); -+ size = key_size_for_type(genseck2.type); -+ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); -+ secure_key = util_zalloc(secure_key_size); -+ -+ genseck2.key = secure_key; -+ genseck2.keylen = size; -+ -+ rc = pkey_genseck2(pkey_fd, &genseck2, verbose); -+ if (rc != 0) { -+ warnx("Failed to generate a secure key: %s", strerror(-rc)); -+ goto out; -+ } - - if (xts) { -- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); -- if (rc < 0) { -- rc = -errno; -- warnx("Failed to generate a secure key: %s", -- strerror(errno)); -- warnx("Make sure that all available CCA crypto " -- "adapters are setup with the same master key"); -+ free(genseck2.apqns); -+ genseck2.apqns = NULL; -+ genseck2.apqn_entries = 0; -+ -+ /* -+ * Ensure to generate 2nd key with an APQN that has the same -+ * master key that is used by the 1st key. -+ */ -+ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, -+ PKEY_FLAGS_MATCH_CUR_MKVP, apqns, -+ &genseck2.apqns, -+ &genseck2.apqn_entries, verbose); -+ if (rc != 0) { -+ if (rc == -ENODEV || rc == -ENOTSUP) -+ warnx("No APQN is available that can generate " -+ "a secure key of type %s", key_type); -+ else -+ warnx("Failed to build a list of APQNs that " -+ "can generate a secure key of type %s: " -+ "%s", key_type, strerror(-rc)); - goto out; - } - -- memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey, -- SECURE_KEY_SIZE); -+ genseck2.key = secure_key + size; -+ genseck2.keylen = size; -+ -+ rc = pkey_genseck2(pkey_fd, &genseck2, verbose); -+ if (rc != 0) { -+ warnx("Failed to generate a secure key: %s", -+ strerror(-rc)); -+ goto out; -+ } - } - - pr_verbose(verbose, "Successfully generated a secure key"); -@@ -359,6 +998,7 @@ int generate_secure_key_random(int pkey_fd, const char *keyfile, - rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); - - out: -+ free(genseck2.apqns); - free(secure_key); - return rc; - } -@@ -374,89 +1014,125 @@ out: - * determines the keybits. - * @param[in] xts if true an XTS key is generated - * @param[in] clearkeyfile the file name of the clear key to read -- * @param[in] card the card number to use (or AUTOSELECT) -- * @param[in] domain the domain number to use (or AUTOSELECT) -+ * @param[in] key_type the type of the key -+ * @param[in] apqns a zero terminated array of pointers to APQN-strings, -+ * or NULL for AUTOSELECT - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 on success, a negative errno in case of an error - */ - int generate_secure_key_clear(int pkey_fd, const char *keyfile, - size_t keybits, bool xts, -- const char *clearkeyfile, -- u16 card, u16 domain, -- bool verbose) -+ const char *clearkeyfile, const char *key_type, -+ const char **apqns, bool verbose) - { -- struct pkey_clr2seck clr2sec; -+ struct pkey_clr2seck2 clr2seck2; - size_t secure_key_size; - size_t clear_key_size; - u8 *secure_key; - u8 *clear_key; -+ size_t size; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); - util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); - util_assert(clearkeyfile != NULL, - "Internal error: clearkeyfile is NULL"); -+ util_assert(key_type != NULL, "Internal error: key_type is NULL"); - -- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); -- secure_key = util_malloc(secure_key_size); -+ pr_verbose(verbose, "Generate secure key from a clear key"); - - clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size, - verbose); - if (clear_key == NULL) - return -EINVAL; - -- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); -+ memset(&clr2seck2, 0, sizeof(clr2seck2)); - -- clr2sec.cardnr = card; -- clr2sec.domain = domain; -- switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, xts)) { -- case 128: -- clr2sec.keytype = PKEY_KEYTYPE_AES_128; -- break; -- case 192: -- clr2sec.keytype = PKEY_KEYTYPE_AES_192; -- break; -- case 256: -- clr2sec.keytype = PKEY_KEYTYPE_AES_256; -- break; -- default: -+ memcpy(&clr2seck2.clrkey, clear_key, -+ HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); -+ -+ clr2seck2.type = key_type_to_pkey_type(key_type); -+ if (clr2seck2.type == 0) { -+ warnx("Key-type not supported; %s", key_type); -+ return -ENOTSUP; -+ } -+ -+ clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS( -+ clear_key_size * 8, xts)); -+ if (clr2seck2.size == 0) { - warnx("Invalid clear key size: '%lu' bytes", clear_key_size); -- rc = -EINVAL; -- goto out; -+ return -EINVAL; -+ } -+ if (keybits == 192 && xts) { -+ warnx("Invalid clear key size for XTS: '%lu' bytes", -+ clear_key_size); -+ return -EINVAL; - } - -- memcpy(&clr2sec.clrkey, clear_key, -- HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); -+ rc = build_apqn_list_for_key_type(pkey_fd, clr2seck2.type, apqns, -+ &clr2seck2.apqns, -+ &clr2seck2.apqn_entries, verbose); -+ if (rc != 0) { -+ if (rc == -ENODEV || rc == -ENOTSUP) -+ warnx("No APQN is available that can generate a secure " -+ "key of type %s", key_type); -+ else -+ warnx("Failed to build a list of APQNs that can " -+ "generate a secure key of type %s: %s", key_type, -+ strerror(-rc)); -+ return rc; -+ } - -- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); -- if (rc < 0) { -- rc = -errno; -- warnx("Failed to generate a secure key from a " -- "clear key: %s", strerror(errno)); -- warnx("Make sure that all available CCA crypto adapters are " -- "setup with the same master key"); -+ size = key_size_for_type(clr2seck2.type); -+ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); -+ secure_key = util_zalloc(secure_key_size); -+ -+ clr2seck2.key = secure_key; -+ clr2seck2.keylen = size; -+ -+ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); -+ if (rc != 0) { -+ warnx("Failed to generate a secure key: %s", strerror(-rc)); - goto out; - } - -- memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE); -- - if (xts) { -- memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2, -+ free(clr2seck2.apqns); -+ clr2seck2.apqns = NULL; -+ clr2seck2.apqn_entries = 0; -+ -+ memcpy(&clr2seck2.clrkey, clear_key + clear_key_size / 2, - clear_key_size / 2); - -- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); -- if (rc < 0) { -- rc = -errno; -- warnx("Failed to generate a secure key from " -- "a clear key: %s", strerror(errno)); -- warnx("Make sure that all available CCA crypto " -- "adapters are setup with the same master key"); -+ /* -+ * Ensure to generate 2nd key with an APQN that has the same -+ * master key that is used by the 1st key. -+ */ -+ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, -+ PKEY_FLAGS_MATCH_CUR_MKVP, apqns, -+ &clr2seck2.apqns, -+ &clr2seck2.apqn_entries, verbose); -+ if (rc != 0) { -+ if (rc == -ENODEV || rc == -ENOTSUP) -+ warnx("No APQN is available that can generate " -+ "a secure key of type %s", key_type); -+ else -+ warnx("Failed to build a list of APQNs that " -+ "can generate a secure key of type %s: " -+ "%s", key_type, strerror(-rc)); - goto out; - } - -- memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey, -- SECURE_KEY_SIZE); -+ clr2seck2.key = secure_key + size; -+ clr2seck2.keylen = size; -+ -+ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); -+ if (rc != 0) { -+ warnx("Failed to generate a secure key: %s", -+ strerror(-rc)); -+ goto out; -+ } - } - - pr_verbose(verbose, -@@ -465,10 +1141,11 @@ int generate_secure_key_clear(int pkey_fd, const char *keyfile, - rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); - - out: -- memset(&clr2sec, 0, sizeof(clr2sec)); -+ memset(&clr2seck2, 0, sizeof(clr2seck2)); - memset(clear_key, 0, clear_key_size); - free(clear_key); - free(secure_key); -+ free(clr2seck2.apqns); - return rc; - } - -@@ -476,84 +1153,58 @@ out: - * Validates an XTS secure key (the second part) - * - * @param[in] pkey_fd the pkey file descriptor -+ * @param[in] apqn the APQN to verify the key with - * @param[in] secure_key a buffer containing the secure key - * @param[in] secure_key_size the secure key size - * @param[in] part1_keysize the key size of the first key part -- * @param[in] part1_attributes the attributes of the first key part -+ * @param[in] part1_flags the flags of the first key part - * @param[out] clear_key_bitsize on return , the cryptographic size of the - * clear key - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 on success, a negative errno in case of an error - */ --static int validate_secure_xts_key(int pkey_fd, -+static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn, - u8 *secure_key, size_t secure_key_size, -- u16 part1_keysize, u32 part1_attributes, -- size_t *clear_key_bitsize, bool verbose) -+ enum pkey_key_size part1_keysize, -+ u32 part1_flags, size_t *clear_key_bitsize, -+ bool verbose) - { -- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; -- struct pkey_verifykey verifykey; -- struct secaeskeytoken *token2; -+ struct pkey_verifykey2 verifykey2; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); - util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ util_assert(apqn != NULL, "Internal error: apqn is NULL"); - -- /* XTS uses 2 secure key tokens concatenated to each other */ -- token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE); -- -- if (secure_key_size != 2 * SECURE_KEY_SIZE) { -- pr_verbose(verbose, "Size of secure key is too small: " -- "%lu expected %lu", secure_key_size, -- 2 * SECURE_KEY_SIZE); -- return -EINVAL; -- } -- -- if (token->bitsize != token2->bitsize) { -- pr_verbose(verbose, "XTS secure key contains 2 clear keys of " -- "different sizes"); -- return -EINVAL; -- } -- if (token->keysize != token2->keysize) { -- pr_verbose(verbose, "XTS secure key contains 2 keys of " -- "different sizes"); -- return -EINVAL; -- } -- if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) { -- pr_verbose(verbose, "XTS secure key contains 2 keys using " -- "different CCA master keys"); -- return -EINVAL; -- } -- -- memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey)); -+ memset(&verifykey2, 0, sizeof(verifykey2)); -+ verifykey2.key = secure_key + (secure_key_size / 2); -+ verifykey2.keylen = secure_key_size / 2; -+ verifykey2.cardnr = apqn->card; -+ verifykey2.domain = apqn->domain; - -- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); -+ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); - if (rc < 0) { -- rc = -errno; -- pr_verbose(verbose, "Failed to validate a secure key: %s", -- strerror(-rc)); -+ pr_verbose(verbose, "Failed to validate the 2nd part of the " -+ "XTS secure key on APQN %02x.%04x: %s", apqn->card, -+ apqn->domain, strerror(-rc)); - return rc; - } - -- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { -- pr_verbose(verbose, "Secure key is not an AES key"); -- return -EINVAL; -- } -- -- if (verifykey.keysize != part1_keysize) { -+ if (verifykey2.size != part1_keysize) { - pr_verbose(verbose, "XTS secure key contains 2 keys using " - "different key sizes"); - return -EINVAL; - } - -- if (verifykey.attributes != part1_attributes) { -+ if (verifykey2.flags != part1_flags) { - pr_verbose(verbose, "XTS secure key contains 2 keys using " -- "different attributes"); -+ "different master keys"); - return -EINVAL; - } - -- if (clear_key_bitsize) -- *clear_key_bitsize += verifykey.keysize; -+ if (clear_key_bitsize && verifykey2.size != PKEY_SIZE_UNKNOWN) -+ *clear_key_bitsize += verifykey2.size; - - return 0; - } -@@ -568,6 +1219,8 @@ static int validate_secure_xts_key(int pkey_fd, - * clear key - * @param[out] is_old_mk in return set to 1 to indicate if the secure key - * is currently enciphered by the OLD CCA master key -+ * @param[in] apqns a zero terminated array of pointers to APQN-strings, -+ * or NULL for AUTOSELECT - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 on success, a negative errno in case of an error -@@ -575,59 +1228,89 @@ static int validate_secure_xts_key(int pkey_fd, - int validate_secure_key(int pkey_fd, - u8 *secure_key, size_t secure_key_size, - size_t *clear_key_bitsize, int *is_old_mk, -- bool verbose) -+ const char **apqns, bool verbose) - { -- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; -- struct pkey_verifykey verifykey; -+ struct pkey_verifykey2 verifykey2; -+ struct pkey_apqn *list = NULL; -+ u32 i, list_entries = 0; -+ bool xts, valid; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); - util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); - -- if (secure_key_size < SECURE_KEY_SIZE) { -- pr_verbose(verbose, "Size of secure key is too small: " -- "%lu expected %lu", secure_key_size, -- SECURE_KEY_SIZE); -- return -EINVAL; -- } -- -- memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey)); -+ xts = is_xts_key(secure_key, secure_key_size); - -- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); -- if (rc < 0) { -- rc = -errno; -- pr_verbose(verbose, "Failed to validate a secure key: %s", -- strerror(-rc)); -+ rc = build_apqn_list_for_key(pkey_fd, secure_key, -+ HALF_KEYSIZE_FOR_XTS(secure_key_size, xts), -+ PKEY_FLAGS_MATCH_CUR_MKVP | -+ PKEY_FLAGS_MATCH_ALT_MKVP, -+ apqns, &list, &list_entries, verbose); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to build a list of APQNs that can " -+ "validate this secure key: %s", strerror(-rc)); - return rc; - } - -- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { -- pr_verbose(verbose, "Secure key is not an AES key"); -- return -EINVAL; -- } -+ if (is_old_mk != NULL) -+ *is_old_mk = true; -+ if (clear_key_bitsize != NULL) -+ *clear_key_bitsize = 0; - -- if (clear_key_bitsize) -- *clear_key_bitsize = verifykey.keysize; -+ valid = false; -+ for (i = 0; i < list_entries; i++) { -+ memset(&verifykey2, 0, sizeof(verifykey2)); -+ verifykey2.key = secure_key; -+ verifykey2.keylen = HALF_KEYSIZE_FOR_XTS(secure_key_size, xts); -+ verifykey2.cardnr = list[i].card; -+ verifykey2.domain = list[i].domain; - -- /* XTS uses 2 secure key tokens concatenated to each other */ -- if (secure_key_size > SECURE_KEY_SIZE) { -- rc = validate_secure_xts_key(pkey_fd, -- secure_key, secure_key_size, -- verifykey.keysize, -- verifykey.attributes, -- clear_key_bitsize, -- verbose); -- if (rc != 0) -- return rc; -+ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); -+ if (rc < 0) { -+ pr_verbose(verbose, "Failed to validate the secure key " -+ "on APQN %02x.%04x: %s", list[i].card, -+ list[i].domain, strerror(-rc)); -+ continue; -+ } -+ -+ if (is_xts_key(secure_key, secure_key_size)) { -+ rc = validate_secure_xts_key(pkey_fd, &list[i], -+ secure_key, -+ secure_key_size, -+ verifykey2.size, -+ verifykey2.flags, -+ clear_key_bitsize, -+ verbose); -+ if (rc != 0) -+ continue; -+ -+ } -+ -+ valid = true; -+ -+ if (clear_key_bitsize) { -+ if (verifykey2.size != PKEY_SIZE_UNKNOWN) -+ *clear_key_bitsize += verifykey2.size; -+ clear_key_bitsize = NULL; /* Set it only once */ -+ } -+ -+ /* -+ * If at least one of the APQNs have a matching current MK, -+ * then don't report OLD, even if some match the old MK. -+ */ -+ if (is_old_mk && -+ (verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP)) -+ *is_old_mk = false; - } - -- if (is_old_mk) -- *is_old_mk = (verifykey.attributes & -- PKEY_VERIFY_ATTR_OLD_MKVP) != 0; -+ if (!valid) -+ return -ENODEV; - - pr_verbose(verbose, "Secure key validation completed successfully"); - -- return 0; -+ if (list != NULL) -+ free(list); -+ return rc; - } - - /** -@@ -642,7 +1325,7 @@ int validate_secure_key(int pkey_fd, - * - * @returns 0 on success, a negative errno in case of an error - */ --int generate_key_verification_pattern(const char *key, size_t key_size, -+int generate_key_verification_pattern(const u8 *key, size_t key_size, - char *vp, size_t vp_len, bool verbose) - { - int tfmfd = -1, opfd = -1, rc = 0; -@@ -677,7 +1360,7 @@ int generate_key_verification_pattern(const char *key, size_t key_size, - } - - snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)", -- key_size > SECURE_KEY_SIZE ? "xts" : "cbc"); -+ is_xts_key(key, key_size) ? "xts" : "cbc"); - - tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); - if (tfmfd < 0) { -@@ -770,23 +1453,337 @@ out: - return rc; - } - --int get_master_key_verification_pattern(const u8 *secure_key, -- size_t secure_key_size, u64 *mkvp, -- bool verbose) -+int get_master_key_verification_pattern(const u8 *key, size_t key_size, -+ u64 *mkvp, bool UNUSED(verbose)) - { -- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; -+ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; -+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; - -- util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ util_assert(key != NULL, "Internal error: secure_key is NULL"); - util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); - -- if (secure_key_size < SECURE_KEY_SIZE) { -- pr_verbose(verbose, "Size of secure key is too small: " -- "%lu expected %lu", secure_key_size, -- SECURE_KEY_SIZE); -+ if (is_cca_aes_data_key(key, key_size)) -+ *mkvp = datakey->mkvp; -+ else if (is_cca_aes_cipher_key(key, key_size)) -+ memcpy(mkvp, cipherkey->kvp, sizeof(*mkvp)); -+ else - return -EINVAL; -+ -+ return 0; -+} -+ -+/** -+ * Check if the specified key is a CCA AESDATA key token. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an CCA AESDATA token type -+ */ -+bool is_cca_aes_data_key(const u8 *key, size_t key_size) -+{ -+ struct tokenheader *hdr = (struct tokenheader *)key; -+ -+ if (key == NULL || key_size < AESDATA_KEY_SIZE) -+ return false; -+ -+ if (hdr->type != TOKEN_TYPE_CCA_INTERNAL) -+ return false; -+ if (hdr->version != TOKEN_VERSION_AESDATA) -+ return false; -+ -+ return true; -+} -+ -+/** -+ * Check if the specified key is a CCA AESCIPHER key token. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an CCA AESCIPHER token type -+ */ -+bool is_cca_aes_cipher_key(const u8 *key, size_t key_size) -+{ -+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; -+ -+ if (key == NULL || key_size < AESCIPHER_KEY_SIZE) -+ return false; -+ -+ if (cipherkey->type != TOKEN_TYPE_CCA_INTERNAL) -+ return false; -+ if (cipherkey->version != TOKEN_VERSION_AESCIPHER) -+ return false; -+ if (cipherkey->length > key_size) -+ return false; -+ -+ if (cipherkey->kms != 0x03) /* key wrapped by master key */ -+ return false; -+ if (cipherkey->kwm != 0x02) /* key wrapped using AESKW */ -+ return false; -+ if (cipherkey->pfv != 0x00 && cipherkey->pfv != 0x01) /* V0 or V1 */ -+ return false; -+ if (cipherkey->adv != 0x01) /* Should have ass. data sect. version 1 */ -+ return false; -+ if (cipherkey->at != 0x02) /* Algorithm: AES */ -+ return false; -+ if (cipherkey->kt != 0x0001) /* Key type: CIPHER */ -+ return false; -+ if (cipherkey->adl != 26) /* Ass. data section length should be 26 */ -+ return false; -+ if (cipherkey->kll != 0) /* Should have no key label */ -+ return false; -+ if (cipherkey->eadl != 0) /* Should have no ext associated data */ -+ return false; -+ if (cipherkey->uadl != 0) /* Should have no user associated data */ -+ return false; -+ if (cipherkey->kufc != 2) /* Should have 2 KUFs */ -+ return false; -+ if (cipherkey->kmfc != 3) /* Should have 3 KMFs */ -+ return false; -+ -+ return true; -+} -+ -+/** -+ * Check if the specified key is an XTS type key -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an XTS key type -+ */ -+bool is_xts_key(const u8 *key, size_t key_size) -+{ -+ if (is_cca_aes_data_key(key, key_size)) { -+ if (key_size == 2 * AESDATA_KEY_SIZE && -+ is_cca_aes_data_key(key + AESDATA_KEY_SIZE, -+ key_size - AESDATA_KEY_SIZE)) -+ return true; -+ } else if (is_cca_aes_cipher_key(key, key_size)) { -+ if (key_size == 2 * AESCIPHER_KEY_SIZE && -+ is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE, -+ key_size - AESCIPHER_KEY_SIZE)) -+ return true; - } - -- *mkvp = token->mkvp; -+ return false; -+} -+ -+/** -+ * Gets the size in bits of the effective key of the specified secure key -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * @param[out] bitsize On return, contains the size in bits of the key. -+ * If the key size can not be determined, then 0 is -+ * passed back as bitsize. -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) -+{ -+ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; -+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; -+ -+ util_assert(bitsize != NULL, "Internal error: bitsize is NULL"); -+ -+ if (is_cca_aes_data_key(key, key_size)) { -+ *bitsize = datakey->bitsize; -+ if (key_size == 2 * AESDATA_KEY_SIZE) { -+ datakey = (struct aesdatakeytoken *)key + -+ AESDATA_KEY_SIZE; -+ *bitsize += datakey->bitsize; -+ } -+ } else if (is_cca_aes_cipher_key(key, key_size)) { -+ if (cipherkey->pfv == 0x00) /* V0 payload */ -+ *bitsize = cipherkey->pl - 384; -+ else -+ *bitsize = 0; /* Unknown */ -+ if (key_size > cipherkey->length) { -+ cipherkey = (struct aescipherkeytoken *)key + -+ cipherkey->length; -+ if (cipherkey->pfv == 0x00) /* V0 payload */ -+ *bitsize += cipherkey->pl - 384; -+ } -+ } else { -+ return -EINVAL; -+ } - - return 0; - } -+ -+/** -+ * Returns the type of the key -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns a static string on success, NULL in case of an error -+ */ -+const char *get_key_type(const u8 *key, size_t key_size) -+{ -+ if (is_cca_aes_data_key(key, key_size)) -+ return KEY_TYPE_CCA_AESDATA; -+ if (is_cca_aes_cipher_key(key, key_size)) -+ return KEY_TYPE_CCA_AESCIPHER; -+ -+ return NULL; -+} -+ -+/** -+ * Returns the minimum card level for a specific key type -+ * -+ * @param[in] key_type the type of the key -+ * -+ * @returns the minimum card level, or -1 for unknown key types -+ */ -+int get_min_card_level_for_keytype(const char *key_type) -+{ -+ if (key_type == NULL) -+ return -1; -+ -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) -+ return 3; -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) -+ return 6; -+ -+ return -1; -+} -+ -+/** -+ * Performs extended checks on an AES CIPHER key. It checks the key usage -+ * fields (KUFs) and key management fields (KMFs) of the key. The function -+ * returns -EINVAL and issues warning messages if a mismatch is detected. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+int check_aes_cipher_key(const u8 *key, size_t key_size) -+{ -+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; -+ bool mismatch = false; -+ -+ if (!is_cca_aes_cipher_key(key, key_size)) { -+ warnx("The key is not of type '"KEY_TYPE_CCA_AESCIPHER"'"); -+ return -EINVAL; -+ } -+ -+ if ((cipherkey->kuf1 & 0x8000) == 0) { -+ printf("WARNING: The secure key can not be used for " -+ "encryption\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kuf1 & 0x4000) == 0) { -+ printf("WARNING: The secure key can not be used for " -+ "decryption\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kuf1 & 0x2000) { -+ printf("INFO: The secure key can be used for data translate\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kuf1 & 0x1000) { -+ printf("WARNING: The secure key can only be used in UDXs\n"); -+ mismatch = true; -+ } -+ -+ if (cipherkey->kmf1 & 0x8000) { -+ printf("WARNING: The secure key can be exported using a " -+ "symmetric key\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf1 & 0x4000) { -+ printf("WARNING: The secure key can be exported using an " -+ "unauthenticated asymmetric key\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf1 & 0x2000) { -+ printf("WARNING: The secure key can be exported using an " -+ "authenticated asymmetric key\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf1 & 0x1000) { -+ printf("WARNING: The secure key can be exported using a RAW " -+ "key\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf1 & 0x0800) == 0) { -+ printf("WARNING: The secure key can not be transformed into a " -+ "CPACF protected key\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf1 & 0x0080) == 0) { -+ printf("WARNING: The secure key can be exported using a DES " -+ "key\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf1 & 0x0040) == 0) { -+ printf("WARNING: The secure key can be exported using an AES " -+ "key\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf1 & 0x0008) == 0) { -+ printf("WARNING: The secure key can be exported using an RSA " -+ "key\n"); -+ mismatch = true; -+ } -+ -+ if (cipherkey->kmf2 & 0xC000) { -+ printf("WARNING: The secure key is incomplete\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf2 & 0x0010) { -+ printf("WARNING: The secure key was previously encrypted with " -+ "an untrusted KEK\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf2 & 0x0008) { -+ printf("WARNING: The secure key was previously in a format " -+ "without type or usage attributes\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf2 & 0x0004) { -+ printf("WARNING: The secure key was previously encrypted with " -+ "a key weaker than itself\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf2 & 0x0002) { -+ printf("WARNING: The secure key was previously in a non-CCA " -+ "format\n"); -+ mismatch = true; -+ } -+ if (cipherkey->kmf2 & 0x0001) { -+ printf("WARNING: The secure key was previously encrypted in " -+ "ECB mode\n"); -+ mismatch = true; -+ } -+ -+ if ((cipherkey->kmf3 & 0xFF00) == 0x0000 || -+ (cipherkey->kmf3 & 0x00FF) == 0x0000) { -+ printf("WARNING: The secure key was created by an unknown " -+ "method\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf3 & 0xFF00) == 0x0400 || -+ (cipherkey->kmf3 & 0x00FF) == 0x0004) { -+ printf("WARNING: The secure key was created from cleartext key " -+ "components\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf3 & 0xFF00) == 0x0500 || -+ (cipherkey->kmf3 & 0x00FF) == 0x0005) { -+ printf("WARNING: The secure key was entered as a cleartext key " -+ "value\n"); -+ mismatch = true; -+ } -+ if ((cipherkey->kmf3 & 0x00FF) == 0x0012) { -+ printf("WARNING: The secure key was converted from a CCA " -+ "key-token that had no export control attributes\n"); -+ mismatch = true; -+ } -+ -+ return mismatch ? -EINVAL : 0; -+} -diff --git a/zkey/pkey.h b/zkey/pkey.h -index c0aac2e..3dfd588 100644 ---- a/zkey/pkey.h -+++ b/zkey/pkey.h -@@ -18,10 +18,23 @@ - /* - * Definitions for the /dev/pkey kernel module interface - */ --struct secaeskeytoken { -- u8 type; /* 0x01 for internal key token */ -+struct tokenheader { -+ u8 type; - u8 res0[3]; -- u8 version; /* should be 0x04 */ -+ u8 version; -+ u8 res1[3]; -+} __packed; -+ -+#define TOKEN_TYPE_NON_CCA 0x00 -+#define TOKEN_TYPE_CCA_INTERNAL 0x01 -+ -+#define TOKEN_VERSION_AESDATA 0x04 -+#define TOKEN_VERSION_AESCIPHER 0x05 -+ -+struct aesdatakeytoken { -+ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ -+ u8 res0[3]; -+ u8 version; /* should be TOKEN_VERSION_AESDATA (0x04) */ - u8 res1[1]; - u8 flag; /* key flags */ - u8 res2[1]; -@@ -33,22 +46,60 @@ struct secaeskeytoken { - u8 tvv[4]; /* token validation value */ - } __packed; - --#define SECURE_KEY_SIZE sizeof(struct secaeskeytoken) -+struct aescipherkeytoken { -+ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ -+ u8 res0; -+ u16 length; /* length of token */ -+ u8 version; /* should be TOKEN_VERSION_CIPHER (0x05) */ -+ u8 res1[3]; -+ u8 kms; /* key material state, should be 0x03 */ -+ u8 kvptype; /* key verification pattern type */ -+ u8 kvp[16]; /* key verification pattern */ -+ u8 kwm; /* key wrapping method, should be 0x02 */ -+ u8 kwh; /* key wrapping hash algorithm */ -+ u8 pfv; /* payload format version, should be 0x00*/ -+ u8 res2; -+ u8 adv; /* associated data section version */ -+ u8 res3; -+ u16 adl; /* associated data length */ -+ u8 kll; /* length of optional key label */ -+ u8 eadl; /* extended associated data length */ -+ u8 uadl; /* user associated data length */ -+ u8 res4; -+ u16 pl; /* payload bit length */ -+ u8 res5; -+ u8 at; /* algorithm type, should be 0x02 (AES) */ -+ u16 kt; /* key type, should be 0x001 (CIPHER) */ -+ u8 kufc; /* key usage field count */ -+ u16 kuf1; /* key usage field 1 */ -+ u16 kuf2; /* key usage field 2 */ -+ u8 kmfc; /* key management field count */ -+ u16 kmf1; /* key management field 1 */ -+ u16 kmf2; /* key management field 2 */ -+ u16 kmf3; /* key management field 3 */ -+ u8 varpart[80]; /* variable part */ -+} __packed; -+ -+#define AESDATA_KEY_SIZE sizeof(struct aesdatakeytoken) -+#define AESCIPHER_KEY_SIZE sizeof(struct aescipherkeytoken) -+ -+#define MAX_SECURE_KEY_SIZE MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) -+#define MIN_SECURE_KEY_SIZE MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) - - struct pkey_seckey { -- u8 seckey[SECURE_KEY_SIZE]; /* the secure key blob */ -+ u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */ - }; - - struct pkey_clrkey { - u8 clrkey[32]; /* 16, 24, or 32 byte clear key value */ - }; - --#define PKEY_IOCTL_MAGIC 'p' --#define AUTOSELECT 0xFFFF --#define PKEYDEVICE "/dev/pkey" --#define PKEY_KEYTYPE_AES_128 1 --#define PKEY_KEYTYPE_AES_192 2 --#define PKEY_KEYTYPE_AES_256 3 -+#define PKEY_IOCTL_MAGIC 'p' -+#define AUTOSELECT 0xFFFF -+#define PKEYDEVICE "/dev/pkey" -+#define PKEY_KEYTYPE_AES_128 1 -+#define PKEY_KEYTYPE_AES_192 2 -+#define PKEY_KEYTYPE_AES_256 3 - - struct pkey_genseck { - u16 cardnr; /* in: card to use or FFFF for any */ -@@ -82,6 +133,100 @@ struct pkey_verifykey { - - #define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey) - -+enum pkey_key_type { -+ PKEY_TYPE_CCA_DATA = (u32) 1, -+ PKEY_TYPE_CCA_CIPHER = (u32) 2, -+}; -+ -+enum pkey_key_size { -+ PKEY_SIZE_AES_128 = (u32) 128, -+ PKEY_SIZE_AES_192 = (u32) 192, -+ PKEY_SIZE_AES_256 = (u32) 256, -+ PKEY_SIZE_UNKNOWN = (u32) 0xFFFFFFFF, -+}; -+ -+#define PKEY_FLAGS_MATCH_CUR_MKVP 0x00000002 -+#define PKEY_FLAGS_MATCH_ALT_MKVP 0x00000004 -+ -+#define PKEY_KEYGEN_XPRT_SYM 0x00008000 -+#define PKEY_KEYGEN_XPRT_UASY 0x00004000 -+#define PKEY_KEYGEN_XPRT_AASY 0x00002000 -+#define PKEY_KEYGEN_XPRT_RAW 0x00001000 -+#define PKEY_KEYGEN_XPRT_CPAC 0x00000800 -+#define PKEY_KEYGEN_XPRT_DES 0x00000080 -+#define PKEY_KEYGEN_XPRT_AES 0x00000040 -+#define PKEY_KEYGEN_XPRT_RSA 0x00000008 -+ -+struct pkey_apqn { -+ u16 card; -+ u16 domain; -+}; -+ -+struct pkey_genseck2 { -+ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */ -+ u32 apqn_entries; /* in: # of apqn target list entries */ -+ enum pkey_key_type type; /* in: key type to generate */ -+ enum pkey_key_size size; /* in: key size to generate */ -+ u32 keygenflags; /* in: key generation flags */ -+ u8 *key; /* in: pointer to key blob buffer */ -+ u32 keylen; /* in: available key blob buffer size */ -+ /* out: actual key blob size */ -+}; -+ -+#define PKEY_GENSECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x11, struct pkey_genseck2) -+ -+struct pkey_clr2seck2 { -+ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */ -+ u32 apqn_entries; /* in: # of apqn target list entries */ -+ enum pkey_key_type type; /* in: key type to generate */ -+ enum pkey_key_size size; /* in: key size to generate */ -+ u32 keygenflags; /* in: key generation flags */ -+ struct pkey_clrkey clrkey; /* in: the clear key value */ -+ u8 *key; /* in: pointer to key blob buffer */ -+ u32 keylen; /* in: available key blob buffer size */ -+ /* out: actual key blob size */ -+}; -+ -+#define PKEY_CLR2SECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x12, struct pkey_clr2seck2) -+ -+struct pkey_verifykey2 { -+ u8 *key; /* in: pointer to key blob */ -+ u32 keylen; /* in: key blob size */ -+ u16 cardnr; /* in/out: card number */ -+ u16 domain; /* in/out: domain number */ -+ enum pkey_key_type type; /* out: the key type */ -+ enum pkey_key_size size; /* out: the key size */ -+ u32 flags; /* out: additional key info flags */ -+}; -+ -+#define PKEY_VERIFYKEY2 _IOWR(PKEY_IOCTL_MAGIC, 0x17, struct pkey_verifykey2) -+ -+struct pkey_apqns4key { -+ u8 *key; /* in: pointer to key blob */ -+ u32 keylen; /* in: key blob size */ -+ u32 flags; /* in: match controlling flags */ -+ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/ -+ u32 apqn_entries; /* in: max # of apqn entries in list */ -+ /* out: # apqns stored into the list */ -+}; -+ -+#define PKEY_APQNS4K _IOWR(PKEY_IOCTL_MAGIC, 0x1B, struct pkey_apqns4key) -+ -+struct pkey_apqns4keytype { -+ enum pkey_key_type type; /* in: key type */ -+ u8 cur_mkvp[32]; /* in: current mkvp */ -+ u8 alt_mkvp[32]; /* in: alternate mkvp */ -+ u32 flags; /* in: match controlling flags */ -+ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/ -+ u32 apqn_entries; /* in: max # of apqn entries in list */ -+ /* out: # apqns stored into the list */ -+}; -+ -+#define PKEY_APQNS4KT _IOWR(PKEY_IOCTL_MAGIC, 0x1C, struct pkey_apqns4keytype) -+ -+#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA" -+#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER" -+ - #define PAES_BLOCK_SIZE 16 - #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) - #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) -@@ -89,14 +234,13 @@ struct pkey_verifykey { - int open_pkey_device(bool verbose); - - int generate_secure_key_random(int pkey_fd, const char *keyfile, -- size_t keybits, bool xts, u16 card, u16 domain, -- bool verbose); -+ size_t keybits, bool xts, const char *key_type, -+ const char **apqns, bool verbose); - - int generate_secure_key_clear(int pkey_fd, const char *keyfile, - size_t keybits, bool xts, -- const char *clearkeyfile, -- u16 card, u16 domain, -- bool verbose); -+ const char *clearkeyfile, const char *key_type, -+ const char **apqns, bool verbose); - - u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, - bool verbose); -@@ -107,13 +251,20 @@ int write_secure_key(const char *keyfile, const u8 *secure_key, - int validate_secure_key(int pkey_fd, - u8 *secure_key, size_t secure_key_size, - size_t *clear_key_bitsize, int *is_old_mk, -- bool verbose); -+ const char **apqns, bool verbose); - --int generate_key_verification_pattern(const char *key, size_t key_size, -+int generate_key_verification_pattern(const u8 *key, size_t key_size, - char *vp, size_t vp_len, bool verbose); - --int get_master_key_verification_pattern(const u8 *secure_key, -- size_t secure_key_size, u64 *mkvp, -- bool verbose); -+int get_master_key_verification_pattern(const u8 *key, size_t key_size, -+ u64 *mkvp, bool verbose); -+ -+bool is_cca_aes_data_key(const u8 *key, size_t key_size); -+bool is_cca_aes_cipher_key(const u8 *key, size_t key_size); -+bool is_xts_key(const u8 *key, size_t key_size); -+int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize); -+const char *get_key_type(const u8 *key, size_t key_size); -+int get_min_card_level_for_keytype(const char *key_type); -+int check_aes_cipher_key(const u8 *key, size_t key_size); - - #endif -diff --git a/zkey/utils.c b/zkey/utils.c -index 9dc4af7..e70ebcd 100644 ---- a/zkey/utils.c -+++ b/zkey/utils.c -@@ -118,6 +118,49 @@ out: - return rc; - } - -+/** -+ * Returns the level of the card. For a CEX3C 3 is returned, for a CEX4C 4, -+ * and so on. -+ * -+ * @param[in] card card number -+ * -+ * @returns The card level, or -1 of the level can not be determined. -+ */ -+int sysfs_get_card_level(int card) -+{ -+ char *dev_path; -+ char type[20]; -+ int rc; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -+ if (!util_path_is_dir(dev_path)) { -+ rc = -1; -+ goto out; -+ } -+ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { -+ rc = -1; -+ goto out; -+ } -+ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { -+ rc = -1; -+ goto out; -+ } -+ if (type[4] != 'C') { -+ rc = -1; -+ goto out; -+ } -+ if (type[3] < '1' || type[3] > '9') { -+ rc = -1; -+ goto out; -+ } -+ -+ rc = type[3] - '0'; -+ -+out: -+ free(dev_path); -+ return rc; -+} -+ - /** - * Gets the 8 character ASCII serial number string of an card from the sysfs. - * -@@ -436,12 +479,14 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) - { - struct print_apqn_info *info = (struct print_apqn_info *)handler_data; - struct mk_info mk_info; -- int rc; -+ int rc, level; - - rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); - if (rc == -ENOTSUP) - return rc; - -+ level = sysfs_get_card_level(card); -+ - util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain); - - if (rc == 0) { -@@ -470,6 +515,11 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) - util_rec_set(info->rec, "OLD", "?"); - } - -+ if (level > 0) -+ util_rec_set(info->rec, "TYPE", "CEX%dC", level); -+ else -+ util_rec_set(info->rec, "TYPE", "?"); -+ - util_rec_print(info->rec); - - return 0; -@@ -499,6 +549,7 @@ int print_mk_info(const char *apqns, bool verbose) - util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK"); - util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK"); - util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK"); -+ util_rec_def(info.rec, "TYPE", UTIL_REC_ALIGN_LEFT, 6, "TYPE"); - util_rec_print_hdr(info.rec); - - rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose); -@@ -511,6 +562,7 @@ struct cross_check_info { - u64 mkvp; - u64 new_mkvp; - bool key_mkvp; -+ int min_level; - u32 num_cur_match; - u32 num_old_match; - u32 num_new_match; -@@ -525,7 +577,7 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - struct cross_check_info *info = (struct cross_check_info *)handler_data; - struct mk_info mk_info; - char temp[200]; -- int rc; -+ int rc, level; - - rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); - if (rc == -ENODEV) { -@@ -539,6 +591,19 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - - info->num_checked++; - -+ if (info->min_level >= 0) { -+ level = sysfs_get_card_level(card); -+ -+ if (level < info->min_level) { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The card level " -+ "is less than CEX%dC.", card, domain, -+ info->min_level); -+ util_print_indented(temp, 0); -+ } -+ } -+ - if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " -@@ -662,6 +727,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - * @param[in] mkvp The master key verification pattern of a secure key. - * If this is all zero, then the master keys are not - * matched against it. -+ * @param[in] min_level The minimum card level required. If min_level is -1 then -+ * the card level is not checked. - * @param[in] print_mks if true, then a the full master key info of all - * specified APQns is printed, in case of a mismatch. - * @param[in] verbose if true, verbose messages are printed -@@ -671,7 +738,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - * -ENOTSUP is returned when the mkvps sysfs attribute is not - * available, because the zcrypt kernel module is on an older level. - */ --int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) -+int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, -+ bool print_mks, bool verbose) - { - struct cross_check_info info; - char temp[200]; -@@ -680,10 +748,12 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) - memset(&info, 0, sizeof(info)); - info.key_mkvp = mkvp != 0; - info.mkvp = mkvp; -+ info.min_level = min_level; - info.verbose = verbose; - -- pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s", -- mkvp, apqns != NULL ? apqns : "ANY"); -+ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx and " -+ "min-level %d: %s", mkvp, min_level, -+ apqns != NULL ? apqns : "ANY"); - - rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose); - if (rc != 0) -@@ -723,3 +793,27 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose) - - return rc; - } -+ -+/* -+ * Prompts for yes or no. Returns true if 'y' or 'yes' was entered. -+ * -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns true if 'y' or 'yes' was entered (case insensitive). Returns false -+ * otherwise. -+ */ -+bool prompt_for_yes(bool verbose) -+{ -+ char str[20]; -+ -+ if (fgets(str, sizeof(str), stdin) == NULL) -+ return false; -+ -+ if (str[strlen(str) - 1] == '\n') -+ str[strlen(str) - 1] = '\0'; -+ pr_verbose(verbose, "Prompt reply: '%s'", str); -+ if (strcasecmp(str, "y") == 0 || strcasecmp(str, "yes") == 0) -+ return true; -+ -+ return false; -+} -diff --git a/zkey/utils.h b/zkey/utils.h -index c4dfb2b..2a915eb 100644 ---- a/zkey/utils.h -+++ b/zkey/utils.h -@@ -18,6 +18,8 @@ int sysfs_is_card_online(int card); - - int sysfs_is_apqn_online(int card, int domain); - -+int sysfs_get_card_level(int card); -+ - int sysfs_get_serialnr(int card, char serialnr[9], bool verbose); - - #define MK_STATE_EMPTY 0 -@@ -48,7 +50,9 @@ int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, - - int print_mk_info(const char *apqns, bool verbose); - --int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, -- bool verbose); -+int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, -+ bool print_mks, bool verbose); -+ -+bool prompt_for_yes(bool verbose); - - #endif -diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c -index c683fd4..7ba2842 100644 ---- a/zkey/zkey-cryptsetup.c -+++ b/zkey/zkey-cryptsetup.c -@@ -1329,22 +1329,25 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key, - return rc; - } - --static int check_keysize_and_cipher_mode(size_t keysize) -+static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize) - { -- if (keysize == 0) { -+ if (keysize < MIN_SECURE_KEY_SIZE || -+ keysize > 2 * MAX_SECURE_KEY_SIZE) { - warnx("Invalid volume key size"); - return -EINVAL; - } - - if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) { -- if (keysize != 2 * SECURE_KEY_SIZE) { -+ if (keysize < 2 * MIN_SECURE_KEY_SIZE || -+ (key != NULL && !is_xts_key(key, keysize))) { - warnx("The volume key size %lu is not valid for the " - "cipher mode '%s'", keysize, - crypt_get_cipher_mode(g.cd)); - return -EINVAL; - } - } else { -- if (keysize != SECURE_KEY_SIZE) { -+ if (keysize > MAX_SECURE_KEY_SIZE || -+ (key != NULL && is_xts_key(key, keysize))) { - warnx("The volume key size %lu is not valid for the " - "cipher mode '%s'", keysize, - crypt_get_cipher_mode(g.cd)); -@@ -1377,7 +1380,7 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize, - vkeysize = crypt_get_volume_key_size(g.cd); - pr_verbose("Volume key size: %lu", vkeysize); - -- rc = check_keysize_and_cipher_mode(vkeysize); -+ rc = check_keysize_and_cipher_mode(NULL, vkeysize); - if (rc != 0) - return rc; - -@@ -1489,7 +1492,7 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize, - keyslot = rc; - - rc = validate_secure_key(g.pkey_fd, (u8 *)vkey, vkeysize, clear_keysize, -- &is_old, g.verbose); -+ &is_old, NULL, g.verbose); - if (rc != 0) { - if (invalid_msg != NULL) - warnx("%s", invalid_msg); -@@ -1571,7 +1574,7 @@ static int reencipher_prepare(int token) - if (rc != 0) - goto out; - -- rc = generate_key_verification_pattern(key, keysize, -+ rc = generate_key_verification_pattern((u8 *)key, keysize, - reenc_tok.verification_pattern, - sizeof(reenc_tok.verification_pattern), - g.verbose); -@@ -1851,8 +1854,8 @@ static int reencipher_complete(int token) - - } - -- rc = generate_key_verification_pattern(key, keysize, vp, sizeof(vp), -- g.verbose); -+ rc = generate_key_verification_pattern((u8 *)key, keysize, vp, -+ sizeof(vp), g.verbose); - if (rc != 0) { - warnx("Failed to generate the verification pattern: %s", - strerror(-rc)); -@@ -1969,7 +1972,7 @@ static int command_validate(void) - goto out; - - rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize, -- &is_old_mk, g.verbose); -+ &is_old_mk, NULL, g.verbose); - is_valid = (rc == 0); - - token = find_token(g.cd, PAES_REENC_TOKEN_NAME); -@@ -1998,7 +2001,9 @@ static int command_validate(void) - printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); - printf(" Secure key size: %lu bytes\n", keysize); - printf(" XTS type key: %s\n", -- keysize > SECURE_KEY_SIZE ? "Yes" : "No"); -+ is_xts_key((u8 *)key, keysize) ? "Yes" : "No"); -+ printf(" Key type: %s\n", -+ get_key_type((u8 *)key, keysize)); - if (is_valid) { - printf(" Clear key size: %lu bits\n", clear_keysize); - printf(" Enciphered with: %s CCA master key (MKVP: " -@@ -2074,7 +2079,7 @@ static int command_setvp(void) - - token = find_token(g.cd, PAES_VP_TOKEN_NAME); - -- rc = generate_key_verification_pattern(key, keysize, -+ rc = generate_key_verification_pattern((const u8 *)key, keysize, - vp_tok.verification_pattern, - sizeof(vp_tok.verification_pattern), - g.verbose); -@@ -2129,12 +2134,12 @@ static int command_setkey(void) - if (newkey == NULL) - return EXIT_FAILURE; - -- rc = check_keysize_and_cipher_mode(newkey_size); -+ rc = check_keysize_and_cipher_mode(newkey, newkey_size); - if (rc != 0) - goto out; - - rc = validate_secure_key(g.pkey_fd, newkey, newkey_size, NULL, -- &is_old_mk, g.verbose); -+ &is_old_mk, NULL, g.verbose); - if (rc != 0) { - warnx("The secure key in file '%s' is not valid", - g.master_key_file); -@@ -2164,21 +2169,14 @@ static int command_setkey(void) - if (rc < 0) - goto out; - -- if (keysize != newkey_size) { -- warnx("The secure key in file '%s' has an invalid size", -- g.master_key_file); -- rc = -EINVAL; -- goto out; -- } -- -- if (memcmp(newkey, key, keysize) == 0) { -+ if (keysize == newkey_size && memcmp(newkey, key, keysize) == 0) { - warnx("The secure key in file '%s' is equal to the current " - "volume key, setkey is ignored", g.master_key_file); - rc = 0; - goto out; - } - -- rc = generate_key_verification_pattern((char *)newkey, newkey_size, vp, -+ rc = generate_key_verification_pattern(newkey, newkey_size, vp, - sizeof(vp), g.verbose); - if (rc != 0) { - warnx("Failed to generate the verification pattern: %s", -diff --git a/zkey/zkey.1 b/zkey/zkey.1 -index c7cea83..31fff43 100644 ---- a/zkey/zkey.1 -+++ b/zkey/zkey.1 -@@ -62,7 +62,8 @@ in size. - The \fBzkey\fP tool can operate in two modes. When argument - .I secure\-key\-file - is specified then it operates on the secure key contained in the specified file. --This applies to commands \fBgenerate\fP, \fBvalidate\fP, and \fBreencipher\fP. -+This applies to commands \fBgenerate\fP, \fBvalidate\fP, \fBreencipher\fP, and -+\fBconvert\fP. - When the - .B \-\-name - option is specified then it operates on a secure key contained in the secure -@@ -79,6 +80,8 @@ key repository. - .RB [ \-\-xts | \-x ] - .RB [ \-\-clearkey | \-c - .IR clear\-key\-file ] -+.RB [ \-\-key-type | \-K -+.IR type ] - .RB [ \-\-verbose | \-V ] - . - .PP -@@ -102,6 +105,8 @@ key repository. - .RB [ \-\-xts | \-x ] - .RB [ \-\-clearkey | \-c - .IR clear\-key\-file ] -+.RB [ \-\-key-type | \-K -+.IR type ] - .RB [ \-\-verbose | \-V ] - .PP - Use the -@@ -129,6 +134,15 @@ additional information can be associated with a secure key using the - , or the - .B \-\-sector-size - options. -+.PP -+You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, and -+\fBCCA-AESCIPHER\fP keys. Specify the type of the secure key using the -+.B \-\-key\-type -+option. The default key type is CCA-AESDATA. -+.PP -+.B Note: -+Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic -+adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. - . - .SS "Validating secure AES keys" - . -@@ -336,6 +350,12 @@ additional information can be associated with a secure key using the - , or the - .B \-\-sector-size - options. -+.PP -+.B Note: -+The \fBimport\fP command requires the CCA host library (libcsulcca.so) -+to be installed when secure keys of type \fBCCA-AESCIPHER\fP are imported. -+For the supported environments and downloads, see: -+\fIhttp://www.ibm.com/security/cryptocards\fP - . - .SS "Export AES secure keys from the secure key repository" - . -@@ -354,7 +374,6 @@ to a file in the file system. Specify the name of the key that is to be exported - using the - .B \-\-name - option. You cannot use wildcards. --When wildcards are used you must quote the value. - The exported secure key also remains in the secure key repository. - . - .SS "List AES secure keys contained in the secure key repository" -@@ -369,6 +388,8 @@ The exported secure key also remains in the secure key repository. - .IR card1.domain1[,card2.domain2[,...]] ] - .RB [ \-\-volume-type | \-t - .IR type ] -+.RB [ \-\-key-type | \-K -+.IR type ] - .RB [ \-\-verbose | \-V ] - . - .PP -@@ -383,7 +404,7 @@ listed that are associated with the specified volume and device-mapper name. - .PP - The - .B list --command displays the attributes of the secure keys, such as key sizes, -+command displays the attributes of the secure keys, such as key sizes, key type, - whether it is a secure key that can be used for the XTS cipher mode, the textual - description, associated cryptographic adapters (APQNs) and volumes, the - sector size, the key verification pattern, and timestamps for key creation, last -@@ -411,9 +432,11 @@ option. - .PP - .B Note: - When removing a secure key that is associated with one or multiple volumes, -+and the key's volume type is \fBplain\fP, - a message informs you about the associated volumes. When the secure key is - removed, these volumes can no longer be used, unless you have a backup of the --secure key. -+secure key. For keys with volume type \fBluks2\fP no such message is issued, -+because the secure key is contained in the LUKS2 header. - . - .SS "Change existing AES secure keys contained the secure key repository" - . -@@ -490,6 +513,15 @@ option and the new name using the - .B \-\-new-name - option. You cannot use wildcards. - . -+.B Note: -+When renaming a secure key that is associated with one or multiple volumes and -+the key's volume type is \fBplain\fP, a message informs you about the -+associated volumes. When the secure key is renamed, these volumes can no -+longer be used, unless you change the name of the secure key in the 'cryptsetup -+plainOpen' commands and in the '/etc/crypttab' entries. -+For keys with volume type \fBluks2\fP no such message is issued, because the -+secure key is contained in the LUKS2 header. -+. - .SS "Copy (duplicate) existing AES secure keys in the secure key repository" - . - .B zkey -@@ -649,6 +681,72 @@ questions, you can specify the - option. These options are passed to the generated command(s) and behave in the - same way as with \fBcryptsetup\fP. - . -+.SS "Convert existing AES secure keys from one key type to another type" -+. -+.B zkey -+.BR convert | con -+.I secure\-key\-file -+.RB \-\-key-type | \-K -+.IR type -+.RB [ \-\-no\-apqn\-check ] -+.RB [ \-\-force | \-F ] -+.RB [ \-\-verbose | \-V ] -+. -+.PP -+.B zkey -+.BR convert | con -+.B \-\-name | \-N -+.IR key-name -+.RB \-\-key-type | \-K -+.IR type -+.RB [ \-\-no\-apqn\-check ] -+.RB [ \-\-force | \-F ] -+.RB [ \-\-verbose | \-V ] -+. -+.PP -+Use the -+.B convert -+command to convert an existing secure key from one key type to another type. -+You can convert secure keys of type CCA-AESDATA to type CCA-AESCIPHER only. -+ -+.B Note: -+Secure keys converted to type \fBCCA-AESCIPHER\fP require an IBM cryptographic -+adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. -+ -+The secure key can either be contained in a file in the file system, or in a -+secure key repository. To convert a secure key contained in a file, specify -+the file name with option \fIsecure\-key\-file\fP. To convert a secure key -+contained in the secure key repository, specify the name of the key -+that is to be converted using the -+.B \-\-name -+option. You cannot use wildcards. The convert command prompts for -+a confirmation, unless you specify the -+.B \-\-force -+option. -+.PP -+.B Note: -+Converting a secure key is irreversible! -+When converting a secure key that is associated with one or multiple volumes, -+a message informs you about the associated volumes. When the secure key is -+converted, this might have an effect on these volumes. -+.P -+For volumes with volume type \fBplain\fP, you must adapt the crypttab entries -+and change the key size parameter to \fBsize=\fP or run -+command \fBzkey crypttab --volumes \fP for each associated volume to -+re-generate the crypttab entries. -+.P -+Associated volumes of type \fLUKS2\fP still contain the secure AES volume key of -+the original type. To change the secure AES volume key in the LUKS2 header, -+run command \fBzkey-cryptsetup setkey --master-key-file -+\fP for each associated volume. -+. -+.P -+.B Note: -+The \fBconvert\fP command requires the CCA host library (libcsulcca.so) -+to be installed. The required CCA IBM cryptographic adapter firmware version -+is 6.3.27 or later. For the supported environments and downloads, see: -+\fIhttp://www.ibm.com/security/cryptocards\fP -+. - . - . - . -@@ -718,6 +816,13 @@ This option is only available if - has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, - the default volume type is \fBplain\fP. - This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-K ", " \-\-key-type\~\fItype\fP -+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP -+and \fBCCA-AESCIPHER\fP. If this option is omitted, then a secure key of type -+CCA-AESDATA is generated. Secure keys of type \fBCCA-AESCIPHER\fP require an -+IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g. -+a CEX6C. - . - . - . -@@ -897,6 +1002,11 @@ This option is only available if - .B zkey - has been compiled with LUKS2 support enabled. - This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-K ", " \-\-key-type\~\fItype\fP -+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP -+and \fBCCA-AESCIPHER\fP. Only keys with the specified key type are listed. -+This option is only used for secure keys contained in the secure key repository. - . - . - . -@@ -1160,6 +1270,30 @@ cryptsetup command(s). - . - . - . -+.SS "Options for the convert command" -+.TP -+.BR \-N ", " \-\-name\~\fIkey-name\fP -+Specifies the name of the secure key in the secure key repository. You cannot -+use wildcards. -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-K ", " \-\-key-type\~\fItype\fP -+Specifies the key type to which the secure key shall be converted to. -+Possible values are \fBCCA-AESCIPHER\fP. Secure keys of type \fBCCA-AESCIPHER\fP -+require an IBM cryptographic adapter in CCA coprocessor mode of version 6 or -+later, e.g. a CEX6C. -+.TP -+.BR \-\-no\-apqn\-check -+Do not check if the associated APQNs are available and capable of converting -+the secure key to type CCA-AESCIPHER. -+This option is only used for secure keys contained in the secure key repository. -+.TP -+.BR \-F ", " \-\-force\fP -+The user is prompted to confirm the convertion of a secure key. Use this option -+to convert a secure key without prompting for a confirmation. -+. -+. -+. - .SS "General options" - .TP - .BR \-V ", " \-\-verbose -diff --git a/zkey/zkey.c b/zkey/zkey.c -index a0dbc0b..3ae9a9b 100644 ---- a/zkey/zkey.c -+++ b/zkey/zkey.c -@@ -73,6 +73,7 @@ static struct zkey_globals { - long int sector_size; - char *volume_type; - char *newname; -+ char *key_type; - bool run; - bool batch_mode; - char *keyfile; -@@ -105,6 +106,7 @@ static struct zkey_globals { - #define COMMAND_COPY "copy " - #define COMMAND_CRYPTTAB "crypttab" - #define COMMAND_CRYPTSETUP "cryptsetup" -+#define COMMAND_CONVERT "convert" - - #define ZKEY_COMMAND_MAX_LEN 10 - -@@ -216,6 +218,15 @@ static struct util_opt opt_vec[] = { - .command = COMMAND_GENERATE, - }, - #endif -+ { -+ .option = { "key-type", required_argument, NULL, 'K'}, -+ .argument = "type", -+ .desc = "The type of the key. Possible values are '" -+ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " -+ "When this option is omitted, the default is '" -+ KEY_TYPE_CCA_AESDATA"'", -+ .command = COMMAND_GENERATE, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -432,6 +443,15 @@ static struct util_opt opt_vec[] = { - .command = COMMAND_LIST, - }, - #endif -+ { -+ .option = { "key-type", required_argument, NULL, 'K'}, -+ .argument = "type", -+ .desc = "The type of the key. Possible values are '" -+ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " -+ "Use this option to list all keys with the specified " -+ "key type.", -+ .command = COMMAND_LIST, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -745,6 +765,38 @@ static struct util_opt opt_vec[] = { - .flags = UTIL_OPT_FLAG_NOSHORT, - }, - #endif -+ /***********************************************************/ -+ { -+ .flags = UTIL_OPT_FLAG_SECTION, -+ .desc = "OPTIONS", -+ .command = COMMAND_CONVERT, -+ }, -+ { -+ .option = { "name", required_argument, NULL, 'N'}, -+ .argument = "NAME", -+ .desc = "Name of the secure AES key in the repository that is " -+ "to be converted", -+ .command = COMMAND_CONVERT, -+ }, -+ { -+ .option = { "key-type", required_argument, NULL, 'K'}, -+ .argument = "type", -+ .desc = "The type of the key to convert the secure key to. " -+ "Possible values are '"KEY_TYPE_CCA_AESCIPHER"'. ", -+ .command = COMMAND_CONVERT, -+ }, -+ { -+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, -+ .desc = "Do not check if the associated APQN(s) are available", -+ .command = COMMAND_CONVERT, -+ .flags = UTIL_OPT_FLAG_NOSHORT, -+ }, -+ { -+ .option = {"force", 0, NULL, 'F'}, -+ .desc = "Do not prompt for a confirmation when converting a " -+ "key", -+ .command = COMMAND_CONVERT, -+ }, - /***********************************************************/ - { - .flags = UTIL_OPT_FLAG_SECTION, -@@ -793,6 +845,7 @@ static int command_rename(void); - static int command_copy(void); - static int command_crypttab(void); - static int command_cryptsetup(void); -+static int command_convert(void); - - static struct zkey_command zkey_commands[] = { - { -@@ -927,6 +980,21 @@ static struct zkey_command zkey_commands[] = { - .has_options = 1, - .need_keystore = 1, - }, -+ { -+ .command = COMMAND_CONVERT, -+ .abbrev_len = 3, -+ .function = command_convert, -+ .need_cca_library = 1, -+ .need_pkey_device = 1, -+ .short_desc = "Convert a secure AES key", -+ .long_desc = "Convert an existing secure AES key that is " -+ "either contained in SECURE-KEY-FILE or is stored " -+ "in the repository from one key type to another " -+ "type.", -+ .has_options = 1, -+ .pos_arg = "[SECURE-KEY-FILE]", -+ .pos_arg_optional = 1, -+ }, - { .command = NULL } - }; - -@@ -1009,9 +1077,8 @@ static int command_generate_clear(void) - - rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg, - g.keybits, g.xts, -- g.clearkeyfile, -- AUTOSELECT, AUTOSELECT, -- g.verbose); -+ g.clearkeyfile, g.key_type, -+ NULL, g.verbose); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1026,9 +1093,8 @@ static int command_generate_random(void) - int rc; - - rc = generate_secure_key_random(g.pkey_fd, g.pos_arg, -- g.keybits, g.xts, -- AUTOSELECT, AUTOSELECT, -- g.verbose); -+ g.keybits, g.xts, g.key_type, -+ NULL, g.verbose); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1048,7 +1114,7 @@ static int command_generate_repository(void) - rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, - g.apqns, g.noapqncheck, g.sector_size, - g.keybits, g.xts, g.clearkeyfile, -- g.volume_type, g.pkey_fd); -+ g.volume_type, g.key_type, g.pkey_fd); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1075,6 +1141,8 @@ static int command_generate(void) - util_prg_print_parse_error(); - return EXIT_FAILURE; - } -+ if (g.key_type == NULL) -+ g.key_type = KEY_TYPE_CCA_AESDATA; - if (g.name != NULL) - return command_generate_repository(); - if (g.pos_arg != NULL) { -@@ -1103,7 +1171,9 @@ static int command_generate(void) - return EXIT_FAILURE; - } - -- rc = cross_check_apqns(NULL, 0, true, g.verbose); -+ rc = cross_check_apqns(NULL, 0, -+ get_min_card_level_for_keytype(g.key_type), -+ true, g.verbose); - if (rc == -EINVAL) - return EXIT_FAILURE; - if (rc != 0 && rc != -ENOTSUP) { -@@ -1169,7 +1239,7 @@ static int command_reencipher_file(void) - return EXIT_FAILURE; - - rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL, -- &is_old_mk, g.verbose); -+ &is_old_mk, NULL, g.verbose); - if (rc != 0) { - warnx("The secure key in file '%s' is not valid", g.pos_arg); - rc = EXIT_FAILURE; -@@ -1385,14 +1455,14 @@ static int command_validate_file(void) - return EXIT_FAILURE; - - rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, -- &clear_key_size, &is_old_mk, g.verbose); -+ &clear_key_size, &is_old_mk, NULL, g.verbose); - if (rc != 0) { - warnx("The secure key in file '%s' is not valid", g.pos_arg); - rc = EXIT_FAILURE; - goto out; - } - -- rc = generate_key_verification_pattern((char *)secure_key, -+ rc = generate_key_verification_pattern(secure_key, - secure_key_size, vp, sizeof(vp), - g.verbose); - if (rc != 0) { -@@ -1416,9 +1486,11 @@ static int command_validate_file(void) - printf("Validation of secure key in file '%s':\n", g.pos_arg); - printf(" Status: Valid\n"); - printf(" Secure key size: %lu bytes\n", secure_key_size); -+ printf(" Key type: %s\n", -+ get_key_type(secure_key, secure_key_size)); - printf(" Clear key size: %lu bits\n", clear_key_size); - printf(" XTS type key: %s\n", -- secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No"); -+ is_xts_key(secure_key, secure_key_size) ? "Yes" : "No"); - printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n", - is_old_mk ? "OLD" : "CURRENT", mkvp); - printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, -@@ -1426,7 +1498,10 @@ static int command_validate_file(void) - printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, - &vp[VERIFICATION_PATTERN_LEN / 2]); - -- rc = cross_check_apqns(NULL, mkvp, true, g.verbose); -+ rc = cross_check_apqns(NULL, mkvp, -+ get_min_card_level_for_keytype( -+ get_key_type(secure_key, secure_key_size)), -+ true, g.verbose); - if (rc == -EINVAL) - return EXIT_FAILURE; - if (rc != 0 && rc != -ENOTSUP) { -@@ -1496,7 +1571,7 @@ static int command_import(void) - - rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, - g.apqns, g.noapqncheck, g.sector_size, -- g.pos_arg, g.volume_type); -+ g.pos_arg, g.volume_type, &g.cca); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1530,7 +1605,7 @@ static int command_list(void) - int rc; - - rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns, -- g.volume_type); -+ g.volume_type, g.key_type); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1665,6 +1740,206 @@ static int command_cryptsetup(void) - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } - -+/* -+ * Command handler for 'convert'. -+ * -+ * Converts secure keys from one key type to another -+ */ -+static int command_convert_file(void) -+{ -+ u8 output_key[2 * MAX_SECURE_KEY_SIZE]; -+ unsigned int output_key_size; -+ size_t secure_key_size; -+ int rc, is_old_mk; -+ int selected = 1; -+ u8 *secure_key; -+ int min_level; -+ u64 mkvp; -+ -+ if (g.name != NULL) { -+ warnx("Option '--name|-N' is not valid for " -+ "re-enciphering a key outside of the repository"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ if (g.noapqncheck) { -+ warnx("Option '--no-apqn-check' is not valid for " -+ "converting a key outside of the repository"); -+ util_prg_print_parse_error(); -+ return EXIT_FAILURE; -+ } -+ -+ min_level = get_min_card_level_for_keytype(g.key_type); -+ if (min_level < 0) { -+ warnx("Invalid key-type specified: %s", g.key_type); -+ return EXIT_FAILURE; -+ } -+ -+ rc = cross_check_apqns(NULL, 0, min_level, true, g.verbose); -+ if (rc == -EINVAL) -+ return EXIT_FAILURE; -+ if (rc != 0 && rc != -ENOTSUP) { -+ warnx("Your master key setup is improper"); -+ return EXIT_FAILURE; -+ } -+ -+ /* Read the secure key to be re-enciphered */ -+ secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); -+ if (secure_key == NULL) -+ return EXIT_FAILURE; -+ -+ rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL, -+ &is_old_mk, NULL, g.verbose); -+ if (rc != 0) { -+ warnx("The secure key in file '%s' is not valid", g.pos_arg); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ &mkvp, g.verbose); -+ if (rc != 0) { -+ warnx("Failed to get the master key verification pattern: %s", -+ strerror(-rc)); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ if (strcasecmp(get_key_type(secure_key, secure_key_size), -+ g.key_type) == 0) { -+ warnx("The secure key in file '%s' is already of type %s", -+ g.pos_arg, get_key_type(secure_key, secure_key_size)); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ if (is_cca_aes_data_key(secure_key, secure_key_size)) { -+ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) { -+ warnx("The secure key in file '%s' can not be " -+ "converted into type %s", g.pos_arg, g.key_type); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { -+ warnx("The secure key in file '%s' is already of type %s", -+ g.pos_arg, KEY_TYPE_CCA_AESCIPHER); -+ rc = EXIT_FAILURE; -+ goto out; -+ } else { -+ warnx("The secure key in file '%s' has an unsupported key type", -+ g.pos_arg); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -+ FLAG_SEL_CCA_MATCH_CUR_MKVP, -+ g.verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ selected = 0; -+ } -+ if (rc != 0) { -+ warnx("No APQN found that is suitable for " -+ "converting the secure AES key in file '%s'", g.pos_arg); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ if (!g.force) { -+ util_print_indented("ATTENTION: Converting a secure key is " -+ "irreversible, and might have an effect " -+ "on the volumes encrypted with it!", 0); -+ printf("%s: Convert key in file '%s' [y/N]? ", -+ program_invocation_short_name, g.pos_arg); -+ if (!prompt_for_yes(g.verbose)) { -+ warnx("Operation aborted"); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ } -+ -+ memset(output_key, 0, sizeof(output_key)); -+ output_key_size = sizeof(output_key); -+ rc = convert_aes_data_to_cipher_key(&g.cca, secure_key, secure_key_size, -+ output_key, &output_key_size, -+ g.verbose); -+ if (rc != 0) { -+ warnx("Converting the secure key from %s to %s has failed", -+ get_key_type(secure_key, secure_key_size), g.key_type); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ rc = restrict_key_export(&g.cca, output_key, output_key_size, -+ g.verbose); -+ if (rc != 0) { -+ warnx("Export restricting the converted secure key has failed"); -+ if (!selected) -+ print_msg_for_cca_envvars("secure AES key"); -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ -+ pr_verbose("Secure key was converted successfully"); -+ -+ /* Write the converted secure key */ -+ rc = write_secure_key(g.outputfile ? g.outputfile : g.pos_arg, -+ output_key, output_key_size, g.verbose); -+ if (rc != 0) -+ rc = EXIT_FAILURE; -+out: -+ free(secure_key); -+ return rc; -+} -+ -+/* -+ * Command handler for 'convert in repository'. -+ * -+ * Converts secure keys from one key type to another -+ */ -+static int command_convert_repository(void) -+{ -+ int rc; -+ -+ if (g.name == NULL) { -+ misc_print_required_parm("--name/-N"); -+ return EXIT_FAILURE; -+ } -+ -+ rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck, -+ g.force, g.pkey_fd, &g.cca); -+ -+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; -+} -+ -+/* -+ * Command handler for 'convert'. -+ * -+ * Converts secure keys from one key type to another -+ */ -+static int command_convert(void) -+{ -+ if (g.key_type == NULL) { -+ misc_print_required_parm("--key-type/-K"); -+ return EXIT_FAILURE; -+ } -+ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) { -+ warnx("Secure keys can only be converted into key type %s", -+ KEY_TYPE_CCA_AESCIPHER); -+ return EXIT_FAILURE; -+ } -+ -+ if (g.pos_arg != NULL) -+ return command_convert_file(); -+ else -+ return command_convert_repository(); -+ -+ return EXIT_SUCCESS; -+} -+ - /** - * Opens the keystore. The keystore directory is either the - * default directory or as specified in an environment variable -@@ -1851,6 +2126,9 @@ int main(int argc, char *argv[]) - case 'r': - g.run = 1; - break; -+ case 'K': -+ g.key_type = optarg; -+ break; - case 'F': - g.force = 1; - break; --- -2.21.3 - - -From a9fe5d1477161d658bcade0f85309eefe9fcb0bc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 13:29:28 +0100 -Subject: [PATCH 36/57] zcrypt: CEX7S exploitation support (#1723837) - -Description: CEX7S exploitation support to lszcrypt, chzcrypt - and zcryptstats. - -Upstream-ID: b631c74df57e9ea52f54b4d6be79b63bc89e5eee -Upstream-ID: e9c030f2026b1b8e0399679600845c298aeb508d -Upstream-ID: 784ac7d190080a3cf230995eedd3743bddfa4b5c -Upstream-ID: 4fc0c3cfefb8fb23a83ef629ac3f4a967fc0e77f -Upstream-ID: e15e8a1bfa15e2179f30c6fe2e937ddd1a5e53c1 ---- - zconf/zcrypt/chzcrypt.8 | 4 +-- - zconf/zcrypt/chzcrypt.c | 4 +-- - zconf/zcrypt/lszcrypt.8 | 66 ++++++++++++++++++++++++++++++++++++-- - zconf/zcrypt/lszcrypt.c | 20 +++++++----- - zconf/zcrypt/zcryptstats.8 | 5 +++ - zconf/zcrypt/zcryptstats.c | 27 ++++++++++------ - 6 files changed, 102 insertions(+), 24 deletions(-) - -diff --git a/zconf/zcrypt/chzcrypt.8 b/zconf/zcrypt/chzcrypt.8 -index 2021645..2697c96 100644 ---- a/zconf/zcrypt/chzcrypt.8 -+++ b/zconf/zcrypt/chzcrypt.8 -@@ -1,8 +1,8 @@ --.\" Copyright 2017 IBM Corp. -+.\" Copyright 2019 IBM Corp. - .\" s390-tools is free software; you can redistribute it and/or modify - .\" it under the terms of the MIT license. See LICENSE for details. - .\" --.TH CHZCRYPT 8 "OCT 2017" "s390-tools" -+.TH CHZCRYPT 8 "AUG 2019" "s390-tools" - .SH NAME - chzcrypt \- modify zcrypt configuration - .SH SYNOPSIS -diff --git a/zconf/zcrypt/chzcrypt.c b/zconf/zcrypt/chzcrypt.c -index 6521056..2fc8441 100644 ---- a/zconf/zcrypt/chzcrypt.c -+++ b/zconf/zcrypt/chzcrypt.c -@@ -1,7 +1,7 @@ - /* - * chzcrypt - Tool to modify zcrypt configuration - * -- * Copyright IBM Corp. 2008, 2017 -+ * Copyright IBM Corp. 2008, 2019 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. -@@ -47,7 +47,7 @@ const struct util_prg prg = { - { - .owner = "IBM Corp.", - .pub_first = 2008, -- .pub_last = 2017, -+ .pub_last = 2019, - }, - UTIL_PRG_COPYRIGHT_END - } -diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8 -index 826e109..f9ce4a5 100644 ---- a/zconf/zcrypt/lszcrypt.8 -+++ b/zconf/zcrypt/lszcrypt.8 -@@ -1,6 +1,6 @@ - .\" lszcrypt.8 - .\" --.\" Copyright 2017 IBM Corp. -+.\" Copyright 2019 IBM Corp. - .\" s390-tools is free software; you can redistribute it and/or modify - .\" it under the terms of the MIT license. See LICENSE for details. - .\" -@@ -10,7 +10,7 @@ - .\" nroff -man lszcrypt.8 - .\" to process this source - .\" --.TH LSZCRYPT 8 "OCT 2017" "s390-tools" -+.TH LSZCRYPT 8 "AUG 2019" "s390-tools" - .SH NAME - lszcrypt \- display zcrypt device and configuration information - .SH SYNOPSIS -@@ -111,6 +111,68 @@ Displays help text and exits. - .TP 8 - .B -v, --version - Displays version information and exits. -+.SH VERBOSE LISTING DETAILS -+Some of the columns showing up in verbose listing mode may need some -+explanation: -+.TP -+.B TYPE and HWTYPE -+The HWTYPE is a numeric value showing which type of hardware the zcrypt -+device driver presumes that this crypto card is. The currently known values -+are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6 and 13=CEX7. -+.br -+The TYPE is a human readable value showing the hardware type and the basic -+function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So -+for example CEX6P means a CEX6 card in EP11 Coprocessor mode. -+.TP -+.B REQUESTS -+This is the counter value of successful processed requests on card or queue -+level. Successful here means the request was processed without any failure -+in the whole processing chain. -+.TP -+.B PENDING -+The underlying firmware and hardware layer usually provide some queuing -+space for requests. When this queue is already filled up, the zcrypt device -+driver maintains a software queue of pending requests. The sum of these -+both values is displayed here and shows the amount of requests waiting for -+processing on card or queue level. -+.TP -+.B FUNCTIONS -+This column shows firmware and hardware function details: -+.br -+S - APSC available: card/queue can handle requests with the special bit -+enabled. -+.br -+M - Accelerator card/queue with support for RSA ME with up to 4k key size. -+.br -+C - Accelerator card/queue with support for RSA CRT with up to 4k key size. -+.br -+D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode). -+.br -+A - Card/queue is providing Accelerator functions (this is the Accelerator mode). -+.br -+X - Card/queue is providing EP11 functions (this is the EP11 Coprocessor mode). -+.br -+N - APXA available (ability to address more than 16 crypto cards and domains). -+.br -+F - Full function support (opposed to restricted function support, see below). -+.br -+R - Restricted function support. The F and R flag both reflect if a -+hypervisor is somehow restricting this crypto resource in a virtual -+environment. Dependent on the hypervisor configuration the crypto requests -+may be filtered by the hypervisor to allow only a subset of functions -+within the virtual runtime environment. For example a shared CCA -+Coprocessor may be restricted by the hypervisor to allow only clear key -+operations within the guests. -+.TP -+.B DRIVER -+.br -+Shows which card or queue device driver currently handles this crypto -+resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX7 -+hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware), -+cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved -+for use by kvm hypervisor for kvm guests and not accessible to host -+applications). It is also valid to have no driver handling a queue which is -+shown as a -no-driver- entry. - .SH EXAMPLES - .TP - .B lszcrypt -diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c -index 580407f..1b2befe 100644 ---- a/zconf/zcrypt/lszcrypt.c -+++ b/zconf/zcrypt/lszcrypt.c -@@ -1,7 +1,7 @@ - /** - * lszcrypt - Display zcrypt devices and configuration settings - * -- * Copyright IBM Corp. 2008, 2018 -+ * Copyright IBM Corp. 2008, 2019 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. -@@ -12,6 +12,7 @@ - - #include "lib/util_base.h" - #include "lib/util_file.h" -+#include "lib/util_libc.h" - #include "lib/util_opt.h" - #include "lib/util_panic.h" - #include "lib/util_path.h" -@@ -85,7 +86,7 @@ const struct util_prg prg = { - { - .owner = "IBM Corp.", - .pub_first = 2008, -- .pub_last = 2018, -+ .pub_last = 2019, - }, - UTIL_PRG_COPYRIGHT_END - } -@@ -301,9 +302,10 @@ static void show_capability(const char *id_str) - printf("%s\n", CAP_CCA); - printf("%s", CAP_RNG); - break; -- case 10: -- case 11: -- case 12: -+ case 10: /* CEX4S */ -+ case 11: /* CEX5S */ -+ case 12: /* CEX6S */ -+ case 13: /* CEX7S */ - if (func_val & MASK_ACCEL) { - if (func_val & MASK_RSA4K) - printf("%s", CAP_RSA4K); -@@ -620,7 +622,7 @@ static void show_devices_argv(char *argv[]) - { - struct util_rec *rec = util_rec_new_wide("-"); - struct dirent **dev_vec, **subdev_vec; -- char *ap, *grp_dev, *path, card[16], sub_dev[16]; -+ char *ap, *grp_dev, *path, *card, *sub_dev; - int id, dom, i, n, dev_cnt, sub_cnt; - - /* check if ap driver is available */ -@@ -639,14 +641,16 @@ static void show_devices_argv(char *argv[]) - if (sscanf(argv[i], "%x.%x", &id, &dom) >= 1) { - /* at least the id field was valid */ - if (id >= 0 && dom >= 0) { /* single subdevice */ -- sprintf(sub_dev, "%02x.%04x", id, dom); -+ util_asprintf(&sub_dev, "%02x.%04x", id, dom); - grp_dev = util_path_sysfs("devices/ap/card%02x", - id); - show_subdevice(rec, grp_dev, sub_dev); - free(grp_dev); -+ free(sub_dev); - } else { /* group device */ -- sprintf(card, "card%02x", id); -+ util_asprintf(&card, "card%02x", id); - show_device(rec, card); -+ free(card); - } - return; - } -diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8 -index 8c31c69..7d000d2 100644 ---- a/zconf/zcrypt/zcryptstats.8 -+++ b/zconf/zcrypt/zcryptstats.8 -@@ -72,6 +72,11 @@ the parallel execution of cryptographic operations. - Cryptographic performance measurement data might not be available when Linux - is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an - error message and exits. -+.PP -+.B Note: -+\fBzcryptstats\fP utilizes the device node \fB/dev/chsc\fP. When this device -+node is not available, you might have to load kernel module \fBchsc_sch\fP using -+\fBmodprobe chsc_sch\fP to make it available. - . - . - . -diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c -index 136d5ba..3bb2078 100644 ---- a/zconf/zcrypt/zcryptstats.c -+++ b/zconf/zcrypt/zcryptstats.c -@@ -147,8 +147,9 @@ struct chsc_cmb_area { - #define CRYPTO_TYPE_CEX4S 10 - #define CRYPTO_TYPE_CEX5S 11 - #define CRYPTO_TYPE_CEX6S 12 -+#define CRYPTO_TYPE_CEX7S 13 - --#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S -+#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX7S - - struct crypto_counter { - const char *name; -@@ -235,8 +236,8 @@ const struct crypto_mode mode_pcica[1] = { - .counters = counter_pcica }, - }; - --#define NUM_CEX456_MODES 11 --const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { -+#define NUM_CEX4567_MODES 11 -+const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = { - { 0 }, - { 0 }, - { 0 }, -@@ -256,7 +257,7 @@ const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = { - .counters = counter_ep11 }, - }; - --#define NUM_CRYPTO_TYPES 13 -+#define NUM_CRYPTO_TYPES 14 - const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { - { 0 }, - { 0 }, -@@ -275,12 +276,14 @@ const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = { - .modes = mode_accel }, - { .name = "CEX3C", .num_modes = NUM_COPROC_MODES, - .modes = mode_coproc }, -- { .name = "CEX4", .num_modes = NUM_CEX456_MODES, -- .modes = mode_cex456 }, -- { .name = "CEX5", .num_modes = NUM_CEX456_MODES, -- .modes = mode_cex456 }, -- { .name = "CEX6", .num_modes = NUM_CEX456_MODES, -- .modes = mode_cex456 }, -+ { .name = "CEX4", .num_modes = NUM_CEX4567_MODES, -+ .modes = mode_cex4567 }, -+ { .name = "CEX5", .num_modes = NUM_CEX4567_MODES, -+ .modes = mode_cex4567 }, -+ { .name = "CEX6", .num_modes = NUM_CEX4567_MODES, -+ .modes = mode_cex4567 }, -+ { .name = "CEX7", .num_modes = NUM_CEX4567_MODES, -+ .modes = mode_cex4567 }, - }; - - -@@ -2393,7 +2396,11 @@ int main(int argc, char *argv[]) - - g.chsc_fd = open(CHSC_DEVICE, O_RDWR); - if (g.chsc_fd < 0) { -+ rc = errno; - warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno)); -+ if (rc == ENOENT) -+ warnx("You might have to load kernel module 'chsc_sch' " -+ "using 'modprobe chsc_sch'"); - return EXIT_FAILURE; - } - pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE); --- -2.21.3 - - -From afcf268f8af2a6e925c44c057cb04c2dc38c9b10 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 13:48:23 +0100 -Subject: [PATCH 37/57] lstape, lsluns: handle non-zfcp; lin_tape multiple - paths (#1766569) - -Symptom: lstape shows unexpected additional Device suffix numbers in - excess output columns for each additional path of the same - tape/changer driven by the IBM lin_tape device driver - (independent of actual path failover enablement in lin_tape). - It also shows a wrong number of found devices in the header - (without --scsi-only). - - lstape prints error about "Unexpected extra argument:" and the - usage for "sg_inq" along with wrong tabular output. - - lsluns prints ENOENT error text for "cat" on SCSI device sysfs - attributes hba_id, wwpn, and fcp_lun. - - lstape with --verbose option prints ENOENT error text for - "cat" on SCSI device sysfs attributes hba_id and wwpn. - - lstape man page: Description of --type and filter - for channel tapes is incomplete. SCSI output description is - incomplete. - - lstape shows "N/A" instead of the HBA device bus-ID with - virtio-scsi-ccw. - -Problem: s390-tools-1.8.0 before the first upstream commit b627b8d8e1ab - ("Initial s390-tools-2.0.0 import") introduced SCSI - tape/changer output for lstape. It used the SCSI device serial - number as lookup key to find a match in IBM lin_tape device - driver proc-fs output for a given SCSI device name. Multiple - paths to the same tape/changer have the same serial number. - Multiple matches cause excess arguments to printf. Explaining - the resulting output, the bash man page says: "The format is - reused as necessary to consume all of the arguments." This - also causes a wrong number of found devices. - - The default bash settings have nullglob disabled so if - $SCSI_DEV/scsi_generic* aka - /sys/bus/scsi/devices/*:*:*:*/scsi_generic* does not match - anything, it leaves the glob pattern unmodified and - SG_DEV=$(basename $SG_DEV/*) results in the literal "*". If - $SG_INQ exists, it invoked sg_inq with more than the one - allowed positional argument for a SCSI generic device node - "sg_inq /dev/*". Causing error messages and the usage of - sg_inq to land in $TAPE_SERIAL. - - lsluns iterates SCSI generic devices and unconditionally - reads zfcp-specific SCSI device sysfs attributes hba_id, wwpn, - and fcp_lun. - - lstape --verbose unconditionally reads zfcp-specific SCSI - device sysfs attributes hba_id and wwpn. - - filter missing from synopsis. example - at wrong place with filter. filter - option description is a duplicate of filter - option description. SCSI output description misses fields. - - Lstape only used the zfcp-specific sysfs attribute hba_id. - -Solution: Prefer sysfs to find lin_tape device name for SCSI device. - Fallback: The lin_tape proc-fs output format has changed over - the years. The HBA device driver string can contain whitespace - (e.g. "Virtio SCSI HBA") and breaks the field numbers with - tokenized parsing. Grep for the SCSI device name as word (to - skip names with same substring, such as 0:0:1:1 also matching - 0:0:1:10) and cut the first field 'Number' (lin_tape device - name suffix). If there is no SCSI column at all [lin_tape - before v2.2.0] (and no SCSI LLDD or other column with a name - accidentally matching an existing SCSI device name), we get no - match and better bail out with the initialized "N/A" for the - lstape column "Device". - - To not have to rely on the nullglob setting, explicitly check - for the existence of $SCSI_DEV/scsi_generic before evaluating - SG_DEV=$(basename $SG_DEV/*). Also handle availability of - sg_inq but absence of scsi_generic individually to provide the - user with a hint if only sg is missing. - - Simply skip non-zfcp SCSI devices, such as iSCSI or - virtio-scsi-ccw, to not erroneously access absent attributes. - - Assume "N/A" for HBA and WWPN of non-zfcp SCSI devices, such - as iSCSI or virtio-scsi-ccw, to not erroneously access absent - zfcp-specific sysfs attributes. - - Add filter to synopsis. Move example - to option. Replace filter option - description. Move existing SCSI output description to a new - subsection and add description of missing fields. - - Also search sysfs for an ancestor with subsystem ccw. - -Reproduction: Attach more than one path to the same SCSI tape or changer and - load the IBM lin_tape device driver. - - Unload sg kernel module. - - Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw. - - Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw. - - man lstape - - Attach SCSI tape or changer with virtio-scsi-ccw to a KVM - guest and run "lstape --verbose". - -Upstream-ID: ef4dc7a45b1b80c58076a0a317245569d84b2754 -Upstream-ID: cdc787db1ba17dd2c0ed841fd7443bc32be63b65 -Upstream-ID: 80e0c41b896e1eeffc594416b4c2787ed29363b6 -Upstream-ID: eba744a25edcc7202068c1d46fd99d4c98c1acf2 -Upstream-ID: 4cf8f5f46c0f80b49a2a70854372289c3fda932b -Upstream-ID: b9b3d2d23069e021693a251a3aadd4eefc30e6ea -Upstream-ID: 34260f17360f1034c562c941b0f879c2204e40b7 -Upstream-ID: d5291eed1c46e1c97831e771b2975575ba268992 ---- - zconf/lsluns | 14 ++++++--- - zconf/lsluns.8 | 5 ++- - zconf/lstape | 69 ++++++++++++++++++++++++++++++++++------- - zconf/lstape.8 | 84 +++++++++++++++++++++++++++++++++++++++----------- - 4 files changed, 135 insertions(+), 37 deletions(-) - -diff --git a/zconf/lsluns b/zconf/lsluns -index f88aa0f..bfe48f1 100755 ---- a/zconf/lsluns -+++ b/zconf/lsluns -@@ -2,7 +2,7 @@ - # - # lsluns - list LUNs discovered in the FC SAN, or show encryption state of attached LUNs - # --# Copyright IBM Corp. 2008, 2017 -+# Copyright IBM Corp. 2008, 2018 - # - # s390-tools is free software; you can redistribute it and/or modify - # it under the terms of the MIT license. See LICENSE for details. -@@ -152,6 +152,11 @@ sub get_lun_hash - my %lun_hash; - - foreach my $device () { -+ # skip non-zfcp SCSI devices and avoid file access error messages -+ next unless -r "$device/device/fcp_lun"; -+ next unless -r "$device/device/wwpn"; -+ next unless -r "$device/device/hba_id"; -+ - my $l = `cat $device/device/fcp_lun`; - my $p = `cat $device/device/wwpn`; - my $a = `cat $device/device/hba_id`; -@@ -170,9 +175,8 @@ sub get_lun_hash - sub lsluns_usage { - print <] ... [-p ] ... [-h] [-v] - -@@ -220,7 +224,7 @@ EOD - - sub lsluns_version { - print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n"; -- print "Copyright IBM Corp. 2008, 2017\n"; -+ print "Copyright IBM Corp. 2008, 2018\n"; - } - - sub lsluns_invalid_usage { -diff --git a/zconf/lsluns.8 b/zconf/lsluns.8 -index cca649a..a4a1849 100644 ---- a/zconf/lsluns.8 -+++ b/zconf/lsluns.8 -@@ -28,9 +28,8 @@ zfcp-attached LUNs - - .SH DESCRIPTION - .PP --This tool is designed for environments where all SCSI devices are attached --through the zfcp device driver. Expect error messages in mixed environments --such as with iSCSI. -+This tool is designed for environments with SCSI devices attached -+through the zfcp device driver. - - .B lsluns - lists all logical unit numbers (LUNs) discovered in the -diff --git a/zconf/lstape b/zconf/lstape -index 3090451..e2410e1 100755 ---- a/zconf/lstape -+++ b/zconf/lstape -@@ -2,7 +2,7 @@ - # - # lstape - Tool to show information about tape devices - # --# Copyright IBM Corp. 2003, 2017 -+# Copyright IBM Corp. 2003, 2018 - # - # s390-tools is free software; you can redistribute it and/or modify - # it under the terms of the MIT license. See LICENSE for details. -@@ -48,6 +48,9 @@ function PrintUsage() { - : -v|--version - : Display the version of the tools package and - : the lstape command. -+ : -+ :$(basename $0) without the --ccw-only option causes extra SAN traffic -+ :for each SCSI tape or changer device by invoking the sg_inq command. - EOD - } - -@@ -55,7 +58,7 @@ function PrintVersion() - { - cat <<-EOD - $CMD: version %S390_TOOLS_VERSION% -- Copyright IBM Corp. 2003, 2017 -+ Copyright IBM Corp. 2003, 2018 - EOD - } - -@@ -220,6 +223,24 @@ function SysfsCreateListCCW() { - ' | sort - } - -+# handle SCSI device not necessarily zfcp-attached, e.g. virtio-scsi-ccw -+function SCSISearchCCWBusid() -+{ -+ local SCSI_DEV=$1 -+ local SDEVCAN=$(readlink -e $SCSI_DEV) -+ while [ -n "$SDEVCAN" ]; do -+ # ascend to parent: strip last path part -+ SDEVCAN=${SDEVCAN%/*} -+ [ -h $SDEVCAN/subsystem ] || continue -+ local SUBSYSTEM=$(readlink -e $SDEVCAN/subsystem) -+ if [ "${SUBSYSTEM##*/}" = "ccw" ]; then -+ echo ${SDEVCAN##*/} -+ return -+ fi -+ done -+ echo "N/A" -+} -+ - function SysfsCreateListSCSI() - { - for SCSI_DEV in $1/bus/scsi/devices/*:*:*:*; do -@@ -249,11 +270,16 @@ function SysfsCreateListSCSI() - if [ -h $SG_DEV ]; then - # deprecated sysfs layout - SG_DEV=$(echo $SG_DEV | awk -F: '{print $NF}') -- else -+ elif [ -d $SCSI_DEV/scsi_generic ]; then - SG_DEV=$(basename $SG_DEV/*) -+ else -+ SG_DEV="" - fi - -- if [ "$SG_INQ" != "" ]; then -+ if [ -z "$SG_DEV" ]; then -+ SG_DEV="N/A" -+ TAPE_SERIAL="NO/SG" -+ elif [ "$SG_INQ" != "" ]; then - TAPE_SERIAL=$( - sg_inq /dev/$SG_DEV | - awk '/serial/{print $NF}' -@@ -291,17 +317,31 @@ function SysfsCreateListSCSI() - if [ "$CHG_IDX" != "" ]; then - TAPE_DEV=$CHG_IDX - fi -+ elif [ "$(echo "$SCSI_LIST"|grep lin_tape)" != "" ]; then -+ # bash glob sorts so IBMtape0 comes before IBMtape0n -+ local IBM_PATH=$( -+ ls -1d $SCSI_DEV/lin_tape/$DEV_NAME[0-9]* | -+ head -n 1) -+ if [ -d "$IBM_PATH" ]; then -+ IBM_IDX=${IBM_PATH##*/} -+ else -+ # deprecated sysfs layout -+ IBM_IDX=$( -+ echo "$SCSI_LIST" | -+ awk -F: '/lin_tape\:'"$DEV_NAME"'[0-9]+$/{print $NF}' -+ ) -+ fi -+ if [ "$IBM_IDX" != "" ]; then -+ TAPE_DEV=$IBM_IDX -+ fi - elif [ -r /proc/scsi/$DEV_NAME ]; then -- if [ "$TAPE_SERIAL" != "NO/INQ" ]; then - IBM_IDX=$( -- awk '$3 == "'$TAPE_SERIAL'"{ -- print $1 -- }' /proc/scsi/$DEV_NAME -+ grep -wF "$SCSI_ID" /proc/scsi/$DEV_NAME | -+ cut -d ' ' -f 1 - ) - if [ "$IBM_IDX" != "" ]; then - TAPE_DEV=$DEV_NAME$IBM_IDX - fi -- fi - fi - - printf "$SCSIFORMAT" \ -@@ -313,9 +353,16 @@ function SysfsCreateListSCSI() - $STATE - - if $VERBOSE; then -+ if [ -r $SCSI_DEV/hba_id ]; then -+ HBA_ID=$(cat $SCSI_DEV/hba_id) -+ else -+ HBA_ID=$(SCSISearchCCWBusid $SCSI_DEV) -+ fi -+ WWPN="N/A" -+ [ -r $SCSI_DEV/wwpn ] && WWPN=$(cat $SCSI_DEV/wwpn) - printf "$SCSIVFORMAT" \ -- $(cat $SCSI_DEV/hba_id) \ -- $(cat $SCSI_DEV/wwpn) \ -+ "$HBA_ID" \ -+ "$WWPN" \ - $TAPE_SERIAL - fi - done -diff --git a/zconf/lstape.8 b/zconf/lstape.8 -index 544cb08..019b6e5 100644 ---- a/zconf/lstape.8 -+++ b/zconf/lstape.8 -@@ -1,8 +1,8 @@ --.\" Copyright 2017 IBM Corp. -+.\" Copyright 2017, 2018 IBM Corp. - .\" s390-tools is free software; you can redistribute it and/or modify - .\" it under the terms of the MIT license. See LICENSE for details. - .\" --.TH LSTAPE 8 "Jul 2007" "s390-tools" -+.TH LSTAPE 8 "Jun 2018" "s390-tools" - - .SH NAME - lstape \- list tape devices. -@@ -20,6 +20,8 @@ lstape \- list tape devices. - .br - .RB [ -t - .IR [, ] "" ...] -+.br -+.RI [ ...] - - .SH DESCRIPTION - The lstape command lists all available tape devices on the current host. For -@@ -27,19 +29,8 @@ channel attached tape devices this output is the same as the contents of - /proc/tapedevices (which is obsolete) but also includes offline devices. By - default all tape devices are displayed. - --Since SCSI tape devices are accessed differently to channel attached tape --devices they are only visible if they are known to the SCSI layer. There --are at least two possible drivers that can claim a SCSI tape device and the --lstape command tries to find out which one this is. For the generic tape --and changer driver the device names start with "st" or "sch", while for the --IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown, --the correct driver could not be obtained. --This happens for example if there is no sg_inq command installed which is --required to read the drive's serial number which in turn is used to find out --the device number of the IBM tape driver. -- --The serial number of a SCSI tape can be displayed with the --verbose option. If --there is no sg_inq command available "NO/INQ" is shown as the tape's serial. -+The lstape command without the --ccw-only option causes extra SAN traffic -+for each SCSI tape or changer device by invoking the sg_inq command. - - .SH OPTIONS - .TP 8 -@@ -75,12 +66,64 @@ on the output of SCSI devices. - - .TP - .BR -t | --type " \fI\fR" --Limit output to given device types (currently only applies to channel attached -+Limit output to given device types, for example 3490 -+(currently only applies to channel-attached - tape devices). - - .TP --\fB\fR = --Device type of devices that should be displayed (e.g. 3490). -+.I -+Limits the output to information about the specified tape device or -+devices only. For CCW-attached devices only. -+ -+.SH OUTPUT FIELDS FOR SCSI TAPE/CHANGER DEVICES -+.TP -+.B Generic -+SCSI generic device file for the tape drive, for example /dev/sg0. -+"N/A" if the SCSI generic (sg) kernel functionality is not available. -+.TP -+.B Device -+Main character device node file for accessing the tape drive or medium changer. -+SCSI tape devices are only visible if they are known to the SCSI layer. There -+are at least two possible drivers that can claim a SCSI tape device. The -+lstape command tries to determine the device driver. For the generic tape -+and changer driver the device names start with "st" or "sch", while for the -+IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown, -+the device driver could not be determined. -+.TP -+.B Target -+Linux SCSI device name in H:C:T:L format. -+.TP -+.B Vendor -+The vendor field from the SCSI device. -+.TP -+.B Model -+The model field from the SCSI device. -+.TP -+.B Type -+"tapedrv" for a tape drive or "changer" for a medium changer. -+.TP -+.B State -+The state of the SCSI device object in the kernel. -+Any state other than "running" can indicate problems. -+ -+.PP -+ -+For SCSI devices, the --verbose option additionally displays: -+.TP -+.B HBA -+The device bus-ID of the FCP device -+or of the virtio-scsi-ccw virtual HBA -+through which the tape drive is attached. -+"N/A" if the device does not have a sysfs ancestor with subsystem ccw. -+.TP -+.B WWPN -+The WWPN (worldwide port name) of the tape drive in the SAN. -+"N/A" if device is not attached through zfcp. -+.TP -+.B Serial -+The serial number. -+"NO/INQ" if there is no sg_inq command available. -+"NO/SG" if no SCSI generic (sg) kernel support is available. - - .SH EXAMPLES - \fBlstape\fR -@@ -92,3 +135,8 @@ List all tape devices that are available - .RS - Show all 3490 CCW devices that are online. - .RE -+ -+\fBlstape --scsi-only --verbose\fR -+.RS -+Show all SCSI tape or changer devices with maximum information. -+.RE --- -2.21.3 - - -From 164d283c73cc17b8a5927a449ed59671545f7066 Mon Sep 17 00:00:00 2001 -From: Javier Martinez Canillas -Date: Tue, 1 Oct 2019 15:33:59 +0200 -Subject: [PATCH 38/57] zipl: fix the scanned tokens array size calculation - (#1751587) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The zipl config file (zipl.conf) and the BootLoaderSpec (BLS) fragments -in /boot/loader/entries define a set of tokens that are parsed by zipl. - -These are stored in an array of tokens whose size is calculated to make -sure that there is enough memory allocated for all the scanned tokens. - -But the size calculation logic was wrong, since it was checking if the -current size was enough to store a single token per BLS fragment, while -up to 4 tokens can be defined in a BLS file: a section heading and the -image, ramdisk and parameter keywords. - -This led to zipl being killed by a SIGABRT signal when trying to parse -more tokens than the ones that could fit in the scanned tokens array: - -Using config file '/etc/zipl.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-9.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-8.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-7.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-6.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-5.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-4.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-3.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-2.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-1.conf' -Using BLS config file '/boot/loader/entries/vmlinuz-0.conf' -double free or corruption (out) -Aborted (core dumped) - -Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/68 -Closes: https://github.com/ibm-s390-tools/s390-tools/pull/73 -Signed-off-by: Javier Martinez Canillas -Signed-off-by: Stefan Haberland -Signed-off-by: Jan Höppner - -(cherry picked from commit 8bfe18e674e17a20682dbd1fa0e4813c789e22e5) ---- - zipl/src/scan.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index e57361e..ee04251 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -751,8 +751,14 @@ scan_bls(const char* blsdir, struct scan_token** token, int scan_size) - - remaining = scan_size - count; - -- if (remaining < n) { -- size = scan_size - remaining + n; -+ /* The array of scanned tokens is allocated when the zipl config file is -+ * parsed. Its size is a multiple of INITIAL_ARRAY_LENGTH so it may have -+ * enough space to scan all the tokens that are defined in the BLS files. -+ * Calculate if is enough assuming that a BLS fragment can contain up to -+ * 4 tokens: a section heading and 3 keywords (image, ramdisk, parameter). -+ */ -+ if (remaining < n * 4) { -+ size = scan_size - remaining + (n * 4); - buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token)); - if (!buffer) - goto err; --- -2.21.3 - - -From ba8021cb17f74ea5130d7c5acdc267b6fb0a433c Mon Sep 17 00:00:00 2001 -From: Eric Sandeen -Date: Wed, 12 Sep 2018 09:40:22 -0500 -Subject: [PATCH 39/57] zipl: use FIEMAP mapping ioctl if it exists (moved from - Patch101) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -zipl currently uses the FIBMAP ioctl to map blocks for the bootloader; -on XFS, if FIBMAP is requested on a reflinked file, it will fail - -and FIBMAP returns 0 in this case, which is indistinguishable from a -hole. This causes boot to fail because the file is not mapped. - -We can use the FIEMAP ioctl instead, which is able to map reflinked -files. While FIEMAP is able to map entire extents at once, here we -simply use it to obtain the mapping block-by-block so that it fits -in with the current FIBMAP calls. - -Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/34 -Closes: https://github.com/ibm-s390-tools/s390-tools/pull/36 -Signed-off-by: Eric Sandeen -Reviewed-by: Peter Oberparleiter -Reviewed-by: Stefan Haberland -Tested-by: Stefan Haberland -Signed-off-by: Jan Höppner - -(cherry picked from commit 07e30951f8f31cdb5ea3cbd020254239be522e6a) ---- - zipl/src/disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 53 insertions(+), 4 deletions(-) - -diff --git a/zipl/src/disk.c b/zipl/src/disk.c -index 0d8e779..43092bf 100644 ---- a/zipl/src/disk.c -+++ b/zipl/src/disk.c -@@ -21,6 +21,8 @@ - #include - #include - #include -+#include -+#include - - #include "lib/util_proc.h" - -@@ -550,8 +552,12 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, - { - struct statfs buf; - blocknum_t phy_per_fs; -- int mapped; -+ blocknum_t mapped; -+ int block; - int subblock; -+ int fiemap_size; -+ int map_offset; -+ struct fiemap *fiemap; - - /* No file system: partition or raw disk */ - if (info->fs_block_size == -1) { -@@ -576,12 +582,55 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, - } - /* Get mapping in file system blocks */ - phy_per_fs = info->fs_block_size / info->phy_block_size; -- mapped = logical / phy_per_fs; - subblock = logical % phy_per_fs; -- if (ioctl(fd, FIBMAP, &mapped)) { -- error_reason("Could not get file mapping"); -+ -+ /* First try FIEMAP, more complicated to set up */ -+ fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent); -+ -+ fiemap = misc_malloc(fiemap_size); -+ if (!fiemap) - return -1; -+ memset(fiemap, 0, fiemap_size); -+ -+ fiemap->fm_extent_count = 1; -+ fiemap->fm_flags = FIEMAP_FLAG_SYNC; -+ /* fm_start, fm_length in bytes; logical is in physical block units */ -+ fiemap->fm_start = logical * info->phy_block_size; -+ fiemap->fm_length = info->phy_block_size; -+ -+ if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) { -+ /* FIEMAP failed, fall back to FIBMAP */ -+ block = logical / phy_per_fs; -+ if (ioctl(fd, FIBMAP, &block)) { -+ error_reason("Could not get file mapping"); -+ free(fiemap); -+ return -1; -+ } -+ mapped = block; -+ } else { -+ if (fiemap->fm_mapped_extents) { -+ if (fiemap->fm_extents[0].fe_flags & -+ FIEMAP_EXTENT_ENCODED) { -+ error_reason("File mapping is encoded"); -+ free(fiemap); -+ return -1; -+ } -+ /* -+ * returned extent may start prior to our request -+ */ -+ map_offset = fiemap->fm_start - -+ fiemap->fm_extents[0].fe_logical; -+ mapped = fiemap->fm_extents[0].fe_physical + -+ map_offset; -+ /* set mapped to fs block units */ -+ mapped = mapped / info->fs_block_size; -+ } else { -+ mapped = 0; -+ } - } -+ -+ free(fiemap); -+ - if (mapped == 0) { - /* This is a hole in the file */ - *physical = 0; --- -2.21.3 - - -From 658eef5bc3ea1ec9b917c35a36dcbe1219bd0b08 Mon Sep 17 00:00:00 2001 -From: Javier Martinez Canillas -Date: Tue, 6 Nov 2018 13:29:32 +0100 -Subject: [PATCH 40/57] zipl: use the BLS "title" field as the IPL section name - (moved from Patch103) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Most bootloaders use the BootLoaderSpec "title" field to name the entries -in their boot menu. The zipl bootloader used the "version" field instead, -since it was wrongly assumed that the zipl boot menu didn't support names -that contained spaces, which are usually present in a BLS "title" field. - -But this is not the case, names with space characters are supported by the -IPL and is just a constraint of the section heading in the zipl.conf file. - -So to be consistent with all the other bootloaders, use the "title" field -also on zipl when populating the boot menu entries from BLS files. - -Closes: https://github.com/ibm-s390-tools/s390-tools/pull/47 -Signed-off-by: Javier Martinez Canillas -Reviewed-by: Peter Oberparleiter -Reviewed-by: Stefan Haberland sth@linux.ibm.com -Signed-off-by: Jan Höppner - -(cherry picked from commit 49510442133e63bfd650a7b7e27e388d38baba81) ---- - scripts/zipl-switch-to-blscfg | 1 + - zipl/src/scan.c | 2 +- - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/scripts/zipl-switch-to-blscfg b/scripts/zipl-switch-to-blscfg -index 5272033..5141b1c 100755 ---- a/scripts/zipl-switch-to-blscfg -+++ b/scripts/zipl-switch-to-blscfg -@@ -150,6 +150,7 @@ while IFS='= ' read key val; do - if [ -f "${OUTPUT}" ]; then - print_error "BLS file ${OUTPUT} already exists" - fi -+ echo "title $section" >> ${OUTPUT} - fi - elif [[ $val ]]; then - val="$(echo $val | sed -e 's/^[ \t"]*//;s/[ \t"]*$//')" -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index ee04251..b8fea13 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -702,7 +702,7 @@ scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan, - file->buffer[key_end] = '\0'; - file->buffer[val_end] = '\0'; - -- if (strncmp("version", &file->buffer[key_start], key_end - key_start) == 0) { -+ if (strncmp("title", &file->buffer[key_start], key_end - key_start) == 0) { - scan_append_section_heading(scan, index, &file->buffer[val_start]); - } - --- -2.21.3 - - -From 2515287113812346ef8a3bb00a80632b698ab542 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 7 Nov 2019 14:37:36 +0100 -Subject: [PATCH 41/57] zipl: config file handling improvements for CoreOS - (#1764706) - -Use TOOLS_SYSCONFDIR for the default zipl.conf path - c01bcfa73a6d795f33df639ba36304a704d13561 -zipl: ship a minimal zipl.conf - 19259aeb656df5cbc7bda89599442dd51d80a7a8 -zipl: allow check for other locations of zipl.conf - 275105fe3d64d214f982a5c0eb113d806853919b -zipl: add value of target= as search path for BLS case - d71628326d80e623fc9f008fe4ea93edb5592b2e ---- - zipl/Makefile | 1 + - zipl/doc/zipl.conf.minimal | 10 ++++ - zipl/include/scan.h | 1 + - zipl/include/zipl.h | 4 +- - zipl/man/Makefile | 10 +++- - zipl/man/{zipl.8 => zipl.8.in} | 2 +- - zipl/man/{zipl.conf.5 => zipl.conf.5.in} | 17 +++++-- - zipl/src/job.c | 23 +++++++++- - zipl/src/scan.c | 58 ++++++++++++++++++++++++ - 9 files changed, 117 insertions(+), 9 deletions(-) - create mode 100644 zipl/doc/zipl.conf.minimal - rename zipl/man/{zipl.8 => zipl.8.in} (99%) - rename zipl/man/{zipl.conf.5 => zipl.conf.5.in} (96%) - -diff --git a/zipl/Makefile b/zipl/Makefile -index a02e997..931b113 100644 ---- a/zipl/Makefile -+++ b/zipl/Makefile -@@ -8,6 +8,7 @@ all: - install: all - $(MAKE) -C src install - $(MAKE) -C man install -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 doc/zipl.conf.minimal $(DESTDIR)$(TOOLS_LIBDIR)/zipl.conf - - clean: - $(MAKE) -C src clean -diff --git a/zipl/doc/zipl.conf.minimal b/zipl/doc/zipl.conf.minimal -new file mode 100644 -index 0000000..9d6fa5e ---- /dev/null -+++ b/zipl/doc/zipl.conf.minimal -@@ -0,0 +1,10 @@ -+# This is an example of a minimal zipl.conf file that can be used when the -+# sections are defined in BootLoaderSpec fragments files. -+# -+# See the zipl and zipl.conf man page for more details. -+[defaultboot] -+defaultauto -+prompt=1 -+timeout=5 -+secure=auto -+target=/boot -diff --git a/zipl/include/scan.h b/zipl/include/scan.h -index 4dcee0b..f39e96e 100644 ---- a/zipl/include/scan.h -+++ b/zipl/include/scan.h -@@ -126,6 +126,7 @@ char* scan_keyword_name(enum scan_keyword_id id); - int scan_check_defaultboot(struct scan_token* scan); - struct scan_token* scan_build_automenu(struct scan_token* scan); - int scan_check(struct scan_token* scan); -+int scan_check_bls(struct scan_token *scan); - int scan_find_section(struct scan_token* scan, char* name, enum scan_id type, - int offset); - int scan_check_section_data(char* keyword[], int* line, char* name, -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index 6f2d115..0ef06e4 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -47,7 +47,9 @@ - #define DEFAULTBOOT_SECTION "defaultboot" - - #define ZIPL_CONF_VAR "ZIPLCONF" --#define ZIPL_DEFAULT_CONF "/etc/zipl.conf" -+#define ZIPL_RUNTIME_CONF "/run/zipl/zipl.conf" -+#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf" -+#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf" - #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" - #define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" - #define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure" -diff --git a/zipl/man/Makefile b/zipl/man/Makefile -index 6439205..1d8dbe3 100644 ---- a/zipl/man/Makefile -+++ b/zipl/man/Makefile -@@ -6,7 +6,13 @@ all: - install: - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man5 - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 -- $(INSTALL) -m 644 -c zipl.8 $(DESTDIR)$(MANDIR)/man8 -- $(INSTALL) -m 644 -c zipl.conf.5 $(DESTDIR)$(MANDIR)/man5 -+ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \ -+ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \ -+ > $(DESTDIR)$(MANDIR)/man8/zipl.8 -+ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \ -+ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \ -+ > $(DESTDIR)$(MANDIR)/man5/zipl.conf.5 -+ chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \ -+ $(DESTDIR)$(MANDIR)/man5/zipl.conf.5 - - clean: -diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8.in -similarity index 99% -rename from zipl/man/zipl.8 -rename to zipl/man/zipl.8.in -index 2f873f3..f12e27b 100644 ---- a/zipl/man/zipl.8 -+++ b/zipl/man/zipl.8.in -@@ -120,7 +120,7 @@ Print version information, then exit. - .TP - .BR "\-c " " or " "\-\-config=" - Use the specified . If none is supplied, the environment --variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used. -+variable ZIPLCONF is evaluated if set, otherwise %SYSCONFDIR%/zipl.conf is used. - - .TP - .BR "\-b " " or " "\-\-blsdir=" -diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5.in -similarity index 96% -rename from zipl/man/zipl.conf.5 -rename to zipl/man/zipl.conf.5.in -index 71d1252..ef40287 100644 ---- a/zipl/man/zipl.conf.5 -+++ b/zipl/man/zipl.conf.5.in -@@ -17,8 +17,13 @@ boot loader tool - .BR zipl (8)). - .br - --By default this configuration file is located at /etc/zipl.conf. A different --location may be specified either using the '\-\-config' option of -+By default -+.B zipl -+checks for -+.I zipl.conf -+at /run/zipl/zipl.conf, %SYSCONFDIR%/zipl.conf, %TOOLS_LIBDIR%/zipl.conf in that -+order - whichever is found first will be used. Users can specifically choose a -+location using the '\-\-config' option of - .B zipl - or by setting the ZIPLCONF shell environment variable. - .br -@@ -138,7 +143,13 @@ initrd /initramfs-4.15.9 - options root=/dev/dasda1 console=ttyS0 - .PP - --The location of the linux and initrd has to be specified relative to the boot partition. The BLS config files are only used to specify the IPL sections, a zipl.conf configuration files is still needed for global parameters. -+The location of the linux and initrd has to be specified relative to the boot -+partition. The BLS config files are only used to specify the IPL sections, a -+zipl.conf configuration file is still needed for global parameters. For this -+purpose, a minimal zipl.conf configuration file is shipped at -+%TOOLS_LIBDIR%/zipl.conf which would help when used with BLS config files, by -+not requiring users to create the traditional configuration file at -+%SYSCONFDIR%/zipl.conf. - - .B Boot menu - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 1178a7d..7d61c84 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -59,6 +59,14 @@ static struct option options[] = { - /* Command line option abbreviations */ - static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:"; - -+/* Locations of zipl.conf configuration file */ -+static const char *zipl_conf[] = { -+ ZIPL_RUNTIME_CONF, -+ ZIPL_DEFAULT_CONF, -+ ZIPL_MINIMAL_CONF, -+ NULL -+}; -+ - struct command_line { - char* data[SCAN_KEYWORD_NUM]; - char* config; -@@ -1783,7 +1791,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - char* filename; - char *blsdir; - char* source; -- int rc, scan_size; -+ int i, rc, scan_size; - - /* Read configuration file */ - if (cmdline->config != NULL) { -@@ -1797,7 +1805,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - ZIPL_CONF_VAR ")"; - } else { - /* Use default config file */ -- filename = ZIPL_DEFAULT_CONF; -+ for (i = 0; zipl_conf[i]; i++) { -+ if (misc_check_readable_file(zipl_conf[i]) == 0) { -+ filename = zipl_conf[i]; -+ break; -+ } -+ } - source = ""; - } - printf("Using config file '%s'%s\n", filename, source); -@@ -1840,6 +1853,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - scan_free(scan); - return rc; - } -+ rc = scan_check_bls(scan); -+ if (rc) { -+ error_text("BLS parsing '%s'", blsdir); -+ scan_free(scan); -+ return rc; -+ } - /* Get job from config file data */ - if (cmdline->menu != NULL) - rc = get_menu_job(scan, cmdline->menu, job); -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index b8fea13..f4228d3 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -1559,6 +1559,64 @@ scan_check(struct scan_token* scan) - return 0; - } - -+/* -+ * Check if kernel and initrd image paths provided by BLS files are readable. -+ * If not, add value of 'scan_keyword_target' into search path and silently -+ * update scan list. -+ */ -+int -+scan_check_bls(struct scan_token *scan) -+{ -+ int i, rc; -+ char *target_value = NULL; -+ char *img_value = NULL; -+ char *buffer = NULL; -+ /* -+ * In the BLS case, each BLS section heading inherits a keyword -+ * assignment target= from zipl.conf, and they are all the same. -+ * -+ */ -+ for (i = 0 ; scan[i].id != scan_id_empty; i++) { -+ if (scan[i].id == scan_id_keyword_assignment && -+ scan[i].content.keyword.keyword == scan_keyword_target) { -+ target_value = scan[i].content.keyword.value; -+ break; -+ } -+ } -+ if (!target_value) -+ return -1; -+ for (i = 0 ; scan[i].id != scan_id_empty; i++) { -+ if (scan[i].id != scan_id_keyword_assignment) -+ continue; -+ if (scan[i].content.keyword.keyword == scan_keyword_image || -+ scan[i].content.keyword.keyword == scan_keyword_ramdisk) { -+ -+ rc = misc_check_readable_file( -+ scan[i].content.keyword.value); -+ if (rc) { -+ misc_asprintf(&img_value, "%s%s", -+ target_value, -+ scan[i].content.keyword.value); -+ rc = misc_check_readable_file(img_value); -+ if (rc) { -+ error_text( -+ "Image file '%s' is not accessible", -+ scan[i].content.keyword.value); -+ return rc; -+ } -+ buffer = (char *) -+ misc_malloc(strlen(img_value) + 1); -+ if (buffer == NULL) -+ return -1; -+ memcpy(buffer, img_value, strlen(img_value)); -+ buffer[strlen(img_value)] = 0; -+ free(scan[i].content.keyword.value); -+ scan[i].content.keyword.value = buffer; -+ } -+ } -+ } -+ return 0; -+} - - static int - scan_get_defaultboot_type(char* keyword[], int line[], int section_line, --- -2.21.3 - - -From 1ca71226d392c59855432ee992e263be680644d3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 27 Jan 2020 11:22:42 +0100 -Subject: [PATCH 42/57] zkey: Fix display of clear key size for XTS keys - (#1794375) - -Description: zkey: Fix display of clear key size for XTS keys -Symptom: The 'zkey list' command shows bogus values for the - keys 'Clear key size' for XTS keys of type CCA-AESDATA - or CCA-AESCIPHER. -Problem: XTS keys consist of 2 keys concatenated to each other. - To calculate the clear key size, the clear key size of - both keys must be added. The code does not address the - second key correctly, and thus reads the clear key size - of the second key from an invalid memory location. This - results in bogus values reported as clear key size. - This bug has been introduced with feature SEC1717 "Cipher - key support" with commit 298fab68fee8 "zkey: Preparations - for introducing a new key type". -Solution: Correct the addressing of the second key. -Reproduction: Generate an XTS key of type CCA-AESDATA or CCA-AESCIPHER - and then run 'zkey list'. -Upstream-ID: e7f446432b92b293e758099842843cfb1f18fa97 ---- - zkey/pkey.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index 462f9fe..640ff86 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -1591,8 +1591,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - if (is_cca_aes_data_key(key, key_size)) { - *bitsize = datakey->bitsize; - if (key_size == 2 * AESDATA_KEY_SIZE) { -- datakey = (struct aesdatakeytoken *)key + -- AESDATA_KEY_SIZE; -+ datakey = (struct aesdatakeytoken *)(key + -+ AESDATA_KEY_SIZE); - *bitsize += datakey->bitsize; - } - } else if (is_cca_aes_cipher_key(key, key_size)) { -@@ -1601,8 +1601,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - else - *bitsize = 0; /* Unknown */ - if (key_size > cipherkey->length) { -- cipherkey = (struct aescipherkeytoken *)key + -- cipherkey->length; -+ cipherkey = (struct aescipherkeytoken *)(key + -+ cipherkey->length); - if (cipherkey->pfv == 0x00) /* V0 payload */ - *bitsize += cipherkey->pl - 384; - } --- -2.21.3 - - -From 2beeedb4f5eca1cf2faf34d1a0db176ea887854f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 27 Jan 2020 11:39:24 +0100 -Subject: [PATCH 43/57] zkey: Fix listing of keys on file systems reporting - DT_UNKNOWN (#1792957) - -Description: zkey: Fix listing of keys on file systems reporting DT_UNKNOWN. -Symptom: When the zkey key repository is located in a file system that - does not have full support for report the file type, such as - XFS, the 'zkey list' command does not show any keys, although - keys exist in the repository. -Problem: The zkey list function uses scandir() to look for files in the - zkey key repository directory. It checks the dirent.d_type field - to consider only regular files, but skips all others. File - systems that do not have full support for returning the file - type in d_type will return DT_UNKNOWN instead. zkey skips - those directory entries and thus does not show any keys. -Solution: Also consider directory entries with d_type = DT_UNKNOWN. -Reproduction: Use zkey with a zkey repository directory located in a file - system that does not have full support for returning the file - type, such as XFS. Generate a key in the repository and then - list the key s with 'zkey list'. - Note: Newly created XFS file systems usually support returning - the file type, but existing XFS file systems might not. To - create an XFS file system that does not support returning the - file type, use 'mkfs.xfs -f -m crc=0 -n ftype=0' to create - the file system. -Upstream-ID: 0de533aef9def920fed751c6025e4f19c4cba763 ---- - zkey/keystore.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zkey/keystore.c b/zkey/keystore.c -index af67721..e6be3a4 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -906,7 +906,7 @@ static int _keystore_info_file_filter(const struct dirent *dirent) - { - size_t len; - -- if (dirent->d_type != DT_REG) -+ if (dirent->d_type != DT_REG && dirent->d_type != DT_UNKNOWN) - return 0; - - len = strlen(dirent->d_name); --- -2.21.3 - - -From 931db1dba8822a14ad15c52ae95e0d8a219dd94b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:29:10 +0200 -Subject: [PATCH 44/57] zipl/libc: Fix potential buffer overflow in printf - (#1807973) - -Description: zipl/libc: Fix potential buffer overflow in printf -Symptom: Crash of the zipl boot loader during boot. -Problem: The zipl boot loaders have their own minimalistic libc - implementation. In it printf and sprintf use vsprintf for string - formatting. Per definition vsprintf assumes that the buffer it - writes to is large enough to contain the formatted string and - performs no size checks. This is problematic for the boot - loaders because the buffer they use are often allocated on the - stack. Thus even small changes to the string format can - potentially cause buffer overflows on the stack. -Solution: Implement vsnprintf and make use of it. -Reproduction: Use printf to print a string with >81 characters (exact number - depends on the stack layout/compiler used). -Upstream-ID: 6fe9e6c55c69c14971dca55551009f5060418aae -Upstream-ID: 8874b908254c47c8a6fd7a1aca2c7371c11035c4 -Upstream-ID: f7430027b41d5ad6220e962a179c2a5213330a44 -Upstream-ID: 36fed0e6c6590631c4ce1707c8fe3c3397bcce4d ---- - zipl/boot/libc.c | 360 ++++++++++++++++++++++++++++-------------- - zipl/boot/libc.h | 4 +- - zipl/boot/menu.c | 2 +- - zipl/boot/menu.h | 1 - - zipl/boot/tape2dump.c | 2 +- - 5 files changed, 248 insertions(+), 121 deletions(-) - -diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c -index 5944c4e..bd88d15 100644 ---- a/zipl/boot/libc.c -+++ b/zipl/boot/libc.c -@@ -125,81 +125,6 @@ int strncmp(const char *s1, const char *s2, unsigned long count) - return 0; - } - --/* -- * Convert number to string -- * -- * Parameters: -- * -- * - buf: Output buffer -- * - base: Base used for formatting (e.g. 10 or 16) -- * - val: Number to format -- * - zero: If > 0, fill with leading zeros, otherwise use blanks -- * - count: Minimum number of characters used for output string -- */ --static int num_to_str(char *buf, int base, unsigned long val, int zero, -- unsigned long count) --{ -- static const char conv_vec[] = {'0', '1', '2', '3', '4', '5', '6', '7', -- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; -- unsigned long num = 0, val_work = val, in_number = 1; -- int i; -- -- /* Count number of characters needed for number */ -- do { -- num++; -- val_work /= base; -- } while (val_work); -- /* Real character number overwrites count */ -- if (count < num) -- count = num; -- /* Format number */ -- for (i = count - 1; i >= 0; i--) { -- if (in_number) { -- buf[i] = conv_vec[val % base]; -- val /= base; -- in_number = val ? 1 : 0; -- } else { -- buf[i] = zero ? '0' : ' '; -- } -- } -- buf[count] = 0; -- return count; --} -- --/* -- * Convert string to string with indentation -- */ --static int str_to_str(char *buf, const char *str, unsigned long count) --{ -- unsigned long size; -- -- size = strlen(str); -- if (count < size) -- count = size; -- else -- memset(buf, ' ', count - size); -- strcpy(buf + (count - size), str); -- return count; --} -- --/* -- * Convert string to number with given base -- */ --unsigned long strtoul(const char *nptr, char **endptr, int base) --{ -- unsigned long val = 0; -- -- while (isdigit(*nptr)) { -- if (val != 0) -- val *= base; -- val += *nptr - '0'; -- nptr++; -- } -- if (endptr) -- *endptr = (char *) nptr; -- return val; --} -- - /* - * Convert ebcdic string to number with given base - */ -@@ -218,79 +143,282 @@ unsigned long ebcstrtoul(char *nptr, char **endptr, int base) - return val; - } - --/* -- * Convert string to number with given base -- */ --static int sprintf_fmt(char type, char *buf, unsigned long val, int zero, -- int count) -+static int skip_atoi(const char **c) - { -- switch (type) { -+ int i = 0; -+ -+ do { -+ i = i*10 + *((*c)++) - '0'; -+ } while (isdigit(**c)); -+ -+ return i; -+} -+ -+enum format_type { -+ FORMAT_TYPE_NONE, -+ FORMAT_TYPE_STR, -+ FORMAT_TYPE_ULONG, -+}; -+ -+struct printf_spec { -+ unsigned int type:8; /* format_type enum */ -+ signed int field_width:24; /* width of output field */ -+ unsigned int zeropad:1; /* pad numbers with zero */ -+ unsigned int base:8; /* number base, 8, 10 or 16 only */ -+ signed int precision:16; /* # of digits/chars */ -+}; -+ -+#define FIELD_WIDTH_MAX ((1 << 23) - 1) -+ -+static int format_decode(const char *fmt, struct printf_spec *spec) -+{ -+ const char *start = fmt; -+ -+ spec->type = FORMAT_TYPE_NONE; -+ while (*fmt) { -+ if (*fmt == '%') -+ break; -+ fmt++; -+ } -+ -+ /* return current non-format string */ -+ if (fmt != start || !*fmt) -+ return fmt - start; -+ -+ /* first char is '%', skip it */ -+ fmt++; -+ if (*fmt == '0') { -+ spec->zeropad = 1; -+ fmt++; -+ } -+ -+ spec->field_width = -1; -+ if (isdigit(*fmt)) -+ spec->field_width = skip_atoi(&fmt); -+ -+ spec->precision = -1; -+ if (*fmt == '.') { -+ fmt++; -+ if (isdigit(*fmt)) -+ spec->precision = skip_atoi(&fmt); -+ } -+ -+ /* always use long form, i.e. ignore long qualifier */ -+ if (*fmt == 'l') -+ fmt++; -+ -+ switch (*fmt) { - case 's': -- return str_to_str(buf, (const char *) val, count); -- case 'x': -- return num_to_str(buf, 16, val, zero, count); -+ spec->type = FORMAT_TYPE_STR; -+ break; -+ -+ case 'o': -+ spec->base = 8; -+ spec->type = FORMAT_TYPE_ULONG; -+ break; -+ - case 'u': -- return num_to_str(buf, 10, val, zero, count); -+ spec->base = 10; -+ spec->type = FORMAT_TYPE_ULONG; -+ break; -+ -+ case 'x': -+ spec->base = 16; -+ spec->type = FORMAT_TYPE_ULONG; -+ break; -+ - default: - libc_stop(EINTERNAL); - } -- return 0; -+ -+ return ++fmt - start; -+} -+ -+static char *string(char *buf, char *end, const char *s, -+ struct printf_spec *spec) -+{ -+ int limit = spec->precision; -+ int len = 0; -+ int spaces; -+ -+ /* Copy string to buffer */ -+ while (limit--) { -+ char c = *s++; -+ if (!c) -+ break; -+ if (buf < end) -+ *buf = c; -+ buf++; -+ len++; -+ } -+ -+ /* right align if necessary */ -+ if (len < spec->field_width && buf < end) { -+ spaces = spec->field_width - len; -+ if (spaces >= end - buf) -+ spaces = end - buf; -+ memmove(buf + spaces, buf, len); -+ memset(buf, ' ', spaces); -+ buf += spaces; -+ } -+ -+ return buf; -+} -+ -+static char *number(char *buf, char *end, unsigned long val, -+ struct printf_spec *spec) -+{ -+ /* temporary buffer to prepare the string. -+ * Worst case: base = 8 -> 3 bits per char -> 2.67 chars per byte */ -+ char tmp[3 * sizeof(val)]; -+ static const char vec[] = {'0', '1', '2', '3', '4', '5', '6', '7', -+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; -+ int field_width = spec->field_width; -+ int precision = spec->precision; -+ int len; -+ -+ /* prepare string in reverse order */ -+ len = 0; -+ while (val) { -+ tmp[len++] = vec[val % spec->base]; -+ val /= spec->base; -+ } -+ -+ if (len > precision) -+ precision = len; -+ -+ field_width -= precision; -+ while (field_width-- > 0) { -+ char c = spec->zeropad ? '0' : ' '; -+ if (buf < end) -+ *buf = c; -+ buf++; -+ } -+ -+ /* needed if no field width but a precision is given */ -+ while (len < precision--) { -+ if (buf < end) -+ *buf = '0'; -+ buf++; -+ } -+ -+ while (len-- > 0) { -+ if (buf < end) -+ *buf = tmp[len]; -+ buf++; -+ } -+ -+ return buf; - } - - /* -- * Print formated string (va version) -+ * vsnprintf - Format string and place in a buffer -+ * -+ * This funcion only supports a subset of format options defined in the -+ * C standard, i.e. -+ * specifiers: -+ * * %s (strings) -+ * * %o (unsigned int octal) -+ * * %u (unsigned int decimal) -+ * * %x (unsigned int hexadecimal) -+ * -+ * length modifier: -+ * * 'l' (ignored, see below) -+ * -+ * flag: -+ * * '0' (zero padding for integers) -+ * -+ * precision and field width as integers, i.e. _not_ by asterix '*'. -+ * -+ * The integer specifiers (o, u and, x) always use the long form, i.e. -+ * assume the argument to be of type 'unsigned long int'. -+ * -+ * Returns the number of characters the function would have generated for -+ * the given input (excluding the trailing '\0'. If the return value is -+ * greater than or equal @size the resulting string is trunctuated. - */ --static void vsprintf(char *str, const char *fmt, va_list va) -+static int vsnprintf(char *buf, unsigned long size, const char *fmt, -+ va_list args) - { -- unsigned long val, zero, count; -- char *fmt_next; -+ struct printf_spec spec = {0}; -+ char *str, *end; - -- do { -- if (*fmt == '%') { -- fmt++; -- if (*fmt == '0') { -- zero = 1; -- fmt++; -- } else { -- zero = 0; -+ str = buf; -+ end = buf + size; -+ -+ /* use negative (large positive) buffer sizes as indication for -+ * unknown/unlimited buffer sizes. */ -+ if (end < buf) { -+ end = ((void *)-1); -+ size = end - buf; -+ } -+ -+ while (*fmt) { -+ const char *old_fmt = fmt; -+ int read = format_decode(fmt, &spec); -+ int copy; -+ -+ fmt += read; -+ -+ switch (spec.type) { -+ case FORMAT_TYPE_NONE: -+ copy = read; -+ if (str < end) { -+ if (copy > end - str) -+ copy = end - str; -+ memcpy(str, old_fmt, copy); - } -- /* No number found by strtoul: count=0 fmt_next=fmt */ -- count = strtoul(fmt, &fmt_next, 10); -- fmt = fmt_next; -- if (*fmt == 'l') -- fmt++; -- val = va_arg(va, unsigned long); -- str += sprintf_fmt(*fmt, str, val, zero, count); -- fmt++; -- } else { -- *str++ = *fmt++; -+ str += read; -+ break; -+ -+ case FORMAT_TYPE_STR: -+ str = string(str, end, va_arg(args, char *), &spec); -+ break; -+ -+ case FORMAT_TYPE_ULONG: -+ str = number(str, end, va_arg(args, unsigned long), -+ &spec); -+ break; - } -- } while (*fmt); -- *str = 0; -+ } -+ -+ if (size) { -+ if (str < end) -+ *str = '\0'; -+ else -+ end[-1] = '\0'; -+ } -+ return str - buf; - } - - /* -- * Write formated string to string -+ * Write formatted string to buffer - */ --void sprintf(char *str, const char *fmt, ...) -+void snprintf(char *buf, unsigned long size, const char *fmt, ...) - { - va_list va; - - va_start(va, fmt); -- vsprintf(str, fmt, va); -+ vsnprintf(buf, size, fmt, va); - va_end(va); - } - - /* -- * Print formated string -+ * Print formatted string to console - */ - void printf(const char *fmt, ...) - { -- char buf[81]; -+ char buf[LINE_LENGTH + 1]; -+ int len; - va_list va; - - va_start(va, fmt); -- vsprintf(buf, fmt, va); -+ len = vsnprintf(buf, sizeof(buf), fmt, va); -+ if (len > LINE_LENGTH) { -+ buf[LINE_LENGTH - 1] = '.'; -+ buf[LINE_LENGTH - 2] = '.'; -+ buf[LINE_LENGTH - 3] = '.'; -+ } - sclp_print(buf); - va_end(va); - } -diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h -index 44097fa..68ecd00 100644 ---- a/zipl/boot/libc.h -+++ b/zipl/boot/libc.h -@@ -40,6 +40,7 @@ - #define ENOTTY 25 /* Not a typewriter */ - - #define MIB (1024ULL * 1024) -+#define LINE_LENGTH 80 /* max line length printed by printf */ - - typedef unsigned long long uint64_t; - typedef unsigned int uint32_t; -@@ -47,13 +48,12 @@ typedef unsigned short uint16_t; - typedef unsigned char uint8_t; - - void printf(const char *, ...); --void sprintf(char *, const char *, ...); -+void snprintf(char *buf, unsigned long size, const char *fmt, ...); - void *memcpy(void *, const void *, unsigned long); - void *memmove(void *, const void *, unsigned long); - void *memset(void *, int c, unsigned long); - char *strcat(char *, const char *); - int strncmp(const char *, const char *, unsigned long); --unsigned long strtoul(const char *, char **, int); - unsigned long ebcstrtoul(char *, char **, int); - int strlen(const char *); - char *strcpy(char *, const char *); -diff --git a/zipl/boot/menu.c b/zipl/boot/menu.c -index 3f68620..35ac4de 100644 ---- a/zipl/boot/menu.c -+++ b/zipl/boot/menu.c -@@ -186,7 +186,7 @@ boot: - (void *)&__stage2_params + TEXT_OFFSET)); - - /* append 'BOOT_IMAGE=' to parmline */ -- sprintf(endstring, " BOOT_IMAGE=%u", value); -+ snprintf(endstring, sizeof(endstring), " BOOT_IMAGE=%u", value); - if ((strlen(cmd_line_extra) + strlen(endstring)) < COMMAND_LINE_SIZE) - strcat(cmd_line_extra, endstring); - -diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h -index 1b103a8..ee0f2c5 100644 ---- a/zipl/boot/menu.h -+++ b/zipl/boot/menu.h -@@ -20,7 +20,6 @@ - /* max command line length */ - #define COMMAND_LINE_SIZE 896 - #define BOOT_MENU_ENTRIES 63 --#define LINE_LENGTH 80 - #define PARAM_SIZE 8 - #define TEXT_OFFSET 4 - -diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c -index 1da3ce1..800b943 100644 ---- a/zipl/boot/tape2dump.c -+++ b/zipl/boot/tape2dump.c -@@ -186,7 +186,7 @@ static void progress_print_disp(unsigned long addr) - - if (addr % (1024 * 1024 * 16) != 0) - return; -- sprintf(msg, "%08u", addr >> 20); -+ snprintf(msg, sizeof(msg), "%08u", addr >> 20); - ccw_load_display(msg); - } - --- -2.21.3 - - -From c3fe7d1b44512bb0e645b54476cafc4a27141022 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jan=20H=C3=B6ppner?= -Date: Wed, 20 Nov 2019 22:03:54 +0100 -Subject: [PATCH 45/57] dasdview: Fix exit status in error cases (#1783288) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In error cases, dasdview returns -1 which results in the return value -255. This is due to the fact that only the low-order 8 bits are used for -the status value. See 2.13 Status Information [1] in the POSIX standard -and the exit() POSIX man page [2] for more details. - -Instead of returning -1, use the EXIT_FAILURE constant to indicate -unsuccessful termination properly. This change also makes the exit -status consistent for all error cases in dasdview, as some exit() calls -already use EXIT_FAILURE. - -[1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html -[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exit.html - -Reviewed-by: Stefan Haberland -Signed-off-by: Jan Höppner -(cherry picked from commit bfbf456b839a3684fbb1d4649b12d2ea57704750) ---- - dasdview/dasdview.c | 76 ++++++++++++++++++++++----------------------- - 1 file changed, 38 insertions(+), 38 deletions(-) - -diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c -index 3dc5890..1f837e9 100644 ---- a/dasdview/dasdview.c -+++ b/dasdview/dasdview.c -@@ -160,19 +160,19 @@ dasdview_get_info(dasdview_info_t *info) - if (err != EBADF) - zt_error_print("dasdview: " - "Could not retrieve geo information!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (dasd_get_blocksize(info->device, &info->blksize) != 0) { - zt_error_print("dasdview: " - "Could not retrieve blocksize information!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (dasd_get_info(info->device, &info->dasd_info) != 0) { - zt_error_print("dasdview: " - "Could not retrieve disk information!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - characteristics = (struct dasd_eckd_characteristics *) -@@ -261,7 +261,7 @@ dasdview_parse_input(unsigned long long *p, dasdview_info_t *info, char *s) - error: - zt_error_print("dasdview: usage error\n" - "%s is not a valid begin/size value!", s); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - /* -@@ -454,7 +454,7 @@ dasdview_print_vlabel(dasdview_info_t *info) - if (rc) { - zt_error_print("error when reading label from device:" - " rc=%d\n", rc); -- exit(-1); -+ exit(EXIT_FAILURE); - } - lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); - memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); -@@ -600,7 +600,7 @@ dasdview_print_volser(dasdview_info_t *info) - if (rc) { - zt_error_print("error when reading label from device:" - " rc=%d\n", rc); -- exit(-1); -+ exit(EXIT_FAILURE); - } - lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); - memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); -@@ -644,7 +644,7 @@ dasdview_read_vtoc(dasdview_info_t *info) - zt_error_print("dasdview: disk layout error\n" - "%s is not formatted with the z/OS " - "compatible disk layout!\n", info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - vtocblk = (u_int64_t)vtoc_get_cyl_from_cchhb(&vlabel.vtoc) * -@@ -664,7 +664,7 @@ dasdview_read_vtoc(dasdview_info_t *info) - if ((vtocblk <= 0) || (vtocblk > maxblk)) { - zt_error_print("dasdview: VTOC error\n" - "Volume label VTOC pointer is not valid!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - vtoc_read_label(info->device, (vtocblk - 1) * info->blksize, -@@ -676,7 +676,7 @@ dasdview_read_vtoc(dasdview_info_t *info) - /* format4 DSCB is invalid */ - zt_error_print("dasdview: VTOC error\n" - "Format 4 DSCB is invalid!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - info->f4c++; -@@ -724,19 +724,19 @@ dasdview_read_vtoc(dasdview_info_t *info) - if (info->f4c > 1) { - zt_error_print("dasdview: VTOC error\n" - "More than one FMT4 DSCB!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info->f5c > 1) { - zt_error_print("dasdview: VTOC error\n" - "More than one FMT5 DSCB!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info->f7c > 1) { - zt_error_print("dasdview: VTOC error\n" - "More than one FMT7 DSCB!\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - } - -@@ -975,7 +975,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1, - } - if (rc) { - zt_error_print("dasdview: Broken format 3 DSCB chain \n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL; - -@@ -990,7 +990,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1, - if (f3->DS3FMTID != 0xf3) { - zt_error_print("dasdview: Broken format 3 DSCB" - " chain \n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - for (j = 0; j < 4; ++j) - dasdview_print_short_info_extent_raw(&f3->DS3EXTNT[j]); -@@ -1002,7 +1002,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1, - if (rc) { - zt_error_print("dasdview: Broken format 3 DSCB" - " chain \n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - } - printf("\n"); -@@ -1025,7 +1025,7 @@ static void dasdview_print_vtoc_info_raw(dasdview_info_t *info) - rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); - if (rc) { - zt_error_print("dasdview: could not allocate DSCB iterator \n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { - if (dscb->fmtid == 0xf1) -@@ -1058,7 +1058,7 @@ static void dasdview_print_vtoc_info_raw(dasdview_info_t *info) - rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); - if (rc) { - zt_error_print("dasdview: could not allocate DSCB iterator \n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { - if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8) -@@ -1739,23 +1739,23 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info) - if (rc) { - zt_error_print("error when reading label from device:" - " rc=%d\n", rc); -- exit(-1); -+ exit(EXIT_FAILURE); - } - rc = lzds_dasd_alloc_rawvtoc(info->dasd); - if (rc == EINVAL) { - zt_error_print("dasdview: Cannot read VTOC because disk does" - " not contain valid VOL1 label.\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } else if (rc) { - zt_error_print("error when reading vtoc from device:" - " rc=%d\n", rc); -- exit(-1); -+ exit(EXIT_FAILURE); - } - rc = lzds_dasd_get_rawvtoc(info->dasd, &info->rawvtoc); - if (rc || !info->rawvtoc) { - zt_error_print("dasdview: libvtoc could not read vtoc\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info->vtoc_info || info->vtoc_all) -@@ -1764,7 +1764,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info) - rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); - if (rc) { - zt_error_print("dasdview: could not allocate DSCB iterator\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - while (!lzds_dscbiterator_get_next_dscb(it, &record)) - dasdview_print_vtoc_dscb(info, record); -@@ -1858,7 +1858,7 @@ static void dasdview_view_standard(dasdview_info_t *info) - zt_error_print("dasdview: open error\n" - "Unable to open device %s in read-only mode!\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - j = (info->begin / SEEK_STEP); -@@ -1877,7 +1877,7 @@ static void dasdview_view_standard(dasdview_info_t *info) - zt_error_print("dasdview: seek error\n" - "Unable to seek in device %s!\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - b++; - a += SEEK_STEP; -@@ -1890,7 +1890,7 @@ static void dasdview_view_standard(dasdview_info_t *info) - zt_error_print("dasdview: seek error\n" - "Unable to seek in device %s!\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - } - -@@ -1926,7 +1926,7 @@ static void dasdview_view_standard(dasdview_info_t *info) - zt_error_print("dasdview: read error\n" - "Unable to read from device %s!\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info->format1) -@@ -1945,7 +1945,7 @@ static void dasdview_view_standard(dasdview_info_t *info) - zt_error_print("dasdview: read error\n" - "Unable to read from device %s!\n", - info->device); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info->format1) -@@ -2112,18 +2112,18 @@ static void dasdview_view_raw(dasdview_info_t *info) - trackdata = memalign(4096, trckbuffsize * RAWTRACKSIZE); - if (!trackdata) { - zt_error_print("failed to allocate memory\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - rc = lzds_dasd_alloc_dasdhandle(info->dasd, &dasdh); - if (rc) { - zt_error_print("failed to allocate memory\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - rc = lzds_dasdhandle_open(dasdh); - if (rc) { - lzds_dasdhandle_free(dasdh); - zt_error_print("failed to open device\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - /* residual is the number of tracks we still have to read */ - residual = tracks_to_read; -@@ -2135,7 +2135,7 @@ static void dasdview_view_raw(dasdview_info_t *info) - trckend, trackdata); - if (rc) { - perror("Error on read"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - data = trackdata; - for (i = 0; i < trckcount; ++i) { -@@ -2153,7 +2153,7 @@ static void dasdview_view_raw(dasdview_info_t *info) - lzds_dasdhandle_free(dasdh); - if (rc < 0) { - perror("Error on closing file"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - } - -@@ -2253,7 +2253,7 @@ int main(int argc, char *argv[]) - zt_error_print("dasdview: usage error\n" - "%s is no valid argument for" - " option -t/--vtoc\n", optarg); -- exit(-1); -+ exit(EXIT_FAILURE); - } - info.vtoc = 1; - info.action_specified = 1; -@@ -2301,13 +2301,13 @@ int main(int argc, char *argv[]) - rc = lzds_zdsroot_alloc(&info.zdsroot); - if (rc) { - zt_error_print("Could not allocate index\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - rc = lzds_zdsroot_add_device(info.zdsroot, info.device, - &info.dasd); - if (rc) { - zt_error_print("Could not add device to index\n"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - } - -@@ -2328,7 +2328,7 @@ int main(int argc, char *argv[]) - if (info.begin > max) { - zt_error_print("dasdview: usage error\n" - "'begin' value is not within disk range!"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if (info.size_specified) -@@ -2343,7 +2343,7 @@ int main(int argc, char *argv[]) - zt_error_print("dasdview: usage error\n" - "'begin' + 'size' is not within " - "disk range!"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - if ((info.begin_specified || info.size_specified) && -@@ -2355,7 +2355,7 @@ int main(int argc, char *argv[]) - zt_error_print("dasdview: usage error\n" - "Options -1 or -2 make only sense with " - "options -b or -s!"); -- exit(-1); -+ exit(EXIT_FAILURE); - } - - /* do the output */ --- -2.21.3 - - -From 54b3e8b2d925fcd9083fc020b75c98b83c299f9d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:37:10 +0200 -Subject: [PATCH 46/57] zkey: Fix display of XTS attribute for validate command - (#1808494) - -Description: zkey: Fix display of XTS attribute for validate command -Symptom: The 'zkey validate' command shows an invalid value for - the XTS attribute. -Problem: Due to a use after free of the secure key, the XTS attribute - is not determined correctly, and is displayed incorrectly. - Function is_xts_key() is called with a secure key that has - already been freed and thus most likely returns false. - This bug has been introduced with feature SEC1717 "Cipher - key support" with commit 298fab68fee8 "zkey: Preparations for - introducing a new key type" -Solution: Free the secure key only after the last use. -Reproduction: Generate an XTS key of type CCA-AESDATA or CCA-AESCIPHER - and then run 'zkey validate'. -Upstream-ID: f75f4aff8f6e4ae148bde858ee1cb7f1066f5f23 ---- - zkey/keystore.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/zkey/keystore.c b/zkey/keystore.c -index e6be3a4..4f90bdd 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -2516,7 +2516,7 @@ static int _keystore_process_validate(struct keystore *keystore, - size_t clear_key_bitsize; - size_t secure_key_size; - char *apqns = NULL; -- u8 *secure_key; -+ u8 *secure_key = NULL; - int is_old_mk; - int rc, valid; - u64 mkvp; -@@ -2550,8 +2550,7 @@ static int _keystore_process_validate(struct keystore *keystore, - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, - &mkvp, keystore->verbose); -- free(secure_key); -- if (rc) -+ if (rc != 0) - goto out; - - _keystore_print_record(info->rec, name, properties, 1, -@@ -2577,6 +2576,8 @@ static int _keystore_process_validate(struct keystore *keystore, - info->num_warnings++; - - out: -+ if (secure_key != NULL) -+ free(secure_key); - if (apqns != NULL) - free(apqns); - if (apqn_list != NULL) --- -2.21.3 - - -From c1d79840cd820a58ad629bb18dd9b1643c1ca5a6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:38:25 +0200 -Subject: [PATCH 47/57] zkey: Fix display of clear key size for CCA-AESCIPHER - keys (#1808492) - -Description: zkey: Fix display of clear key size for CCA-AESCIPHER keys -Symptom: The 'zkey list' command shows bogus values for the - keys 'Clear key size' for keys of type CCA-AESCIPHER. -Problem: Secure keys of type CCA-AESCIPHER are variable length, - dependent on the effective key size (e.g. 128, 192, or 256 - bits). However, the key blob stored is padded to a fixed - length, so that all key blobs of type CCA-AESCIPHER are - the same size, regardless of the effective key bit size. - To code to display the clear key bitsize does not correctly - handle the padding and may treat a non-XTS key like an XTS - key and thus reads past the end of the key blob. This - results in bogus values reported as clear key size. - This bug has been introduced with feature SEC1717 "Cipher - key support" with commit ddde3f354f35 ("zkey: Introduce th - CCA-AESCIPHER key type"). -Solution: Correct the handling of key of type CCA-AESCIPHER. -Reproduction: Generate a key of type CCA-AESCIPHER and then run - 'zkey list'. -Upstream-ID: 49cbaba302f002aa7f148631a76fc21a3069bc25 ---- - zkey/pkey.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index 640ff86..793ed9e 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -1600,9 +1600,9 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - *bitsize = cipherkey->pl - 384; - else - *bitsize = 0; /* Unknown */ -- if (key_size > cipherkey->length) { -+ if (key_size == 2 * AESCIPHER_KEY_SIZE) { - cipherkey = (struct aescipherkeytoken *)(key + -- cipherkey->length); -+ AESCIPHER_KEY_SIZE); - if (cipherkey->pfv == 0x00) /* V0 payload */ - *bitsize += cipherkey->pl - 384; - } --- -2.21.3 - - -From 8575d2798cfa9087fcdabe944e38c0ad2ac69e9f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:40:41 +0200 -Subject: [PATCH 48/57] zipl: fix secure boot config handling (#1814322) - -Description: zipl: fix secure boot config handling -Symptom: The config file parsing for secure boot worked not as - it was expected to be. For example a config section - setting was not evaluated properly. - It is not possible to specify command line option -S - without other options. - Additionally the man page showed an invalid example. -Problem: The config file parsing was not implemented properly. -Solution: The hierarchy of the secure boot settings in the config - file is: - defaultboot > menu > section - Allow that --secure or -S is specified on command line - without the need to allow all options on the command - line. Also ensure that the command line option - overrules the config option and correctly ensure that - secure boot is only set for SCSI devices. - Fix man page example. -Reproduction: Run zipl with a secure= setting in a configuration - section or specify -S on command line. -Upstream-ID: dcce14923c3e9615df53773d1d8a3a22cbb23b96 -Upstream-ID: 27f6c0a167da8d08f7f3343360528528f85d661f -Upstream-ID: 6f9337d1016e00f360cf4a81d39a42df5184b3a2 -Upstream-ID: 299fd2b7729f35c6fe3be18964f7e5e6a365f94d ---- - zipl/include/job.h | 1 + - zipl/include/zipl.h | 1 + - zipl/man/zipl.conf.5.in | 6 ++-- - zipl/src/bootmap.c | 14 +++++++- - zipl/src/job.c | 75 ++++++++++++++++++++++++++--------------- - zipl/src/zipl.c | 9 ++++- - 6 files changed, 74 insertions(+), 32 deletions(-) - -diff --git a/zipl/include/job.h b/zipl/include/job.h -index fce811c..e04a7c1 100644 ---- a/zipl/include/job.h -+++ b/zipl/include/job.h -@@ -94,6 +94,7 @@ struct job_menu_entry { - char* name; - enum job_id id; - union job_menu_entry_data data; -+ int is_secure; - }; - - struct job_menu_data { -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index 0ef06e4..e5842ed 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -59,6 +59,7 @@ - - #define MAX_DUMP_VOLUMES 32 - -+#define SECURE_BOOT_UNDEFINED -1 - #define SECURE_BOOT_DISABLED 0 - #define SECURE_BOOT_ENABLED 1 - #define SECURE_BOOT_AUTO 2 -diff --git a/zipl/man/zipl.conf.5.in b/zipl/man/zipl.conf.5.in -index ef40287..20731b0 100644 ---- a/zipl/man/zipl.conf.5.in -+++ b/zipl/man/zipl.conf.5.in -@@ -87,8 +87,6 @@ below). - .br - defaultmenu = menu1 - .br --secure = auto --.br - - [linux] - .br -@@ -122,6 +120,8 @@ prompt = 1 - .br - timeout = 0 - .br -+secure = auto -+.br - .PP - - .B BootLoaderSpec configuration files -@@ -533,7 +533,7 @@ non-default memory location. - .B secure - = - .IR auto / 1 / 0 --(configuration only) -+(configuration and menu) - .IP - .B Configuration section: - .br -diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c -index 456c2ef..5926622 100644 ---- a/zipl/src/bootmap.c -+++ b/zipl/src/bootmap.c -@@ -942,6 +942,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, - { - disk_blockptr_t* table; - int entries, component_header; -+ int is_secure; - int i; - int rc; - -@@ -1013,13 +1014,18 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, - component_header_ipl; - printf("\n"); - } -+ if (job->is_secure != SECURE_BOOT_UNDEFINED) -+ is_secure = job->is_secure; -+ else -+ is_secure = -+ job->data.menu.entry[i].is_secure; - rc = add_ipl_program(fd, - &job->data.menu.entry[i].data.ipl, - &table[job->data.menu.entry[i].pos], - verbose || job->command_line, - job->add_files, component_header, - info, &job->target, -- job->is_secure); -+ is_secure); - break; - case job_print_usage: - case job_print_version: -@@ -1130,6 +1136,12 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - disk_get_type_name(info->type)); - goto out_disk_free_info; - } -+ /* Check if secure boot was enabled only for SCSI */ -+ if (job->is_secure == SECURE_BOOT_ENABLED && -+ info->type != disk_type_scsi) { -+ error_reason("Secure boot forced for non-SCSI disk type"); -+ goto out_disk_free_info; -+ } - if (verbose) { - printf("Target device information\n"); - disk_print_info(info); -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 7d61c84..80ddb34 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -80,6 +80,7 @@ struct command_line { - int add_files; - int dry_run; - int force; -+ int is_secure; - enum scan_section_type type; - }; - -@@ -97,6 +98,22 @@ store_option(struct command_line* cmdline, enum scan_keyword_id keyword, - return 0; - } - -+static int -+set_secure_ipl(char *keyword, int *is_secure) -+{ -+ if (strcmp(keyword, "auto") == 0) { -+ *is_secure = SECURE_BOOT_AUTO; -+ } else if (strcmp(keyword, "0") == 0) { -+ *is_secure = SECURE_BOOT_DISABLED; -+ } else if (strcmp(keyword, "1") == 0) { -+ *is_secure = SECURE_BOOT_ENABLED; -+ } else { -+ error_reason("Invalid secure boot setting '%s'", -+ keyword); -+ return -1; -+ } -+ return 0; -+} - - static int - get_command_line(int argc, char* argv[], struct command_line* line) -@@ -110,6 +127,7 @@ get_command_line(int argc, char* argv[], struct command_line* line) - memset((void *) &cmdline, 0, sizeof(struct command_line)); - cmdline.type = section_invalid; - is_keyword = 0; -+ cmdline.is_secure = SECURE_BOOT_UNDEFINED; - /* Process options */ - do { - opt = getopt_long(argc, argv, option_string, options, NULL); -@@ -225,9 +243,7 @@ get_command_line(int argc, char* argv[], struct command_line* line) - cmdline.menu = optarg; - break; - case 'S': -- is_keyword = 1; -- rc = store_option(&cmdline, scan_keyword_secure, -- optarg); -+ rc = set_secure_ipl(optarg, &cmdline.is_secure); - break; - case 'h': - cmdline.help = 1; -@@ -1048,6 +1064,21 @@ check_job_mvdump_data(struct job_mvdump_data* dump, char* name) - return 0; - } - -+static int -+check_secure_boot(struct job_data *job) -+{ -+ switch (job->is_secure) { -+ case SECURE_BOOT_UNDEFINED: -+ case SECURE_BOOT_DISABLED: -+ case SECURE_BOOT_ENABLED: -+ case SECURE_BOOT_AUTO: -+ return 0; -+ default: -+ error_reason("Invalid secure boot setting '%d'", -+ job->is_secure); -+ return -1; -+ } -+} - - static int - check_job_data(struct job_data* job) -@@ -1092,6 +1123,8 @@ check_job_data(struct job_data* job) - case job_mvdump: - rc = check_job_mvdump_data(&job->data.mvdump, job->name); - } -+ if (!rc) -+ rc = check_secure_boot(job); - return rc; - } - -@@ -1277,27 +1310,6 @@ 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) - { -@@ -1382,7 +1394,7 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) - /* Fill in secure boot */ - if (data[(int) scan_keyword_secure] != NULL) { - rc = set_secure_ipl(data[(int) scan_keyword_secure], -- job); -+ &job->is_secure); - if (rc) - return rc; - } -@@ -1546,7 +1558,7 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - case scan_keyword_secure: - rc = set_secure_ipl( - scan[i].content.keyword.value, -- job); -+ &job->is_secure); - if (rc) - return rc; - break; -@@ -1608,6 +1620,7 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - sizeof(struct job_menu_entry) * job->data.menu.num); - /* Fill in data */ - current = 0; -+ job->data.menu.entry->is_secure = SECURE_BOOT_UNDEFINED; - for (i=index+1; (scan[i].id != scan_id_empty) && - (scan[i].id != scan_id_section_heading) && - (scan[i].id != scan_id_menu_heading); i++) { -@@ -1639,6 +1652,7 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - if (temp_job == NULL) - return -1; - memset((void *) temp_job, 0, sizeof(struct job_data)); -+ temp_job->is_secure = SECURE_BOOT_UNDEFINED; - rc = get_job_from_section_data(data, temp_job, - job->data.menu.entry[current].name); - if (rc) { -@@ -1651,6 +1665,8 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - job->data.menu.entry[current].id = job_ipl; - job->data.menu.entry[current].data.ipl = - temp_job->data.ipl; -+ job->data.menu.entry[current].is_secure = -+ temp_job->is_secure; - memset((void *) &temp_job->data.ipl, 0, - sizeof(struct job_ipl_data)); - break; -@@ -1899,7 +1915,7 @@ job_get(int argc, char* argv[], struct job_data** data) - job->add_files = cmdline.add_files; - job->data.mvdump.force = cmdline.force; - job->dry_run = cmdline.dry_run; -- job->is_secure = SECURE_BOOT_AUTO; -+ job->is_secure = SECURE_BOOT_UNDEFINED; - /* Get job data from user input */ - if (cmdline.help) { - job->command_line = 1; -@@ -1918,6 +1934,11 @@ job_get(int argc, char* argv[], struct job_data** data) - job_free(job); - return rc; - } -+ if (cmdline.is_secure != SECURE_BOOT_UNDEFINED) -+ job->is_secure = cmdline.is_secure; -+ else if (job->id != job_menu && job->is_secure == SECURE_BOOT_UNDEFINED) -+ job->is_secure = SECURE_BOOT_AUTO; -+ - /* Check job data for validity */ - rc = check_job_data(job); - if (rc) { -diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c -index 65eefdb..2c53489 100644 ---- a/zipl/src/zipl.c -+++ b/zipl/src/zipl.c -@@ -68,6 +68,8 @@ static const char* usage_text[] = { - "-P, --parameters PARMLINE Use specified kernel PARMLINE", - "-T, --tape TAPEDEV Install bootloader on tape device TAPEDEV", - "-s, --segment SEGMENT,ADDR Install a segment from file SEGMENT", -+"-k, --kdump=auto Install a kdump kernel that can be used as a", -+" stand-alone dump tool", - "-d, --dumpto DUMPDEV[,SIZE] Install a system dump record on tape device", - " or disk partition DUMPDEV", - "-M, --mvdump DEVLIST[,SIZE] Install a multi-volume dump record on each", -@@ -78,7 +80,12 @@ static const char* usage_text[] = { - "-n, --noninteractive Answer all confirmation questions with 'yes'", - "-V, --verbose Provide more verbose output", - "-a, --add-files Add all referenced files to bootmap file", --" --dry-run Simulate run but don't modify IPL records" -+" --dry-run Simulate run but don't modify IPL records", -+"-S, --secure SWITCH Control the zIPL secure boot support.", -+" auto (default):", -+" Write signatures if available and supported", -+" 1: Write signatures regardless of support", -+" 0: Do not write signatures" - }; - - --- -2.21.3 - - -From 9c07b5194f80b57c6d3e74d868b52a96f831653a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:42:25 +0200 -Subject: [PATCH 49/57] zpcictl: Initiate recover after reset (#1814303) - -Description: zpcictl: Initiate recover after reset -Symptom: If a PCI function is reset using zpcictl --reset, the function - is in an error state. -Problem: zpcictl --reset only issues a SCLP reset and leaves the PCI - function in an error state. -Solution: Initiate an OS level recovery by calling - /sys/bus/devices//recover after the SCLP reset. -Reproduction: Call zpcictl --reset - Under z/VM check the state of the function with 'vmcp q pcif' -Upstream-ID: 73bab8e1a1dd93a90ffb5a39e4dc91f194a2f063 -Upstream-ID: bc0d40c5803d4c5426b17b6d59aa0f1e46a2aacc -Upstream-ID: d77234ddb68719819c7e8380c71dbebc555539ab -Upstream-ID: 304c3d8086bc2a9230c5404f9c9fec72de08d229 ---- - zpcictl/zpcictl.c | 63 ++++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 57 insertions(+), 6 deletions(-) - -diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c -index f2a55ad..7cbfad9 100644 ---- a/zpcictl/zpcictl.c -+++ b/zpcictl/zpcictl.c -@@ -96,6 +96,35 @@ static void fopen_err(char *path) - exit(EXIT_FAILURE); - } - -+static void fclose_err(char *path) -+{ -+ if (errno == EIO || errno == EOPNOTSUPP) -+ warnx("Unsupported operation: %s: %s", path, strerror(errno)); -+ else -+ warnx("Could not close file: %s: %s", path, strerror(errno)); -+ free(path); -+ exit(EXIT_FAILURE); -+ -+} -+ -+static void fread_err(FILE *fp, char *path) -+{ -+ warnx("Could not read file: %s: %s", path, strerror(errno)); -+ if (fclose(fp)) -+ fclose_err(path); -+ free(path); -+ exit(EXIT_FAILURE); -+} -+ -+static void fwrite_err(FILE *fp, char *path) -+{ -+ warnx("Could not write to file: %s: %s", path, strerror(errno)); -+ if (fclose(fp)) -+ fclose_err(path); -+ free(path); -+ exit(EXIT_FAILURE); -+} -+ - #define READ_CHUNK_SIZE 512 - - static char *collect_smart_data(struct zpci_device *pdev) -@@ -150,14 +179,35 @@ static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) - fp = fopen(path, "r"); - if (!fp) - fopen_err(path); -- fscanf(fp, "%x", &val); -- fclose(fp); -+ if (fscanf(fp, "%x", &val) != 1) { -+ fread_err(fp, path); -+ } -+ if (fclose(fp)) -+ fclose_err(path); - free(path); - - return val; - } - --static void sysfs_write_data(struct zpci_report_error *report, char *slot) -+static void sysfs_write_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, "w"); -+ if (!fp) -+ fopen_err(path); -+ if (fprintf(fp, "%x", val) < 0) { -+ fwrite_err(fp, path); -+ } -+ if (fclose(fp)) -+ fclose_err(path); -+ free(path); -+} -+ -+static void sysfs_report_error(struct zpci_report_error *report, char *slot) - { - size_t r_size; - char *path; -@@ -170,9 +220,9 @@ static void sysfs_write_data(struct zpci_report_error *report, char *slot) - if (!fp) - fopen_err(path); - if (fwrite(report, 1, r_size, fp) != r_size) -- warnx("Could not write to file: %s: %s", path, strerror(errno)); -+ fwrite_err(fp, path); - if (fclose(fp)) -- warnx("Could not close file: %s: %s", path, strerror(errno)); -+ fclose_err(path); - free(path); - } - -@@ -316,7 +366,7 @@ static void sclp_issue_action(struct zpci_device *pdev, int action) - strncpy(report.data.log_data, sdata, sizeof(report.data.log_data)); - free(sdata); - } -- sysfs_write_data(&report, pdev->slot); -+ sysfs_report_error(&report, pdev->slot); - } - - /* -@@ -325,6 +375,7 @@ static void sclp_issue_action(struct zpci_device *pdev, int action) - static void sclp_reset_device(struct zpci_device *pdev) - { - sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET); -+ sysfs_write_value(pdev, "recover", 1); - } - - /* --- -2.21.3 - - -From d9db8c5d6adfc0ee8665a6c51bce8b347fbfc212 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:47:35 +0200 -Subject: [PATCH 50/57] dbginfo.sh: Extend data collection (#1814323) - -Description: dbginfo.sh: Extend data collection -Symptom: This update covers various symptoms on dbginfo.sh data - collection: - - There is no data collection for ethtool, tc and bridge - output for debugging network issues - - There is no data collection for hyptop output - - There is no data collection for nvme devices - - There is no data collection for smc devices - - Sometimes the lsof output is too long which makes the - runtime.out hard to be opened - - The journalctl was limited to 50000 lines -Problem: Following problems exist: - - Missing data collection for ethtool, tc and bridge - - Missing data collection for hyptop output - - Missing data collection for nvme devices - - Missing data collection for smc devices - - Sometimes the lsof output is too long which makes the - runtime.out hard to be opened - - The journalctl was limited to 50000 lines. A very long - journalctl will overwrite the beginning part -Solution: - Extend the data collection to collect output for - ethtool, tc, bridge and hyptop output - - Extend the data collection to collect nvme and smc - device information - - Write the output of lsof to a separate file - - Extend journalctl to 100000 lines -Reproduction: Run this script and verify the output -Upstream-ID: bbd88f26c98c9be7e82c7a7e745d8d07b0be0e5b -Upstream-ID: 60bda7ed0d29cfa840e7262663b097a6ef167c80 -Upstream-ID: 36292cb1663f703e9247e6b9e84f36748aaf44d0 -Upstream-ID: e6a3b1810487f08eb0633172ee81b9fdcbcd1a6d -Upstream-ID: 7a2a96e8a38023145bc700838ccc8fa30a6e4228 -Upstream-ID: 852fb9dfb8f0bf1aa742a89895132e2c78d2f8dd -Upstream-ID: 125df5288779d17be3cc357c1eaa36c138e802ce -Upstream-ID: c079e48d7d398f7fdcd58dfeb734c924c780f789 -Upstream-ID: 235e997a7718298ed9d8208efc60069406b5bbdd -Upstream-ID: 0652c687e3df6d570b0123d31c137faec9a8ffb3 ---- - scripts/dbginfo.sh | 132 ++++++++++++++++++++++++++++++++++++++----- - scripts/dbginfo.sh.1 | 38 ++++++++----- - 2 files changed, 143 insertions(+), 27 deletions(-) - -diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh -index f3d7f43..574bd03 100755 ---- a/scripts/dbginfo.sh -+++ b/scripts/dbginfo.sh -@@ -167,9 +167,21 @@ readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out" - # File that includes content of files from sysfs - readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfsfiles.out" - -+# File that includes the output of lsof -+readonly OUTPUT_FILE_LSOF="${WORKPATH}open_files.out" -+ - # File that includes content of OSA OAT - readonly OUTPUT_FILE_OSAOAT="${WORKPATH}osa_oat" - -+# File that includes content of Ethtool commands -+readonly OUTPUT_FILE_ETHTOOL="${WORKPATH}ethtool.out" -+ -+# File that includes content of tc commands -+readonly OUTPUT_FILE_TC="${WORKPATH}tc.out" -+ -+# File that includes content of bridge commands -+readonly OUTPUT_FILE_BRIDGE="${WORKPATH}bridge.out" -+ - # File that includes the output of journalctl - readonly OUTPUT_FILE_JOURNALCTL="${WORKPATH}journalctl.out" - -@@ -189,7 +201,7 @@ readonly OUTPUT_FILE_NVME="${WORKPATH}nvme.out" - readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" - - # The amount of steps running the whole collections --readonly COLLECTION_COUNT=12 -+readonly COLLECTION_COUNT=15 - - # The kernel version (e.g. '2' from 2.6.32 or '3' from 3.2.1) - readonly KERNEL_VERSION=$(uname -r 2>/dev/null | cut -d'.' -f1) -@@ -367,7 +379,7 @@ CONFIGFILES="\ - $(find /lib/modules -name modules.dep 2>/dev/null)\ - /etc/docker\ - /lib/systemd/system/docker.service\ -- /usr/lib/systemd/system/docker.service\ -+ /usr/lib/systemd/system\ - /etc/apparmor.d\ - " - -@@ -394,6 +406,7 @@ CMDS="uname -a\ - :ip link show\ - :ip ntable\ - :ip a sh\ -+ :ip -s -s link\ - :firewall-cmd --list-all\ - :ipcs -a\ - :netstat -pantu\ -@@ -441,7 +454,8 @@ CMDS="uname -a\ - :SPident\ - :rpm -qa | sort\ - :sysctl -a\ -- :lsof\ -+ :lsof \ -+ > '${OUTPUT_FILE_LSOF}'\ - :mount\ - :df -h\ - :df -i\ -@@ -451,7 +465,7 @@ CMDS="uname -a\ - :java -version\ - :cat /root/.bash_history\ - :env\ -- :journalctl --all --no-pager --since=$(date -d '5 day ago' +%Y-%m-%d) --until=now --lines=50000 \ -+ :journalctl --all --no-pager --lines=100000 --output=short-precise\ - > '${OUTPUT_FILE_JOURNALCTL}'\ - :openssl engine\ - :systemd-delta\ -@@ -465,6 +479,10 @@ CMDS="uname -a\ - :docker version\ - :docker stats --no-stream\ - :systemctl status docker.service\ -+ :blockdev --report\ -+ :lvdisplay\ -+ :lspci -vv\ -+ :smc_dbg\ - " - - ######################################## -@@ -549,6 +567,11 @@ collect_cmdsout() { - done - IFS="${ifs_orig}" - -+ if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then -+ call_run_command "hyptop -b -d 1 -n 5 -f \#,c,m,C:s,M:s,o -S c" "${OUTPUT_FILE_CMD}" -+ else call_run_command "hyptop -b -d 1 -n 5 -f \#,T,c,e,m,C:s,E:s,M:s,o -S c" "${OUTPUT_FILE_CMD}" -+ fi -+ - pr_log_stdout " " - } - -@@ -743,6 +766,83 @@ collect_osaoat() { - pr_log_stdout " " - } - -+######################################## -+collect_ethtool() { -+ local network_devices -+ local network_device -+ -+ network_devices=$(ls /sys/class/net 2>/dev/null) -+ if which ethtool >/dev/null 2>&1; then -+ if test -n "${network_devices}"; then -+ pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output" -+ for network_device in ${network_devices}; do -+ call_run_command "ethtool ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -k ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -a ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -c ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -g ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -i ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -l ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -P ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -S ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ call_run_command "ethtool -T ${network_device}" "${OUTPUT_FILE_ETHTOOL}" -+ done -+ else -+ pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output skipped - no devices" -+ fi -+ else -+ pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output skipped - not available" -+ fi -+ -+ pr_log_stdout " " -+} -+ -+######################################## -+collect_tc() { -+ local network_devices -+ local network_device -+ -+ network_devices=$(ls /sys/class/net 2>/dev/null) -+ if which tc >/dev/null 2>&1; then -+ if test -n "${network_devices}"; then -+ pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting tc output" -+ for network_device in ${network_devices}; do -+ call_run_command "tc -s qdisc show dev ${network_device}" "${OUTPUT_FILE_TC}" -+ done -+ else -+ pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting tc output skipped - no devices" -+ fi -+ else -+ pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting tc output skipped - not available" -+ fi -+ -+ pr_log_stdout " " -+} -+ -+######################################## -+collect_bridge() { -+ local network_devices -+ local network_device -+ -+ network_devices=$(ls /sys/class/net 2>/dev/null) -+ if which bridge >/dev/null 2>&1; then -+ if test -n "${network_devices}"; then -+ pr_syslog_stdout "10 of ${COLLECTION_COUNT}: Collecting bridge output" -+ for network_device in ${network_devices}; do -+ call_run_command "bridge -d link show dev ${network_device}" "${OUTPUT_FILE_BRIDGE}" -+ call_run_command "bridge -s fdb show dev ${network_device}" "${OUTPUT_FILE_BRIDGE}" -+ call_run_command "bridge -d mdb show dev ${network_device}" "${OUTPUT_FILE_BRIDGE}" -+ done -+ else -+ pr_syslog_stdout "10 of ${COLLECTION_COUNT}: Collecting bridge output skipped - no devices" -+ fi -+ else -+ pr_syslog_stdout "10 of ${COLLECTION_COUNT}: Collecting bridge output skipped - not available" -+ fi -+ -+ pr_log_stdout " " -+} -+ - ######################################## - # OpenVSwitch - collect_ovs() { -@@ -759,7 +859,7 @@ collect_ovs() { - :ovsdb-client dump\ - " - if test -n "${br_list}"; then -- pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output" -+ pr_syslog_stdout "11 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output" - IFS=: - for ovscmd in ${ovscmds}; do - IFS=${ifs_orig} call_run_command "${ovscmd}" "${OUTPUT_FILE_OVS}.out" -@@ -778,7 +878,7 @@ collect_ovs() { - IFS="${ifs_orig}" - done - else -- pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output skipped" -+ pr_syslog_stdout "11 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output skipped" - fi - - pr_log_stdout " " -@@ -791,12 +891,12 @@ collect_domain_xml() { - - domain_list=$(virsh list --all --name) - if test -n "${domain_list}"; then -- pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting domain xml files" -+ pr_syslog_stdout "12 of ${COLLECTION_COUNT}: Collecting domain xml files" - for domain in ${domain_list}; do - call_run_command "virsh dumpxml ${domain}" "${OUTPUT_FILE_XML}_${domain}.xml" - done - else -- pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting domain xml files skipped" -+ pr_syslog_stdout "12 of ${COLLECTION_COUNT}: Collecting domain xml files skipped" - fi - - pr_log_stdout " " -@@ -810,23 +910,23 @@ collect_docker() { - # call docker inspect for all containers - item_list=$(docker ps -qa) - if test -n "${item_list}"; then -- pr_syslog_stdout "10a of ${COLLECTION_COUNT}: Collecting docker container output" -+ pr_syslog_stdout "13a of ${COLLECTION_COUNT}: Collecting docker container output" - for item in ${item_list}; do - call_run_command "docker inspect ${item}" "${OUTPUT_FILE_DOCKER}" - done - else -- pr_syslog_stdout "10a of ${COLLECTION_COUNT}: Collecting docker container output skipped" -+ pr_syslog_stdout "13a of ${COLLECTION_COUNT}: Collecting docker container output skipped" - fi - - # call docker inspect for all networks - item_list=$(docker network ls -q) - if test -n "${item_list}"; then -- pr_syslog_stdout "10b of ${COLLECTION_COUNT}: Collecting docker network output" -+ pr_syslog_stdout "13b of ${COLLECTION_COUNT}: Collecting docker network output" - for item in ${item_list}; do - call_run_command "docker network inspect ${item}" "${OUTPUT_FILE_DOCKER}" - done - else -- pr_syslog_stdout "10b of ${COLLECTION_COUNT}: Collecting docker network output skipped" -+ pr_syslog_stdout "13b of ${COLLECTION_COUNT}: Collecting docker network output skipped" - fi - - pr_log_stdout " " -@@ -836,7 +936,7 @@ collect_docker() { - collect_nvme() { - local NVME - -- pr_syslog_stdout "11 of ${COLLECTION_COUNT}: Collecting nvme output" -+ pr_syslog_stdout "14 of ${COLLECTION_COUNT}: Collecting nvme output" - call_run_command "nvme list" "${OUTPUT_FILE_NVME}" - - for NVME in /dev/nvme[0-9]*; do -@@ -1143,6 +1243,12 @@ collect_configfiles - - collect_osaoat - -+collect_ethtool -+ -+collect_tc -+ -+collect_bridge -+ - collect_ovs - - collect_domain_xml -diff --git a/scripts/dbginfo.sh.1 b/scripts/dbginfo.sh.1 -index 4771192..ef9fe89 100644 ---- a/scripts/dbginfo.sh.1 -+++ b/scripts/dbginfo.sh.1 -@@ -52,7 +52,7 @@ Sample invocation: - .br - dbginfo.sh: Debug information script version %S390_TOOLS_VERSION% - .br --Copyright IBM Corp. 2002, 2017 -+Copyright IBM Corp. 2002, 2019 - .PP - Hardware platform = s390x - .br -@@ -60,34 +60,44 @@ Kernel version = - .br - Runtime environment = z/VM - .PP --1 of 11: Collecting command output -+1 of 15: Collecting command output - .PP --2 of 11: Collecting z/VM command output -+2 of 15: Collecting z/VM command output - .PP --3 of 11: Collecting procfs -+3 of 15: Collecting procfs - .PP --4 of 11: Collecting sysfs -+4 of 15: Collecting sysfs - .PP --5 of 11: Collecting log files -+5 of 15: Collecting log files - .PP --6 of 11: Collecting config files -+6 of 15: Collecting config files - .PP --7 of 11: Collecting osa oat output skipped \- not available -+7 of 15: Collecting osa oat output skipped \- not available - .PP --8 of 11: Collecting OpenVSwitch output -+8 of 15: Collecting ethtool output - .PP --9 of 11: Collecting domain xml files -+9 of 15: Collecting tc output -+.pp -+10 of 15: Collecting bridge output -+.pp -+11 of 15: Collecting OpenVSwitch output - .PP --10a of 11: Collecting docker container output --10b of 11: Collecting docker network output -+12 of 15: Collecting domain xml files - .PP --11 of 11: Postprocessing -+13a of 15: Collecting docker container output -+13b of 15: Collecting docker network output -+.PP -+14 of 15: Collecting nvme output -+.PP -+15 of 15: Postprocessing - .PP - Finalizing: Creating archive with collected data - .PP - Collected data was saved to: - .br -- >> /data\-collection/DBGINFO\-2015\-02\-26\-21\-39\-16\-host\-012345.tgz << -+ >> /data\-collection/DBGINFO\-2019\-08\-19\-21\-39\-16\-host\-012345.tgz << -+.br -+Review the collected data before sending to your service organization. - .SH HINTS - Run the script with root authority. - .br --- -2.21.3 - - -From 40d2a0d3d48b798ff325b70c02f799ac3829160b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 10:50:10 +0200 -Subject: [PATCH 51/57] lscpumf: New z15 CPU-MF counters not available - (#1821591) - -Description: lscpumf: New z15 CPU-MF counters not available -Symptom: Command lscpumf -c does not show the new - deflate counters on IBM z15. -Problem: The new counter names have not been published - in document SA23-2261-06 by the time te code was - release. -Solution: Add the definition for the new deflate counters. -Reproduction: Run command lscpumf -c and check for counters - rf7, rfc, r107 and r108. -Upstream-ID: 5d2871d626de6c2b3ab6b12783b87a8b3564cb56 ---- - cpumf/Makefile | 3 +- - cpumf/data/cpum-cf-extended-z15.ctr | 376 ++++++++++++++++++++++++++++ - cpumf/data/cpum-cf-hw-counter.map | 5 +- - 3 files changed, 380 insertions(+), 4 deletions(-) - create mode 100644 cpumf/data/cpum-cf-extended-z15.ctr - -diff --git a/cpumf/Makefile b/cpumf/Makefile -index 2e11810..e97f20d 100644 ---- a/cpumf/Makefile -+++ b/cpumf/Makefile -@@ -9,7 +9,8 @@ DATA_FILES = cpum-cf-hw-counter.map \ - cpum-cf-csvn-12345.ctr cpum-cf-csvn-6.ctr \ - cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \ - cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \ -- cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr -+ cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr \ -+ cpum-cf-extended-z15.ctr - LIB_FILES = bin/cpumf_helper - USRBIN_SCRIPTS = bin/lscpumf - USRSBIN_SCRIPTS = bin/chcpumf -diff --git a/cpumf/data/cpum-cf-extended-z15.ctr b/cpumf/data/cpum-cf-extended-z15.ctr -new file mode 100644 -index 0000000..0fc935c ---- /dev/null -+++ b/cpumf/data/cpum-cf-extended-z15.ctr -@@ -0,0 +1,376 @@ -+# Counter decriptions for the -+# IBM z14 extended counter and MT-diagnostic counter set -+# -+# Notes for transactional-execution mode symbolic names: -+# TX .. transactional-execution mode -+# NC .. nonconstrained -+# C .. constrained -+# -+# Undefined counters in the extended counter set: -+# 142 -+# 158-161 -+# 176-223 -+# 227-231 -+# 233-242 -+# 246-255 -+# Undefined counters in the MT-diagnostic counter set: -+# 450-495 -+# -+# -+# Extended Counter Set -+# --------------------------------------------------------------------- -+Counter:128 Name:L1D_RO_EXCL_WRITES -+Short-Description:L1D Read-only Exclusive Writes -+Description: -+A directory write to the Level-1 Data cache where the line was -+originally in a Read-Only state in the cache but has been updated -+to be in the Exclusive state that allows stores to the cache line -+. -+Counter:129 Name:DTLB2_WRITES -+Short-Description:DTLB2 Writes -+Description: -+A translation has been written into The Translation Lookaside -+Buffer 2 (TLB2) and the request was made by the data cache -+. -+Counter:130 Name:DTLB2_MISSES -+Short-Description:DTLB2 Misses -+Description: -+A TLB2 miss is in progress for a request made by the data cache. -+Incremented by one for every TLB2 miss in progress for the Level-1 -+Data cache on this cycle -+. -+Counter:131 Name:DTLB2_HPAGE_WRITES -+Short-Description:DTLB2 One-Megabyte Page Writes -+Description: -+A translation entry was written into the Combined Region and Segment -+Table Entry array in the Level-2 TLB for a one-megabyte page -+. -+Counter:132 Name:DTLB2_GPAGE_WRITES -+Short-Description:DTLB2 Two-Gigabyte Page Writes -+Description: -+A translation entry for a two-gigabyte page was written into the -+Level-2 TLB -+. -+Counter:133 Name:L1D_L2D_SOURCED_WRITES -+Short-Description:L1D L2D Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the -+returned cache line was sourced from the Level-2 Data cache -+. -+Counter:134 Name:ITLB2_WRITES -+Short-Description:ITLB2 Writes -+Description: -+A translation entry has been written into the Translation Lookaside -+Buffer 2 (TLB2) and the request was made by the instruction cache -+. -+Counter:135 Name:ITLB2_MISSES -+Short-Description:ITLB2 Misses -+Description: -+A TLB2 miss is in progress for a request made by the instruction cache. -+Incremented by one for every TLB2 miss in progress for the Level-1 -+Instruction cache in a cycle -+. -+Counter:136 Name:L1I_L2I_SOURCED_WRITES -+Short-Description:L1I L2I Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from the Level-2 Instruction cache -+. -+Counter:137 Name:TLB2_PTE_WRITES -+Short-Description:TLB2 PTE Writes -+Description: -+A translation entry was written into the Page Table Entry array in the -+Level-2 TLB -+. -+Counter:138 Name:TLB2_CRSTE_WRITES -+Short-Description:TLB2 CRSTE Writes -+Description: -+Translation entries were written into the Combined Region and Segment -+Table Entry array and the Page Table Entry array in the Level-2 TLB -+. -+Counter:139 Name:TLB2_ENGINES_BUSY -+Short-Description:TLB2 Engines Busy -+Description: -+The number of Level-2 TLB translation engines busy in a cycle -+. -+Counter:140 Name:TX_C_TEND -+Short-Description:Completed TEND instructions in constrained TX mode -+Description: -+A TEND instruction has completed in a constrained transactional-execution -+mode -+. -+Counter:141 Name:TX_NC_TEND -+Short-Description:Completed TEND instructions in non-constrained TX mode -+Description: -+A TEND instruction has completed in a non-constrained -+transactional-execution mode -+. -+Counter:143 Name:L1C_TLB2_MISSES -+Short-Description:L1C TLB2 Misses -+Description: -+Increments by one for any cycle where a level-1 cache or level-2 TLB miss -+is in progress -+. -+Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES -+Short-Description:L1D On-Chip L3 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an On-Chip Level-3 cache without intervention -+. -+Counter:145 Name:L1D_ONCHIP_MEMORY_SOURCED_WRITES -+Short-Description:L1D On-Chip Memory Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from On-Chip memory -+. -+Counter:146 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV -+Short-Description:L1D On-Chip L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an On-Chip Level-3 cache with intervention -+. -+Counter:147 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES -+Short-Description:L1D On-Cluster L3 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from On-Cluster Level-3 cache withountervention -+. -+Counter:148 Name:L1D_ONCLUSTER_MEMORY_SOURCED_WRITES -+Short-Description:L1D On-Cluster Memory Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an On-Cluster memory -+. -+Counter:149 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES_IV -+Short-Description:L1D On-Cluster L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an On-Cluster Level-3 cache with intervention -+. -+Counter:150 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES -+Short-Description:L1D Off-Cluster L3 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an Off-Cluster Level-3 cache without -+intervention -+. -+Counter:151 Name:L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES -+Short-Description:L1D Off-Cluster Memory Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from Off-Cluster memory -+. -+Counter:152 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV -+Short-Description:L1D Off-Cluster L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an Off-Cluster Level-3 cache with intervention -+. -+Counter:153 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES -+Short-Description:L1D Off-Drawer L3 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an Off-Drawer Level-3 cache without -+intervention -+. -+Counter:154 Name:L1D_OFFDRAWER_MEMORY_SOURCED_WRITES -+Short-Description:L1D Off-Drawer Memory Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from Off-Drawer memory -+. -+Counter:155 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES_IV -+Short-Description:L1D Off-Drawer L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from an Off-Drawer Level-3 cache with intervention -+. -+Counter:156 Name:L1D_ONDRAWER_L4_SOURCED_WRITES -+Short-Description:L1D On-Drawer L4 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from On-Drawer Level-4 cache -+. -+Counter:157 Name:L1D_OFFDRAWER_L4_SOURCED_WRITES -+Short-Description:L1D Off-Drawer L4 Sourced Writes -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from Off-Drawer Level-4 cache -+. -+Counter:158 Name:L1D_ONCHIP_L3_SOURCED_WRITES_RO -+Short-Description:L1D On-Chip L3 Sourced Writes read-only -+Description: -+A directory write to the Level-1 Data cache directory where the returned -+cache line was sourced from On-Chip L3 but a read-only invalidate was -+done to remove other copies of the cache line -+. -+Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES -+Short-Description:L1I On-Chip L3 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache ine was sourced from an On-Chip Level-3 cache without -+intervention -+. -+Counter:163 Name:L1I_ONCHIP_MEMORY_SOURCED_WRITES -+Short-Description:L1I On-Chip Memory Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache ine was sourced from On-Chip memory -+. -+Counter:164 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV -+Short-Description:L1I On-Chip L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache ine was sourced from an On-Chip Level-3 cache with -+intervention -+. -+Counter:165 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES -+Short-Description:L1I On-Cluster L3 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an On-Cluster Level-3 cache without -+intervention -+. -+Counter:166 Name:L1I_ONCLUSTER_MEMORY_SOURCED_WRITES -+Short-Description:L1I On-Cluster Memory Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an On-Cluster memory -+. -+Counter:167 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES_IV -+Short-Description:L1I On-Cluster L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from On-Cluster Level-3 cache with -+intervention -+. -+Counter:168 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES -+Short-Description:L1I Off-Cluster L3 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an Off-Cluster Level-3 cache without -+intervention -+. -+Counter:169 Name:L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES -+Short-Description:L1I Off-Cluster Memory Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from Off-Cluster memory -+. -+Counter:170 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV -+Short-Description:L1I Off-Cluster L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an Off-Cluster Level-3 cache with -+intervention -+. -+Counter:171 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES -+Short-Description:L1I Off-Drawer L3 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an Off-Drawer Level-3 cache without -+intervention -+. -+Counter:172 Name:L1I_OFFDRAWER_MEMORY_SOURCED_WRITES -+Short-Description:L1I Off-Drawer Memory Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from Off-Drawer memory -+. -+Counter:173 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES_IV -+Short-Description:L1I Off-Drawer L3 Sourced Writes with Intervention -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from an Off-Drawer Level-3 cache with -+intervention -+. -+Counter:174 Name:L1I_ONDRAWER_L4_SOURCED_WRITES -+Short-Description:L1I On-Drawer L4 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from On-Drawer Level-4 cache -+. -+Counter:175 Name:L1I_OFFDRAWER_L4_SOURCED_WRITES -+Short-Description:L1I Off-Drawer L4 Sourced Writes -+Description: -+A directory write to the Level-1 Instruction cache directory where the -+returned cache line was sourced from Off-Drawer Level-4 cache -+. -+Counter:224 Name:BCD_DFP_EXECUTION_SLOTS -+Short-Description:BCD DFP Execution Slots -+Description: -+Count of floating point execution slots used for finished Binary Coded -+Decimal to Decimal Floating Point conversions. Instructions: CDZT, -+CXZT, CZDT, CZXT -+. -+Counter:225 Name:VX_BCD_EXECUTION_SLOTS -+Short-Description:VX BCD Execution Slots -+Description: -+Count of floating point execution slots used for finished vector arithmetic -+Binary Coded Decimal instructions. Instructions: VAP, VSP, VMPVMSP, VDP, -+VSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ, VCVB, VCVBG, VCVDVCVDG -+. -+Counter:226 Name:DECIMAL_INSTRUCTIONS -+Short-Description:Decimal Instructions -+Description: -+Decimal instructions dispatched. Instructions: CVB, CVD, AP, CP, DP, ED, -+EDMK, MP, SRP, SP, ZAP -+. -+Counter:232 Name:LAST_HOST_TRANSLATIONS -+Short-Description:Last host translation done -+Description: -+Last Host Translation done -+. -+Counter:243 Name:TX_NC_TABORT -+Short-Description:Aborted transactions in non-constrained TX mode -+Description: -+A transaction abort has occurred in a non-constrained -+transactional-execution mode -+. -+Counter:244 Name:TX_C_TABORT_NO_SPECIAL -+Short-Description:Aborted transactions in constrained TX mode not using special completion logic -+Description: -+A transaction abort has occurred in a constrained transactional-execution -+mode and the CPU is not using any special logic to allow the transaction -+to complete -+. -+Counter:245 Name:TX_C_TABORT_SPECIAL -+Short-Description:Aborted transactions in constrained TX mode using special completion logic -+Description: -+A transaction abort has occurred in a constrained transactional-execution -+mode and the CPU is using special logic to allow the transaction to -+complete -+. -+Counter:247 Name:DFLT_ACCESS -+Short-Description:Cycles CPU spent obtaining access to Deflate unit -+Description: -+Cycles CPU spent obtaining access to Deflate unit -+. -+Counter:252 Name:DFLT_CYCLES -+Short-Description:Cycles CPU is using Deflate unit -+Description: -+Cycles CPU is using Deflate unit -+. -+Counter:264 Name:DFLT_CC -+Short-Description:Increments by one for every DEFLATE CONVERSION CALL instruction executed -+Description: -+Increments by one for every DEFLATE CONVERSION CALL instruction executed -+. -+Counter:265 Name:DFLT_CCERROR -+Short-Description:Increments by one for every DEFLATE CONVERSION CALL instruction executed that ended in Condition Codes 0, 1 or 2 -+Description: -+Increments by one for every DEFLATE CONVERSION CALL instruction executed that ended in Condition Codes 0, 1 or 2 -+. -+# -+# MT-diagnostic counter set -+# --------------------------------------------------------------------- -+Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE -+Short-Description:Cycle count with one thread active -+Description: -+Cycle count with one thread active -+. -+Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE -+Short-Description:Cycle count with two threads active -+Description: -+Cycle count with two threads active -+. -diff --git a/cpumf/data/cpum-cf-hw-counter.map b/cpumf/data/cpum-cf-hw-counter.map -index 5729b95..fec8048 100644 ---- a/cpumf/data/cpum-cf-hw-counter.map -+++ b/cpumf/data/cpum-cf-hw-counter.map -@@ -28,7 +28,6 @@ - 2965 => 'cpum-cf-extended-z13.ctr', - 3906 => 'cpum-cf-extended-z14.ctr', - 3907 => 'cpum-cf-extended-z14.ctr', -- # Identical with z14 -- 8561 => 'cpum-cf-extended-z14.ctr', -- 8562 => 'cpum-cf-extended-z14.ctr', -+ 8561 => 'cpum-cf-extended-z15.ctr', -+ 8562 => 'cpum-cf-extended-z15.ctr', - }; --- -2.21.3 - - -From 4dae25adb621f717938de97aefeb5cae61aec90b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 27 May 2020 11:24:08 +0200 -Subject: [PATCH 52/57] ipl_tools: support clear attribute for FCP and CCW - re-IPL (#1812983) - -This patch adds support for the "clear" sysfs attribute for re-IPL, if -available. This attribute allows to control whether the memory should -be cleared on re-IPL. - -Upstream ID: 4753340e79a1fb1f45e86c39e391fc8eaa03408f ---- - ipl_tools/cmd_chreipl.c | 32 +++++++++++++++++++++++++++++++- - ipl_tools/cmd_lsreipl.c | 6 ++++++ - ipl_tools/man/chreipl.8 | 15 +++++++++++++++ - 3 files changed, 52 insertions(+), 1 deletion(-) - -diff --git a/ipl_tools/cmd_chreipl.c b/ipl_tools/cmd_chreipl.c -index 5ba0b22..640aa0c 100644 ---- a/ipl_tools/cmd_chreipl.c -+++ b/ipl_tools/cmd_chreipl.c -@@ -59,6 +59,7 @@ static const char *const usage_chreipl = - "Options for ccw target:\n" - " -d, --device Device number of the CCW IPL device\n" - " -L, --loadparm Loadparm specification\n" -+" -c, --clear 0|1 Control if memory is cleared on re-IPL\n" - "\n" - "Options for fcp target:\n" - " -d, --device Device number of the adapter of the FCP IPL device\n" -@@ -66,6 +67,7 @@ static const char *const usage_chreipl = - " -w --wwpn World Wide Port Name of the FCP IPL device\n" - " -b, --bootprog Bootprog specification\n" - " -L, --loadparm Loadparm specification\n" -+" -c, --clear 0|1 Control if memory is cleared on re-IPL\n" - "\n" - "Options for nss target:\n" - " -n, --name Identifier of the NSS\n" -@@ -95,6 +97,7 @@ static struct locals { - int target_type_set; - int target_type_auto_mode; - enum reipl_type reipl_type; /* CCW, FCP, NSS */ -+ int reipl_clear; - } l; - - static void __noreturn print_usage_chreipl_exit(void) -@@ -457,6 +460,16 @@ static void set_target_type_auto(const char *arg) - } - } - -+static void set_reipl_clear(const char *arg) -+{ -+ if (arg[0] == '1') -+ l.reipl_clear = 1; -+ else if (arg[0] == '0') -+ l.reipl_clear = 0; -+ else -+ ERR_EXIT("re-IPL clear argument must be either 1 or 0"); -+} -+ - static void parse_chreipl_options(int argc, char *argv[]) - { - int opt, idx; -@@ -471,9 +484,10 @@ static void parse_chreipl_options(int argc, char *argv[]) - { "bootparms", required_argument, NULL, 'p' }, - { "force", no_argument, NULL, 'f' }, - { "version", no_argument, NULL, 'v' }, -+ { "clear", required_argument, NULL, 'c' }, - { NULL, 0, NULL, 0 } - }; -- static const char optstr[] = "hcd:vw:l:fL:b:n:p:"; -+ static const char optstr[] = "hd:vw:l:fL:b:n:p:c:"; - - /* dont run without any argument */ - if (argc == 1) -@@ -490,6 +504,8 @@ static void parse_chreipl_options(int argc, char *argv[]) - else - set_target_type_auto(argv[1]); - -+ l.reipl_clear = -1; -+ - while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) { - switch (opt) { - case 'h': -@@ -518,6 +534,9 @@ static void parse_chreipl_options(int argc, char *argv[]) - case 'f': - l.force_set = 1; - break; -+ case 'c': -+ set_reipl_clear(optarg); -+ break; - case 'v': - print_version_exit(); - default: -@@ -597,6 +616,11 @@ static void chreipl_ccw(void) - strlen(l.bootparms), BOOTPARMS_CCW_MAX); - } - -+ if (l.reipl_clear >= 0) { -+ check_exists("reipl/ccw/clear", "CCW re-IPL clear attribute"); -+ write_str(l.reipl_clear ? "1" : "0", "reipl/ccw/clear"); -+ } -+ - /* - * On old systems that use CCW reipl loadparm cannot be set - */ -@@ -624,6 +648,12 @@ static void chreipl_fcp(void) - ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", - strlen(l.bootparms), BOOTPARMS_FCP_MAX); - } -+ -+ if (l.reipl_clear >= 0) { -+ check_exists("reipl/fcp/clear", "FCP re-IPL clear attribute"); -+ write_str(l.reipl_clear ? "1" : "0", "reipl/fcp/clear"); -+ } -+ - /* - * On old systems the FCP reipl loadparm cannot be set - */ -diff --git a/ipl_tools/cmd_lsreipl.c b/ipl_tools/cmd_lsreipl.c -index c906f83..a829b9d 100644 ---- a/ipl_tools/cmd_lsreipl.c -+++ b/ipl_tools/cmd_lsreipl.c -@@ -59,6 +59,7 @@ void print_fcp(int show_ipl, int dump) - char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" : - "/sys/firmware/reipl/fcp/loadparm"; - char loadparm[9], loadparm_path[PATH_MAX]; -+ char *path_reipl_clear = "/sys/firmware/reipl/fcp/clear"; - - if (dump) - printf("%-12s fcp_dump\n", get_ipl_banner(show_ipl)); -@@ -79,6 +80,8 @@ void print_fcp(int show_ipl, int dump) - } - if (access(path_bootparms, R_OK) == 0) - print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); -+ if (!show_ipl && access(path_reipl_clear, R_OK) == 0) -+ print_fw_str("clear: %s\n", dir, "clear"); - } - - void print_ccw(int show_ipl) -@@ -89,6 +92,7 @@ void print_ccw(int show_ipl) - "/sys/firmware/reipl/ccw/loadparm"; - char *path_bootparms = show_ipl ? "/sys/firmware/ipl/parm" : - "/sys/firmware/reipl/ccw/parm"; -+ char *path_reipl_clear = "/sys/firmware/reipl/ccw/clear"; - - printf("%-12s ccw\n", get_ipl_banner(show_ipl)); - print_fw_str("Device: %s\n", dir, "device"); -@@ -101,6 +105,8 @@ void print_ccw(int show_ipl) - } - if (access(path_bootparms, R_OK) == 0) - print_fw_str("Bootparms: \"%s\"\n", dir, "parm"); -+ if (!show_ipl && access(path_reipl_clear, R_OK) == 0) -+ print_fw_str("clear: %s\n", dir, "clear"); - } - - static void parse_lsreipl_options(int argc, char *argv[]) -diff --git a/ipl_tools/man/chreipl.8 b/ipl_tools/man/chreipl.8 -index 9adb93a..91ddfad 100644 ---- a/ipl_tools/man/chreipl.8 -+++ b/ipl_tools/man/chreipl.8 -@@ -132,6 +132,14 @@ the same as 0.0.5000). - Specifies an entry in the - .BR zipl (8) - boot menu. If this option is omitted, the default menu entry is used. -+ -+.TP -+.BR "\-c" " or " "\-\-clear" -+Specify whether memory should be cleared on re-IPL. Possible values are 0 to -+disable and 1 to enable memory clearing on re-IPL. -+Memory clearing is supported if the "clear" attribute is present in -+/sys/firmware/reipl/ccw/. -+ - .PP - \fBExamples:\fP - .br -@@ -176,6 +184,13 @@ configuration that is defined by the - boot menu. Instead it can be used to control higher level boot loaders - like GRUB. For more details refer to distribution specific documentation. - -+.TP -+.BR "\-c" " or " "\-\-clear" -+Specify whether memory should be cleared on re-IPL. Possible values are 0 to -+disable and 1 to enable memory clearing on re-IPL. -+Memory clearing is supported if the "clear" attribute is present in -+/sys/firmware/reipl/fcp/. -+ - .PP - \fBExamples:\fP - .br --- -2.21.3 - - -From cf5bfa1758a83d0bc29763214d1f194a259bc12a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 28 May 2020 09:51:52 +0200 -Subject: [PATCH 53/57] zipl: Rebase to 2.13.0 (#1821250) - -Summary: zipl: Rebase to 2.13.0 -Description: In order to prepare for the inclusion of the new genprotimg - tool rebase zipl to 2.13.0 level. ---- - Makefile | 4 +- - README.md | 14 +- - cmsfs-fuse/cmsfs-fuse.1 | 2 +- - cmsfs-fuse/dasd.c | 1 + - cmsfs-fuse/helper.h | 2 - - dasdinfo/dasdinfo.8 | 4 +- - genprotimg/.gitignore | 5 + - genprotimg/Makefile | 27 + - genprotimg/README.md | 85 +++ - genprotimg/boot/.gitignore | 4 + - genprotimg/boot/Makefile | 97 +++ - genprotimg/boot/common_memory_layout.h | 25 + - genprotimg/boot/head.S | 29 + - genprotimg/boot/stage3a.c | 62 ++ - genprotimg/boot/stage3a.h | 34 + - genprotimg/boot/stage3a.lds.S | 103 ++++ - genprotimg/boot/stage3a_init.S | 26 + - genprotimg/boot/stage3b.c | 77 +++ - genprotimg/boot/stage3b.h | 42 ++ - genprotimg/boot/stage3b.lds.S | 87 +++ - genprotimg/boot/stage3b_reloc.S | 53 ++ - genprotimg/man/Makefile | 12 + - genprotimg/man/genprotimg.8 | 97 +++ - genprotimg/samples/check_hostkeydoc | 276 +++++++++ - genprotimg/src/Makefile | 101 +++ - genprotimg/src/common.h | 35 ++ - genprotimg/src/genprotimg.c | 181 ++++++ - genprotimg/src/include/pv_crypto_def.h | 25 + - genprotimg/src/include/pv_hdr_def.h | 84 +++ - genprotimg/src/pv/pv_args.c | 405 ++++++++++++ - genprotimg/src/pv/pv_args.h | 53 ++ - genprotimg/src/pv/pv_comp.c | 446 ++++++++++++++ - genprotimg/src/pv/pv_comp.h | 78 +++ - genprotimg/src/pv/pv_comps.c | 252 ++++++++ - genprotimg/src/pv/pv_comps.h | 42 ++ - genprotimg/src/pv/pv_error.c | 37 ++ - genprotimg/src/pv/pv_error.h | 62 ++ - genprotimg/src/pv/pv_hdr.c | 293 +++++++++ - genprotimg/src/pv/pv_hdr.h | 36 ++ - genprotimg/src/pv/pv_image.c | 821 +++++++++++++++++++++++++ - genprotimg/src/pv/pv_image.h | 68 ++ - genprotimg/src/pv/pv_ipib.c | 128 ++++ - genprotimg/src/pv/pv_ipib.h | 27 + - genprotimg/src/pv/pv_opt_item.c | 26 + - genprotimg/src/pv/pv_opt_item.h | 20 + - genprotimg/src/pv/pv_stage3.c | 164 +++++ - genprotimg/src/pv/pv_stage3.h | 30 + - genprotimg/src/utils/align.h | 24 + - genprotimg/src/utils/buffer.c | 69 +++ - genprotimg/src/utils/buffer.h | 31 + - genprotimg/src/utils/crypto.c | 798 ++++++++++++++++++++++++ - genprotimg/src/utils/crypto.h | 104 ++++ - genprotimg/src/utils/file_utils.c | 234 +++++++ - genprotimg/src/utils/file_utils.h | 34 + - include/boot/ipl.h | 191 ++++++ - include/boot/linux_layout.h | 34 + - include/boot/loaders_layout.h | 43 ++ - include/boot/s390.h | 467 ++++++++++++++ - include/boot/sigp.h | 56 ++ - include/lib/util_base.h | 17 +- - include/lib/zt_common.h | 58 ++ - libutil/util_panic_example.c | 2 +- - netboot/Makefile.pxelinux.0 | 4 +- - qethconf/qethconf.8 | 2 +- - vmur/vmur.8 | 2 +- - zconf/qeth/misc.h | 2 - - zdev/include/misc.h | 2 +- - zdump/opts.c | 4 +- - zdump/zg.c | 2 +- - zdump/zg.h | 3 - - zipl/boot/.gitignore | 2 + - zipl/boot/Makefile | 47 +- - zipl/boot/cio.c | 2 +- - zipl/boot/cio.h | 2 +- - zipl/boot/ebcdic.c | 30 + - zipl/boot/ebcdic.h | 45 ++ - zipl/boot/ebcdic_conv.c | 167 +++++ - zipl/boot/ebcdic_conv.h | 21 + - zipl/boot/eckd0_cdl.S | 6 +- - zipl/boot/eckd0_ldl.S | 6 +- - zipl/boot/eckd2.c | 2 +- - zipl/boot/eckd2dump.c | 2 +- - zipl/boot/eckd2dump_mv.c | 8 +- - zipl/boot/eckd2dump_sv.c | 5 +- - zipl/boot/error.h | 6 + - zipl/boot/fba0.S | 6 +- - zipl/boot/fba2.c | 2 +- - zipl/boot/fba2dump.c | 6 +- - zipl/boot/head.S | 6 +- - zipl/boot/kdump.c | 2 +- - zipl/boot/kdump.h | 2 +- - zipl/boot/kdump3.c | 4 +- - zipl/boot/libc.c | 38 +- - zipl/boot/libc.h | 43 +- - zipl/boot/menu.c | 26 +- - zipl/boot/menu.h | 5 - - zipl/boot/s390.h | 99 +-- - zipl/boot/sclp.c | 69 ++- - zipl/boot/sclp.h | 39 +- - zipl/boot/sclp_stage3.c | 2 +- - zipl/boot/sclp_stage3.h | 8 +- - zipl/boot/stage2.c | 7 +- - zipl/boot/stage2.h | 18 +- - zipl/boot/{stage2.lds => stage2.lds.S} | 44 +- - zipl/boot/stage2dump.c | 6 +- - zipl/boot/stage2dump.h | 5 +- - zipl/boot/stage3.c | 281 +++------ - zipl/boot/stage3.h | 179 +----- - zipl/boot/stage3.lds | 18 +- - zipl/boot/stage3.lds.S | 62 ++ - zipl/boot/tape0.S | 25 +- - zipl/boot/tape2dump.c | 10 +- - zipl/include/boot.h | 71 +-- - zipl/include/bootmap.h | 4 +- - zipl/include/misc.h | 1 + - zipl/include/zipl.h | 29 +- - zipl/man/zipl.8.in | 10 +- - zipl/man/zipl.conf.5.in | 8 +- - zipl/src/Makefile | 8 +- - zipl/src/boot.c | 32 +- - zipl/src/bootmap.c | 34 +- - zipl/src/disk.c | 32 +- - zipl/src/install.c | 3 +- - zipl/src/job.c | 23 +- - zipl/src/misc.c | 16 +- - zipl/src/scan.c | 5 + - 126 files changed, 7617 insertions(+), 787 deletions(-) - create mode 100644 genprotimg/.gitignore - create mode 100644 genprotimg/Makefile - create mode 100644 genprotimg/README.md - create mode 100644 genprotimg/boot/.gitignore - create mode 100644 genprotimg/boot/Makefile - create mode 100644 genprotimg/boot/common_memory_layout.h - create mode 100644 genprotimg/boot/head.S - create mode 100644 genprotimg/boot/stage3a.c - create mode 100644 genprotimg/boot/stage3a.h - create mode 100644 genprotimg/boot/stage3a.lds.S - create mode 100644 genprotimg/boot/stage3a_init.S - create mode 100644 genprotimg/boot/stage3b.c - create mode 100644 genprotimg/boot/stage3b.h - create mode 100644 genprotimg/boot/stage3b.lds.S - create mode 100644 genprotimg/boot/stage3b_reloc.S - create mode 100644 genprotimg/man/Makefile - create mode 100644 genprotimg/man/genprotimg.8 - create mode 100644 genprotimg/samples/check_hostkeydoc - create mode 100644 genprotimg/src/Makefile - create mode 100644 genprotimg/src/common.h - create mode 100644 genprotimg/src/genprotimg.c - create mode 100644 genprotimg/src/include/pv_crypto_def.h - create mode 100644 genprotimg/src/include/pv_hdr_def.h - create mode 100644 genprotimg/src/pv/pv_args.c - create mode 100644 genprotimg/src/pv/pv_args.h - create mode 100644 genprotimg/src/pv/pv_comp.c - create mode 100644 genprotimg/src/pv/pv_comp.h - create mode 100644 genprotimg/src/pv/pv_comps.c - create mode 100644 genprotimg/src/pv/pv_comps.h - create mode 100644 genprotimg/src/pv/pv_error.c - create mode 100644 genprotimg/src/pv/pv_error.h - create mode 100644 genprotimg/src/pv/pv_hdr.c - create mode 100644 genprotimg/src/pv/pv_hdr.h - create mode 100644 genprotimg/src/pv/pv_image.c - create mode 100644 genprotimg/src/pv/pv_image.h - create mode 100644 genprotimg/src/pv/pv_ipib.c - create mode 100644 genprotimg/src/pv/pv_ipib.h - create mode 100644 genprotimg/src/pv/pv_opt_item.c - create mode 100644 genprotimg/src/pv/pv_opt_item.h - create mode 100644 genprotimg/src/pv/pv_stage3.c - create mode 100644 genprotimg/src/pv/pv_stage3.h - create mode 100644 genprotimg/src/utils/align.h - create mode 100644 genprotimg/src/utils/buffer.c - create mode 100644 genprotimg/src/utils/buffer.h - create mode 100644 genprotimg/src/utils/crypto.c - create mode 100644 genprotimg/src/utils/crypto.h - create mode 100644 genprotimg/src/utils/file_utils.c - create mode 100644 genprotimg/src/utils/file_utils.h - create mode 100644 include/boot/ipl.h - create mode 100644 include/boot/linux_layout.h - create mode 100644 include/boot/loaders_layout.h - create mode 100644 include/boot/s390.h - create mode 100644 include/boot/sigp.h - create mode 100644 zipl/boot/.gitignore - create mode 100644 zipl/boot/ebcdic.c - create mode 100644 zipl/boot/ebcdic.h - create mode 100644 zipl/boot/ebcdic_conv.c - create mode 100644 zipl/boot/ebcdic_conv.h - rename zipl/boot/{stage2.lds => stage2.lds.S} (57%) - create mode 100644 zipl/boot/stage3.lds.S - -diff --git a/Makefile b/Makefile -index 8a07a4d..f2a4680 100644 ---- a/Makefile -+++ b/Makefile -@@ -8,7 +8,9 @@ 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 zpcictl -+ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ -+ genprotimg -+ - SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) - - all: $(TOOL_DIRS) -diff --git a/README.md b/README.md -index af1024e..af91537 100644 ---- a/README.md -+++ b/README.md -@@ -31,6 +31,9 @@ Package contents - * dasdinfo: - Display unique DASD ID, either UID or volser. - -+ * genprotimg: -+ Create a protected virtualization image. -+ - * udev rules: - - 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/. - - 57-osasnmpd.rules: udev rules for osasnmpd. -@@ -265,9 +268,10 @@ build options: - | pfm | `HAVE_PFM` | cpacfstats | - | net-snmp | `HAVE_SNMP` | osasnmpd | - | glibc-static | `HAVE_LIBC_STATIC` | zfcpdump | --| openssl | `HAVE_OPENSSL` | zkey | -+| openssl | `HAVE_OPENSSL` | genprotimg,zkey | - | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | - | json-c | `HAVE_JSONC` | zkey-cryptsetup | -+| glib2 | `HAVE_GLIB2` | genprotimg | - - This table lists additional build or install options: - -@@ -285,6 +289,14 @@ Build and runtime requirements for specific tools - In the following more details on the build an runtime requirements of - the different tools are provided: - -+* genprotimg: -+ For building genprotimg you need OpenSSL version 1.1.0 or newer -+ installed (openssl-devel.rpm). Also required is glib2 -+ (glib2-devel.rpm). Tip: you may skip the genprotimg build by adding -+ `HAVE_OPENSSL=0` or `HAVE_GLIB2=0`. -+ -+ The runtime requirements are: openssl-libs (>= 1.1.0) and glib2. -+ - * osasnmpd: - You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm) - installed, before building the osasnmpd subagent. -diff --git a/cmsfs-fuse/cmsfs-fuse.1 b/cmsfs-fuse/cmsfs-fuse.1 -index f5fb385..c3082a5 100644 ---- a/cmsfs-fuse/cmsfs-fuse.1 -+++ b/cmsfs-fuse/cmsfs-fuse.1 -@@ -165,7 +165,7 @@ cmsfs-fuse uses a configuration file for automatic translation based on the file - Upon startup, cmsfs-fuse evaluates the file .cmsfs-fuse/filetypes.conf in the user's home directory. If the file does not - exist cmsfs-fuse evaluates the file /etc/cmsfs-fuse/filetypes.conf. - --The filetypes.conf file contains the CMS file types that are automaticaly translated to ASCII if cmsfs-fuse is started -+The filetypes.conf file contains the CMS file types that are automatically translated to ASCII if cmsfs-fuse is started - with the -t option. The syntax of the configuration file is one file type per line. Lines that start with a # followed by a space are treated as - comments and are ignored. The file type is 8 characters long and must consist of valid CMS file name characters only. - -diff --git a/cmsfs-fuse/dasd.c b/cmsfs-fuse/dasd.c -index 33bbfc7..16c2265 100644 ---- a/cmsfs-fuse/dasd.c -+++ b/cmsfs-fuse/dasd.c -@@ -19,6 +19,7 @@ - #include - #include - -+#include "lib/zt_common.h" - #include "cmsfs-fuse.h" - #include "edf.h" - #include "helper.h" -diff --git a/cmsfs-fuse/helper.h b/cmsfs-fuse/helper.h -index e44f76e..0787f59 100644 ---- a/cmsfs-fuse/helper.h -+++ b/cmsfs-fuse/helper.h -@@ -49,6 +49,4 @@ extern FILE *logfile; - fprintf(stderr, COMP "Warning, " __VA_ARGS__); \ - } while (0) - --#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -- - #endif -diff --git a/dasdinfo/dasdinfo.8 b/dasdinfo/dasdinfo.8 -index 4be39cd..2ff28f0 100644 ---- a/dasdinfo/dasdinfo.8 -+++ b/dasdinfo/dasdinfo.8 -@@ -34,7 +34,7 @@ Print DASD uid - This option prints the full uid of the DASD. When z/VM provides two - virtual devices that are actually located on the same real device, the - first four tokens of the uid will be identical for both devices. z/VM --may provide an additional token that allows to distinguish between -+may provide an additional token that can be used to distinguish between - different minidisks. You need both support in the Linux kernel and - z/VM to receive such an additional token. - -@@ -45,7 +45,7 @@ available by applying the PTFs for VM APAR VM64273 on z/VM 5.2.0 and higher. - .BI "-u|--uid" - Print DASD uid without z/VM minidisk token - --z/VM may provide an additional token that allows to distinguish -+z/VM may provide an additional token that can be used to distinguish - between different minidisks (see --extended-uid option). To remain - compatibile with systems that were installed on older Linux or z/VM - levels, the -u option will print the uid excluding any z/VM-provided -diff --git a/genprotimg/.gitignore b/genprotimg/.gitignore -new file mode 100644 -index 0000000..08318df ---- /dev/null -+++ b/genprotimg/.gitignore -@@ -0,0 +1,5 @@ -+tags -+compile_commands.json -+src/.check-dep-genprotimg -+src/.detect-openssl.dep.c -+src/genprotimg -diff --git a/genprotimg/Makefile b/genprotimg/Makefile -new file mode 100644 -index 0000000..127bde2 ---- /dev/null -+++ b/genprotimg/Makefile -@@ -0,0 +1,27 @@ -+# Common definitions -+include ../common.mak -+ -+.DEFAULT_GOAL := all -+ -+PKGDATADIR := "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg" -+TESTS := -+SUBDIRS := boot src man -+RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive -+ -+all: all-recursive -+ -+install: all install-recursive -+ $(INSTALL) -d -m 755 "$(PKGDATADIR)" -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 boot/stage3a.bin "$(PKGDATADIR)" -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 boot/stage3b_reloc.bin "$(PKGDATADIR)" -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(PKGDATADIR)" -+ -+clean: clean-recursive -+ -+$(RECURSIVE_TARGETS): -+ @target=`echo $@ |sed s/-recursive//`; \ -+ for d in $(SUBDIRS); do \ -+ $(MAKE) -C $$d $$target; \ -+ done -+ -+.PHONY: all install clean $(RECURSIVE_TARGETS) -diff --git a/genprotimg/README.md b/genprotimg/README.md -new file mode 100644 -index 0000000..e37ac33 ---- /dev/null -+++ b/genprotimg/README.md -@@ -0,0 +1,85 @@ -+# genprotimg -+ -+`genprotimg` takes a kernel, key files, optionally an initrd image, -+optionally a file containing the kernel command line parameters, and -+generates a single, bootable image file. The generated image file -+consists of a concatenation of a plain text boot loader, the encrypted -+components for kernel, initrd, kernel command line, and the -+integrity-protected PV header, containing the metadata necessary for -+running the guest in protected mode. See [Memory Layout](#memory-layout) -+for details about the internal structure of the created image. -+ -+It is possible to use the generated image as a kernel for zipl or for -+a direct kernel boot using QEMU. -+ -+## Getting started -+ -+If all dependencies are met a simple `make` call in the source tree -+should be enough for building `genprotimg`. -+ -+## Details -+ -+The main idea of `genprotimg` is: -+ -+1. read in all keys, IVs, and other information needed for the -+ encryption of the components and the generation of the PV header -+2. add stub stage3a (so we can calculate the memory addresses) -+3. add components: prepare the components (alignment and encryption) -+ and add them to the memory layout -+4. build and add stage3b: generate the stage3b and add it to the memory layout -+5. generate the PV header: generate the hashes (pld, ald, and tld) of -+ the components and create the PV header and IPIB -+6. parameterize the stub stage3a: uses the IPIB and PV header -+7. write the final image to the specified output path -+ -+### Boot Loader -+ -+The boot loader consists of two parts: -+ -+1. stage3a boot loader (cleartext), this loader is responsible for the -+ transition into the protected mode by doing diag308 subcode 8 and -+ 10 calls. -+2. stage3b boot loader (encrypted), this loader is very similar to the -+ normal zipl stage3 boot loader. It will be loaded by the Ultravisor -+ after the successful transition into protected mode. Like the zipl -+ stage3 boot loader it moves the kernel and patches in the values -+ for initrd and parmline. -+ -+The loaders have the following constraints: -+ -+1. It must be possible to place stage3a and stage3b at a location -+ greater than 0x10000 because the zipl stage3 loader zeroes out -+ everything at addresses lower than 0x10000 of the image. -+2. As the stage3 loader of zipl assumes that the passed kernel image -+ looks like a normal kernel image, the zipl stage3 loader modifies the -+ content at the memory area 0x10400 - 0x10800, therefore we leave this -+ area unused in our stage3a loader. -+3. The default entry address used by the zipl stage3 loader is 0x10000 -+ so we add a simple branch to 0x11000 at 0x10000 so the zipl stage3 -+ loader can modify the area 0x10400 - 0x10800 without affecting the -+ stage3a loader. -+ -+#### Detail about stage3b -+ -+The stage3b.bin is linked at address 0x9000, therefore it will not -+work at another address. The relocation support for the stage3b -+loader, so that it can be placed at addresses != 0x9000, is added in -+the loader with the name stage3b_reloc.bin. By default, if we're -+talking about stage3b we refer to stage3b_reloc.bin. -+ -+### Memory Layout -+ -+The memory layout of the bootable file looks like: -+ -+| Start | End | Use | -+|------------------------|------------|-----------------------------------------------------------------------| -+| 0 | 0x7 | Short PSW, starting instruction at 0x11000 | -+| 0x10000 | 0x10012 | Branch to 0x11000 | -+| 0x10013 | 0x10fff | Left intentionally unused | -+| 0x11000 | 0x12fff | Stage3a | -+| 0x13000 | 0x13fff | IPIB used as argument for the diag308 call | -+| 0x14000 | 0x1[45]fff | UV header used for the diag308 call (size can be either 1 or 2 pages) | -+| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel | -+| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel parameters | -+| NEXT_PAGE_ALIGNED_ADDR | | Encrypted initrd | -+| NEXT_PAGE_ALIGNED_ADDR | | Encrypted stage3b_reloc | -diff --git a/genprotimg/boot/.gitignore b/genprotimg/boot/.gitignore -new file mode 100644 -index 0000000..837684e ---- /dev/null -+++ b/genprotimg/boot/.gitignore -@@ -0,0 +1,4 @@ -+*.elf -+*.lds -+*.bin -+*.d -diff --git a/genprotimg/boot/Makefile b/genprotimg/boot/Makefile -new file mode 100644 -index 0000000..d39bbd6 ---- /dev/null -+++ b/genprotimg/boot/Makefile -@@ -0,0 +1,97 @@ -+# Common definitions -+include ../../common.mak -+ -+ZIPL_DIR := $(rootdir)/zipl -+ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot -+ -+INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include -+INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS)) -+ -+ALL_CFLAGS := $(NO_PIE_CFLAGS) -Os -g \ -+ $(INCLUDE_PARMS) \ -+ -DENABLE_SCLP_ASCII=1 \ -+ -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -+ -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ -+ -fno-delete-null-pointer-checks \ -+ -fexec-charset=IBM1047 -m64 -mpacked-stack \ -+ -mstack-size=4096 -mstack-guard=128 -msoft-float \ -+ -Wall -Wformat-security -Wextra -Werror -+ -+FILES := stage3a.bin stage3b.bin stage3b_reloc.bin -+ -+ZIPL_SRCS_C := libc.c ebcdic.c ebcdic_conv.c sclp.c -+ZIPL_SRCS_ASM := entry.S -+ -+ZIPL_OBJS_C := $(ZIPL_SRCS_C:%.c=%.o) -+ZIPL_OBJS_ASM := $(ZIPL_SRCS_ASM:%.S=%.o) -+ZIPL_OBJS := $(ZIPL_OBJS_C) $(ZIPL_OBJS_ASM) -+ -+ -+all: $(FILES) -+ -+# Prevent make from using some default rules... -+%: %.S -+ -+%.o: %.S Makefile -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< -+ -+%.o: %.c Makefile -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< -+ -+ -+# Dependencies for the .lds generation -+sources_lds_S = $(wildcard *.lds.S) -+dependencies_lds_S = $(sources_lds_S:%.lds.S=.%.lds.d) -+# Include all ".lds.d" dependency files for all make targets except for "clean" -+ifneq ($(MAKECMDGOALS),clean) -+-include $(dependencies_lds_S) -+endif -+ -+%.lds: %.lds.S Makefile -+ $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $< -+ -+# Special rules for zipl object files -+$(ZIPL_OBJS_C): %.o : $(ZIPL_BOOT_DIR)/%.c -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< -+ -+$(ZIPL_OBJS_ASM): %.o : $(ZIPL_BOOT_DIR)/%.S -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< -+ -+dependencies_zipl_c := $(ZIPL_SRCS_C:%.c=.%.o.d) -+ -+$(dependencies_zipl_c): .%.o.d : $(ZIPL_BOOT_DIR)/%.c -+ $(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ -+ -+ifneq ($(MAKECMDGOALS),clean) -+-include $(dependencies_zipl_c) -+endif -+ -+stage3b_reloc.o: stage3b.bin -+ -+stage3a.elf: head.o stage3a_init.o stage3a.o stage3a.lds $(ZIPL_OBJS) -+stage3b.elf: head.o stage3b.o stage3b.lds $(ZIPL_OBJS) -+stage3b_reloc.elf: -+ -+%.elf: %.o -+ case $* in \ -+ stage3a) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \ -+ stage3b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \ -+ stage3b_reloc) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \ -+ esac; \ -+ $(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@ -+ @chmod a-x $@ -+ -+%.bin: %.elf -+ $(OBJCOPY) -O binary \ -+ --only-section=.text* \ -+ --only-section=.ex_table* \ -+ --only-section=.fixup* \ -+ --only-section=.data* \ -+ --only-section=.rodata* \ -+ $< $@ -+ @chmod a-x $@ -+ -+clean: -+ rm -f *.o *.elf *.bin *.map .*.d *.lds -+ -+.PHONY: all clean -diff --git a/genprotimg/boot/common_memory_layout.h b/genprotimg/boot/common_memory_layout.h -new file mode 100644 -index 0000000..4750191 ---- /dev/null -+++ b/genprotimg/boot/common_memory_layout.h -@@ -0,0 +1,25 @@ -+/* -+ * Common memory layout for stage3a and stage3b bootloader. -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 COMMON_MEMORY_LAYOUT_H -+#define COMMON_MEMORY_LAYOUT_H -+ -+#include "boot/loaders_layout.h" -+ -+#define STACK_ADDRESS STAGE3_STACK_ADDRESS -+#define STACK_SIZE STAGE3_STACK_SIZE -+ -+#define HEAP_ADDRESS STAGE3_HEAP_ADDRESS -+#define HEAP_SIZE STAGE3_HEAP_SIZE -+ -+ -+#ifndef __ASSEMBLER__ -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* COMMON_MEMORY_LAYOUT_H */ -diff --git a/genprotimg/boot/head.S b/genprotimg/boot/head.S -new file mode 100644 -index 0000000..8fae460 ---- /dev/null -+++ b/genprotimg/boot/head.S -@@ -0,0 +1,29 @@ -+/* -+ * Entry code for stage 3a boot loader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 "common_memory_layout.h" -+ -+#include "boot/s390.h" -+#include "boot/sigp.h" -+ -+.section .text.start -+.globl _start -+_start: -+ /* Might be called after a diag308 so better set -+ * architecture and addressing mode -+ */ -+ lhi %r1, 1 -+ sigp %r1, %r0, SIGP_SET_ARCHITECTURE -+ sam64 -+ -+ /* Initialize stack */ -+ lgfi %r15, STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD -+ brasl %r14, initialize -+.previous -diff --git a/genprotimg/boot/stage3a.c b/genprotimg/boot/stage3a.c -new file mode 100644 -index 0000000..ae944d2 ---- /dev/null -+++ b/genprotimg/boot/stage3a.c -@@ -0,0 +1,62 @@ -+/* -+ * Main program for stage3a bootloader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 "libc.h" -+#include "stage3a.h" -+ -+#include "lib/zt_common.h" -+#include "boot/s390.h" -+#include "boot/ipl.h" -+#include "sclp.h" -+#include "error.h" -+ -+ -+static volatile struct stage3a_args __section(".loader_parms") loader_parms; -+ -+void __noreturn start(void) -+{ -+ int rc; -+ volatile struct stage3a_args *args = &loader_parms; -+ /* calculate the IPIB memory address */ -+ struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs); -+ -+ /* Calculate the PV header memory address and set it and its -+ * size in the IPIB. This allows the PV header to be position -+ * independent. -+ */ -+ ipib->pv.pv_hdr_addr = (uint64_t)args + args->hdr_offs; -+ ipib->pv.pv_hdr_size = args->hdr_size; -+ -+ /* set up ASCII and line-mode */ -+ sclp_setup(SCLP_LINE_ASCII_INIT); -+ -+ /* test if Secure Execution Unpack facility is available */ -+ stfle(S390_lowcore.stfle_fac_list, -+ ARRAY_SIZE(S390_lowcore.stfle_fac_list)); -+ rc = test_facility(UNPACK_FACILITY); -+ if (rc == 0) -+ panic(ENOPV, "Secure unpack facility is not available\n"); -+ -+ rc = diag308(DIAG308_SET_PV, ipib); -+ if (rc != DIAG308_RC_OK) -+ panic(EPV, "Protected boot setup has failed: 0x%x\n", rc); -+ -+ rc = diag308(DIAG308_UNPACK_PV, 0x0); -+ if (rc != DIAG308_RC_OK) { -+ sclp_setup(SCLP_LINE_ASCII_INIT); -+ panic(EPV, "Protected boot has failed: 0x%x\n", rc); -+ } -+ -+ while (1) -+ ; -+} -+ -+void panic_notify(unsigned long UNUSED(rc)) -+{ -+} -diff --git a/genprotimg/boot/stage3a.h b/genprotimg/boot/stage3a.h -new file mode 100644 -index 0000000..f362e86 ---- /dev/null -+++ b/genprotimg/boot/stage3a.h -@@ -0,0 +1,34 @@ -+/* -+ * Main program for stage3a bootloader. -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 STAGE3A_H -+#define STAGE3A_H -+ -+#include "lib/zt_common.h" -+#include "boot/loaders_layout.h" -+ -+#define STAGE3A_INIT_ENTRY IMAGE_ENTRY -+#define STAGE3A_ENTRY (STAGE3A_INIT_ENTRY + _AC(0x1000, UL)) -+#define STAGE3A_LOAD_ADDRESS IMAGE_LOAD_ADDRESS -+ -+ -+#ifndef __ASSEMBLER__ -+ -+#include -+ -+/* Must not have any padding */ -+struct stage3a_args { -+ uint64_t hdr_offs; -+ uint64_t hdr_size; -+ uint64_t ipib_offs; -+}; -+STATIC_ASSERT(sizeof(struct stage3a_args) == 3 * 8) -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* STAGE3A_H */ -diff --git a/genprotimg/boot/stage3a.lds.S b/genprotimg/boot/stage3a.lds.S -new file mode 100644 -index 0000000..eefb52c ---- /dev/null -+++ b/genprotimg/boot/stage3a.lds.S -@@ -0,0 +1,103 @@ -+/* -+ * Memory layout for stage 3a -+ * ========================== -+ * -+ * General memory layout -+ * --------------------- -+ * -+ * 0x00000 - 0x01fff Lowcore -+ * 0x02000 - 0x05fff Memory allocation (heap) -+ * 0x0f000 - 0x0ffff Stack -+ * 0x10000 - 0x10012 Jump to the "actual" stage3a code -+ * 0x11000 - 0x12fff Stage3a code + arguments (offsets and lengths to the -+ * actual data: IPIB and UV header) -+ */ -+ -+#include "stage3a.h" -+#include "common_memory_layout.h" -+ -+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") -+OUTPUT_ARCH(s390:64-bit) -+ -+ENTRY(_init) -+ -+SECTIONS -+{ -+ . = 0x0; -+ -+ . = HEAP_ADDRESS; -+ __heap_start = .; -+ .heap : { -+ . = . + HEAP_SIZE; -+ ASSERT(__heap_stop - __heap_start == HEAP_SIZE, -+ "Heap section doesn't conform to the described memory layout"); -+ } -+ __heap_stop = .; -+ -+ . = STACK_ADDRESS; -+ __stack_start = .; -+ .stack : { -+ . = . + STACK_SIZE; -+ ASSERT(__stack_end - __stack_start == STACK_SIZE, -+ "Stack section doesn't conform to the described memory layout"); -+ } -+ __stack_end = .; -+ -+ . = STAGE3A_INIT_ENTRY; -+ __text_init_start = .; -+ .text : { -+ stage3a_init.o(.text.init) -+ __text_init_stop = ABSOLUTE(.); -+ /* Text size of text_init must be smaller than 'PARMAREA - IMAGE_ENTRY', -+ * otherwise the text data could be overwritten by the original zipl stage3 -+ * boot loader */ -+ ASSERT(__text_init_stop - __text_init_start < PARMAREA - IMAGE_ENTRY, -+ "Text size must be smaller than 'PARMAREA - IMAGE_ENTRY'"); -+ . = 0x1000; -+ ASSERT(ABSOLUTE(.) == STAGE3A_ENTRY, -+ "Text section doesn't conform to the described memory layout"); -+ head.o(.text.start) -+ *(.text) -+ } -+ -+ .ex_table ALIGN(16) : { -+ __ex_table_start = .; -+ *(.ex_table) -+ __ex_table_stop = .; -+ } -+ -+ .bss ALIGN(16) : { -+ __bss_start = .; -+ *(.bss) -+ __bss_stop = .; -+ } -+ -+ .rodata ALIGN(16) : { -+ *(.rodata) -+ *(.rodata.*) -+ } -+ -+ .data ALIGN(16) : { -+ *(.data) -+ . = ALIGN(16); -+ /* The IPIB offset and the UV header offset and size will be -+ * saved in 'loader_parms' */ -+ __loader_parms_start = .; -+ KEEP(*(.loader_parms)); -+ __loader_parms_stop = .; -+ ASSERT(__loader_parms_stop - __loader_parms_start == 3 * 8, -+ "Data size must be equal to 'sizeof(struct stage3a_args)'"); -+ ASSERT(ABSOLUTE(.) < 0x13000, "Data section doesn't conform to the described memory layout"); -+ } -+ -+ /* List this explicitly as otherwise .note.gnu.build-id will be -+ * put at 0x0 */ -+ .notes : { -+ *(.note.*) -+ } -+ -+ /* Sections to be discarded */ -+ /DISCARD/ : { -+ *(.eh_frame) -+ } -+} -diff --git a/genprotimg/boot/stage3a_init.S b/genprotimg/boot/stage3a_init.S -new file mode 100644 -index 0000000..87c8d75 ---- /dev/null -+++ b/genprotimg/boot/stage3a_init.S -@@ -0,0 +1,26 @@ -+/* -+ * Entry code for stage 3a boot loader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 "stage3a.h" -+#include "boot/sigp.h" -+ -+.section .text.init -+.globl _init -+_init: -+ /* set architecture and switch to 64bit */ -+ lhi %r1, 1 -+ sigp %r1, %r0, SIGP_SET_ARCHITECTURE -+ sam64 -+ /* The original stage3 boot loader will try to store the -+ * kernel command line and the address and size of the -+ * ramdisk. Simply ignore this by starting at 0x11000. -+ */ -+ lgfi %r1, STAGE3A_ENTRY -+ br %r1 -+.previous -diff --git a/genprotimg/boot/stage3b.c b/genprotimg/boot/stage3b.c -new file mode 100644 -index 0000000..b1a7fbe ---- /dev/null -+++ b/genprotimg/boot/stage3b.c -@@ -0,0 +1,77 @@ -+/* -+ * Main program for stage3b bootloader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 "libc.h" -+#include "stage3b.h" -+ -+#include "lib/zt_common.h" -+#include "boot/s390.h" -+#include "boot/linux_layout.h" -+#include "boot/loaders_layout.h" -+#include "sclp.h" -+#include "error.h" -+ -+ -+static volatile struct stage3b_args __section(".loader_parms") loader_parms; -+ -+static inline void __noreturn load_psw(struct psw_t psw) -+{ -+ asm volatile("lpswe %0" : : "Q"(psw) : "cc"); -+ -+ while (1) -+ ; -+} -+ -+void __noreturn start(void) -+{ -+ volatile struct stage3b_args *args = &loader_parms; -+ volatile struct memblob *kernel = &args->kernel; -+ volatile struct memblob *cmdline = &args->cmdline; -+ volatile struct memblob *initrd = &args->initrd; -+ volatile struct psw_t psw = args->psw; -+ -+ /* set up ASCII and line-mode */ -+ sclp_setup(SCLP_LINE_ASCII_INIT); -+ -+ if (kernel->size < IMAGE_LOAD_ADDRESS) -+ panic(EINTERNAL, "Invalid kernel\n"); -+ -+ if (cmdline->size > COMMAND_LINE_SIZE) -+ panic(EINTERNAL, "Command line is too large\n"); -+ -+ /* move the kernel and cut the kernel header */ -+ memmove((void *)IMAGE_LOAD_ADDRESS, -+ (void *)(kernel->src + IMAGE_LOAD_ADDRESS), -+ kernel->size - IMAGE_LOAD_ADDRESS); -+ -+ /* move the kernel cmdline */ -+ memmove((void *)COMMAND_LINE, -+ (void *)cmdline->src, -+ cmdline->size); -+ /* the initrd does not need to be moved */ -+ -+ if (initrd->size != 0) { -+ /* copy initrd start address and size into new kernel space */ -+ *(unsigned long long *)INITRD_START = initrd->src; -+ *(unsigned long long *)INITRD_SIZE = initrd->size; -+ } -+ -+ /* disable ASCII and line-mode */ -+ sclp_setup(SCLP_DISABLE); -+ -+ /* use lpswe instead of diag308 as a I/O subsystem reset is not -+ * needed as this was already done by the diag308 subcode 10 call -+ * in stage3a -+ */ -+ load_psw(psw); -+} -+ -+void panic_notify(unsigned long UNUSED(rc)) -+{ -+} -diff --git a/genprotimg/boot/stage3b.h b/genprotimg/boot/stage3b.h -new file mode 100644 -index 0000000..421f0ea ---- /dev/null -+++ b/genprotimg/boot/stage3b.h -@@ -0,0 +1,42 @@ -+/* -+ * Main program for stage3b bootloader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 STAGE3B_H -+#define STAGE3B_H -+ -+#include "lib/zt_common.h" -+#include "boot/loaders_layout.h" -+ -+#define STAGE3B_ENTRY STAGE3_ENTRY -+#define STAGE3B_LOAD_ADDRESS STAGE3B_ENTRY -+ -+ -+#ifndef __ASSEMBLER__ -+ -+#include -+ -+#include "boot/s390.h" -+ -+/* Must not have any padding included */ -+struct memblob { -+ uint64_t src; -+ uint64_t size; -+}; -+STATIC_ASSERT(sizeof(struct memblob) == 2 * 8) -+ -+/* Must not have any padding included */ -+struct stage3b_args { -+ struct memblob kernel; -+ struct memblob cmdline; -+ struct memblob initrd; -+ struct psw_t psw; -+}; -+STATIC_ASSERT(sizeof(struct stage3b_args) == 3 * sizeof(struct memblob) + 16) -+#endif /* __ASSEMBLER__ */ -+#endif /* STAGE3B_H */ -diff --git a/genprotimg/boot/stage3b.lds.S b/genprotimg/boot/stage3b.lds.S -new file mode 100644 -index 0000000..2711d84 ---- /dev/null -+++ b/genprotimg/boot/stage3b.lds.S -@@ -0,0 +1,87 @@ -+/* -+ * Memory layout for stage 3b -+ * ========================== -+ * -+ * General memory layout -+ * --------------------- -+ * -+ * 0x00000 - 0x01fff Lowcore -+ * 0x02000 - 0x05fff Memory allocation (heap) -+ * 0x0a000 - 0x0efff Stage3b code -+ * 0x0f000 - 0x0ffff Stack -+ */ -+ -+#include "stage3b.h" -+#include "common_memory_layout.h" -+ -+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") -+OUTPUT_ARCH(s390:64-bit) -+ -+ENTRY(_start) -+ -+SECTIONS -+{ -+ . = 0x0; -+ -+ . = HEAP_ADDRESS; -+ __heap_start = .; -+ .heap : { -+ . = . + HEAP_SIZE; -+ ASSERT(__heap_stop - __heap_start == HEAP_SIZE, -+ "Heap section doesn't conform to the described memory layout"); -+ } -+ __heap_stop = .; -+ -+ . = STAGE3B_ENTRY; -+ .text : { -+ head.o(.text.start) -+ *(.text) -+ } -+ -+ .ex_table ALIGN(16) : { -+ __ex_table_start = .; -+ *(.ex_table) -+ __ex_table_stop = .; -+ } -+ -+ .bss ALIGN(16) : { -+ __bss_start = .; -+ *(.bss) -+ __bss_stop = .; -+ } -+ -+ .rodata ALIGN(16) : { -+ *(.rodata) -+ *(.rodata.*) -+ } -+ -+ .data ALIGN(16) : { -+ *(.data) -+ . = ALIGN(16); -+ __loader_parms_start = .; -+ KEEP(*(.loader_parms)); -+ __loader_parms_end = .; -+ ASSERT(__loader_parms_end - __loader_parms_start == 3 * 16 + 16, -+ "Data size must be equal to 'sizeof(struct stage3b_args)'"); -+ } -+ -+ . = STACK_ADDRESS; -+ __stack_start = .; -+ .stack : { -+ . = . + STACK_SIZE; -+ ASSERT(__stack_end - __stack_start == STACK_SIZE, -+ "Stack section doesn't conform to the described memory layout"); -+ } -+ __stack_end = .; -+ -+ /* List this explicitly as otherwise .note.gnu.build-id will be -+ * put at 0x0 */ -+ .notes : { -+ *(.note.*) -+ } -+ -+ /* Sections to be discarded */ -+ /DISCARD/ : { -+ *(.eh_frame) -+ } -+} -diff --git a/genprotimg/boot/stage3b_reloc.S b/genprotimg/boot/stage3b_reloc.S -new file mode 100644 -index 0000000..7b2242d ---- /dev/null -+++ b/genprotimg/boot/stage3b_reloc.S -@@ -0,0 +1,53 @@ -+/* -+ * Relocator code for stage 3b boot loader -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 "stage3b.h" -+#include "boot/sigp.h" -+ -+.macro MEMCPY dst,src,len -+ lgr %r0, \dst -+ lgr %r1, \len -+ lgr %r2, \src -+ lgr %r3, \len -+ -+20: mvcle %r0, %r2, 0 -+ jo 20b -+.endm -+ -+.org 0x0 -+.section .text.start -+.globl stage3b_reloc_start -+stage3b_reloc_start: -+ /* Might be called after a diag308 so better set -+ * architecture and addressing mode -+ */ -+ lhi %r1, 1 -+ sigp %r1, %r0, SIGP_SET_ARCHITECTURE -+ sam64 -+ -+.copy_stage3b: -+ /* Location of stage3b in memory */ -+ larl %r8, stage3b_start -+ -+ /* Destination for stage3b */ -+ lgfi %r9, STAGE3B_LOAD_ADDRESS -+ -+ /* Size of stage3b */ -+ lghi %r11, stage3b_end - stage3b_start -+ -+ /* Copy the stage3b loader to address STAGE3B_LOAD_ADDRESS */ -+ MEMCPY %r9, %r8, %r11 -+ -+ /* Branch to STAGE3B_ENTRY */ -+ lgfi %r9, STAGE3B_ENTRY -+ br %r9 -+stage3b_start: -+ .incbin "stage3b.bin" -+stage3b_end: -+.previous -diff --git a/genprotimg/man/Makefile b/genprotimg/man/Makefile -new file mode 100644 -index 0000000..e60bab4 ---- /dev/null -+++ b/genprotimg/man/Makefile -@@ -0,0 +1,12 @@ -+# Common definitions -+include ../../common.mak -+ -+all: -+ -+install: -+ $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 -+ $(INSTALL) -m 644 -c genprotimg.8 $(DESTDIR)$(MANDIR)/man8 -+ -+clean: -+ -+.PHONY: all install clean -diff --git a/genprotimg/man/genprotimg.8 b/genprotimg/man/genprotimg.8 -new file mode 100644 -index 0000000..597106e ---- /dev/null -+++ b/genprotimg/man/genprotimg.8 -@@ -0,0 +1,97 @@ -+.\" Copyright 2020 IBM Corp. -+.\" s390-tools is free software; you can redistribute it and/or modify -+.\" it under the terms of the MIT license. See LICENSE for details. -+.\" -+.TH GENPROTIMG 8 "March 2020" "s390-tools" -+.SH NAME -+genprotimg \- Create a protected virtualization image -+ -+.SH SYNOPSIS -+.SY -+.B genprotimg -+\fB\-k\fR \fIHOST_KEY_DOCUMENT\fR... -+\fB\-i\fR \fIVMLINUZ\fR -+[\fB\-r\fR \fIRAMDISK\fR] -+[\fB\-p\fR \fIPARMFILE\fR] -+\fB\-o\fR \fIOUTFILE\fR -+[\fIOPTION\fR]... -+.YS -+ -+.SH DESCRIPTION -+.PP -+Use \fBgenprotimg\fR to generate a single bootable image file with -+encrypted and integrity-protected parts. The command requires a kernel -+image, a host-key document, and an output file name. Optionally, -+specify an initial RAM filesystem, and a file containing the kernel -+parameters. Should special circumstances require it, you can -+optionally specify your own keys for the encryption by using the -+experimental options. In the resulting image file, a plain text boot -+loader, the encrypted components for kernel, initial RAM disk, kernel -+parameters, and the encrypted and integrity-protected header are -+concatenated. The header contains metadata necessary for running the -+guest in protected mode. -+.PP -+Use this image file as a kernel image for zipl or for a direct kernel -+boot using QEMU. -+ -+.SH OPTIONS -+.TP -+\fB\-h\fR, \fB\-\-help\fR -+Prints usage information, then exits. -+.TP -+\fB\-\-help-experimental\fR -+Prints experimental usage information, then exits. -+.TP -+\fB\-\-help-all\fR -+Prints all usage information, then exits. -+.TP -+\fB\-V\fR, \fB\-\-verbose\fR -+Provides more detailed output. -+.TP -+\fB\-k\fR, \fB\-\-host-key-document\fR=\fI\,HOST_KEY_DOCUMENT\/\fR -+Specifies a host-key document. At least one is required. Specify this -+option multiple times to enable the image to run on more than one -+host. -+.TP -+\fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR -+Specifies the output file. Required. -+.TP -+\fB\-i\fR, \fB\-\-image\fR=\fI\,VMLINUZ\/\fR -+Specifies the Linux kernel image file. Required. -+.TP -+\fB\-r\fR, \fB\-\-ramdisk\fR=\fI\,RAMDISK\/\fR -+Specifies the RAM disk image. Optional. -+.TP -+\fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR -+Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional. -+.TP -+\fB\-\-no-verify\fR -+Do not require the host-key documents to be valid. For testing -+purposes, do not use for a production image. Optional. -+.TP -+\fB\-v\fR, \fB\-\-version\fR -+Prints version information, then exits. -+ -+.SH EXAMPLE -+.PP -+Generate a protected virtualization image in -+\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, -+the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in -+\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR: -+.PP -+.Vb 1 -+.EX -+\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-o \fI\,/boot/vmlinuz.pv\/\fR -+.EE -+.Ve -+.PP -+ -+.SH NOTES -+.IP "1." 4 -+An ELF file cannot be used as a Linux kernel image. -+.IP "2." 4 -+Remember to re-run \fBzipl\fR after updating a protected -+virtualization image. -+ -+.SH SEE ALSO -+\&\fBzipl\fR\|(5), \fBqemu\fR\|(1) -diff --git a/genprotimg/samples/check_hostkeydoc b/genprotimg/samples/check_hostkeydoc -new file mode 100644 -index 0000000..2327bd3 ---- /dev/null -+++ b/genprotimg/samples/check_hostkeydoc -@@ -0,0 +1,276 @@ -+#!/bin/sh -+# -+# check_hostkeydoc - Verify an IBM Secure Execution host key document -+# -+# Sample script to verify that a host key document is genuine by -+# verifying the issuer, the validity date and the signature. -+# Optionally verify the full trust chain using a CA certficate. -+# -+# Revocation list checking not yet implemented. -+# -+# Sample invocation: -+# -+# ./check_hostkeydoc HKD1234.crt signing-key.crt -+# -+# Copyright IBM Corp. 2020 -+# -+# s390-tools is free software; you can redistribute it and/or modify -+# it under the terms of the MIT license. See LICENSE for details. -+ -+ -+# Allocate temporary files -+ISSUER_PUBKEY_FILE=$(mktemp) -+SIGNATURE_FILE=$(mktemp) -+BODY_FILE=$(mktemp) -+ISSUER_DN_FILE=$(mktemp) -+SUBJECT_DN_FILE=$(mktemp) -+DEF_ISSUER_DN_FILE=$(mktemp) -+CRL_SERIAL_FILE=$(mktemp) -+ -+# Cleanup on exit -+cleanup() -+{ -+ rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \ -+ $ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \ -+ $CRL_SERIAL_FILE -+} -+trap cleanup EXIT -+ -+# Enhanced error checking for bash -+if echo $SHELL | grep /bin/bash > /dev/null -+then -+ set -o pipefail -+ set -o nounset -+fi -+set -e -+ -+# Usage -+usage() -+{ -+cat <<-EOF -+Usage: `basename $1` host-key-doc signing-key-cert [-c CA-cert] [-r CRL] -+ -+Verify an IBM Secure Execution host key document against -+a signing key. -+ -+Note that in order to have the full trust chain verified -+it is necessary to provide the issueing CA's certificate. -+ -+EOF -+} -+ -+check_verify_chain() -+{ -+ # Verify certificate chain in case a CA certificate file/bundle -+ # was specified on the command line. -+ if [ $# = 1 ] -+ then -+ cat >&2 <<-EOF -+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -+No CA certificate specified! Skipping trust chain verification. -+Make sure that '$1' is a valid certificate. -+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -+EOF -+ else -+ openssl verify -crl_download -crl_check $2 && -+ openssl verify -crl_download -crl_check -untrusted $2 $1 || -+ exit 1 -+ fi -+} -+ -+extract_pubkey() -+{ -+ openssl x509 -in $1 -pubkey -noout > $2 -+} -+ -+extract_signature() -+{ -+ # Assuming that the last field is the signature -+ SIGOFFSET=$(openssl asn1parse -in $1 | tail -1 | cut -d : -f 1) -+ -+ openssl asn1parse -in $1 -out $2 -strparse $SIGOFFSET -noout -+} -+ -+extract_body() -+{ -+ # Assuming that the first field is the full cert body -+ SIGOFFSET=$(openssl asn1parse -in $1 | head -2 | tail -1 | cut -d : -f 1) -+ -+ openssl asn1parse -in $1 -out $2 -strparse $SIGOFFSET -noout -+} -+ -+verify_signature() -+{ -+ # Assuming that the signature algorith is SHA512 with RSA -+ openssl sha512 -verify $1 -signature $2 $3 -+} -+ -+canonical_dn() -+{ -+ OBJTYPE=$1 -+ OBJ=$2 -+ DNTYPE=$3 -+ OUTPUT=$4 -+ -+ openssl $OBJTYPE -in $OBJ -$DNTYPE -noout -nameopt multiline \ -+ | sort | grep -v $DNTYPE= > $OUTPUT -+} -+ -+default_issuer() -+{ -+ cat <<-EOF -+ commonName = International Business Machines Corporation -+ countryName = US -+ localityName = Poughkeepsie -+ organizationalUnitName = IBM Z Host Key Signing Service -+ organizationName = International Business Machines Corporation -+ stateOrProvinceName = New York -+EOF -+} -+ -+verify_issuer_files() -+{ -+ default_issuer > $DEF_ISSUER_DN_FILE -+ -+ if ! diff $ISSUER_DN_FILE $DEF_ISSUER_DN_FILE -+ then -+ echo Incorrect default issuer >&2 && exit 1 -+ fi -+ -+ if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE -+ then -+ echo Issuer verification OK -+ else -+ echo Issuer verification failed >&2 && exit 1 -+ fi -+} -+ -+cert_time() -+{ -+ DATE=$(openssl x509 -in $1 -$2 -noout | sed "s/^.*=//") -+ -+ date -d "$DATE" +%s -+} -+ -+crl_time() -+{ -+ DATE=$(openssl crl -in $1 -$2 -noout | sed "s/^.*=//") -+ -+ date -d "$DATE" +%s -+} -+ -+verify_dates() -+{ -+ START="$1" -+ END="$2" -+ MSG="${3:-Certificate}" -+ NOW=$(date +%s) -+ -+ if [ $START -le $NOW -a $NOW -le $END ] -+ then -+ echo "${MSG} dates are OK" -+ else -+ echo "${MSG} date verification failed" >&2 && exit 1 -+ fi -+} -+ -+crl_serials() -+{ -+ openssl crl -in $1 -text -noout | \ -+ grep "Serial Number" > $CRL_SERIAL_FILE -+} -+ -+check_serial() -+{ -+ CERT_SERIAL=$(openssl x509 -in $1 -noout -serial | cut -d = -f 2) -+ -+ grep -q $CERT_SERIAL $CRL_SERIAL_FILE -+} -+ -+check_file() -+{ -+ [ $# = 0 ] || -+ [ -e "$1" ] || -+ (echo "File '$1' not found" >&2 && exit 1) -+} -+ -+# check args -+CRL_FILE= -+CA_FILE= -+ -+args=$(getopt -qu "r:c:h" $*) -+if [ $? = 0 ] -+then -+ set -- $args -+ while [ $1 != "" ] -+ do -+ case $1 in -+ -r) CRL_FILE=$2; shift 2;; -+ -c) CA_FILE=$2; shift 2;; -+ -h) usage $0; exit 0;; -+ --) shift; break;; -+ esac -+ done -+else -+ usage $0 >&2 -+ exit 1 -+fi -+ -+if [ $# -ne 2 ] -+then -+ usage $0 >&2 -+ exit 1 -+fi -+ -+HKD_FILE=$1 -+HKSK_FILE=$2 -+ -+# Check whether all specified files exist -+check_file $HKD_FILE -+check_file $HKSK_FILE -+check_file $CA_FILE -+check_file $CRL_FILE -+ -+# Check trust chain -+check_verify_chain $HKSK_FILE $CA_FILE -+ -+# Verify host key document signature -+echo -n "Checking host key document signature: " -+extract_pubkey $HKSK_FILE $ISSUER_PUBKEY_FILE && -+extract_signature $HKD_FILE $SIGNATURE_FILE && -+extract_body $HKD_FILE $BODY_FILE && -+verify_signature $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE || -+exit 1 -+ -+# Verify the issuer -+canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE -+canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE -+verify_issuer_files -+ -+# Verify dates -+verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate) -+ -+# Check CRL if specified -+if [ -n "$CRL_FILE" ] -+then -+ echo -n "Checking CRL signature: " -+ extract_signature $CRL_FILE $SIGNATURE_FILE && -+ extract_body $CRL_FILE $BODY_FILE && -+ verify_signature $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE || -+ exit 1 -+ -+ echo -n "CRL " -+ canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE -+ canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE -+ verify_issuer_files -+ -+ verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL' -+ -+ crl_serials $CRL_FILE -+ check_serial $HKD_FILE && -+ echo "Certificate is revoked, do not use it anymore!" >&2 && -+ exit 1 -+fi -+ -+# We made it -+echo All checks reqested for \'$HKD_FILE\' were successful -diff --git a/genprotimg/src/Makefile b/genprotimg/src/Makefile -new file mode 100644 -index 0000000..1adeac3 ---- /dev/null -+++ b/genprotimg/src/Makefile -@@ -0,0 +1,101 @@ -+# Common definitions -+include ../../common.mak -+ -+bin_PROGRAM = genprotimg -+ -+PKGDATADIR ?= "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg" -+SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) -+TOP_SRCDIR := $(SRC_DIR)/../ -+ROOT_DIR = $(TOP_SRC_DIR)/../../ -+ZIPL_DIR = $(ROOT_DIR)/zipl -+LOADER_DIR = $(TOP_SRCDIR)/boot -+ -+INCLUDE_PATHS = "$(SRC_DIR)" "$(TOP_SRCDIR)" "$(ROOTDIR)/include" -+INCLUDE_PARMS = $(addprefix -I,$(INCLUDE_PATHS)) -+ -+WARNINGS := -Wall -Wextra -Wshadow \ -+ -Wcast-align -Wwrite-strings -Wmissing-prototypes \ -+ -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline \ -+ -Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \ -+ -Wpointer-arith -Werror \ -+ $(NULL) -+ -+$(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \ -+ pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \ -+ pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \ -+ pv/pv_opt_item.c \ -+ $(NULL) -+$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o) -+ -+ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \ -+ $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \ -+ $(WARNINGS) \ -+ $(NULL) -+ALL_CPPFLAGS += $(INCLUDE_PARMS) -+LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) -+ -+ -+ifneq ($(shell sh -c 'command -v pkg-config'),) -+GLIB2_CFLAGS := $(shell pkg-config --silence-errors --cflags glib-2.0) -+GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0) -+LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto) -+LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto) -+else -+GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -+GLIB2_LIBS := -lglib-2.0 -+LIBCRYPTO_CFLAGS := -+LIBCRYPTO_LIBS := -lcrypto -+endif -+ -+BUILD_TARGETS := skip-$(bin_PROGRAM) -+INSTALL_TARGETS := skip-$(bin_PROGRAM) -+ifneq (${HAVE_OPENSSL},0) -+ifneq (${HAVE_GLIB2},0) -+BUILD_TARGETS := $(bin_PROGRAM) -+INSTALL_TARGETS := install-$(bin_PROGRAM) -+endif -+endif -+ -+all: $(BUILD_TARGETS) -+ -+install: $(INSTALL_TARGETS) -+ -+$(bin_PROGRAM): $($(bin_PROGRAM)_OBJS) -+ -+skip-$(bin_PROGRAM): -+ echo " SKIP $(bin_PROGRAM) due to unresolved dependencies" -+ -+install-$(bin_PROGRAM): $(bin_PROGRAM) -+ $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) -+ $(INSTALL) -c $^ $(DESTDIR)$(USRBINDIR) -+ -+clean: -+ $(RM) -f $($(bin_PROGRAM)_OBJS) $(bin_PROGRAM) .check-dep-$(bin_PROGRAM) .detect-openssl.dep.c -+ -+.PHONY: all install clean skip-$(bin_PROGRAM) install-$(bin_PROGRAM) -+ -+$($(bin_PROGRAM)_OBJS): .check-dep-$(bin_PROGRAM) -+ -+.detect-openssl.dep.c: -+ echo "#include " > $@ -+ echo "#if OPENSSL_VERSION_NUMBER < 0x10100000L" >> $@ -+ echo " #error openssl version 1.1.0 is required" >> $@ -+ echo "#endif" >> $@ -+ echo "static void __attribute__((unused)) test(void) {" >> $@ -+ echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@ -+ echo " EVP_MD_CTX_free(ctx);" >> $@ -+ echo "}" >> $@ -+ -+.check-dep-$(bin_PROGRAM): .detect-openssl.dep.c -+ $(call check_dep, \ -+ "$(bin_PROGRAM)", \ -+ "glib.h", \ -+ "glib2-devel / libglib2.0-dev", \ -+ "HAVE_GLIB2=0") -+ $(call check_dep, \ -+ "$(bin_PROGRAM)", \ -+ $^, \ -+ "openssl-devel / libssl-dev version >= 1.1.0", \ -+ "HAVE_OPENSSL=0", \ -+ "-I.") -+ touch $@ -diff --git a/genprotimg/src/common.h b/genprotimg/src/common.h -new file mode 100644 -index 0000000..401d690 ---- /dev/null -+++ b/genprotimg/src/common.h -@@ -0,0 +1,35 @@ -+#ifndef COMMON_H -+#define COMMON_H -+ -+#define GETTEXT_PACKAGE "genprotimg" -+#include -+#include -+ -+#include "boot/linux_layout.h" -+#include "boot/s390.h" -+#include "lib/zt_common.h" -+ -+static const gchar tool_name[] = "genprotimg"; -+static const gchar copyright_notice[] = "Copyright IBM Corp. 2020"; -+ -+/* default values */ -+#define GENPROTIMG_STAGE3A_PATH (STRINGIFY(PKGDATADIR) "/stage3a.bin") -+#define GENPROTIMG_STAGE3B_PATH (STRINGIFY(PKGDATADIR) "/stage3b_reloc.bin") -+ -+#define DEFAULT_INITIAL_PSW_ADDR IMAGE_ENTRY -+#define DEFAULT_INITIAL_PSW_MASK (PSW_MASK_EA | PSW_MASK_BA) -+ -+#define DO_PRAGMA(x) _Pragma(#x) -+ -+# ifdef __clang__ -+# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ -+ DO_PRAGMA(clang diagnostic push) \ -+ DO_PRAGMA(clang diagnostic ignored "-Wunused-function") \ -+ G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) \ -+ DO_PRAGMA(clang diagnostic pop) -+# else -+# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ -+ G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) -+# endif -+ -+#endif -diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c -new file mode 100644 -index 0000000..0d82550 ---- /dev/null -+++ b/genprotimg/src/genprotimg.c -@@ -0,0 +1,181 @@ -+/* -+ * genprotimg - build relocatable secure images -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "common.h" -+#include "pv/pv_args.h" -+#include "pv/pv_image.h" -+ -+enum { -+ LOG_LEVEL_CRITICAL = 0, -+ LOG_LEVEL_INFO = 1, -+ LOG_LEVEL_DEBUG = 2, -+}; -+ -+static gint log_level = LOG_LEVEL_CRITICAL; -+static gchar *tmp_dir; -+ -+static void rmdir_recursive(gchar *dir_path, GError **err) -+{ -+ const gchar *file = NULL; -+ g_autoptr(GDir) d = NULL; -+ -+ if (!dir_path) -+ return; -+ -+ d = g_dir_open(dir_path, 0, err); -+ if (!d) { -+ g_set_error(err, G_FILE_ERROR, -+ (gint)g_file_error_from_errno(errno), -+ _("Failed to open directory '%s': %s"), dir_path, -+ g_strerror(errno)); -+ return; -+ } -+ -+ while ((file = g_dir_read_name(d)) != NULL) { -+ g_autofree gchar *file_path = -+ g_build_filename(dir_path, file, NULL); -+ /* ignore error */ -+ (void)g_unlink(file_path); -+ } -+ -+ if (g_rmdir(dir_path) != 0) { -+ g_set_error(err, G_FILE_ERROR, -+ (gint)g_file_error_from_errno(errno), -+ _("Failed to remove directory '%s': %s"), dir_path, -+ g_strerror(errno)); -+ return; -+ } -+} -+ -+static void sig_term_handler(int signal G_GNUC_UNUSED) -+{ -+ rmdir_recursive(tmp_dir, NULL); -+ exit(EXIT_FAILURE); -+} -+ -+static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, -+ GLogLevelFlags level, const gchar *message, -+ gpointer user_data G_GNUC_UNUSED) -+{ -+ const gchar *prefix = ""; -+ -+ /* filter out messages depending on debugging level */ -+ if ((level & G_LOG_LEVEL_DEBUG) && log_level < LOG_LEVEL_DEBUG) -+ return; -+ -+ if ((level & G_LOG_LEVEL_INFO) && log_level < LOG_LEVEL_INFO) -+ return; -+ -+ if (level & G_LOG_LEVEL_WARNING) -+ prefix = "WARNING: "; -+ -+ if (level & G_LOG_LEVEL_ERROR) -+ prefix = "ERROR: "; -+ -+ if (level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR)) -+ g_printerr("%s%s\n", prefix, message); -+ else -+ g_print("%s%s\n", prefix, message); -+} -+ -+static void setup_prgname(const gchar *name) -+{ -+ g_set_prgname(name); -+ g_set_application_name(_(name)); -+} -+ -+static void setup_handler(const gint *signals, const gsize signals_n) -+{ -+ /* set up logging handler */ -+ g_log_set_handler(NULL, -+ G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | -+ G_LOG_FLAG_RECURSION, -+ log_handler_cb, NULL); -+ -+ /* set signal handler */ -+ for (gsize i = 0; i < signals_n; i++) -+ signal(signals[i], sig_term_handler); -+} -+ -+static void remove_signal_handler(const gint *signals, const gsize signals_n) -+{ -+ for (gsize i = 0; i < signals_n; i++) -+ signal(signals[i], SIG_DFL); -+} -+ -+gint main(gint argc, gchar *argv[]) -+{ -+ g_autoptr(PvArgs) args = pv_args_new(); -+ gint signals[] = { SIGINT, SIGTERM }; -+ g_autoptr(PvImage) img = NULL; -+ gint ret = EXIT_FAILURE; -+ GError *err = NULL; -+ -+ setlocale(LC_CTYPE, ""); -+ setup_prgname(tool_name); -+ setup_handler(signals, G_N_ELEMENTS(signals)); -+ -+ if (pv_args_parse_options(args, &argc, &argv, &err) < 0) -+ goto error; -+ -+ /* set new log level */ -+ log_level = args->log_level; -+ -+ /* if the user has not specified a temporary directory let's -+ * create one -+ */ -+ if (!args->tmp_dir) { -+ tmp_dir = g_dir_make_tmp("genprotimg-XXXXXX", &err); -+ if (!tmp_dir) -+ goto error; -+ args->tmp_dir = g_strdup(tmp_dir); -+ } -+ -+ /* allocate and initialize ``pv_img`` data structure */ -+ img = pv_img_new(args, GENPROTIMG_STAGE3A_PATH, &err); -+ if (!img) -+ goto error; -+ -+ /* add user components: `args->comps` must be sorted by the -+ * component type => by memory address -+ */ -+ for (GSList *iterator = args->comps; iterator; iterator = iterator->next) { -+ const PvArg *arg = iterator->data; -+ -+ if (pv_img_add_component(img, arg, &err) < 0) -+ goto error; -+ } -+ -+ if (pv_img_finalize(img, GENPROTIMG_STAGE3B_PATH, &err) < 0) -+ goto error; -+ -+ if (pv_img_write(img, args->output_path, &err) < 0) -+ goto error; -+ -+ ret = EXIT_SUCCESS; -+ -+error: -+ if (err) { -+ fputs(err->message, stderr); -+ fputc('\n', stderr); -+ g_clear_error(&err); -+ } -+ rmdir_recursive(tmp_dir, NULL); -+ remove_signal_handler(signals, G_N_ELEMENTS(signals)); -+ g_free(tmp_dir); -+ exit(ret); -+} -diff --git a/genprotimg/src/include/pv_crypto_def.h b/genprotimg/src/include/pv_crypto_def.h -new file mode 100644 -index 0000000..ddb8652 ---- /dev/null -+++ b/genprotimg/src/include/pv_crypto_def.h -@@ -0,0 +1,25 @@ -+/* -+ * PV cryptography related definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_CRYPTO_DEF_H -+#define PV_CRYPTO_DEF_H -+ -+#include -+ -+#include "lib/zt_common.h" -+ -+union ecdh_pub_key { -+ struct { -+ uint8_t x[80]; -+ uint8_t y[80]; -+ }; -+ uint8_t data[160]; -+} __packed; -+ -+#endif -diff --git a/genprotimg/src/include/pv_hdr_def.h b/genprotimg/src/include/pv_hdr_def.h -new file mode 100644 -index 0000000..850ed26 ---- /dev/null -+++ b/genprotimg/src/include/pv_hdr_def.h -@@ -0,0 +1,84 @@ -+/* -+ * PV header definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_HDR_DEF_H -+#define PV_HDR_DEF_H -+ -+#include -+ -+#include "boot/s390.h" -+#include "lib/zt_common.h" -+#include "utils/crypto.h" -+ -+#include "pv_crypto_def.h" -+ -+/* Magic number which is used to identify the file containing the PV -+ * header -+ */ -+#define PV_MAGIC_NUMBER 0x49424d5365634578ULL -+#define PV_VERSION_1 0x00000100U -+ -+/* prevent Ultravisor decryption during unpack operation */ -+#define PV_CFLAG_NO_DECRYPTION 0x10000000ULL -+ -+/* maxima for the PV version 1 */ -+#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE -+#define PV_V1_PV_HDR_MAX_SIZE (2 * PAGE_SIZE) -+ -+typedef struct pv_hdr_key_slot { -+ uint8_t digest_key[SHA256_DIGEST_LENGTH]; -+ uint8_t wrapped_key[32]; -+ uint8_t tag[AES_256_GCM_TAG_SIZE]; -+} __packed PvHdrKeySlot; -+ -+typedef struct pv_hdr_opt_item { -+ uint32_t otype; -+ uint8_t ibk[32]; -+ uint8_t data[]; -+} __packed PvHdrOptItem; -+ -+/* integrity protected data (by GCM tag), but non-encrypted */ -+struct pv_hdr_head { -+ uint64_t magic; -+ uint32_t version; -+ uint32_t phs; -+ uint8_t iv[AES_256_GCM_IV_SIZE]; -+ uint32_t res1; -+ uint64_t nks; -+ uint64_t sea; -+ uint64_t nep; -+ uint64_t pcf; -+ union ecdh_pub_key cust_pub_key; -+ uint8_t pld[SHA512_DIGEST_LENGTH]; -+ uint8_t ald[SHA512_DIGEST_LENGTH]; -+ uint8_t tld[SHA512_DIGEST_LENGTH]; -+} __packed; -+ -+/* Must not have any padding */ -+struct pv_hdr_encrypted { -+ uint8_t cust_comm_key[32]; -+ uint8_t img_enc_key_1[AES_256_XTS_KEY_SIZE / 2]; -+ uint8_t img_enc_key_2[AES_256_XTS_KEY_SIZE / 2]; -+ struct psw_t psw; -+ uint64_t scf; -+ uint32_t noi; -+ uint32_t res2; -+}; -+STATIC_ASSERT(sizeof(struct pv_hdr_encrypted) == -+ 32 + 32 + 32 + sizeof(struct psw_t) + 8 + 4 + 4) -+ -+typedef struct pv_hdr { -+ struct pv_hdr_head head; -+ struct pv_hdr_key_slot *slots; -+ struct pv_hdr_encrypted *encrypted; -+ struct pv_hdr_opt_item **optional_items; -+ uint8_t tag[AES_256_GCM_TAG_SIZE]; -+} PvHdr; -+ -+#endif -diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c -new file mode 100644 -index 0000000..9fb7298 ---- /dev/null -+++ b/genprotimg/src/pv/pv_args.c -@@ -0,0 +1,405 @@ -+/* -+ * PV arguments related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+ -+#include "common.h" -+ -+#include "pv_comp.h" -+#include "pv_error.h" -+#include "pv_args.h" -+ -+static gchar summary[] = -+ "Use genprotimg to create a protected virtualization kernel image file,\n" -+ "which can be loaded using zipl or QEMU."; -+ -+static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2) -+{ -+ g_assert(arg_1); -+ g_assert(arg_2); -+ -+ PvComponentType a = ((PvArg *)arg_1)->type; -+ PvComponentType b = ((PvArg *)arg_2)->type; -+ -+ if (a < b) -+ return -1; -+ if (a == b) -+ return 0; -+ return 1; -+} -+ -+static gint pv_arg_has_type(gconstpointer arg, gconstpointer type) -+{ -+ const PvArg *c = arg; -+ const PvComponentType *t = type; -+ -+ g_assert(arg); -+ -+ if (c->type == *t) -+ return 0; -+ if (c->type < *t) -+ return -1; -+ return 1; -+} -+ -+static gint pv_args_set_defaults(PvArgs *args, GError **err G_GNUC_UNUSED) -+{ -+ if (!args->psw_addr) -+ args->psw_addr = -+ g_strdup_printf("0x%lx", DEFAULT_INITIAL_PSW_ADDR); -+ -+ return 0; -+} -+ -+static gint pv_args_validate_options(PvArgs *args, GError **err) -+{ -+ PvComponentType KERNEL = PV_COMP_TYPE_KERNEL; -+ -+ if (args->unused_values->len > 0) { -+ g_autofree gchar *unused = NULL; -+ -+ for (gsize i = args->unused_values->len; i > 0; i--) { -+ g_autofree gchar *tmp = unused; -+ -+ unused = g_strjoin(" ", g_ptr_array_index(args->unused_values, i - 1), -+ tmp, -+ NULL); -+ } -+ -+ g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_INVALID_ARGUMENT, -+ _("Unrecognized arguments: '%s'.\nUse 'genprotimg --help' for more information"), -+ unused); -+ return -1; -+ } -+ -+ if (!args->output_path) { -+ g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -+ _("Option '--output' is required.\nUse 'genprotimg --help' for more information")); -+ return -1; -+ } -+ -+ if (!g_slist_find_custom(args->comps, &KERNEL, pv_arg_has_type)) { -+ g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -+ _("Option '--image' is required.\nUse 'genprotimg --help' for more information")); -+ return -1; -+ } -+ -+ if (!args->host_keys || g_strv_length(args->host_keys) == 0) { -+ g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -+ _("Option '--host-key-document' is required.\nUse 'genprotimg --help' for more information")); -+ return -1; -+ } -+ -+ if (!args->no_verify) { -+ g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -+ _("Use the option '--no-verify' as the verification support is not available yet.")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static gboolean cb_add_component(const gchar *option, const gchar *value, -+ PvArgs *args, GError **err) -+{ -+ PvArg *comp = NULL; -+ gint type = -1; -+ -+ if (g_str_equal(option, "-i") || g_str_equal(option, "--image")) -+ type = PV_COMP_TYPE_KERNEL; -+ if (g_str_equal(option, "-r") || g_str_equal(option, "--ramdisk")) -+ type = PV_COMP_TYPE_INITRD; -+ if (g_str_equal(option, "-p") || g_str_equal(option, "--parmfile")) -+ type = PV_COMP_TYPE_CMDLINE; -+ -+ if (type < 0) { -+ g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, -+ _("Invalid option '%s': "), option); -+ return FALSE; -+ } -+ -+ if (g_slist_find_custom(args->comps, &type, pv_arg_has_type)) { -+ g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, -+ _("Multiple values for option '%s'"), option); -+ return FALSE; -+ } -+ -+ comp = pv_arg_new((PvComponentType)type, value); -+ args->comps = g_slist_insert_sorted(args->comps, comp, pv_arg_compare); -+ return TRUE; -+} -+ -+static gboolean cb_set_string_option(const gchar *option, const gchar *value, -+ PvArgs *args, GError **err) -+{ -+ gchar **args_option = NULL; -+ -+ if (g_str_equal(option, "-o") || g_str_equal(option, "--output")) -+ args_option = &args->output_path; -+ if (g_str_equal(option, "--x-comp-key")) -+ args_option = &args->xts_key_path; -+ if (g_str_equal(option, "--x-comm-key")) -+ args_option = &args->cust_comm_key_path; -+ if (g_str_equal(option, "--x-header-key")) -+ args_option = &args->cust_root_key_path; -+ if (g_str_equal(option, "--x-pcf")) -+ args_option = &args->pcf; -+ if (g_str_equal(option, "--x-psw")) -+ args_option = &args->psw_addr; -+ if (g_str_equal(option, "--x-scf")) -+ args_option = &args->scf; -+ -+ if (!args_option) { -+ g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, -+ _("Invalid option '%s': "), option); -+ return FALSE; -+ } -+ -+ if (*args_option) { -+ g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, -+ _("Multiple values for option '%s'"), option); -+ return FALSE; -+ } -+ -+ *args_option = g_strdup(value); -+ return TRUE; -+} -+ -+static gboolean cb_set_log_level(const gchar *option G_GNUC_UNUSED, -+ const gchar *value G_GNUC_UNUSED, PvArgs *args, -+ GError **err G_GNUC_UNUSED) -+{ -+ args->log_level++; -+ return TRUE; -+} -+ -+static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED, -+ const gchar *value, PvArgs *args, -+ GError **err G_GNUC_UNUSED) -+{ -+ g_ptr_array_add(args->unused_values, g_strdup(value)); -+ return TRUE; -+} -+ -+#define INDENT " " -+ -+gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], -+ GError **err) -+{ -+ g_autoptr(GOptionContext) context = NULL; -+ gboolean print_version = FALSE; -+ GOptionGroup *group, *x_group; -+ -+ g_autofree gchar *psw_desc = g_strdup_printf( -+ _("Load from the specified hexadecimal ADDRESS.\n" INDENT -+ "Optional; default: '0x%lx'."), -+ DEFAULT_INITIAL_PSW_ADDR); -+ GOptionEntry entries[] = { -+ { .long_name = "host-key-document", -+ .short_name = 'k', -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_FILENAME_ARRAY, -+ .arg_data = &args->host_keys, -+ .description = -+ _("FILE specifies a host-key document. At least\n" INDENT -+ "one is required."), -+ .arg_description = _("FILE") }, -+ { .long_name = "output", -+ .short_name = 'o', -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _("Set FILE as the output file."), -+ .arg_description = _("FILE") }, -+ { .long_name = "image", -+ .short_name = 'i', -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_add_component, -+ .description = _("Use IMAGE as the Linux kernel image."), -+ .arg_description = _("IMAGE") }, -+ { .long_name = "ramdisk", -+ .short_name = 'r', -+ .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_add_component, -+ .description = _("Use RAMDISK as the initial RAM disk\n" INDENT -+ "(optional)."), -+ .arg_description = _("RAMDISK") }, -+ { .long_name = "parmfile", -+ .short_name = 'p', -+ .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_add_component, -+ .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT -+ "(optional)."), -+ .arg_description = _("PARMFILE") }, -+ { .long_name = "no-verify", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_NONE, -+ .arg_data = &args->no_verify, -+ .description = _("Disable the host-key document verification\n" INDENT -+ "(optional)."), -+ .arg_description = NULL }, -+ { .long_name = "verbose", -+ .short_name = 'V', -+ .flags = G_OPTION_FLAG_NO_ARG, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_log_level, -+ .description = _("Provide more detailed output (optional)."), -+ .arg_description = NULL }, -+ { .long_name = "version", -+ .short_name = 'v', -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_NONE, -+ .arg_data = &print_version, -+ .description = _("Print the version and exit."), -+ .arg_description = NULL }, -+ { .long_name = G_OPTION_REMAINING, -+ .short_name = 0, -+ .flags = 0, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_remaining_values, -+ .description = NULL, -+ .arg_description = NULL }, -+ { 0 }, -+ }; -+ -+ GOptionEntry x_entries[] = { -+ { .long_name = "x-comm-key", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _( -+ "Use FILE as the customer communication key.\n" INDENT -+ "Optional; default: auto-generated."), -+ .arg_description = _("FILE") }, -+ { .long_name = "x-comp-key", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _( -+ "Use FILE as the AES 256-bit XTS key\n" INDENT -+ "that is used for the component encryption.\n" INDENT -+ "Optional; default: auto-generated."), -+ .arg_description = _("FILE") }, -+ { .long_name = "x-header-key", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _( -+ "Use FILE as the AES 256-bit GCM header key\n" INDENT -+ "that protects the PV header.\n" INDENT -+ "Optional; default: auto-generated."), -+ .arg_description = _("FILE") }, -+ { .long_name = "x-pcf", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = -+ _("Specify the plaintext control flags\n" INDENT -+ "as a hexadecimal value.\n" INDENT -+ "Optional; default: '0x0'."), -+ .arg_description = _("VALUE") }, -+ { .long_name = "x-psw", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = psw_desc, -+ .arg_description = _("ADDRESS") }, -+ { .long_name = "x-scf", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _("Specify the secret control flags\n" INDENT -+ "as a hexadecimal value.\n" INDENT -+ "Optional; default: '0x0'."), -+ .arg_description = _("VALUE") }, -+ { 0 }, -+ }; -+ -+ context = g_option_context_new( -+ _("- Create a protected virtualization image")); -+ g_option_context_set_summary(context, _(summary)); -+ group = g_option_group_new(GETTEXT_PACKAGE, _("Application Options:"), -+ _("Show help options"), args, NULL); -+ g_option_group_add_entries(group, entries); -+ g_option_context_set_main_group(context, group); -+ -+ x_group = g_option_group_new("experimental", _("Experimental Options:"), -+ _("Show experimental options"), args, NULL); -+ g_option_group_add_entries(x_group, x_entries); -+ g_option_context_add_group(context, x_group); -+ if (!g_option_context_parse(context, argc, argv, err)) -+ return -1; -+ -+ if (print_version) { -+ g_printf(_("%s version %s\n"), tool_name, RELEASE_STRING); -+ g_printf("%s\n", copyright_notice); -+ exit(EXIT_SUCCESS); -+ } -+ -+ if (pv_args_set_defaults(args, err) < 0) -+ return -1; -+ -+ return pv_args_validate_options(args, err); -+} -+ -+PvArgs *pv_args_new(void) -+{ -+ g_autoptr(PvArgs) args = g_new0(PvArgs, 1); -+ -+ args->unused_values = g_ptr_array_new_with_free_func(g_free); -+ return g_steal_pointer(&args); -+} -+ -+void pv_args_free(PvArgs *args) -+{ -+ if (!args) -+ return; -+ -+ g_free(args->pcf); -+ g_free(args->scf); -+ g_free(args->psw_addr); -+ g_free(args->cust_root_key_path); -+ g_free(args->cust_comm_key_path); -+ g_free(args->gcm_iv_path); -+ g_strfreev(args->host_keys); -+ g_free(args->xts_key_path); -+ g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free); -+ g_ptr_array_free(args->unused_values, TRUE); -+ g_free(args->output_path); -+ g_free(args->tmp_dir); -+ g_free(args); -+} -+ -+void pv_arg_free(PvArg *arg) -+{ -+ if (!arg) -+ return; -+ -+ g_free(arg->path); -+ g_free(arg); -+} -+PvArg *pv_arg_new(PvComponentType type, const gchar *path) -+{ -+ g_autoptr(PvArg) ret = g_new0(struct pv_arg, 1); -+ -+ ret->type = type; -+ ret->path = g_strdup(path); -+ return g_steal_pointer(&ret); -+} -diff --git a/genprotimg/src/pv/pv_args.h b/genprotimg/src/pv/pv_args.h -new file mode 100644 -index 0000000..f17e7b8 ---- /dev/null -+++ b/genprotimg/src/pv/pv_args.h -@@ -0,0 +1,53 @@ -+/* -+ * PV arguments related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_ARGS_H -+#define PV_ARGS_H -+ -+#include -+ -+#include "pv_comp.h" -+ -+typedef struct pv_arg { -+ PvComponentType type; -+ gchar *path; -+} PvArg; -+ -+PvArg *pv_arg_new(PvComponentType type, const gchar *path); -+void pv_arg_free(PvArg *arg); -+ -+typedef struct { -+ gint log_level; -+ gint no_verify; -+ gchar *pcf; -+ gchar *scf; -+ gchar *psw_addr; /* PSW address which will be used for the start of -+ * the actual component (e.g. Linux kernel) -+ */ -+ gchar *cust_root_key_path; -+ gchar *cust_comm_key_path; -+ gchar *gcm_iv_path; -+ gchar **host_keys; -+ gchar *xts_key_path; -+ GSList *comps; -+ gchar *output_path; -+ gchar *tmp_dir; -+ GPtrArray *unused_values; -+} PvArgs; -+ -+PvArgs *pv_args_new(void); -+void pv_args_free(PvArgs *args); -+ -+gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], -+ GError **err); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArg, pv_arg_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArgs, pv_args_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_comp.c b/genprotimg/src/pv/pv_comp.c -new file mode 100644 -index 0000000..1f64eea ---- /dev/null -+++ b/genprotimg/src/pv/pv_comp.c -@@ -0,0 +1,446 @@ -+/* -+ * PV component related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "common.h" -+#include "utils/align.h" -+#include "utils/buffer.h" -+#include "utils/crypto.h" -+#include "utils/file_utils.h" -+ -+#include "pv_comp.h" -+#include "pv_error.h" -+ -+static void comp_file_free(CompFile *comp) -+{ -+ if (!comp) -+ return; -+ -+ g_free(comp->path); -+ g_free(comp); -+} -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompFile, comp_file_free) -+ -+static PvComponent *pv_component_new(PvComponentType type, gsize size, -+ PvComponentDataType d_type, void **data, -+ GError **err) -+{ -+ g_autoptr(PvComponent) ret = g_new0(PvComponent, 1); -+ -+ g_assert(type >= 0 && type <= UINT16_MAX); -+ -+ ret->type = (int)type; -+ ret->d_type = (int)d_type; -+ ret->data = g_steal_pointer(data); -+ ret->orig_size = size; -+ -+ if (generate_tweak(&ret->tweak, (uint16_t)type, err) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, -+ GError **err) -+{ -+ g_autoptr(CompFile) file = g_new0(CompFile, 1); -+ gsize size; -+ gint rc; -+ -+ g_assert(path != NULL); -+ -+ rc = file_size(path, &size, err); -+ if (rc < 0) -+ return NULL; -+ -+ file->path = g_strdup(path); -+ file->size = size; -+ return pv_component_new(type, size, DATA_FILE, (void **)&file, err); -+} -+ -+PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf, -+ GError **err) -+{ -+ g_assert(buf); -+ -+ g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE); -+ return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf, -+ err); -+} -+ -+void pv_component_free(PvComponent *component) -+{ -+ if (!component) -+ return; -+ -+ switch ((PvComponentDataType)component->d_type) { -+ case DATA_BUFFER: -+ buffer_clear(&component->buf); -+ break; -+ case DATA_FILE: -+ comp_file_free(component->file); -+ break; -+ } -+ -+ g_free(component); -+} -+ -+gint pv_component_type(const PvComponent *component) -+{ -+ return component->type; -+} -+ -+const gchar *pv_component_name(const PvComponent *component) -+{ -+ gint type = pv_component_type(component); -+ -+ switch ((PvComponentType)type) { -+ case PV_COMP_TYPE_KERNEL: -+ return "kernel"; -+ case PV_COMP_TYPE_INITRD: -+ return "ramdisk"; -+ case PV_COMP_TYPE_CMDLINE: -+ return "parmline"; -+ case PV_COMP_TYPE_STAGE3B: -+ return "stage3b"; -+ } -+ -+ g_assert_not_reached(); -+} -+ -+uint64_t pv_component_size(const PvComponent *component) -+{ -+ switch ((PvComponentDataType)component->d_type) { -+ case DATA_BUFFER: -+ return component->buf->size; -+ case DATA_FILE: -+ return component->file->size; -+ } -+ -+ g_assert_not_reached(); -+} -+ -+uint64_t pv_component_get_src_addr(const PvComponent *component) -+{ -+ return component->src_addr; -+} -+ -+uint64_t pv_component_get_orig_size(const PvComponent *component) -+{ -+ return component->orig_size; -+} -+ -+uint64_t pv_component_get_tweak_prefix(const PvComponent *component) -+{ -+ return GUINT64_FROM_BE(component->tweak.cmp_idx.data); -+} -+ -+gboolean pv_component_is_stage3b(const PvComponent *component) -+{ -+ return pv_component_type(component) == PV_COMP_TYPE_STAGE3B; -+} -+ -+gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path, -+ void *opaque, GError **err) -+{ -+ struct cipher_parms *parms = opaque; -+ -+ switch ((PvComponentDataType)component->d_type) { -+ case DATA_BUFFER: { -+ g_autoptr(Buffer) enc_buf = NULL; -+ -+ if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) { -+ g_autoptr(Buffer) new = NULL; -+ -+ /* create a page aligned copy */ -+ new = buffer_dup(component->buf, TRUE); -+ buffer_clear(&component->buf); -+ component->buf = g_steal_pointer(&new); -+ } -+ enc_buf = encrypt_buf(parms, component->buf, err); -+ if (!enc_buf) -+ return -1; -+ -+ buffer_clear(&component->buf); -+ component->buf = g_steal_pointer(&enc_buf); -+ return 0; -+ } -+ case DATA_FILE: { -+ const gchar *comp_name = pv_component_name(component); -+ gchar *path_in = component->file->path; -+ g_autofree gchar *path_out = NULL; -+ gsize orig_size; -+ gsize prep_size; -+ -+ g_assert(path_in); -+ -+ path_out = g_build_filename(tmp_path, comp_name, NULL); -+ if (encrypt_file(parms, path_in, path_out, &orig_size, -+ &prep_size, err) < 0) -+ return -1; -+ -+ if (component->orig_size != orig_size) { -+ g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, -+ _("File has changed during the preparation '%s'"), -+ path_out); -+ return -1; -+ } -+ -+ g_free(component->file->path); -+ component->file->size = prep_size; -+ component->file->path = g_steal_pointer(&path_out); -+ return 0; -+ } -+ } -+ -+ g_assert_not_reached(); -+} -+ -+/* Page align the size of the component */ -+gint pv_component_align(PvComponent *component, const gchar *tmp_path, -+ void *opaque G_GNUC_UNUSED, GError **err) -+{ -+ if (IS_PAGE_ALIGNED(pv_component_size(component))) -+ return 0; -+ -+ switch (component->d_type) { -+ case DATA_BUFFER: { -+ g_autoptr(Buffer) buf = NULL; -+ -+ buf = buffer_dup(component->buf, TRUE); -+ buffer_clear(&component->buf); -+ component->buf = g_steal_pointer(&buf); -+ return 0; -+ } break; -+ case DATA_FILE: { -+ const gchar *comp_name = pv_component_name(component); -+ g_autofree gchar *path_out = -+ g_build_filename(tmp_path, comp_name, NULL); -+ gchar *path_in = component->file->path; -+ gsize size_out; -+ -+ if (pad_file_right(path_out, path_in, &size_out, PAGE_SIZE, -+ err) < 0) -+ return -1; -+ -+ g_free(component->file->path); -+ component->file->path = g_steal_pointer(&path_out); -+ component->file->size = size_out; -+ return 0; -+ } break; -+ } -+ -+ g_assert_not_reached(); -+} -+ -+/* Convert uint64_t address to byte array */ -+static void uint64_to_uint8_buf(uint8_t dst[8], uint64_t addr) -+{ -+ uint8_t *p = (uint8_t *)&addr; -+ -+ g_assert(dst); -+ -+ for (gint i = 0; i < 8; i++) { -+ /* cppcheck-suppress objectIndex */ -+ dst[i] = p[i]; -+ } -+} -+ -+int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err) -+{ -+ uint64_t addr = pv_component_get_src_addr(comp); -+ uint64_t size = pv_component_size(comp); -+ uint64_t cur = addr; -+ int64_t nep = 0; -+ -+ g_assert(IS_PAGE_ALIGNED(size) && size != 0); -+ -+ do { -+ uint64_t cur_be = GUINT64_TO_BE(cur); -+ uint8_t addr_buf[8]; -+ -+ uint64_to_uint8_buf(addr_buf, cur_be); -+ -+ if (EVP_DigestUpdate(ctx, addr_buf, sizeof(addr_buf)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestUpdate failed")); -+ return -1; -+ } -+ -+ cur += PAGE_SIZE; -+ nep++; -+ } while (cur < addr + size); -+ -+ return nep; -+} -+ -+int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err) -+{ -+ uint64_t size = pv_component_size(comp); -+ int64_t nep = 0; -+ -+ g_assert(IS_PAGE_ALIGNED(size) && size != 0); -+ -+ switch (comp->d_type) { -+ case DATA_BUFFER: { -+ const Buffer *buf = comp->buf; -+ -+ g_assert(buf->size <= INT64_MAX); -+ g_assert(buf->size == size); -+ -+ if (EVP_DigestUpdate(ctx, buf->data, buf->size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestUpdate failed")); -+ return -1; -+ } -+ -+ nep = (int64_t)(buf->size / PAGE_SIZE); -+ break; -+ } -+ case DATA_FILE: { -+ const gchar *in_path = comp->file->path; -+ guchar in_buf[PAGE_SIZE]; -+ gsize num_bytes_read_total = 0; -+ gsize num_bytes_read = 0; -+ FILE *f_in; -+ -+ f_in = file_open(in_path, "rb", err); -+ if (!f_in) -+ return -1; -+ -+ do { -+ /* Read data in blocks. Update the digest -+ * context each read. -+ */ -+ if (file_read(f_in, in_buf, sizeof(*in_buf), -+ sizeof(in_buf), &num_bytes_read, -+ err) < 0) { -+ fclose(f_in); -+ return -1; -+ } -+ num_bytes_read_total += num_bytes_read; -+ -+ if (EVP_DigestUpdate(ctx, in_buf, sizeof(in_buf)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestUpdate failed")); -+ fclose(f_in); -+ return -1; -+ } -+ -+ nep++; -+ } while (num_bytes_read_total < pv_component_size(comp) && -+ num_bytes_read != 0); -+ -+ if (num_bytes_read_total != pv_component_size(comp)) { -+ g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, -+ _("'%s' has changed during the preparation"), -+ in_path); -+ fclose(f_in); -+ return -1; -+ } -+ fclose(f_in); -+ break; -+ } -+ default: -+ g_assert_not_reached(); -+ } -+ -+ return nep; -+} -+ -+int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err) -+{ -+ uint64_t size = pv_component_size(comp); -+ const union tweak *tweak = &comp->tweak; -+ g_autoptr(BIGNUM) tweak_num = NULL; -+ int64_t nep = 0; -+ -+ g_assert(IS_PAGE_ALIGNED(size) && size != 0); -+ -+ tweak_num = BN_bin2bn(tweak->data, sizeof(tweak->data), NULL); -+ if (!tweak_num) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_bin2bn failed")); -+ } -+ -+ for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) { -+ guchar tmp[sizeof(tweak->data)] = { 0 }; -+ -+ g_assert(BN_num_bytes(tweak_num) >= 0); -+ g_assert(sizeof(tmp) - (guint)BN_num_bytes(tweak_num) > 0); -+ -+ if (BN_bn2binpad(tweak_num, tmp, sizeof(tmp)) < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_bn2binpad failed")); -+ } -+ -+ if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestUpdate failed")); -+ return -1; -+ } -+ -+ /* calculate new tweak value */ -+ if (BN_add_word(tweak_num, PAGE_SIZE) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_add_word failed")); -+ } -+ -+ nep++; -+ } -+ -+ return nep; -+} -+ -+gint pv_component_write(const PvComponent *component, FILE *f, GError **err) -+{ -+ uint64_t offset = pv_component_get_src_addr(component); -+ -+ g_assert(f); -+ -+ switch (component->d_type) { -+ case DATA_BUFFER: { -+ const Buffer *buf = component->buf; -+ -+ if (seek_and_write_buffer(f, buf, offset, err) < 0) -+ return -1; -+ -+ return 0; -+ } -+ case DATA_FILE: { -+ const CompFile *file = component->file; -+ -+ if (seek_and_write_file(f, file, offset, err) < 0) -+ return -1; -+ -+ return 0; -+ } -+ } -+ -+ g_assert_not_reached(); -+} -diff --git a/genprotimg/src/pv/pv_comp.h b/genprotimg/src/pv/pv_comp.h -new file mode 100644 -index 0000000..aa1b5ae ---- /dev/null -+++ b/genprotimg/src/pv/pv_comp.h -@@ -0,0 +1,78 @@ -+/* -+ * PV component related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_COMP_H -+#define PV_COMP_H -+ -+#include -+#include -+#include -+ -+#include "utils/crypto.h" -+ -+/* The order of this enum also implicitly defines the order of the -+ * components within the PV image! -+ */ -+typedef enum { -+ PV_COMP_TYPE_KERNEL = 0, -+ PV_COMP_TYPE_CMDLINE = 1, -+ PV_COMP_TYPE_INITRD = 2, -+ PV_COMP_TYPE_STAGE3B = 3, -+} PvComponentType; -+ -+typedef enum { -+ DATA_FILE = 0, -+ DATA_BUFFER, -+} PvComponentDataType; -+ -+typedef struct comp_file { -+ gchar *path; -+ gsize size; -+} CompFile; -+ -+typedef struct { -+ gint type; /* PvComponentType */ -+ gint d_type; /* PvComponentDataType */ -+ union { -+ struct comp_file *file; -+ Buffer *buf; -+ void *data; -+ }; -+ uint64_t src_addr; -+ uint64_t orig_size; -+ union tweak tweak; /* used for the AES XTS encryption */ -+} PvComponent; -+ -+PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, -+ GError **err); -+PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf, -+ GError **err); -+void pv_component_free(PvComponent *component); -+gint pv_component_type(const PvComponent *component); -+const gchar *pv_component_name(const PvComponent *component); -+uint64_t pv_component_size(const PvComponent *component); -+uint64_t pv_component_get_src_addr(const PvComponent *component); -+uint64_t pv_component_get_orig_size(const PvComponent *component); -+uint64_t pv_component_get_tweak_prefix(const PvComponent *component); -+gboolean pv_component_is_stage3b(const PvComponent *component); -+gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path, -+ void *opaque, GError **err); -+gint pv_component_align(PvComponent *component, const gchar *tmp_path, -+ void *opaque G_GNUC_UNUSED, GError **err); -+int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err); -+int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err); -+int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx, -+ GError **err); -+gint pv_component_write(const PvComponent *component, FILE *f, GError **err); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvComponent, pv_component_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_comps.c b/genprotimg/src/pv/pv_comps.c -new file mode 100644 -index 0000000..15d32f0 ---- /dev/null -+++ b/genprotimg/src/pv/pv_comps.c -@@ -0,0 +1,252 @@ -+/* -+ * PV components related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "boot/stage3b.h" -+#include "common.h" -+#include "utils/align.h" -+#include "utils/crypto.h" -+ -+#include "pv_comp.h" -+#include "pv_comps.h" -+#include "pv_error.h" -+#include "pv_stage3.h" -+ -+struct _pv_img_comps { -+ gboolean finalized; -+ uint64_t next_src; -+ uint64_t nep; -+ EVP_MD_CTX *ald; /* context used for the hash of the addresses */ -+ EVP_MD_CTX *pld; /* context used for the hash of the pages content */ -+ EVP_MD_CTX *tld; /* context used for the hash of the tweaks */ -+ GSList *comps; /* elements sorted by component type */ -+}; -+ -+void pv_img_comps_free(PvImgComps *comps) -+{ -+ if (!comps) -+ return; -+ -+ EVP_MD_CTX_free(comps->ald); -+ EVP_MD_CTX_free(comps->pld); -+ EVP_MD_CTX_free(comps->tld); -+ g_slist_free_full(comps->comps, (GDestroyNotify)pv_component_free); -+ g_free(comps); -+} -+ -+PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md, -+ const EVP_MD *tld_md, GError **err) -+{ -+ g_autoptr(PvImgComps) ret = g_new0(PvImgComps, 1); -+ -+ ret->ald = digest_ctx_new(ald_md, err); -+ if (!ret->ald) -+ return NULL; -+ -+ ret->pld = digest_ctx_new(pld_md, err); -+ if (!ret->pld) -+ return NULL; -+ -+ ret->tld = digest_ctx_new(tld_md, err); -+ if (!ret->tld) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+guint pv_img_comps_length(const PvImgComps *comps) -+{ -+ return g_slist_length(comps->comps); -+} -+ -+/* Update hashes and nep */ -+/* Returns 0 in case of success and -1 in case of a failure */ -+static gint pv_img_comps_hash_comp(PvImgComps *comps, const PvComponent *comp, -+ GError **err) -+{ -+ int64_t nep_1 = 0; -+ int64_t nep_2 = 0; -+ int64_t nep_3 = 0; -+ -+ /* update pld */ -+ nep_1 = pv_component_update_pld(comp, comps->pld, err); -+ if (nep_1 < 0) -+ return -1; -+ -+ /* update ald */ -+ nep_2 = pv_component_update_ald(comp, comps->ald, err); -+ if (nep_2 < 0) -+ return -1; -+ -+ /* update tld */ -+ nep_3 = pv_component_update_tld(comp, comps->tld, err); -+ if (nep_3 < 0) -+ return -1; -+ -+ g_assert(nep_1 == nep_2); -+ g_assert(nep_2 == nep_3); -+ -+ /* update comps->nep */ -+ g_assert_true(g_uint64_checked_add(&comps->nep, comps->nep, -+ (uint64_t)nep_1)); -+ return 0; -+} -+ -+gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp, -+ GError **err) -+{ -+ g_assert(comp); -+ g_assert(*comp); -+ g_assert(comps); -+ g_assert(IS_PAGE_ALIGNED(comps->next_src)); -+ -+ uint64_t src_addr = comps->next_src; -+ uint64_t src_size = pv_component_size(*comp) -+ ? PAGE_ALIGN(pv_component_size(*comp)) -+ : PAGE_SIZE; -+ -+ if (comps->finalized) { -+ g_set_error(err, PV_COMPONENT_ERROR, PV_COMPONENT_ERROR_FINALIZED, -+ _("Failed to add component, image is already finalized")); -+ return -1; -+ } -+ -+ /* set the address of the component in the memory layout */ -+ (*comp)->src_addr = src_addr; -+ -+ g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)", -+ pv_component_name(*comp), pv_component_get_src_addr(*comp), -+ pv_component_size(*comp), pv_component_get_orig_size(*comp)); -+ -+ /* append the component and pass the responsibility of @comp -+ * to @comps -+ */ -+ comps->comps = g_slist_append(comps->comps, g_steal_pointer(comp)); -+ comps->next_src += src_size; -+ -+ g_assert(IS_PAGE_ALIGNED(comps->next_src)); -+ g_assert(!*comp); -+ return 0; -+} -+ -+struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps, -+ struct psw_t *psw) -+{ -+ g_autofree struct stage3b_args *ret = g_new0(struct stage3b_args, 1); -+ -+ for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) { -+ const PvComponent *img_comp = iterator->data; -+ uint64_t src_addr, dst_size; -+ -+ g_assert(img_comp); -+ -+ src_addr = pv_component_get_src_addr(img_comp); -+ dst_size = pv_component_get_orig_size(img_comp); -+ -+ g_assert(dst_size <= pv_component_size(img_comp)); -+ -+ switch ((PvComponentType)pv_component_type(img_comp)) { -+ case PV_COMP_TYPE_KERNEL: -+ memblob_init(&ret->kernel, src_addr, dst_size); -+ break; -+ case PV_COMP_TYPE_CMDLINE: -+ memblob_init(&ret->cmdline, src_addr, dst_size); -+ break; -+ case PV_COMP_TYPE_INITRD: -+ memblob_init(&ret->initrd, src_addr, dst_size); -+ break; -+ case PV_COMP_TYPE_STAGE3B: -+ /* nothing needs to be done since it is the -+ * stage3b itself -+ */ -+ break; -+ default: -+ g_assert_not_reached(); -+ break; -+ } -+ } -+ -+ /* for `stage3b_args` big-endian format must be used */ -+ ret->psw.mask = GUINT64_TO_BE(psw->mask); -+ ret->psw.addr = GUINT64_TO_BE(psw->addr); -+ return g_steal_pointer(&ret); -+} -+ -+gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err) -+{ -+ g_assert(IS_PAGE_ALIGNED(comps->next_src)); -+ -+ if (!IS_PAGE_ALIGNED(offset)) { -+ g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET, -+ _("Offset must be page aligned")); -+ return -1; -+ } -+ -+ if (pv_img_comps_length(comps) > 0) { -+ g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET, -+ _("Offset cannot be changed after a component was added")); -+ return -1; -+ } -+ -+ comps->next_src += offset; -+ -+ g_assert(IS_PAGE_ALIGNED(comps->next_src)); -+ return 0; -+} -+ -+GSList *pv_img_comps_get_comps(const PvImgComps *comps) -+{ -+ return comps->comps; -+} -+ -+gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest, -+ Buffer **ald_digest, Buffer **tld_digest, -+ uint64_t *nep, GError **err) -+{ -+ g_autoptr(Buffer) tmp_pld_digest = NULL; -+ g_autoptr(Buffer) tmp_ald_digest = NULL; -+ g_autoptr(Buffer) tmp_tld_digest = NULL; -+ -+ comps->finalized = TRUE; -+ for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) { -+ const PvComponent *comp = iterator->data; -+ -+ /* update hashes and nep */ -+ if (pv_img_comps_hash_comp(comps, comp, err) < 0) -+ return -1; -+ } -+ -+ tmp_pld_digest = digest_ctx_finalize(comps->pld, err); -+ if (!tmp_pld_digest) -+ return -1; -+ -+ tmp_ald_digest = digest_ctx_finalize(comps->ald, err); -+ if (!tmp_ald_digest) -+ return -1; -+ -+ tmp_tld_digest = digest_ctx_finalize(comps->tld, err); -+ if (!tmp_tld_digest) -+ return -1; -+ -+ *pld_digest = g_steal_pointer(&tmp_pld_digest); -+ *ald_digest = g_steal_pointer(&tmp_ald_digest); -+ *tld_digest = g_steal_pointer(&tmp_tld_digest); -+ *nep = comps->nep; -+ return 0; -+} -+ -+PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n) -+{ -+ return g_slist_nth_data(comps->comps, n); -+} -diff --git a/genprotimg/src/pv/pv_comps.h b/genprotimg/src/pv/pv_comps.h -new file mode 100644 -index 0000000..d555e36 ---- /dev/null -+++ b/genprotimg/src/pv/pv_comps.h -@@ -0,0 +1,42 @@ -+/* -+ * PV components related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_COMPS_H -+#define PV_COMPS_H -+ -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "boot/stage3b.h" -+#include "utils/buffer.h" -+ -+#include "pv_comp.h" -+ -+typedef struct _pv_img_comps PvImgComps; -+ -+PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md, -+ const EVP_MD *tld_md, GError **err); -+guint pv_img_comps_length(const PvImgComps *comps); -+GSList *pv_img_comps_get_comps(const PvImgComps *comps); -+struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps, -+ struct psw_t *psw); -+gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp, -+ GError **err); -+PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n); -+gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err); -+gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest, -+ Buffer **ald_digest, Buffer **tld_digest, -+ uint64_t *nep, GError **err); -+void pv_img_comps_free(PvImgComps *comps); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImgComps, pv_img_comps_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_error.c b/genprotimg/src/pv/pv_error.c -new file mode 100644 -index 0000000..9d6f264 ---- /dev/null -+++ b/genprotimg/src/pv/pv_error.c -@@ -0,0 +1,37 @@ -+/* -+ * PV error related functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+ -+#include "pv_error.h" -+ -+GQuark pv_error_quark(void) -+{ -+ return g_quark_from_static_string("pv-error-quark"); -+} -+ -+GQuark pv_crypto_error_quark(void) -+{ -+ return g_quark_from_static_string("pv-crypto-error-quark"); -+} -+ -+GQuark pv_component_error_quark(void) -+{ -+ return g_quark_from_static_string("pv-component-error-quark"); -+} -+ -+GQuark pv_image_error_quark(void) -+{ -+ return g_quark_from_static_string("pv-image-error-quark"); -+} -+ -+GQuark pv_parse_error_quark(void) -+{ -+ return g_quark_from_static_string("pv-parse-error-quark"); -+} -diff --git a/genprotimg/src/pv/pv_error.h b/genprotimg/src/pv/pv_error.h -new file mode 100644 -index 0000000..1dd24fc ---- /dev/null -+++ b/genprotimg/src/pv/pv_error.h -@@ -0,0 +1,62 @@ -+/* -+ * PV error related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_ERROR_H -+#define PV_ERROR_H -+ -+#include -+ -+GQuark pv_error_quark(void); -+GQuark pv_parse_error_quark(void); -+GQuark pv_component_error_quark(void); -+GQuark pv_crypto_error_quark(void); -+GQuark pv_image_error_quark(void); -+ -+#define PV_ERROR pv_error_quark() -+#define PV_PARSE_ERROR pv_parse_error_quark() -+#define PV_CRYPTO_ERROR pv_crypto_error_quark() -+#define PV_COMPONENT_ERROR pv_component_error_quark() -+#define PV_IMAGE_ERROR pv_image_error_quark() -+ -+typedef enum { -+ PV_ERROR_IPIB_SIZE, -+ PV_ERROR_PV_HDR_SIZE, -+ PV_ERROR_INTERNAL, -+} PvErrors; -+ -+typedef enum { -+ PV_PARSE_ERROR_OK = 0, -+ PV_PARSE_ERROR_SYNTAX, -+ PR_PARSE_ERROR_INVALID_ARGUMENT, -+ PR_PARSE_ERROR_MISSING_ARGUMENT, -+} PvParseErrors; -+ -+typedef enum { -+ PV_COMPONENT_ERROR_UNALIGNED, -+ PV_COMPONENT_ERROR_FINALIZED, -+} PvComponentErrors; -+ -+typedef enum { -+ PV_IMAGE_ERROR_OFFSET, -+ PV_IMAGE_ERROR_FINALIZED, -+} PvImageErrors; -+ -+typedef enum { -+ PV_CRYPTO_ERROR_VERIFICATION, -+ PV_CRYPTO_ERROR_INIT, -+ PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ PV_CRYPTO_ERROR_INTERNAL, -+ PV_CRYPTO_ERROR_DERIVE, -+ PV_CRYPTO_ERROR_KEYGENERATION, -+ PV_CRYPTO_ERROR_RANDOMIZATION, -+ PV_CRYPTO_ERROR_INVALID_PARM, -+ PV_CRYPTO_ERROR_INVALID_KEY_SIZE, -+} PvCryptoErrors; -+ -+#endif -diff --git a/genprotimg/src/pv/pv_hdr.c b/genprotimg/src/pv/pv_hdr.c -new file mode 100644 -index 0000000..45e721d ---- /dev/null -+++ b/genprotimg/src/pv/pv_hdr.c -@@ -0,0 +1,293 @@ -+/* -+ * PV header related functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "include/pv_crypto_def.h" -+#include "utils/buffer.h" -+#include "utils/crypto.h" -+ -+#include "pv_comp.h" -+#include "pv_hdr.h" -+#include "pv_image.h" -+ -+void pv_hdr_free(PvHdr *hdr) -+{ -+ if (!hdr) -+ return; -+ -+ g_free(hdr->optional_items); -+ g_free(hdr->encrypted); -+ g_free(hdr->slots); -+ g_free(hdr); -+} -+ -+uint32_t pv_hdr_size(const PvHdr *hdr) -+{ -+ return GUINT32_FROM_BE(hdr->head.phs); -+} -+ -+gboolean pv_hdr_uses_encryption(const PvHdr *hdr) -+{ -+ return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_CFLAG_NO_DECRYPTION); -+} -+ -+uint64_t pv_hdr_enc_size(const PvHdr *hdr) -+{ -+ return GUINT64_FROM_BE(hdr->head.sea); -+} -+ -+uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr) -+{ -+ uint64_t size = pv_hdr_enc_size(hdr); -+ -+ if (size > UINT32_MAX) -+ g_abort(); -+ -+ return (uint32_t)size; -+} -+ -+static guint pv_hdr_tag_size(const PvHdr *hdr) -+{ -+ return sizeof(hdr->tag); -+} -+ -+uint32_t pv_hdr_aad_size(const PvHdr *hdr) -+{ -+ return pv_hdr_size(hdr) - pv_hdr_enc_size_casted(hdr) - -+ pv_hdr_tag_size(hdr); -+} -+ -+uint64_t pv_hdr_get_nks(const PvHdr *hdr) -+{ -+ return GUINT64_FROM_BE(hdr->head.nks); -+} -+ -+/* In-place modification of ``buf`` */ -+static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf, -+ GError **err) -+{ -+ uint32_t hdr_len = pv_hdr_size(hdr); -+ uint32_t aad_len = pv_hdr_aad_size(hdr); -+ guint tag_len = pv_hdr_tag_size(hdr); -+ uint32_t enc_len = pv_hdr_enc_size_casted(hdr); -+ const Buffer aad_part = { .data = buf->data, .size = aad_len }; -+ Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len, -+ .size = enc_len }; -+ Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len, -+ .size = tag_len }; -+ struct cipher_parms parms; -+ int64_t c_len; -+ -+ g_assert(aad_part.size + enc_part.size + tag_part.size == buf->size); -+ g_assert(img->cust_root_key->size <= INT_MAX); -+ g_assert(img->gcm_iv->size <= INT_MAX); -+ g_assert(EVP_CIPHER_key_length(img->gcm_cipher) == -+ (int)img->cust_root_key->size); -+ g_assert(EVP_CIPHER_iv_length(img->gcm_cipher) == (int)img->gcm_iv->size); -+ -+ parms.key = img->cust_root_key; -+ parms.iv_or_tweak = img->gcm_iv; -+ parms.cipher = img->gcm_cipher; -+ -+ /* in-place encryption */ -+ c_len = gcm_encrypt(&enc_part, &aad_part, &parms, &enc_part, &tag_part, err); -+ if (c_len < 0) -+ return -1; -+ -+ g_assert(c_len == enc_len); -+ return 0; -+} -+ -+/* Initializes the unencrypted, but integrity protected part of the PV -+ * header -+ */ -+static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err) -+{ -+ g_autofree union ecdh_pub_key *cust_pub_key = NULL; -+ struct pv_hdr_key_slot *hdr_slot = hdr->slots; -+ struct pv_hdr_head *head = &hdr->head; -+ g_autoptr(Buffer) pld = NULL; -+ g_autoptr(Buffer) ald = NULL; -+ g_autoptr(Buffer) tld = NULL; -+ uint64_t nep = 0; -+ -+ g_assert(sizeof(head->iv) == img->gcm_iv->size); -+ g_assert(sizeof(head->cust_pub_key) == sizeof(*cust_pub_key)); -+ -+ cust_pub_key = evp_pkey_to_ecdh_pub_key(img->cust_pub_priv_key, err); -+ if (!cust_pub_key) -+ return -1; -+ -+ head->magic = GUINT64_TO_BE(PV_MAGIC_NUMBER); -+ head->version = GUINT32_TO_BE(PV_VERSION_1); -+ /* ``phs`` is already set so we can skip it here */ -+ memcpy(head->iv, img->gcm_iv->data, sizeof(head->iv)); -+ /* ``nks`` is already set so we can skip it here */ -+ /* ``sea`` is already set so we can skip it here */ -+ head->pcf = GUINT64_TO_BE(img->pcf); -+ memcpy(head->cust_pub_key.data, cust_pub_key, -+ sizeof(head->cust_pub_key)); -+ -+ if (pv_img_calc_pld_ald_tld_nep(img, &pld, &ald, &tld, &nep, err) < 0) -+ return -1; -+ -+ g_assert(sizeof(head->pld) == pld->size); -+ g_assert(sizeof(head->ald) == ald->size); -+ g_assert(sizeof(head->tld) == tld->size); -+ -+ head->nep = GUINT64_TO_BE(nep); -+ memcpy(head->pld, pld->data, sizeof(head->pld)); -+ memcpy(head->ald, ald->data, sizeof(head->ald)); -+ memcpy(head->tld, tld->data, sizeof(head->tld)); -+ -+ /* set the key slots */ -+ for (GSList *iterator = img->key_slots; iterator; iterator = iterator->next) { -+ const PvHdrKeySlot *slot = iterator->data; -+ -+ g_assert(slot); -+ -+ /* the memory for the slots is pre-allocated so we -+ * have not to allocate and since PvHdrKeySlot is -+ * stored in the big-edian format we can simply use -+ * memcpy. -+ */ -+ memcpy(hdr_slot++, slot, sizeof(*slot)); -+ } -+ -+ return 0; -+} -+ -+/* Initializes the encrypted and also integrity protected part of the -+ * PV header -+ */ -+static gint pv_hdr_enc_init(PvHdr *hdr, const PvImage *img, GError **err) -+{ -+ struct pv_hdr_encrypted *enc = hdr->encrypted; -+ const PvComponent *stage3b; -+ struct psw_t psw; -+ -+ g_assert(sizeof(enc->img_enc_key_1) + sizeof(enc->img_enc_key_2) == -+ EVP_CIPHER_key_length(img->xts_cipher)); -+ g_assert(sizeof(enc->cust_comm_key) == img->cust_comm_key->size); -+ g_assert(img->xts_key->size == -+ (guint)EVP_CIPHER_key_length(img->xts_cipher)); -+ -+ stage3b = pv_img_get_stage3b_comp(img, err); -+ if (!stage3b) -+ return -1; -+ -+ memcpy(enc->cust_comm_key, img->cust_comm_key->data, -+ sizeof(enc->cust_comm_key)); -+ memcpy(enc->img_enc_key_1, img->xts_key->data, -+ sizeof(enc->img_enc_key_1)); -+ memcpy(enc->img_enc_key_2, -+ (uint8_t *)img->xts_key->data + sizeof(enc->img_enc_key_1), -+ sizeof(enc->img_enc_key_2)); -+ -+ /* Setup program check handler */ -+ psw.mask = GUINT64_TO_BE(DEFAULT_INITIAL_PSW_MASK); -+ psw.addr = GUINT64_TO_BE(pv_component_get_src_addr(stage3b)); -+ enc->psw = psw; -+ enc->scf = GUINT64_TO_BE(img->scf); -+ enc->noi = GUINT32_TO_BE(g_slist_length(img->optional_items)); -+ -+ /* set the optional items */ -+ for (GSList *iterator = img->optional_items; iterator; -+ iterator = iterator->next) { -+ const struct pv_hdr_opt_item *item = iterator->data; -+ -+ g_assert(item); -+ -+ /* not supported in the first version */ -+ g_assert_not_reached(); -+ } -+ -+ return 0; -+} -+ -+PvHdr *pv_hdr_new(const PvImage *img, GError **err) -+{ -+ uint32_t noi = g_slist_length(img->optional_items); -+ uint32_t hdr_size = pv_img_get_pv_hdr_size(img); -+ gsize nks = g_slist_length(img->key_slots); -+ uint32_t sea = pv_img_get_enc_size(img); -+ g_autoptr(PvHdr) ret = NULL; -+ -+ g_assert(nks > 0); -+ /* must be a multiple of AES block size */ -+ g_assert(sea % AES_BLOCK_SIZE == 0); -+ g_assert(sea >= sizeof(struct pv_hdr_encrypted)); -+ -+ ret = g_new0(PvHdr, 1); -+ ret->slots = g_new0(struct pv_hdr_key_slot, nks); -+ ret->head.phs = GUINT32_TO_BE(hdr_size); -+ ret->head.nks = GUINT64_TO_BE(nks); -+ ret->head.sea = GUINT64_TO_BE(sea); -+ -+ ret->encrypted = g_new0(struct pv_hdr_encrypted, 1); -+ ret->optional_items = g_malloc0(sea - sizeof(struct pv_hdr_encrypted)); -+ ret->encrypted->noi = GUINT32_TO_BE(noi); -+ -+ if (pv_hdr_aad_init(ret, img, err) < 0) -+ return NULL; -+ -+ if (pv_hdr_enc_init(ret, img, err) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst) -+{ -+ uint64_t nks = pv_hdr_get_nks(hdr); -+ uint8_t *data; -+ -+ g_assert(dst->size == pv_hdr_size(hdr)); -+ g_assert(pv_hdr_enc_size_casted(hdr) >= sizeof(*hdr->encrypted)); -+ -+ data = memcpy(dst->data, &hdr->head, sizeof(hdr->head)); -+ data = memcpy(data + sizeof(hdr->head), hdr->slots, -+ sizeof(struct pv_hdr_key_slot) * nks); -+ data = memcpy(data + sizeof(struct pv_hdr_key_slot) * nks, -+ hdr->encrypted, sizeof(*hdr->encrypted)); -+ if (pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted) > 0) { -+ (void)memcpy(data + sizeof(*hdr->encrypted), -+ hdr->optional_items, -+ pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted)); -+ } -+} -+ -+Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, -+ enum PvCryptoMode mode, GError **err) -+{ -+ uint32_t hdr_size = pv_hdr_size(hdr); -+ g_autoptr(Buffer) ret = NULL; -+ -+ ret = buffer_alloc(hdr_size); -+ pv_hdr_memcpy(hdr, ret); -+ -+ if (mode == PV_ENCRYPT) { -+ /* The buffer @ret is modified in-place */ -+ if (pv_hdr_encrypt(hdr, img, ret, err) < 0) -+ return NULL; -+ } else { -+ /* Simply copy the tag */ -+ memcpy((uint8_t *)ret->data + hdr_size - pv_hdr_tag_size(hdr), -+ hdr->tag, pv_hdr_tag_size(hdr)); -+ } -+ -+ return g_steal_pointer(&ret); -+} -diff --git a/genprotimg/src/pv/pv_hdr.h b/genprotimg/src/pv/pv_hdr.h -new file mode 100644 -index 0000000..8df7a6f ---- /dev/null -+++ b/genprotimg/src/pv/pv_hdr.h -@@ -0,0 +1,36 @@ -+/* -+ * PV header related functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_HDR_H -+#define PV_HDR_H -+ -+#include -+#include -+ -+#include "boot/s390.h" -+#include "include/pv_hdr_def.h" -+#include "utils/crypto.h" -+#include "utils/buffer.h" -+ -+#include "pv_image.h" -+ -+PvHdr *pv_hdr_new(const PvImage *img, GError **err); -+void pv_hdr_free(PvHdr *hdr); -+G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr); -+Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, -+ enum PvCryptoMode mode, GError **err); -+uint32_t pv_hdr_size(const PvHdr *hdr); -+uint32_t pv_hdr_aad_size(const PvHdr *hdr); -+uint64_t pv_hdr_enc_size(const PvHdr *hdr); -+uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr); -+uint64_t pv_hdr_get_nks(const PvHdr *hdr); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdr, pv_hdr_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c -new file mode 100644 -index 0000000..7ec5fe9 ---- /dev/null -+++ b/genprotimg/src/pv/pv_image.c -@@ -0,0 +1,821 @@ -+/* -+ * PV image related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "boot/stage3a.h" -+#include "common.h" -+#include "include/pv_crypto_def.h" -+#include "include/pv_hdr_def.h" -+#include "utils/align.h" -+#include "utils/crypto.h" -+#include "utils/file_utils.h" -+ -+#include "pv_args.h" -+#include "pv_comps.h" -+#include "pv_error.h" -+#include "pv_hdr.h" -+#include "pv_image.h" -+#include "pv_ipib.h" -+#include "pv_opt_item.h" -+#include "pv_stage3.h" -+ -+const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err) -+{ -+ const PvComponent *comp; -+ -+ g_return_val_if_fail(pv_img_comps_length(img->comps) >= 1, NULL); -+ -+ comp = pv_img_comps_get_nth_comp(img->comps, -+ pv_img_comps_length(img->comps) - 1); -+ if (!pv_component_is_stage3b(comp)) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Failed to get 'stage3b' component")); -+ return NULL; -+ } -+ return comp; -+} -+ -+typedef gint (*prepare_func)(PvComponent *obj, const gchar *tmp_path, -+ void *opaque, GError **err); -+ -+static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp, -+ GError **err) -+{ -+ struct cipher_parms parms = { 0 }; -+ g_autoptr(Buffer) tweak = NULL; -+ prepare_func func = NULL; -+ void *opaque = NULL; -+ gint rc; -+ -+ if (img->pcf & PV_CFLAG_NO_DECRYPTION) { -+ /* we only need to align the components */ -+ func = pv_component_align; -+ opaque = NULL; -+ } else { -+ const EVP_CIPHER *cipher = img->xts_cipher; -+ -+ g_assert_cmpint((int)img->xts_key->size, ==, -+ EVP_CIPHER_key_length(cipher)); -+ g_assert_cmpint((int)PAGE_SIZE % EVP_CIPHER_block_size(cipher), -+ ==, 0); -+ g_assert_cmpint(sizeof(comp->tweak), ==, -+ EVP_CIPHER_iv_length(cipher)); -+ g_assert(img->xts_key->size <= UINT_MAX); -+ -+ tweak = buffer_alloc(sizeof(comp->tweak.data)); -+ memcpy(tweak->data, comp->tweak.data, tweak->size); -+ func = pv_component_align_and_encrypt; -+ parms.cipher = cipher; -+ parms.key = img->xts_key; -+ parms.iv_or_tweak = tweak; -+ -+ opaque = &parms; -+ } -+ -+ rc = (*func)(comp, img->tmp_dir, opaque, err); -+ if (rc < 0) -+ return -1; -+ -+ return 0; -+} -+ -+static Buffer *pv_img_read_key(const gchar *path, guint key_size, -+ GError **err) -+{ -+ g_autoptr(Buffer) tmp_ret = NULL; -+ Buffer *ret = NULL; -+ gsize bytes_read; -+ FILE *f = NULL; -+ gsize size; -+ -+ if (file_size(path, &size, err) != 0) -+ return NULL; -+ -+ if (size - key_size != 0) { -+ g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE, -+ _("Wrong file size '%s': read %zd, expected %u"), path, size, -+ key_size); -+ return NULL; -+ } -+ -+ f = file_open(path, "rb", err); -+ if (!f) -+ return NULL; -+ -+ tmp_ret = buffer_alloc(size); -+ if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0) -+ goto err; -+ -+ if (bytes_read - key_size != 0) { -+ g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE, -+ _("Wrong file size '%s': read %zd, expected %u"), -+ path, bytes_read, key_size); -+ goto err; -+ } -+ -+ ret = g_steal_pointer(&tmp_ret); -+err: -+ if (f) -+ fclose(f); -+ return ret; -+} -+ -+static EVP_PKEY *pv_img_get_cust_pub_priv_key(gint nid, GError **err) -+{ -+ return generate_ec_key(nid, err); -+} -+ -+static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths, -+ X509_STORE *store, gint nid, -+ GError **err) -+{ -+ g_autoslist(EVP_PKEY) ret = NULL; -+ -+ g_assert(host_cert_paths); -+ -+ for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL; -+ iterator++) { -+ g_autoptr(EVP_PKEY) host_key = NULL; -+ const gchar *path = *iterator; -+ -+ g_assert(path); -+ -+ host_key = read_ec_pubkey_cert(store, nid, path, err); -+ if (!host_key) -+ return NULL; -+ -+ ret = g_slist_append(ret, g_steal_pointer(&host_key)); -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path, -+ GError **err) -+{ -+ gint key_len = EVP_CIPHER_key_length(cipher); -+ -+ g_assert(key_len > 0); -+ -+ if (path) -+ return pv_img_read_key(path, (guint)key_len, err); -+ -+ return generate_aes_key((guint)key_len, err); -+} -+ -+static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path, -+ GError **err) -+{ -+ gint iv_len = EVP_CIPHER_iv_length(cipher); -+ -+ g_assert(iv_len > 0); -+ -+ if (path) -+ return pv_img_read_key(path, (guint)iv_len, err); -+ -+ return generate_aes_iv((guint)iv_len, err); -+} -+ -+static int hex_str_toull(const gchar *nptr, uint64_t *dst, -+ GError **err) -+{ -+ uint64_t value; -+ gchar *end; -+ -+ g_assert(dst); -+ -+ if (!g_str_is_ascii(nptr)) { -+ g_set_error(err, PV_ERROR, EINVAL, -+ _("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"), -+ nptr); -+ return -1; -+ } -+ -+ value = g_ascii_strtoull(nptr, &end, 16); -+ if ((value == G_MAXUINT64 && errno == ERANGE) || -+ (end && *end != '\0')) { -+ g_set_error(err, PV_ERROR, EINVAL, -+ _("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"), -+ nptr); -+ return -1; -+ } -+ *dst = value; -+ return 0; -+} -+ -+static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s, -+ GError **err) -+{ -+ if (psw_addr_s) { -+ uint64_t psw_addr; -+ -+ if (hex_str_toull(psw_addr_s, &psw_addr, err) < 0) -+ return -1; -+ -+ img->initial_psw.addr = psw_addr; -+ } -+ -+ return 0; -+} -+ -+static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s, -+ const gchar *scf_s, GError **err) -+{ -+ uint64_t flags; -+ -+ if (pcf_s) { -+ if (hex_str_toull(pcf_s, &flags, err) < 0) -+ return -1; -+ -+ img->pcf = flags; -+ } -+ -+ if (scf_s) { -+ if (hex_str_toull(scf_s, &flags, err) < 0) -+ return -1; -+ -+ img->scf = flags; -+ } -+ -+ return 0; -+} -+ -+/* read in the keys or auto-generate them */ -+static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) -+{ -+ g_autoptr(X509_STORE) store = NULL; -+ -+ g_assert(img->xts_cipher); -+ g_assert(img->cust_comm_cipher); -+ g_assert(img->gcm_cipher); -+ g_assert(img->nid); -+ -+ img->xts_key = pv_img_get_key(img->xts_cipher, args->xts_key_path, err); -+ if (!img->xts_key) -+ return -1; -+ -+ img->cust_comm_key = pv_img_get_key(img->cust_comm_cipher, -+ args->cust_comm_key_path, err); -+ if (!img->cust_comm_key) -+ return -1; -+ -+ img->cust_root_key = -+ pv_img_get_key(img->gcm_cipher, args->cust_root_key_path, err); -+ if (!img->cust_root_key) -+ return -1; -+ -+ img->gcm_iv = pv_img_get_iv(img->gcm_cipher, args->gcm_iv_path, err); -+ if (!img->gcm_iv) -+ return -1; -+ -+ img->cust_pub_priv_key = pv_img_get_cust_pub_priv_key(img->nid, err); -+ if (!img->cust_pub_priv_key) -+ return -1; -+ -+ img->host_pub_keys = -+ pv_img_get_host_keys(args->host_keys, store, img->nid, err); -+ if (!img->host_pub_keys) -+ return -1; -+ -+ return 0; -+} -+ -+static void pv_img_add_host_slot(PvImage *img, PvHdrKeySlot *slot) -+{ -+ img->key_slots = g_slist_append(img->key_slots, slot); -+} -+ -+static void pv_hdr_key_slot_free(PvHdrKeySlot *slot) -+{ -+ if (!slot) -+ return; -+ -+ g_free(slot); -+} -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free) -+ -+static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher, -+ const Buffer *cust_root_key, -+ EVP_PKEY *cust_key, EVP_PKEY *host_key, -+ GError **err) -+{ -+ g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1); -+ g_autofree union ecdh_pub_key *pub = NULL; -+ g_autoptr(Buffer) exchange_key = NULL; -+ g_autoptr(Buffer) digest_key = NULL; -+ g_autoptr(Buffer) iv = NULL; -+ Buffer pub_buf; -+ /* No AAD data is used */ -+ Buffer aad = { .data = NULL, .size = 0 }; -+ /* Set the output buffers for the encrypted data and the -+ * generated GCM tag -+ */ -+ Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) }; -+ Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) }; -+ struct cipher_parms parms; -+ int64_t c_len = 0; -+ -+ g_assert(EVP_CIPHER_iv_length(gcm_cipher) >= 0); -+ -+ pub = evp_pkey_to_ecdh_pub_key(host_key, err); -+ if (!pub) -+ return NULL; -+ -+ pub_buf.data = pub->data; -+ pub_buf.size = sizeof(*pub); -+ digest_key = sha256_buffer(&pub_buf, err); -+ if (!digest_key) -+ return NULL; -+ -+ g_assert(digest_key->size == sizeof(ret->digest_key)); -+ /* set `digest_key` field */ -+ memcpy(ret->digest_key, digest_key->data, sizeof(ret->digest_key)); -+ -+ exchange_key = compute_exchange_key(cust_key, host_key, err); -+ if (!exchange_key) -+ return NULL; -+ -+ /* initialize cipher parameters */ -+ g_assert(exchange_key->size <= INT_MAX); -+ g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher)); -+ -+ /* create zero IV */ -+ iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher)); -+ parms.iv_or_tweak = iv; -+ parms.key = exchange_key; -+ parms.cipher = gcm_cipher; -+ -+ /* Encrypt the customer root key that is used for the encryption -+ * of the PV header -+ */ -+ c_len = gcm_encrypt(cust_root_key, &aad, &parms, &enc, &tag, err); -+ if (c_len < 0) -+ return NULL; -+ -+ g_assert(c_len == (int64_t)cust_root_key->size); -+ return g_steal_pointer(&ret); -+} -+ -+static gint pv_img_set_host_slots(PvImage *img, GError **err) -+{ -+ for (GSList *iterator = img->host_pub_keys; iterator; iterator = iterator->next) { -+ EVP_PKEY *host_key = iterator->data; -+ -+ g_assert(host_key); -+ -+ PvHdrKeySlot *slot = pv_hdr_key_slot_new(img->gcm_cipher, -+ img->cust_root_key, -+ img->cust_pub_priv_key, -+ host_key, err); -+ if (!slot) -+ return -1; -+ -+ pv_img_add_host_slot(img, slot); -+ } -+ -+ return 0; -+} -+ -+static gint pv_img_set_comps_offset(PvImage *img, uint64_t offset, GError **err) -+{ -+ return pv_img_comps_set_offset(img->comps, offset, err); -+} -+ -+PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err) -+{ -+ g_autoptr(PvImage) ret = g_new0(PvImage, 1); -+ uint64_t offset; -+ -+ g_assert(args->tmp_dir); -+ g_assert(stage3a_path); -+ -+ if (args->no_verify) -+ g_warning(_("host-key document verification is disabled. Your workload is not secured.")); -+ -+ ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err); -+ if (!ret->comps) -+ return NULL; -+ -+ ret->cust_comm_cipher = EVP_aes_256_gcm(); -+ ret->gcm_cipher = EVP_aes_256_gcm(); -+ ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR; -+ ret->initial_psw.mask = DEFAULT_INITIAL_PSW_MASK; -+ ret->nid = NID_secp521r1; -+ ret->tmp_dir = g_strdup(args->tmp_dir); -+ ret->xts_cipher = EVP_aes_256_xts(); -+ -+ /* set initial PSW that will be loaded by the stage3b */ -+ if (pv_img_set_psw_addr(ret, args->psw_addr, err) < 0) -+ return NULL; -+ -+ /* set the control flags: PCF and SCF */ -+ if (pv_img_set_control_flags(ret, args->pcf, args->scf, err) < 0) -+ return NULL; -+ -+ /* read in the keys */ -+ if (pv_img_set_keys(ret, args, err) < 0) -+ return NULL; -+ -+ if (pv_img_set_host_slots(ret, err) < 0) -+ return NULL; -+ -+ /* allocate enough memory for the stage3a args and load the -+ * stage3a template into memory and set the loader_psw -+ */ -+ if (pv_img_load_and_set_stage3a(ret, stage3a_path, err) < 0) -+ return NULL; -+ -+ offset = PAGE_ALIGN(STAGE3A_LOAD_ADDRESS + ret->stage3a->size); -+ -+ /* shift right all components by the size of stage3a loader */ -+ if (pv_img_set_comps_offset(ret, offset, err) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+void pv_img_free(PvImage *img) -+{ -+ if (!img) -+ return; -+ -+ g_slist_free_full(img->optional_items, -+ (GDestroyNotify)pv_opt_item_free); -+ g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free); -+ g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free); -+ EVP_PKEY_free(img->cust_pub_priv_key); -+ buffer_clear(&img->stage3a); -+ pv_img_comps_free(img->comps); -+ g_free(img->tmp_dir); -+ buffer_free(img->xts_key); -+ buffer_free(img->cust_root_key); -+ buffer_free(img->gcm_iv); -+ buffer_free(img->cust_comm_key); -+ g_free(img); -+} -+ -+static gint pv_img_prepare_and_add_component(PvImage *img, PvComponent **comp, -+ GError **err) -+{ -+ g_assert(comp); -+ g_assert(*comp); -+ -+ /* prepares the component: does the alignment and encryption -+ * if required -+ */ -+ if (pv_img_prepare_component(img, *comp, err) < 0) -+ return -1; -+ -+ /* calculates the memory layout and adds the component to its -+ * internal list -+ */ -+ if (pv_img_comps_add_component(img->comps, comp, err) < 0) -+ return -1; -+ -+ g_assert(!*comp); -+ return 0; -+} -+ -+gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err) -+{ -+ g_autoptr(PvComponent) comp = NULL; -+ -+ comp = pv_component_new_file(arg->type, arg->path, err); -+ if (!comp) -+ return -1; -+ -+ if (pv_img_prepare_and_add_component(img, &comp, err) < 0) -+ return -1; -+ -+ g_assert(!comp); -+ return 0; -+} -+ -+gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald, -+ Buffer **tld, uint64_t *nep, GError **err) -+{ -+ return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err); -+} -+ -+static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err) -+{ -+ g_autofree struct stage3b_args *args = NULL; -+ -+ args = pv_img_comps_get_stage3b_args(img->comps, &img->initial_psw); -+ if (!args) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Cannot generate stage3b arguments")); -+ return -1; -+ } -+ -+ build_stage3b(stage3b, args); -+ return 0; -+} -+ -+gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err) -+{ -+ g_autoptr(PvComponent) comp = NULL; -+ g_autoptr(Buffer) stage3b = NULL; -+ -+ stage3b = stage3b_getblob(path, err); -+ if (!stage3b) -+ return -1; -+ -+ /* set the stage3b data */ -+ if (pv_img_build_stage3b(img, stage3b, err) < 0) -+ return -1; -+ -+ comp = pv_component_new_buf(PV_COMP_TYPE_STAGE3B, stage3b, err); -+ if (!comp) -+ return -1; -+ -+ if (pv_img_prepare_and_add_component(img, &comp, err) < 0) -+ return -1; -+ -+ g_assert(!comp); -+ return 0; -+} -+ -+static uint32_t pv_img_get_aad_size(const PvImage *img) -+{ -+ uint32_t key_size, size = 0; -+ -+ g_assert(sizeof(struct pv_hdr_head) <= UINT32_MAX); -+ g_assert(sizeof(struct pv_hdr_key_slot) <= UINT32_MAX); -+ -+ g_assert_true(g_uint_checked_add(&size, size, -+ (uint32_t)sizeof(struct pv_hdr_head))); -+ g_assert_true(g_uint_checked_mul(&key_size, -+ (uint32_t)sizeof(struct pv_hdr_key_slot), -+ g_slist_length(img->key_slots))); -+ g_assert_true(g_uint_checked_add(&size, size, key_size)); -+ return size; -+} -+ -+static uint32_t pv_img_get_opt_items_size(const PvImage *img) -+{ -+ uint32_t ret = 0; -+ -+ g_assert(img); -+ -+ for (GSList *iterator = img->optional_items; iterator; -+ iterator = iterator->next) { -+ const struct pv_hdr_opt_item *item = iterator->data; -+ -+ g_assert(item); -+ g_assert_true(g_uint_checked_add(&ret, ret, pv_opt_item_size(item))); -+ } -+ return ret; -+} -+ -+uint32_t pv_img_get_enc_size(const PvImage *img) -+{ -+ uint32_t ret = 0; -+ -+ g_assert(sizeof(struct pv_hdr_encrypted) <= UINT32_MAX); -+ -+ g_assert_true(g_uint_checked_add( -+ &ret, ret, (uint32_t)sizeof(struct pv_hdr_encrypted))); -+ g_assert_true( -+ g_uint_checked_add(&ret, ret, pv_img_get_opt_items_size(img))); -+ return ret; -+} -+ -+static uint32_t pv_img_get_tag_size(const PvImage *img G_GNUC_UNUSED) -+{ -+ g_assert(sizeof(((struct pv_hdr *)0)->tag) <= UINT32_MAX); -+ -+ return (uint32_t)sizeof(((struct pv_hdr *)0)->tag); -+} -+ -+uint32_t pv_img_get_pv_hdr_size(const PvImage *img) -+{ -+ uint32_t size = 0; -+ -+ g_assert_true( -+ g_uint_checked_add(&size, size, pv_img_get_aad_size(img))); -+ g_assert_true( -+ g_uint_checked_add(&size, size, pv_img_get_enc_size(img))); -+ g_assert_true( -+ g_uint_checked_add(&size, size, pv_img_get_tag_size(img))); -+ return size; -+} -+ -+static gint get_stage3a_data_size(const PvImage *img, gsize *data_size, -+ GError **err) -+{ -+ gsize ipib_size, hdr_size; -+ -+ g_assert(data_size); -+ g_assert(*data_size == 0); -+ -+ ipib_size = pv_ipib_get_size(pv_img_comps_length(img->comps)); -+ if (ipib_size > PV_V1_IPIB_MAX_SIZE) { -+ g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE, -+ _("IPIB size is too large: '%zu' > '%zu'"), -+ ipib_size, PV_V1_IPIB_MAX_SIZE); -+ return -1; -+ } -+ -+ hdr_size = pv_img_get_pv_hdr_size(img); -+ if (hdr_size > PV_V1_PV_HDR_MAX_SIZE) { -+ g_set_error(err, PV_ERROR, PV_ERROR_PV_HDR_SIZE, -+ _("PV header size is too large: '%zu' > '%zu'"), -+ hdr_size, PV_V1_PV_HDR_MAX_SIZE); -+ return -1; -+ } -+ -+ *data_size += PAGE_ALIGN(ipib_size); -+ *data_size += PAGE_ALIGN(hdr_size); -+ return 0; -+} -+ -+gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err) -+{ -+ g_autoptr(Buffer) stage3a = NULL; -+ gsize bin_size, data_size = 0; -+ -+ if (get_stage3a_data_size(img, &data_size, err) < 0) -+ return -1; -+ -+ stage3a = stage3a_getblob(path, &bin_size, data_size, err); -+ if (!stage3a) -+ return -1; -+ -+ img->stage3a_psw.addr = STAGE3A_ENTRY; -+ img->stage3a_psw.mask = DEFAULT_INITIAL_PSW_MASK; -+ -+ /* set addresses and size */ -+ img->stage3a = g_steal_pointer(&stage3a); -+ img->stage3a_bin_size = bin_size; -+ return 0; -+} -+ -+/* Creates the PV IPIB and sets the stage3a arguments */ -+static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size, -+ GSList *comps, const Buffer *hdr, GError **err) -+{ -+ g_autofree struct ipl_parameter_block *ipib = NULL; -+ -+ g_assert(stage3a); -+ g_assert(hdr); -+ -+ ipib = pv_ipib_new(comps, hdr, err); -+ if (!ipib) -+ return -1; -+ -+ if (build_stage3a(stage3a, stage3a_bin_size, hdr, ipib, err) < 0) -+ return -1; -+ -+ g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)", "stage3a", -+ STAGE3A_LOAD_ADDRESS, stage3a->size, stage3a->size); -+ return 0; -+} -+ -+/* Creates the actual PV header (serialized and AES-GCM encrypted) */ -+static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err) -+{ -+ g_autoptr(Buffer) hdr_buf = NULL; -+ g_autoptr(PvHdr) hdr = NULL; -+ -+ hdr = pv_hdr_new(img, err); -+ if (!hdr) -+ return NULL; -+ -+ hdr_buf = pv_hdr_serialize(hdr, img, PV_ENCRYPT, err); -+ if (!hdr_buf) -+ return NULL; -+ -+ return g_steal_pointer(&hdr_buf); -+} -+ -+/* No changes to the components are allowed after calling this -+ * function -+ */ -+gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err) -+{ -+ g_autoptr(Buffer) hdr = NULL; -+ -+ /* load stage3b template into memory and add it to the list of -+ * components. This must be done before calling -+ * `pv_img_load_and_set_stage3a`. -+ */ -+ if (pv_img_add_stage3b_comp(pv, stage3b_path, err) < 0) -+ return -1; -+ -+ /* create the PV header */ -+ hdr = pv_img_create_pv_hdr(pv, err); -+ if (!hdr) -+ return -1; -+ -+ /* generate stage3a. At this point in time the PV header and -+ * the stage3b must be generated and encrypted -+ */ -+ if (pv_img_build_stage3a(pv->stage3a, pv->stage3a_bin_size, -+ pv_img_comps_get_comps(pv->comps), hdr, err) < 0) -+ return -1; -+ -+ return 0; -+} -+ -+static gint convert_psw_to_short_psw(const struct psw_t *psw, uint64_t *dst, -+ GError **err) -+{ -+ g_assert(psw); -+ g_assert(dst); -+ -+ uint64_t psw_addr = psw->addr; -+ uint64_t psw_mask = psw->mask; -+ -+ /* test if PSW mask can be converted */ -+ if (psw_mask & PSW32_ADDR_MASK) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Failed to convert PSW to short PSW")); -+ return -1; -+ } -+ -+ /* test for bit 12 */ -+ if (psw_mask & PSW_MASK_BIT_12) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Failed to convert PSW to short PSW")); -+ return -1; -+ } -+ -+ /* test if PSW addr can be converted */ -+ if (psw_addr & ~PSW32_ADDR_MASK) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Failed to convert PSW to short PSW")); -+ return -1; -+ } -+ -+ *dst = psw_mask; -+ /* set bit 12 to 1 */ -+ *dst |= PSW_MASK_BIT_12; -+ *dst |= psw_addr; -+ return 0; -+} -+ -+static gint write_short_psw(FILE *f, struct psw_t *psw, GError **err) -+{ -+ uint64_t short_psw, short_psw_be; -+ -+ if (convert_psw_to_short_psw(psw, &short_psw, err) < 0) -+ return -1; -+ -+ short_psw_be = GUINT64_TO_BE(short_psw); -+ return file_write(f, &short_psw_be, 1, sizeof(short_psw_be), NULL, err); -+} -+ -+gint pv_img_write(PvImage *img, const gchar *path, GError **err) -+{ -+ gint ret = -1; -+ FILE *f = file_open(path, "wb", err); -+ -+ if (!f) -+ return -1; -+ -+ if (write_short_psw(f, &img->stage3a_psw, err) < 0) { -+ g_prefix_error(err, _("Failed to write image '%s': "), path); -+ goto err; -+ } -+ -+ if (seek_and_write_buffer(f, img->stage3a, STAGE3A_LOAD_ADDRESS, err) < -+ 0) { -+ g_prefix_error(err, _("Failed to write image '%s': "), path); -+ goto err; -+ } -+ -+ /* list is sorted by component type => by address */ -+ for (GSList *iterator = pv_img_comps_get_comps(img->comps); iterator; -+ iterator = iterator->next) { -+ gint rc; -+ const PvComponent *comp = iterator->data; -+ -+ rc = pv_component_write(comp, f, err); -+ if (rc < 0) { -+ g_prefix_error(err, _("Failed to write image '%s': "), -+ path); -+ goto err; -+ } -+ } -+ -+ ret = 0; -+err: -+ if (f) -+ fclose(f); -+ return ret; -+} -diff --git a/genprotimg/src/pv/pv_image.h b/genprotimg/src/pv/pv_image.h -new file mode 100644 -index 0000000..7c624e2 ---- /dev/null -+++ b/genprotimg/src/pv/pv_image.h -@@ -0,0 +1,68 @@ -+/* -+ * PV image related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_IMAGE_H -+#define PV_IMAGE_H -+ -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "utils/buffer.h" -+ -+#include "pv_args.h" -+#include "pv_comp.h" -+#include "pv_comps.h" -+#include "pv_stage3.h" -+ -+typedef struct { -+ gchar *tmp_dir; /* directory used for temporary files */ -+ Buffer *stage3a; /* stage3a containing IPIB and PV header */ -+ gsize stage3a_bin_size; /* size of stage3a.bin */ -+ struct psw_t stage3a_psw; /* (short) PSW that is written to -+ * location 0 of the created image -+ */ -+ struct psw_t initial_psw; /* PSW loaded by stage3b */ -+ EVP_PKEY *cust_pub_priv_key; /* customer private/public key */ -+ GSList *host_pub_keys; /* public host keys */ -+ gint nid; /* Elliptic Curve used for the key derivation */ -+ /* keys and cipher used for the AES-GCM encryption */ -+ Buffer *cust_root_key; -+ Buffer *gcm_iv; -+ const EVP_CIPHER *gcm_cipher; -+ /* Information for the IPIB and PV header */ -+ uint64_t pcf; -+ uint64_t scf; -+ Buffer *cust_comm_key; -+ const EVP_CIPHER *cust_comm_cipher; -+ Buffer *xts_key; -+ const EVP_CIPHER *xts_cipher; -+ GSList *key_slots; -+ GSList *optional_items; -+ PvImgComps *comps; -+} PvImage; -+ -+PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err); -+void pv_img_free(PvImage *img); -+gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err); -+gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err); -+gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald, -+ Buffer **tld, uint64_t *nep, GError **err); -+gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err); -+const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err); -+gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err); -+uint32_t pv_img_get_enc_size(const PvImage *img); -+uint32_t pv_img_get_pv_hdr_size(const PvImage *img); -+gint pv_img_write(PvImage *img, const gchar *path, GError **err); -+ -+G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImage, pv_img_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_ipib.c b/genprotimg/src/pv/pv_ipib.c -new file mode 100644 -index 0000000..2517e54 ---- /dev/null -+++ b/genprotimg/src/pv/pv_ipib.c -@@ -0,0 +1,128 @@ -+/* -+ * PV IPIB related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "boot/ipl.h" -+#include "boot/s390.h" -+#include "common.h" -+#include "include/pv_hdr_def.h" -+#include "lib/zt_common.h" -+#include "utils/align.h" -+#include "utils/buffer.h" -+ -+#include "pv_comp.h" -+#include "pv_error.h" -+#include "pv_ipib.h" -+ -+uint64_t pv_ipib_get_size(uint32_t num_comp) -+{ -+ gsize ipib_size = sizeof(struct ipl_pl_hdr) + -+ sizeof(struct ipl_pb0_pv) + -+ num_comp * sizeof(struct ipl_pb0_pv_comp); -+ -+ /* the minimal size is one page */ -+ return MAX(ipib_size, PAGE_SIZE); -+} -+ -+static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps, -+ const Buffer *hdr) -+{ -+ g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX); -+ g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX); -+ g_assert(sizeof(struct ipl_pb0_pv) <= UINT32_MAX); -+ g_assert(ipib); -+ -+ guint comps_length = g_slist_length(comps); -+ uint32_t ipl_pl_hdr_size = (uint32_t)sizeof(struct ipl_pl_hdr); -+ struct ipl_pb0_pv *pv = &ipib->pv; -+ uint32_t ipib_comps_size; -+ uint32_t blk0_len; -+ uint32_t ipib_size; -+ gsize i; -+ -+ g_assert_true( -+ g_uint_checked_mul(&ipib_comps_size, comps_length, -+ (uint32_t)sizeof(struct ipl_pb0_pv_comp))); -+ g_assert_true(g_uint_checked_add(&blk0_len, (uint32_t)sizeof(*pv), -+ ipib_comps_size)); -+ g_assert(ipl_pl_hdr_size + blk0_len <= PAGE_SIZE); -+ -+ ipib_size = MAX(ipl_pl_hdr_size + blk0_len, (uint32_t)PAGE_SIZE); -+ g_assert(pv_ipib_get_size(comps_length) == ipib_size); -+ -+ pv->pbt = IPL_TYPE_PV; -+ pv->len = GUINT32_TO_BE(blk0_len); -+ pv->num_comp = GUINT32_TO_BE(comps_length); -+ /* both values will be overwritten during the IPL process by -+ * the stage3a loader -+ */ -+ pv->pv_hdr_addr = GUINT64_TO_BE(0x0); -+ pv->pv_hdr_size = GUINT64_TO_BE(hdr->size); -+ -+ ipib->hdr.len = GUINT32_TO_BE(ipib_size); -+ ipib->hdr.version = IPL_PARM_BLOCK_VERSION; -+ -+ i = 0; -+ for (GSList *iterator = comps; iterator; iterator = iterator->next, i++) { -+ const PvComponent *comp = iterator->data; -+ uint64_t comp_addr, comp_size; -+ -+ g_assert(comp); -+ -+ comp_addr = pv_component_get_src_addr(comp); -+ comp_size = pv_component_size(comp); -+ -+ g_assert(IS_PAGE_ALIGNED(comp_size)); -+ -+ pv->components[i].addr = GUINT64_TO_BE(comp_addr); -+ pv->components[i].len = GUINT64_TO_BE(comp_size); -+ pv->components[i].tweak_pref = -+ GUINT64_TO_BE(pv_component_get_tweak_prefix(comp)); -+ if (i > 0) { -+ /* tweak prefixes of the components must grow -+ * strictly monotonous -+ */ -+ g_assert(GUINT64_FROM_BE(pv->components[i].tweak_pref) > -+ GUINT64_FROM_BE(pv->components[i - 1].tweak_pref)); -+ } -+ } -+ -+ return 0; -+} -+ -+IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err) -+{ -+ uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps)); -+ g_autoptr(IplParameterBlock) ret = NULL; -+ -+ if (ipib_size > PV_V1_IPIB_MAX_SIZE) { -+ g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE, -+ _("IPIB size is too large: %lu < %lu"), ipib_size, -+ PAGE_SIZE); -+ return NULL; -+ } -+ -+ ret = g_malloc0(ipib_size); -+ if (pv_ipib_init(ret, comps, hdr) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+void pv_ipib_free(IplParameterBlock *ipib) -+{ -+ if (!ipib) -+ return; -+ -+ g_free(ipib); -+} -diff --git a/genprotimg/src/pv/pv_ipib.h b/genprotimg/src/pv/pv_ipib.h -new file mode 100644 -index 0000000..9331790 ---- /dev/null -+++ b/genprotimg/src/pv/pv_ipib.h -@@ -0,0 +1,27 @@ -+/* -+ * PV IPIB related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_IPIB_H -+#define PV_IPIB_H -+ -+#include -+#include -+ -+#include "boot/ipl.h" -+#include "utils/buffer.h" -+ -+typedef struct ipl_parameter_block IplParameterBlock; -+ -+uint64_t pv_ipib_get_size(uint32_t num_comp); -+IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err); -+void pv_ipib_free(IplParameterBlock *ipib); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free) -+ -+#endif -diff --git a/genprotimg/src/pv/pv_opt_item.c b/genprotimg/src/pv/pv_opt_item.c -new file mode 100644 -index 0000000..39b9a8d ---- /dev/null -+++ b/genprotimg/src/pv/pv_opt_item.c -@@ -0,0 +1,26 @@ -+/* -+ * PV optional item related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+ -+#include "pv_opt_item.h" -+ -+uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item G_GNUC_UNUSED) -+{ -+ /* not implemented yet */ -+ g_assert_not_reached(); -+} -+ -+void pv_opt_item_free(struct pv_hdr_opt_item *item) -+{ -+ if (!item) -+ return; -+ -+ g_free(item); -+} -diff --git a/genprotimg/src/pv/pv_opt_item.h b/genprotimg/src/pv/pv_opt_item.h -new file mode 100644 -index 0000000..34bb0a9 ---- /dev/null -+++ b/genprotimg/src/pv/pv_opt_item.h -@@ -0,0 +1,20 @@ -+/* -+ * PV optional item related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_OPT_ITEM_H -+#define PV_OPT_ITEM_H -+ -+#include -+ -+#include "include/pv_hdr_def.h" -+ -+uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item); -+void pv_opt_item_free(struct pv_hdr_opt_item *item); -+ -+#endif -diff --git a/genprotimg/src/pv/pv_stage3.c b/genprotimg/src/pv/pv_stage3.c -new file mode 100644 -index 0000000..a1e5b16 ---- /dev/null -+++ b/genprotimg/src/pv/pv_stage3.c -@@ -0,0 +1,164 @@ -+/* -+ * PV stage3 loader related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+ -+#include "boot/ipl.h" -+#include "boot/stage3a.h" -+#include "boot/stage3b.h" -+#include "common.h" -+#include "utils/align.h" -+ -+#include "pv_error.h" -+#include "pv_stage3.h" -+ -+#define STAGE3A_ARGS(data_ptr, loader_size) \ -+ ((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \ -+ sizeof(struct stage3a_args))) -+ -+static Buffer *loader_getblob(const gchar *filename, gsize *loader_size, -+ gsize args_size, gsize data_size, -+ gboolean data_aligned, GError **err) -+{ -+ g_autoptr(GMappedFile) mapped_file = NULL; -+ g_autoptr(Buffer) ret = NULL; -+ gsize size, tmp_loader_size; -+ gchar *loader_data; -+ -+ g_assert(loader_size); -+ -+ mapped_file = g_mapped_file_new(filename, FALSE, err); -+ if (!mapped_file) -+ return NULL; -+ -+ loader_data = g_mapped_file_get_contents(mapped_file); -+ if (!loader_data) { -+ g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF, -+ _("File '%s' is empty"), filename); -+ return NULL; -+ } -+ tmp_loader_size = g_mapped_file_get_length(mapped_file); -+ -+ if (tmp_loader_size < args_size) { -+ g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF, -+ _("File size less than expected: %lu < %ln"), -+ tmp_loader_size, loader_size); -+ return NULL; -+ } -+ -+ /* For example, the PV header and IPIB data must be page -+ * aligned. -+ */ -+ size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) + -+ data_size; -+ -+ ret = buffer_alloc(size); -+ -+ /* copy the loader "template" */ -+ memcpy(ret->data, loader_data, tmp_loader_size); -+ /* reset our dummy data (offsets and length) to zeros */ -+ memset((uint8_t *)ret->data + tmp_loader_size - args_size, 0, -+ args_size); -+ *loader_size = tmp_loader_size; -+ return g_steal_pointer(&ret); -+} -+ -+Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size, -+ gsize data_size, GError **err) -+{ -+ return loader_getblob(filename, loader_size, -+ sizeof(struct stage3a_args), data_size, TRUE, -+ err); -+} -+ -+/* For the memory layout see stage3a.lds */ -+/* Set the right offsets and sizes in the stage3a template + add -+ * the IPIB block with the PV header -+ */ -+static gint stage3a_set_data(Buffer *loader, gsize loader_size, -+ const Buffer *hdr, struct ipl_parameter_block *ipib, -+ GError **err) -+{ -+ uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len); -+ gsize args_size = sizeof(struct stage3a_args); -+ uint32_t hdr_size = (uint32_t)hdr->size; -+ uint64_t args_addr, next_data_addr; -+ -+ if (hdr->size > UINT32_MAX) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("Invalid header size: %zu"), hdr->size); -+ return -1; -+ } -+ -+ /* we assume here that the loader ``stage3a`` is loaded page -+ * aligned in the guest -+ */ -+ args_addr = (uint64_t)loader->data + loader_size - args_size; -+ -+ /* therefore `next_data_addr` is also page aligned */ -+ next_data_addr = (uint64_t)loader->data + PAGE_ALIGN(loader_size); -+ -+ /* copy IPIB data */ -+ memcpy((void *)next_data_addr, ipib, ipib_size); -+ -+ /* set IPIB offset in relation to the stage3a arguments */ -+ STAGE3A_ARGS(loader->data, loader_size)->ipib_offs = -+ GUINT64_TO_BE(next_data_addr - args_addr); -+ -+ next_data_addr = next_data_addr + PAGE_ALIGN(ipib_size); -+ /* copy PV header */ -+ memcpy((void *)next_data_addr, hdr->data, hdr_size); -+ /* set PV header size and offset in relation to the stage3a -+ * arguments -+ */ -+ STAGE3A_ARGS(loader->data, loader_size)->hdr_offs = -+ GUINT64_TO_BE(next_data_addr - args_addr); -+ STAGE3A_ARGS(loader->data, loader_size)->hdr_size = GUINT64_TO_BE(hdr_size); -+ -+ return 0; -+} -+ -+gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr, -+ struct ipl_parameter_block *ipib, GError **err) -+{ -+ return stage3a_set_data(loader, loader_size, hdr, ipib, err); -+} -+ -+Buffer *stage3b_getblob(const gchar *filename, GError **err) -+{ -+ g_autoptr(Buffer) ret = NULL; -+ gsize rb_size; -+ -+ ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0, -+ FALSE, err); -+ if (!ret) -+ return NULL; -+ -+ g_assert(ret->size == rb_size); -+ return g_steal_pointer(&ret); -+} -+ -+void build_stage3b(Buffer *stage3b, const struct stage3b_args *args) -+{ -+ g_assert(stage3b->size > sizeof(*args)); -+ -+ /* at the end of the stage3b there are the stage3b args -+ * positioned -+ */ -+ memcpy((uint8_t *)stage3b->data + stage3b->size - sizeof(*args), args, -+ sizeof(*args)); -+} -+ -+void memblob_init(struct memblob *arg, uint64_t src, uint64_t size) -+{ -+ arg->src = GUINT64_TO_BE(src); -+ arg->size = GUINT64_TO_BE(size); -+} -diff --git a/genprotimg/src/pv/pv_stage3.h b/genprotimg/src/pv/pv_stage3.h -new file mode 100644 -index 0000000..baaf921 ---- /dev/null -+++ b/genprotimg/src/pv/pv_stage3.h -@@ -0,0 +1,30 @@ -+/* -+ * PV stage3 loader related definitions and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_STAGE3_H -+#define PV_STAGE3_H -+ -+#include -+#include -+#include -+ -+#include "boot/ipl.h" -+#include "boot/s390.h" -+#include "boot/stage3b.h" -+#include "utils/buffer.h" -+ -+Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size, -+ gsize data_size, GError **err); -+gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr, -+ struct ipl_parameter_block *ipib, GError **err); -+Buffer *stage3b_getblob(const gchar *filename, GError **err); -+void build_stage3b(Buffer *stage3b, const struct stage3b_args *args); -+void memblob_init(struct memblob *arg, uint64_t src, uint64_t size); -+ -+#endif -diff --git a/genprotimg/src/utils/align.h b/genprotimg/src/utils/align.h -new file mode 100644 -index 0000000..fa19e58 ---- /dev/null -+++ b/genprotimg/src/utils/align.h -@@ -0,0 +1,24 @@ -+/* -+ * Alignment utils -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_UTILS_ALIGN_H -+#define PV_UTILS_ALIGN_H -+ -+#include "boot/s390.h" -+#include "lib/zt_common.h" -+ -+#define IS_ALIGNED(addr, size) (!(addr & (size - 1))) -+ -+/* align addr to the next page boundary */ -+#define PAGE_ALIGN(addr) ALIGN((unsigned long)addr, PAGE_SIZE) -+ -+/* test whether an address is aligned to PAGE_SIZE or not */ -+#define IS_PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)(addr), PAGE_SIZE) -+ -+#endif -diff --git a/genprotimg/src/utils/buffer.c b/genprotimg/src/utils/buffer.c -new file mode 100644 -index 0000000..35aed74 ---- /dev/null -+++ b/genprotimg/src/utils/buffer.c -@@ -0,0 +1,69 @@ -+/* -+ * Buffer functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "align.h" -+#include "buffer.h" -+#include "common.h" -+#include "file_utils.h" -+ -+Buffer *buffer_alloc(gsize size) -+{ -+ Buffer *ret = g_new0(Buffer, 1); -+ -+ ret->data = g_malloc0(size); -+ ret->size = size; -+ return ret; -+} -+ -+Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned) -+{ -+ Buffer *ret; -+ gsize size; -+ -+ if (!buf) -+ return NULL; -+ -+ size = buf->size; -+ if (page_aligned) -+ size = PAGE_ALIGN(size); -+ -+ ret = buffer_alloc(size); -+ -+ /* content will be 0-right-padded */ -+ memcpy(ret->data, buf->data, buf->size); -+ return ret; -+} -+ -+gint buffer_write(const Buffer *buf, FILE *file, GError **err) -+{ -+ return file_write(file, buf->data, buf->size, 1, NULL, err); -+} -+ -+void buffer_free(Buffer *buf) -+{ -+ if (!buf) -+ return; -+ -+ g_free(buf->data); -+ g_free(buf); -+} -+ -+void buffer_clear(Buffer **buf) -+{ -+ if (!buf || !*buf) -+ return; -+ -+ buffer_free(*buf); -+ *buf = NULL; -+} -diff --git a/genprotimg/src/utils/buffer.h b/genprotimg/src/utils/buffer.h -new file mode 100644 -index 0000000..7239d5c ---- /dev/null -+++ b/genprotimg/src/utils/buffer.h -@@ -0,0 +1,31 @@ -+/* -+ * Buffer definition and functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_UTILS_BUFFER_H -+#define PV_UTILS_BUFFER_H -+ -+#include -+#include -+ -+#include "common.h" -+ -+typedef struct Buffer { -+ void *data; -+ gsize size; /* in bytes */ -+} Buffer; -+ -+Buffer *buffer_alloc(gsize size); -+void buffer_free(Buffer *buf); -+void buffer_clear(Buffer **buf); -+gint buffer_write(const Buffer *buf, FILE *file, GError **err); -+Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned); -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free) -+ -+#endif -diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c -new file mode 100644 -index 0000000..b0c4aa9 ---- /dev/null -+++ b/genprotimg/src/utils/crypto.c -@@ -0,0 +1,798 @@ -+/* -+ * General cryptography helper functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "boot/s390.h" -+#include "common.h" -+#include "include/pv_crypto_def.h" -+#include "pv/pv_error.h" -+ -+#include "buffer.h" -+#include "crypto.h" -+ -+EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) -+{ -+ g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); -+ -+ if (!ctx) -+ g_abort(); -+ -+ if (EVP_DigestInit_ex(ctx, md, NULL) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestInit_ex failed")); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ctx); -+} -+ -+Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err) -+{ -+ gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx)); -+ g_autoptr(Buffer) ret = NULL; -+ guint digest_size; -+ -+ g_assert(md_size > 0); -+ -+ ret = buffer_alloc((guint)md_size); -+ if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestFinal_ex failed")); -+ return NULL; -+ } -+ -+ g_assert(digest_size == (guint)md_size); -+ g_assert(digest_size == ret->size); -+ return g_steal_pointer(&ret); -+} -+ -+/* Returns the digest of @buf using the hash algorithm @md */ -+static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err) -+{ -+ g_autoptr(EVP_MD_CTX) md_ctx = NULL; -+ g_autoptr(Buffer) ret = NULL; -+ g_assert(buf); -+ -+ md_ctx = digest_ctx_new(md, err); -+ if (!md_ctx) -+ return NULL; -+ -+ if (EVP_DigestUpdate(md_ctx, buf->data, buf->size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_DigestUpdate failed")); -+ return NULL; -+ } -+ -+ ret = digest_ctx_finalize(md_ctx, err); -+ if (!ret) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+/* Returns the SHA256 digest of @buf */ -+Buffer *sha256_buffer(const Buffer *buf, GError **err) -+{ -+ g_autoptr(Buffer) ret = NULL; -+ -+ ret = digest_buffer(EVP_sha256(), buf, err); -+ if (!ret) -+ return NULL; -+ -+ g_assert(ret->size == SHA256_DIGEST_LENGTH); -+ return g_steal_pointer(&ret); -+} -+ -+/* Convert a EVP_PKEY to the key format used in the PV header */ -+union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err) -+{ -+ g_autofree union ecdh_pub_key *ret = g_new0(union ecdh_pub_key, 1); -+ g_autoptr(BIGNUM) pub_x_big = NULL; -+ g_autoptr(BIGNUM) pub_y_big = NULL; -+ g_autoptr(EC_KEY) ec_key = NULL; -+ const EC_POINT *pub_key; -+ const EC_GROUP *grp; -+ -+ ec_key = EVP_PKEY_get1_EC_KEY(key); -+ if (!ec_key) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Key has the wrong type")); -+ return NULL; -+ } -+ -+ pub_key = EC_KEY_get0_public_key(ec_key); -+ if (!pub_key) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to get public key")); -+ return NULL; -+ } -+ -+ grp = EC_KEY_get0_group(ec_key); -+ if (!grp) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to get EC group")); -+ return NULL; -+ } -+ -+ pub_x_big = BN_new(); -+ if (!pub_x_big) -+ g_abort(); -+ -+ pub_y_big = BN_new(); -+ if (!pub_y_big) -+ g_abort(); -+ -+ if (EC_POINT_get_affine_coordinates_GFp(grp, pub_key, pub_x_big, -+ pub_y_big, NULL) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Cannot convert key to internal format")); -+ return NULL; -+ } -+ -+ if (BN_bn2binpad(pub_x_big, ret->x, sizeof(ret->x)) < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Cannot convert key to internal format")); -+ return NULL; -+ } -+ -+ if (BN_bn2binpad(pub_y_big, ret->y, sizeof(ret->y)) < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Cannot convert key to internal format")); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) -+{ -+ g_autoptr(EVP_PKEY_CTX) ctx = NULL; -+ g_autoptr(Buffer) ret = NULL; -+ gsize key_size; -+ -+ ctx = EVP_PKEY_CTX_new(cust, NULL); -+ if (!ctx) -+ g_abort(); -+ -+ if (EVP_PKEY_derive_init(ctx) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Key derivation failed")); -+ return NULL; -+ } -+ -+ if (EVP_PKEY_derive_set_peer(ctx, host) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Key derivation failed")); -+ return NULL; -+ } -+ -+ /* Determine buffer length */ -+ if (EVP_PKEY_derive(ctx, NULL, &key_size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, -+ _("Key derivation failed")); -+ return NULL; -+ } -+ -+ ret = buffer_alloc(key_size); -+ if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, -+ _("Key derivation failed")); -+ return NULL; -+ } -+ -+ g_assert(ret->size == key_size); -+ return g_steal_pointer(&ret); -+} -+ -+Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) -+{ -+ g_autoptr(Buffer) raw = buffer_alloc(70); -+ g_autoptr(Buffer) ret = NULL; -+ g_autoptr(Buffer) key = NULL; -+ guchar *data; -+ -+ key = derive_key(cust, host, err); -+ if (!key) -+ return NULL; -+ -+ g_assert(key->size == 66); -+ g_assert(key->size < raw->size); -+ -+ /* ANSI X.9.63-2011: 66 bytes x with leading 7 bits and -+ * concatenate 32 bit int '1' -+ */ -+ memcpy(raw->data, key->data, key->size); -+ data = raw->data; -+ data[66] = 0x00; -+ data[67] = 0x00; -+ data[68] = 0x00; -+ data[69] = 0x01; -+ -+ ret = sha256_buffer(raw, err); -+ if (!ret) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+gint generate_tweak(union tweak *tweak, uint16_t i, GError **err) -+{ -+ tweak->cmp_idx.idx = GUINT16_TO_BE(i); -+ if (RAND_bytes(tweak->cmp_idx.rand, sizeof(tweak->cmp_idx.rand)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION, -+ _("Generating a tweak failed because the required amount of random data is not available")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static Buffer *generate_rand_data(guint size, const gchar *err_msg, -+ GError **err) -+{ -+ g_autoptr(Buffer) buf = buffer_alloc(size); -+ -+ g_assert(size <= INT_MAX); -+ -+ if (RAND_bytes(buf->data, (int)size) != 1) { -+ g_set_error_literal(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_RANDOMIZATION, -+ err_msg); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&buf); -+} -+ -+Buffer *generate_aes_iv(guint size, GError **err) -+{ -+ return generate_rand_data(size, -+ _("Generating a IV failed because the required amount of random data is not available"), -+ err); -+} -+ -+Buffer *generate_aes_key(guint size, GError **err) -+{ -+ return generate_rand_data(size, -+ _("Generating a key failed because the required amount of random data is not available"), -+ err); -+} -+ -+EVP_PKEY *generate_ec_key(gint nid, GError **err) -+{ -+ g_autoptr(EVP_PKEY_CTX) ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); -+ g_autoptr(EVP_PKEY) ret = NULL; -+ -+ if (!ctx) -+ g_abort(); -+ -+ if (EVP_PKEY_keygen_init(ctx) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, -+ _("EC key could not be auto-generated")); -+ return NULL; -+ } -+ -+ if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, -+ _("EC key could not be auto-generated")); -+ return NULL; -+ } -+ -+ if (EVP_PKEY_keygen(ctx, &ret) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, -+ _("EC key could not be auto-generated")); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+static gboolean certificate_uses_correct_curve(EVP_PKEY *key, gint nid, -+ GError **err) -+{ -+ g_autoptr(EC_KEY) ec = NULL; -+ gint rc; -+ -+ g_assert(key); -+ -+ if (EVP_PKEY_id(key) != EVP_PKEY_EC) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, -+ _("No EC key found")); -+ return FALSE; -+ } -+ -+ ec = EVP_PKEY_get1_EC_KEY(key); -+ if (!ec) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, -+ _("No EC key found")); -+ return FALSE; -+ } -+ -+ if (EC_KEY_check_key(ec) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, -+ _("Invalid EC key")); -+ return FALSE; -+ } -+ -+ rc = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); -+ if (rc != nid) { -+ /* maybe the NID is unset */ -+ if (rc == 0) { -+ g_autoptr(EC_GROUP) grp = EC_GROUP_new_by_curve_name(nid); -+ const EC_POINT *pub = EC_KEY_get0_public_key(ec); -+ g_autoptr(BN_CTX) ctx = BN_CTX_new(); -+ -+ if (EC_POINT_is_on_curve(grp, pub, ctx) != 1) { -+ g_set_error_literal(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INVALID_PARM, -+ _("Invalid EC curve")); -+ return FALSE; -+ } -+ } else { -+ /* NID was set but doesn't match with the expected NID -+ */ -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INVALID_PARM, -+ _("Wrong NID used: '%d'"), -+ EC_GROUP_get_curve_name(EC_KEY_get0_group(ec))); -+ return FALSE; -+ } -+ } -+ -+ return TRUE; -+} -+ -+static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err) -+{ -+ g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new(); -+ if (!csc) -+ g_abort(); -+ -+ if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT, -+ _("Failed to initialize X.509 store")); -+ return FALSE; -+ } -+ -+ if (X509_verify_cert(csc) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, -+ _("Failed to verify host-key document")); -+ return FALSE; -+ } -+ -+ return TRUE; -+} -+ -+static X509 *load_certificate(const gchar *path, GError **err) -+{ -+ g_autoptr(X509) ret = NULL; -+ g_autoptr(BIO) bio = BIO_new_file(path, "rb"); -+ -+ if (!bio) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("Failed to read host-key document: '%s'"), path); -+ return NULL; -+ } -+ -+ ret = PEM_read_bio_X509(bio, NULL, 0, NULL); -+ if (!ret) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("Failed to load host-key document: '%s'"), path); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, -+ GError **err) -+{ -+ g_autoptr(EVP_PKEY) ret = NULL; -+ g_autoptr(X509) cert = NULL; -+ -+ cert = load_certificate(path, err); -+ if (!cert) -+ return NULL; -+ -+ if (store && !verify_certificate(store, cert, err)) { -+ g_prefix_error(err, -+ _("Failed to load host-key document: '%s': "), -+ path); -+ return NULL; -+ } -+ -+ ret = X509_get_pubkey(cert); -+ if (!ret) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, -+ _("Failed to get public key from host-key document: '%s'"), -+ path); -+ return NULL; -+ } -+ -+ if (!certificate_uses_correct_curve(ret, nid, err)) { -+ g_prefix_error(err, -+ _("Failed to load host-key document: '%s': "), -+ path); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in, -+ BIO *b_out, gsize *size_in, gsize *size_out, -+ gboolean encrypt, GError **err) -+{ -+ gint num_bytes_read, num_bytes_written; -+ g_autoptr(EVP_CIPHER_CTX) ctx = NULL; -+ g_autoptr(BIGNUM) tweak_num = NULL; -+ const EVP_CIPHER *cipher = parms->cipher; -+ gint cipher_block_size = EVP_CIPHER_block_size(cipher); -+ guchar in_buf[PAGE_SIZE], -+ out_buf[PAGE_SIZE + (guint)cipher_block_size]; -+ const Buffer *key = parms->key; -+ const Buffer *tweak = parms->iv_or_tweak; -+ g_autofree guchar *tmp_tweak = NULL; -+ gint out_len, tweak_size; -+ gsize tmp_size_in = 0, tmp_size_out = 0; -+ -+ g_assert(cipher_block_size > 0); -+ g_assert(key); -+ g_assert(tweak); -+ g_assert(tweak->size <= INT_MAX); -+ -+ /* copy the value for leaving the original value untouched */ -+ tmp_tweak = g_malloc0(tweak->size); -+ memcpy(tmp_tweak, tweak->data, tweak->size); -+ tweak_size = (int)tweak->size; -+ tweak_num = BN_bin2bn(tmp_tweak, tweak_size, NULL); -+ if (!tweak_num) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_bin2bn failed")); -+ return -1; -+ } -+ -+ ctx = EVP_CIPHER_CTX_new(); -+ if (!ctx) -+ g_abort(); -+ -+ /* don't set the key or tweak right away as we want to check -+ * lengths before -+ */ -+ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherInit_ex failed")); -+ return -1; -+ } -+ -+ /* Now we can set the key and tweak */ -+ if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, tmp_tweak, encrypt) != -+ 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherInit_ex failed")); -+ return -1; -+ } -+ -+ do { -+ memset(in_buf, 0, sizeof(in_buf)); -+ /* Read in data in 4096 bytes blocks. Update the ciphering -+ * with each read. -+ */ -+ num_bytes_read = BIO_read(b_in, in_buf, (int)PAGE_SIZE); -+ if (num_bytes_read < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to read")); -+ return -1; -+ } -+ tmp_size_in += (guint)num_bytes_read; -+ -+ /* in case we reached the end and it's not the special -+ * case of an empty component we can break here -+ */ -+ if (num_bytes_read == 0 && tmp_size_in != 0) -+ break; -+ -+ if (EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf, -+ sizeof(in_buf)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherUpdate failed")); -+ return -1; -+ } -+ g_assert(out_len >= 0); -+ -+ num_bytes_written = BIO_write(b_out, out_buf, out_len); -+ if (num_bytes_written < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to write")); -+ return -1; -+ } -+ g_assert(num_bytes_written == out_len); -+ -+ tmp_size_out += (guint)num_bytes_written; -+ -+ /* Set new tweak value. Please keep in mind that the -+ * tweaks are stored in big-endian form. Therefore we -+ * must use the correct OpenSSL functions -+ */ -+ if (BN_add_word(tweak_num, PAGE_SIZE) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_add_word failed")); -+ } -+ g_assert(BN_num_bytes(tweak_num) > 0); -+ g_assert(BN_num_bytes(tweak_num) <= tweak_size); -+ -+ if (BN_bn2binpad(tweak_num, tmp_tweak, tweak_size) < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("BN_bn2binpad failed")); -+ }; -+ -+ /* set new tweak */ -+ if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, tmp_tweak, -+ encrypt) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherInit_ex failed")); -+ return -1; -+ } -+ } while (num_bytes_read == PAGE_SIZE); -+ -+ /* Now cipher the final block and write it out to file */ -+ if (EVP_CipherFinal_ex(ctx, out_buf, &out_len) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherFinal_ex failed")); -+ return -1; -+ } -+ g_assert(out_len >= 0); -+ -+ num_bytes_written = BIO_write(b_out, out_buf, out_len); -+ if (num_bytes_written < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to write")); -+ return -1; -+ } -+ g_assert(out_len == num_bytes_written); -+ tmp_size_out += (guint)out_len; -+ -+ if (BIO_flush(b_out) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Failed to flush")); -+ return -1; -+ } -+ -+ *size_in = tmp_size_in; -+ *size_out = tmp_size_out; -+ return 0; -+} -+ -+static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms, -+ const Buffer *in, gboolean encrypt, -+ GError **err) -+{ -+ g_autoptr(Buffer) ret = NULL; -+ g_autoptr(BIO) b_out = NULL; -+ g_autoptr(BIO) b_in = NULL; -+ gsize in_size, out_size; -+ gchar *data = NULL; -+ long data_size; -+ -+ g_assert(in->size <= INT_MAX); -+ -+ b_in = BIO_new_mem_buf(in->data, (int)in->size); -+ if (!b_in) -+ g_abort(); -+ -+ b_out = BIO_new(BIO_s_mem()); -+ if (!b_out) -+ g_abort(); -+ -+ if (__encrypt_decrypt_bio(parms, b_in, b_out, &in_size, &out_size, -+ encrypt, err) < 0) -+ return NULL; -+ -+ data_size = BIO_get_mem_data(b_out, &data); -+ if (data_size < 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("Could not read buffer")); -+ return NULL; -+ } -+ -+ ret = buffer_alloc((unsigned long)data_size); -+ memcpy(ret->data, data, ret->size); -+ return g_steal_pointer(&ret); -+} -+ -+Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in, -+ GError **err) -+{ -+ return __encrypt_decrypt_buffer(parms, in, TRUE, err); -+} -+ -+Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in, -+ GError **err) -+{ -+ return __encrypt_decrypt_buffer(parms, in, FALSE, err); -+} -+ -+static gint __encrypt_decrypt_file(const struct cipher_parms *parms, -+ const gchar *path_in, const gchar *path_out, -+ gsize *size_in, gsize *size_out, gboolean encrypt, -+ GError **err) -+{ -+ g_autoptr(BIO) b_out = NULL; -+ g_autoptr(BIO) b_in = NULL; -+ -+ b_in = BIO_new_file(path_in, "rb"); -+ if (!b_in) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("Failed to read file '%s'"), path_in); -+ return -1; -+ } -+ -+ b_out = BIO_new_file(path_out, "wb"); -+ if (!b_out) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("Failed to write file '%s'"), path_out); -+ return -1; -+ } -+ -+ if (__encrypt_decrypt_bio(parms, b_in, b_out, size_in, size_out, -+ encrypt, err) < 0) -+ return -1; -+ -+ return 0; -+} -+ -+gint encrypt_file(const struct cipher_parms *parms, const gchar *path_in, -+ const gchar *path_out, gsize *in_size, gsize *out_size, -+ GError **err) -+{ -+ return __encrypt_decrypt_file(parms, path_in, path_out, in_size, -+ out_size, TRUE, err); -+} -+ -+G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms, -+ const gchar *path_in, const gchar *path_out, -+ gsize *in_size, gsize *out_size, -+ GError **err) -+{ -+ return __encrypt_decrypt_file(parms, path_in, path_out, in_size, -+ out_size, FALSE, err); -+} -+ -+/* GCM mode uses (zero-)padding */ -+static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad, -+ const struct cipher_parms *parms, -+ Buffer *out, Buffer *tag, -+ enum PvCryptoMode mode, GError **err) -+{ -+ g_autoptr(EVP_CIPHER_CTX) ctx = NULL; -+ const EVP_CIPHER *cipher = parms->cipher; -+ const Buffer *iv = parms->iv_or_tweak; -+ gboolean encrypt = mode == PV_ENCRYPT; -+ const Buffer *key = parms->key; -+ int64_t ret = -1; -+ gint len = -1; -+ -+ g_assert(cipher); -+ g_assert(key); -+ g_assert(iv); -+ /* Checks for later casts */ -+ g_assert(aad->size <= INT_MAX); -+ g_assert(in->size <= INT_MAX); -+ g_assert(tag->size <= INT_MAX); -+ g_assert(iv->size <= INT_MAX); -+ g_assert(out->size == in->size); -+ -+ ctx = EVP_CIPHER_CTX_new(); -+ if (!ctx) -+ g_abort(); -+ -+ /* First, set the cipher algorithm so we can verify our key/IV lengths -+ */ -+ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CIPHER_CTX_new failed")); -+ return -1; -+ } -+ -+ /* Set IV length */ -+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv->size, NULL) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CIPHER_CTX_ex failed")); -+ return -1; -+ } -+ -+ /* Initialise key and IV */ -+ if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, iv->data, encrypt) != -+ 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherInit_ex failed")); -+ return -1; -+ } -+ -+ if (aad->size > 0) { -+ /* Provide any AAD data */ -+ if (EVP_CipherUpdate(ctx, NULL, &len, aad->data, -+ (int)aad->size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherUpdate failed")); -+ return -1; -+ } -+ g_assert(len == (int)aad->size); -+ } -+ -+ /* Provide data to be en/decrypted */ -+ if (EVP_CipherUpdate(ctx, out->data, &len, in->data, (int)in->size) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherUpdate failed")); -+ return -1; -+ } -+ ret = len; -+ -+ if (!encrypt) { -+ /* Set expected tag value */ -+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, -+ (int)tag->size, tag->data) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("Setting the GCM tag failed")); -+ return -1; -+ } -+ } -+ -+ /* Finalize the en/decryption */ -+ if (EVP_CipherFinal_ex(ctx, (guchar *)out->data + len, &len) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("EVP_CipherFinal_ex failed")); -+ return -1; -+ } -+ ret += len; -+ -+ if (encrypt) { -+ /* Get the tag */ -+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, -+ (int)tag->size, tag->data) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("Getting the GCM tag failed")); -+ return -1; -+ } -+ } -+ -+ g_assert(ret == (int)in->size); -+ return ret; -+} -+ -+int64_t gcm_encrypt(const Buffer *in, const Buffer *aad, -+ const struct cipher_parms *parms, Buffer *out, Buffer *tag, -+ GError **err) -+{ -+ return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err); -+} -diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h -new file mode 100644 -index 0000000..34418ed ---- /dev/null -+++ b/genprotimg/src/utils/crypto.h -@@ -0,0 +1,104 @@ -+/* -+ * General cryptography helper functions and definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_UTILS_CRYPTO_H -+#define PV_UTILS_CRYPTO_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "common.h" -+#include "include/pv_crypto_def.h" -+#include "lib/zt_common.h" -+ -+#include "buffer.h" -+ -+#define AES_256_GCM_IV_SIZE 12 -+#define AES_256_GCM_TAG_SIZE 16 -+ -+#define AES_256_XTS_TWEAK_SIZE 16 -+#define AES_256_XTS_KEY_SIZE 64 -+ -+enum PvCryptoMode { -+ PV_ENCRYPT, -+ PV_DECRYPT, -+}; -+ -+typedef GSList HostKeyList; -+ -+/* Register auto cleanup functions */ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_GROUP, EC_GROUP_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_KEY, EC_KEY_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_POINT, EC_POINT_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) -+ -+union cmp_index { -+ struct { -+ uint16_t idx; -+ guchar rand[6]; -+ } __packed; -+ uint64_t data; -+}; -+ -+/* The tweak is always stored in big endian format */ -+union tweak { -+ struct { -+ union cmp_index cmp_idx; -+ uint64_t page_idx; /* page index */ -+ } __packed; -+ uint8_t data[AES_256_XTS_TWEAK_SIZE]; -+}; -+ -+struct cipher_parms { -+ const EVP_CIPHER *cipher; -+ const Buffer *key; -+ const Buffer *iv_or_tweak; -+}; -+ -+EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, -+ GError **err); -+Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); -+Buffer *generate_aes_key(guint size, GError **err); -+Buffer *generate_aes_iv(guint size, GError **err); -+EVP_PKEY *generate_ec_key(gint nid, GError **err); -+gint generate_tweak(union tweak *tweak, uint16_t i, GError **err); -+union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err); -+EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err); -+Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err); -+Buffer *sha256_buffer(const Buffer *buf, GError **err); -+int64_t gcm_encrypt(const Buffer *in, const Buffer *aad, -+ const struct cipher_parms *parms, Buffer *out, -+ Buffer *tag, GError **err); -+gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path, -+ const gchar *path_out, gsize *in_size, gsize *out_size, -+ GError **err); -+Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in, -+ GError **err); -+G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms, -+ const Buffer *in, GError **err); -+ -+#endif -diff --git a/genprotimg/src/utils/file_utils.c b/genprotimg/src/utils/file_utils.c -new file mode 100644 -index 0000000..1d6fc37 ---- /dev/null -+++ b/genprotimg/src/utils/file_utils.c -@@ -0,0 +1,234 @@ -+/* -+ * General file utils -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pv/pv_error.h" -+ -+#include "align.h" -+#include "buffer.h" -+#include "common.h" -+#include "file_utils.h" -+ -+FILE *file_open(const gchar *filename, const gchar *mode, GError **err) -+{ -+ FILE *f = fopen(filename, mode); -+ -+ if (!f) { -+ g_set_error(err, G_FILE_ERROR, -+ (gint)g_file_error_from_errno(errno), -+ _("Failed to open file '%s': %s"), filename, -+ g_strerror(errno)); -+ return NULL; -+ } -+ -+ return f; -+} -+ -+gint file_size(const gchar *filename, gsize *size, GError **err) -+{ -+ GStatBuf st_buf; -+ -+ g_assert(size); -+ -+ if (g_stat(filename, &st_buf) != 0) { -+ g_set_error(err, G_FILE_ERROR, -+ (gint)g_file_error_from_errno(errno), -+ _("Failed to get file status '%s': %s"), filename, -+ g_strerror(errno)); -+ return -1; -+ } -+ -+ if (!S_ISREG(st_buf.st_mode)) { -+ g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, -+ _("File '%s' is not a regular file"), filename); -+ return -1; -+ } -+ -+ if (st_buf.st_size < 0) { -+ g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, -+ _("Invalid file size for '%s': %zu"), filename, -+ st_buf.st_size); -+ return -1; -+ } -+ -+ *size = (gsize)st_buf.st_size; -+ return 0; -+} -+ -+/* Returns 0 on success, otherwise -1. Stores the total number of -+ * elements successfully read in @count_read -+ */ -+gint file_read(FILE *in, void *ptr, gsize size, gsize count, -+ gsize *count_read, GError **err) -+{ -+ gsize tmp_count_read; -+ -+ tmp_count_read = fread(ptr, size, count, in); -+ if (count_read) -+ *count_read = tmp_count_read; -+ -+ if (ferror(in)) { -+ g_set_error(err, G_FILE_ERROR, 0, _("Failed to read file")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+gint file_write(FILE *out, const void *ptr, gsize size, gsize count, -+ gsize *count_written, GError **err) -+{ -+ gsize tmp_count_written; -+ -+ tmp_count_written = fwrite(ptr, size, count, out); -+ if (count_written) -+ *count_written = tmp_count_written; -+ -+ if (tmp_count_written != count || ferror(out)) { -+ g_set_error(err, G_FILE_ERROR, 0, _("Failed to write file")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static gint file_seek(FILE *f, uint64_t offset, GError **err) -+{ -+ gint rc; -+ -+ if (offset > LONG_MAX) { -+ g_set_error(err, PV_ERROR, 0, _("Offset is too large")); -+ return -1; -+ } -+ -+ rc = fseek(f, (long)offset, SEEK_SET); -+ if (rc != 0) { -+ g_set_error(err, G_FILE_ERROR, -+ (gint)g_file_error_from_errno(errno), -+ _("Failed to seek: '%s'"), g_strerror(errno)); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset, -+ GError **err) -+{ -+ gsize bytes_read, bytes_written; -+ gsize total_bytes_read = 0; -+ FILE *i = NULL; -+ gchar buf[4096]; -+ gint ret = -1; -+ -+ if (file_seek(o, offset, err) < 0) -+ return -1; -+ -+ i = file_open(ifile->path, "rb", err); -+ if (!i) -+ return -1; -+ -+ do { -+ if (file_read(i, buf, 1, sizeof(buf), &bytes_read, err) < 0) { -+ g_prefix_error(err, _("Failed to read file '%s': "), -+ ifile->path); -+ goto err; -+ } -+ -+ if (bytes_read == 0) -+ break; -+ -+ total_bytes_read += bytes_read; -+ -+ if (file_write(o, buf, bytes_read, 1, &bytes_written, err) < 0) -+ goto err; -+ } while (bytes_written != 0); -+ -+ if (ifile->size != total_bytes_read) { -+ g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, -+ _("'%s' has changed during the preparation"), -+ ifile->path); -+ goto err; -+ } -+ -+ ret = 0; -+err: -+ fclose(i); -+ return ret; -+} -+ -+gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset, -+ GError **err) -+{ -+ if (file_seek(o, offset, err) < 0) -+ return -1; -+ -+ if (buffer_write(buf, o, err) < 0) -+ return -1; -+ -+ return 0; -+} -+ -+gint pad_file_right(const gchar *path_out, const gchar *path_in, gsize *size_out, -+ guint padding, GError **err) -+{ -+ FILE *f_in, *f_out = NULL; -+ guchar buf[padding]; -+ gsize num_bytes_written; -+ gsize num_bytes_read; -+ uint64_t size_in = 0; -+ gint ret = -1; -+ -+ *size_out = 0; -+ f_in = file_open(path_in, "rb", err); -+ if (!f_in) -+ goto err; -+ -+ f_out = file_open(path_out, "wb", err); -+ if (!f_out) -+ goto err; -+ -+ do { -+ memset(buf, 0, sizeof(buf)); -+ -+ if (file_read(f_in, buf, 1, sizeof(buf), &num_bytes_read, err) < 0) { -+ g_prefix_error(err, _("Failed to read file '%s': "), -+ path_in); -+ goto err; -+ } -+ -+ size_in += num_bytes_read; -+ -+ if (file_write(f_out, buf, 1, sizeof(buf), &num_bytes_written, err)) { -+ g_prefix_error(err, _("Failed to write file '%s': "), -+ path_out); -+ goto err; -+ } -+ -+ *size_out += num_bytes_written; -+ } while (num_bytes_read == padding); -+ -+ g_assert(num_bytes_written == ALIGN(num_bytes_read, padding)); -+ -+ ret = 0; -+err: -+ if (f_out) -+ fclose(f_out); -+ if (f_in) -+ fclose(f_in); -+ return ret; -+} -diff --git a/genprotimg/src/utils/file_utils.h b/genprotimg/src/utils/file_utils.h -new file mode 100644 -index 0000000..47df114 ---- /dev/null -+++ b/genprotimg/src/utils/file_utils.h -@@ -0,0 +1,34 @@ -+/* -+ * General file utils -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 PV_FILE_UTILS_H -+#define PV_FILE_UTILS_H -+ -+#include -+#include -+#include -+ -+#include "pv/pv_comp.h" -+ -+#include "buffer.h" -+ -+FILE *file_open(const gchar *filename, const gchar *mode, GError **err); -+gint file_size(const gchar *filename, gsize *size, GError **err); -+gint file_read(FILE *in, void *ptr, gsize size, gsize count, -+ gsize *count_read, GError **err); -+gint file_write(FILE *out, const void *ptr, gsize size, gsize count, -+ gsize *count_written, GError **err); -+gint pad_file_right(const gchar *path_out, const gchar *path_in, -+ gsize *size_out, guint padding, GError **err); -+gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset, -+ GError **err); -+gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset, -+ GError **err); -+ -+#endif -diff --git a/include/boot/ipl.h b/include/boot/ipl.h -new file mode 100644 -index 0000000..bc9b7fe ---- /dev/null -+++ b/include/boot/ipl.h -@@ -0,0 +1,191 @@ -+/* -+ * IPL related definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 IPL_H -+#define IPL_H -+ -+#include "lib/zt_common.h" -+#include "s390.h" -+ -+#define IPL_FLAG_SECURE 0x40 -+ -+#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80 -+#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40 -+ -+#define IPL_MAX_SUPPORTED_VERSION 0 -+#define IPL_PARM_BLOCK_VERSION 0x1 -+ -+/* IPL Types */ -+#define IPL_TYPE_PV 0x5 -+ -+ -+#ifndef __ASSEMBLER__ -+ -+#include -+ -+/* 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; -+ -+/* Structure must not have any padding */ -+struct ipl_pb0_pv_comp { -+ uint64_t tweak_pref; -+ uint64_t addr; -+ uint64_t len; -+}; -+STATIC_ASSERT(sizeof(struct ipl_pb0_pv_comp) == 3 * 8) -+ -+/* IPL Parameter Block 0 for PV */ -+struct ipl_pb0_pv { -+ uint32_t len; -+ uint8_t pbt; -+ uint8_t reserved1[3]; -+ uint8_t loadparm[8]; -+ uint8_t reserved2[84]; -+ uint8_t reserved3[3]; -+ uint8_t version; -+ uint8_t reserved4[4]; -+ uint32_t num_comp; -+ uint64_t pv_hdr_addr; -+ uint64_t pv_hdr_size; -+ struct ipl_pb0_pv_comp components[]; -+} __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; -+ struct ipl_pb0_pv pv; -+ char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)]; -+ }; -+} __packed; -+ -+/* 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 */ -+/* Structure must not have any padding */ -+struct ipl_rb_hdr { -+ uint32_t len; -+ uint8_t rbt; -+ uint8_t reserved1[11]; -+}; -+STATIC_ASSERT(sizeof(struct ipl_rb_hdr) == 4 + 1 + 11) -+ -+/* 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]; -+}; -+ -+/* Structure must not have any padding */ -+struct ipl_rb_components { -+ uint32_t len; -+ uint8_t rbt; -+ uint8_t reserved1[11]; -+ struct ipl_rb_component_entry entries[]; -+}; -+STATIC_ASSERT(sizeof(struct ipl_rb_components) == 4 + 1 + 11) -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* IPL_H */ -diff --git a/include/boot/linux_layout.h b/include/boot/linux_layout.h -new file mode 100644 -index 0000000..83f91a2 ---- /dev/null -+++ b/include/boot/linux_layout.h -@@ -0,0 +1,34 @@ -+/* -+ * s390 Linux layout definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 LINUX_LAYOUT_H -+#define LINUX_LAYOUT_H -+ -+#include "lib/zt_common.h" -+ -+/* Entry address offsets */ -+#define IMAGE_ENTRY _AC(0x10000, UL) -+#define IMAGE_ENTRY_KDUMP _AC(0x10010, UL) -+ -+/* Parameter address offsets */ -+#define PARMAREA _AC(0x10400, UL) -+#define IPL_DEVICE _AC(0x10400, UL) -+#define INITRD_START _AC(0x10408, UL) -+#define INITRD_SIZE _AC(0x10410, UL) -+#define OLDMEM_BASE _AC(0x10418, UL) -+#define OLDMEM_SIZE _AC(0x10420, UL) -+#define COMMAND_LINE _AC(0x10480, UL) -+ -+/* Parameter sizes */ -+#define COMMAND_LINE_SIZE 896 -+ -+ -+#ifndef __ASSEMBLER__ -+#endif /* __ASSEMBLER__ */ -+#endif /* LINUX_LAYOUT_H */ -diff --git a/include/boot/loaders_layout.h b/include/boot/loaders_layout.h -new file mode 100644 -index 0000000..5fc6d25 ---- /dev/null -+++ b/include/boot/loaders_layout.h -@@ -0,0 +1,43 @@ -+/* -+ * zipl stage2/stage3 layout definitions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 LOADERS_LAYOUT_H -+#define LOADERS_LAYOUT_H -+ -+#include "lib/zt_common.h" -+#include "linux_layout.h" -+ -+#define STAGE2_DESC _AC(0x78, UL) -+#define STAGE2_ENTRY _AC(0x2018, UL) -+#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL) -+#define STAGE2_HEAP_SIZE _AC(0x3000, UL) -+#define STAGE2_STACK_ADDRESS _AC(0xe400, UL) -+#define STAGE2_STACK_SIZE _AC(0x1c00, UL) -+ -+#define STAGE3_ENTRY _AC(0xa000, UL) -+ -+#define STAGE2_LOAD_ADDRESS _AC(0x2000, UL) -+#define STAGE3_LOAD_ADDRESS STAGE3_ENTRY -+#define IMAGE_LOAD_ADDRESS IMAGE_ENTRY -+ -+#define STAGE3_MAXIMUM_SIZE _AC(0x3000, UL) -+#define STAGE3_HEAP_SIZE _AC(0x4000, UL) -+#define STAGE3_HEAP_ADDRESS _AC(0x2000, UL) -+#define STAGE3_STACK_SIZE _AC(0x1000, UL) -+#define STAGE3_STACK_ADDRESS _AC(0xF000, UL) -+#define STAGE3_PARAMS_ADDRESS _AC(0x9000, UL) -+#define STAGE3_PARAMS_MAXIMUM_SIZE _AC(0x1000, UL) -+ -+#define COMMAND_LINE_EXTRA _AC(0xE000, UL) -+#define COMMAND_LINE_EXTRA_SIZE _AC(0x0400, UL) -+ -+#ifndef __ASSEMBLER__ -+#endif /* __ASSEMBLER__ */ -+#endif /* LOADERS_LAYOUT_H */ -diff --git a/include/boot/s390.h b/include/boot/s390.h -new file mode 100644 -index 0000000..8262f5a ---- /dev/null -+++ b/include/boot/s390.h -@@ -0,0 +1,467 @@ -+/* -+ * s390 related definitions and functions. -+ * -+ * Copyright IBM Corp. 2013, 2020 -+ * -+ * 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 S390_H -+#define S390_H -+ -+#include "lib/zt_common.h" -+#include "boot/sigp.h" -+ -+#define __LC_IPLDEV 0x0c6c -+#define __LC_OS_INFO 0x0e18 -+ -+#define PAGE_SIZE _AC(4096, UL) -+ -+/* Minimum size of a stack frame in bytes */ -+#define STACK_FRAME_OVERHEAD _AC(160, U) -+ -+/* Facilities */ -+#define UNPACK_FACILITY _AC(161, U) -+ -+#define PSW32_ADDR_MASK _AC(0x000000007fffffff, UL) -+#define PSW_MASK_BA _AC(0x0000000080000000, UL) -+#define PSW_MASK_EA _AC(0x0000000100000000, UL) -+#define PSW_MASK_BIT_12 _AC(0x0008000000000000, UL) -+#define PSW_LOAD _AC(0x0008000080000000, UL) -+#define PSW_DISABLED_WAIT _AC(0x000a000000000000, UL) -+ -+ -+#ifndef __ASSEMBLER__ -+ -+/* -+ * Helper macro for exception table entries -+ */ -+#define EX_TABLE(_fault, _target) \ -+ ".section .ex_table,\"a\"\n" \ -+ ".align 4\n" \ -+ ".long (" #_fault ")\n" \ -+ ".long (" #_target ")\n" \ -+ ".previous\n" -+ -+struct psw_t { -+ uint64_t mask; -+ 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 { -+ uint8_t pad_0x0000[0x0014-0x0000]; /* 0x0000 */ -+ uint32_t ipl_parmblock_ptr; /* 0x0014 */ -+ uint8_t pad_0x0018[0x0080-0x0018]; /* 0x0018 */ -+ uint32_t ext_params; /* 0x0080 */ -+ uint16_t ext_cpu_addr; /* 0x0084 */ -+ uint16_t ext_int_code; /* 0x0086 */ -+ uint16_t svc_ilc; /* 0x0088 */ -+ uint16_t svc_code; /* 0x008a */ -+ uint16_t pgm_ilc; /* 0x008c */ -+ uint16_t pgm_code; /* 0x008e */ -+ uint32_t data_exc_code; /* 0x0090 */ -+ uint16_t mon_class_num; /* 0x0094 */ -+ uint16_t per_perc_atmid; /* 0x0096 */ -+ uint64_t per_address; /* 0x0098 */ -+ uint8_t exc_access_id; /* 0x00a0 */ -+ uint8_t per_access_id; /* 0x00a1 */ -+ uint8_t op_access_id; /* 0x00a2 */ -+ uint8_t ar_access_id; /* 0x00a3 */ -+ uint8_t pad_0x00a4[0x00a8-0x00a4]; /* 0x00a4 */ -+ uint64_t trans_exc_code; /* 0x00a8 */ -+ uint64_t monitor_code; /* 0x00b0 */ -+ uint16_t subchannel_id; /* 0x00b8 */ -+ uint16_t subchannel_nr; /* 0x00ba */ -+ uint32_t io_int_parm; /* 0x00bc */ -+ uint32_t io_int_word; /* 0x00c0 */ -+ uint8_t pad_0x00c4[0x00c8-0x00c4]; /* 0x00c4 */ -+ uint32_t stfl_fac_list; /* 0x00c8 */ -+ uint8_t pad_0x00cc[0x00e8-0x00cc]; /* 0x00cc */ -+ uint32_t mcck_interruption_code[2]; /* 0x00e8 */ -+ uint8_t pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */ -+ uint32_t external_damage_code; /* 0x00f4 */ -+ uint64_t failing_storage_address; /* 0x00f8 */ -+ uint8_t pad_0x0100[0x0110-0x0100]; /* 0x0100 */ -+ uint64_t breaking_event_addr; /* 0x0110 */ -+ uint8_t pad_0x0118[0x0120-0x0118]; /* 0x0118 */ -+ struct psw_t restart_old_psw; /* 0x0120 */ -+ struct psw_t external_old_psw; /* 0x0130 */ -+ struct psw_t svc_old_psw; /* 0x0140 */ -+ struct psw_t program_old_psw; /* 0x0150 */ -+ struct psw_t mcck_old_psw; /* 0x0160 */ -+ struct psw_t io_old_psw; /* 0x0170 */ -+ uint8_t pad_0x0180[0x01a0-0x0180]; /* 0x0180 */ -+ struct psw_t restart_psw; /* 0x01a0 */ -+ struct psw_t external_new_psw; /* 0x01b0 */ -+ struct psw_t svc_new_psw; /* 0x01c0 */ -+ struct psw_t program_new_psw; /* 0x01d0 */ -+ struct psw_t mcck_new_psw; /* 0x01e0 */ -+ struct psw_t io_new_psw; /* 0x01f0 */ -+ -+ /* Save areas. */ -+ uint64_t save_area_sync[8]; /* 0x0200 */ -+ uint64_t save_area_async[8]; /* 0x0240 */ -+ uint64_t save_area_restart[1]; /* 0x0280 */ -+ uint8_t pad_0x0288[0x0290-0x0288]; /* 0x0288 */ -+ -+ /* Return psws. */ -+ struct psw_t return_psw; /* 0x0290 */ -+ struct psw_t return_mcck_psw; /* 0x02a0 */ -+ -+ /* CPU accounting and timing values. */ -+ uint64_t sync_enter_timer; /* 0x02b0 */ -+ uint64_t async_enter_timer; /* 0x02b8 */ -+ uint64_t mcck_enter_timer; /* 0x02c0 */ -+ uint64_t exit_timer; /* 0x02c8 */ -+ uint64_t user_timer; /* 0x02d0 */ -+ uint64_t system_timer; /* 0x02d8 */ -+ uint64_t steal_timer; /* 0x02e0 */ -+ uint64_t last_update_timer; /* 0x02e8 */ -+ uint64_t last_update_clock; /* 0x02f0 */ -+ uint64_t int_clock; /* 0x02f8 */ -+ uint64_t mcck_clock; /* 0x0300 */ -+ uint64_t clock_comparator; /* 0x0308 */ -+ -+ /* Current process. */ -+ uint64_t current_task; /* 0x0310 */ -+ uint64_t thread_info; /* 0x0318 */ -+ uint64_t kernel_stack; /* 0x0320 */ -+ -+ /* Interrupt, panic and restart stack. */ -+ uint64_t async_stack; /* 0x0328 */ -+ uint64_t panic_stack; /* 0x0330 */ -+ uint64_t restart_stack; /* 0x0338 */ -+ -+ /* Restart function and parameter. */ -+ uint64_t restart_fn; /* 0x0340 */ -+ uint64_t restart_data; /* 0x0348 */ -+ uint64_t restart_source; /* 0x0350 */ -+ -+ /* Address space pointer. */ -+ uint64_t kernel_asce; /* 0x0358 */ -+ uint64_t user_asce; /* 0x0360 */ -+ uint64_t current_pid; /* 0x0368 */ -+ -+ /* SMP info area */ -+ uint32_t cpu_nr; /* 0x0370 */ -+ uint32_t softirq_pending; /* 0x0374 */ -+ uint64_t percpu_offset; /* 0x0378 */ -+ uint64_t vdso_per_cpu_data; /* 0x0380 */ -+ uint64_t machine_flags; /* 0x0388 */ -+ uint64_t ftrace_func; /* 0x0390 */ -+ uint64_t gmap; /* 0x0398 */ -+ uint8_t pad_0x03a0[0x0400-0x03a0]; /* 0x03a0 */ -+ -+ /* Interrupt response block. */ -+ uint8_t irb[64]; /* 0x0400 */ -+ -+ /* Per cpu primary space access list */ -+ uint32_t paste[16]; /* 0x0440 */ -+ -+ uint8_t pad_0x0480[0x0e00-0x0480]; /* 0x0480 */ -+ -+ /* -+ * 0xe00 contains the address of the IPL Parameter Information -+ * block. Dump tools need IPIB for IPL after dump. -+ * Note: do not change the position of any fields in 0x0e00-0x0f00 -+ */ -+ uint64_t ipib; /* 0x0e00 */ -+ uint32_t ipib_checksum; /* 0x0e08 */ -+ uint64_t vmcore_info; /* 0x0e0c */ -+ uint8_t pad_0x0e14[0x0e18-0x0e14]; /* 0x0e14 */ -+ uint64_t os_info; /* 0x0e18 */ -+ uint8_t pad_0x0e20[0x0f00-0x0e20]; /* 0x0e20 */ -+ -+ /* Extended facility list */ -+ uint64_t stfle_fac_list[32]; /* 0x0f00 */ -+ uint8_t pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ -+ uint64_t vector_save_area_addr; /* 0x11b0 */ -+ /* 64 bit extparam used for pfault/diag 250: defined by architecture */ -+ uint64_t ext_params2; /* 0x11B8 */ -+ uint8_t pad_0x11c0[0x1200-0x11C0]; /* 0x11C0 */ -+ -+ /* CPU register save area: defined by architecture */ -+ uint64_t floating_pt_save_area[16]; /* 0x1200 */ -+ uint64_t gpregs_save_area[16]; /* 0x1280 */ -+ struct psw_t psw_save_area; /* 0x1300 */ -+ uint8_t pad_0x1310[0x1318-0x1310]; /* 0x1310 */ -+ uint32_t prefixreg_save_area; /* 0x1318 */ -+ uint32_t fpt_creg_save_area; /* 0x131c */ -+ uint8_t pad_0x1320[0x1324-0x1320]; /* 0x1320 */ -+ uint32_t tod_progreg_save_area; /* 0x1324 */ -+ uint32_t cpu_timer_save_area[2]; /* 0x1328 */ -+ uint32_t clock_comp_save_area[2]; /* 0x1330 */ -+ uint8_t pad_0x1338[0x1340-0x1338]; /* 0x1338 */ -+ uint32_t access_regs_save_area[16]; /* 0x1340 */ -+ uint64_t cregs_save_area[16]; /* 0x1380 */ -+ uint8_t pad_0x1400[0x1800-0x1400]; /* 0x1400 */ -+ -+ /* Transaction abort diagnostic block */ -+ uint8_t pgm_tdb[256]; /* 0x1800 */ -+ -+ /* align to the top of the prefix area */ -+ uint8_t pad_0x1900[0x2000-0x1900]; /* 0x1900 */ -+} __packed __aligned(8192); -+ -+#define S390_lowcore (*((struct _lowcore *) 0)) -+ -+ -+static __always_inline int page_is_valid(unsigned long addr) -+{ -+ unsigned long tmp; -+ int rc; -+ -+ asm volatile( -+ "0: ic %1,%2\n" -+ "1: lhi %0,1\n" -+ "2:\n" -+ ".pushsection .fixup, \"ax\"\n" -+ "3: xr %0,%0\n" -+ " jg 2b\n" -+ ".popsection\n" -+ EX_TABLE(0b, 3b) EX_TABLE(1b, 3b) -+ : "=d" (rc), "=d" (tmp) -+ : "Q" (*(unsigned long *) addr) -+ : "cc"); -+ return rc; -+} -+ -+static __always_inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) -+{ -+ register unsigned long reg2 asm("2") = (unsigned long) buf; -+ register unsigned long reg3 asm("3") = (unsigned long) len; -+ -+ asm volatile( -+ "0: cksm %0,%1\n" /* do checksum on longs */ -+ " jo 0b\n" -+ : "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory"); -+ return sum; -+} -+ -+#define __ctl_store(array, low, high) ({ \ -+ typedef struct { char _[sizeof(array)]; } __may_alias addrtype;\ -+ asm volatile( \ -+ " stctg %1,%2,%0\n" \ -+ : "=Q" (*(addrtype *)(&array)) \ -+ : "i" (low), "i" (high)); \ -+}) -+ -+#define __ctl_load(array, low, high) ({ \ -+ typedef struct { char _[sizeof(array)]; } __may_alias addrtype; \ -+ asm volatile( \ -+ " lctlg %1,%2,%0\n" \ -+ : : "Q" (*(addrtype *)(&array)), \ -+ "i" (low), "i" (high)); \ -+}) -+ -+static __always_inline void __ctl_set_bit(unsigned int cr, unsigned int bit) -+{ -+ unsigned long reg; -+ -+ __ctl_store(reg, cr, cr); -+ reg |= 1UL << bit; -+ __ctl_load(reg, cr, cr); -+} -+ -+/* -+ * DIAG 308 support -+ */ -+enum diag308_subcode { -+ DIAG308_REL_HSA = 2, -+ DIAG308_IPL = 3, -+ DIAG308_DUMP = 4, -+ DIAG308_SET = 5, -+ DIAG308_STORE = 6, -+ DIAG308_SET_PV = 8, -+ DIAG308_UNPACK_PV = 10, -+}; -+ -+enum diag308_rc { -+ DIAG308_RC_OK = 0x0001, -+}; -+ -+static __always_inline unsigned long diag308(unsigned long subcode, void *addr) -+{ -+ register unsigned long _addr asm("0") = (unsigned long) addr; -+ register unsigned long _rc asm("1") = 0; -+ -+ asm volatile( -+ " diag %0,%2,0x308\n" -+ "0:\n" -+ : "+d" (_addr), "+d" (_rc) -+ : "d" (subcode) : "cc", "memory"); -+ return _rc; -+} -+ -+/* -+ * Store CPU address -+ */ -+static __always_inline unsigned short stap(void) -+{ -+ unsigned short cpu_address; -+ -+ asm volatile("stap %0" : "=m" (cpu_address)); -+ return cpu_address; -+} -+ -+/* -+ * Program the clock comparator -+ */ -+static __always_inline void set_clock_comparator(uint64_t time) -+{ -+ asm volatile("sckc %0" : : "Q" (time)); -+} -+ -+/* -+ * Program the CPU timer -+ */ -+static __always_inline void set_cpu_timer(uint64_t timer) -+{ -+ asm volatile("spt %0" : : "Q" (timer)); -+} -+ -+/* -+ * Get current time (store clock) -+ */ -+static __always_inline unsigned long long get_tod_clock(void) -+{ -+ unsigned long long clk; -+ -+ asm volatile("stck %0" : "=Q" (clk) : : "cc"); -+ return clk; -+} -+ -+/* -+ * Get ID of current CPU -+ */ -+struct cpuid { -+ unsigned int version:8; -+ unsigned int ident:24; -+ unsigned int machine:16; -+ unsigned int unused:16; -+} __packed __aligned(8); -+ -+static __always_inline void get_cpu_id(struct cpuid *ptr) -+{ -+ asm volatile("stidp %0" : "=Q" (*ptr)); -+} -+ -+/* -+ * Check if we run under z/VM -+ */ -+static __always_inline int is_zvm(void) -+{ -+ struct cpuid cpuid; -+ -+ get_cpu_id(&cpuid); -+ return cpuid.version == 0xff; -+} -+ -+/* To avoid conflicts add a macro guard since __vector128 is also -+ * defined in 'linux/asm/types.h'. -+ */ -+#ifndef _S390_TYPES_H -+/* -+ * Vector register definition -+ */ -+typedef struct { -+ uint32_t u[4]; -+} __vector128; -+#endif -+ -+/* -+ * Save vector registers -+ */ -+static __always_inline void save_vx_regs(__vector128 *vxrs) -+{ -+ typedef struct { __vector128 _[32]; } addrtype; -+ -+ asm volatile( -+ " la 1,%0\n" -+ " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ -+ " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ -+ : "=Q" (*(addrtype *) vxrs) : : "1"); -+} -+ -+/* -+ * Save vector registers safe -+ */ -+static __always_inline void save_vx_regs_safe(__vector128 *vxrs) -+{ -+ unsigned long cr0; -+ -+ __ctl_store(cr0, 0, 0); -+ __ctl_set_bit(0, 17); -+ __ctl_set_bit(0, 18); /* AFP-register-control */ -+ save_vx_regs(vxrs); -+ __ctl_load(cr0, 0, 0); -+} -+ -+#define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ -+ -+static __always_inline int __test_facility(unsigned long nr, void *facilities) -+{ -+ unsigned char *ptr; -+ -+ if (nr >= MAX_FACILITY_BIT) -+ return 0; -+ ptr = (unsigned char *) facilities + (nr >> 3); -+ return (*ptr & (0x80 >> (nr & 7))) != 0; -+} -+ -+/* -+ * The test_facility function uses the bit odering where the MSB is bit 0. -+ * That makes it easier to query facility bits with the bit number as -+ * documented in the Principles of Operation. -+ */ -+static __always_inline int test_facility(unsigned long nr) -+{ -+ return __test_facility(nr, &S390_lowcore.stfle_fac_list); -+} -+ -+static __always_inline unsigned long __stfle_asm(uint64_t *stfle_fac_list, unsigned int size) -+{ -+ register unsigned long reg0 asm("0") = size - 1; -+ -+ asm volatile( -+ ".insn s,0xb2b00000,0(%1)" /* stfle */ -+ : "+d" (reg0) -+ : "a" (stfle_fac_list) -+ : "memory", "cc"); -+ return reg0; -+} -+ -+/** -+ * stfle - Store facility list extended -+ * @stfle_fac_list: array where facility list can be stored -+ * @size: size of passed in array in double words -+ */ -+static __always_inline void stfle(uint64_t *stfle_fac_list, unsigned int size) -+{ -+ unsigned long nr; -+ -+ asm volatile( -+ " .insn s,0xb2b10000,0(0)\n" /* stfl */ -+ "0:\n" -+ EX_TABLE(0b, 0b) -+ : "+m" (S390_lowcore.stfl_fac_list)); -+ nr = 4; /* bytes stored by stfl */ -+ memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); -+ if (S390_lowcore.stfl_fac_list & 0x01000000) { -+ /* More facility bits available with stfle */ -+ nr = __stfle_asm(stfle_fac_list, size); -+ nr = MIN((nr + 1) * 8, size * 8UL); -+ } -+ memset((char *) stfle_fac_list + nr, 0, size * 8 - nr); -+} -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* S390_H */ -diff --git a/include/boot/sigp.h b/include/boot/sigp.h -new file mode 100644 -index 0000000..1606580 ---- /dev/null -+++ b/include/boot/sigp.h -@@ -0,0 +1,56 @@ -+/* -+ * SIGP related definitions and functions. -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * 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 S390_SIGP_H -+#define S390_SIGP_H -+ -+/* Signal Processor Order Codes */ -+#define SIGP_STOP_AND_STORE_STATUS 9 -+#define SIGP_SET_ARCHITECTURE 18 -+#define SIGP_SET_MULTI_THREADING 22 -+#define SIGP_STORE_ASTATUS_AT_ADDRESS 23 -+ -+/* Signal Processor Condition Codes */ -+#define SIGP_CC_ORDER_CODE_ACCEPTED 0 -+#define SIGP_CC_BUSY 2 -+ -+ -+#ifndef __ASSEMBLER__ -+ -+#include -+ -+static inline int sigp(uint16_t addr, uint8_t order, uint32_t parm, -+ uint32_t *status) -+{ -+ register unsigned int reg1 asm ("1") = parm; -+ int cc; -+ -+ asm volatile( -+ " sigp %1,%2,0(%3)\n" -+ " ipm %0\n" -+ " srl %0,28\n" -+ : "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc"); -+ if (status && cc == 1) -+ *status = reg1; -+ return cc; -+} -+ -+static inline int sigp_busy(uint16_t addr, uint8_t order, uint32_t parm, -+ uint32_t *status) -+{ -+ int cc; -+ -+ do { -+ cc = sigp(addr, order, parm, status); -+ } while (cc == SIGP_CC_BUSY); -+ return cc; -+} -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* S390_SIGP_H */ -diff --git a/include/lib/util_base.h b/include/lib/util_base.h -index c4b52a1..74f67f9 100644 ---- a/include/lib/util_base.h -+++ b/include/lib/util_base.h -@@ -14,6 +14,7 @@ - - #include - #include -+#include "zt_common.h" - - void util_hexdump(FILE *fh, const char *tag, const void *data, int cnt); - void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group, -@@ -22,22 +23,6 @@ void util_print_indented(const char *str, int indent); - - #define UTIL_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - --#define MIN(x, y) \ --({ \ -- __typeof__(x) _x = (x); \ -- __typeof__(y) _y = (y); \ -- \ -- _x < _y ? _x : _y; \ --}) -- --#define MAX(x, y) \ --({ \ -- __typeof__(x) _x = (x); \ -- __typeof__(y) _y = (y); \ -- \ -- _x > _y ? _x : _y; \ --}) -- - static inline void util_ptr_vec_free(void **ptr_vec, int count) - { - int i; -diff --git a/include/lib/zt_common.h b/include/lib/zt_common.h -index c486428..8f4f164 100644 ---- a/include/lib/zt_common.h -+++ b/include/lib/zt_common.h -@@ -15,6 +15,21 @@ - #define STRINGIFY_1(x) #x - #define STRINGIFY(x) STRINGIFY_1(x) - -+/* Use this macro to make constant macros usable in both assembler and -+ * C code. -+ * -+ * Usage example: -+ * #define IMAGE_ENTRY _AC(0x10000, UL) -+ */ -+#ifdef __ASSEMBLER__ -+#define _AC(X, TYPE) X -+#else -+#define _AC(X, TYPE) X##TYPE -+#endif -+ -+ -+#ifndef __ASSEMBLER__ -+ - #ifdef UNUSED - #elif defined(__GNUC__) - # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) -@@ -22,6 +37,17 @@ - # define UNUSED(x) x - #endif - -+#ifdef STATIC_ASSERT -+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5) -+# define STATIC_ASSERT(test) _Static_assert((test), "(" #test ") failed"); -+#else -+# define STATIC_ASSERT(test) -+#endif -+ -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) -+#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) -+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -+ - #define RELEASE_STRING STRINGIFY (S390_TOOLS_RELEASE) - #define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR) - #define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR) -@@ -31,6 +57,37 @@ - #define __packed __attribute__((packed)) - #define __aligned(x) __attribute__((aligned(x))) - #define __may_alias __attribute__((may_alias)) -+#define __section(x) __attribute__((__section__(#x))) -+#define __noinline __attribute__((__noinline__)) -+/* The Linux kernel (in stddef.h) and glibc (sys/cdefs.h) define -+ * __always_inline. Therefore undefine it first to allow the headers -+ * to be included first. -+ */ -+#undef __always_inline -+#define __always_inline inline __attribute__((always_inline)) -+ -+#define __pa32(x) ((uint32_t)(unsigned long)(x)) -+#define __pa(x) ((unsigned long)(x)) -+ -+#define barrier() __asm__ __volatile__("": : :"memory") -+ -+#undef MIN -+#define MIN(x, y) \ -+ ({ \ -+ __typeof__(x) _x = (x); \ -+ __typeof__(y) _y = (y); \ -+ \ -+ _x < _y ? _x : _y; \ -+ }) -+ -+#undef MAX -+#define MAX(x, y) \ -+ ({ \ -+ __typeof__(x) _x = (x); \ -+ __typeof__(y) _y = (y); \ -+ \ -+ _x > _y ? _x : _y; \ -+ }) - - typedef unsigned long long u64; - typedef signed long long s64; -@@ -41,4 +98,5 @@ typedef signed short int s16; - typedef unsigned char u8; - typedef signed char s8; - -+#endif /* __ASSEMBLER__ */ - #endif /* LIB_ZT_COMMON_H */ -diff --git a/libutil/util_panic_example.c b/libutil/util_panic_example.c -index a676bce..aa9c159 100644 ---- a/libutil/util_panic_example.c -+++ b/libutil/util_panic_example.c -@@ -15,10 +15,10 @@ - #include - #include - -+#include "lib/zt_common.h" - #include "lib/util_panic.h" - - /* Make functions noinline to have a nice backtrace */ --#define __noinline __attribute__((noinline)) - - /* - * Test util_panic() -diff --git a/netboot/Makefile.pxelinux.0 b/netboot/Makefile.pxelinux.0 -index 093e0cf..41296cb 100644 ---- a/netboot/Makefile.pxelinux.0 -+++ b/netboot/Makefile.pxelinux.0 -@@ -33,8 +33,8 @@ pxelinux.initramfs: $(BBINSTALL) - $(BUSYBOX)/_install: - wget https://busybox.net/downloads/$(BUSYBOX).tar.bz2 - tar xjf $(BUSYBOX).tar.bz2 -- make -C $(BUSYBOX) defconfig -- make -C $(BUSYBOX) install -+ $(MAKE) -C $(BUSYBOX) defconfig -+ $(MAKE) -C $(BUSYBOX) install - - install: - -diff --git a/qethconf/qethconf.8 b/qethconf/qethconf.8 -index deb58a6..0669384 100644 ---- a/qethconf/qethconf.8 -+++ b/qethconf/qethconf.8 -@@ -58,7 +58,7 @@ e.g. \-xc0a80a26 - .TP - \fIMASKBITS\fR - Number of bits set in the network mask. --This allows to specify an address range, e.g. 192.168.10.0/24. -+Network masks can be used to specify address ranges, e.g. 192.168.10.0/24. - This subparameter is only valid for function IPA. - .TP - \fIINTERFACE\fR -diff --git a/vmur/vmur.8 b/vmur/vmur.8 -index 6fd3a21..f0a03de 100644 ---- a/vmur/vmur.8 -+++ b/vmur/vmur.8 -@@ -21,7 +21,7 @@ vmur \- Work with z/VM spool file queues - . - . - .SH DESCRIPTION --The \*v program allows to read, create, list, purge, or order files -+With the \*v program you can read, create, list, purge, or order files - on the z/VM spool files queues (RDR, PUN, and PRT). - - \*v supports the following commands: -diff --git a/zconf/qeth/misc.h b/zconf/qeth/misc.h -index d9df17a..3f43327 100644 ---- a/zconf/qeth/misc.h -+++ b/zconf/qeth/misc.h -@@ -12,8 +12,6 @@ - - #include - --#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -- - char *misc_link_target(const char *fmt, ...); - bool misc_str_in_list(const char *str, const char *strings[], int array_size); - int misc_argz_add_from_file(char **argz, size_t *argz_len, -diff --git a/zdev/include/misc.h b/zdev/include/misc.h -index 8103433..6f2106d 100644 ---- a/zdev/include/misc.h -+++ b/zdev/include/misc.h -@@ -15,10 +15,10 @@ - #include - #include - -+#include "lib/zt_common.h" - #include "lib/util_list.h" - #include "exit_code.h" - --#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - #define SCOPE_ACTIVE(x) ((x) & config_active ? 1 : 0) - #define SCOPE_PERSISTENT(x) ((x) & config_persistent ? 1 : 0) - #define SCOPE_AUTOCONF(x) ((x) & config_autoconf ? 1 : 0) -diff --git a/zdump/opts.c b/zdump/opts.c -index 387c102..81ff7aa 100644 ---- a/zdump/opts.c -+++ b/zdump/opts.c -@@ -167,7 +167,7 @@ static void argv_fuse_set(char **argv, int argc) - static void action_set(enum zg_action action) - { - if (g.opts.action_specified) -- ERR_EXIT("Please specifiy only one of the \"-i\", \"-d\", " -+ ERR_EXIT("Please specify only one of the \"-i\", \"-d\", " - "\"-m\" or \"-u\" option"); - g.opts.action = action; - g.opts.action_specified = 1; -@@ -183,7 +183,7 @@ static void verify_opts(void) - g.opts.action != ZG_ACTION_STDOUT && - g.opts.action != ZG_ACTION_DUMP_INFO) - ERR_EXIT("The \"--select\" option can only be " -- "specifed for info, mount, or copy"); -+ "specified for info, mount, or copy"); - } - if (!g.opts.fmt_specified) - return; -diff --git a/zdump/zg.c b/zdump/zg.c -index 869c361..378cbf9 100644 ---- a/zdump/zg.c -+++ b/zdump/zg.c -@@ -432,7 +432,7 @@ char *zg_devnode_create(dev_t dev) - char *file_path; - unsigned int i; - -- for (i = 0; i < ARRAY_ELEMENT_CNT(dir_vec); i++) { -+ for (i = 0; i < ARRAY_SIZE(dir_vec); i++) { - if (dir_vec[i] == NULL) - continue; - file_path = devnode_create_dir(dir_vec[i], dev); -diff --git a/zdump/zg.h b/zdump/zg.h -index b97a3d3..efde05b 100644 ---- a/zdump/zg.h -+++ b/zdump/zg.h -@@ -122,10 +122,7 @@ do { \ - * Misc - */ - #define PAGE_SIZE 4096UL --#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) --#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) - #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) --#define ARRAY_ELEMENT_CNT(x) (sizeof(x) / sizeof(x[0])) - #define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) - - static inline u32 zg_csum_partial(const void *buf, int len, u32 sum) -diff --git a/zipl/boot/.gitignore b/zipl/boot/.gitignore -new file mode 100644 -index 0000000..352157d ---- /dev/null -+++ b/zipl/boot/.gitignore -@@ -0,0 +1,2 @@ -+*.lds -+*.lds.d -diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile -index 0faf3ec..359189e 100644 ---- a/zipl/boot/Makefile -+++ b/zipl/boot/Makefile -@@ -1,7 +1,10 @@ - # Common definitions - include ../../common.mak - --CFLAGS_BOOT = $(NO_PIE_CFLAGS) -Os -g -I../include -D__ASSEMBLY__ \ -+INCLUDE_PATHS := $(rootdir)/zipl/include $(rootdir)/include -+INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS)) -+ -+ALL_CFLAGS = $(NO_PIE_CFLAGS) -Os -g $(INCLUDE_PARMS) \ - -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ - -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ - -fno-delete-null-pointer-checks \ -@@ -21,32 +24,43 @@ all: data.o data.h tape0.bin stage3.bin - %: %.S - - %.o: %.S -- $(CC) $(CFLAGS_BOOT) -c -o $@ $< -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< - - %.o: %.c -- $(CC) $(CFLAGS_BOOT) -c -o $@ $< -+ $(CC) $(ALL_CFLAGS) -c -o $@ $< -+ -+# Dependencies for the .lds generation -+sources_lds_S = $(wildcard *.lds.S) -+dependencies_lds_S = $(sources_lds_S:%.lds.S=.%.lds.d) -+# Include all ".lds.d" dependency files for all make targets except for "clean" -+ifneq ($(MAKECMDGOALS),clean) -+-include $(dependencies_lds_S) -+endif -+ -+%.lds: %.lds.S -+ $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $< - - eckd2dump_sv.exec: \ - head.o stage2dump.o cio.o eckd2dump.o eckd2dump_sv.o \ -- libc.o sclp.o entry.o -+ libc.o ebcdic.o sclp.o entry.o stage2.lds - eckd2dump_mv.exec: \ - head.o stage2dump.o cio.o eckd2dump.o eckd2dump_mv.o \ -- libc.o sclp.o entry.o -+ libc.o ebcdic.o sclp.o entry.o stage2.lds - fba2dump.exec: \ - head.o stage2dump.o cio.o fba2dump.o \ -- libc.o sclp.o entry.o -+ libc.o ebcdic.o sclp.o entry.o stage2.lds - tape2dump.exec: \ - head.o stage2dump.o cio.o tape2dump.o \ -- libc.o sclp.o entry.o --eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \ -- kdump2.o kdump.o entry.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 stage3.lds -+ libc.o ebcdic.o sclp.o entry.o stage2.lds -+eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o ebcdic.o menu.o sclp.o \ -+ kdump2.o kdump.o entry.o stage2.lds -+fba2.exec: head.o stage2.o cio.o fba2.o libc.o ebcdic.o menu.o sclp.o \ -+ kdump2.o kdump.o entry.o stage2.lds -+stage3.exec: head.o stage3.o kdump3.o libc.o ebcdic.o ebcdic_conv.o sclp.o \ -+ sclp_stage3.o kdump.o entry.o stage3.lds - - %.exec: %.o -- @STAGE=$$( \ -+ STAGE=$$( \ - echo $@ | awk ' \ - match($$0,/[0-9]+b*/){ \ - print substr($$0,RSTART,RLENGTH) \ -@@ -59,7 +73,7 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ - 2) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage2.lds";; \ - 3) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3.lds";; \ - esac; \ -- $(LINK) $$SFLAGS -m64 $^ -o $@ -+ $(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@ - - %.bin: %.exec - $(OBJCOPY) -O binary \ -@@ -70,6 +84,7 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ - --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 \ -@@ -103,6 +118,6 @@ data.h: data.o - - clean: - rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \ -- stage3.bin -+ stage3.bin *.lds *.lds.d - - .PHONY: all clean -diff --git a/zipl/boot/cio.c b/zipl/boot/cio.c -index 453feaf..8a0d140 100644 ---- a/zipl/boot/cio.c -+++ b/zipl/boot/cio.c -@@ -12,7 +12,7 @@ - #include "cio.h" - #include "error.h" - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - static unsigned long initial_lpm = 0x00; - static const char *msg_essch = "Start subchannel failed"; -diff --git a/zipl/boot/cio.h b/zipl/boot/cio.h -index 0f49e08..fc39608 100644 ---- a/zipl/boot/cio.h -+++ b/zipl/boot/cio.h -@@ -12,7 +12,7 @@ - #define CIO_H - - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - /* Condition codes */ - #define CC_INITIATED 0 -diff --git a/zipl/boot/ebcdic.c b/zipl/boot/ebcdic.c -new file mode 100644 -index 0000000..be3441b ---- /dev/null -+++ b/zipl/boot/ebcdic.c -@@ -0,0 +1,30 @@ -+/* -+ * EBCDIC specific functions -+ * -+ * Copyright IBM Corp. 2013, 2020 -+ * -+ * 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 "ebcdic.h" -+ -+ -+/* -+ * Convert ebcdic string to number with given base -+ */ -+unsigned long ebcdic_strtoul(char *nptr, char **endptr, int base) -+{ -+ unsigned long val = 0; -+ -+ while (ebcdic_isdigit(*nptr)) { -+ if (val != 0) -+ val *= base; -+ val += *nptr - 0xf0; -+ nptr++; -+ } -+ if (endptr) -+ *endptr = (char *)nptr; -+ return val; -+} -diff --git a/zipl/boot/ebcdic.h b/zipl/boot/ebcdic.h -new file mode 100644 -index 0000000..e5392dd ---- /dev/null -+++ b/zipl/boot/ebcdic.h -@@ -0,0 +1,45 @@ -+/* -+ * EBCDIC specific functions -+ * -+ * Copyright IBM Corp. 2013, 2020 -+ * -+ * 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 EBCDIC_H -+#define EBCDIC_H -+ -+#include "lib/zt_common.h" -+ -+ -+#ifndef __ASSEMBLER__ -+ -+unsigned long ebcdic_strtoul(char *, char **, int); -+ -+static __always_inline int ecbdic_isspace(char c) -+{ -+ return (c == 0x40) || (c == 0x05) || (c == 0x15) || (c == 0x25) || -+ (c == 0x0b) || (c == 0x0c) || (c == 0x0d); -+} -+ -+static __always_inline int ebcdic_isdigit(char c) -+{ -+ return (c >= 0xf0) && (c <= 0xf9); -+} -+ -+static __always_inline int ebcdic_isupper(char c) -+{ -+ return (c >= 0xC1 && c <= 0xC9) || (c >= 0xD1 && c <= 0xD9) || -+ (c >= 0xE2 && c <= 0xE9); -+} -+ -+static __always_inline char ebcdic_tolower(char c) -+{ -+ if (ebcdic_isupper(c)) -+ c -= 0x40; -+ return c; -+} -+#endif /* __ASSEMBLER__ */ -+#endif /* EBCDIC_H */ -diff --git a/zipl/boot/ebcdic_conv.c b/zipl/boot/ebcdic_conv.c -new file mode 100644 -index 0000000..7fc6086 ---- /dev/null -+++ b/zipl/boot/ebcdic_conv.c -@@ -0,0 +1,167 @@ -+/* -+ * EBCDIC conversion functions -+ * -+ * Copyright IBM Corp. 2013, 2020 -+ * -+ * 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 "ebcdic_conv.h" -+#include "libc.h" -+#include "boot/s390.h" -+ -+ -+static unsigned char ebcdic_037[256] = { -+/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ -+ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, -+/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ -+ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -+/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -+ -ENP ->LF */ -+ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, -+/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -+ -IUS */ -+ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -+ -INP */ -+ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, -+/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -+ -SW */ -+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, -+/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ -+ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, -+/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ -+ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, -+/* 0x40 SP RSP ä ---- */ -+ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, -+/* 0x48 . < ( + | */ -+ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, -+/* 0x50 & ---- */ -+ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, -+/* 0x58 ß ! $ * ) ; */ -+ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, -+/* 0x60 - / ---- Ä ---- ---- ---- */ -+ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, -+/* 0x68 ---- , % _ > ? */ -+ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, -+/* 0x70 --- ---- ---- ---- ---- ---- ---- */ -+ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+/* 0x78 * ` : # @ ' = " */ -+ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, -+/* 0x80 * a b c d e f g */ -+ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, -+/* 0x88 h i ---- ---- ---- */ -+ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, -+/* 0x90 ° j k l m n o p */ -+ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, -+/* 0x98 q r ---- ---- */ -+ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, -+/* 0xA0 ~ s t u v w x */ -+ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, -+/* 0xA8 y z ---- ---- ---- ---- */ -+ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, -+/* 0xB0 ^ ---- § ---- */ -+ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, -+/* 0xB8 ---- [ ] ---- ---- ---- ---- */ -+ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, -+/* 0xC0 { A B C D E F G */ -+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, -+/* 0xC8 H I ---- ö ---- */ -+ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, -+/* 0xD0 } J K L M N O P */ -+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, -+/* 0xD8 Q R ---- ü */ -+ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, -+/* 0xE0 \ S T U V W X */ -+ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, -+/* 0xE8 Y Z ---- Ö ---- ---- ---- */ -+ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, -+/* 0xF0 0 1 2 3 4 5 6 7 */ -+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, -+/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ -+ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 -+}; -+ -+static unsigned char ebcdic_500[256] = { -+/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ -+ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, -+/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ -+ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -+/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -+ -ENP ->LF */ -+ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, -+/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -+ -IUS */ -+ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -+ -INP */ -+ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, -+/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -+ -SW */ -+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, -+/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ -+ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, -+/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ -+ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, -+/* 0x40 SP RSP ä ---- */ -+ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, -+/* 0x48 . < ( + | */ -+ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, -+/* 0x50 & ---- */ -+ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, -+/* 0x58 ß ! $ * ) ; */ -+ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, -+/* 0x60 - / ---- Ä ---- ---- ---- */ -+ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, -+/* 0x68 ---- , % _ > ? */ -+ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, -+/* 0x70 --- ---- ---- ---- ---- ---- ---- */ -+ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+/* 0x78 * ` : # @ ' = " */ -+ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, -+/* 0x80 * a b c d e f g */ -+ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, -+/* 0x88 h i ---- ---- ---- */ -+ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, -+/* 0x90 ° j k l m n o p */ -+ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, -+/* 0x98 q r ---- ---- */ -+ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, -+/* 0xA0 ~ s t u v w x */ -+ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, -+/* 0xA8 y z ---- ---- ---- ---- */ -+ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, -+/* 0xB0 ^ ---- § ---- */ -+ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, -+/* 0xB8 ---- [ ] ---- ---- ---- ---- */ -+ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, -+/* 0xC0 { A B C D E F G */ -+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, -+/* 0xC8 H I ---- ö ---- */ -+ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, -+/* 0xD0 } J K L M N O P */ -+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, -+/* 0xD8 Q R ---- ü */ -+ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, -+/* 0xE0 \ S T U V W X */ -+ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, -+/* 0xE8 Y Z ---- Ö ---- ---- ---- */ -+ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, -+/* 0xF0 0 1 2 3 4 5 6 7 */ -+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, -+/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ -+ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 -+}; -+ -+void ebcdic_to_ascii(unsigned char *target, const unsigned char *source, -+ unsigned int l) -+{ -+ unsigned char *ebc; -+ unsigned int i; -+ -+ ebc = is_zvm() ? ebcdic_037 : ebcdic_500; -+ for (i = 0; i < l; i++) -+ target[i] = ebc[source[i]]; -+} -diff --git a/zipl/boot/ebcdic_conv.h b/zipl/boot/ebcdic_conv.h -new file mode 100644 -index 0000000..af7e726 ---- /dev/null -+++ b/zipl/boot/ebcdic_conv.h -@@ -0,0 +1,21 @@ -+/* -+ * EBCDIC conversion functions -+ * -+ * Copyright IBM Corp. 2013, 2020 -+ * -+ * 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 EBCDIC_CONV_H -+#define EBCDIC_CONV_H -+ -+ -+#ifndef __ASSEMBLER__ -+ -+void ebcdic_to_ascii(unsigned char *target, const unsigned char *source, -+ unsigned int l); -+ -+#endif /* __ASSEMBLER__ */ -+#endif /* EBCDIC_CONV_H */ -diff --git a/zipl/boot/eckd0_cdl.S b/zipl/boot/eckd0_cdl.S -index c3f6f48..8a537aa 100644 ---- a/zipl/boot/eckd0_cdl.S -+++ b/zipl/boot/eckd0_cdl.S -@@ -13,9 +13,13 @@ - # The 1st CCW reads stage 1 IPL record. - # The 2nd CCW tics to the loaded stage 1 IPL record. - # -+ -+#include "boot/loaders_layout.h" -+#include "boot/s390.h" -+ - .org 0x0 - .globl _start - _start: -- .long 0x00080000,0x80002018 # PSW :the first 24 byte are loaded by IPL -+ .quad PSW_LOAD+STAGE2_ENTRY # PSW :the first 24 byte are loaded by IPL - .long 0x06000018,0x60000068 # read IPL record 2 - .long 0x08000018,0x00000000 # TIC to stage 1 -diff --git a/zipl/boot/eckd0_ldl.S b/zipl/boot/eckd0_ldl.S -index 31d3510..78bf7eb 100644 ---- a/zipl/boot/eckd0_ldl.S -+++ b/zipl/boot/eckd0_ldl.S -@@ -14,9 +14,13 @@ - # The 2nd CCW reads stage 1 IPL record (re-read record 1) and continues with - # command chaining at address 0x18 - # -+ -+#include "boot/loaders_layout.h" -+#include "boot/s390.h" -+ - .org 0x0 - .globl _start - _start: -- .long 0x00080000,0x80002018 # PSW :the first 24 byte are loaded by IPL -+ .quad PSW_LOAD+STAGE2_ENTRY # PSW :the first 24 byte are loaded by IPL - .long 0x16002000,0x60000016 # Read record zero - .long 0x06000000,0x60000080 # Read IPL record 1 again -diff --git a/zipl/boot/eckd2.c b/zipl/boot/eckd2.c -index 3943d45..7ac6da2 100644 ---- a/zipl/boot/eckd2.c -+++ b/zipl/boot/eckd2.c -@@ -10,7 +10,7 @@ - */ - - #include "eckd.h" --#include "s390.h" -+#include "boot/s390.h" - #include "stage2.h" - - int extract_length(void *data) -diff --git a/zipl/boot/eckd2dump.c b/zipl/boot/eckd2dump.c -index 3cfa158..28598ad 100644 ---- a/zipl/boot/eckd2dump.c -+++ b/zipl/boot/eckd2dump.c -@@ -12,7 +12,7 @@ - #include "cio.h" - #include "eckd2dump.h" - #include "error.h" --#include "s390.h" -+#include "boot/s390.h" - #include "stage2dump.h" - - #define ECKD_CCW_LOCATE_RECORD 0x47 -diff --git a/zipl/boot/eckd2dump_mv.c b/zipl/boot/eckd2dump_mv.c -index 9fb25fd..964db10 100644 ---- a/zipl/boot/eckd2dump_mv.c -+++ b/zipl/boot/eckd2dump_mv.c -@@ -9,6 +9,8 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "lib/zt_common.h" -+ - #include "eckd2dump.h" - #include "error.h" - #include "stage2dump.h" -@@ -19,8 +21,7 @@ - /* - * Magic number at start of dump record - */ --uint64_t magic __attribute__((section(".stage2.head"))) -- = 0x584d554c54363401ULL; /* XMULT64, version 1 */ -+uint64_t __section(.stage2.head) magic = 0x584d554c54363401ULL; /* XMULT64, version 1 */ - - /* - * Parameter format for ECKD MV dumper (13 bytes): -@@ -59,8 +60,7 @@ struct mvdump_parm_table { - (MAX_DUMP_VOLUMES * (sizeof(struct mvdump_param) + 1))]; - } __packed; - --static struct mvdump_parm_table mvdump_table -- __attribute__((section(".eckd2dump_mv.tail"))); -+static struct mvdump_parm_table __section(.eckd2dump_mv.tail) mvdump_table; - - static int volnr_current; - -diff --git a/zipl/boot/eckd2dump_sv.c b/zipl/boot/eckd2dump_sv.c -index 047cc22..84e783b 100644 ---- a/zipl/boot/eckd2dump_sv.c -+++ b/zipl/boot/eckd2dump_sv.c -@@ -9,6 +9,8 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "lib/zt_common.h" -+ - #include "eckd2dump.h" - #include "error.h" - #include "stage2dump.h" -@@ -16,8 +18,7 @@ - /* - * Magic number at start of dump record - */ --uint64_t magic __attribute__((section(".stage2.head"))) = -- 0x5845434b44363401ULL; /* "XECKD64", version 1 */ -+uint64_t __section(.stage2.head) magic = 0x5845434b44363401ULL; /* "XECKD64", version 1 */ - - /* - * ECKD parameter block passed by zipl -diff --git a/zipl/boot/error.h b/zipl/boot/error.h -index 715973e..63eae4b 100644 ---- a/zipl/boot/error.h -+++ b/zipl/boot/error.h -@@ -71,4 +71,10 @@ - #define ENOTIME 0x00004605 /* The zipl time stamps do not match */ - #define ENOMSS 0x00004606 /* Could not enable MSS */ - -+/* -+ * PV error codes -+ */ -+#define ENOPV 0x00004607 /* No support for PV */ -+#define EPV 0x00004608 /* PV error */ -+ - #endif /* ERROR_H */ -diff --git a/zipl/boot/fba0.S b/zipl/boot/fba0.S -index d8f4769..1119f3d 100644 ---- a/zipl/boot/fba0.S -+++ b/zipl/boot/fba0.S -@@ -15,10 +15,14 @@ - # The 1st CCW reads block 0 again with READ IPL - # The 2nd CCW tics to the re-loaded stage 1 IPL record. - # -+ -+#include "boot/loaders_layout.h" -+#include "boot/s390.h" -+ - .org 0x0 - .globl _start - _start: -- .long 0x00080000,0x80002018 # The first 24 byte are loaded -+ .quad PSW_LOAD+STAGE2_ENTRY # The first 24 byte are loaded - .long 0x02000000,0x60000000+.Lend # by ipl to addresses 0-23. - .long 0x08000000+.Ltic,0x40000000 # Tic to continuation - # -diff --git a/zipl/boot/fba2.c b/zipl/boot/fba2.c -index 7d3b5cc..1a570c2 100644 ---- a/zipl/boot/fba2.c -+++ b/zipl/boot/fba2.c -@@ -11,7 +11,7 @@ - - #include "fba.h" - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - int extract_length(void *data) { - struct linear_blockptr *blockptr = (struct linear_blockptr *)data; -diff --git a/zipl/boot/fba2dump.c b/zipl/boot/fba2dump.c -index 9a8f5fe..100556b 100644 ---- a/zipl/boot/fba2dump.c -+++ b/zipl/boot/fba2dump.c -@@ -9,19 +9,19 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "lib/zt_common.h" - #include "error.h" - #include "fba.h" - #include "stage2dump.h" - --#define BLK_PWRT 64 /* Blocks per write */ -+#define BLK_PWRT 64U /* Blocks per write */ - #define BLK_SIZE 0x200 /* FBA block size */ - #define BLK_PER_PAGE (PAGE_SIZE / BLK_SIZE) /* FBA blocks per page */ - - /* - * Magic number at start of dump record - */ --uint64_t magic __attribute__((section(".stage2.head"))) -- = 0x5844464241363401ULL; /* XDFBA64, version 1 */ -+uint64_t __section(.stage2.head) magic = 0x5844464241363401ULL; /* XDFBA64, version 1 */ - - /* - * FBA dump device partition specification -diff --git a/zipl/boot/head.S b/zipl/boot/head.S -index cc68b8a..f2395f6 100644 ---- a/zipl/boot/head.S -+++ b/zipl/boot/head.S -@@ -9,16 +9,18 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "boot/sigp.h" -+ - .section .text.start - .globl _start - _start: - basr %r13,0 - 0: la %r7,2 /* First try code 2: */ - la %r6,0 /* 64 bit psws are restored */ -- sigp %r7,%r6,0x12 /* Switch to 64 bit */ -+ sigp %r7,%r6,SIGP_SET_ARCHITECTURE /* Switch to 64 bit */ - bc 8,.Lswitched_64-0b(%r13) /* Accepted ? */ - la %r7,1 /* Failed - try code 1 */ -- sigp %r7,%r6,0x12 /* Switch to 64 bit */ -+ sigp %r7,%r6,SIGP_SET_ARCHITECTURE /* Switch to 64 bit */ - .Lswitched_64: - sam64 /* Switch to 64 bit addr mode */ - basr %r13,0 -diff --git a/zipl/boot/kdump.c b/zipl/boot/kdump.c -index fc0281e..eb3ae07 100644 ---- a/zipl/boot/kdump.c -+++ b/zipl/boot/kdump.c -@@ -13,7 +13,7 @@ - #include "kdump.h" - #include "libc.h" - #include "menu.h" --#include "s390.h" -+#include "boot/s390.h" - - void kdump_failed(unsigned long reason) - { -diff --git a/zipl/boot/kdump.h b/zipl/boot/kdump.h -index f6fae1c..f81d20c 100644 ---- a/zipl/boot/kdump.h -+++ b/zipl/boot/kdump.h -@@ -14,7 +14,7 @@ - #define KDUMP_H - - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - #define OS_INFO_VERSION_MAJOR_SUPPORTED 1 - #define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */ -diff --git a/zipl/boot/kdump3.c b/zipl/boot/kdump3.c -index 3fff01d..5d118dd 100644 ---- a/zipl/boot/kdump3.c -+++ b/zipl/boot/kdump3.c -@@ -100,9 +100,9 @@ void kdump_stage3(void) - { - unsigned long crash_base, crash_size; - -- if (!(stage3_flags & STAGE3_FLAG_KDUMP)) -+ if (!(_stage3_parms.flags & STAGE3_FLAG_KDUMP)) - return; -- if (!(stage3_flags & STAGE3_FLAG_SCSI)) -+ if (!(_stage3_parms.flags & STAGE3_FLAG_SCSI)) - kdump_stage3_dasd(&crash_base, &crash_size); - else - kdump_stage3_scsi(&crash_base, &crash_size); -diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c -index bd88d15..3442a86 100644 ---- a/zipl/boot/libc.c -+++ b/zipl/boot/libc.c -@@ -9,11 +9,16 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "libc.h" -+ - #include - -+#include "lib/zt_common.h" -+#include "boot/s390.h" -+ - #include "error.h" --#include "libc.h" - #include "sclp.h" -+#include "ebcdic.h" - - extern char __heap_start[]; - extern char __heap_stop[]; -@@ -125,24 +130,6 @@ int strncmp(const char *s1, const char *s2, unsigned long count) - return 0; - } - --/* -- * Convert ebcdic string to number with given base -- */ --unsigned long ebcstrtoul(char *nptr, char **endptr, int base) --{ -- unsigned long val = 0; -- -- while (ebc_isdigit(*nptr)) { -- if (val != 0) -- val *= base; -- val += *nptr - 0xf0; -- nptr++; -- } -- if (endptr) -- *endptr = (char *) nptr; -- return val; --} -- - static int skip_atoi(const char **c) - { - int i = 0; -@@ -279,10 +266,10 @@ static char *number(char *buf, char *end, unsigned long val, - - /* prepare string in reverse order */ - len = 0; -- while (val) { -+ do { - tmp[len++] = vec[val % spec->base]; - val /= spec->base; -- } -+ } while (val); - - if (len > precision) - precision = len; -@@ -419,8 +406,11 @@ void printf(const char *fmt, ...) - buf[LINE_LENGTH - 2] = '.'; - buf[LINE_LENGTH - 3] = '.'; - } -- sclp_print(buf); - va_end(va); -+ sclp_print(buf); -+#ifdef ENABLE_SCLP_ASCII -+ sclp_print_ascii(buf); -+#endif /* ENABLE_SCLP_ASCII */ - } - - /* -@@ -471,7 +461,7 @@ void pgm_check_handler_fn(void) - libc_stop(psw_old->addr); - } - --__attribute__ ((noinline)) void load_wait_psw(uint64_t psw_mask, struct psw_t *psw) -+void __noinline load_wait_psw(uint64_t psw_mask, struct psw_t *psw) - { - struct psw_t wait_psw = { .mask = psw_mask, .addr = 0 }; - struct psw_t old_psw, *wait_psw_ptr = &wait_psw; -@@ -511,7 +501,7 @@ void initialize(void) - /* - * Load disabled wait PSW with reason code in address field - */ --void libc_stop(unsigned long reason) -+void __noreturn libc_stop(unsigned long reason) - { - struct psw_t psw; - -diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h -index 68ecd00..7c4df72 100644 ---- a/zipl/boot/libc.h -+++ b/zipl/boot/libc.h -@@ -11,7 +11,8 @@ - #ifndef LIBC_H - #define LIBC_H - --#define NULL ((void *) 0) -+#include -+#include - - #define EPERM 1 /* Operation not permitted */ - #define ENOENT 2 /* No such file or directory */ -@@ -42,11 +43,6 @@ - #define MIB (1024ULL * 1024) - #define LINE_LENGTH 80 /* max line length printed by printf */ - --typedef unsigned long long uint64_t; --typedef unsigned int uint32_t; --typedef unsigned short uint16_t; --typedef unsigned char uint8_t; -- - void printf(const char *, ...); - void snprintf(char *buf, unsigned long size, const char *fmt, ...); - void *memcpy(void *, const void *, unsigned long); -@@ -54,16 +50,23 @@ void *memmove(void *, const void *, unsigned long); - void *memset(void *, int c, unsigned long); - char *strcat(char *, const char *); - int strncmp(const char *, const char *, unsigned long); --unsigned long ebcstrtoul(char *, char **, int); - int strlen(const char *); - char *strcpy(char *, const char *); - unsigned long get_zeroed_page(void); - void free_page(unsigned long); - void initialize(void); --void libc_stop(unsigned long) __attribute__((noreturn)); -+void libc_stop(unsigned long); - void start(void); - void pgm_check_handler(void); - void pgm_check_handler_fn(void); -+void panic_notify(unsigned long reason); -+ -+#define panic(reason, ...) \ -+ do { \ -+ printf(__VA_ARGS__); \ -+ panic_notify(reason); \ -+ libc_stop(reason); \ -+ } while (0) - - static inline int isdigit(int c) - { -@@ -75,28 +78,4 @@ static inline int isspace(char c) - return (c == 32) || (c >= 9 && c <= 13); - } - --static inline int ebc_isspace(char c) --{ -- return (c == 0x40) || (c == 0x05) || (c == 0x15) || (c == 0x25) || -- (c == 0x0b) || (c == 0x0c) || (c == 0x0d); --} -- --static inline int ebc_isdigit(char c) --{ -- return (c >= 0xf0) && (c <= 0xf9); --} -- --static inline int ebc_isupper(char c) --{ -- return (c >= 0xC1 && c <= 0xC9) || (c >= 0xD1 && c <= 0xD9) || -- (c >= 0xE2 && c <= 0xE9); --} -- --static inline char ebc_tolower(char c) --{ -- if (ebc_isupper(c)) -- c -= 0x40; -- return c; --} -- - #endif /* LIBC_H */ -diff --git a/zipl/boot/menu.c b/zipl/boot/menu.c -index 35ac4de..1e40c08 100644 ---- a/zipl/boot/menu.c -+++ b/zipl/boot/menu.c -@@ -3,7 +3,7 @@ - * - * Bootmenu Subroutines - * -- * Copyright IBM Corp. 2013, 2017 -+ * Copyright IBM Corp. 2013, 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. -@@ -12,6 +12,9 @@ - #include "libc.h" - #include "menu.h" - #include "sclp.h" -+#include "ebcdic.h" -+#include "boot/linux_layout.h" -+#include "boot/loaders_layout.h" - - static const char *msg_econfig = "Error: undefined configuration\n"; - -@@ -27,7 +30,6 @@ static void menu_prompt(int timeout) - static int menu_read(void) - { - char *temp_area = (char *)get_zeroed_page(); -- uint16_t *configs = __stage2_params.config; - int timeout, rc, i, count = 0; - char *endptr; - int value; -@@ -56,11 +58,11 @@ static int menu_read(void) - /* input under zVM needs to be converted to lower case */ - if (is_zvm()) - for (i = 0; i < count; i++) -- temp_area[i] = ebc_tolower(temp_area[i]); -- value = ebcstrtoul((char *)temp_area, &endptr, 10); -+ temp_area[i] = ebcdic_tolower(temp_area[i]); -+ value = ebcdic_strtoul((char *)temp_area, &endptr, 10); - - if ((endptr != temp_area) && (value < BOOT_MENU_ENTRIES - 1) && -- (configs[value] != 0)) { -+ (__stage2_params.config[value] != 0)) { - /* valid config found - finish */ - break; - } else { -@@ -80,14 +82,13 @@ out_free_page: - - static int menu_list(void) - { -- uint16_t *configs = __stage2_params.config; - char *name; - int i; - - for (i = 0; i < BOOT_MENU_ENTRIES; i++) { -- if (configs[i] == 0) -+ if (__stage2_params.config[i] == 0) - continue; -- name = configs[i] + ((void *)&__stage2_params); -+ name = __stage2_params.config[i] + ((void *)&__stage2_params); - printf("%s\n", name); - if (i == 0) - printf("\n"); -@@ -114,7 +115,7 @@ static int menu_param(unsigned long *value) - int i; - - if (!sclp_param(loadparm)) -- *value = ebcstrtoul(loadparm, &endptr, 10); -+ *value = ebcdic_strtoul(loadparm, &endptr, 10); - - /* got number, done */ - if (endptr != loadparm) -@@ -123,7 +124,7 @@ static int menu_param(unsigned long *value) - /* no number, check for keyword */ - i = 0; - /* skip leading whitespaces */ -- while (ebc_isspace(loadparm[i]) && (i < PARAM_SIZE)) -+ while ((i < PARAM_SIZE) && ecbdic_isspace(loadparm[i])) - i++; - - if (!strncmp(&loadparm[i], "PROMPT", 6)) { -@@ -136,7 +137,6 @@ static int menu_param(unsigned long *value) - - int menu(void) - { -- uint16_t *configs = __stage2_params.config; - unsigned long value = 0; - char *cmd_line_extra; - char endstring[15]; -@@ -178,11 +178,11 @@ int menu(void) - - boot: - /* sanity - config entry not valid */ -- if (configs[value] == 0) -+ if (__stage2_params.config[value] == 0) - panic(EINTERNAL, "%s", msg_econfig); - - printf("Booting %s\n", -- (char *)(configs[value] + -+ (char *)(__stage2_params.config[value] + - (void *)&__stage2_params + TEXT_OFFSET)); - - /* append 'BOOT_IMAGE=' to parmline */ -diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h -index ee0f2c5..c6d688c 100644 ---- a/zipl/boot/menu.h -+++ b/zipl/boot/menu.h -@@ -14,11 +14,6 @@ - - #include "stage2.h" - --/* address of extra command line */ --#define COMMAND_LINE_EXTRA 0xE000 -- --/* max command line length */ --#define COMMAND_LINE_SIZE 896 - #define BOOT_MENU_ENTRIES 63 - #define PARAM_SIZE 8 - #define TEXT_OFFSET 4 -diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h -index 6e5a7ae..886233b 100644 ---- a/zipl/boot/s390.h -+++ b/zipl/boot/s390.h -@@ -11,14 +11,9 @@ - #ifndef S390_H - #define S390_H - --#include "../../include/lib/zt_common.h" --#include "libc.h" -+#include "lib/zt_common.h" -+#include "boot/sigp.h" - --#define __pa32(x) ((uint32_t)(unsigned long)(x)) --#define __pa(x) ((unsigned long)(x)) --#define MIN(x, y) ((x) < (y) ? (x) : (y)) --#define barrier() __asm__ __volatile__("": : :"memory") --#define inline inline __attribute__((always_inline)) - - /* - * Helper macro for exception table entries -@@ -195,7 +190,7 @@ struct _lowcore { - - /* align to the top of the prefix area */ - uint8_t pad_0x1900[0x2000-0x1900]; /* 0x1900 */ --} __packed; -+} __packed __aligned(8192); - - #define S390_lowcore (*((struct _lowcore *) 0)) - -@@ -213,7 +208,7 @@ do { \ - libc_stop(reason); \ - } while (0) - --static inline int page_is_valid(unsigned long addr) -+static __always_inline int page_is_valid(unsigned long addr) - { - unsigned long tmp; - int rc; -@@ -233,7 +228,7 @@ static inline int page_is_valid(unsigned long addr) - return rc; - } - --static inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) -+static __always_inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) - { - register unsigned long reg2 asm("2") = (unsigned long) buf; - register unsigned long reg3 asm("3") = (unsigned long) len; -@@ -261,7 +256,7 @@ static inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) - "i" (low), "i" (high)); \ - }) - --static inline void __ctl_set_bit(unsigned int cr, unsigned int bit) -+static __always_inline void __ctl_set_bit(unsigned int cr, unsigned int bit) - { - unsigned long reg; - -@@ -281,7 +276,7 @@ enum diag308_subcode { - DIAG308_STORE = 6, - }; - --static inline int diag308(unsigned long subcode, void *addr) -+static __always_inline int diag308(unsigned long subcode, void *addr) - { - register unsigned long _addr asm("0") = (unsigned long) addr; - register unsigned long _rc asm("1") = 0; -@@ -294,47 +289,10 @@ static inline int diag308(unsigned long subcode, void *addr) - return _rc; - } - --/* -- * Signal Processor -- */ --#define SIGP_STOP_AND_STORE_STATUS 9 --#define SIGP_SET_MULTI_THREADING 22 --#define SIGP_STORE_ASTATUS_AT_ADDRESS 23 -- --#define SIGP_CC_ORDER_CODE_ACCEPTED 0 --#define SIGP_CC_BUSY 2 -- --static inline int sigp(uint16_t addr, uint8_t order, uint32_t parm, -- uint32_t *status) --{ -- register unsigned int reg1 asm ("1") = parm; -- int cc; -- -- asm volatile( -- " sigp %1,%2,0(%3)\n" -- " ipm %0\n" -- " srl %0,28\n" -- : "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc"); -- if (status && cc == 1) -- *status = reg1; -- return cc; --} -- --static inline int sigp_busy(uint16_t addr, uint8_t order, uint32_t parm, -- uint32_t *status) --{ -- int cc; -- -- do { -- cc = sigp(addr, order, parm, status); -- } while (cc == SIGP_CC_BUSY); -- return cc; --} -- - /* - * Store CPU address - */ --static inline unsigned short stap(void) -+static __always_inline unsigned short stap(void) - { - unsigned short cpu_address; - -@@ -345,7 +303,7 @@ static inline unsigned short stap(void) - /* - * Program the clock comparator - */ --static inline void set_clock_comparator(uint64_t time) -+static __always_inline void set_clock_comparator(uint64_t time) - { - asm volatile("sckc %0" : : "Q" (time)); - } -@@ -353,7 +311,7 @@ static inline void set_clock_comparator(uint64_t time) - /* - * Program the CPU timer - */ --static inline void set_cpu_timer(uint64_t timer) -+static __always_inline void set_cpu_timer(uint64_t timer) - { - asm volatile("spt %0" : : "Q" (timer)); - } -@@ -361,7 +319,7 @@ static inline void set_cpu_timer(uint64_t timer) - /* - * Get current time (store clock) - */ --static inline unsigned long long get_tod_clock(void) -+static __always_inline unsigned long long get_tod_clock(void) - { - unsigned long long clk; - -@@ -379,7 +337,7 @@ struct cpuid { - unsigned int unused:16; - } __packed __aligned(8); - --static inline void get_cpu_id(struct cpuid *ptr) -+static __always_inline void get_cpu_id(struct cpuid *ptr) - { - asm volatile("stidp %0" : "=Q" (*ptr)); - } -@@ -387,7 +345,7 @@ static inline void get_cpu_id(struct cpuid *ptr) - /* - * Check if we run under z/VM - */ --static inline int is_zvm(void) -+static __always_inline int is_zvm(void) - { - struct cpuid cpuid; - -@@ -405,7 +363,7 @@ typedef struct { - /* - * Save vector registers - */ --static inline void save_vx_regs(__vector128 *vxrs) -+static __always_inline void save_vx_regs(__vector128 *vxrs) - { - typedef struct { __vector128 _[32]; } addrtype; - -@@ -419,7 +377,7 @@ static inline void save_vx_regs(__vector128 *vxrs) - /* - * Save vector registers safe - */ --static inline void save_vx_regs_safe(__vector128 *vxrs) -+static __always_inline void save_vx_regs_safe(__vector128 *vxrs) - { - unsigned long cr0; - -@@ -432,7 +390,7 @@ static inline void save_vx_regs_safe(__vector128 *vxrs) - - #define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ - --static inline int __test_facility(unsigned long nr, void *facilities) -+static __always_inline int __test_facility(unsigned long nr, void *facilities) - { - unsigned char *ptr; - -@@ -447,17 +405,29 @@ static inline int __test_facility(unsigned long nr, void *facilities) - * That makes it easier to query facility bits with the bit number as - * documented in the Principles of Operation. - */ --static inline int test_facility(unsigned long nr) -+static __always_inline int test_facility(unsigned long nr) - { - return __test_facility(nr, &S390_lowcore.stfle_fac_list); - } - -+static __always_inline unsigned long __stfle_asm(uint64_t *stfle_fac_list, int size) -+{ -+ register unsigned long reg0 asm("0") = size - 1; -+ -+ asm volatile( -+ ".insn s,0xb2b00000,0(%1)" /* stfle */ -+ : "+d" (reg0) -+ : "a" (stfle_fac_list) -+ : "memory", "cc"); -+ return reg0; -+} -+ - /** - * stfle - Store facility list extended - * @stfle_fac_list: array where facility list can be stored - * @size: size of passed in array in double words - */ --static inline void stfle(u64 *stfle_fac_list, int size) -+static __always_inline void stfle(uint64_t *stfle_fac_list, int size) - { - unsigned long nr; - -@@ -470,13 +440,8 @@ static inline void stfle(u64 *stfle_fac_list, int size) - memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); - if (S390_lowcore.stfl_fac_list & 0x01000000) { - /* More facility bits available with stfle */ -- register unsigned long reg0 asm("0") = size - 1; -- -- asm volatile(".insn s,0xb2b00000,0(%1)" /* stfle */ -- : "+d" (reg0) -- : "a" (stfle_fac_list) -- : "memory", "cc"); -- nr = (reg0 + 1) * 8; /* # bytes stored by stfle */ -+ nr = __stfle_asm(stfle_fac_list, size); -+ nr = MIN((nr + 1) * 8, size * 8UL); - } - memset((char *) stfle_fac_list + nr, 0, size * 8 - nr); - } -diff --git a/zipl/boot/sclp.c b/zipl/boot/sclp.c -index 04006b2..95229ac 100644 ---- a/zipl/boot/sclp.c -+++ b/zipl/boot/sclp.c -@@ -9,9 +9,14 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "libc.h" - #include "error.h" --#include "s390.h" -+#include "boot/s390.h" - #include "sclp.h" -+#include "ebcdic.h" -+#ifdef ENABLE_SCLP_ASCII -+# include "ebcdic_conv.h" -+#endif /* ENABLE_SCLP_ASCII */ - - /* Perform service call. Return 0 on success, non-zero otherwise. */ - static int sclp_service_call(unsigned int command, void *sccb) -@@ -125,20 +130,24 @@ int sclp_setup(int initialise) - - switch (initialise) { - case SCLP_INIT: -- sccb->receive_mask = 0x80000000; -- sccb->send_mask = 0x40000000; -+ sccb->receive_mask = SCLP_EVENT_MASK_OPCMD; -+ sccb->send_mask = SCLP_EVENT_MASK_MSG; - break; - case SCLP_DISABLE: -- sccb->receive_mask = 0x0; -- sccb->send_mask = 0x0; -+ sccb->receive_mask = SCLP_EVENT_MASK_DISABLE; -+ sccb->send_mask = SCLP_EVENT_MASK_DISABLE; -+ break; -+ case SCLP_LINE_ASCII_INIT: -+ sccb->receive_mask = SCLP_EVENT_MASK_DISABLE; -+ sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_ASCII; - break; - case SCLP_HSA_INIT: -- sccb->receive_mask = 0x0; -- sccb->send_mask = 0x40000010; -+ sccb->receive_mask = SCLP_EVENT_MASK_DISABLE; -+ sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_SDIAS; - break; - case SCLP_HSA_INIT_ASYNC: -- sccb->receive_mask = 0x00000010; -- sccb->send_mask = 0x40000010; -+ sccb->receive_mask = SCLP_EVENT_MASK_SDIAS; -+ sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_SDIAS; - break; - } - -@@ -160,6 +169,48 @@ out_free_page: - return rc; - } - -+#ifdef ENABLE_SCLP_ASCII -+/* Content of @buffer must be EBCDIC encoded. The function used for -+ * the conversion `ebcdic_to_ascii` differentiates whether the code -+ * runs on z/VM or not and then selects the appropriate EBCDIC -+ * coding. -+ */ -+int sclp_print_ascii(const char *buffer) -+{ -+ struct write_sccb *sccb = NULL; -+ int rc, str_len = strlen(buffer); -+ unsigned long data_len = str_len + 1; -+ -+ /* don't overflow the sccb buffer */ -+ if (data_len > SCCB_MAX_DATA_LEN) -+ data_len = SCCB_MAX_DATA_LEN; -+ -+ sccb = (void *)get_zeroed_page(); -+ sccb->header.length = sizeof(struct write_sccb) - sizeof(struct mdb) -+ + data_len; -+ sccb->header.function_code = SCLP_FC_NORMAL_WRITE; -+ sccb->msg_buf.header.length = sizeof(struct msg_buf) - sizeof(struct mdb) -+ + data_len; -+ sccb->msg_buf.header.type = SCLP_EVENT_DATA_ASCII; -+ sccb->msg_buf.header.flags = 0; -+ ebcdic_to_ascii(sccb->msg_buf.data, -+ (const unsigned char *)buffer, -+ data_len - 1); -+ sccb->msg_buf.data[data_len - 1] = '\0'; -+ -+ /* SCLP command for write data */ -+ rc = start_sclp(SCLP_CMD_WRITE_DATA, sccb); -+ if (rc || sccb->header.response_code != 0x20) { -+ rc = 1; -+ goto out_free_page; -+ } -+ rc = 0; -+out_free_page: -+ free_page((unsigned long) sccb); -+ return rc; -+} -+#endif /* ENABLE_SCLP_ASCII */ -+ - int sclp_print(char *buffer) - { - struct write_sccb *sccb; -diff --git a/zipl/boot/sclp.h b/zipl/boot/sclp.h -index ffee587..a215dd1 100644 ---- a/zipl/boot/sclp.h -+++ b/zipl/boot/sclp.h -@@ -13,7 +13,7 @@ - #define SCLP_H - - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - /* vector keys and ids */ - #define GDS_ID_MDSMU 0x1310 -@@ -28,10 +28,18 @@ - #define SCLP_CMD_READ_INFO2 0x00020001 - #define SCLP_CMD_READ_DATA 0x00770005 - --#define PSW_EXT_MASK 0x00080000ULL --#define PSW_EXT_ADDR 0x80000000ULL --#define PSW_WAIT_MASK 0x010a0000ULL --#define PSW_WAIT_ADDR 0x00000000ULL -+/* SCLP function codes */ -+#define SCLP_FC_NORMAL_WRITE 0 -+ -+/* SCLP event data types */ -+#define SCLP_EVENT_DATA_ASCII 0x1a -+ -+/* SCLP event masks */ -+#define SCLP_EVENT_MASK_DISABLE 0x00000000 -+#define SCLP_EVENT_MASK_SDIAS 0x00000010 -+#define SCLP_EVENT_MASK_ASCII 0x00000040 -+#define SCLP_EVENT_MASK_MSG 0x40000000 -+#define SCLP_EVENT_MASK_OPCMD 0x80000000 - - #define CTL_SERVICE_SIGNAL 0x0200 - #define CTL_CLOCK_COMPARATOR 0x0800 -@@ -40,6 +48,11 @@ - #define SCLP_DISABLE 0x1 - #define SCLP_HSA_INIT 0x2 - #define SCLP_HSA_INIT_ASYNC 0x3 -+#define SCLP_LINE_ASCII_INIT 0x4 -+ -+#define SCCB_SIZE PAGE_SIZE -+#define SCCB_MAX_DATA_LEN (SCCB_SIZE - sizeof(struct sccb_header) \ -+ - sizeof(struct evbuf_header)) - - typedef uint32_t sccb_mask_t; - -@@ -53,19 +66,23 @@ struct gds_subvector { - uint8_t key; - } __packed; - -+/* Structure must not have any padding */ - struct sccb_header { - uint16_t length; - uint8_t function_code; - uint8_t control_mask[3]; - uint16_t response_code; --} __packed; -+}; -+STATIC_ASSERT(sizeof(struct sccb_header) == 2 + 1 + 3 + 2) - -+/* Structure must not have any padding */ - struct evbuf_header { - uint16_t length; - uint8_t type; - uint8_t flags; - uint16_t _reserved; --} __packed; -+}; -+STATIC_ASSERT(sizeof(struct evbuf_header) == 2 + 1 + 1 + 2) - - struct mto { - uint16_t length; -@@ -104,7 +121,10 @@ struct mdb { - - struct msg_buf { - struct evbuf_header header; -- struct mdb mdb; -+ union { -+ struct mdb mdb; -+ uint8_t data[0]; -+ }; - } __packed; - - struct write_sccb { -@@ -154,6 +174,9 @@ struct read_sccb { - int start_sclp(unsigned int, void *); - int sclp_setup(int); - int sclp_print(char *); -+# ifdef ENABLE_SCLP_ASCII -+int sclp_print_ascii(const char *); -+# endif /* ENABLE_SCLP_ASCII */ - int sclp_param(char *); - int sclp_read(unsigned long, void *, int *); - int sclp_read_info(struct read_info_sccb *sccb); -diff --git a/zipl/boot/sclp_stage3.c b/zipl/boot/sclp_stage3.c -index 7deab4f..9acb670 100644 ---- a/zipl/boot/sclp_stage3.c -+++ b/zipl/boot/sclp_stage3.c -@@ -9,6 +9,7 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "libc.h" - #include "sclp.h" - #include "sclp_stage3.h" - -@@ -104,7 +105,6 @@ int sclp_hsa_get_size(unsigned long *hsa_size) - sccb = (void *)get_zeroed_page(); - sccb->header.length = sizeof(*sccb); - -- sccb->header.length = sizeof(*sccb); - sccb->evbuf.header.length = sizeof(struct sdias_evbuf); - sccb->evbuf.header.type = EVTYP_SDIAS; - sccb->evbuf.event_qual = EQ_SIZE; -diff --git a/zipl/boot/sclp_stage3.h b/zipl/boot/sclp_stage3.h -index ae0c05c..d115e0d 100644 ---- a/zipl/boot/sclp_stage3.h -+++ b/zipl/boot/sclp_stage3.h -@@ -17,9 +17,6 @@ - #define SDIAS_EVSTATE_ALL_STORED 0x00 - #define SDIAS_EVSTATE_PART_STORED 0x10 - --#define SDIAS_EVSTATE_ALL_STORED 0x00 --#define SDIAS_EVSTATE_PART_STORED 0x10 -- - struct sdias_evbuf { - struct evbuf_header header; - uint8_t event_qual; -@@ -40,10 +37,13 @@ struct sdias_evbuf { - uint16_t dbs; - } __packed; - -+/* Structure must not have any padding */ - struct sdias_sccb { - struct sccb_header header; - struct sdias_evbuf evbuf; --} __packed; -+}; -+STATIC_ASSERT(sizeof(struct sdias_sccb) == -+ sizeof(struct sccb_header) + sizeof(struct sdias_evbuf)) - - - int sclp_hsa_copy(void *, unsigned long, unsigned long); -diff --git a/zipl/boot/stage2.c b/zipl/boot/stage2.c -index 99e1bd1..78591c1 100644 ---- a/zipl/boot/stage2.c -+++ b/zipl/boot/stage2.c -@@ -9,10 +9,13 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "lib/zt_common.h" -+ - #include "error.h" - #include "libc.h" - #include "menu.h" --#include "s390.h" -+#include "boot/s390.h" -+#include "boot/loaders_layout.h" - #include "stage2.h" - - static int is_null_descriptor(disk_blockptr_t *address) -@@ -141,4 +144,4 @@ void panic_notify(unsigned long UNUSED(reason)) - { - } - --uint64_t stage2_head __attribute__((section(".stage2.head"))); -+uint64_t __section(.stage2.head) stage2_head; -diff --git a/zipl/boot/stage2.h b/zipl/boot/stage2.h -index 8fd5bd3..b29eea1 100644 ---- a/zipl/boot/stage2.h -+++ b/zipl/boot/stage2.h -@@ -12,15 +12,17 @@ - #ifndef COMMON_H - #define COMMON_H - --#include "cio.h" --#include "error.h" --#include "libc.h" --#include "s390.h" -+#include "lib/zt_common.h" - --#define DESCR_PER_BLOCK 16 -+#define DESCR_PER_BLOCK _AC(16, U) - --/* ADRESS */ --#define STAGE2_DESC 0x78 -+ -+#ifndef __ASSEMBLER__ -+ -+#include "libc.h" -+#include "boot/s390.h" -+#include "cio.h" -+#include "error.h" - - /* Layout of ECKD disk block pointer */ - struct eckd_blockptr { -@@ -74,6 +76,6 @@ int extract_length(void *); - int is_zero_block(void *); - void kdump_stage2(unsigned long); - -+#endif /* __ASSEMBLER__ */ - - #endif /* COMMON_H */ -- -diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds.S -similarity index 57% -rename from zipl/boot/stage2.lds -rename to zipl/boot/stage2.lds.S -index 34ed014..c4c9456 100644 ---- a/zipl/boot/stage2.lds -+++ b/zipl/boot/stage2.lds.S -@@ -25,15 +25,19 @@ - * 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes) - */ - -+#include "boot/loaders_layout.h" -+ - SECTIONS - { -- . = 0x78; -+ . = STAGE2_DESC; - __stage2_desc = .; - -- . = 0x2000; -+ . = STAGE2_LOAD_ADDRESS; - .stage2.head : { *(.stage2.head) } -- . = 0x2018; -- .text.start : { *(.text.start) } -+ . = STAGE2_ENTRY; -+ .text.start : { -+ *(.text.start) -+ } - .text : { *(.text) } - __ex_table_start = .; - .ex_table : { *(.ex_table) } -@@ -52,12 +56,38 @@ SECTIONS - .bss : { *(.bss) } - __bss_stop = .; - -- . = 0x6000; -+ . = STAGE2_HEAP_ADDRESS; - __heap_start = .; -- . = 0x9000; -+ .heap : { -+ . += STAGE2_HEAP_SIZE; -+ ASSERT(__heap_stop - __heap_start == STAGE2_HEAP_SIZE, -+ "Heap section doesn't conform to the described memory layout"); -+ } - __heap_stop = .; - -- . = 0xf000; -+ /* Memory reserved for stage3. Use a dummy section to check if changes -+ * in stage3 memory layout work with stage2 and vice versa. -+ */ -+ . = STAGE3_PARAMS_ADDRESS; -+ .stage3 : { -+ . += STAGE3_PARAMS_MAXIMUM_SIZE; -+ -+ . = STAGE3_LOAD_ADDRESS - STAGE3_PARAMS_ADDRESS; -+ . += STAGE3_MAXIMUM_SIZE; -+ -+ . = COMMAND_LINE_EXTRA - STAGE3_PARAMS_ADDRESS; -+ . += COMMAND_LINE_EXTRA_SIZE; -+ } -+ -+ . = STAGE2_STACK_ADDRESS; -+ __stack_start = .; -+ .stack : { -+ . += STAGE2_STACK_SIZE; -+ ASSERT(__stack_end - __stack_start == STAGE2_STACK_SIZE, -+ "Stack section doesn't conform to the described memory layout"); -+ } -+ __stack_end = .; -+ - .eh_frame : { *(.eh_frame) } - .note.gnu.build-id : { *(.note.gnu.build-id) } - } -diff --git a/zipl/boot/stage2dump.c b/zipl/boot/stage2dump.c -index 4fa3d15..9ba5d48 100644 ---- a/zipl/boot/stage2dump.c -+++ b/zipl/boot/stage2dump.c -@@ -11,6 +11,9 @@ - - #include - -+#include "lib/zt_common.h" -+ -+#include "libc.h" - #include "error.h" - #include "sclp.h" - #include "stage2dump.h" -@@ -34,8 +37,7 @@ struct ipib_info { - /* - * Tail parameters - */ --struct stage2dump_parm_tail parm_tail -- __attribute__ ((section(".stage2dump.tail"))) = { -+struct stage2dump_parm_tail parm_tail = { - .mem_upper_limit = 0xffffffffffffffffULL, - }; - -diff --git a/zipl/boot/stage2dump.h b/zipl/boot/stage2dump.h -index 2a7e7fa..784b299 100644 ---- a/zipl/boot/stage2dump.h -+++ b/zipl/boot/stage2dump.h -@@ -13,7 +13,7 @@ - #define STAGE2DUMP_H - - #include "libc.h" --#include "s390.h" -+#include "boot/s390.h" - - #define IPL_SC *((struct subchannel_id *) &S390_lowcore.subchannel_id) - #define ROUND_DOWN(x, a) ((x) & ~((typeof(x))(a) - 1)) -@@ -28,8 +28,7 @@ struct stage2dump_parm_tail { - uint64_t mem_upper_limit; - } __packed; - --extern struct stage2dump_parm_tail parm_tail -- __attribute__ ((section(".stage2dump.tail"))); -+extern struct stage2dump_parm_tail __section(.stage2dump.tail) parm_tail; - - /* - * S390 dump format defines -diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c -index f7a9597..bb490dc 100644 ---- a/zipl/boot/stage3.c -+++ b/zipl/boot/stage3.c -@@ -3,16 +3,23 @@ - * - * Main program for stage3 bootloader - * -- * Copyright IBM Corp. 2013, 2017 -+ * Copyright IBM Corp. 2013, 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 "libc.h" --#include "s390.h" -+#include "boot/sigp.h" -+#include "boot/s390.h" -+#include "boot/sigp.h" -+#include "boot/linux_layout.h" -+#include "boot/loaders_layout.h" -+ - #include "stage3.h" - #include "error.h" -+#include "ebcdic.h" -+#include "ebcdic_conv.h" - - #define for_each_rb_entry(entry, rb) \ - for (entry = rb->entries; \ -@@ -21,162 +28,9 @@ - - 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 const char *msg_sipl_noparm = "Secure boot failure: unable to load ipl parameter"; - --static unsigned char ebc_037[256] = { --/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ -- 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, --/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ -- 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, --/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -- -ENP ->LF */ -- 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, --/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -- -IUS */ -- 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, --/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -- -INP */ -- 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, --/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -- -SW */ -- 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, --/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ -- 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, --/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ -- 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, --/* 0x40 SP RSP ä ---- */ -- 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, --/* 0x48 . < ( + | */ -- 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, --/* 0x50 & ---- */ -- 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, --/* 0x58 ß ! $ * ) ; */ -- 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, --/* 0x60 - / ---- Ä ---- ---- ---- */ -- 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, --/* 0x68 ---- , % _ > ? */ -- 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, --/* 0x70 --- ---- ---- ---- ---- ---- ---- */ -- 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, --/* 0x78 * ` : # @ ' = " */ -- 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, --/* 0x80 * a b c d e f g */ -- 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, --/* 0x88 h i ---- ---- ---- */ -- 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, --/* 0x90 ° j k l m n o p */ -- 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, --/* 0x98 q r ---- ---- */ -- 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, --/* 0xA0 ~ s t u v w x */ -- 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, --/* 0xA8 y z ---- ---- ---- ---- */ -- 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, --/* 0xB0 ^ ---- § ---- */ -- 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, --/* 0xB8 ---- [ ] ---- ---- ---- ---- */ -- 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, --/* 0xC0 { A B C D E F G */ -- 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, --/* 0xC8 H I ---- ö ---- */ -- 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, --/* 0xD0 } J K L M N O P */ -- 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, --/* 0xD8 Q R ---- ü */ -- 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, --/* 0xE0 \ S T U V W X */ -- 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, --/* 0xE8 Y Z ---- Ö ---- ---- ---- */ -- 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, --/* 0xF0 0 1 2 3 4 5 6 7 */ -- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, --/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ -- 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 --}; -- --static unsigned char ebc_500[256] = { --/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ -- 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, --/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ -- 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, --/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -- -ENP ->LF */ -- 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, --/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -- -IUS */ -- 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, --/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -- -INP */ -- 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, --/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -- -SW */ -- 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, --/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ -- 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, --/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ -- 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, --/* 0x40 SP RSP ä ---- */ -- 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, --/* 0x48 . < ( + | */ -- 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, --/* 0x50 & ---- */ -- 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, --/* 0x58 ß ! $ * ) ; */ -- 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, --/* 0x60 - / ---- Ä ---- ---- ---- */ -- 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, --/* 0x68 ---- , % _ > ? */ -- 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, --/* 0x70 --- ---- ---- ---- ---- ---- ---- */ -- 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, --/* 0x78 * ` : # @ ' = " */ -- 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, --/* 0x80 * a b c d e f g */ -- 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, --/* 0x88 h i ---- ---- ---- */ -- 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, --/* 0x90 ° j k l m n o p */ -- 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, --/* 0x98 q r ---- ---- */ -- 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, --/* 0xA0 ~ s t u v w x */ -- 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, --/* 0xA8 y z ---- ---- ---- ---- */ -- 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, --/* 0xB0 ^ ---- § ---- */ -- 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, --/* 0xB8 ---- [ ] ---- ---- ---- ---- */ -- 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, --/* 0xC0 { A B C D E F G */ -- 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, --/* 0xC8 H I ---- ö ---- */ -- 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, --/* 0xD0 } J K L M N O P */ -- 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, --/* 0xD8 Q R ---- ü */ -- 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, --/* 0xE0 \ S T U V W X */ -- 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, --/* 0xE8 Y Z ---- Ö ---- ---- ---- */ -- 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, --/* 0xF0 0 1 2 3 4 5 6 7 */ -- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, --/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ -- 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 --}; -- --static void ebcdic_to_ascii(unsigned char *target, unsigned char *source, -- unsigned int l) --{ -- unsigned char *ebc; -- unsigned int i; -- -- ebc = is_zvm() ? ebc_037 : ebc_500; -- for (i = 0; i < l; i++) -- target[i] = ebc[source[i]]; --} -- --static void --start_kernel(void) -+static inline void __noreturn start_kernel(void) - { - struct psw_t *psw = &S390_lowcore.program_new_psw; - unsigned long addr, code; -@@ -194,11 +48,25 @@ start_kernel(void) - " sam31\n" - " sr %r1,%r1\n" - " sr %r2,%r2\n" -- " sigp %r1,%r2,0x12\n" -+ " sigp %r1,%r2,%[order]\n" - " lpsw 0\n" - : [addr] "=&d" (addr), - [code] "+&d" (code) -- : [psw] "a" (psw) ); -+ : [psw] "a" (psw), -+ [order] "L" (SIGP_SET_ARCHITECTURE)); -+ while (1); -+} -+ -+unsigned int store_ipl_parmblock(struct ipl_pl_hdr *pl_hdr) -+{ -+ int rc; -+ -+ rc = diag308(DIAG308_STORE, pl_hdr); -+ if (rc == DIAG308_RC_OK && -+ pl_hdr->version <= IPL_MAX_SUPPORTED_VERSION) -+ return 0; -+ -+ return 1; - } - - unsigned int -@@ -238,6 +106,10 @@ is_verified_address(unsigned long image_addr) - - rb_hdr = (void *) rb_hdr + rb_hdr->len; - } -+ -+ if (!comps) -+ return 0; -+ - for_each_rb_entry(comp, comps) { - if (image_addr == comp->addr && - comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED && -@@ -251,20 +123,23 @@ unsigned int - secure_boot_enabled() - { - struct ipl_pl_hdr *pl_hdr; -- unsigned long tmp; -+ unsigned int rc; - -- tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; -- pl_hdr = (struct ipl_pl_hdr *) tmp; -+ pl_hdr = (void *)get_zeroed_page(); -+ if (!pl_hdr || store_ipl_parmblock(pl_hdr)) -+ panic(ESECUREBOOT, "%s", msg_sipl_noparm); -+ rc = !!(pl_hdr->flags & IPL_FLAG_SECURE); -+ free_page((unsigned long) pl_hdr); - -- return pl_hdr->flags & IPL_FLAG_SECURE; -+ return rc; - } - - void start(void) - { - unsigned int subchannel_id; - unsigned char *cextra = (unsigned char *)COMMAND_LINE_EXTRA; -- unsigned char *command_line = (unsigned char *)COMMAND_LINE; -- unsigned int begin = 0, end = 0, length = 0; -+ unsigned char *cmdline = (unsigned char *)COMMAND_LINE; -+ unsigned int cmdline_len = 0, cextra_len = 0; - - /* - * IPL process is secure we have to use default IPL values and -@@ -272,80 +147,84 @@ void start(void) - * verified component. If it is not IPL is aborted. - */ - if (secure_boot_enabled()) { -- if (_image_addr != DEFAULT_IMAGE_ADDR || -- _load_psw != DEFAULT_PSW_LOAD) -+ if (_stage3_parms.image_addr != IMAGE_LOAD_ADDRESS || -+ _stage3_parms.load_psw != DEFAULT_PSW_LOAD) - panic(ESECUREBOOT, "%s", msg_sipl_inval); - -- if (!is_verified_address(_load_psw & PSW_ADDR_MASK)) -+ if (!is_verified_address(_stage3_parms.load_psw & PSW32_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); -+ memmove((void *)_stage3_parms.image_addr, -+ (void *)_stage3_parms.image_addr + IMAGE_LOAD_ADDRESS, -+ _stage3_parms.image_len - IMAGE_LOAD_ADDRESS); - - /* store subchannel ID into low core and into new kernel space */ - subchannel_id = S390_lowcore.subchannel_id; - *(unsigned int *)__LC_IPLDEV = subchannel_id; -- *(unsigned int *)IPL_DEVICE = subchannel_id; -+ *(unsigned long long *)IPL_DEVICE = subchannel_id; - - /* if valid command line is given, copy it into new kernel space */ -- if (_parm_addr != UNSPECIFIED_ADDRESS) { -- memcpy((void *)COMMAND_LINE, -- (void *)(unsigned long *)_parm_addr, COMMAND_LINE_SIZE); -+ if (_stage3_parms.parm_addr != UNSPECIFIED_ADDRESS) { -+ memcpy(cmdline, -+ (void *)(unsigned long *)_stage3_parms.parm_addr, -+ COMMAND_LINE_SIZE); - /* terminate \0 */ -- *(char *)(COMMAND_LINE + COMMAND_LINE_SIZE - 1) = 0; -+ cmdline[COMMAND_LINE_SIZE - 1] = 0; - } - - /* convert extra parameter to ascii */ -- if (!_extra_parm || !*cextra) -+ if (!_stage3_parms.extra_parm || !*cextra) - goto noextra; - - /* Handle extra kernel parameters specified in DASD boot menu. */ - ebcdic_to_ascii(cextra, cextra, COMMAND_LINE_SIZE); - -- /* remove leading whitespace */ -- while (begin <= COMMAND_LINE_SIZE && cextra[begin] == 0x20) -- begin++; -- - /* determine length of extra parameter */ -- while (length <= COMMAND_LINE_SIZE && cextra[length] != 0) -- length++; -+ cextra_len = MIN(strlen((const char *)cextra), COMMAND_LINE_SIZE - 1); -+ -+ /* remove leading whitespace of extra parameter */ -+ while (cextra_len > 0 && *cextra == 0x20) { -+ cextra++; -+ cextra_len--; -+ } - -- /* find end of original parm line */ -- while (command_line[end] != 0 && command_line[end] != 0) -- end++; -+ /* determine length of original parm line */ -+ cmdline_len = MIN(strlen((const char *)cmdline), -+ COMMAND_LINE_SIZE - 1); - - /* - * if extra parm string starts with '=' replace original string, - * else append - */ -- if (cextra[begin] == 0x3d) { -- memcpy((void *)COMMAND_LINE, (void *)(cextra + begin), -- length); -- } else { -- /* check if length is within max value */ -- length = (end + 1 + length <= COMMAND_LINE_SIZE) ? length : -- (COMMAND_LINE_SIZE - end - 1); -+ if (*cextra == 0x3d && cextra_len >= 1) { -+ /* skip '=' */ -+ cextra++; -+ cextra_len--; -+ memcpy(cmdline, cextra, cextra_len); -+ cmdline[cextra_len] = 0; -+ } else if (cmdline_len + 1 <= COMMAND_LINE_SIZE - 1) { - /* add blank */ -- command_line[end] = 0x20; -- end++; -+ cmdline[cmdline_len] = 0x20; -+ cmdline_len++; -+ /* check if length is within max value */ -+ cextra_len = (cmdline_len + cextra_len <= COMMAND_LINE_SIZE - 1) ? -+ cextra_len : (COMMAND_LINE_SIZE - 1 - cmdline_len); - /* append string */ -- memcpy((void *)(command_line + end), -- (void *)(cextra + begin), length); -+ memcpy(cmdline + cmdline_len, cextra, cextra_len); - /* terminate 0 */ -- command_line[end + length] = 0; -+ cmdline[cmdline_len + cextra_len] = 0; - } - - noextra: - /* copy initrd start address and size intop new kernle space */ -- *(unsigned long long *)INITRD_START = _initrd_addr; -- *(unsigned long long *)INITRD_SIZE = _initrd_len; -+ *(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr; -+ *(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len; - - /* store address of new kernel to 0 to be able to start it */ -- *(unsigned long long *)0 = _load_psw; -+ *(unsigned long long *)0 = _stage3_parms.load_psw; - - kdump_stage3(); - -diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h -index 3a02001..a11257e 100644 ---- a/zipl/boot/stage3.h -+++ b/zipl/boot/stage3.h -@@ -12,171 +12,34 @@ - #ifndef STAGE3_H - #define STAGE3_H - --#include "libc.h" --#include "s390.h" -+#include "lib/zt_common.h" -+#include "boot/s390.h" -+#include "boot/ipl.h" -+#include "boot/linux_layout.h" - --#define IPL_DEVICE 0x10404 --#define INITRD_START 0x10408 --#define INITRD_SIZE 0x10410 --#define OLDMEM_BASE 0x10418 --#define OLDMEM_SIZE 0x10420 --#define COMMAND_LINE 0x10480 --#define COMMAND_LINE_SIZE 896 --#define COMMAND_LINE_EXTRA 0xE000 - - #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, -+#define DEFAULT_PSW_LOAD 0x0008000080010000UL -+ -+#define UNSPECIFIED_ADDRESS -1UL -+ -+/* Stage 3 bootloader parameter structure */ -+/* Structure must not have any padding */ -+struct stage3_parms { -+ unsigned long long parm_addr; /* address of parmline */ -+ unsigned long long initrd_addr; /* address of initrd */ -+ unsigned long long initrd_len; /* length of initrd */ -+ unsigned long long load_psw; /* load psw of kernel */ -+ unsigned long long extra_parm; /* use extra parm line mechanism? */ -+ unsigned long long flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */ -+ unsigned long long image_len; /* length of kernel */ -+ unsigned long long image_addr; /* target address of kernel */ - }; -+STATIC_ASSERT(sizeof(struct stage3_parms) == 8 * 8) - --/* 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 struct stage3_parms _stage3_parms; - extern void kdump_stage3(); - - #endif /* STAGE3_H */ -diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds -index c67bc32..a9f17fb 100644 ---- a/zipl/boot/stage3.lds -+++ b/zipl/boot/stage3.lds -@@ -10,7 +10,7 @@ - * 0x6000-0x8fff free - * 0x9000-0x9fff Stage3 parameter - * 0xa000-0xdfff Stage3 code + data -- * 0xe000-0xffff Stack -+ * 0xf000-0xffff Stack - */ - - SECTIONS -@@ -25,21 +25,7 @@ SECTIONS - - /* stage 3 parameter */ - . = 0x9000; -- _parm_addr = .; -- . = 0x9008; -- _initrd_addr = .; -- . = 0x9010; -- _initrd_len = .; -- . = 0x9018; -- _load_psw = .; -- . = 0x9020; -- _extra_parm = .; -- . = 0x9028; -- stage3_flags =.; -- . = 0x9030; -- _image_len = .; -- . = 0x9038; -- _image_addr = .; -+ _stage3_parms = .; - - . = 0xa000; - .text.start : { *(.text.start) } -diff --git a/zipl/boot/stage3.lds.S b/zipl/boot/stage3.lds.S -new file mode 100644 -index 0000000..2362d4f ---- /dev/null -+++ b/zipl/boot/stage3.lds.S -@@ -0,0 +1,62 @@ -+/* -+ * 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 -+ * 0xf000-0xffff Stack -+ */ -+ -+#include "boot/loaders_layout.h" -+ -+SECTIONS -+{ -+ . = 0x0; -+ -+ . = STAGE3_HEAP_ADDRESS; -+ __heap_start = .; -+ .heap : { -+ . += STAGE3_HEAP_SIZE; -+ ASSERT(__heap_stop - __heap_start == STAGE3_HEAP_SIZE, -+ "Heap section doesn't conform to the described memory layout"); -+ } -+ __heap_stop = .; -+ -+ /* stage 3 parameter */ -+ . = STAGE3_PARAMS_ADDRESS; -+ _stage3_parms = .; -+ -+ . = STAGE3_ENTRY; -+ .text.start : { *(.text.start) } -+ .text : { *(.text) } -+ __ex_table_start = .; -+ .ex_table : { *(.ex_table) } -+ __ex_table_stop = .; -+ .eh_frame : { *(.eh_frame) } -+ -+ __bss_start = .; -+ .bss : { *(.bss) } -+ __bss_stop = .; -+ .rodata : {*(.rodata) } -+ .data : { *(.data) } -+ -+ . = COMMAND_LINE_EXTRA; -+ .cmdline_extra : { -+ . += COMMAND_LINE_EXTRA_SIZE; -+ } -+ -+ . = STAGE3_STACK_ADDRESS; -+ __stack_start = .; -+ .stack : { -+ . += STAGE3_STACK_SIZE; -+ ASSERT(__stack_end - __stack_start == STAGE3_STACK_SIZE, -+ "Stack section doesn't conform to the described memory layout"); -+ } -+ __stack_end = .; -+} -diff --git a/zipl/boot/tape0.S b/zipl/boot/tape0.S -index 2b1e3f4..e7c605d 100644 ---- a/zipl/boot/tape0.S -+++ b/zipl/boot/tape0.S -@@ -7,28 +7,23 @@ - # it under the terms of the MIT license. See LICENSE for details. - # - -+#include "boot/sigp.h" -+#include "boot/linux_layout.h" -+#include "boot/s390.h" -+ - IPL_BS = 1024 # block size for tape access - IPL_OFF = 0x4000 # temporary kernel load addr --COMMAND_LINE_SIZE = 896 # max command line length --KERNEL_OFF = 0x10000 # kernel start code offset -+KERNEL_OFF = IMAGE_ENTRY # kernel start code offset - # relative to image start - __LC_IO_NEW_PSW = 0x1f0 # IO New PSW addr - - --# Parameter address offsets -- --PARMAREA = 0x10400 # Parameter area offset --IPL_DEVICE = 0x10400 # IPL device offset --INITRD_START = 0x10408 # ramdisk addr offset --INITRD_SIZE = 0x10410 # ramdisk size offset --COMMAND_LINE = 0x10480 # command line offset -- - # Default IPL parameter - will be overwritten by zIPL - - RAMDISK_ORIGIN = 0x800000 # default ramdisk load addr - RAMDISK_SIZE = 0x800000 # default ramdisk size - PARMFILE_ADDR = 0x1000 # default parmfile load addr --KERNEL_ADDR = 0x10000 # default kernel load addr -+KERNEL_ADDR = IMAGE_ENTRY # default kernel load addr - - - .org 0x0 -@@ -37,7 +32,7 @@ _start: - - # Stage 0 loader - loads first block of this loader - -- .long 0x00080000,0x80000000+iplstart # PSW to start execution -+ .quad PSW_LOAD+iplstart # PSW to start execution - .long 0x27000000,0x60000001 # backward Space Block - .long 0x02000000,0x20000000+IPL_BS # read IPL_BS bytes to addr 0 - -@@ -63,7 +58,7 @@ _parm_addr: - _initrd_addr: - .quad RAMDISK_ORIGIN # ramdisk load address - _load_psw: -- .quad 0x0008000080000000+KERNEL_ADDR # kernel start PSW -+ .quad PSW_LOAD+KERNEL_ADDR # kernel start PSW - - # - # Subroutine to load from tape until tape mark -@@ -184,10 +179,10 @@ iplstart: - 0: - la %r7,2 #/* First try code 2: */ - la %r6,0 #/* 64 bit psws are restored */ -- sigp %r7,%r6,0x12 #/* Switch to 64 bit */ -+ sigp %r7,%r6,SIGP_SET_ARCHITECTURE #/* Switch to 64 bit */ - bc 8,.Lswitched_64-0b(%r13) #/* Accepted ? */ - la %r7,1 #/* Failed - try code 1 */ -- sigp %r7,%r6,0x12 #/* Switch to 64 bit */ -+ sigp %r7,%r6,SIGP_SET_ARCHITECTURE #/* Switch to 64 bit */ - .Lswitched_64: - sam64 #/* Switch to 64 bit addr mode */ - basr %r13,0 -diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c -index 800b943..638c524 100644 ---- a/zipl/boot/tape2dump.c -+++ b/zipl/boot/tape2dump.c -@@ -9,9 +9,13 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include "libc.h" -+#include "lib/zt_common.h" -+#include "boot/loaders_layout.h" -+#include "boot/s390.h" -+ - #include "cio.h" - #include "error.h" --#include "libc.h" - #include "stage2dump.h" - - #define BLK_SIZE 0x8000 /* We write 32 KB at a time */ -@@ -33,8 +37,8 @@ struct tape_head { - uint64_t ccw2; - } __packed; - --struct tape_head tape_head __attribute__((section(".stage2.head"))) = { -- .psw = 0x0008000080002018ULL, /* Start code at 0x2018 */ -+struct tape_head __section(.stage2.head) tape_head = { -+ .psw = PSW_LOAD | STAGE2_ENTRY, /* Start code at 0x2018 */ - .ccw1 = 0x0700000060000001ULL, /* Rewind ccw */ - .ccw2 = 0x0200200020003000ULL, /* CCW to load dump tool to 0x2000 */ - }; -diff --git a/zipl/include/boot.h b/zipl/include/boot.h -index 6bd2a9b..35cb981 100644 ---- a/zipl/include/boot.h -+++ b/zipl/include/boot.h -@@ -14,6 +14,8 @@ - - #include - -+#include "lib/zt_common.h" -+ - #include "disk.h" - #include "job.h" - #include "zipl.h" -@@ -51,7 +53,7 @@ struct scsi_dump_sb { - uint64_t csum_offset; - uint64_t csum_size; - uint64_t csum; --} __attribute((packed)); -+} __packed; - - #define SCSI_DUMP_SB_MAGIC 0x5a46435044554d50ULL; /* ZFCPDUMP */ - /* To avoid a csum entry of 0 a seed is used */ -@@ -63,7 +65,7 @@ struct scsi_dump_sb { - struct scsi_dump_param { - uint64_t block; - uint64_t reserved; --} __attribute((packed)); -+} __packed; - /* ECKD dump parameter */ - - struct eckd_dump_param { -@@ -73,14 +75,14 @@ struct eckd_dump_param { - uint8_t num_heads; - uint8_t bpt; - char reserved[4]; --} __attribute((packed, may_alias)); -+} __packed __may_alias; - - /* FBA dump parameter */ - - struct fba_dump_param { - uint64_t start_blk; - uint64_t blockct; --} __attribute((packed)); -+} __packed; - - struct boot_info_bp_dump { - union { -@@ -89,7 +91,7 @@ struct boot_info_bp_dump { - struct scsi_dump_param scsi; - } param; - uint8_t unused[16]; --} __attribute__ ((packed)); -+} __packed; - - /* - * Layout of block pointer for linear devices -@@ -101,7 +103,7 @@ struct linear_blockptr { - uint16_t size; - uint16_t blockct; - uint8_t reserved[4]; --} __attribute((packed)); -+} __packed; - - /* - * Layout of block pointer for cylinder/head/sector devices -@@ -115,7 +117,7 @@ struct eckd_blockptr { - uint16_t size; - uint8_t blockct; - uint8_t reserved[8]; --} __attribute((packed)); -+} __packed; - - struct boot_info_bp_ipl { - union { -@@ -123,7 +125,7 @@ struct boot_info_bp_ipl { - struct linear_blockptr lin; - } bm_ptr; - uint8_t unused[16]; --} __attribute__ ((packed)); -+} __packed; - - struct boot_info { - char magic[4]; -@@ -135,7 +137,7 @@ struct boot_info { - struct boot_info_bp_dump dump; - struct boot_info_bp_ipl ipl; - } bp; --} __attribute__ ((packed)); -+} __packed; - - struct boot_ccw0 { - uint8_t cmd; -@@ -144,21 +146,21 @@ struct boot_ccw0 { - uint8_t flags; - uint8_t pad; - uint16_t count; --} __attribute__ ((packed)); -+} __packed; - - /* Boot data structures for FBA disks */ - - struct boot_fba_locread { - struct boot_ccw0 locate; - struct boot_ccw0 read; --} __attribute__ ((packed)); -+} __packed; - - struct boot_fba_locdata { - uint8_t command; - uint8_t dummy; - uint16_t blockct; - uint32_t blocknr; --} __attribute__ ((packed)); -+} __packed; - - struct boot_fba_stage0 { - uint64_t psw; -@@ -169,13 +171,13 @@ struct boot_fba_stage0 { - struct boot_fba_locdata locdata[2]; - uint64_t reserved[4]; - struct boot_info boot_info; --} __attribute__ ((packed)); -+} __packed; - - struct boot_fba_stage1b { - struct boot_fba_locread locread[STAGE2_BLK_CNT_MAX]; - struct boot_fba_locdata locdata[STAGE2_BLK_CNT_MAX]; - uint8_t unused[448]; --} __attribute__ ((packed)); -+} __packed; - - /* Boot data structures for ECKD disks */ - -@@ -184,14 +186,14 @@ struct boot_eckd_ccw1 { - uint8_t flags; - uint16_t count; - uint32_t address; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_ssrt { - struct boot_ccw0 seek; - struct boot_ccw0 search; - struct boot_ccw0 tic; - struct boot_ccw0 read; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_seekarg { - uint16_t pad; -@@ -199,32 +201,32 @@ struct boot_eckd_seekarg { - uint16_t head; - uint8_t sec; - uint8_t pad2; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_cdl_stage0 { - uint64_t psw; - struct boot_ccw0 read; - struct boot_ccw0 tic; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_ldl_stage0 { - uint64_t psw; - struct boot_ccw0 read_r0; - struct boot_ccw0 read_r1; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_stage1 { - struct boot_eckd_ssrt ssrt[2]; - struct boot_ccw0 tic1b; - struct boot_eckd_seekarg seek[2]; - struct boot_info boot_info; --} __attribute__ ((packed)); -+} __packed; - - struct boot_eckd_stage1b { - struct boot_eckd_ssrt ssrt[STAGE2_BLK_CNT_MAX]; - struct boot_eckd_seekarg seek[STAGE2_BLK_CNT_MAX]; - uint8_t unused[64]; --} __attribute__ ((packed)); -+} __packed; - - /* Stage 2 boot menu parameter structure */ - -@@ -236,25 +238,8 @@ struct boot_stage2_params { - uint16_t banner; - uint16_t config[BOOT_MENU_ENTRIES + 1]; - uint64_t config_kdump; --} __attribute__ ((packed)); -- -- --/* Stage 3 bootloader parameter structure */ -- --struct boot_stage3_params { -- uint64_t parm_addr; -- uint64_t initrd_addr; -- uint64_t initrd_len; -- 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)); -+} __packed; - --#define STAGE3_FLAG_SCSI 0x0001 --#define STAGE3_FLAG_KDUMP 0x0002 - - /* Tape IPL bootloader parameter structure */ - -@@ -275,7 +260,7 @@ struct mvdump_param { - uint8_t blocksize; - uint8_t bpt; - uint8_t num_heads; --} __attribute__ ((packed)); -+} __packed; - - struct mvdump_parm_table { - uint64_t timestamp; -@@ -284,7 +269,7 @@ struct mvdump_parm_table { - uint8_t ssid[MAX_DUMP_VOLUMES]; - unsigned char reserved[512 - sizeof(uint64_t) - sizeof(uint16_t) - - (MAX_DUMP_VOLUMES * (sizeof(struct mvdump_param) + 1))]; --} __attribute__ ((packed)); -+} __packed; - - void boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, - void *param); -@@ -310,8 +295,8 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, - int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job); - 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); -+ address_t entry, int extra_parm, uint64_t flags, -+ address_t image_addr, 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 4a0bd04..f49e394 100644 ---- a/zipl/include/bootmap.h -+++ b/zipl/include/bootmap.h -@@ -12,6 +12,8 @@ - #ifndef BOOTMAP_H - #define BOOTMAP_H - -+#include "lib/zt_common.h" -+ - #include "disk.h" - #include "job.h" - #include "zipl.h" -@@ -23,7 +25,7 @@ struct signature_header { - uint8_t format; - uint8_t reserved[3]; - uint32_t length; --} __attribute((packed)); -+} __packed; - - typedef union { - uint64_t load_address; -diff --git a/zipl/include/misc.h b/zipl/include/misc.h -index 5a349a7..f222d01 100644 ---- a/zipl/include/misc.h -+++ b/zipl/include/misc.h -@@ -46,6 +46,7 @@ char* misc_make_path(char* dirname, char* filename); - int misc_temp_dev(dev_t dev, int blockdev, char** devno); - int misc_temp_dev_from_file(char* file, char** devno); - void misc_free_temp_dev(char* device); -+void misc_free_temp_file(char *filename); - int misc_check_writable_directory(const char* directory); - int misc_check_readable_file(const char* filename); - int misc_check_writable_device(const char* devno, int blockdev, int chardev); -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index e5842ed..8ed839e 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -14,32 +14,17 @@ - - #include - #include "lib/zt_common.h" -+#include "boot/loaders_layout.h" - - #define ZIPL_MAGIC "zIPL" - #define ZIPL_MAGIC_SIZE 4 - #define DISK_LAYOUT_ID 0x00000001 - --#define ZIPL_STAGE2_LOAD_ADDRESS 0x2000 --#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 0x80000000UL - #define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */ --#define UNSPECIFIED_ADDRESS -1ULL --#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 UNSPECIFIED_ADDRESS -1UL -+#define MAXIMUM_PARMLINE_SIZE 0x380UL -+#define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000UL - - #define BOOTMAP_FILENAME "bootmap" - #define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX" -@@ -75,10 +60,6 @@ typedef uint64_t address_t; - * resulting return code or 0. */ - #define DRY_RUN_FUNC(x) (dry_run ? 0 : (x)) - --#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) --#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) --#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -- - extern int verbose; - extern int interactive; - extern int dry_run; -diff --git a/zipl/man/zipl.8.in b/zipl/man/zipl.8.in -index f12e27b..70e6134 100644 ---- a/zipl/man/zipl.8.in -+++ b/zipl/man/zipl.8.in -@@ -59,9 +59,9 @@ tool implements a boot menu which includes the following features: - .IP " -" - display a list of available configurations - .IP " -" --allow to choose a configuration -+choose a configuration - .IP " -" --allow to specify additional kernel command line parameters -+specify additional kernel command line parameters - .PP - - See the -@@ -360,11 +360,11 @@ Control the zIPL secure boot support. - 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 -+ Write signatures regardless of support indicated by the local -+ system. Missing signatures for stage 3 and kernel IPL files - will result in an error. - 0 -- No signatures will be written. -+ Do not write signatures. - - - .SH EXAMPLE -diff --git a/zipl/man/zipl.conf.5.in b/zipl/man/zipl.conf.5.in -index 20731b0..41bf976 100644 ---- a/zipl/man/zipl.conf.5.in -+++ b/zipl/man/zipl.conf.5.in -@@ -538,17 +538,17 @@ non-default memory location. - .B Configuration section: - .br - Control the zIPL secure boot support. --Set this option to one of the following: -+Set this option to one of the following values: - .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. -+Write signatures regardless of support indicated by the local system. -+Missing signatures for stage 3 and kernel IPL files will result in an error. - .IP " - " 12 - .BR 0: --No signatures will be written. -+Do not write signatures. - - The default value for - .B 'secure' -diff --git a/zipl/src/Makefile b/zipl/src/Makefile -index fd77670..61d0e67 100644 ---- a/zipl/src/Makefile -+++ b/zipl/src/Makefile -@@ -42,11 +42,13 @@ clean: - - # Additional manual dependencies - -+.boot.o.d boot.o: ../boot/data.h -+ - ../boot/data.h: -- make -C ../boot data.h -+ $(MAKE) -C ../boot data.h - - ../boot/data.o: -- make -C ../boot data.o -+ $(MAKE) -C ../boot data.o - - ../boot/stage3.bin: -- make -C ../boot stage3.bin -+ $(MAKE) -C ../boot stage3.bin -diff --git a/zipl/src/boot.c b/zipl/src/boot.c -index b13bcb7..281b5b2 100644 ---- a/zipl/src/boot.c -+++ b/zipl/src/boot.c -@@ -17,6 +17,8 @@ - #include - #include - -+#include "stage3.h" -+ - #include "../boot/data.h" - #include "boot.h" - #include "bootmap.h" -@@ -79,14 +81,14 @@ boot_check_data(void) - int - 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) -+ address_t entry, int extra_parm, uint64_t flags, -+ address_t image_addr, size_t image_len) - { -- struct boot_stage3_params params; -+ struct stage3_parms params; - void* data; - -- if (image_addr != (image_addr & PSW_ADDRESS_MASK)) { -- error_reason("Kernel image load address to high (31 bit " -+ if (entry != (entry & PSW32_ADDR_MASK)) { -+ error_reason("Kernel image entry point to high (31 bit " - "addressing mode)"); - return -1; - } -@@ -99,7 +101,7 @@ boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, - params.parm_addr = (uint64_t) parm_addr; - params.initrd_addr = (uint64_t) initrd_addr; - params.initrd_len = (uint64_t) initrd_len; -- params.load_psw = (uint64_t)(image_addr | PSW_LOAD); -+ params.load_psw = (uint64_t)(entry | PSW_LOAD); - params.extra_parm = (uint64_t) extra_parm; - params.flags = flags; - params.image_len = (uint64_t) image_len; -@@ -118,7 +120,7 @@ boot_init_fba_stage0(struct boot_fba_stage0 *stage0, - blocknum_t i; - - /* Initialize stage 0 data */ -- memcpy(stage0, DATA_ADDR(fba0), sizeof(*stage0)); -+ memcpy(stage0, DATA_ADDR(fba0), DATA_SIZE(fba0)); - /* Fill in blocklist for stage 2 loader */ - if (stage1b_count > STAGE1B_BLK_CNT_MAX) { - error_reason("Not enough room for FBA stage 1b loader"); -@@ -138,7 +140,7 @@ boot_init_fba_stage0(struct boot_fba_stage0 *stage0, - void - boot_init_eckd_ldl_stage0(struct boot_eckd_ldl_stage0 *stage0) - { -- memcpy(stage0, DATA_ADDR(eckd0_ldl), sizeof(*stage0)); -+ memcpy(stage0, DATA_ADDR(eckd0_ldl), DATA_SIZE(eckd0_ldl)); - /* Fill in size of stage 1 plus stage 0 loader */ - stage0->read_r1.count = sizeof(struct boot_eckd_stage1) + - sizeof(struct boot_eckd_ldl_stage0); -@@ -147,7 +149,7 @@ boot_init_eckd_ldl_stage0(struct boot_eckd_ldl_stage0 *stage0) - void - boot_init_eckd_cdl_stage0(struct boot_eckd_cdl_stage0 *stage0) - { -- memcpy(stage0, DATA_ADDR(eckd0_cdl), sizeof(*stage0)); -+ memcpy(stage0, DATA_ADDR(eckd0_cdl), DATA_SIZE(eckd0_cdl)); - /* Fill in size of stage 1 loader */ - stage0->read.count = sizeof(struct boot_eckd_stage1); - } -@@ -158,7 +160,7 @@ boot_init_eckd_stage1(struct boot_eckd_stage1 *stage1, - { - blocknum_t i; - -- memcpy(stage1, DATA_ADDR(eckd1), sizeof(*stage1)); -+ memcpy(stage1, DATA_ADDR(eckd1), DATA_SIZE(eckd1)); - /* Fill in blocklist for stage 1b loader */ - if (stage1b_count > STAGE1B_BLK_CNT_MAX) { - error_reason("Not enough room for ECKD stage 1b loader " -@@ -186,7 +188,7 @@ boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, - { - blocknum_t i; - -- memcpy(stage1b, DATA_ADDR(fba1b), sizeof(*stage1b)); -+ memcpy(stage1b, DATA_ADDR(fba1b), DATA_SIZE(fba1b)); - if (stage2_count > STAGE2_BLK_CNT_MAX) { - error_reason("Not enough room for FBA stage 2 loader"); - return -1; -@@ -195,7 +197,7 @@ boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, - stage1b->locdata[i].blocknr = - (uint32_t) stage2_list[i].linear.block; - stage1b->locread[i].read.address_lo = -- ZIPL_STAGE2_LOAD_ADDRESS + i * FBA_BLK_SIZE; -+ STAGE2_LOAD_ADDRESS + i * FBA_BLK_SIZE; - } - /* Terminate CCW chain */ - stage1b->locread[i - 1].read.flags &= ~CCW_FLAG_CC; -@@ -208,7 +210,7 @@ boot_init_eckd_stage1b(struct boot_eckd_stage1b *stage1b, - { - blocknum_t i; - -- memcpy(stage1b, DATA_ADDR(eckd1b), sizeof(*stage1b)); -+ memcpy(stage1b, DATA_ADDR(eckd1b), DATA_SIZE(eckd1b)); - if (stage2_count > STAGE2_BLK_CNT_MAX) { - error_reason("Not enough room for ECKD stage 2 loader " - "(try larger block size)"); -@@ -220,7 +222,7 @@ boot_init_eckd_stage1b(struct boot_eckd_stage1b *stage1b, - stage1b->seek[i].head = stage2_list[i].chs.head | - ((stage2_list[i].chs.cyl >> 12) & 0xfff0); - stage1b->seek[i].sec = stage2_list[i].chs.sec; -- stage1b->ssrt[i].read.address_lo = ZIPL_STAGE2_LOAD_ADDRESS + -+ stage1b->ssrt[i].read.address_lo = STAGE2_LOAD_ADDRESS + - i * stage2_list[i].chs.size; - stage1b->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI; - } -@@ -236,7 +238,7 @@ boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr, - struct boot_tape_ipl_params params; - void* buffer; - -- if (image_addr != (image_addr & PSW_ADDRESS_MASK)) { -+ if (image_addr != (image_addr & PSW32_ADDR_MASK)) { - error_reason("Kernel image load address to high (31 bit " - "addressing mode)"); - return -1; -diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c -index 5926622..30128c9 100644 ---- a/zipl/src/bootmap.c -+++ b/zipl/src/bootmap.c -@@ -18,8 +18,11 @@ - #include - #include - -+#include "lib/zt_common.h" - #include "lib/util_part.h" - #include "lib/util_path.h" -+#include "boot/s390.h" -+#include "stage3.h" - - #include "boot.h" - #include "bootmap.h" -@@ -128,7 +131,10 @@ check_secure_boot_support(void) - if (!fp) - return false; - -- fscanf(fp, "%d", &val); -+ if (fscanf(fp, "%d", &val) != 1) { -+ fclose(fp); -+ return false; -+ } - fclose(fp); - - return val ? true : false; -@@ -220,7 +226,7 @@ struct component_entry { - uint8_t data[23]; - uint8_t type; - component_data compdat; --} __attribute((packed)); -+} __packed; - - typedef enum { - component_execute = 0x01, -@@ -260,7 +266,7 @@ struct component_header { - uint8_t magic[4]; - uint8_t type; - uint8_t reserved[27]; --} __attribute((packed)); -+} __packed; - - typedef enum { - component_header_ipl = 0x00, -@@ -623,7 +629,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - } - - /* Add stage 3 loader to bootmap */ -- rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS, -+ rc = add_component_file(fd, ZIPL_STAGE3_PATH, STAGE3_LOAD_ADDRESS, - signature_size, VOID_ADD(table, offset), 1, - info, target, &comp_loc[comp_nr]); - if (rc) { -@@ -640,17 +646,17 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - 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, -+ ipl->is_kdump ? IMAGE_ENTRY_KDUMP : -+ IMAGE_ENTRY, - (info->type == disk_type_scsi) ? 0 : 1, -- flags, image_size); -+ flags, ipl->image_addr, image_size); - if (rc) { - free(table); - return rc; - } - rc = add_component_buffer(fd, stage3_params, stage3_params_size, - (component_data) (uint64_t) -- DEFAULT_STAGE3_PARAMS_ADDRESS, -+ STAGE3_PARAMS_ADDRESS, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr], component_load); - free(stage3_params); -@@ -788,7 +794,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, - create_component_entry(VOID_ADD(table, offset), NULL, - component_execute, - (component_data) (uint64_t) -- (ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD), -+ (STAGE3_ENTRY | PSW_LOAD), - info); - /* Write component table */ - rc = disk_write_block_aligned(fd, table, info->phy_block_size, -@@ -1183,7 +1189,7 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - ulong unused_size; - - /* Use approximated stage 3 size as starting point */ -- size = MINIMUM_ADDRESS; -+ size = IMAGE_LOAD_ADDRESS; - - /* Ramdisk */ - if (job->data.dump.ramdisk != NULL) { -@@ -1195,7 +1201,7 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - /* Kernel */ - if (stat(job->data.dump.image, &st)) - goto out_misc_free_temp_dev; -- size += DIV_ROUND_UP(st.st_size - 0x10000, -+ size += DIV_ROUND_UP(st.st_size - IMAGE_LOAD_ADDRESS, - info->phy_block_size); - /* Parmfile */ - size += DIV_ROUND_UP(DUMP_PARAM_MAX_LEN, info->phy_block_size); -@@ -1282,9 +1288,7 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - break; - } - if (dry_run) { -- if (remove(filename) == -1) -- fprintf(stderr, "Warning: could not remove temporary " -- "file %s!\n", filename); -+ misc_free_temp_file(filename); - } else if (job->id != job_dump_partition) { - /* Rename to final bootmap name */ - mapname = misc_make_path(job->target.bootmap_dir, -@@ -1311,6 +1315,8 @@ out_disk_free_info: - disk_free_info(info); - out_close_fd: - close(fd); -+ if (job->id != job_dump_partition) -+ misc_free_temp_file(filename); - out_free_filename: - free(filename); - return -1; -diff --git a/zipl/src/disk.c b/zipl/src/disk.c -index 43092bf..8abf668 100644 ---- a/zipl/src/disk.c -+++ b/zipl/src/disk.c -@@ -198,8 +198,8 @@ disk_get_info(const char* device, struct job_target_data* target, - long devsize; - FILE *fh; - char *script_pre = TOOLS_LIBDIR "/zipl_helper."; -- char script_file[80]; -- char ppn_cmd[80]; -+ char *script_file = NULL; -+ char *ppn_cmd = NULL; - char buffer[80]; - char value[40]; - int majnum, minnum; -@@ -235,23 +235,22 @@ disk_get_info(const char* device, struct job_target_data* target, - } - data->source = source_user; - /* Check if targetbase script is available */ -- strcpy(script_file, script_pre); -- if (data->drv_name) { -- strcat(script_file, data->drv_name); -- } -+ if (data->drv_name) -+ misc_asprintf(&script_file, "%s%s", script_pre, data->drv_name); -+ else -+ misc_asprintf(&script_file, "%s", script_pre); - if ((target->targetbase == NULL) && - (!stat(script_file, &script_stats))) { - data->source = source_script; - /* Run targetbase script */ -- strcpy(ppn_cmd, script_file); - if (target->bootmap_dir == NULL) { - /* happens in case of partition dump */ -- snprintf(ppn_cmd, sizeof(ppn_cmd), "%s %d:%d", -- script_file, major(stats.st_rdev), -- minor(stats.st_rdev)); -+ misc_asprintf(&ppn_cmd, "%s %d:%d", -+ script_file, major(stats.st_rdev), -+ minor(stats.st_rdev)); - } else { -- strcat(ppn_cmd, " "); -- strcat(ppn_cmd, target->bootmap_dir); -+ misc_asprintf(&ppn_cmd, "%s %s", -+ script_file, target->bootmap_dir); - } - printf("Run %s\n", ppn_cmd); - fh = popen(ppn_cmd, "r"); -@@ -406,6 +405,12 @@ disk_get_info(const char* device, struct job_target_data* target, - "determined."); - goto out_close; - } -+ /* NVMe path, driver name is 'blkext' */ -+ } else if (strcmp(data->drv_name, "blkext") == 0) { -+ data->devno = -1; -+ data->type = disk_type_scsi; -+ data->partnum = stats.st_rdev & SCSI_PARTN_MASK; -+ data->device = stats.st_rdev & ~SCSI_PARTN_MASK; - } else { - /* Driver name is unknown */ - error_reason("Unsupported device driver '%s'", data->drv_name); -@@ -444,9 +449,12 @@ type_determined: - data->fs_block_size = -1; - close(fd); - *info = data; -+ free(script_file); - return 0; - out_close: - close(fd); -+ free(ppn_cmd); -+ free(script_file); - free(data); - return -1; - -diff --git a/zipl/src/install.c b/zipl/src/install.c -index 20b53f5..32f9dfa 100644 ---- a/zipl/src/install.c -+++ b/zipl/src/install.c -@@ -23,6 +23,7 @@ - #include - #include - -+#include "lib/zt_common.h" - #include "lib/util_sys.h" - - #include "boot.h" -@@ -89,7 +90,7 @@ update_scsi_mbr(void* bootblock, disk_blockptr_t* table, - uint8_t program_table_pointer[16]; - uint8_t reserved2[0x50]; - struct boot_info boot_info; -- } __attribute__ ((packed))* mbr; -+ } __packed* mbr; - struct scsi_dump_param param; - void* buffer; - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 80ddb34..2c9cef8 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -23,6 +23,7 @@ - #include "job.h" - #include "misc.h" - #include "scan.h" -+#include "zipl.h" - - /* Command line options */ - static struct option options[] = { -@@ -671,12 +672,12 @@ check_component_address_data(struct component_loc *cl, int num, char *name, - address_limit); - return -1; - } -- if (*cl[i].addrp < MINIMUM_ADDRESS) { -+ if (*cl[i].addrp < IMAGE_LOAD_ADDRESS) { - if (name != NULL) - error_text("Section '%s'", name); - error_reason("Component '%s' falls below available " - "address space (limit is 0x%08x)", -- cl[i].name, MINIMUM_ADDRESS); -+ cl[i].name, IMAGE_LOAD_ADDRESS); - return -1; - } - } -@@ -714,12 +715,12 @@ finalize_component_address_data(struct component_loc *cl, int num, - for (j = -1; j < i; j++) { - if (j < 0) { - /* Try address before first component */ -- addr = MINIMUM_ADDRESS; -+ addr = IMAGE_LOAD_ADDRESS; - } else { - /* Try address after component j */ - addr = *cl[j].addrp + cl[j].size; -- if (addr < MINIMUM_ADDRESS) -- addr = MINIMUM_ADDRESS; -+ if (addr < IMAGE_LOAD_ADDRESS) -+ addr = IMAGE_LOAD_ADDRESS; - } - addr = ALIGN(addr, cl[i].align); - if (addr + cl[i].size > address_limit) { -@@ -913,7 +914,7 @@ check_job_dump_images(struct job_dump_data* dump, char* name) - dump->image = misc_strdup(ZFCPDUMP_IMAGE); - if (dump->image == NULL) - return -1; -- dump->image_addr = DEFAULT_IMAGE_ADDRESS; -+ dump->image_addr = IMAGE_LOAD_ADDRESS; - - /* Ramdisk is no longer required with new initramfs dump system */ - if (misc_check_readable_file(ZFCPDUMP_INITRD)) -@@ -1359,7 +1360,7 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) - return -1; - if (extract_address(job->data.ipl.image, - &job->data.ipl.image_addr)) { -- job->data.ipl.image_addr = DEFAULT_IMAGE_ADDRESS; -+ job->data.ipl.image_addr = IMAGE_LOAD_ADDRESS; - } - /* Fill in parmline */ - rc = get_parmline(data[(int) scan_keyword_parmfile], -@@ -1414,7 +1415,7 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) - return -1; - if (extract_address(job->data.ipl_tape.image, - &job->data.ipl_tape.image_addr)) { -- job->data.ipl_tape.image_addr = DEFAULT_IMAGE_ADDRESS; -+ job->data.ipl_tape.image_addr = IMAGE_LOAD_ADDRESS; - } - /* Fill in parmline */ - rc = get_parmline(data[(int) scan_keyword_parmfile], -@@ -1804,7 +1805,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - { - struct scan_token* scan; - struct scan_token* new_scan; -- char* filename; -+ const char *filename = NULL; - char *blsdir; - char* source; - int i, rc, scan_size; -@@ -1827,6 +1828,10 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - break; - } - } -+ if (filename == NULL) { -+ error_text("No zipl configuration was readable"); -+ return -1; -+ } - source = ""; - } - printf("Using config file '%s'%s\n", filename, source); -diff --git a/zipl/src/misc.c b/zipl/src/misc.c -index 057c9a0..8d1a2ee 100644 ---- a/zipl/src/misc.c -+++ b/zipl/src/misc.c -@@ -9,6 +9,7 @@ - * it under the terms of the MIT license. See LICENSE for details. - */ - -+#include - #include - #include - #include -@@ -296,7 +297,7 @@ misc_temp_dev(dev_t dev, int blockdev, char** devno) - char filename[] = "zipl0000"; - mode_t mode; - unsigned int path; -- int retry; -+ unsigned int retry; - int rc; - int fd; - -@@ -310,7 +311,8 @@ misc_temp_dev(dev_t dev, int blockdev, char** devno) - if (pathname[path] == NULL) - continue; - for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) { -- sprintf(filename, "zipl%04d", retry); -+ assert(retry < 10000); -+ sprintf(filename, "zipl%04u", retry); - result = misc_make_path(pathname[path], filename); - if (result == NULL) - return -1; -@@ -366,6 +368,16 @@ misc_free_temp_dev(char* device) - free(device); - } - -+/* Delete temporary bootmap file */ -+void -+misc_free_temp_file(char *filename) -+{ -+ if (remove(filename)) { -+ fprintf(stderr, -+ "Warning: Could not remove temporary file %s: %s", -+ filename, strerror(errno)); -+ } -+} - - /* Write COUNT bytes from memory at location DATA to the file identified by - * file descriptor FD. Return 0 when all bytes were successfully written, -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index f4228d3..38fa545 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -699,6 +699,10 @@ scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan, - } - - val_end = file->pos; -+ -+ while (val_end > val_start && isblank(file->buffer[val_end - 1])) -+ val_end--; -+ - file->buffer[key_end] = '\0'; - file->buffer[val_end] = '\0'; - -@@ -786,6 +790,7 @@ scan_bls(const char* blsdir, struct scan_token** token, int scan_size) - case EOF: - break; - case '\t': -+ case '\n': - case '\0': - case ' ': - file.pos++; --- -2.21.3 - - -From efd2e0495726f8890fbff31053081472f948aeca Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 28 May 2020 09:54:34 +0200 -Subject: [PATCH 54/57] ipl-tools: Add nvme device support to zipl, - lsreipl/chreipl (#1525178) - -Summary: ipl-tools: Add nvme device support to zipl, lsreipl/chreipl -Description: Code is also added to lsreipl and chreipl to list and change - sysfs entries associated with nvme reipl kernel parameters. - Code is also added to zipl to allow bootload installation to - nvme devices. -Upstream-ID: 71b36d17f019c9e2cf218351520d5b55a6c2d479 -Upstream-ID: 0472b5ea5c97f2c59a938deebe53b7f27e8a9a32 ---- - include/lib/util_libc.h | 1 + - ipl_tools/Makefile | 6 +- - ipl_tools/cmd_chreipl.c | 153 ++++++++++++++++++++++++++++++++++-- - ipl_tools/cmd_lsreipl.c | 33 ++++++++ - ipl_tools/ipl_tools.h | 11 +++ - ipl_tools/man/chreipl.8 | 53 ++++++++++++- - ipl_tools/nvme.c | 169 ++++++++++++++++++++++++++++++++++++++++ - libutil/util_libc.c | 27 +++++++ - zipl/src/disk.c | 95 +++++++++++++++++++++- - 9 files changed, 538 insertions(+), 10 deletions(-) - create mode 100644 ipl_tools/nvme.c - -diff --git a/include/lib/util_libc.h b/include/lib/util_libc.h -index dc6b3d0..394aca1 100644 ---- a/include/lib/util_libc.h -+++ b/include/lib/util_libc.h -@@ -130,6 +130,7 @@ char *util_strcat_realloc(char *str1, const char *str2); - void util_str_toupper(char *str); - - char *util_strstrip(char *s); -+size_t util_strlcpy(char *dest, const char *src, size_t size); - - #ifdef __cplusplus - } -diff --git a/ipl_tools/Makefile b/ipl_tools/Makefile -index 506d5cd..e5ad00e 100644 ---- a/ipl_tools/Makefile -+++ b/ipl_tools/Makefile -@@ -1,11 +1,13 @@ - include ../common.mak - -+libs = $(rootdir)/libutil/libutil.a -+ - all: chreipl lsreipl chshut lsshut - --objects = main.o ccw.o fcp.o system.o shutdown.o \ -+objects = main.o ccw.o fcp.o nvme.o system.o shutdown.o \ - cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o - --chreipl: $(objects) -+chreipl: $(objects) $(libs) - $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - lsreipl: -diff --git a/ipl_tools/cmd_chreipl.c b/ipl_tools/cmd_chreipl.c -index 640aa0c..9638e49 100644 ---- a/ipl_tools/cmd_chreipl.c -+++ b/ipl_tools/cmd_chreipl.c -@@ -27,12 +27,14 @@ enum target_type { - TT_FCP, - TT_NSS, - TT_NODE, -+ TT_NVME, - }; - - enum reipl_type { - REIPL_FCP, - REIPL_CCW, -- REIPL_NSS -+ REIPL_NSS, -+ REIPL_NVME, - }; - - static const char *const usage_chreipl = -@@ -40,6 +42,7 @@ static const char *const usage_chreipl = - "\n" - " chreipl [ccw] [-d] [OPTIONS]\n" - " chreipl [fcp] [-d] [-w] [-l] [OPTIONS]\n" -+" chreipl nvme [-i] [-s] [OPTIONS]\n" - " chreipl [node] [OPTIONS]\n" - " chreipl nss [-n] [OPTIONS]\n" - " chreipl [-h] [-v]\n" -@@ -47,6 +50,7 @@ static const char *const usage_chreipl = - "The following re-IPL targets are supported:\n" - " ccw IPL from CCW device\n" - " fcp IPL from FCP device\n" -+" nvme IPL from NVME device\n" - " nss IPL from NSS\n" - " node IPL from device specified by device node or directory\n" - "\n" -@@ -69,6 +73,12 @@ static const char *const usage_chreipl = - " -L, --loadparm Loadparm specification\n" - " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" - "\n" -+"Options for nvme target:\n" -+" -i, --fid PCI Function ID of NVME IPL device (hex)\n" -+" -s --nsid Namespace ID of NVME IPL device (decimal, default 1)\n" -+" -b, --bootprog Bootprog specification\n" -+" -L, --loadparm Loadparm specification\n" -+"\n" - "Options for nss target:\n" - " -n, --name Identifier of the NSS\n" - "\n" -@@ -85,6 +95,10 @@ static struct locals { - char lun[20]; /* 18 character +0x" */ - int lun_set; - char busid[10]; /* Bus ID e.g. 0.0.4711 */ -+ int fid_set; -+ char fid[FID_MAX_LEN]; -+ int nsid_set; -+ char nsid[11]; /* 10 decimal chars + null */ - int busid_set; - char dev[15]; /* Device (e.g. dasda) */ - int dev_set; -@@ -93,10 +107,10 @@ static struct locals { - char bootparms[4096]; - int bootparms_set; - int force_set; -- enum target_type target_type; /* CCW, FCP, NSS or NODE */ -+ enum target_type target_type; /* CCW,FCP,NVME,NSS or NODE */ - int target_type_set; - int target_type_auto_mode; -- enum reipl_type reipl_type; /* CCW, FCP, NSS */ -+ enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */ - int reipl_clear; - } l; - -@@ -228,6 +242,34 @@ static void set_wwpn(const char *wwpn) - l.wwpn_set = 1; - } - -+static void set_nvme_nsid(const char *nsid) -+{ -+ unsigned long long nsid_tmp; -+ char *endptr; -+ -+ nsid_tmp = strtoull(nsid, &endptr, 10); -+ if (*endptr) -+ ERR_EXIT("NSID \"%s\" is not a decimal number", nsid); -+ snprintf(l.nsid, sizeof(l.nsid), "%08llu", nsid_tmp); -+ l.nsid_set = 1; -+} -+ -+static void set_nvme_fid(const char *fid) -+{ -+ unsigned long long fid_tmp; -+ char *endptr; -+ -+ fid_tmp = strtoull(fid, &endptr, 16); -+ if (*endptr) -+ ERR_EXIT("FID \"%s\" is not a hexadecimal number", fid); -+ snprintf(l.fid, sizeof(l.fid), "0x%08llx", fid_tmp); -+ l.fid_set = 1; -+ -+ /* nsid defaults to 1, if not already set */ -+ if (!l.nsid_set) -+ set_nvme_nsid("1"); -+} -+ - static void parse_fcp_args(char *nargv[], int nargc) - { - /* -@@ -247,6 +289,28 @@ static void parse_fcp_args(char *nargv[], int nargc) - set_lun(nargv[2]); - } - -+static void parse_nvme_args(char *nargv[], int nargc) -+{ -+ /* -+ * we might be called like this: -+ * chreipl nvme 0x13 1 -+ */ -+ if (l.busid_set || l.fid_set || l.nsid_set || l.dev_set) -+ ERR_EXIT("Use either options or positional parameters"); -+ if (nargc > 2) -+ ERR_EXIT("Too many arguments specified for \"nvme\" re-IPL " -+ "type"); -+ else if (nargc < 1) -+ ERR_EXIT("The \"nvme\" re-IPL type requires function id, and " -+ "optional namespace id"); -+ set_nvme_fid(nargv[0]); -+ -+ if (nargc == 2) -+ set_nvme_nsid(nargv[1]); -+ else -+ set_nvme_nsid("1"); -+} -+ - static void parse_ccw_args(char *nargv[], int nargc) - { - /* -@@ -287,6 +351,13 @@ static void dev_from_part(char *dev_name) - dev_name[i] = 0; - } - -+static void dev_from_part_nvme(char *dev_name) -+{ -+ char *delim = strrchr(dev_name, 'p'); -+ if (delim) -+ *delim = 0; -+} -+ - static int set_reipl_type(const char *dev_name) - { - if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 || -@@ -294,11 +365,18 @@ static int set_reipl_type(const char *dev_name) - l.reipl_type = REIPL_CCW; - else if (strncmp(dev_name, "sd", strlen("sd")) == 0) - l.reipl_type = REIPL_FCP; -+ else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0) -+ l.reipl_type = REIPL_NVME; - else - return -1; - - strncpy(l.dev, dev_name, sizeof(l.dev)); -- dev_from_part(l.dev); -+ -+ if (l.reipl_type == REIPL_NVME) -+ dev_from_part_nvme(l.dev); -+ else -+ dev_from_part(l.dev); -+ - l.dev_set = 1; - return 0; - } -@@ -399,6 +477,9 @@ static void parse_pos_args(char *nargv[], int nargc) - case TT_FCP: - parse_fcp_args(nargv, nargc); - break; -+ case TT_NVME: -+ parse_nvme_args(nargv, nargc); -+ break; - case TT_CCW: - parse_ccw_args(nargv, nargc); - break; -@@ -420,6 +501,14 @@ static void check_fcp_opts(void) - "and LUN"); - } - -+static void check_nvme_opts(void) -+{ -+ if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set) -+ ERR_EXIT("Invalid option for \"nvme\" target specified"); -+ if (!(l.fid_set && l.nsid_set)) -+ ERR_EXIT("The \"nvme\" target requires FID, and optional NSID"); -+} -+ - static void check_ccw_opts(void) - { - if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set) -@@ -479,6 +568,8 @@ static void parse_chreipl_options(int argc, char *argv[]) - { "device", required_argument, NULL, 'd' }, - { "lun", required_argument, NULL, 'l' }, - { "wwpn", required_argument, NULL, 'w' }, -+ { "fid", required_argument, NULL, 'i' }, -+ { "nsid", required_argument, NULL, 's' }, - { "loadparm", required_argument, NULL, 'L' }, - { "name", required_argument, NULL, 'n' }, - { "bootparms", required_argument, NULL, 'p' }, -@@ -487,7 +578,7 @@ static void parse_chreipl_options(int argc, char *argv[]) - { "clear", required_argument, NULL, 'c' }, - { NULL, 0, NULL, 0 } - }; -- static const char optstr[] = "hd:vw:l:fL:b:n:p:c:"; -+ static const char optstr[] = "hd:vw:l:fL:b:n:p:c:i:s:"; - - /* dont run without any argument */ - if (argc == 1) -@@ -499,6 +590,8 @@ static void parse_chreipl_options(int argc, char *argv[]) - set_target_type(TT_CCW, 0); - else if (strcmp(argv[1], "nss") == 0) - set_target_type(TT_NSS, 0); -+ else if (strcmp(argv[1], "nvme") == 0) -+ set_target_type(TT_NVME, 0); - else if (strcmp(argv[1], "node") == 0) - set_target_type(TT_NODE, 0); - else -@@ -513,9 +606,15 @@ static void parse_chreipl_options(int argc, char *argv[]) - case 'd': - set_device(optarg); - break; -+ case 'i': -+ set_nvme_fid(optarg); -+ break; - case 'l': - set_lun(optarg); - break; -+ case 's': -+ set_nvme_nsid(optarg); -+ break; - case 'w': - set_wwpn(optarg); - break; -@@ -677,6 +776,40 @@ static void chreipl_fcp(void) - print_fcp(0, 0); - } - -+static void chreipl_nvme(void) -+{ -+ check_nvme_opts(); -+ -+ if (!nvme_is_device(l.fid, l.nsid) && !l.force_set) { -+ ERR_EXIT("Could not find NVME device with fid %s and nsid %s", -+ l.fid, l.nsid); -+ } -+ check_exists("reipl/nvme/fid", "\"nvme\" re-IPL target"); -+ -+ if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_FCP_MAX) { -+ ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", -+ strlen(l.bootparms), BOOTPARMS_FCP_MAX); -+ } -+ -+ write_str_optional(l.loadparm, "reipl/nvme/loadparm", l.loadparm_set, -+ "loadparm"); -+ write_str_optional(l.bootparms, "reipl/nvme/scp_data", l.bootparms_set, -+ "boot parameters"); -+ write_str(l.fid, "reipl/nvme/fid"); -+ write_str(l.nsid, "reipl/nvme/nsid"); -+ /* -+ * set the boot record logical block address. Master boot -+ * record. It is always 0 for Linux -+ */ -+ write_str("0", "reipl/nvme/br_lba"); -+ if (!l.bootprog_set) -+ sprintf(l.bootprog, "0"); -+ write_str(l.bootprog, "reipl/nvme/bootprog"); -+ write_str("nvme", "reipl/reipl_type"); -+ -+ print_nvme(0, 0); -+} -+ - static void chreipl_nss(void) - { - check_nss_opts(); -@@ -717,6 +850,13 @@ static void chreipl_node(void) - l.busid_set = 1; - chreipl_fcp(); - break; -+ case REIPL_NVME: -+ nvme_fid_get(l.dev, l.fid); -+ l.fid_set = 1; -+ nvme_nsid_get(l.dev, l.nsid); -+ l.nsid_set = 1; -+ chreipl_nvme(); -+ break; - default: - ERR_EXIT("Internal error: chreipl_node"); - } -@@ -732,6 +872,9 @@ void cmd_chreipl(int argc, char *argv[]) - case TT_FCP: - chreipl_fcp(); - break; -+ case TT_NVME: -+ chreipl_nvme(); -+ break; - case TT_NSS: - chreipl_nss(); - break; -diff --git a/ipl_tools/cmd_lsreipl.c b/ipl_tools/cmd_lsreipl.c -index a829b9d..af9b9bc 100644 ---- a/ipl_tools/cmd_lsreipl.c -+++ b/ipl_tools/cmd_lsreipl.c -@@ -84,6 +84,35 @@ void print_fcp(int show_ipl, int dump) - print_fw_str("clear: %s\n", dir, "clear"); - } - -+void print_nvme(int show_ipl, int dump) -+{ -+ char *dir = show_ipl ? "ipl" : "reipl/nvme"; -+ char *path_bootparms = show_ipl ? "/sys/firmware/ipl/scp_data" : -+ "/sys/firmware/reipl/nvme/scp_data"; -+ char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" : -+ "/sys/firmware/reipl/nvme/loadparm"; -+ char loadparm[9], loadparm_path[PATH_MAX]; -+ -+ if (dump) -+ printf("%-12s nvme_dump\n", get_ipl_banner(show_ipl)); -+ else -+ printf("%-12s nvme\n", get_ipl_banner(show_ipl)); -+ -+ print_fw_str("FID: %s\n", dir, "fid"); -+ print_fw_str("NSID: %s\n", dir, "nsid"); -+ print_fw_str("bootprog: %s\n", dir, "bootprog"); -+ print_fw_str("br_lba: %s\n", dir, "br_lba"); -+ if (access(path_loadparm, R_OK) == 0) { -+ sprintf(loadparm_path, "%s/%s", dir, "loadparm"); -+ read_fw_str(loadparm, loadparm_path, sizeof(loadparm)); -+ if (strcmp(loadparm, " ") == 0) -+ loadparm[0] = 0; -+ printf("Loadparm: \"%s\"\n", loadparm); -+ } -+ if (access(path_bootparms, R_OK) == 0) -+ print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); -+} -+ - void print_ccw(int show_ipl) - { - char loadparm[9], loadparm_path[PATH_MAX]; -@@ -155,6 +184,10 @@ void cmd_lsreipl(int argc, char *argv[]) - print_fcp(l.ipl_set, 0); - else if (strcmp(reipl_type_str, "fcp_dump") == 0) - print_fcp(l.ipl_set, 1); -+ else if (strcmp(reipl_type_str, "nvme") == 0) -+ print_nvme(l.ipl_set, 0); -+ else if (strcmp(reipl_type_str, "nvme_dump") == 0) -+ print_nvme(l.ipl_set, 1); - else if (strcmp(reipl_type_str, "ccw") == 0) - print_ccw(l.ipl_set); - else if (strcmp(reipl_type_str, "nss") == 0) -diff --git a/ipl_tools/ipl_tools.h b/ipl_tools/ipl_tools.h -index 50d0b74..576d33a 100644 ---- a/ipl_tools/ipl_tools.h -+++ b/ipl_tools/ipl_tools.h -@@ -43,6 +43,7 @@ extern void cmd_chreipl(int argc, char *argv[]); - - extern void print_ccw(int show_ipl); - extern void print_fcp(int show_ipl, int dump); -+extern void print_nvme(int show_ipl, int dump); - extern void print_nss(int show_ipl); - - /* -@@ -70,6 +71,16 @@ extern void fcp_lun_get(const char *device, char *lun); - extern void fcp_wwpn_get(const char *device, char *wwpn); - extern void fcp_busid_get(const char *device, char *devno); - -+/* -+ * NVME -+ */ -+#define FID_MAX_LEN 11 /* 8 characters + 0x + null */ -+#define NVME_PATH_MAX (PATH_MAX + NAME_MAX + 1) -+ -+extern void nvme_fid_get(const char *device, char *fid); -+extern void nvme_nsid_get(const char *device, char *nsid); -+int nvme_is_device(char *fid_str, char *nsid_str); -+ - /* - * CCW - */ -diff --git a/ipl_tools/man/chreipl.8 b/ipl_tools/man/chreipl.8 -index 91ddfad..e6ffc16 100644 ---- a/ipl_tools/man/chreipl.8 -+++ b/ipl_tools/man/chreipl.8 -@@ -39,6 +39,9 @@ Specify a DASD CCW device for reboot - .RB "- " fcp : - Specify a FCP device for reboot - .TP -+.RB "- " nvme : -+Specify an NVMe device for reboot -+.TP - .RB "- " nss : - Specify a named saved system (NSS) for reboot - .TP -@@ -118,6 +121,11 @@ WWPN 0x500507630300c562, and LUN 0x401040b300000000. In addition to that - append kernel parameter "mem=" to restrict memory to 512 MB: - - \fB# chreipl 0.0.1700 0x500507630300c562 0x401040b300000000 -p "mem=512M"\fP -+ -+4. Next time reboot from the NVMe device with function id 0x13, namespace 1: -+ -+\fB# chreipl nvme 0x13 1 -+ - .SH ccw - Use the ccw re-IPL target for DASD devices that are accessed by the hardware - using channel command word (CCW) channels. -@@ -207,6 +215,44 @@ use options instead of positional parameters: - .br - - \fB# chreipl fcp -d 0.0.1700 -w 0x5005076... -l 0x401040b3... -b 2\fP -+.SH nvme -+Use the nvme re-IPL target for specifying an NVMe disk for reboot. -+.TP -+.BR "\-i" " or " "\-\-fid" -+PCI Function ID of NVME IPL device (hex). -+ -+.TP -+.BR "\-s" " or " "\-\-nsid" -+Namespace ID of the NVME IPL device (decimal, default 1). -+ -+.TP -+.BR "\-b" " or " "\-\-bootprog" -+Specifies an entry in the boot configuration by defining the IPL boot -+program selector. If omitted, '0' will be used. -+ -+.TP -+.BR "\-L" " or " "\-\-loadparm" -+The loadparm for the nvme re-IPL target is not used to control the boot -+configuration that is defined by the -+.BR zipl (8) -+boot menu. Instead it can be used to control higher level boot loaders -+like GRUB. For more details refer to distribution specific documentation. -+ -+.PP -+\fBExamples:\fP -+.br -+ -+1. Next time reboot from the NVMe disk with function-id 0x13 and namespace 1: -+.br -+ -+\fB# chreipl nvme 0x13 1\fP -+.br -+ -+2. Use same configuration as (1) but choose boot program selector 2 and -+use options instead of positional parameters: -+.br -+ -+\fB# chreipl nvme -i 0x13 -s 1 -b 2\fP - .SH nss - Use the nss re-IPL target to specify z/VM named saved systems (NSS) for - reboot. -@@ -221,7 +267,7 @@ Use the NSS named LINUX1 for the next reboot: - - \fB# chreipl nss LINUX1\fP - .SH node --You can identify DASD or SCSI re-IPL devices indirectly through a device -+You can identify DASD, SCSI, or NVMe re-IPL devices indirectly through a device - node or directory. The chreipl tool then determines the information - that you would otherwise have to specify with the ccw or fcp target. - .PP -@@ -244,6 +290,11 @@ that you would otherwise have to specify with the ccw or fcp target. - - \fB# chreipl node /mnt/boot\fP - -+4. Next time reboot from the NVMe device represented by /dev/nvme0n1 -+.br -+ -+\fB# chreipl node /dev/nvme0n1\fP -+ - .SH SEE ALSO - .BR lsreipl (8), - .BR zipl (8), -diff --git a/ipl_tools/nvme.c b/ipl_tools/nvme.c -new file mode 100644 -index 0000000..2a95972 ---- /dev/null -+++ b/ipl_tools/nvme.c -@@ -0,0 +1,169 @@ -+/* -+ * ipl_tools - Linux for System z reipl and shutdown tools -+ * -+ * NVMe device functions -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_libc.h" -+#include "lib/util_file.h" -+#include "ipl_tools.h" -+ -+/* -+ * Return the fid of a device -+ */ -+void nvme_fid_get(const char *device, char *fid) -+{ -+ char path[PATH_MAX], buf[FID_MAX_LEN]; -+ -+ snprintf(path, PATH_MAX, "/sys/block/%s/device/device/function_id", -+ device); -+ if (util_file_read_line(buf, FID_MAX_LEN, path)) -+ ERR_EXIT_ERRNO("Could not read from \"%s\"", path); -+ -+ util_strlcpy(fid, buf, FID_MAX_LEN); -+} -+/* -+ * Return the nsid of a device -+ */ -+void nvme_nsid_get(const char *device, char *nsid) -+{ -+ char path[PATH_MAX], buf[FID_MAX_LEN]; -+ -+ snprintf(path, PATH_MAX, "/sys/block/%s/nsid", device); -+ if (util_file_read_line(buf, FID_MAX_LEN, path)) -+ ERR_EXIT_ERRNO("Could not read from \"%s\"", path); -+ -+ util_strlcpy(nsid, buf, FID_MAX_LEN); -+} -+ -+static int next_entry(DIR *dir, char *in_path, char *out_path, -+ unsigned char entry_type) -+{ -+ struct dirent *dirent; -+ char temp_path[NVME_PATH_MAX]; -+ -+ while ((dirent = readdir(dir)) != NULL) { -+ if (strcmp(dirent->d_name, ".") == 0 || -+ strcmp(dirent->d_name, "..") == 0 || -+ dirent->d_type != entry_type) -+ continue; -+ -+ /* Resolve the symlink, if needed */ -+ if (dirent->d_type == DT_LNK) { -+ snprintf(temp_path, sizeof(temp_path), "%s/%s", in_path, -+ dirent->d_name); -+ if (!realpath(temp_path, out_path)) -+ ERR_EXIT_ERRNO("Could not resolve link %s", -+ temp_path); -+ return 1; -+ } -+ -+ snprintf(out_path, NVME_PATH_MAX, "%s/%s", in_path, -+ dirent->d_name); -+ return 1; -+ } -+ return 0; -+} -+ -+static int nvme_getdev_by_fid(char *fidstr, char *devpath) -+{ -+ char temp_path[PATH_MAX+19], real_path[PATH_MAX]; -+ char *sys_path = "/sys/class/nvme"; -+ u_int64_t target_fid, curfid; -+ DIR *dir; -+ char *end; -+ int rc = -1; -+ -+ target_fid = strtoul(fidstr, &end, 16); -+ if (*end) -+ ERR_EXIT("Invalid function_id given %s", fidstr); -+ -+ dir = opendir(sys_path); -+ if (!dir) -+ ERR_EXIT("Could not open %s", sys_path); -+ -+ errno = 0; -+ while (next_entry(dir, sys_path, real_path, DT_LNK)) { -+ snprintf(temp_path, sizeof(temp_path), "%s/%s", real_path, -+ "device/function_id"); -+ if (access(temp_path, F_OK)) -+ continue; -+ -+ if (util_file_read_ul(&curfid, 16, temp_path)) -+ ERR_EXIT("Invalid function_id found in %s", temp_path); -+ -+ if (curfid == target_fid) { -+ strncpy(devpath, real_path, PATH_MAX); -+ rc = 0; -+ break; -+ } -+ } -+ -+ closedir(dir); -+ return rc; -+} -+ -+static int nvme_getdev_by_nsid(char *nsid_str, char *path, char *dev_path) -+{ -+ char full_path[NVME_PATH_MAX+1], nsid_path[sizeof(full_path)+5]; -+ char *end; -+ u_int64_t nsid, curnsid; -+ DIR *dir; -+ -+ nsid = strtoul(nsid_str, &end, 10); -+ if (*end) -+ ERR_EXIT_ERRNO("Invalid namespace id given %s", nsid_str); -+ -+ dir = opendir(path); -+ if (!dir) -+ ERR_EXIT_ERRNO("Could not open %s", path); -+ -+ errno = 0; -+ while (next_entry(dir, path, full_path, DT_DIR)) { -+ snprintf(nsid_path, sizeof(nsid_path), "%s/%s", full_path, -+ "nsid"); -+ if (access(nsid_path, F_OK)) -+ continue; -+ -+ if (util_file_read_ul(&curnsid, 10, nsid_path)) -+ ERR_EXIT("Invalid namespace id found in %s", nsid_path); -+ -+ if (curnsid == nsid) { -+ strncpy(dev_path, full_path, NVME_PATH_MAX+1); -+ closedir(dir); -+ return 0; -+ } -+ } -+ closedir(dir); -+ return -1; -+} -+ -+static int nvme_getdev(char *fid_str, char *nsid_str, char *dev_path) -+{ -+ char path_tmp[NVME_PATH_MAX]; -+ -+ if (nvme_getdev_by_fid(fid_str, path_tmp)) -+ return -1; -+ -+ return nvme_getdev_by_nsid(nsid_str, path_tmp, dev_path); -+} -+ -+/* -+ * Check if the specified fid and nsid leads to a valid nvme device -+ */ -+int nvme_is_device(char *fid_str, char *nsid_str) -+{ -+ char path_tmp[NVME_PATH_MAX+1]; -+ -+ return !(nvme_getdev(fid_str, nsid_str, path_tmp)); -+} -diff --git a/libutil/util_libc.c b/libutil/util_libc.c -index 127b921..d3e7676 100644 ---- a/libutil/util_libc.c -+++ b/libutil/util_libc.c -@@ -228,3 +228,30 @@ char *util_strstrip(char *s) - - return s; - } -+ -+/** -+ * Copy \a src to buffer \a dest of size \a size. At most size - 1 -+ * chars will be copied. \a dest will always be NUL terminated. -+ * -+ * Note: If the return value is greater than or equal to size truncation -+ * occurred. -+ * -+ * @param[in] dest Destination buffer -+ * @param[in] src Source string -+ * @param[in] size Size of destination buffer -+ * -+ * @returns strlen Length of \a src string -+ */ -+size_t util_strlcpy(char *dest, const char *src, size_t size) -+{ -+ size_t str_len = strlen(src); -+ size_t len; -+ -+ if (size) { -+ len = MIN(size - 1, str_len); -+ memcpy(dest, src, len); -+ dest[len] = '\0'; -+ } -+ -+ return str_len; -+} -diff --git a/zipl/src/disk.c b/zipl/src/disk.c -index 8abf668..5baeaeb 100644 ---- a/zipl/src/disk.c -+++ b/zipl/src/disk.c -@@ -89,6 +89,88 @@ disk_determine_dasd_type(struct disk_info *data, - return 0; - } - -+static int blkext_get_partnum(dev_t dev) -+{ -+ char path[PATH_MAX], *buf; -+ int dev_major, dev_minor, partnum = -1; -+ -+ dev_major = major(dev); -+ dev_minor = minor(dev); -+ snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", -+ dev_major, dev_minor); -+ -+ if (misc_read_special_file(path, &buf, NULL, 1)) { -+ error_text("Could not read from path '%s'", path); -+ return -1; -+ } -+ -+ partnum = atoi(buf); -+ free(buf); -+ if (partnum < 0) { -+ error_text("Bad partition number in '%s'", path); -+ return -1; -+ } -+ -+ return partnum; -+} -+ -+static int blkext_is_base_device(dev_t dev) -+{ -+ int dev_major, dev_minor; -+ char path[PATH_MAX]; -+ struct stat stats; -+ -+ dev_major = major(dev); -+ dev_minor = minor(dev); -+ -+ snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", -+ dev_major, dev_minor); -+ return (stat(path, &stats)); -+} -+ -+static int blkext_get_base_dev(dev_t dev, dev_t *base_dev) -+{ -+ int base_major, base_minor; -+ char dev_path[PATH_MAX], base_path[PATH_MAX]; -+ char *temp_path, *buf; -+ -+ misc_asprintf(&temp_path, "/sys/dev/block/%d:%d", major(dev), minor(dev)); -+ if (!realpath(temp_path, dev_path)) { -+ error_reason(strerror(errno)); -+ error_text("Could not resolve link %s", temp_path); -+ free(temp_path); -+ return -1; -+ } -+ free(temp_path); -+ -+ misc_asprintf(&temp_path, "%s/..", dev_path); -+ if (!realpath(temp_path, base_path)) { -+ error_reason(strerror(errno)); -+ error_text("Could not resolve path %s", temp_path); -+ free(temp_path); -+ return -1; -+ } -+ free(temp_path); -+ -+ misc_asprintf(&temp_path, "%s/dev", base_path); -+ if (misc_read_special_file(temp_path, &buf, NULL, 1)) { -+ error_text("Could not read from path '%s'", temp_path); -+ free(temp_path); -+ return -1; -+ } -+ free(temp_path); -+ -+ if (sscanf(buf, "%i:%i", &base_major, &base_minor) != 2) { -+ error_text("Could not parse major:minor from string '%s'", buf); -+ free(buf); -+ return -1; -+ } -+ -+ free(buf); -+ *base_dev = makedev(base_major, base_minor); -+ return 0; -+} -+ - /* Return non-zero for ECKD type. */ - int - disk_is_eckd(disk_type_t type) -@@ -409,8 +491,17 @@ disk_get_info(const char* device, struct job_target_data* target, - } else if (strcmp(data->drv_name, "blkext") == 0) { - data->devno = -1; - data->type = disk_type_scsi; -- data->partnum = stats.st_rdev & SCSI_PARTN_MASK; -- data->device = stats.st_rdev & ~SCSI_PARTN_MASK; -+ -+ if (blkext_is_base_device(stats.st_rdev)) { -+ data->device = stats.st_rdev; -+ data->partnum = 0; -+ } else { -+ if (blkext_get_base_dev(stats.st_rdev, &data->device)) -+ goto out_close; -+ data->partnum = blkext_get_partnum(stats.st_rdev); -+ if (data->partnum == -1) -+ goto out_close; -+ } - } else { - /* Driver name is unknown */ - error_reason("Unsupported device driver '%s'", data->drv_name); --- -2.21.3 - - -From de78dc56b9fa81f78151f419396026f7c7214ac7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 28 May 2020 10:00:16 +0200 -Subject: [PATCH 55/57] zkey: Add support for EP11 secure keys (#1723845) - -Summary: zkey: Add support for EP11 secure keys -Description: With z15 and the CEX7 there is new crypto infrastructure - available to support derivation of EP11 secure keys to - protected keys. - The zkey as well as the zkey-cryptsetup tools are enhanced - to support EP11 secure keys. That is, zkey can manage - CCA AES DATA keys, CCA AES CIPHER keys, and now also EP11 - Secure keys. The key type must be specified at key generation - time, the default remains to generate CCA AES DATA keys. -Upstream-ID: d4cee7e65a4a3abf68380041a0bcd38075906f15 -Upstream-ID: 52192908ebc5b802de877e10d48cdc4ce972c355 -Upstream-ID: 1fd78d9f5ef795f466490a9aa5d72d039d6d27c4 -Upstream-ID: 1a87d3a3b45fdc005116680f1ed5eaf41fdc38d3 -Upstream-ID: 82c86fa8bd1108dcca77e6610d9e42a29c7584b1 -Upstream-ID: 2ca442feed2b7a963105301590e0f192f227c7d7 -Upstream-ID: ce4704f36537828d9378ea7640777f5b1275dfe8 -Upstream-ID: d91e728e3c3b72e56ee48c60ebe99464bf8d8df6 -Upstream-ID: b48aa5f4355c4547012bb60620ed4fa52e241e9d -Upstream-ID: a7e47685e0d05b0af79402cd69b2aa984a9b5ce6 -Upstream-ID: 88e6a18f960c8cfe0abd67ae7bc454fc37f222ef -Upstream-ID: 0be7efc956340a20ecf6a5d2e71278dc66f03f05 -Upstream-ID: f52aeabca4615e8330670f793d27d38e2bd81930 -Upstream-ID: 729a98fcb30330273e1d05d0f170e4567b8fb67c -Upstream-ID: 33031241c320f0479b39f55a0f86c5786f4c1822 ---- - zkey/Makefile | 20 +- - zkey/cca.c | 20 +- - zkey/cca.h | 2 +- - zkey/ep11.c | 510 ++++++++++++++++++++++++++++++++++++ - zkey/ep11.h | 178 +++++++++++++ - zkey/keystore.c | 223 ++++++++-------- - zkey/keystore.h | 7 +- - zkey/pkey.c | 290 +++++++++++++++++++-- - zkey/pkey.h | 81 +++++- - zkey/utils.c | 573 +++++++++++++++++++++++++++++++++-------- - zkey/utils.h | 44 +++- - zkey/zkey-cryptsetup.1 | 53 ++-- - zkey/zkey-cryptsetup.c | 216 +++++++--------- - zkey/zkey.1 | 98 ++++--- - zkey/zkey.c | 180 ++++++------- - 15 files changed, 1947 insertions(+), 548 deletions(-) - create mode 100644 zkey/ep11.c - create mode 100644 zkey/ep11.h - -diff --git a/zkey/Makefile b/zkey/Makefile -index f92de4f..100e408 100644 ---- a/zkey/Makefile -+++ b/zkey/Makefile -@@ -64,20 +64,22 @@ zkey-cryptsetup-skip-jsonc: - - all: $(BUILD_TARGETS) - --zkey.o: zkey.c pkey.h cca.h misc.h --pkey.o: pkey.c pkey.h --cca.o: cca.c cca.h pkey.h utils.h --utils.o: utils.h -+zkey.o: zkey.c pkey.h cca.h ep11.h misc.h -+pkey.o: pkey.c pkey.h cca.h ep11.h utils.h -+cca.o: cca.c cca.h pkey.h ep11.h utils.h -+ep11.o: ep11.c ep11.h pkey.h cca.h utils.h -+utils.o: utils.h pkey.h cca.h ep11.h - properties.o: check-dep-zkey properties.c properties.h --keystore.o: keystore.c keystore.h properties.h pkey.h cca.h utils.h --zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h misc.h -+keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h -+zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \ -+ ep11.h misc.h utils.h - - zkey: LDLIBS = -ldl -lcrypto --zkey: zkey.o pkey.o cca.o properties.o keystore.o utils.o $(libs) -+zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o $(libs) - $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - --zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c --zkey-cryptsetup: zkey-cryptsetup.o pkey.o cca.o utils.o $(libs) -+zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto -+zkey-cryptsetup: zkey-cryptsetup.o pkey.o cca.o ep11.o utils.o $(libs) - $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ - - install-common: -diff --git a/zkey/cca.c b/zkey/cca.c -index 01f7bfd..411a276 100644 ---- a/zkey/cca.c -+++ b/zkey/cca.c -@@ -558,7 +558,7 @@ int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose) - { - unsigned int adapters, adapter; - char adapter_serialnr[9]; -- char apqn_serialnr[9]; -+ char apqn_serialnr[SERIALNR_LENGTH]; - char temp[10]; - int rc, found = 0; - -@@ -630,7 +630,7 @@ int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose) - } - - struct find_mkvp_info { -- u64 mkvp; -+ u8 mkvp[MKVP_LENGTH]; - unsigned int flags; - bool found; - int card; -@@ -653,12 +653,12 @@ static int find_mkvp(int card, int domain, void *handler_data) - - if (info->flags & FLAG_SEL_CCA_MATCH_CUR_MKVP) - if (mk_info.cur_mk.mk_state == MK_STATE_VALID && -- mk_info.cur_mk.mkvp == info->mkvp) -+ MKVP_EQ(mk_info.cur_mk.mkvp, info->mkvp)) - found = true; - - if (info->flags & FLAG_SEL_CCA_MATCH_OLD_MKVP) - if (mk_info.old_mk.mk_state == MK_STATE_VALID && -- mk_info.old_mk.mkvp == info->mkvp) -+ MKVP_EQ(mk_info.old_mk.mkvp, info->mkvp)) - found = true; - - if (info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET) -@@ -700,25 +700,27 @@ static int find_mkvp(int card, int domain, void *handler_data) - * because the zcrypt kernel module is on an older level. -ENODEV is - * returned if no APQN is available with the desired mkvp. - */ --int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, -+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u8 *mkvp, const char *apqns, - unsigned int flags, bool verbose) - { - struct find_mkvp_info info; - int rc; - - util_assert(cca != NULL, "Internal error: cca is NULL"); -+ util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); - -- pr_verbose(verbose, "Select mkvp %016llx in APQNs %s for the CCA host " -- "library", mkvp, apqns == 0 ? "ANY" : apqns); -+ pr_verbose(verbose, "Select mkvp %s in APQNs %s for the CCA host " -+ "library", printable_mkvp(CARD_TYPE_CCA, mkvp), -+ apqns == 0 ? "ANY" : apqns); - -- info.mkvp = mkvp; -+ memcpy(info.mkvp, mkvp, sizeof(info.mkvp)); - info.flags = flags; - info.found = false; - info.card = 0; - info.domain = 0; - info.verbose = verbose; - -- rc = handle_apqns(apqns, find_mkvp, &info, verbose); -+ rc = handle_apqns(apqns, CARD_TYPE_CCA, find_mkvp, &info, verbose); - if (rc < 0) - return rc; - -diff --git a/zkey/cca.h b/zkey/cca.h -index 2b248ec..c4761d5 100644 ---- a/zkey/cca.h -+++ b/zkey/cca.h -@@ -129,7 +129,7 @@ int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose); - #define FLAG_SEL_CCA_MATCH_OLD_MKVP 0x02 - #define FLAG_SEL_CCA_NEW_MUST_BE_SET 0x80 - --int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns, -+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u8 *mkvp, const char *apqns, - unsigned int flags, bool verbose); - - void print_msg_for_cca_envvars(const char *key_name); -diff --git a/zkey/ep11.c b/zkey/ep11.c -new file mode 100644 -index 0000000..22f81f7 ---- /dev/null -+++ b/zkey/ep11.c -@@ -0,0 +1,510 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lib/util_base.h" -+#include "lib/util_libc.h" -+#include "lib/util_panic.h" -+ -+#include "ep11.h" -+#include "pkey.h" -+#include "utils.h" -+ -+#define pr_verbose(verbose, fmt...) do { \ -+ if (verbose) \ -+ warnx(fmt); \ -+ } while (0) -+ -+/* -+ * Definitions for the EP11 library -+ */ -+#define EP11_LIBRARY_NAME "libep11.so" -+#define EP11_LIBRARY_VERSION 3 -+#define EP11_WEB_PAGE "http://www.ibm.com/security/cryptocards" -+ -+/** -+ * Returns the major and minor version of the of the used EP11 host library. -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error -+ */ -+static int get_ep11_version(struct ep11_lib *ep11, bool verbose) -+{ -+ unsigned int host_version; -+ CK_ULONG version_len = sizeof(host_version); -+ CK_RV rc; -+ -+ rc = ep11->dll_m_get_xcp_info(&host_version, &version_len, -+ CK_IBM_XCPHQ_VERSION, 0, 0); -+ if (rc != CKR_OK) { -+ pr_verbose(verbose, "Failed to obtain the EP11 host library " -+ "version: m_get_xcp_info: 0x%lx", rc); -+ return -EIO; -+ } -+ -+ pr_verbose(verbose, "host_version: 0x%08x", host_version); -+ -+ ep11->version.major = (host_version & 0x00FF0000) >> 16; -+ ep11->version.minor = host_version & 0x000000FF; -+ /* -+ * EP11 host library < v2.0 returns an invalid version (i.e. 0x100). -+ * This can safely be treated as version 1.0 -+ */ -+ if (ep11->version.major == 0) { -+ ep11->version.major = 1; -+ ep11->version.minor = 0; -+ } -+ -+ pr_verbose(verbose, "EP11 library version: %u.%u", -+ ep11->version.major, ep11->version.minor); -+ -+ return 0; -+} -+ -+/** -+ * Loads the EP11 library and provides the entry points of several functions. -+ * -+ * @param[out] ep11 on return this contains the address of the EP11 -+ * library and certain EP11 symbols. dlclose() should -+ * be used to free the library when no longer needed. -+ * @param verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, -ELIBACC in case of library load errors -+ */ -+int load_ep11_library(struct ep11_lib *ep11, bool verbose) -+{ -+ char lib_name[256]; -+ int libver; -+ int rc; -+ -+ util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); -+ -+ /* Load the EP11 library with highest available version'd SO name */ -+ for (libver = EP11_LIBRARY_VERSION; libver >= 0; libver--) { -+ if (libver > 0) -+ sprintf(lib_name, "%s.%d", EP11_LIBRARY_NAME, libver); -+ else -+ sprintf(lib_name, "%s", EP11_LIBRARY_NAME); -+ -+ ep11->lib_ep11 = dlopen(lib_name, RTLD_GLOBAL | RTLD_NOW); -+ if (ep11->lib_ep11 != NULL) -+ break; -+ } -+ if (ep11->lib_ep11 == NULL) { -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM Z Enterprise PKCS #11 " -+ "(EP11) Support Program (EP11 host library).\n" -+ "For the supported environments and downloads, see:\n%s", -+ EP11_WEB_PAGE); -+ return -ELIBACC; -+ } -+ -+ /* Get several EP11 host library functions */ -+ ep11->dll_m_init = (m_init_t)dlsym(ep11->lib_ep11, "m_init"); -+ ep11->dll_m_add_module = (m_add_module_t)dlsym(ep11->lib_ep11, -+ "m_add_module"); -+ ep11->dll_m_rm_module = (m_rm_module_t)dlsym(ep11->lib_ep11, -+ "m_rm_module"); -+ ep11->dll_m_get_xcp_info = (m_get_xcp_info_t)dlsym(ep11->lib_ep11, -+ "m_get_xcp_info"); -+ -+ ep11->dll_m_admin = (m_admin_t)dlsym(ep11->lib_ep11, "m_admin"); -+ ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t)dlsym(ep11->lib_ep11, -+ "xcpa_cmdblock"); -+ if (ep11->dll_xcpa_cmdblock == NULL) -+ ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t)dlsym(ep11->lib_ep11, -+ "ep11a_cmdblock"); -+ ep11->dll_xcpa_internal_rv = (xcpa_internal_rv_t)dlsym(ep11->lib_ep11, -+ "xcpa_internal_rv"); -+ if (ep11->dll_xcpa_internal_rv == NULL) -+ ep11->dll_xcpa_internal_rv = -+ (xcpa_internal_rv_t)dlsym(ep11->lib_ep11, -+ "ep11a_internal_rv"); -+ -+ /* dll_m_add_module and dll_m_rm_module may be NULL for V1 EP11 lib */ -+ if (ep11->dll_m_init == NULL || -+ ep11->dll_m_get_xcp_info == NULL || -+ ep11->dll_m_admin == NULL || -+ ep11->dll_xcpa_cmdblock == NULL || -+ ep11->dll_xcpa_internal_rv == NULL) { -+ pr_verbose(verbose, "%s", dlerror()); -+ warnx("The command requires the IBM Z Enterprise PKCS #11 " -+ "(EP11) Support Program (EP11 host library).\n" -+ "For the supported environments and downloads, see:\n%s", -+ EP11_WEB_PAGE); -+ dlclose(ep11->lib_ep11); -+ ep11->lib_ep11 = NULL; -+ return -ELIBACC; -+ } -+ -+ /* Initialize the EP11 library */ -+ rc = ep11->dll_m_init(); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to initialize the EP11 host " -+ "library: m_init: 0x%x", rc); -+ dlclose(ep11->lib_ep11); -+ ep11->lib_ep11 = NULL; -+ return -ELIBACC; -+ } -+ -+ pr_verbose(verbose, "EP11 library '%s' has been loaded successfully", -+ lib_name); -+ -+ return get_ep11_version(ep11, verbose); -+} -+ -+/** -+ * Get an EP11 target handle for a specific APQN (card and domain) -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] card the card number -+ * @param[in] domain the domain number -+ * @param[out] target on return: the target handle for the APQN -+ * @param verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of errors -+ */ -+int get_ep11_target_for_apqn(struct ep11_lib *ep11, int card, int domain, -+ target_t *target, bool verbose) -+{ -+ ep11_target_t *target_list; -+ struct XCP_Module module; -+ CK_RV rc; -+ -+ util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); -+ util_assert(target != NULL, "Internal error: target is NULL"); -+ -+ *target = XCP_TGT_INIT; -+ -+ if (ep11->dll_m_add_module != NULL) { -+ memset(&module, 0, sizeof(module)); -+ module.version = ep11->version.major >= 3 ? XCP_MOD_VERSION_2 -+ : XCP_MOD_VERSION_1; -+ module.flags = XCP_MFL_MODULE; -+ module.module_nr = card; -+ XCPTGTMASK_SET_DOM(module.domainmask, domain); -+ rc = ep11->dll_m_add_module(&module, target); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to add APQN %02x.%04x: " -+ "m_add_module rc=0x%lx", card, domain, rc); -+ return -EIO; -+ } -+ } else { -+ /* Fall back to old target handling */ -+ target_list = (ep11_target_t *)calloc(1, sizeof(ep11_target_t)); -+ if (target_list == NULL) -+ return -ENOMEM; -+ target_list->length = 1; -+ target_list->apqns[0] = card; -+ target_list->apqns[1] = domain; -+ *target = (target_t)target_list; -+ } -+ -+ return 0; -+} -+ -+/** -+ * Free an EP11 target handle -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] target the target handle to free -+ * -+ * @returns 0 on success, a negative errno in case of errors -+ */ -+void free_ep11_target_for_apqn(struct ep11_lib *ep11, target_t target) -+{ -+ util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); -+ -+ if (ep11->dll_m_rm_module != NULL) { -+ ep11->dll_m_rm_module(NULL, target); -+ } else { -+ /* -+ * With the old target handling, target is a pointer to -+ * ep11_target_t -+ */ -+ free((ep11_target_t *)target); -+ } -+} -+ -+struct find_mkvp_info { -+ u8 mkvp[MKVP_LENGTH]; -+ unsigned int flags; -+ bool found; -+ int card; -+ int domain; -+ bool verbose; -+}; -+ -+static int find_mkvp(int card, int domain, void *handler_data) -+{ -+ struct find_mkvp_info *info = (struct find_mkvp_info *)handler_data; -+ struct mk_info mk_info; -+ bool found = false; -+ int rc; -+ -+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); -+ if (rc == -ENODEV) -+ return 0; -+ if (rc != 0) -+ return rc; -+ -+ if (info->flags & FLAG_SEL_EP11_MATCH_CUR_MKVP) -+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID && -+ MKVP_EQ(mk_info.cur_mk.mkvp, info->mkvp)) -+ found = true; -+ -+ if (info->flags & FLAG_SEL_EP11_NEW_MUST_BE_SET) -+ if (mk_info.new_mk.mk_state != MK_STATE_COMMITTED) -+ found = false; -+ -+ if (found) { -+ info->card = card; -+ info->domain = domain; -+ info->found = true; -+ -+ pr_verbose(info->verbose, "%02x.%04x has the desired mkvp%s", -+ card, domain, -+ info->flags & FLAG_SEL_EP11_NEW_MUST_BE_SET ? -+ " and NEW MK set" : ""); -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/** -+ * Selects an APQN to be used for the Ep11 host library that has the specified -+ * master key verification pattern -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] mkvp the master key verification pattern to search for -+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -+ * or an empty string, then all online EP11 APQNs are -+ * checked. -+ * @param[in] flags Flags that control the MKVM matching and NEW register -+ * checking. Multiple flags can be combined. -+ * @param[out] target on return: the target handle for the APQN. If this is -+ * NULL, then no target is built. -+ * @param[out] card on return: the card that was selected (can be NULL) -+ * @param[out] domain on return: the domain that was selected (can be NULL) -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of errors -+ */ -+int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, -+ const char *apqns, unsigned int flags, -+ target_t *target, int *card, int *domain, -+ bool verbose) -+{ -+ struct find_mkvp_info info; -+ int rc; -+ -+ util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); -+ util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); -+ -+ pr_verbose(verbose, "Select mkvp %s in APQNs %s for the EP11 host " -+ "library", printable_mkvp(CARD_TYPE_EP11, mkvp), -+ apqns == 0 ? "ANY" : apqns); -+ -+ memcpy(info.mkvp, mkvp, sizeof(info.mkvp)); -+ info.flags = flags; -+ info.found = false; -+ info.card = 0; -+ info.domain = 0; -+ info.verbose = verbose; -+ -+ rc = handle_apqns(apqns, CARD_TYPE_EP11, find_mkvp, &info, verbose); -+ if (rc < 0) -+ return rc; -+ -+ if (!info.found) -+ return -ENODEV; -+ -+ if (target != NULL) { -+ rc = get_ep11_target_for_apqn(ep11, info.card, info.domain, -+ target, verbose); -+ if (rc != 0) -+ return rc; -+ } -+ -+ if (card != NULL) -+ *card = info.card; -+ if (domain != NULL) -+ *domain = info.domain; -+ -+ return 0; -+} -+ -+/** -+ * Performs an EP11 administrative request to Re-encrypt a single EP11 secure -+ * key with a new EP11 master key (wrapping key). -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] target the target handle to use for the re-encipher operation -+ * @param[in] card the card that corresponds to the target handle -+ * @param[in] domain the domain that corresponds to the target handle -+ * @param[in/out] ep11key the EP11 key token to reencipher. The re-enciphered -+ * secure key will be returned in this buffer. -+ * @param[in] ep11key_size the size of the secure key -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of errors -+ */ -+static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target, int card, -+ int domain, struct ep11keytoken *ep11key, -+ unsigned int ep11key_size, bool verbose) -+{ -+ CK_BYTE resp[MAX_BLOBSIZE]; -+ CK_BYTE req[MAX_BLOBSIZE]; -+ char ep11_token_header[sizeof(ep11key->head)]; -+ struct XCPadmresp lrb; -+ struct XCPadmresp rb; -+ size_t resp_len; -+ size_t blob_len; -+ long req_len; -+ CK_RV rv; -+ int rc; -+ -+ blob_len = ep11key->head.length; -+ if (blob_len > ep11key_size) { -+ pr_verbose(verbose, "Blob length larger than secure key size"); -+ return -EINVAL; -+ } -+ -+ rb.domain = domain; -+ lrb.domain = domain; -+ -+ /* The token header is an overlay over the (all zero) session field */ -+ memcpy(ep11_token_header, ep11key, sizeof(ep11_token_header)); -+ memset(ep11key->session, 0, sizeof(ep11key->session)); -+ -+ resp_len = sizeof(resp); -+ req_len = ep11->dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, -+ &rb, NULL, (unsigned char *)ep11key, -+ blob_len); -+ if (req_len < 0) { -+ pr_verbose(verbose, "Failed to build XCP command block"); -+ return -EIO; -+ } -+ -+ rv = ep11->dll_m_admin(resp, &resp_len, NULL, 0, req, req_len, NULL, 0, -+ target); -+ if (rv != CKR_OK || resp_len == 0) { -+ pr_verbose(verbose, "Command XCP_ADM_REENCRYPT failed. " -+ "rc = 0x%lx, resp_len = %ld", rv, resp_len); -+ return -EIO; -+ } -+ -+ rc = ep11->dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to parse response. rc = %d", rc); -+ return -EIO; -+ } -+ -+ if (rv != CKR_OK) { -+ pr_verbose(verbose, "Failed to re-encrypt the EP11 secure key. " -+ "rc = 0x%lx", rv); -+ switch (rv) { -+ case CKR_IBM_WKID_MISMATCH: -+ warnx("The EP11 secure key is currently encrypted " -+ "under a different master that does not match " -+ "the master key in the CURRENT master key " -+ "register of APQN %02X.%04X", card, domain); -+ break; -+ } -+ return -EIO; -+ } -+ -+ if (blob_len != lrb.pllen) { -+ pr_verbose(verbose, "Re-encrypted EP11 secure key size has " -+ "changed: org-len: %lu, new-len: %lu", blob_len, -+ lrb.pllen); -+ return -EIO; -+ } -+ -+ memcpy(ep11key, lrb.payload, blob_len); -+ memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header)); -+ -+ return 0; -+} -+ -+/** -+ * Re-encipher an EP11 secure key with a new EP11 master key (wrapping key). -+ * -+ * @param[in] ep11 the EP11 library structure -+ * @param[in] target the target handle to use for the re-encipher operation -+ * @param[in] card the card that corresponds to the target handle -+ * @param[in] domain the domain that corresponds to the target handle -+ * @param[in/out] secure_key the EP11 key token to reencipher. The re-enciphered -+ * secure key will be returned in this buffer. -+ * @param[in] secure_key_size the size of the secure key -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of errors -+ */ -+int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, int card, -+ int domain, u8 *secure_key, -+ unsigned int secure_key_size, bool verbose) -+{ -+ struct ep11keytoken *ep11key = (struct ep11keytoken *)secure_key; -+ CK_IBM_DOMAIN_INFO dinf; -+ CK_ULONG dinf_len = sizeof(dinf); -+ CK_RV rv; -+ int rc; -+ -+ util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); -+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ -+ rv = ep11->dll_m_get_xcp_info(&dinf, &dinf_len, CK_IBM_XCPQ_DOMAIN, 0, -+ target); -+ if (rv != CKR_OK) { -+ pr_verbose(verbose, "Failed to query domain information for " -+ "%02X.%04X: m_get_xcp_info rc: 0x%lx", card, domain, -+ rv); -+ return -EIO; -+ } -+ -+ if ((dinf.flags & CK_IBM_DOM_COMMITTED_NWK) == 0) { -+ warnx("The NEW master key register of APQN %02X.%04X is not " -+ "in COMMITTED state", card, domain); -+ return -ENODEV; -+ } -+ -+ rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, -+ secure_key_size, verbose); -+ if (rc != 0) -+ return rc; -+ -+ if (is_xts_key(secure_key, secure_key_size)) { -+ secure_key += EP11_KEY_SIZE; -+ secure_key_size -= EP11_KEY_SIZE; -+ ep11key = (struct ep11keytoken *)secure_key; -+ -+ rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, -+ secure_key_size, verbose); -+ if (rc != 0) -+ return rc; -+ } -+ -+ return 0; -+} -+ -diff --git a/zkey/ep11.h b/zkey/ep11.h -new file mode 100644 -index 0000000..3a0c2b5 ---- /dev/null -+++ b/zkey/ep11.h -@@ -0,0 +1,178 @@ -+/* -+ * zkey - Generate, re-encipher, and validate secure keys -+ * -+ * This header file defines the interface to the EP11 host library. -+ * -+ * Copyright IBM Corp. 2019 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef EP11_H -+#define EP11_H -+ -+#include -+ -+#include "lib/zt_common.h" -+ -+/* EP11 definitions */ -+ -+typedef uint64_t target_t; -+typedef unsigned long int CK_ULONG; -+typedef CK_ULONG CK_RV; -+typedef unsigned char CK_BYTE; -+typedef CK_BYTE CK_CHAR; -+typedef CK_ULONG *CK_ULONG_PTR; -+typedef void *CK_VOID_PTR; -+ -+typedef struct XCP_ModuleSocket { -+ char host[256 + 1]; -+ uint32_t port; -+} *XCP_ModuleSocket_t; -+ -+typedef struct XCP_DomainPerf { -+ unsigned int lastperf[256]; -+} *XCP_DomainPerf_t; -+ -+typedef struct XCP_Module { -+ uint32_t version; -+ uint64_t flags; -+ uint32_t domains; -+ unsigned char domainmask[256 / 8]; -+ struct XCP_ModuleSocket socket; -+ uint32_t module_nr; -+ void *mhandle; -+ struct XCP_DomainPerf perf; -+ /* ----- end of v1 fields ----- */ -+ uint32_t api; -+ /* ----- end of v2 fields ----- */ -+} *XCP_Module_t; -+ -+typedef enum { -+ XCP_MFL_SOCKET = 1, -+ XCP_MFL_MODULE = 2, -+ XCP_MFL_MHANDLE = 4, -+ XCP_MFL_PERF = 8, -+ XCP_MFL_VIRTUAL = 0x10, -+ XCP_MFL_STRICT = 0x20, -+ XCP_MFL_PROBE = 0x40, -+ XCP_MFL_ALW_TGT_ADD = 0x80, -+ XCP_MFL_MAX = 0xff -+} XCP_Module_Flags; -+ -+#define XCP_MOD_VERSION_1 1 -+#define XCP_MOD_VERSION_2 2 -+#define XCP_TGT_INIT ~0UL -+ -+#define XCPTGTMASK_SET_DOM(mask, domain) \ -+ mask[((domain)/8)] |= (1 << (7-(domain)%8)) -+ -+#define XCP_SERIALNR_CHARS 8 -+#define XCP_ADMCTR_BYTES ((size_t) (128/8)) -+#define XCP_KEYCSUM_BYTES (256/8) -+ -+#define XCP_ADM_REENCRYPT 25 /* transform blobs to next WK */ -+ -+#define MAX_BLOBSIZE 8192 -+ -+#define CKR_VENDOR_DEFINED 0x80000000 -+#define CKR_IBM_WKID_MISMATCH CKR_VENDOR_DEFINED + 0x10001 -+ -+typedef struct XCPadmresp { -+ uint32_t fn; -+ uint32_t domain; -+ uint32_t domainInst; -+ -+ /* module ID || module instance */ -+ unsigned char module[XCP_SERIALNR_CHARS + XCP_SERIALNR_CHARS]; -+ unsigned char modNr[XCP_SERIALNR_CHARS]; -+ unsigned char modInst[XCP_SERIALNR_CHARS]; -+ -+ unsigned char tctr[XCP_ADMCTR_BYTES]; /* transaction counter */ -+ -+ CK_RV rv; -+ uint32_t reason; -+ -+ const unsigned char *payload; -+ size_t pllen; -+} *XCPadmresp_t; -+ -+typedef struct CK_IBM_DOMAIN_INFO { -+ CK_ULONG domain; -+ CK_BYTE wk[XCP_KEYCSUM_BYTES]; -+ CK_BYTE nextwk[XCP_KEYCSUM_BYTES]; -+ CK_ULONG flags; -+ CK_BYTE mode[8]; -+} CK_IBM_DOMAIN_INFO; -+ -+#define CK_IBM_DOM_COMMITTED_NWK 8 -+ -+#define CK_IBM_XCPHQ_VERSION 0xff000001 -+#define CK_IBM_XCPQ_DOMAIN 3 -+ -+#define MAX_APQN 256 -+ -+typedef struct { -+ short format; -+ short length; -+ short apqns[2 * MAX_APQN]; -+} __packed ep11_target_t; -+ -+#define CKR_OK 0x00000000 -+ -+typedef int (*m_init_t) (void); -+typedef int (*m_add_module_t) (XCP_Module_t module, target_t *target); -+typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target); -+typedef CK_RV (*m_get_xcp_info_t)(CK_VOID_PTR pinfo, CK_ULONG_PTR infbytes, -+ unsigned int query, unsigned int subquery, -+ target_t target); -+typedef unsigned long int (*m_admin_t)(unsigned char *resp1, size_t *r1len, -+ unsigned char *resp2, size_t *r2len, -+ const unsigned char *cmd, size_t clen, -+ const unsigned char *sigs, size_t slen, -+ target_t target); -+typedef long (*xcpa_cmdblock_t)(unsigned char *blk, size_t blen, -+ unsigned int fn, const struct XCPadmresp *minf, -+ const unsigned char *tctr, -+ const unsigned char *payload, size_t plen); -+typedef long (*xcpa_internal_rv_t)(const unsigned char *rsp, size_t rlen, -+ struct XCPadmresp *rspblk, CK_RV *rv); -+ -+struct ep11_version { -+ unsigned int minor; -+ unsigned int major; -+}; -+ -+struct ep11_lib { -+ void *lib_ep11; -+ m_init_t dll_m_init; -+ m_add_module_t dll_m_add_module; -+ m_rm_module_t dll_m_rm_module; -+ m_get_xcp_info_t dll_m_get_xcp_info; -+ m_admin_t dll_m_admin; -+ xcpa_cmdblock_t dll_xcpa_cmdblock; -+ xcpa_internal_rv_t dll_xcpa_internal_rv; -+ struct ep11_version version; -+}; -+ -+int load_ep11_library(struct ep11_lib *ep11, bool verbose); -+ -+int get_ep11_target_for_apqn(struct ep11_lib *ep11, int card, int domain, -+ target_t *target, bool verbose); -+ -+void free_ep11_target_for_apqn(struct ep11_lib *ep11, target_t target); -+ -+#define FLAG_SEL_EP11_MATCH_CUR_MKVP 0x01 -+#define FLAG_SEL_EP11_NEW_MUST_BE_SET 0x80 -+ -+int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, -+ const char *apqns, unsigned int flags, -+ target_t *target, int *card, int *domain, -+ bool verbose); -+ -+int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, int card, -+ int domain, u8 *secure_key, -+ unsigned int secure_key_size, bool verbose); -+ -+#endif -diff --git a/zkey/keystore.c b/zkey/keystore.c -index 4f90bdd..2cf37bc 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -343,6 +343,8 @@ static int _keystore_valid_key_type(const char *key_type) - return 1; - if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) - return 1; -+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -+ return 1; - - return 0; - } -@@ -1085,6 +1087,7 @@ free: - struct apqn_check { - bool noonlinecheck; - bool nomsg; -+ enum card_type cardtype; - }; - - /** -@@ -1106,6 +1109,7 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), - int rc, card, domain; - regmatch_t pmatch[1]; - regex_t reg_buf; -+ unsigned int num; - - *normalized = NULL; - -@@ -1120,7 +1124,9 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), - goto out; - } - -- if (sscanf(apqn, "%x.%x", &card, &domain) != 2) { -+ if (sscanf(apqn, "%x.%x%n", &card, &domain, &num) != 2 || -+ num != strlen(apqn) || card < 0 || card > 0xff || -+ domain < 0 || domain > 0xFFFF) { - warnx("the APQN '%s' is not valid", apqn); - rc = -EINVAL; - goto out; -@@ -1133,11 +1139,11 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set), - goto out; - } - -- rc = sysfs_is_apqn_online(card, domain); -+ rc = sysfs_is_apqn_online(card, domain, info->cardtype); - if (rc != 1) { - if (info->nomsg == 0) - warnx("The APQN %02x.%04x is %s", card, domain, -- rc == -1 ? "not a CCA card" : "not online"); -+ rc == -1 ? "not the correct type" : "not online"); - rc = -EIO; - goto out; - } else { -@@ -1574,7 +1580,9 @@ static int _keystore_create_info_file(struct keystore *keystore, - struct volume_check vol_check = { .keystore = keystore, .name = name, - .set = 0 }; - struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, -- .nomsg = 0 }; -+ .nomsg = 0, -+ .cardtype = get_card_type_for_keytype( -+ key_type), }; - struct properties *key_props; - char temp[10]; - int rc; -@@ -1722,9 +1730,11 @@ int keystore_generate_key(struct keystore *keystore, const char *name, - if (rc != 0) - goto out_free_key_filenames; - -- rc = cross_check_apqns(apqns, 0, -- get_min_card_level_for_keytype(key_type), true, -- keystore->verbose); -+ rc = cross_check_apqns(apqns, NULL, -+ get_min_card_level_for_keytype(key_type), -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), -+ true, keystore->verbose); - if (rc == -EINVAL) - goto out_free_key_filenames; - if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -@@ -1801,7 +1811,7 @@ out_free_key_filenames: - * default is used. - * @param[in] import_file The name of a secure key containing the key to import - * @param[in] volume_type the type of volume -- * @param[in] cca the CCA library struct -+ * @param[in] lib the external library struct - * - * @returns 0 for success or a negative errno in case of an error - */ -@@ -1809,15 +1819,15 @@ int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, - const char *apqns, bool noapqncheck, size_t sector_size, - const char *import_file, const char *volume_type, -- struct cca_lib *cca) -+ struct ext_lib *lib) - { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct properties *key_props = NULL; - size_t secure_key_size; - const char *key_type; -+ u8 mkvp[MKVP_LENGTH]; - int selected = 1; - u8 *secure_key; -- u64 mkvp; - int rc; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); -@@ -1848,7 +1858,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, - } - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, keystore->verbose); -+ mkvp, keystore->verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); -@@ -1856,8 +1866,10 @@ int keystore_import_key(struct keystore *keystore, const char *name, - } - - rc = cross_check_apqns(apqns, mkvp, -- get_min_card_level_for_keytype(key_type), true, -- keystore->verbose); -+ get_min_card_level_for_keytype(key_type), -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), -+ true, keystore->verbose); - if (rc == -EINVAL) - goto out_free_key; - if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { -@@ -1866,13 +1878,13 @@ int keystore_import_key(struct keystore *keystore, const char *name, - } - - if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { -- if (cca->lib_csulcca == NULL) { -- rc = load_cca_library(cca, keystore->verbose); -+ if (lib->cca->lib_csulcca == NULL) { -+ rc = load_cca_library(lib->cca, keystore->verbose); - if (rc != 0) - goto out_free_key; - } - -- rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -+ rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, - FLAG_SEL_CCA_MATCH_CUR_MKVP | - FLAG_SEL_CCA_MATCH_OLD_MKVP, - keystore->verbose); -@@ -1887,7 +1899,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, - goto out_free_key; - } - -- rc = restrict_key_export(cca, secure_key, secure_key_size, -+ rc = restrict_key_export(lib->cca, secure_key, secure_key_size, - keystore->verbose); - if (rc != 0) { - warnx("Failed to export-restrict the imported secure " -@@ -1991,9 +2003,9 @@ int keystore_change_key(struct keystore *keystore, const char *name, - struct properties *key_props = NULL; - char *apqns_prop, *key_type; - size_t secure_key_size; -+ u8 mkvp[MKVP_LENGTH]; - u8 *secure_key; - char temp[30]; -- u64 mkvp; - int rc; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); -@@ -2050,7 +2062,7 @@ int keystore_change_key(struct keystore *keystore, const char *name, - - rc = get_master_key_verification_pattern(secure_key, - secure_key_size, -- &mkvp, -+ mkvp, - keystore->verbose); - free(secure_key); - if (rc) -@@ -2060,6 +2072,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, - key_type = properties_get(key_props, PROP_NAME_KEY_TYPE); - rc = cross_check_apqns(apqns_prop, mkvp, - get_min_card_level_for_keytype(key_type), -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), - true, keystore->verbose); - free(apqns_prop); - free(key_type); -@@ -2274,7 +2288,7 @@ static void _keystore_print_record(struct util_rec *rec, - bool validation, const char *skey_filename, - size_t secure_key_size, bool is_xts, - size_t clear_key_bitsize, bool valid, -- bool is_old_mk, bool reenc_pending, u64 mkvp) -+ bool is_old_mk, bool reenc_pending, u8 *mkvp) - { - char temp_vp[VERIFICATION_PATTERN_LEN + 2]; - char *volumes_argz = NULL; -@@ -2338,11 +2352,17 @@ static void _keystore_print_record(struct util_rec *rec, - if (validation) { - if (valid) - util_rec_set(rec, REC_MASTERKEY, -- "%s CCA master key (MKVP: %016llx)", -- is_old_mk ? "OLD" : "CURRENT", mkvp); -+ "%s master key (MKVP: %s)", -+ is_old_mk ? "OLD" : "CURRENT", -+ printable_mkvp( -+ get_card_type_for_keytype(key_type), -+ mkvp)); - else - util_rec_set(rec, REC_MASTERKEY, -- "(unknown, MKVP: %016llx)", mkvp); -+ "(unknown, MKVP: %s)", -+ printable_mkvp( -+ get_card_type_for_keytype(key_type), -+ mkvp)); - } - if (volumes_argz != NULL) - util_rec_set_argz(rec, REC_VOLUMES, volumes_argz, -@@ -2424,7 +2444,7 @@ struct validate_info { - */ - static int _keystore_display_apqn_status(struct keystore *keystore, - struct properties *properties, -- u64 mkvp) -+ u8 *mkvp) - { - int rc, warning = 0; - char *apqns; -@@ -2437,7 +2457,9 @@ static int _keystore_display_apqn_status(struct keystore *keystore, - apqns = properties_get(properties, PROP_NAME_APQNS); - key_type = properties_get(properties, PROP_NAME_KEY_TYPE); - rc = cross_check_apqns(apqns, mkvp, -- get_min_card_level_for_keytype(key_type), true, -+ get_min_card_level_for_keytype(key_type), -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), true, - keystore->verbose); - if (rc != 0 && rc != -ENOTSUP) - warning = 1; -@@ -2515,11 +2537,11 @@ static int _keystore_process_validate(struct keystore *keystore, - char **apqn_list = NULL; - size_t clear_key_bitsize; - size_t secure_key_size; -+ u8 mkvp[MKVP_LENGTH]; - char *apqns = NULL; - u8 *secure_key = NULL; - int is_old_mk; - int rc, valid; -- u64 mkvp; - - rc = _keystore_ensure_keyfiles_exist(file_names, name); - if (rc != 0) -@@ -2549,7 +2571,7 @@ static int _keystore_process_validate(struct keystore *keystore, - } - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, keystore->verbose); -+ mkvp, keystore->verbose); - if (rc != 0) - goto out; - -@@ -2562,9 +2584,9 @@ static int _keystore_process_validate(struct keystore *keystore, - - if (valid && is_old_mk) { - util_print_indented("WARNING: The secure key is currently " -- "enciphered with the OLD CCA master key. " -+ "enciphered with the OLD master key. " - "To mitigate the danger of data loss " -- "re-encipher it with the CURRENT CCA " -+ "re-encipher it with the CURRENT " - "master key\n", 0); - info->num_warnings++; - } -@@ -2647,7 +2669,7 @@ struct reencipher_params { - struct reencipher_info { - struct reencipher_params params; - int pkey_fd; -- struct cca_lib *cca; -+ struct ext_lib *lib; - unsigned long num_reenciphered; - unsigned long num_failed; - unsigned long num_skipped; -@@ -2658,7 +2680,7 @@ struct reencipher_info { - * - * @param[in] keystore the keystore - * @param[in] name the name of the key -- * @param[in] cca the CCA library struct -+ * @param[in] lib the external library struct - * @param[in] params reenciphering parameters - * @param[in] secure_key a buffer containing the secure key - * @param[in] secure_key_size the size of the secure key -@@ -2670,37 +2692,29 @@ struct reencipher_info { - */ - static int _keystore_perform_reencipher(struct keystore *keystore, - const char *name, -- struct cca_lib *cca, -+ struct ext_lib *lib, - struct reencipher_params *params, - u8 *secure_key, size_t secure_key_size, - bool is_old_mk, const char *apqns) - { -- int rc, selected = 1; -- u64 mkvp; -- -- rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, keystore->verbose); -- if (rc != 0) { -- warnx("Failed to get the master key verification pattern: %s", -- strerror(-rc)); -- return rc; -- } -+ bool selected; -+ int rc; - - if (!params->from_old && !params->to_new) { - /* Autodetect reencipher mode */ - if (is_old_mk) { - params->from_old = 1; - util_print_indented("The secure key is currently " -- "enciphered with the OLD CCA " -+ "enciphered with the OLD " - "master key and is being " - "re-enciphered with the CURRENT " -- "CCA master key\n", 0); -+ "master key\n", 0); - } else { - params->to_new = 1; - util_print_indented("The secure key is currently " -- "enciphered with the CURRENT CCA " -+ "enciphered with the CURRENT " - "master key and is being " -- "re-enciphered with the NEW CCA " -+ "re-enciphered with the NEW " - "master key\n", 0); - } - } -@@ -2711,64 +2725,52 @@ static int _keystore_perform_reencipher(struct keystore *keystore, - - pr_verbose(keystore, - "Secure key '%s' will be re-enciphered from OLD " -- "to the CURRENT CCA master key", name); -- -- rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -- FLAG_SEL_CCA_MATCH_OLD_MKVP, -- keystore->verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -- if (rc != 0) { -- warnx("No APQN found that is suitable for " -- "re-enciphering this secure AES key"); -- return rc; -- } -+ "to the CURRENT master key", name); - -- rc = key_token_change(cca, secure_key, secure_key_size, -- METHOD_OLD_TO_CURRENT, -- keystore->verbose); -+ rc = reencipher_secure_key(lib, secure_key, secure_key_size, -+ apqns, REENCIPHER_OLD_TO_CURRENT, -+ &selected, keystore->verbose); - if (rc != 0) { -- warnx("Failed to re-encipher '%s' from OLD to " -- "CURRENT CCA master key", name); -- if (!selected) -- print_msg_for_cca_envvars("secure AES key"); -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering this secure AES key"); -+ } else { -+ warnx("Failed to re-encipher '%s' from OLD to " -+ "CURRENT master key", name); -+ if (!selected && -+ !is_ep11_aes_key(secure_key, -+ secure_key_size)) -+ print_msg_for_cca_envvars( -+ "secure AES key"); -+ } - return rc; - } - } - if (params->to_new) { - pr_verbose(keystore, - "Secure key '%s' will be re-enciphered from " -- "CURRENT to the NEW CCA master key", name); -+ "CURRENT to the NEW master key", name); - - if (params->inplace == -1) - params->inplace = 0; - -- rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, -- FLAG_SEL_CCA_MATCH_CUR_MKVP | -- FLAG_SEL_CCA_NEW_MUST_BE_SET, -- keystore->verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -- if (rc != 0) { -- util_print_indented("No APQN found that is suitable " -- "for re-enciphering this secure " -- "AES key and has the NEW master " -- "key loaded", 0); -- return rc; -- } -- -- rc = key_token_change(cca, secure_key, secure_key_size, -- METHOD_CURRENT_TO_NEW, -- keystore->verbose); -+ rc = reencipher_secure_key(lib, secure_key, secure_key_size, -+ apqns, REENCIPHER_CURRENT_TO_NEW, -+ &selected, keystore->verbose); - if (rc != 0) { -- warnx("Failed to re-encipher '%s' from CURRENT to " -- "NEW CCA master key", name); -- if (!selected) -- print_msg_for_cca_envvars("secure AES key"); -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering this secure AES key and " -+ "has the NEW master key loaded"); -+ } else { -+ warnx("Failed to re-encipher '%s' from CURRENT " -+ "to NEW master key", name); -+ if (!selected && -+ !is_ep11_aes_key(secure_key, -+ secure_key_size)) -+ print_msg_for_cca_envvars( -+ "secure AES key"); -+ } - return rc; - } - } -@@ -2848,7 +2850,7 @@ static int _keystore_process_reencipher(struct keystore *keystore, - if (params.complete) { - warnx("Key '%s' is not valid, re-enciphering is not " - "completed", name); -- warnx("The new CCA master key might yet have to be set " -+ warnx("The new master key might yet have to be set " - "as the CURRENT master key."); - } else { - warnx("Key '%s' is not valid, it is not re-enciphered", -@@ -2862,7 +2864,7 @@ static int _keystore_process_reencipher(struct keystore *keystore, - if (!params.complete) { - printf("Re-enciphering key '%s'\n", name); - -- rc = _keystore_perform_reencipher(keystore, name, info->cca, -+ rc = _keystore_perform_reencipher(keystore, name, info->lib, - ¶ms, secure_key, - secure_key_size, is_old_mk, - properties_get(properties, -@@ -2931,7 +2933,7 @@ static int _keystore_process_reencipher(struct keystore *keystore, - - if (params.inplace != 1) { - util_asprintf(&temp, "Staged re-enciphering is initiated for " -- "key '%s'. After the NEW CCA master key has been " -+ "key '%s'. After the NEW master key has been " - "set to become the CURRENT master key run " - "'zkey reencipher' with option '--complete' to " - "complete the re-enciphering process", name); -@@ -2967,17 +2969,17 @@ out: - * @param[in] name_filter the name filter to select the key (can be NULL) - * @param[in] apqn_filter the APQN filter to seletc the key (can be NULL) - * @param[in] from_old If true the key is reenciphered from the OLD to the -- * CURRENT CCA master key. -+ * CURRENT master key. - * @param[in] to_new If true the key is reenciphered from the CURRENT to -- * the OLD CCA master key. -+ * the OLD master key. - * @param[in] inplace if true, the key will be re-enciphere in-place - * @param[in] staged if true, the key will be re-enciphere not in-place - * @param[in] complete if true, a pending re-encipherment is completed - * @param[in] pkey_fd the file descriptor of /dev/pkey -- * @param[in] cca the CCA library struct -- * Note: if both from Old and toNew are FALSE, then the reencipherement mode is -+ * @param[in] lib the external library struct -+ * Note: if both fromOld and toNew are FALSE, then the reencipherement mode is - * detected automatically. If both are TRUE then the key is reenciphered -- * from the OLD to the NEW CCA master key. -+ * from the OLD to the NEW master key. - * Note: if both inplace and staged are FLASE, then the key is re-enciphered - * inplace when for OLD-to-CURRENT, and is reenciphered staged for - * CURRENT-to-NEW. -@@ -2987,7 +2989,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - const char *apqn_filter, - bool from_old, bool to_new, bool inplace, - bool staged, bool complete, int pkey_fd, -- struct cca_lib *cca) -+ struct ext_lib *lib) - { - struct reencipher_info info; - int rc; -@@ -3003,7 +3005,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - info.params.inplace = 0; - info.params.complete = complete; - info.pkey_fd = pkey_fd; -- info.cca = cca; -+ info.lib = lib; - info.num_failed = 0; - info.num_reenciphered = 0; - info.num_skipped = 0; -@@ -3956,13 +3958,13 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - * @param[in] noapqncheck if true, the specified APQN(s) are not checked for - * existence and type. - * @param[in] pkey_fd the file descriptor of /dev/pkey -- * @param[in] cca the CCA library struct -+ * @param[in] lib the external library struct - * - * @returns 0 for success or a negative errno in case of an error - */ - int keystore_convert_key(struct keystore *keystore, const char *name, - const char *key_type, bool noapqncheck, bool quiet, -- int pkey_fd, struct cca_lib *cca) -+ int pkey_fd, struct ext_lib *lib) - { - struct key_filenames file_names = { NULL, NULL, NULL }; - u8 output_key[2 * MAX_SECURE_KEY_SIZE]; -@@ -3973,9 +3975,9 @@ int keystore_convert_key(struct keystore *keystore, const char *name, - char **apqn_list = NULL; - size_t secure_key_size; - u8 *secure_key = NULL; -+ u8 mkvp[MKVP_LENGTH]; - char *apqns = NULL; - char *temp; -- u64 mkvp; - - util_assert(keystore != NULL, "Internal error: keystore is NULL"); - util_assert(name != NULL, "Internal error: name is NULL"); -@@ -4028,7 +4030,10 @@ int keystore_convert_key(struct keystore *keystore, const char *name, - if (apqns != NULL) - apqn_list = str_list_split(apqns); - -- rc = cross_check_apqns(apqns, 0, min_level, true, keystore->verbose); -+ rc = cross_check_apqns(apqns, NULL, min_level, -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), -+ true, keystore->verbose); - if (rc == -EINVAL) - goto out; - if (rc != 0 && rc != -ENOTSUP && !noapqncheck) { -@@ -4044,11 +4049,11 @@ int keystore_convert_key(struct keystore *keystore, const char *name, - goto out; - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, keystore->verbose); -+ mkvp, keystore->verbose); - if (rc) - goto out; - -- rc = select_cca_adapter_by_mkvp(cca, mkvp, NULL, -+ rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, - FLAG_SEL_CCA_MATCH_CUR_MKVP, - keystore->verbose); - if (rc == -ENOTSUP) { -@@ -4078,7 +4083,7 @@ int keystore_convert_key(struct keystore *keystore, const char *name, - - memset(output_key, 0, sizeof(output_key)); - output_key_size = sizeof(output_key); -- rc = convert_aes_data_to_cipher_key(cca, secure_key, -+ rc = convert_aes_data_to_cipher_key(lib->cca, secure_key, - secure_key_size, output_key, - &output_key_size, - keystore->verbose); -@@ -4090,7 +4095,7 @@ int keystore_convert_key(struct keystore *keystore, const char *name, - goto out; - } - -- rc = restrict_key_export(cca, output_key, output_key_size, -+ rc = restrict_key_export(lib->cca, output_key, output_key_size, - keystore->verbose); - if (rc != 0) { - warnx("Export restricting the converted secure key '%s' has " -diff --git a/zkey/keystore.h b/zkey/keystore.h -index b17a575..d952814 100644 ---- a/zkey/keystore.h -+++ b/zkey/keystore.h -@@ -14,7 +14,6 @@ - - #include - --#include "cca.h" - #include "pkey.h" - - struct keystore { -@@ -38,7 +37,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, - const char *apqns, bool noapqncheck, size_t sector_size, - const char *import_file, const char *volume_type, -- struct cca_lib *cca); -+ struct ext_lib *lib); - - int keystore_change_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, -@@ -56,7 +55,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, - const char *apqn_filter, - bool from_old, bool to_new, bool inplace, - bool staged, bool complete, int pkey_fd, -- struct cca_lib *cca); -+ struct ext_lib *lib); - - int keystore_copy_key(struct keystore *keystore, const char *name, - const char *newname, const char *volumes); -@@ -83,7 +82,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, - - int keystore_convert_key(struct keystore *keystore, const char *name, - const char *key_type, bool noapqncheck, bool quiet, -- int pkey_fd, struct cca_lib *cca); -+ int pkey_fd, struct ext_lib *lib); - - void keystore_free(struct keystore *keystore); - -diff --git a/zkey/pkey.c b/zkey/pkey.c -index 793ed9e..83e3b3f 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -26,6 +26,7 @@ - #include "lib/util_panic.h" - - #include "pkey.h" -+#include "utils.h" - - #ifndef AF_ALG - #define AF_ALG 38 -@@ -691,6 +692,10 @@ static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type, - apqn_entries, - verbose); - return rc; -+ case -EINVAL: -+ /* This is usually due to an unsupported key type */ -+ rc = -ENOTSUP; -+ goto out; - default: - goto out; - } -@@ -753,7 +758,6 @@ static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags, - u32 *apqn_entries, bool verbose) - { - struct pkey_apqns4key apqns4key; -- u64 mkvp; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -@@ -796,16 +800,14 @@ static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags, - if (!is_cca_aes_data_key(key, keylen)) - return -ENOTSUP; - -- rc = get_master_key_verification_pattern(key, keylen, -- &mkvp, -- verbose); -- if (rc != 0) -- return rc; -- - rc = build_apqn_list_for_aes_data(apqn_list, apqns, - apqn_entries, - verbose); - return rc; -+ case -EINVAL: -+ /* This is usually due to an unsupported key type */ -+ rc = -ENOTSUP; -+ goto out; - default: - goto out; - } -@@ -857,6 +859,8 @@ static enum pkey_key_type key_type_to_pkey_type(const char *key_type) - return PKEY_TYPE_CCA_DATA; - if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) - return PKEY_TYPE_CCA_CIPHER; -+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -+ return PKEY_TYPE_EP11; - - return 0; - } -@@ -875,6 +879,8 @@ static size_t key_size_for_type(enum pkey_key_type type) - return AESDATA_KEY_SIZE; - case PKEY_TYPE_CCA_CIPHER: - return AESCIPHER_KEY_SIZE; -+ case PKEY_TYPE_EP11: -+ return EP11_KEY_SIZE; - default: - return 0; - } -@@ -1218,7 +1224,7 @@ static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn, - * @param[out] clear_key_bitsize on return , the cryptographic size of the - * clear key - * @param[out] is_old_mk in return set to 1 to indicate if the secure key -- * is currently enciphered by the OLD CCA master key -+ * is currently enciphered by the OLD master key - * @param[in] apqns a zero terminated array of pointers to APQN-strings, - * or NULL for AUTOSELECT - * @param[in] verbose if true, verbose messages are printed -@@ -1234,6 +1240,7 @@ int validate_secure_key(int pkey_fd, - struct pkey_apqn *list = NULL; - u32 i, list_entries = 0; - bool xts, valid; -+ u32 flags; - int rc; - - util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); -@@ -1241,11 +1248,15 @@ int validate_secure_key(int pkey_fd, - - xts = is_xts_key(secure_key, secure_key_size); - -+ flags = PKEY_FLAGS_MATCH_CUR_MKVP; -+ if (is_cca_aes_data_key(secure_key, secure_key_size) || -+ is_cca_aes_cipher_key(secure_key, secure_key_size)) -+ flags |= PKEY_FLAGS_MATCH_ALT_MKVP; -+ - rc = build_apqn_list_for_key(pkey_fd, secure_key, - HALF_KEYSIZE_FOR_XTS(secure_key_size, xts), -- PKEY_FLAGS_MATCH_CUR_MKVP | -- PKEY_FLAGS_MATCH_ALT_MKVP, -- apqns, &list, &list_entries, verbose); -+ flags, apqns, &list, &list_entries, -+ verbose); - if (rc != 0) { - pr_verbose(verbose, "Failed to build a list of APQNs that can " - "validate this secure key: %s", strerror(-rc)); -@@ -1454,18 +1465,22 @@ out: - } - - int get_master_key_verification_pattern(const u8 *key, size_t key_size, -- u64 *mkvp, bool UNUSED(verbose)) -+ u8 *mkvp, bool UNUSED(verbose)) - { - struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; - struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; -+ struct ep11keytoken *ep11key = (struct ep11keytoken *)key; - - util_assert(key != NULL, "Internal error: secure_key is NULL"); - util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); - -+ memset(mkvp, 0, MKVP_LENGTH); - if (is_cca_aes_data_key(key, key_size)) -- *mkvp = datakey->mkvp; -+ memcpy(mkvp, &datakey->mkvp, sizeof(datakey->mkvp)); - else if (is_cca_aes_cipher_key(key, key_size)) -- memcpy(mkvp, cipherkey->kvp, sizeof(*mkvp)); -+ memcpy(mkvp, &cipherkey->kvp, sizeof(cipherkey->kvp)); -+ else if (is_ep11_aes_key(key, key_size)) -+ memcpy(mkvp, &ep11key->wkvp, sizeof(ep11key->wkvp)); - else - return -EINVAL; - -@@ -1545,6 +1560,34 @@ bool is_cca_aes_cipher_key(const u8 *key, size_t key_size) - return true; - } - -+/** -+ * Check if the specified key is a EP11 AES key token. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an EP11 AES token type -+ */ -+bool is_ep11_aes_key(const u8 *key, size_t key_size) -+{ -+ struct ep11keytoken *ep11key = (struct ep11keytoken *)key; -+ -+ if (key == NULL || key_size < EP11_KEY_SIZE) -+ return false; -+ -+ if (ep11key->head.type != TOKEN_TYPE_NON_CCA) -+ return false; -+ if (ep11key->head.version != TOKEN_VERSION_EP11_AES) -+ return false; -+ if (ep11key->head.length > key_size) -+ return false; -+ -+ if (ep11key->version != 0x1234) -+ return false; -+ -+ return true; -+} -+ - /** - * Check if the specified key is an XTS type key - * -@@ -1565,6 +1608,11 @@ bool is_xts_key(const u8 *key, size_t key_size) - is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE, - key_size - AESCIPHER_KEY_SIZE)) - return true; -+ } else if (is_ep11_aes_key(key, key_size)) { -+ if (key_size == 2 * EP11_KEY_SIZE && -+ is_ep11_aes_key(key + EP11_KEY_SIZE, -+ key_size - EP11_KEY_SIZE)) -+ return true; - } - - return false; -@@ -1585,6 +1633,7 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - { - struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; - struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; -+ struct ep11keytoken *ep11key = (struct ep11keytoken *)key; - - util_assert(bitsize != NULL, "Internal error: bitsize is NULL"); - -@@ -1606,6 +1655,12 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - if (cipherkey->pfv == 0x00) /* V0 payload */ - *bitsize += cipherkey->pl - 384; - } -+ } else if (is_ep11_aes_key(key, key_size)) { -+ *bitsize = ep11key->head.keybitlen; -+ if (key_size == 2 * EP11_KEY_SIZE) { -+ ep11key = (struct ep11keytoken *)(key + EP11_KEY_SIZE); -+ *bitsize += ep11key->head.keybitlen; -+ } - } else { - return -EINVAL; - } -@@ -1627,7 +1682,8 @@ const char *get_key_type(const u8 *key, size_t key_size) - return KEY_TYPE_CCA_AESDATA; - if (is_cca_aes_cipher_key(key, key_size)) - return KEY_TYPE_CCA_AESCIPHER; -- -+ if (is_ep11_aes_key(key, key_size)) -+ return KEY_TYPE_EP11_AES; - return NULL; - } - -@@ -1647,10 +1703,48 @@ int get_min_card_level_for_keytype(const char *key_type) - return 3; - if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) - return 6; -+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -+ return 7; - - return -1; - } - -+const struct fw_version *get_min_fw_version_for_keytype(const char *key_type) -+{ -+ static const struct fw_version ep11_fw_version = { -+ .major = 0, .minor = 0, .api_ordinal = 4, }; -+ -+ if (key_type == NULL) -+ return NULL; -+ -+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -+ return &ep11_fw_version; -+ -+ return NULL; -+} -+ -+/** -+ * Returns the card type required for a specific key type -+ * -+ * @param[in] key_type the type of the key -+ * -+ * @returns the card type, or CARD_TYPE_ANY for unknown key types -+ */ -+enum card_type get_card_type_for_keytype(const char *key_type) -+{ -+ if (key_type == NULL) -+ return CARD_TYPE_ANY; -+ -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) -+ return CARD_TYPE_CCA; -+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) -+ return CARD_TYPE_CCA; -+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -+ return CARD_TYPE_EP11; -+ -+ return CARD_TYPE_ANY; -+} -+ - /** - * Performs extended checks on an AES CIPHER key. It checks the key usage - * fields (KUFs) and key management fields (KMFs) of the key. The function -@@ -1787,3 +1881,169 @@ int check_aes_cipher_key(const u8 *key, size_t key_size) - - return mismatch ? -EINVAL : 0; - } -+ -+static int reencipher_cca_secure_key(struct cca_lib *cca, u8 *secure_key, -+ size_t secure_key_size, const char *apqns, -+ u8 *mkvp, enum reencipher_method method, -+ bool *apqn_selected, bool verbose) -+{ -+ unsigned int flags; -+ int rc; -+ -+ if (method == REENCIPHER_OLD_TO_CURRENT) -+ flags = FLAG_SEL_CCA_MATCH_OLD_MKVP; -+ else -+ flags = FLAG_SEL_CCA_MATCH_CUR_MKVP | -+ FLAG_SEL_CCA_NEW_MUST_BE_SET; -+ -+ *apqn_selected = true; -+ -+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, flags, -+ verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ *apqn_selected = false; -+ } -+ if (rc != 0) { -+ pr_verbose(verbose, "No APQN found that is suitable " -+ "for re-enciphering this secure key"); -+ return rc; -+ } -+ -+ rc = key_token_change(cca, secure_key, secure_key_size, -+ method == REENCIPHER_OLD_TO_CURRENT ? -+ METHOD_OLD_TO_CURRENT : -+ METHOD_CURRENT_TO_NEW, -+ verbose); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to re-encipher secure key: " -+ "%s", strerror(-rc)); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static int reencipher_ep11_secure_key(struct ep11_lib *ep11, u8 *secure_key, -+ size_t secure_key_size, const char *apqns, -+ u8 *mkvp, bool *apqn_selected, -+ bool verbose) -+{ -+ unsigned int flags; -+ int card, domain; -+ target_t target; -+ int rc; -+ -+ flags = FLAG_SEL_EP11_MATCH_CUR_MKVP | -+ FLAG_SEL_EP11_NEW_MUST_BE_SET; -+ -+ *apqn_selected = true; -+ -+ rc = select_ep11_apqn_by_mkvp(ep11, mkvp, apqns, flags, -+ &target, &card, &domain, verbose); -+ if (rc == -ENOTSUP) { -+ rc = 0; -+ *apqn_selected = false; -+ } -+ if (rc != 0) { -+ pr_verbose(verbose, "No APQN found that is suitable " -+ "for re-enciphering this secure key"); -+ return rc; -+ } -+ -+ rc = reencipher_ep11_key(ep11, target, card, domain, -+ secure_key, secure_key_size, verbose); -+ free_ep11_target_for_apqn(ep11, target); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to re-encipher secure key: " -+ "%s", strerror(-rc)); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+ -+/** -+ * Re-enciphers a secure key -+ * -+ * @param[in] lib the external library struct -+ * @param[in] secure_key a buffer containing the secure key -+ * @param[in] secure_key_size the secure key size -+ * @param[in] apqns a comma separated list of APQNs. If NULL is -+ * specified, or an empty string, then all online -+ * APQNs of the matching type are subject to be used. -+ * @param[in] method the re-encipher method -+ * @param[out] apqn_selected On return: true if a specific APQN was selected. -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 on success, a negative errno in case of an error. -+ * -ENODEV is returned if no APQN could be found with a matching master key. -+ * -EIO is returned if the re-enciphering has failed. -+ */ -+int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, -+ size_t secure_key_size, const char *apqns, -+ enum reencipher_method method, bool *apqn_selected, -+ bool verbose) -+{ -+ u8 mkvp[MKVP_LENGTH]; -+ int rc; -+ -+ util_assert(lib != NULL, "Internal error: lib is NULL"); -+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); -+ util_assert(apqn_selected != NULL, -+ "Internal error: apqn_selected is NULL"); -+ -+ *apqn_selected = true; -+ -+ rc = get_master_key_verification_pattern(secure_key, secure_key_size, -+ mkvp, verbose); -+ if (rc != 0) { -+ pr_verbose(verbose, "Failed to get the master key verification " -+ "pattern: %s", strerror(-rc)); -+ return rc; -+ } -+ -+ if (is_ep11_aes_key(secure_key, secure_key_size)) { -+ /* EP11 secure key: need the EP11 host library */ -+ if (lib->ep11->lib_ep11 == NULL) { -+ rc = load_ep11_library(lib->ep11, verbose); -+ if (rc != 0) -+ return rc; -+ } -+ -+ if (method == REENCIPHER_OLD_TO_CURRENT) { -+ util_print_indented("ERROR: An APQN of a IBM " -+ "cryptographic adapter in EP11 " -+ "coprocessor mode does not have an " -+ "OLD master key register. Thus, " -+ "you can not re-encipher a secure " -+ "key of type 'EP11-AES' from the " -+ "OLD to the CURRENT master key " -+ "register.\n", 0); -+ return -EINVAL; -+ } -+ -+ rc = reencipher_ep11_secure_key(lib->ep11, secure_key, -+ secure_key_size, apqns, mkvp, -+ apqn_selected, verbose); -+ } else if (is_cca_aes_data_key(secure_key, secure_key_size) || -+ is_cca_aes_cipher_key(secure_key, secure_key_size)) { -+ /* CCA secure key: need the CCA host library */ -+ if (lib->cca->lib_csulcca == NULL) { -+ rc = load_cca_library(lib->cca, verbose); -+ if (rc != 0) -+ return rc; -+ } -+ -+ rc = reencipher_cca_secure_key(lib->cca, secure_key, -+ secure_key_size, apqns, mkvp, -+ method, apqn_selected, verbose); -+ } else { -+ pr_verbose(verbose, "Invalid key type"); -+ rc = -EINVAL; -+ } -+ -+ return rc; -+} -+ -diff --git a/zkey/pkey.h b/zkey/pkey.h -index 3dfd588..93c48af 100644 ---- a/zkey/pkey.h -+++ b/zkey/pkey.h -@@ -15,6 +15,9 @@ - - #include "lib/zt_common.h" - -+#include "cca.h" -+#include "ep11.h" -+ - /* - * Definitions for the /dev/pkey kernel module interface - */ -@@ -25,11 +28,17 @@ struct tokenheader { - u8 res1[3]; - } __packed; - --#define TOKEN_TYPE_NON_CCA 0x00 --#define TOKEN_TYPE_CCA_INTERNAL 0x01 -+#define TOKEN_TYPE_NON_CCA 0x00 -+#define TOKEN_TYPE_CCA_INTERNAL 0x01 -+ -+/* CCA-Internal token versions */ -+#define TOKEN_VERSION_AESDATA 0x04 -+#define TOKEN_VERSION_AESCIPHER 0x05 - --#define TOKEN_VERSION_AESDATA 0x04 --#define TOKEN_VERSION_AESCIPHER 0x05 -+/* Non-CCA token versions */ -+#define TOKEN_VERSION_PROTECTED_KEY 0x01 -+#define TOKEN_VERSION_CLEAR_KEY 0x02 -+#define TOKEN_VERSION_EP11_AES 0x03 - - struct aesdatakeytoken { - u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ -@@ -80,11 +89,36 @@ struct aescipherkeytoken { - u8 varpart[80]; /* variable part */ - } __packed; - -+struct ep11keytoken { -+ union { -+ u8 session[32]; -+ struct { -+ u8 type; /* TOKEN_TYPE_NON_CCA (0x00) */ -+ u8 res0; /* unused */ -+ u16 length; /* length of token */ -+ u8 version; /* TOKEN_VERSION_EP11_AES (0x03) */ -+ u8 res1; /* unused */ -+ u16 keybitlen; /* clear key bit len, 0 for unknown */ -+ } head; -+ }; -+ u8 wkvp[16]; /* wrapping key verification pattern */ -+ u64 attr; /* boolean key attributes */ -+ u64 mode; /* mode bits */ -+ u16 version; /* 0x1234, ep11 blob struct version */ -+ u8 iv[14]; -+ u8 encrypted_key_data[144]; -+ u8 mac[32]; -+ u8 padding[64]; -+} __packed; -+ - #define AESDATA_KEY_SIZE sizeof(struct aesdatakeytoken) - #define AESCIPHER_KEY_SIZE sizeof(struct aescipherkeytoken) -+#define EP11_KEY_SIZE sizeof(struct ep11keytoken) - --#define MAX_SECURE_KEY_SIZE MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) --#define MIN_SECURE_KEY_SIZE MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE) -+#define MAX_SECURE_KEY_SIZE MAX(EP11_KEY_SIZE, \ -+ MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)) -+#define MIN_SECURE_KEY_SIZE MIN(EP11_KEY_SIZE, \ -+ MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)) - - struct pkey_seckey { - u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */ -@@ -136,6 +170,7 @@ struct pkey_verifykey { - enum pkey_key_type { - PKEY_TYPE_CCA_DATA = (u32) 1, - PKEY_TYPE_CCA_CIPHER = (u32) 2, -+ PKEY_TYPE_EP11 = (u32) 3, - }; - - enum pkey_key_size { -@@ -226,11 +261,30 @@ struct pkey_apqns4keytype { - - #define KEY_TYPE_CCA_AESDATA "CCA-AESDATA" - #define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER" -+#define KEY_TYPE_EP11_AES "EP11-AES" - - #define PAES_BLOCK_SIZE 16 - #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) - #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) - -+#define MKVP_LENGTH 16 -+ -+static const u8 zero_mkvp[MKVP_LENGTH] = { 0x00 }; -+ -+#define MKVP_EQ(mkvp1, mkvp2) (memcmp(mkvp1, mkvp2, MKVP_LENGTH) == 0) -+#define MKVP_ZERO(mkvp) (mkvp == NULL || MKVP_EQ(mkvp, zero_mkvp)) -+ -+enum card_type { -+ CARD_TYPE_ANY = -1, -+ CARD_TYPE_CCA = 1, -+ CARD_TYPE_EP11 = 2, -+}; -+ -+struct ext_lib { -+ struct cca_lib *cca; -+ struct ep11_lib *ep11; -+}; -+ - int open_pkey_device(bool verbose); - - int generate_secure_key_random(int pkey_fd, const char *keyfile, -@@ -257,14 +311,27 @@ int generate_key_verification_pattern(const u8 *key, size_t key_size, - char *vp, size_t vp_len, bool verbose); - - int get_master_key_verification_pattern(const u8 *key, size_t key_size, -- u64 *mkvp, bool verbose); -+ u8 *mkvp, bool verbose); - - bool is_cca_aes_data_key(const u8 *key, size_t key_size); - bool is_cca_aes_cipher_key(const u8 *key, size_t key_size); -+bool is_ep11_aes_key(const u8 *key, size_t key_size); - bool is_xts_key(const u8 *key, size_t key_size); - int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize); - const char *get_key_type(const u8 *key, size_t key_size); - int get_min_card_level_for_keytype(const char *key_type); -+const struct fw_version *get_min_fw_version_for_keytype(const char *key_type); -+enum card_type get_card_type_for_keytype(const char *key_type); - int check_aes_cipher_key(const u8 *key, size_t key_size); - -+enum reencipher_method { -+ REENCIPHER_OLD_TO_CURRENT = 1, -+ REENCIPHER_CURRENT_TO_NEW = 2, -+}; -+ -+int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, -+ size_t secure_key_size, const char *apqns, -+ enum reencipher_method method, bool *apqn_selected, -+ bool verbose); -+ - #endif -diff --git a/zkey/utils.c b/zkey/utils.c -index e70ebcd..a2d1c37 100644 ---- a/zkey/utils.c -+++ b/zkey/utils.c -@@ -25,6 +25,8 @@ - #include "lib/util_rec.h" - #include "lib/util_base.h" - -+ #include -+ - #include "utils.h" - #include "properties.h" - -@@ -34,14 +36,16 @@ - } while (0) - - /** -- * Checks if the specified card is of type CCA and is online -+ * Checks if the specified card is of the specified type and is online - * - * @param[in] card card number -+ * @param[in] cardtype card type (CCA, EP11 or ANY) - * -- * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its -- * not a CCA card. -+ * @returns 1 if its card of the specified type and is online, -+ * 0 if offline, -+ * -1 if its not the specified type. - */ --int sysfs_is_card_online(int card) -+int sysfs_is_card_online(int card, enum card_type cardtype) - { - long int online; - char *dev_path; -@@ -69,9 +73,21 @@ int sysfs_is_card_online(int card) - rc = 0; - goto out; - } -- if (type[4] != 'C') { -- rc = -1; -- goto out; -+ switch (cardtype) { -+ case CARD_TYPE_CCA: -+ if (type[4] != 'C') { -+ rc = -1; -+ goto out; -+ } -+ break; -+ case CARD_TYPE_EP11: -+ if (type[4] != 'P') { -+ rc = -1; -+ goto out; -+ } -+ break; -+ default: -+ break; - } - - out: -@@ -80,21 +96,23 @@ out: - } - - /** -- * Checks if the specified APQN is of type CCA and is online -+ * Checks if the specified APQN is of the specified type and is online - * - * @param[in] card card number - * @param[in] domain the domain -+ * @param[in] cardtype card type (CCA, EP11 or ANY) - * -- * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its -- * not a CCA card. -+ * @returns 1 if its card of the specified type and is online, -+ * 0 if offline, -+ * -1 if its not the specified type. - */ --int sysfs_is_apqn_online(int card, int domain) -+int sysfs_is_apqn_online(int card, int domain, enum card_type cardtype) - { - long int online; - char *dev_path; - int rc = 1; - -- rc = sysfs_is_card_online(card); -+ rc = sysfs_is_card_online(card, cardtype); - if (rc != 1) - return rc; - -@@ -145,7 +163,7 @@ int sysfs_get_card_level(int card) - rc = -1; - goto out; - } -- if (type[4] != 'C') { -+ if (type[4] != 'C' && type[4] != 'P') { - rc = -1; - goto out; - } -@@ -162,18 +180,62 @@ out: - } - - /** -- * Gets the 8 character ASCII serial number string of an card from the sysfs. -+ * Returns the type of the card. For a CEXnC CARD_TYPE_CCA is returned, -+ * for a CEXnP CARD_TYPE_EP11. -+ * -+ * @param[in] card card number -+ * -+ * @returns The card type, or -1 of the type can not be determined. -+ */ -+enum card_type sysfs_get_card_type(int card) -+{ -+ char *dev_path; -+ char type[20]; -+ enum card_type cardtype; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -+ if (!util_path_is_dir(dev_path)) { -+ cardtype = -1; -+ goto out; -+ } -+ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { -+ cardtype = -1; -+ goto out; -+ } -+ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { -+ cardtype = -1; -+ goto out; -+ } -+ switch (type[4]) { -+ case 'C': -+ cardtype = CARD_TYPE_CCA; -+ break; -+ case 'P': -+ cardtype = CARD_TYPE_EP11; -+ break; -+ default: -+ cardtype = -1; -+ break; -+ } -+ -+out: -+ free(dev_path); -+ return cardtype; -+} -+ -+/** -+ * Gets the 8-16 character ASCII serial number string of an card from the sysfs. - * - * @param[in] card card number -- * @param[out] serialnr Result buffer -+ * @param[out] serialnr Result buffer. Must be at least SERIALNR_LENGTH long. - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 if the serial number was returned. -ENODEV if the APQN is not -- * available, or is not a CCA card. -ENOTSUP if the serialnr sysfs -- * attribute is not available, because the zcrypt kernel module is -- * on an older level. -+ * available, or is not a CCA or EP11 card. -+ * -ENOTSUP if the serialnr sysfs attribute is not available, because -+ * the zcrypt kernel module is on an older level. - */ --int sysfs_get_serialnr(int card, char serialnr[9], bool verbose) -+int sysfs_get_serialnr(int card, char *serialnr, bool verbose) - { - char *dev_path; - int rc = 0; -@@ -181,7 +243,7 @@ int sysfs_get_serialnr(int card, char serialnr[9], bool verbose) - if (serialnr == NULL) - return -EINVAL; - -- if (sysfs_is_card_online(card) != 1) -+ if (sysfs_is_card_online(card, CARD_TYPE_ANY) != 1) - return -ENODEV; - - dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -@@ -189,7 +251,8 @@ int sysfs_get_serialnr(int card, char serialnr[9], bool verbose) - rc = -ENODEV; - goto out; - } -- if (util_file_read_line(serialnr, 9, "%s/serialnr", dev_path) != 0) { -+ if (util_file_read_line(serialnr, SERIALNR_LENGTH, "%s/serialnr", -+ dev_path) != 0) { - rc = -ENOTSUP; - goto out; - } -@@ -209,11 +272,79 @@ out: - return rc; - } - --static int parse_mk_info(char *line, struct mk_info *mk_info) -+/** -+ * Gets the firmware version of an card from the sysfs. -+ * Currently only EP11 cards provide this information. -+ * -+ * @param[in] card card number -+ * @param[out] fw_version On return: The firmware version numbers -+ * @param[in] verbose if true, verbose messages are printed -+ * -+ * @returns 0 if the firmware version was returned. -ENODEV if the APQN is not -+ * available, or is not a CCA or EP11 card. -+ * -ENOTSUP if the fw_version sysfs attribute is not available, because -+ * the zcrypt kernel module is on an older level, or because the card -+ * type does not provide this information. -+ */ -+int sysfs_get_firmware_version(int card, struct fw_version *fw_version, -+ bool verbose) -+{ -+ char *dev_path; -+ char buf[50]; -+ int rc = 0; -+ -+ if (fw_version == NULL) -+ return -EINVAL; -+ -+ if (sysfs_is_card_online(card, CARD_TYPE_ANY) != 1) -+ return -ENODEV; -+ -+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); -+ if (!util_path_is_dir(dev_path)) { -+ rc = -ENODEV; -+ goto out; -+ } -+ if (util_file_read_line(buf, sizeof(buf), "%s/FW_version", -+ dev_path) != 0) { -+ rc = -ENOTSUP; -+ goto out; -+ } -+ -+ if (sscanf(buf, "%d.%d", &fw_version->major, &fw_version->minor) != 2) { -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ if (util_file_read_line(buf, sizeof(buf), "%s/API_ordinalnr", -+ dev_path) != 0) { -+ rc = -ENOTSUP; -+ goto out; -+ } -+ -+ if (sscanf(buf, "%d", &fw_version->api_ordinal) != 1) { -+ rc = -ENODEV; -+ goto out; -+ } -+ -+ -+ pr_verbose(verbose, "Firmware version of %02x: %d.%d (API: %d)", card, -+ fw_version->major, fw_version->minor, -+ fw_version->api_ordinal); -+out: -+ if (rc != 0) -+ pr_verbose(verbose, "Failed to get firmware version for " -+ "%02x: %s", card, strerror(-rc)); -+ -+ free(dev_path); -+ return rc; -+} -+ -+static int parse_cca_mk_info(char *line, struct mk_info *mk_info) - { - struct mk_info_reg *mk_reg; - char *save; - char *tok; -+ u64 mkvp; - - tok = strtok_r(line, " ", &save); - if (tok == NULL) -@@ -256,9 +387,79 @@ static int parse_mk_info(char *line, struct mk_info *mk_info) - if (tok == NULL) - return -EIO; - -- if (sscanf(tok, "%llx", &mk_reg->mkvp) != 1) -+ if (sscanf(tok, "%llx", &mkvp) != 1) - return -EIO; - -+ memcpy(mk_reg->mkvp, &mkvp, sizeof(mkvp)); -+ -+ return 0; -+} -+ -+static int parse_ep11_mk_info(char *line, struct mk_info *mk_info) -+{ -+ struct mk_info_reg *mk_reg; -+ unsigned char *buf; -+ char *save; -+ char *tok; -+ long len; -+ -+ tok = strtok_r(line, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "WK") != 0) -+ return 0; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "NEW:") == 0) -+ mk_reg = &mk_info->new_mk; -+ else if (strcasecmp(tok, "CUR:") == 0) -+ mk_reg = &mk_info->cur_mk; -+ else -+ return -EIO; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ if (strcasecmp(tok, "valid") == 0) -+ mk_reg->mk_state = MK_STATE_VALID; -+ else if (strcasecmp(tok, "invalid") == 0) -+ mk_reg->mk_state = MK_STATE_INVALID; -+ else if (strcasecmp(tok, "empty") == 0) -+ mk_reg->mk_state = MK_STATE_EMPTY; -+ else if (strcasecmp(tok, "uncommitted") == 0) -+ mk_reg->mk_state = MK_STATE_UNCOMMITTED; -+ else if (strcasecmp(tok, "committed") == 0) -+ mk_reg->mk_state = MK_STATE_COMMITTED; -+ else -+ mk_reg->mk_state = MK_STATE_UNKNOWN; -+ -+ tok = strtok_r(NULL, " ", &save); -+ if (tok == NULL) -+ return -EIO; -+ -+ /* -+ * EP11 uses a 32 byte master key verification pattern. -+ * Usually only the first 16 bytes are used, so we store only up to -+ * 16 bytes. -+ */ -+ if (strlen(tok) >= MKVP_LENGTH * 2) { -+ if (strncmp(tok, "0x", 2) == 0) -+ tok += 2; -+ -+ buf = OPENSSL_hexstr2buf(tok, &len); -+ if (buf == NULL) -+ return -EIO; -+ if (len > MKVP_LENGTH) -+ len = MKVP_LENGTH; -+ memcpy(mk_reg->mkvp, buf, len); -+ OPENSSL_free(buf); -+ } -+ - return 0; - } - -@@ -272,12 +473,13 @@ static int parse_mk_info(char *line, struct mk_info *mk_info) - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 if the master key info was returned. -ENODEV if the APQN is not -- * available, or is not a CCA card. -ENOTSUP if the mkvps sysfs -- * attribute is not available, because the zcrypt kernel module is -- * on an older level. -+ * available, or is not a CCA or EP11 card. -+ * -ENOTSUP if the mkvps sysfs attribute is not available, because the -+ * zcrypt kernel module is on an older level. - */ - int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) - { -+ enum card_type cardtype; - char *dev_path; - char *p, *end; - char buf[100]; -@@ -292,9 +494,11 @@ int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) - mk_info->cur_mk.mk_state = MK_STATE_UNKNOWN; - mk_info->old_mk.mk_state = MK_STATE_UNKNOWN; - -- if (sysfs_is_apqn_online(card, domain) != 1) -+ if (sysfs_is_apqn_online(card, domain, CARD_TYPE_ANY) != 1) - return -ENODEV; - -+ cardtype = sysfs_get_card_type(card); -+ - dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/mkvps", - card, card, domain); - if (!util_path_is_reg_file(dev_path)) { -@@ -310,14 +514,22 @@ int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) - - /* - * Expected contents: -- * AES NEW: -- * AES CUR: -- * AES OLD: -- * with -- * : 'empty' or 'partial' or 'full' -- * , : 'valid' or 'invalid' -- * , , -+ * AES CUR: -+ * AES OLD: -+ * with -+ * : 'empty' or 'partial' or 'full' -+ * , : 'valid' or 'invalid' -+ * , , : -+ * 8 byte hex string with leading 0x -+ * For EP11 cards: -+ * WK NEW: -+ * WK CUR: -+ * with -+ * : 'invalid' or 'valid' -+ * : 'empty' or 'uncommitted' or 'committed' -+ * and : '-' or a 32 byte hash pattern - */ - while ((p = fgets(buf, sizeof(buf), fp)) != NULL) { - end = memchr(buf, '\n', sizeof(buf)); -@@ -329,7 +541,17 @@ int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) - pr_verbose(verbose, "mkvp for %02x.%04x: %s", card, domain, - buf); - -- rc = parse_mk_info(buf, mk_info); -+ switch (cardtype) { -+ case CARD_TYPE_CCA: -+ rc = parse_cca_mk_info(buf, mk_info); -+ break; -+ case CARD_TYPE_EP11: -+ rc = parse_ep11_mk_info(buf, mk_info); -+ break; -+ default: -+ rc = -EINVAL; -+ break; -+ } - if (rc != 0) - break; - } -@@ -338,7 +560,8 @@ int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose) - - if (mk_info->new_mk.mk_state == MK_STATE_UNKNOWN && - mk_info->cur_mk.mk_state == MK_STATE_UNKNOWN && -- mk_info->old_mk.mk_state == MK_STATE_UNKNOWN) -+ (cardtype == CARD_TYPE_CCA && -+ mk_info->old_mk.mk_state == MK_STATE_UNKNOWN)) - rc = -EIO; - out: - if (rc != 0) -@@ -349,8 +572,9 @@ out: - return rc; - } - --static int scan_for_domains(int card, apqn_handler_t handler, -- void *handler_data, bool verbose) -+static int scan_for_domains(int card, enum card_type cardtype, -+ apqn_handler_t handler, void *handler_data, -+ bool verbose) - { - struct dirent **namelist; - char fname[290]; -@@ -369,9 +593,9 @@ static int scan_for_domains(int card, apqn_handler_t handler, - - pr_verbose(verbose, "Found %02x.%04x", card, domain); - -- if (sysfs_is_apqn_online(card, domain) != 1) { -+ if (sysfs_is_apqn_online(card, domain, cardtype) != 1) { - pr_verbose(verbose, "APQN %02x.%04x is offline or not " -- "CCA", card, domain); -+ "the correct type", card, domain); - continue; - } - -@@ -385,8 +609,8 @@ static int scan_for_domains(int card, apqn_handler_t handler, - } - - --static int scan_for_apqns(apqn_handler_t handler, void *handler_data, -- bool verbose) -+static int scan_for_apqns(enum card_type cardtype, apqn_handler_t handler, -+ void *handler_data, bool verbose) - { - struct dirent **namelist; - int i, n, card, rc = 0; -@@ -405,13 +629,14 @@ static int scan_for_apqns(apqn_handler_t handler, void *handler_data, - - pr_verbose(verbose, "Found card %02x", card); - -- if (sysfs_is_card_online(card) != 1) { -- pr_verbose(verbose, "Card %02x is offline or not CCA", -- card); -+ if (sysfs_is_card_online(card, cardtype) != 1) { -+ pr_verbose(verbose, "Card %02x is offline or not the " -+ "correct type", card); - continue; - } - -- rc = scan_for_domains(card, handler, handler_data, verbose); -+ rc = scan_for_domains(card, cardtype, handler, handler_data, -+ verbose); - if (rc != 0) - break; - } -@@ -421,21 +646,22 @@ static int scan_for_apqns(apqn_handler_t handler, void *handler_data, - } - - /** -- * Calls the handler for all APQNs specified in the apqns parameter, or of this -- * is NULL, for all online CCA APQNs found in sysfs. In case sysfs is inspected, -- * the cards and domains are processed in alphabetical order. -+ * Calls the handler for all APQNs specified in the apqns parameter, or if this -+ * is NULL, for all online CCA or EP11 APQNs found in sysfs. In case sysfs is -+ * inspected, the cards and domains are processed in alphabetical order. - * - * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -- * or an empty string, then all online CCA APQNs are -- * handled. -+ * or an empty string, then all online CCA or EP11 APQNs -+ * are handled. -+ * @param[in] cardtype card type (CCA, EP11 or ANY) - * @param[in] handler a handler function that is called for each APQN - * @param[in] handler_data private data that is passed to the handler - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 for success or a negative errno in case of an error - */ --int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, -- bool verbose) -+int handle_apqns(const char *apqns, enum card_type cardtype, -+ apqn_handler_t handler, void *handler_data, bool verbose) - { - int card, domain; - char *copy, *tok; -@@ -443,7 +669,7 @@ int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, - int rc = 0; - - if (apqns == NULL || (apqns != NULL && strlen(apqns) == 0)) { -- rc = scan_for_apqns(handler, handler_data, verbose); -+ rc = scan_for_apqns(cardtype, handler, handler_data, verbose); - } else { - copy = util_strdup(apqns); - tok = strtok_r(copy, ",", &save); -@@ -472,6 +698,7 @@ int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, - - struct print_apqn_info { - struct util_rec *rec; -+ enum card_type cardtype; - bool verbose; - }; - -@@ -480,33 +707,41 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) - struct print_apqn_info *info = (struct print_apqn_info *)handler_data; - struct mk_info mk_info; - int rc, level; -+ enum card_type type; - - rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); - if (rc == -ENOTSUP) - return rc; - - level = sysfs_get_card_level(card); -+ type = sysfs_get_card_type(card); - - util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain); - -+ if (info->cardtype != CARD_TYPE_ANY && type != info->cardtype) -+ rc = -EINVAL; -+ - if (rc == 0) { -- if (mk_info.new_mk.mk_state == MK_STATE_FULL) -- util_rec_set(info->rec, "NEW", "%016llx", -- mk_info.new_mk.mkvp); -+ if (mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == MK_STATE_COMMITTED) -+ util_rec_set(info->rec, "NEW", "%s", -+ printable_mkvp(type, mk_info.new_mk.mkvp)); - else if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) - util_rec_set(info->rec, "NEW", "partially loaded"); -+ else if (mk_info.new_mk.mk_state == MK_STATE_UNCOMMITTED) -+ util_rec_set(info->rec, "NEW", "uncommitted"); - else - util_rec_set(info->rec, "NEW", "-"); - - if (mk_info.cur_mk.mk_state == MK_STATE_VALID) -- util_rec_set(info->rec, "CUR", "%016llx", -- mk_info.cur_mk.mkvp); -+ util_rec_set(info->rec, "CUR", "%s", -+ printable_mkvp(type, mk_info.cur_mk.mkvp)); - else - util_rec_set(info->rec, "CUR", "-"); - - if (mk_info.old_mk.mk_state == MK_STATE_VALID) -- util_rec_set(info->rec, "OLD", "%016llx", -- mk_info.old_mk.mkvp); -+ util_rec_set(info->rec, "OLD", "%s", -+ printable_mkvp(type, mk_info.old_mk.mkvp)); - else - util_rec_set(info->rec, "OLD", "-"); - } else { -@@ -515,8 +750,9 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) - util_rec_set(info->rec, "OLD", "?"); - } - -- if (level > 0) -- util_rec_set(info->rec, "TYPE", "CEX%dC", level); -+ if (level > 0 && type != CARD_TYPE_ANY) -+ util_rec_set(info->rec, "TYPE", "CEX%d%c", level, -+ type == CARD_TYPE_CCA ? 'C' : 'P'); - else - util_rec_set(info->rec, "TYPE", "?"); - -@@ -529,40 +765,51 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data) - * Prints master key information for all specified APQNs - * - * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -- * or an empty string, then all online CCA APQNs are -- * printed. -+ * or an empty string, then all online CCA or EP11 APQNs -+ * are printed. -+ * @param[in] cardtype card type (CCA, EP11 or ANY) - * @param[in] verbose if true, verbose messages are printed - * - * @returns 0 for success or a negative errno in case of an error. -ENOTSUP is - * returned when the mkvps sysfs attribute is not available, because - * the zcrypt kernel module is on an older level. - */ --int print_mk_info(const char *apqns, bool verbose) -+int print_mk_info(const char *apqns, enum card_type cardtype, bool verbose) - { - struct print_apqn_info info; -- int rc; -+ int rc, mklen; - - info.verbose = verbose; -+ info.cardtype = cardtype; - info.rec = util_rec_new_wide("-"); - -+ if (cardtype == CARD_TYPE_CCA) -+ mklen = 16; -+ else -+ mklen = 32; -+ - util_rec_def(info.rec, "APQN", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN"); -- util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK"); -- util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK"); -- util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK"); -+ util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, mklen, "NEW MK"); -+ util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, mklen, "CURRENT MK"); -+ if (cardtype != CARD_TYPE_EP11) -+ util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, mklen, -+ "OLD MK"); - util_rec_def(info.rec, "TYPE", UTIL_REC_ALIGN_LEFT, 6, "TYPE"); - util_rec_print_hdr(info.rec); - -- rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose); -+ rc = handle_apqns(apqns, cardtype, print_apqn_mk_info, &info, verbose); - - util_rec_free(info.rec); - return rc; - } - - struct cross_check_info { -- u64 mkvp; -- u64 new_mkvp; -+ u8 mkvp[MKVP_LENGTH]; -+ u8 new_mkvp[MKVP_LENGTH]; - bool key_mkvp; -+ enum card_type cardtype; - int min_level; -+ const struct fw_version *min_fw_version; - u32 num_cur_match; - u32 num_old_match; - u32 num_new_match; -@@ -575,15 +822,17 @@ struct cross_check_info { - static int cross_check_mk_info(int card, int domain, void *handler_data) - { - struct cross_check_info *info = (struct cross_check_info *)handler_data; -+ struct fw_version fw_version; -+ enum card_type type; - struct mk_info mk_info; - char temp[200]; -- int rc, level; -+ int rc, level = 0; - - rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose); - if (rc == -ENODEV) { - info->print_mks = 1; - printf("WARNING: APQN %02x.%04x: Not available or not of " -- "type CCA\n", card, domain); -+ "the correct type\n", card, domain); - return 0; - } - if (rc != 0) -@@ -591,6 +840,19 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - - info->num_checked++; - -+ if (info->cardtype != CARD_TYPE_ANY) { -+ type = sysfs_get_card_type(card); -+ if (type != info->cardtype) { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The card type " -+ "is not CEXn%c.", card, domain, -+ info->cardtype == CARD_TYPE_CCA ? 'C' : 'P'); -+ util_print_indented(temp, 0); -+ return 0; -+ } -+ } -+ - if (info->min_level >= 0) { - level = sysfs_get_card_level(card); - -@@ -598,25 +860,63 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - info->print_mks = 1; - info->mismatch = 1; - sprintf(temp, "WARNING: APQN %02x.%04x: The card level " -- "is less than CEX%dC.", card, domain, -+ "is less than CEX%dn.", card, domain, - info->min_level); - util_print_indented(temp, 0); - } - } - -+ if (info->min_fw_version != NULL) { -+ rc = sysfs_get_firmware_version(card, &fw_version, -+ info->verbose); -+ if (rc == 0) { -+ if (fw_version.api_ordinal < -+ info->min_fw_version->api_ordinal) { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The " -+ "firmware version is too less to " -+ "support secure keys of that type", -+ card, domain); -+ util_print_indented(temp, 0); -+ } -+ if (info->min_level > 0 && info->min_level == level && -+ (fw_version.major < info->min_fw_version->major || -+ (fw_version.major == info->min_fw_version->major && -+ fw_version.minor < info->min_fw_version->minor))) { -+ info->print_mks = 1; -+ info->mismatch = 1; -+ sprintf(temp, "WARNING: APQN %02x.%04x: The " -+ "firmware version is too less to " -+ "support secure keys of that type", -+ card, domain); -+ util_print_indented(temp, 0); -+ } -+ } -+ } -+ - if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " - "register is only partially loaded.", card, domain); - util_print_indented(temp, 0); - } -+ if (mk_info.new_mk.mk_state == MK_STATE_UNCOMMITTED) { -+ info->print_mks = 1; -+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " -+ "register is loaded but uncommitted.", card, domain); -+ util_print_indented(temp, 0); -+ } - -- if (info->new_mkvp == 0 && -- mk_info.new_mk.mk_state == MK_STATE_FULL) -- info->new_mkvp = mk_info.new_mk.mkvp; -+ if (MKVP_ZERO(info->new_mkvp) && -+ (mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == MK_STATE_COMMITTED)) -+ memcpy(info->new_mkvp, mk_info.new_mk.mkvp, -+ sizeof(info->new_mkvp)); - -- if (mk_info.new_mk.mk_state == MK_STATE_FULL && -- mk_info.new_mk.mkvp != info->new_mkvp) { -+ if ((mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == MK_STATE_COMMITTED) && -+ !MKVP_EQ(mk_info.new_mk.mkvp, info->new_mkvp)) { - info->print_mks = 1; - sprintf(temp, "WARNING: APQN %02x.%04x: The NEW master key " - "register contains a different master key than " -@@ -634,15 +934,16 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - } - - if (mk_info.old_mk.mk_state == MK_STATE_VALID && -- mk_info.old_mk.mkvp == mk_info.cur_mk.mkvp) { -+ MKVP_EQ(mk_info.old_mk.mkvp, mk_info.cur_mk.mkvp)) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The OLD master key " - "register contains the same master key as the CURRENT " - "master key register.", card, domain); - util_print_indented(temp, 0); - } -- if (mk_info.new_mk.mk_state == MK_STATE_FULL && -- mk_info.new_mk.mkvp == mk_info.cur_mk.mkvp) { -+ if ((mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == MK_STATE_COMMITTED) && -+ MKVP_EQ(mk_info.new_mk.mkvp, mk_info.cur_mk.mkvp)) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " - "register contains the same master key as the CURRENT " -@@ -651,7 +952,7 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - } - if (mk_info.new_mk.mk_state == MK_STATE_FULL && - mk_info.old_mk.mk_state == MK_STATE_VALID && -- mk_info.new_mk.mkvp == mk_info.old_mk.mkvp) { -+ MKVP_EQ(mk_info.new_mk.mkvp, mk_info.old_mk.mkvp)) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key " - "register contains the same master key as the OLD " -@@ -659,28 +960,29 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - util_print_indented(temp, 0); - } - -- if (info->mkvp == 0) -- info->mkvp = mk_info.cur_mk.mkvp; -+ if (MKVP_ZERO(info->mkvp)) -+ memcpy(info->mkvp, mk_info.cur_mk.mkvp, sizeof(info->mkvp)); - - if (info->key_mkvp) { - if (mk_info.cur_mk.mk_state == MK_STATE_VALID && -- mk_info.cur_mk.mkvp == info->mkvp) -+ MKVP_EQ(mk_info.cur_mk.mkvp, info->mkvp)) - info->num_cur_match++; - - if (mk_info.old_mk.mk_state == MK_STATE_VALID && -- mk_info.old_mk.mkvp == info->mkvp) -+ MKVP_EQ(mk_info.old_mk.mkvp, info->mkvp)) - info->num_old_match++; - -- if (mk_info.new_mk.mk_state == MK_STATE_FULL && -- mk_info.new_mk.mkvp == info->mkvp) -+ if ((mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == MK_STATE_COMMITTED) && -+ MKVP_EQ(mk_info.new_mk.mkvp, info->mkvp)) - info->num_new_match++; - } - -- if (mk_info.cur_mk.mkvp != info->mkvp) { -+ if (!MKVP_EQ(mk_info.cur_mk.mkvp, info->mkvp)) { - - if (info->key_mkvp) { - if (mk_info.old_mk.mk_state == MK_STATE_VALID && -- mk_info.old_mk.mkvp == info->mkvp) { -+ MKVP_EQ(mk_info.old_mk.mkvp, info->mkvp)) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The master" - " key has been changed to a new " -@@ -688,8 +990,10 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - "not yet been re-enciphered.", card, - domain); - util_print_indented(temp, 0); -- } else if (mk_info.new_mk.mk_state == MK_STATE_FULL && -- mk_info.new_mk.mkvp == info->mkvp) { -+ } else if ((mk_info.new_mk.mk_state == MK_STATE_FULL || -+ mk_info.new_mk.mk_state == -+ MK_STATE_COMMITTED) && -+ MKVP_EQ(mk_info.new_mk.mkvp, info->mkvp)) { - info->print_mks = 1; - sprintf(temp, "INFO: APQN %02x.%04x: The master" - " key has been changed but is not " -@@ -722,13 +1026,16 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - * out an information message about the APQNs that have a different master key. - * - * @param[in] apqns a comma separated list of APQNs. If NULL is specified, -- * or an empty string, then all online CCA APQNs are -+ * or an empty string, then all online APQNs are - * checked. - * @param[in] mkvp The master key verification pattern of a secure key. -- * If this is all zero, then the master keys are not -- * matched against it. -+ * If this is all zero or NULL, then the master keys are -+ * not matched against it. - * @param[in] min_level The minimum card level required. If min_level is -1 then - * the card level is not checked. -+ * @param[in] min_fw_version The minimum firmware version required. If NULL tne -+ * the firmware version is not checked. -+ * @param[in] cardtype card type (CCA, EP11 or ANY) - * @param[in] print_mks if true, then a the full master key info of all - * specified APQns is printed, in case of a mismatch. - * @param[in] verbose if true, verbose messages are printed -@@ -738,40 +1045,49 @@ static int cross_check_mk_info(int card, int domain, void *handler_data) - * -ENOTSUP is returned when the mkvps sysfs attribute is not - * available, because the zcrypt kernel module is on an older level. - */ --int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, -- bool print_mks, bool verbose) -+int cross_check_apqns(const char *apqns, u8 *mkvp, int min_level, -+ const struct fw_version *min_fw_version, -+ enum card_type cardtype, bool print_mks, bool verbose) - { - struct cross_check_info info; - char temp[200]; - int rc; - - memset(&info, 0, sizeof(info)); -- info.key_mkvp = mkvp != 0; -- info.mkvp = mkvp; -+ info.key_mkvp = !MKVP_ZERO(mkvp); -+ if (mkvp != NULL) -+ memcpy(info.mkvp, mkvp, sizeof(info.mkvp)); -+ info.cardtype = cardtype; - info.min_level = min_level; -+ info.min_fw_version = min_fw_version; - info.verbose = verbose; - -- pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx and " -- "min-level %d: %s", mkvp, min_level, -+ pr_verbose(verbose, "Cross checking APQNs with mkvp %s, " -+ "min-level %d, and min-fw-version %u.%u (api: %u): %s", -+ printable_mkvp(cardtype, info.mkvp), -+ min_level, -+ min_fw_version != NULL ? min_fw_version->major : 0, -+ min_fw_version != NULL ? min_fw_version->minor : 0, -+ min_fw_version != NULL ? min_fw_version->api_ordinal : 0, - apqns != NULL ? apqns : "ANY"); - -- rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose); -+ rc = handle_apqns(apqns, cardtype, cross_check_mk_info, &info, verbose); - if (rc != 0) - return rc; - - if (info.mismatch) { - if (info.key_mkvp) - printf("WARNING: Not all APQNs have the correct master " -- "key (%016llx).\n", mkvp); -+ "key (%s) or fulfill the requirements.\n", -+ printable_mkvp(cardtype, info.mkvp)); - else - printf("WARNING: Not all APQNs have the same master " -- "key.\n"); -- -+ "key or fulfill the requirements.\n"); - rc = -ENODEV; - } - if (info.num_checked == 0) { - printf("WARNING: None of the APQNs is available or of " -- "type CCA\n"); -+ "the correct type\n"); - rc = -ENODEV; - } - if (info.num_old_match > 0 && info.num_new_match > 0) { -@@ -787,7 +1103,7 @@ int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, - - if (print_mks && info.print_mks) { - printf("\n"); -- print_mk_info(apqns, verbose); -+ print_mk_info(apqns, cardtype, verbose); - printf("\n"); - } - -@@ -817,3 +1133,38 @@ bool prompt_for_yes(bool verbose) - - return false; - } -+ -+/* -+ * Returns a printable version of the specified master key verification pattern -+ * (MKVP) for the specified card type. Different card types use different -+ * number of bytes for MKVP. -+ * -+ * @param[in] cardtype card type (CCA, EP11 or ANY) -+ * @param[in] mkvp the master key verification pattern to print -+ * -+ * @returns address of a static char array containing the printed MKVP, or NULL -+ * in case of an error. -+ */ -+char *printable_mkvp(enum card_type cardtype, u8 *mkvp) -+{ -+ static char mkvp_print_buf[MKVP_LENGTH * 2 + 1]; -+ -+ if (mkvp == NULL) -+ return NULL; -+ -+ switch (cardtype) { -+ case CARD_TYPE_CCA: -+ /* CCA uses an 8 byte MKVP */ -+ sprintf(mkvp_print_buf, "%016llx", *((u64 *)mkvp)); -+ break; -+ case CARD_TYPE_EP11: -+ /* EP11 uses an 32 byte MKVP, but truncated to 16 bytes*/ -+ sprintf(mkvp_print_buf, "%016llx%016llx", *((u64 *)&mkvp[0]), -+ *((u64 *)&mkvp[8])); -+ break; -+ default: -+ return NULL; -+ } -+ -+ return mkvp_print_buf; -+} -diff --git a/zkey/utils.h b/zkey/utils.h -index 2a915eb..dd743f6 100644 ---- a/zkey/utils.h -+++ b/zkey/utils.h -@@ -14,30 +14,47 @@ - - #include "lib/zt_common.h" - --int sysfs_is_card_online(int card); -+#include "pkey.h" - --int sysfs_is_apqn_online(int card, int domain); -+int sysfs_is_card_online(int card, enum card_type cardtype); -+ -+int sysfs_is_apqn_online(int card, int domain, enum card_type cardtype); - - int sysfs_get_card_level(int card); - --int sysfs_get_serialnr(int card, char serialnr[9], bool verbose); -+enum card_type sysfs_get_card_type(int card); -+ -+#define SERIALNR_LENGTH 17 -+ -+int sysfs_get_serialnr(int card, char *serialnr, bool verbose); -+ -+struct fw_version { -+ unsigned int major; -+ unsigned int minor; -+ unsigned int api_ordinal; -+}; -+ -+int sysfs_get_firmware_version(int card, struct fw_version *fw_version, -+ bool verbose); - - #define MK_STATE_EMPTY 0 --#define MK_STATE_PARTIAL 1 --#define MK_STATE_FULL 2 -+#define MK_STATE_PARTIAL 1 /* For CCA only */ -+#define MK_STATE_FULL 2 /* For CCA only */ - #define MK_STATE_VALID 3 - #define MK_STATE_INVALID 4 -+#define MK_STATE_UNCOMMITTED 5 /* For EP11 only */ -+#define MK_STATE_COMMITTED 6 /* For EP11 only */ - #define MK_STATE_UNKNOWN -1 - - struct mk_info_reg { - int mk_state; -- u64 mkvp; -+ u8 mkvp[MKVP_LENGTH]; - }; - - struct mk_info { - struct mk_info_reg new_mk; - struct mk_info_reg cur_mk; -- struct mk_info_reg old_mk; -+ struct mk_info_reg old_mk; /* only available on CCA cards */ - }; - - int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, -@@ -45,14 +62,17 @@ int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, - - typedef int(*apqn_handler_t) (int card, int domain, void *handler_data); - --int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data, -- bool verbose); -+int handle_apqns(const char *apqns, enum card_type cardtype, -+ apqn_handler_t handler, void *handler_data, bool verbose); - --int print_mk_info(const char *apqns, bool verbose); -+int print_mk_info(const char *apqns, enum card_type cardtype, bool verbose); - --int cross_check_apqns(const char *apqns, u64 mkvp, int min_level, -- bool print_mks, bool verbose); -+int cross_check_apqns(const char *apqns, u8 *mkvp, int min_level, -+ const struct fw_version *min_fw_version, -+ enum card_type cardtype, bool print_mks, bool verbose); - - bool prompt_for_yes(bool verbose); - -+char *printable_mkvp(enum card_type cardtype, u8 *mkvp); -+ - #endif -diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1 -index 35e3591..69ec794 100644 ---- a/zkey/zkey-cryptsetup.1 -+++ b/zkey/zkey-cryptsetup.1 -@@ -28,7 +28,7 @@ zkey\-cryptsetup \- Manage secure AES volume keys of volumes encrypted with - Use \fBzkey\-cryptsetup\fP to validate and re-encipher secure AES - volume keys of volumes encrypted with \fBLUKS2\fP and the \fBpaes\fP cipher. - These secure AES volume keys are enciphered with a master key of an IBM --cryptographic adapter in CCA coprocessor mode. -+cryptographic adapter in CCA or EP11 coprocessor mode. - .PP - To encrypt a volume using \fBLUKS2\fP and the \fBpaes\fP cipher, generate a - secure AES key using \fBzkey\fP: \fB'zkey generate luks.key --xts'\fP. -@@ -112,7 +112,7 @@ Use the - .B reencipher - command to re-encipher a secure AES volume key of a volume encrypted with - \fBLUKS2\fP and the \fBpaes\fP cipher. A secure AES volume key must be --re-enciphered when the master key of the cryptographic adapter in CCA -+re-enciphered when the master key of the cryptographic adapter in CCA or EP11 - coprocessor mode changes. - .PP - The cryptographic adapter in CCA coprocessor mode has three different registers -@@ -135,11 +135,15 @@ the current master key. You can pro-actively re-encipher a secure key with the - option to do this. - .RE - .PP -+\fBNote:\fP An EP11 cryptographic adapter has only two registers to store master -+keys, \fBCURRENT\fP and \fBNEW\fP. -+.PP - Use the - .B \-\-from\-old - option to re-encipher a secure volume key that is currently enciphered with - the master key in the \fBOLD\fP register with the master key in the --\fBCURRENT\fP register. -+\fBCURRENT\fP register. This option is only available for secure keys of type -+\fBCCA-AESDATA\fP or \fBCCA-AESCIPHER\fP. - .PP - .PP - If both the -@@ -171,14 +175,14 @@ Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is performed in-place per - default. You can use option \fB--in-place\fP to force an in-place - re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case. Be aware that - an encrypted volume with a secure volume key that was re-enciphered in-place --from \fBCURRENT\fP to \fBNEW\fP is no longer usable, until the new CCA master --key has been made the current one. -+from \fBCURRENT\fP to \fBNEW\fP is no longer usable, until the new CCA or EP11 -+master key has been made the current one. - .PP - \fBStaged\fP mode means that the re-enciphered secure volume key is stored in a - separate (unbound) key slot in the LUKS2 header of the encrypted volume. Thus - all key slots containing the current secure volume key are still valid at this --point. Once the new CCA master key has been set (made active), you must rerun --the reencipher command with option \fB--complete\fP to complete the staged -+point. Once the new CCA or EP11 master key has been set (made active), you must -+rerun the reencipher command with option \fB--complete\fP to complete the staged - re-enciphering. When completing the staged re-enciphering, the (unbound) key - slot containing the re-enciphered secure volume key becomes the active - key slot and, optionally, all key slots containing the old secure volume key -@@ -217,9 +221,11 @@ function used to encrypt the volume key in the LUKS key slots is of less - relevance. - .PP - .B Note: --The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) --to be installed. For the supported environments and downloads, see: --\fIhttp://www.ibm.com/security/cryptocards\fP -+The \fBreencipher\fP command requires the CCA host library (libcsulcca.so, for) -+for secure volume keys of type CCA-AESDATA or CCA-AESCIPHER, or the IBM Z -+Enterprise PKCS #11 (EP11) Support Program (EP11 host library) for secure volume -+keys of type EP11-AES to be installed. For the supported environments and -+downloads, see: \fIhttp://www.ibm.com/security/cryptocards\fP - . - . - . -@@ -293,12 +299,13 @@ command to set a new secure AES volume key for a volume encrypted with - \fBLUKS2\fP and the \fBpaes\fP cipher. Use this command to recover from an - invalid secure AES volume key contained in the LUKS2 header. - A secure AES volume key contained in the LUKS2 header can become invalid when --the CCA master key is changed without re-enciphering the secure volume key. -+the CCA or EP11 master key is changed without re-enciphering the secure volume -+key. - .PP - You can recover the secure volume key only if you have a copy of the secure key --in a file, and this copy was re-enciphered when the CCA master key has been --changed. Thus, the copy of the secure key must be currently enciphered with the --CCA master key in the CURRENT or OLD master key register. -+in a file, and this copy was re-enciphered when the CCA or EP11 master key has -+been changed. Thus, the copy of the secure key must be currently enciphered with -+the CCA or EP11 master key in the CURRENT or OLD master key register. - Specify the secure key file with option - .B \-\-master\-key\-file - to set this secure key as the new volume key. -@@ -369,17 +376,17 @@ Forces that the re-enciphering of a secure volume key in the LUKS2 - header is performed in staged mode. Staged mode means that the re-enciphered - secure volume key is stored in a separate (unbound) key slot in the LUKS2 - header of the encrypted volume. Thus all key slots containing the current --secure volume key are still valid at this point. Once the new CCA master key --has been set (made active), you must rerun the reencipher command with option --\fB--complete\fP to complete the staged re-enciphering. Re-enciphering from --\fBCURRENT\fP to \fBNEW\fP is performed in staged mode per default. -+secure volume key are still valid at this point. Once the new CCA or EP11 master -+key has been set (made active), you must rerun the reencipher command with -+option \fB--complete\fP to complete the staged re-enciphering. Re-enciphering -+from \fBCURRENT\fP to \fBNEW\fP is performed in staged mode per default. - .TP - .BR \-p ", " \-\-complete --Completes a staged re-enciphering. Use this option after the new CCA master key --has been set (made active). When completing the staged re-enciphering, the --(unbound) key slot containing the re-enciphered secure volume key becomes --the active key slot and, optionally, all key slots containing the old secure --volume key are removed. -+Completes a staged re-enciphering. Use this option after the new CCA or EP11 -+master key has been set (made active). When completing the staged -+re-enciphering, the (unbound) key slot containing the re-enciphered secure -+volume key becomes the active key slot and, optionally, all key slots containing -+the old secure volume key are removed. - .TP - .BR \-q ", " \-\-batch\-mode - Suppresses all confirmation questions. Use with care! -diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c -index 7ba2842..24c0126 100644 ---- a/zkey/zkey-cryptsetup.c -+++ b/zkey/zkey-cryptsetup.c -@@ -35,6 +35,8 @@ - #include "misc.h" - #include "pkey.h" - #include "cca.h" -+#include "ep11.h" -+#include "utils.h" - - /* Detect if cryptsetup 2.1 or later is available */ - #ifdef CRYPT_LOG_DEBUG_JSON -@@ -104,12 +106,16 @@ static struct zkey_cryptsetup_globals { - bool batch_mode; - bool debug; - bool verbose; -+ struct ext_lib lib; - struct cca_lib cca; -+ struct ep11_lib ep11; - int pkey_fd; - struct crypt_device *cd; - } g = { - .tries = 3, - .pkey_fd = -1, -+ .lib.cca = &g.cca, -+ .lib.ep11 = &g.ep11, - }; - - /* -@@ -195,7 +201,7 @@ static struct util_opt opt_vec[] = { - { - .option = {"complete", 0, NULL, 'c'}, - .desc = "Completes a staged re-enciphering. Use this option " -- "after the new CCA master key has been set (made " -+ "after the new master key has been set (made " - "active)", - .command = COMMAND_REENCIPHER, - }, -@@ -268,6 +274,7 @@ struct zkey_cryptsetup_command { - unsigned int abbrev_len; - int (*function)(void); - int need_cca_library; -+ int need_ep11_library; - int need_pkey_device; - char *short_desc; - char *long_desc; -@@ -286,7 +293,7 @@ static struct zkey_cryptsetup_command zkey_cryptsetup_commands[] = { - .command = COMMAND_REENCIPHER, - .abbrev_len = 2, - .function = command_reencipher, -- .need_cca_library = 1, -+ /* Will load the CCA or EP11 library on demand */ - .need_pkey_device = 1, - .short_desc = "Re-encipher a secure volume key", - .long_desc = "Re-encipher a secure volume key of a volume " -@@ -624,7 +631,7 @@ out_err: - if (rc != 0) - secure_free(pass, MAX_PASSWORD_SIZE + 1); - else -- write(outfd, "\n", 1); -+ rc = write(outfd, "\n", 1) == 1 ? 0 : -EIO; - - if (infd != STDIN_FILENO) - close(infd); -@@ -1198,7 +1205,7 @@ out: - /* - * Prompts for yes or no. Returns true if 'y' or 'yes' was entered. - */ --static bool prompt_for_yes(void) -+static bool _prompt_for_yes(void) - { - char str[20]; - -@@ -1295,7 +1302,7 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key, - "now in unbound state. Do you want to remove " - "these key slots [y/N]?", 0); - -- if (!prompt_for_yes()) -+ if (!_prompt_for_yes()) - return 0; - - for (i = 0, n = 0; ; i++) { -@@ -1535,12 +1542,11 @@ static int reencipher_prepare(int token) - char *password = NULL; - size_t password_len; - char *key = NULL; -- int selected = 1; - size_t keysize; - int is_old_mk; -+ bool selected; - char *prompt; - char *msg; -- u64 mkvp; - int rc; - - if (token >= 0) { -@@ -1551,7 +1557,7 @@ static int reencipher_prepare(int token) - util_print_indented(msg, 0); - free(msg); - -- if (!prompt_for_yes()) { -+ if (!_prompt_for_yes()) { - warnx("Device '%s' is left unchanged", g.pos_arg); - return -ECANCELED; - } -@@ -1598,88 +1604,66 @@ static int reencipher_prepare(int token) - if (is_old_mk) { - g.fromold = 1; - util_asprintf(&msg, "The secure volume key of device " -- "'%s' is enciphered with the OLD CCA " -+ "'%s' is enciphered with the OLD " - "master key and is being re-enciphered " -- "with the CURRENT CCA master key.", -+ "with the CURRENT master key.", - g.pos_arg); - util_print_indented(msg, 0); - free(msg); - } else { - g.tonew = 1; - util_asprintf(&msg, "The secure volume key of device " -- "'%s' is enciphered with the CURRENT CCA " -+ "'%s' is enciphered with the CURRENT " - "master key and is being re-enciphered " -- "with the NEW CCA master key.", -+ "with the NEW master key.", - g.pos_arg); - util_print_indented(msg, 0); - free(msg); - } - } - -- rc = get_master_key_verification_pattern((u8 *)key, keysize, &mkvp, -- g.verbose); -- if (rc != 0) { -- warnx("Failed to get the master key verification pattern: %s", -- strerror(-rc)); -- goto out; -- } -- - if (g.fromold) { -- rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -- FLAG_SEL_CCA_MATCH_OLD_MKVP, -- g.verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -+ rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, -+ NULL, REENCIPHER_OLD_TO_CURRENT, -+ &selected, g.verbose); - if (rc != 0) { -- util_print_indented("No APQN found that is suitable " -- "for re-enciphering the secure AES " -- "volume key from the OLD to the " -- "CURRENT CCA master key.", 0); -- goto out; -- } -- -- rc = key_token_change(&g.cca, (u8 *)key, keysize, -- METHOD_OLD_TO_CURRENT, g.verbose); -- if (rc != 0) { -- warnx("Failed to re-encipher the secure volume key of " -- "device '%s'\n", g.pos_arg); -- if (!selected) -- print_msg_for_cca_envvars( -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume " -+ "key from the OLD to the CURRENT master " -+ "key."); -+ } else { -+ warnx("Failed to re-encipher the secure volume " -+ "key for device '%s'\n", g.pos_arg); -+ if (!selected && -+ !is_ep11_aes_key((u8 *)key, keysize)) -+ print_msg_for_cca_envvars( - "secure AES volume key"); -- rc = -EINVAL; -+ rc = -EINVAL; -+ } - goto out; - } - } - - if (g.tonew) { -- rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -- FLAG_SEL_CCA_MATCH_CUR_MKVP | -- FLAG_SEL_CCA_NEW_MUST_BE_SET, -- g.verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -+ rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, -+ NULL, REENCIPHER_CURRENT_TO_NEW, -+ &selected, g.verbose); - if (rc != 0) { -- util_print_indented("No APQN found that is suitable " -- "for re-enciphering the secure AES " -- "volume key from the CURRENT to " -- "the NEW CCA master key.", 0); -- goto out; -- } -- -- rc = key_token_change(&g.cca, (u8 *)key, keysize, -- METHOD_CURRENT_TO_NEW, -- g.verbose); -- if (rc != 0) { -- warnx("Failed to re-encipher the secure volume key of " -- "device '%s'\n", g.pos_arg); -- if (!selected) -- print_msg_for_cca_envvars( -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume " -+ "key from the CURRENT to the NEW master " -+ "key."); -+ } else { -+ warnx("Failed to re-encipher the secure volume " -+ "key for device '%s'\n", g.pos_arg); -+ if (!selected && -+ !is_ep11_aes_key((u8 *)key, keysize)) -+ print_msg_for_cca_envvars( - "secure AES volume key"); -- rc = -EINVAL; -+ rc = -EINVAL; -+ } - goto out; - } - } -@@ -1721,7 +1705,7 @@ static int reencipher_prepare(int token) - rc = 0; - - util_asprintf(&msg, "Staged re-enciphering is initiated for " -- "device '%s'. After the NEW CCA master key has been set " -+ "device '%s'. After the NEW master key has been set " - "to become the CURRENT master key, run 'zkey-cryptsetup " - "reencipher' with option '--complete' to complete the " - "re-enciphering process.", g.pos_arg, -@@ -1746,12 +1730,11 @@ static int reencipher_complete(int token) - char *password = NULL; - size_t password_len; - char *key = NULL; -- int selected = 1; - size_t keysize; - int is_old_mk; -+ bool selected; - char *prompt; - char *msg; -- u64 mkvp; - int rc; - - rc = get_reencipher_token(g.cd, token, &tok, true); -@@ -1762,7 +1745,7 @@ static int reencipher_complete(int token) - } - - util_asprintf(&msg, "The re-enciphered secure volume key for " -- "device '%s' is not valid.\nThe new CCA master key might " -+ "device '%s' is not valid.\nThe new master key might " - "yet have to be set as the CURRENT master key.", - g.pos_arg); - util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ", -@@ -1780,56 +1763,41 @@ static int reencipher_complete(int token) - - if (is_old_mk) { - util_asprintf(&msg, "The re-enciphered secure volume key " -- "of device '%s' is enciphered with the CCA " -+ "of device '%s' is enciphered with the " - "master key from the OLD master key register. " -- "The CCA master key might have changed again, " -+ "The master key might have changed again, " - "before the previous volume key re-enciphering " - "was completed.\n" - "Do you want to re-encipher the secure key with " -- "the CCA master key in the CURRENT master key " -+ "the master key in the CURRENT master key " - "register [y/N]?", g.pos_arg); - util_print_indented(msg, 0); - free(msg); - -- if (!prompt_for_yes()) { -+ if (!_prompt_for_yes()) { - warnx("Re-enciphering was aborted"); - rc = -ECANCELED; - goto out; - } - -- rc = get_master_key_verification_pattern((u8 *)key, keysize, -- &mkvp, g.verbose); -- if (rc != 0) { -- warnx("Failed to get the master key verification " -- "pattern: %s", -- strerror(-rc)); -- goto out; -- } -- -- rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -- FLAG_SEL_CCA_MATCH_OLD_MKVP, -- g.verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -+ rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, -+ NULL, REENCIPHER_OLD_TO_CURRENT, -+ &selected, g.verbose); - if (rc != 0) { -- util_print_indented("No APQN found that is suitable " -- "for re-enciphering the secure AES " -- "volume key from the OLD to the " -- "CURRENT CCA master key.", 0); -- goto out; -- } -- -- rc = key_token_change(&g.cca, (u8 *)key, keysize, -- METHOD_OLD_TO_CURRENT, g.verbose); -- if (rc != 0) { -- warnx("Failed to re-encipher the secure volume key for " -- "device '%s'\n", g.pos_arg); -- if (!selected) -- print_msg_for_cca_envvars( -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume " -+ "key from the OLD to the CURRENT master " -+ "key."); -+ } else { -+ warnx("Failed to re-encipher the secure volume " -+ "key for device '%s'\n", g.pos_arg); -+ if (!selected && -+ !is_ep11_aes_key((u8 *)key, keysize)) -+ print_msg_for_cca_envvars( - "secure AES volume key"); -- rc = -EINVAL; -+ rc = -EINVAL; -+ } - goto out; - } - -@@ -1952,13 +1920,14 @@ static int command_validate(void) - int reenc_pending = 0, vp_tok_avail = 0, is_valid = 0, is_old_mk = 0; - struct reencipher_token reenc_tok; - struct vp_token vp_tok; -+ const char *key_type; -+ u8 mkvp[MKVP_LENGTH]; - size_t clear_keysize; - size_t keysize = 0; - char *key = NULL; - char *prompt; - char *msg; - int token; -- u64 mkvp; - int rc; - - util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); -@@ -1990,28 +1959,32 @@ static int command_validate(void) - } - - rc = get_master_key_verification_pattern((u8 *)key, keysize, -- &mkvp, g.verbose); -+ mkvp, g.verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); - goto out; - } - -+ key_type = get_key_type((u8 *)key, keysize); -+ - printf("Validation of secure volume key of device '%s':\n", g.pos_arg); - printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); - printf(" Secure key size: %lu bytes\n", keysize); - printf(" XTS type key: %s\n", - is_xts_key((u8 *)key, keysize) ? "Yes" : "No"); -- printf(" Key type: %s\n", -- get_key_type((u8 *)key, keysize)); -+ printf(" Key type: %s\n", key_type); - if (is_valid) { - printf(" Clear key size: %lu bits\n", clear_keysize); -- printf(" Enciphered with: %s CCA master key (MKVP: " -- "%016llx)\n", is_old_mk ? "OLD" : "CURRENT", mkvp); -+ printf(" Enciphered with: %s master key (MKVP: " -+ "%s)\n", is_old_mk ? "OLD" : "CURRENT", -+ printable_mkvp(get_card_type_for_keytype(key_type), -+ mkvp)); - } else { - printf(" Clear key size: (unknown)\n"); -- printf(" Enciphered with: (unknown, MKVP: %016llx)\n", -- mkvp); -+ printf(" Enciphered with: (unknown, MKVP: %s)\n", -+ printable_mkvp(get_card_type_for_keytype(key_type), -+ mkvp)); - } - if (vp_tok_avail) - print_verification_pattern(vp_tok.verification_pattern); -@@ -2029,10 +2002,10 @@ static int command_validate(void) - - if (is_old_mk) - util_print_indented("\nWARNING: The secure volume key is " -- "currently enciphered with the OLD CCA " -+ "currently enciphered with the OLD " - "master key. To mitigate the danger of " - "data loss re-encipher the volume key with " -- "the CURRENT CCA master key.", 0); -+ "the CURRENT master key.", 0); - - if (is_valid && !vp_tok_avail) { - util_asprintf(&msg, "\nWARNING: The volume key cannot be " -@@ -2148,14 +2121,14 @@ static int command_setkey(void) - - if (is_old_mk) { - util_asprintf(&msg, "The secure key in file '%s' is " -- "enciphered with the CCA master key in the OLD " -+ "enciphered with the master key in the OLD " - "master key register. Do you want to set this " - "key as the new volume key anyway [y/N]?", - g.master_key_file); - util_print_indented(msg, 0); - free(msg); - -- if (!prompt_for_yes()) { -+ if (!_prompt_for_yes()) { - warnx("Device '%s' is left unchanged", g.pos_arg); - rc = -EINVAL; - goto out; -@@ -2213,7 +2186,7 @@ static int command_setkey(void) - util_print_indented(msg, 0); - free(msg); - -- if (!prompt_for_yes()) { -+ if (!_prompt_for_yes()) { - warnx("Device '%s' is left unchanged", g.pos_arg); - rc = -EINVAL; - goto out; -@@ -2429,6 +2402,13 @@ int main(int argc, char *argv[]) - goto out; - } - } -+ if (command->need_ep11_library) { -+ rc = load_ep11_library(&g.ep11, g.verbose); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ } - if (command->need_pkey_device) { - g.pkey_fd = open_pkey_device(g.verbose); - if (g.pkey_fd == -1) { -diff --git a/zkey/zkey.1 b/zkey/zkey.1 -index 31fff43..6326f19 100644 ---- a/zkey/zkey.1 -+++ b/zkey/zkey.1 -@@ -24,9 +24,9 @@ zkey \- Manage secure AES keys - . - .SH DESCRIPTION - Use the \fBzkey\fP tool to generate and manage secure AES keys that are --enciphered with a master key of an IBM cryptographic adapter in CCA coprocessor --mode. You can also use the \fBzkey\fP tool to validate and re-encipher secure --AES keys. -+enciphered with a master key of an IBM cryptographic adapter in CCA or EP11 -+coprocessor mode, dependent on the key type. You can also use the \fBzkey\fP -+tool to validate and re-encipher secure AES keys. - .PP - The secure keys can either be stored in a file in the file system, or - in the secure key repository. The default location of the secure key repository -@@ -43,7 +43,7 @@ group \fBzkeyadm\fP. - When storing the secure key in a key repository, additional information, such as - a textual description of the key, can be associated with a secure key. - You can associate a secure key with one or multiple cryptographic adapters --(APQNs) that are set up with the same CCA master key. -+(APQNs) that are set up with the same CCA or EP11 master key. - You can also associate a secure key with one or multiple volumes - (block devices), which are encrypted using dm-crypt with the secure key. The - volume association also contains the device-mapper name, separated by a colon, -@@ -52,7 +52,7 @@ key. - .PP - The generated secure key is saved in a file with a size of 64 or 128 bytes. - The file contains an AES key with a length of 128, 192, or 256 bits. The key is --enciphered with the master key of the CCA cryptographic adapter. -+enciphered with the master key of the CCA or EP11 cryptographic adapter. - Secure keys that are used for the XTS cipher mode can be 128 or 256 bits - in size. - . -@@ -111,13 +111,13 @@ key repository. - .PP - Use the - .B generate --command to generate a new secure AES key either randomly within the CCA -+command to generate a new secure AES key either randomly within the CCA or EP11 - cryptographic adapter, or from a clear AES key specified as input. When specifying - a clear key as input, the clear key should be kept in a secure place, or be - securely erased after creation of the secure key. The secure key itself does - not need to be kept secure, because it can only be used together with a --CCA cryptographic adapter that contains the master key with which the secure --key was generated. -+CCA or EP11 cryptographic adapter that contains the master key with which the -+secure key was generated. - .PP - The generated secure key can either be stored in a file in the file system, - or in the secure key repository. To store the generated secure key in a -@@ -135,14 +135,17 @@ additional information can be associated with a secure key using the - .B \-\-sector-size - options. - .PP --You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, and --\fBCCA-AESCIPHER\fP keys. Specify the type of the secure key using the -+You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, -+\fBCCA-AESCIPHER\fP, and \fBEP11-AES\fP keys. -+Specify the type of the secure key using the - .B \-\-key\-type - option. The default key type is CCA-AESDATA. - .PP - .B Note: - Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic - adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. -+Secure keys of type \fBEP11-AES\fP require an IBM cryptographic -+adapter in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P. - . - .SS "Validating secure AES keys" - . -@@ -223,7 +226,7 @@ are validated. - Use the - .B reencipher - command to re-encipher an existing secure key with a new master key. --A secure key must be re-enciphered when the master key of the CCA -+A secure key must be re-enciphered when the master key of the CCA or EP11 - cryptographic adapter changes. - .PP - The CCA cryptographic adapter has three different registers to store -@@ -246,11 +249,15 @@ the current master key. You can pro-actively re-encipher a secure key with the - option to do this. - .RE - .PP -+\fBNote:\fP An EP11 cryptographic adapter has only two registers to store master -+keys, \fBCURRENT\fP and \fBNEW\fP. -+.PP - Use the - .B \-\-from\-old - option to re-encipher a secure key that is currently enciphered with - the master key in the \fBOLD\fP register with the master key in the --\fBCURRENT\fP register. -+\fBCURRENT\fP register. This option is only available for secure keys of type -+\fBCCA-AESDATA\fP or \fBCCA-AESCIPHER\fP. - .PP - .PP - If both the -@@ -301,19 +308,23 @@ the re-enciphered secure key. Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is - performed in-place per default. You can use option \fB\-\-in-place\fP to force an - in-place re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case. Be aware that - a secure key that was re-enciphered in-place from \fBCURRENT\fP to \fBNEW\fP --is no longer valid, until the new CCA master key has been made the current one. -+is no longer valid, until the new CCA or EP11 master key has been made the -+current one. - .PP - \fBStaged\fP mode means that the re-enciphered secure key is stored in a - separate file in the secure key repository. Thus the current secure key is still --valid at this point. Once the new CCA master key has been set (made active), you --must rerun the reencipher command with option \fB\-\-complete\fP to complete the --staged re-enciphering. Re-enciphering from \fBCURRENT\fP to \fBNEW\fP is --performed in staged mode per default. You can use option \fB\-\-staged\fP to force --a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case. -+valid at this point. Once the new CCA or EP11 master key has been set (made -+active), you must rerun the reencipher command with option \fB\-\-complete\fP -+to complete the staged re-enciphering. Re-enciphering from \fBCURRENT\fP to -+\fBNEW\fP is performed in staged mode per default. You can use option -+\fB\-\-staged\fP to force 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. For the supported environments and downloads, see: -+The \fBreencipher\fP command requires the CCA host library (libcsulcca.so, for) -+for secure keys of type CCA-AESDATA or CCA-AESCIPHER, or the IBM Z Enterprise -+PKCS #11 (EP11) Support Program (EP11 host library) for secure keys of type -+EP11-AES 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" -@@ -490,8 +501,8 @@ associations with one command. - .B Note: - The secure key itself cannot be changed, only information about the secure - key is changed. To rename a secure key, use the \fBrename\fP command. --To re-encipher a secure key with a new CCA master key, use the \fBreencipher\fP --command. -+To re-encipher a secure key with a new CCA or EP11 master key, use the -+\fBreencipher\fP command. - . - .SS "Rename existing AES secure keys in the secure key repository" - . -@@ -788,7 +799,7 @@ A specific volume can only be associated with a single secure key. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Each APQN association specifies a card and domain number separated - by a period (like lszcrypt displays it). When at least one APQN is specified, -@@ -818,11 +829,13 @@ the default volume type is \fBplain\fP. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-K ", " \-\-key-type\~\fItype\fP --Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP --and \fBCCA-AESCIPHER\fP. If this option is omitted, then a secure key of type --CCA-AESDATA is generated. Secure keys of type \fBCCA-AESCIPHER\fP require an --IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g. --a CEX6C. -+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP, -+\fBCCA-AESCIPHER\fP, and \fBEP11-AES\fP. If this option is omitted, then a -+secure key of type CCA-AESDATA is generated. -+Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic adapter -+in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. -+Secure keys of type \fBEP11-AES\fP require an IBM cryptographic adapter -+in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P. - . - . - . -@@ -835,7 +848,7 @@ When wildcards are used you must quote the value. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQNs). You can use wildcards in the APQN specification. - All secure keys contained in the secure key repository - which are associated with the specified APQNs are validated. -@@ -858,6 +871,8 @@ master key in the CURRENT register with the master key in the NEW register. - .BR \-o ", " \-\-from\-old - Re-enciphers a secure AES key that is currently enciphered with the - master key in the OLD register with the master key in the CURRENT register. -+This option is only available for secure keys of type CCA-AESDATA and -+CCA-AESCIPHER. - .TP - .BR \-f ", " \-\-output\~\fIoutput\-file\fP - Specifies the name of the output file to which the re-enciphered secure key -@@ -873,7 +888,7 @@ When wildcards are used you must quote the value. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQNs). You can use wildcards in the APQN specification. - All secure keys contained in the secure key repository - which are associated with the specified APQNs are re-enciphered. -@@ -892,16 +907,16 @@ This option is only used for secure keys contained in the secure key repository. - Forces that the re-enciphering of a secure AES key contained in the secure key - repository is performed in staged mode. Staged mode means that the re-enciphered - secure key is stored in a separate file in the secure key repository. Thus the --current secure key is still valid at this point. Once the new CCA master key has --been set (made active), you must rerun the reencipher command with option --\fB\-\-complete\fP to complete the staged re-enciphering. -+current secure key is still valid at this point. Once the new CCA or EP11 master -+key has been set (made active), you must rerun the reencipher command with -+option \fB\-\-complete\fP to complete the staged re-enciphering. - Re-enciphering from CURRENT to NEW is performed in staged mode per default. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-p ", " \-\-complete --Completes a staged re-enciphering. Use this option after the new CCA master key --has been set (made active). This option replaces the secure key by its --re-enciphered version in the secure key repository. -+Completes a staged re-enciphering. Use this option after the new CCA or EP11 -+master key has been set (made active). This option replaces the secure key by -+its re-enciphered version in the secure key repository. - This option is only used for secure keys contained in the secure key repository. - . - . -@@ -926,7 +941,7 @@ A specific volume can only be associated with a single secure key. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Each APQN association specifies a card and domain number separated - by a period (like lszcrypt displays it). All specified APQNs must be online, -@@ -986,7 +1001,7 @@ When wildcards are used you must quote the value. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Only those keys are listed, which are associated with the specified - APQNs. Each APQN association specifies a card and domain number separated -@@ -1004,8 +1019,9 @@ has been compiled with LUKS2 support enabled. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-K ", " \-\-key-type\~\fItype\fP --Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP --and \fBCCA-AESCIPHER\fP. Only keys with the specified key type are listed. -+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP, -+\fBCCA-AESCIPHER\fP, and \fBEP11-AES\fP. Only keys with the specified key type -+are listed. - This option is only used for secure keys contained in the secure key repository. - . - . -@@ -1050,7 +1066,7 @@ A specific volume can only be associated with a single secure key. - This option is only used for secure keys contained in the secure key repository. - .TP - .BR \-a ", " \-\-apqns\~\fI[+|-]card1.domain1[,card2.domain2[,...]]\fP --Specifies a comma-separated list of cryptographic adapters in CCA -+Specifies a comma-separated list of cryptographic adapters in CCA or EP11 - coprocessor mode (APQN) which are associated with the secure AES key in the - repository. Each APQN association specifies a card and domain number separated - by a period (like lszcrypt displays it). -diff --git a/zkey/zkey.c b/zkey/zkey.c -index 3ae9a9b..e6356c0 100644 ---- a/zkey/zkey.c -+++ b/zkey/zkey.c -@@ -28,6 +28,7 @@ - #include "lib/zt_common.h" - - #include "cca.h" -+#include "ep11.h" - #include "keystore.h" - #include "misc.h" - #include "pkey.h" -@@ -83,12 +84,16 @@ static struct zkey_globals { - bool force; - bool open; - bool format; -+ struct ext_lib lib; - struct cca_lib cca; -+ struct ep11_lib ep11; - int pkey_fd; - struct keystore *keystore; - } g = { - .pkey_fd = -1, - .sector_size = -1, -+ .lib.cca = &g.cca, -+ .lib.ep11 = &g.ep11, - }; - - /* -@@ -164,7 +169,7 @@ static struct util_opt opt_vec[] = { - .desc = "Name of the secure AES key in the repository. If " - "option --name/-N is specified, then the generated " - "secure AES key is stored in the repository. Parameter " -- "SECURE-KEY-FILE is not used when option --name/-M is " -+ "SECURE-KEY-FILE is not used when option --name/-N is " - "specified", - .command = COMMAND_GENERATE, - }, -@@ -222,9 +227,9 @@ static struct util_opt opt_vec[] = { - .option = { "key-type", required_argument, NULL, 'K'}, - .argument = "type", - .desc = "The type of the key. Possible values are '" -- KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " -- "When this option is omitted, the default is '" -- KEY_TYPE_CCA_AESDATA"'", -+ KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' " -+ "and '"KEY_TYPE_EP11_AES"'. When this option is " -+ "omitted, the default is '"KEY_TYPE_CCA_AESDATA"'", - .command = COMMAND_GENERATE, - }, - /***********************************************************/ -@@ -259,7 +264,7 @@ static struct util_opt opt_vec[] = { - { - .option = {"complete", 0, NULL, 'p'}, - .desc = "Completes a staged re-enciphering. Use this option " -- "after the new CCA master key has been set (made " -+ "after the new master key has been set (made " - "active)", - .command = COMMAND_REENCIPHER, - }, -@@ -447,9 +452,9 @@ static struct util_opt opt_vec[] = { - .option = { "key-type", required_argument, NULL, 'K'}, - .argument = "type", - .desc = "The type of the key. Possible values are '" -- KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. " -- "Use this option to list all keys with the specified " -- "key type.", -+ KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' " -+ "and '"KEY_TYPE_EP11_AES"'. Use this option to list " -+ "all keys with the specified key type.", - .command = COMMAND_LIST, - }, - /***********************************************************/ -@@ -822,6 +827,7 @@ struct zkey_command { - unsigned int abbrev_len; - int (*function)(void); - int need_cca_library; -+ int need_ep11_library; - int need_pkey_device; - char *short_desc; - char *long_desc; -@@ -868,13 +874,13 @@ static struct zkey_command zkey_commands[] = { - .command = COMMAND_REENCIPHER, - .abbrev_len = 2, - .function = command_reencipher, -- .need_cca_library = 1, -+ /* Will load the CCA or EP11 library on demand */ - .need_pkey_device = 1, - .short_desc = "Re-encipher an existing secure AES key", - .long_desc = "Re-encipher an existing secure AES " - "key that is either contained in SECURE-KEY-FILE " - "or is stored in the repository with another " -- "CCA master key", -+ "master key", - .has_options = 1, - .pos_arg = "[SECURE-KEY-FILE]", - .pos_arg_optional = 1, -@@ -1171,8 +1177,10 @@ static int command_generate(void) - return EXIT_FAILURE; - } - -- rc = cross_check_apqns(NULL, 0, -+ rc = cross_check_apqns(NULL, NULL, - get_min_card_level_for_keytype(g.key_type), -+ get_min_fw_version_for_keytype(g.key_type), -+ get_card_type_for_keytype(g.key_type), - true, g.verbose); - if (rc == -EINVAL) - return EXIT_FAILURE; -@@ -1198,9 +1206,8 @@ static int command_reencipher_file(void) - { - size_t secure_key_size; - int rc, is_old_mk; -- int selected = 1; - u8 *secure_key; -- u64 mkvp; -+ bool selected; - - if (g.name != NULL) { - warnx("Option '--name|-N' is not valid for " -@@ -1246,30 +1253,21 @@ static int command_reencipher_file(void) - goto out; - } - -- rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, g.verbose); -- if (rc != 0) { -- warnx("Failed to get the master key verification pattern: %s", -- strerror(-rc)); -- rc = EXIT_FAILURE; -- goto out; -- } -- - if (!g.fromold && !g.tonew) { - /* Autodetect reencipher option */ - if (is_old_mk) { - g.fromold = 1; - util_print_indented("The secure key is currently " -- "enciphered with the OLD CCA " -+ "enciphered with the OLD " - "master key and is being " - "re-enciphered with the CURRENT " -- "CCA master key\n", 0); -+ "master key\n", 0); - } else { - g.tonew = 1; - util_print_indented("The secure key is currently " -- "enciphered with the CURRENT CCA " -+ "enciphered with the CURRENT " - "master key and is being " -- "re-enciphered with the NEW CCA " -+ "re-enciphered with the NEW " - "master key\n", 0); - } - } -@@ -1278,68 +1276,56 @@ static int command_reencipher_file(void) - if (g.fromold) { - if (!is_old_mk) { - warnx("The secure key is already enciphered " -- "with the CURRENT CCA master key"); -+ "with the CURRENT master key"); - rc = EXIT_FAILURE; - goto out; - } - - pr_verbose("Secure key will be re-enciphered from OLD to the " -- "CURRENT CCA master key"); -- -- rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -- FLAG_SEL_CCA_MATCH_OLD_MKVP, -- g.verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -- if (rc != 0) { -- warnx("No APQN found that is suitable for " -- "re-enciphering the secure AES volume key"); -- rc = EXIT_FAILURE; -- goto out; -- } -+ "CURRENT master key"); - -- rc = key_token_change(&g.cca, secure_key, secure_key_size, -- METHOD_OLD_TO_CURRENT, -- g.verbose); -+ rc = reencipher_secure_key(&g.lib, secure_key, secure_key_size, -+ NULL, REENCIPHER_OLD_TO_CURRENT, -+ &selected, g.verbose); - if (rc != 0) { -- warnx("Re-encipher from OLD to CURRENT CCA " -- "master key has failed\n"); -- if (!selected) -- print_msg_for_cca_envvars("secure AES key"); -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume " -+ "key"); -+ } else { -+ warnx("Re-encipher from OLD to CURRENT " -+ "master key has failed\n"); -+ if (!selected && -+ !is_ep11_aes_key(secure_key, -+ secure_key_size)) -+ print_msg_for_cca_envvars( -+ "secure AES key"); -+ } - rc = EXIT_FAILURE; - goto out; - } - } - if (g.tonew) { - pr_verbose("Secure key will be re-enciphered from CURRENT " -- "to the NEW CCA master key"); -- -- rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, -- FLAG_SEL_CCA_MATCH_CUR_MKVP | -- FLAG_SEL_CCA_NEW_MUST_BE_SET, -- g.verbose); -- if (rc == -ENOTSUP) { -- rc = 0; -- selected = 0; -- } -- if (rc != 0) { -- util_print_indented("No APQN found that is suitable " -- "for re-enciphering this secure " -- "AES key and has the NEW master " -- "key loaded", 0); -- rc = EXIT_FAILURE; -- goto out; -- } -+ "to the NEW master key"); - -- rc = key_token_change(&g.cca, secure_key, secure_key_size, -- METHOD_CURRENT_TO_NEW, g.verbose); -+ rc = reencipher_secure_key(&g.lib, secure_key, secure_key_size, -+ NULL, REENCIPHER_CURRENT_TO_NEW, -+ &selected, g.verbose); - if (rc != 0) { -- warnx("Re-encipher from CURRENT to NEW CCA " -- "master key has failed\n"); -- if (!selected) -- print_msg_for_cca_envvars("secure AES key"); -+ if (rc == -ENODEV) { -+ warnx("No APQN found that is suitable for " -+ "re-enciphering the secure AES volume " -+ "key and has the NEW master key loaded"); -+ } else { -+ warnx("Re-encipher from CURRENT to NEW " -+ "master key has failed\n"); -+ if (!selected && -+ !is_ep11_aes_key(secure_key, -+ secure_key_size)) -+ print_msg_for_cca_envvars( -+ "secure AES key"); -+ } - rc = EXIT_FAILURE; - goto out; - } -@@ -1360,7 +1346,7 @@ out: - /* - * Command handler for 'reencipher in repository'. - * -- * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. -+ * Re-encipher the specified secure key with the NEW or CURRENT master key. - */ - static int command_reencipher_repository(void) - { -@@ -1395,7 +1381,7 @@ static int command_reencipher_repository(void) - - rc = keystore_reencipher_key(g.keystore, g.name, g.apqns, g.fromold, - g.tonew, g.inplace, g.staged, g.complete, -- g.pkey_fd, &g.cca); -+ g.pkey_fd, &g.lib); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1403,7 +1389,7 @@ static int command_reencipher_repository(void) - /* - * Command handler for 'reencipher'. - * -- * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. -+ * Re-encipher the specified secure key with the NEW or CURRENT master key. - */ - static int command_reencipher(void) - { -@@ -1425,9 +1411,10 @@ static int command_validate_file(void) - char vp[VERIFICATION_PATTERN_LEN]; - size_t secure_key_size; - size_t clear_key_size; -+ const char *key_type; -+ u8 mkvp[MKVP_LENGTH]; - u8 *secure_key; - int is_old_mk; -- u64 mkvp; - int rc; - - if (g.name != NULL) { -@@ -1475,7 +1462,7 @@ static int command_validate_file(void) - } - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, g.verbose); -+ mkvp, g.verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); -@@ -1483,24 +1470,27 @@ static int command_validate_file(void) - goto out; - } - -+ key_type = get_key_type(secure_key, secure_key_size); -+ - printf("Validation of secure key in file '%s':\n", g.pos_arg); - printf(" Status: Valid\n"); - printf(" Secure key size: %lu bytes\n", secure_key_size); -- printf(" Key type: %s\n", -- get_key_type(secure_key, secure_key_size)); -+ printf(" Key type: %s\n", key_type); - printf(" Clear key size: %lu bits\n", clear_key_size); - printf(" XTS type key: %s\n", - is_xts_key(secure_key, secure_key_size) ? "Yes" : "No"); -- printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n", -- is_old_mk ? "OLD" : "CURRENT", mkvp); -+ printf(" Enciphered with: %s master key (MKVP: %s)\n", -+ is_old_mk ? "OLD" : "CURRENT", -+ printable_mkvp(get_card_type_for_keytype(key_type), mkvp)); - printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, - vp); - printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, - &vp[VERIFICATION_PATTERN_LEN / 2]); - - rc = cross_check_apqns(NULL, mkvp, -- get_min_card_level_for_keytype( -- get_key_type(secure_key, secure_key_size)), -+ get_min_card_level_for_keytype(key_type), -+ get_min_fw_version_for_keytype(key_type), -+ get_card_type_for_keytype(key_type), - true, g.verbose); - if (rc == -EINVAL) - return EXIT_FAILURE; -@@ -1571,7 +1561,7 @@ static int command_import(void) - - rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, - g.apqns, g.noapqncheck, g.sector_size, -- g.pos_arg, g.volume_type, &g.cca); -+ g.pos_arg, g.volume_type, &g.lib); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -1750,11 +1740,11 @@ static int command_convert_file(void) - u8 output_key[2 * MAX_SECURE_KEY_SIZE]; - unsigned int output_key_size; - size_t secure_key_size; -+ u8 mkvp[MKVP_LENGTH]; - int rc, is_old_mk; - int selected = 1; - u8 *secure_key; - int min_level; -- u64 mkvp; - - if (g.name != NULL) { - warnx("Option '--name|-N' is not valid for " -@@ -1775,7 +1765,10 @@ static int command_convert_file(void) - return EXIT_FAILURE; - } - -- rc = cross_check_apqns(NULL, 0, min_level, true, g.verbose); -+ rc = cross_check_apqns(NULL, NULL, min_level, -+ get_min_fw_version_for_keytype(g.key_type), -+ get_card_type_for_keytype(g.key_type), -+ true, g.verbose); - if (rc == -EINVAL) - return EXIT_FAILURE; - if (rc != 0 && rc != -ENOTSUP) { -@@ -1797,7 +1790,7 @@ static int command_convert_file(void) - } - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, -- &mkvp, g.verbose); -+ mkvp, g.verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); -@@ -1910,7 +1903,7 @@ static int command_convert_repository(void) - } - - rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck, -- g.force, g.pkey_fd, &g.cca); -+ g.force, g.pkey_fd, &g.lib); - - return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -@@ -2225,6 +2218,13 @@ int main(int argc, char *argv[]) - goto out; - } - } -+ if (command->need_ep11_library) { -+ rc = load_ep11_library(&g.ep11, g.verbose); -+ if (rc != 0) { -+ rc = EXIT_FAILURE; -+ goto out; -+ } -+ } - if (command->need_pkey_device) { - g.pkey_fd = open_pkey_device(g.verbose); - if (g.pkey_fd == -1) { -@@ -2240,6 +2240,8 @@ int main(int argc, char *argv[]) - out: - if (g.cca.lib_csulcca) - dlclose(g.cca.lib_csulcca); -+ if (g.ep11.lib_ep11) -+ dlclose(g.ep11.lib_ep11); - if (g.pkey_fd >= 0) - close(g.pkey_fd); - if (g.keystore) --- -2.21.3 - - -From 03becee6f57d9ec59a593efe388de81fb5b72643 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 28 May 2020 12:13:27 +0200 -Subject: [PATCH 56/57] zdev: report FC Endpoint Security of zfcp device - (#1723843) - -Summary: zdev: report FC Endpoint Security of zfcp devices -Description: Report FC Endpoint Security of zfcp devices, which - includes: - 1. Add support to report Fibre Channel (FC) - Endpoint Security related information for zfcp-host - and zfcp-lun devices. - 2. Format and print HBA FC Endpoint Security trace - records -Upstream-ID: c063273b145c1f182968f318c04d3d8ea4dc0d9f -Upstream-ID: 67496afe577d9bcf74d1100ac5216e05343930c8 -Upstream-ID: 16b2799150981e9fc134573957d156d560f6d758 -Upstream-ID: fbf8513d43656fddeecd844bbcdf468dd3cbd4cb ---- - scripts/zfcpdbf | 44 +++++++++++++++++++++++++++++++++++++++- - zdev/include/attrib.h | 4 +++- - zdev/src/device.c | 9 +++++++- - zdev/src/export.c | 4 +++- - zdev/src/table_attribs.c | 14 ++++++++----- - zdev/src/zfcp_host.c | 22 ++++++++++++++++++++ - zdev/src/zfcp_lun.c | 34 +++++++++++++++++++++++++++---- - 7 files changed, 118 insertions(+), 13 deletions(-) - -diff --git a/scripts/zfcpdbf b/scripts/zfcpdbf -index 1fb2fe3..7eca30d 100755 ---- a/scripts/zfcpdbf -+++ b/scripts/zfcpdbf -@@ -2,7 +2,7 @@ - # - # zfcpdbf - Tool to interpret the information from logging/tracing sources - # --# Copyright IBM Corp. 2010, 2017 -+# Copyright IBM Corp. 2010, 2020 - # - # s390-tools is free software; you can redistribute it and/or modify - # it under the terms of the MIT license. See LICENSE for details. -@@ -640,6 +640,47 @@ sub _print_hba_id3 - } - } - -+sub _print_hba_id5 -+{ -+ my $fsf_req_id = shift(); -+ my $rec = shift(); -+ my $payload_length = shift(); -+ my $rec_received = shift(); -+ my $rec_issued; -+ -+ $rec_issued = stck_to_timeval(substr($rec, 0, 16)); -+ -+ if (defined $timediff) { # do we want to see delayed responses ? -+ my @t_arr = split(/[-:]/, $rec_received); -+ -+ my $ts1 = mktime($t_arr[5], $t_arr[4], $t_arr[3], -+ $t_arr[2], $t_arr[1] - 1, $t_arr[0] - 1900); -+ -+ my ($ts2, $us2) = split(/:/, $rec_issued); -+ -+ my $ts_received = $ts1 . "." . $t_arr[6]; -+ my $ts_issued = $ts2 . "." . $us2; -+ -+ if (($ts_received - $ts_issued) >= $timediff) { -+ print "WARNING: delayed response above ", -+ "skip level of $timediff seconds.\n"; -+ } -+ } -+ -+ print "FSF issued : ",str_timestamp($rec_issued),"\n"; -+ print "FSF stat : 0x", substr($rec, 16, 8), "\n"; -+ print "FSF stat qual : n/a\n"; -+ print "Prot stat : n/a\n"; -+ print "Prot stat qual : n/a\n"; -+ -+ print "Port handle : 0x", substr($rec, 24, 8), "\n"; -+ print "LUN handle : n/a\n"; -+ -+ print "WWPN : 0x", substr($rec, 32, 16), "\n"; -+ print "FCES old : 0x", substr($rec, 48, 8), "\n"; -+ print "FCES new : 0x", substr($rec, 56, 8), "\n"; -+} -+ - sub print_deferr_common - { - my $rec = shift(); -@@ -842,6 +883,7 @@ sub assign_callback_subs - $print_hba_id[1] = \&_print_hba_id1; - $print_hba_id[2] = \&_print_hba_id2; - $print_hba_id[3] = \&_print_hba_id3; -+ $print_hba_id[5] = \&_print_hba_id5; - - $print_rec_id[1] = \&_print_rec_id1; - $print_rec_id[2] = \&_print_rec_id2; -diff --git a/zdev/include/attrib.h b/zdev/include/attrib.h -index 052d4ce..24910ef 100644 ---- a/zdev/include/attrib.h -+++ b/zdev/include/attrib.h -@@ -1,7 +1,7 @@ - /* - * zdev - Modify and display the persistent configuration of devices - * -- * Copyright IBM Corp. 2016, 2017 -+ * Copyright IBM Corp. 2016, 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. -@@ -181,6 +181,7 @@ struct value_map { - * @activeonly: This attribute should only be changed in the active config - * @unstable: The value read is not the last value written - * @writeonly: This attribute cannot be read from -+ * @readonly: This attribute cannot be written to - * @rewrite: Writing the same value multiple times has side-effects - * @mandatory: This attribute cannot be removed from a configured device - * @newline: There must be a newline when writing to this attribute -@@ -211,6 +212,7 @@ struct attrib { - unsigned int activeonly :1; - unsigned int unstable :1; - unsigned int writeonly :1; -+ unsigned int readonly :1; - unsigned int rewrite :1; - unsigned int mandatory :1; - unsigned int newline :1; -diff --git a/zdev/src/device.c b/zdev/src/device.c -index ea15abc..1b1ff76 100644 ---- a/zdev/src/device.c -+++ b/zdev/src/device.c -@@ -214,6 +214,9 @@ static exit_code_t apply_setting(struct device *dev, config_t config, - /* Check for known attribute. */ - a = subtype_find_dev_attrib(st, key); - if (a) { -+ /* Check for read-only attribute. */ -+ if (a->readonly) -+ goto err_readonly; - /* Check for acceptable value of known attribute. */ - if (!force && !attrib_check_value(a, value)) - goto err_invalid_forceable; -@@ -306,6 +309,10 @@ err_int_noactive: - delayed_err("Internal attribute '%s' cannot be set in the active config\n", - key); - return EXIT_INVALID_SETTING; -+ -+err_readonly: -+ delayed_err("Cannot modify read-only attribute: %s\n", key); -+ return EXIT_INVALID_SETTING; - } - - /* Apply device settings from strlist to device. */ -@@ -520,7 +527,7 @@ void device_read_active_settings(struct device *dev, read_scope_t scope) - a = attrib_find(st->dev_attribs, name); - s = setting_list_apply_actual(dev->active.settings, a, name, - value); -- if (link || (scope == scope_all && -+ if ((a && a->readonly) || link || (scope == scope_all && - !util_path_is_writable(path))) - s->readonly = 1; - if (link) -diff --git a/zdev/src/export.c b/zdev/src/export.c -index a66db0b..e538c93 100644 ---- a/zdev/src/export.c -+++ b/zdev/src/export.c -@@ -1,7 +1,7 @@ - /* - * zdev - Modify and display the persistent configuration of devices - * -- * Copyright IBM Corp. 2016, 2017 -+ * Copyright IBM Corp. 2016, 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. -@@ -71,6 +71,8 @@ static bool is_exportable(struct setting *s, config_t config) - - if (!a) - return true; -+ if (a->readonly) -+ return false; - if (a->mandatory) - return true; - if (SCOPE_ACTIVE(config)) { -diff --git a/zdev/src/table_attribs.c b/zdev/src/table_attribs.c -index 50100c7..c6bb86a 100644 ---- a/zdev/src/table_attribs.c -+++ b/zdev/src/table_attribs.c -@@ -1,7 +1,7 @@ - /* - * zdev - Modify and display the persistent configuration of devices - * -- * Copyright IBM Corp. 2016, 2017 -+ * Copyright IBM Corp. 2016, 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. -@@ -186,13 +186,15 @@ static void table_attribs_show_details_one(struct table_attrib *t, - indent(j, "The default value is '%s'.\n", a->defval); - } - -- printf("\n"); -- indent(i, "ACCEPTED VALUES\n"); -- attrib_print_acceptable(a, j); -+ if (!a->readonly) { -+ printf("\n"); -+ indent(i, "ACCEPTED VALUES\n"); -+ attrib_print_acceptable(a, j); -+ } - - if (!(a->multi || a->activeonly || a->unstable || a->writeonly || - a->rewrite || a->mandatory || a->newline || a->activerem || -- a->nounload || a->check)) -+ a->nounload || a->check || a->readonly)) - return; - - printf("\n"); -@@ -211,6 +213,8 @@ static void table_attribs_show_details_one(struct table_attrib *t, - } - if (a->writeonly) - indent(j, "- You cannot read this attribute\n"); -+ if (a->readonly) -+ indent(j, "- You cannot write to this attribute\n"); - if (a->rewrite) { - indent(j, "- Setting the same value multiple times may have " - "additional effects\n"); -diff --git a/zdev/src/zfcp_host.c b/zdev/src/zfcp_host.c -index 729bc42..ba91f35 100644 ---- a/zdev/src/zfcp_host.c -+++ b/zdev/src/zfcp_host.c -@@ -70,6 +70,27 @@ static struct attrib zfcp_host_attr_port_rescan = { - .accept = ACCEPT_ARRAY(ACCEPT_NUM(1)), - }; - -+static struct attrib zfcp_host_attr_fc_security = { -+ .name = "fc_security", -+ .title = "Show FC Endpoint Security capability of FCP device", -+ .desc = -+ "This read-only attribute shows the Fibre Channel Endpoint Security\n" -+ "capabilities of the FCP device.\n" -+ "\n" -+ "Possible values are either one of the following:\n" -+ " unknown : The Fibre Channel Endpoint Security capabilities\n" -+ " of the FCP device are not known\n" -+ " unsupported : The FCP device does not support Fibre Channel\n" -+ " Endpoint Security\n" -+ " none : The FCP device does not report any Fibre Channel\n" -+ " Endpoint Security capabilities\n" -+ "\n" -+ "Or one or more comma-separated values:\n" -+ " Authentication: The FCP device supports authentication\n" -+ " Encryption : The FCP device supports encryption\n", -+ .readonly = 1, -+}; -+ - /* - * zfcp host methods. - */ -@@ -248,6 +269,7 @@ struct subtype zfcp_host_subtype = { - &zfcp_host_attr_failed, - &zfcp_host_attr_port_remove, - &zfcp_host_attr_port_rescan, -+ &zfcp_host_attr_fc_security, - &internal_attr_early, - ), - .unknown_dev_attribs = 1, -diff --git a/zdev/src/zfcp_lun.c b/zdev/src/zfcp_lun.c -index f16cbfd..6fb5637 100644 ---- a/zdev/src/zfcp_lun.c -+++ b/zdev/src/zfcp_lun.c -@@ -33,6 +33,7 @@ - #include "zfcp_lun.h" - - #define DEVNAME "zFCP LUN" -+#define FC_SECURITY_VATTR "fc_security" - - /* - * zfcp lun namespace. -@@ -413,6 +414,24 @@ static struct attrib zfcp_lun_attr_scsi_delete = { - .accept = ACCEPT_ARRAY(ACCEPT_NUM(1)), - }; - -+static struct attrib zfcp_lun_attr_fc_security = { -+ .name = FC_SECURITY_VATTR, -+ .title = "Show FC Endpoint Security state of connection", -+ .desc = -+ "This read-only attribute shows the current state of Fibre Channel\n" -+ "Endpoint Security of the connection between the FCP device and the\n" -+ "FC remote port used to access the LUN:\n" -+ " unknown : The Fibre Channel Endpoint Security state of the\n" -+ " connection is not known\n" -+ " unsupported : The FCP device does not support Fibre Channel\n" -+ " Endpoint Security\n" -+ " none : The connection has no Fibre Channel Endpoint\n" -+ " Security\n" -+ " Authentication: The connection has been authenticated\n" -+ " Encryption : The connection is encrypted\n", -+ .readonly = 1, -+}; -+ - /* - * zfcp lun device methods. - */ -@@ -759,10 +778,8 @@ static char *zfcp_lun_st_get_active_attrib_path(struct subtype *st, - char *hctl, *devpath, *path; - size_t len = strlen(SCSI_ATTR_PREFIX); - -- if (!starts_with(name, SCSI_ATTR_PREFIX) || -- !(name[len] == 0 || name[len] == '/' )) -- devpath = path_get_zfcp_lun_dev(dev->devid); -- else { -+ if (starts_with(name, SCSI_ATTR_PREFIX) && -+ (name[len] == 0 || name[len] == '/')) { - hctl = scsi_hctl_from_zfcp_lun_devid(dev->devid); - if (!hctl) - return NULL; -@@ -770,6 +787,14 @@ static char *zfcp_lun_st_get_active_attrib_path(struct subtype *st, - free(hctl); - - name += strlen(SCSI_ATTR_PREFIX); -+ } else if (strcmp(name, FC_SECURITY_VATTR) == 0) { -+ devpath = path_get_zfcp_port_dev(dev->devid); -+ if (!util_path_exists(devpath)) { -+ free(devpath); -+ return NULL; -+ } -+ } else { -+ devpath = path_get_zfcp_lun_dev(dev->devid); - } - - while (*name == '/') -@@ -1098,6 +1123,7 @@ struct subtype zfcp_lun_subtype = { - &zfcp_lun_attr_scsi_timeout, - &zfcp_lun_attr_scsi_state, - &zfcp_lun_attr_scsi_delete, -+ &zfcp_lun_attr_fc_security, - &internal_attr_early, - ), - .prefixes = STRING_ARRAY(SCSI_ATTR_PREFIX), --- -2.21.3 - - -From 7a4e27a9c5c705cf12b4e4f94177941dbad9cc30 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 17 Jun 2020 13:40:47 +0200 -Subject: [PATCH 57/57] zipl rebase: remove redundant files (#1849960) - -related #1821250 ---- - zipl/boot/s390.h | 449 ------------------------------------------- - zipl/boot/stage3.lds | 43 ----- - 2 files changed, 492 deletions(-) - delete mode 100644 zipl/boot/s390.h - delete mode 100644 zipl/boot/stage3.lds - -diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h -deleted file mode 100644 -index 886233b..0000000 ---- a/zipl/boot/s390.h -+++ /dev/null -@@ -1,449 +0,0 @@ --/* -- * zipl - zSeries Initial Program Loader tool -- * -- * System z specific functions -- * -- * Copyright IBM Corp. 2013, 2017 -- * -- * s390-tools is free software; you can redistribute it and/or modify -- * it under the terms of the MIT license. See LICENSE for details. -- */ --#ifndef S390_H --#define S390_H -- --#include "lib/zt_common.h" --#include "boot/sigp.h" -- -- --/* -- * Helper macro for exception table entries -- */ --#define EX_TABLE(_fault, _target) \ -- ".section .ex_table,\"a\"\n" \ -- ".align 4\n" \ -- ".long (" #_fault ")\n" \ -- ".long (" #_target ")\n" \ -- ".previous\n" -- --struct psw_t { -- uint64_t mask; -- 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 { -- uint8_t pad_0x0000[0x0014-0x0000]; /* 0x0000 */ -- uint32_t ipl_parmblock_ptr; /* 0x0014 */ -- uint8_t pad_0x0018[0x0080-0x0018]; /* 0x0018 */ -- uint32_t ext_params; /* 0x0080 */ -- uint16_t ext_cpu_addr; /* 0x0084 */ -- uint16_t ext_int_code; /* 0x0086 */ -- uint16_t svc_ilc; /* 0x0088 */ -- uint16_t svc_code; /* 0x008a */ -- uint16_t pgm_ilc; /* 0x008c */ -- uint16_t pgm_code; /* 0x008e */ -- uint32_t data_exc_code; /* 0x0090 */ -- uint16_t mon_class_num; /* 0x0094 */ -- uint16_t per_perc_atmid; /* 0x0096 */ -- uint64_t per_address; /* 0x0098 */ -- uint8_t exc_access_id; /* 0x00a0 */ -- uint8_t per_access_id; /* 0x00a1 */ -- uint8_t op_access_id; /* 0x00a2 */ -- uint8_t ar_access_id; /* 0x00a3 */ -- uint8_t pad_0x00a4[0x00a8-0x00a4]; /* 0x00a4 */ -- uint64_t trans_exc_code; /* 0x00a8 */ -- uint64_t monitor_code; /* 0x00b0 */ -- uint16_t subchannel_id; /* 0x00b8 */ -- uint16_t subchannel_nr; /* 0x00ba */ -- uint32_t io_int_parm; /* 0x00bc */ -- uint32_t io_int_word; /* 0x00c0 */ -- uint8_t pad_0x00c4[0x00c8-0x00c4]; /* 0x00c4 */ -- uint32_t stfl_fac_list; /* 0x00c8 */ -- uint8_t pad_0x00cc[0x00e8-0x00cc]; /* 0x00cc */ -- uint32_t mcck_interruption_code[2]; /* 0x00e8 */ -- uint8_t pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */ -- uint32_t external_damage_code; /* 0x00f4 */ -- uint64_t failing_storage_address; /* 0x00f8 */ -- uint8_t pad_0x0100[0x0110-0x0100]; /* 0x0100 */ -- uint64_t breaking_event_addr; /* 0x0110 */ -- uint8_t pad_0x0118[0x0120-0x0118]; /* 0x0118 */ -- struct psw_t restart_old_psw; /* 0x0120 */ -- struct psw_t external_old_psw; /* 0x0130 */ -- struct psw_t svc_old_psw; /* 0x0140 */ -- struct psw_t program_old_psw; /* 0x0150 */ -- struct psw_t mcck_old_psw; /* 0x0160 */ -- struct psw_t io_old_psw; /* 0x0170 */ -- uint8_t pad_0x0180[0x01a0-0x0180]; /* 0x0180 */ -- struct psw_t restart_psw; /* 0x01a0 */ -- struct psw_t external_new_psw; /* 0x01b0 */ -- struct psw_t svc_new_psw; /* 0x01c0 */ -- struct psw_t program_new_psw; /* 0x01d0 */ -- struct psw_t mcck_new_psw; /* 0x01e0 */ -- struct psw_t io_new_psw; /* 0x01f0 */ -- -- /* Save areas. */ -- uint64_t save_area_sync[8]; /* 0x0200 */ -- uint64_t save_area_async[8]; /* 0x0240 */ -- uint64_t save_area_restart[1]; /* 0x0280 */ -- uint8_t pad_0x0288[0x0290-0x0288]; /* 0x0288 */ -- -- /* Return psws. */ -- struct psw_t return_psw; /* 0x0290 */ -- struct psw_t return_mcck_psw; /* 0x02a0 */ -- -- /* CPU accounting and timing values. */ -- uint64_t sync_enter_timer; /* 0x02b0 */ -- uint64_t async_enter_timer; /* 0x02b8 */ -- uint64_t mcck_enter_timer; /* 0x02c0 */ -- uint64_t exit_timer; /* 0x02c8 */ -- uint64_t user_timer; /* 0x02d0 */ -- uint64_t system_timer; /* 0x02d8 */ -- uint64_t steal_timer; /* 0x02e0 */ -- uint64_t last_update_timer; /* 0x02e8 */ -- uint64_t last_update_clock; /* 0x02f0 */ -- uint64_t int_clock; /* 0x02f8 */ -- uint64_t mcck_clock; /* 0x0300 */ -- uint64_t clock_comparator; /* 0x0308 */ -- -- /* Current process. */ -- uint64_t current_task; /* 0x0310 */ -- uint64_t thread_info; /* 0x0318 */ -- uint64_t kernel_stack; /* 0x0320 */ -- -- /* Interrupt, panic and restart stack. */ -- uint64_t async_stack; /* 0x0328 */ -- uint64_t panic_stack; /* 0x0330 */ -- uint64_t restart_stack; /* 0x0338 */ -- -- /* Restart function and parameter. */ -- uint64_t restart_fn; /* 0x0340 */ -- uint64_t restart_data; /* 0x0348 */ -- uint64_t restart_source; /* 0x0350 */ -- -- /* Address space pointer. */ -- uint64_t kernel_asce; /* 0x0358 */ -- uint64_t user_asce; /* 0x0360 */ -- uint64_t current_pid; /* 0x0368 */ -- -- /* SMP info area */ -- uint32_t cpu_nr; /* 0x0370 */ -- uint32_t softirq_pending; /* 0x0374 */ -- uint64_t percpu_offset; /* 0x0378 */ -- uint64_t vdso_per_cpu_data; /* 0x0380 */ -- uint64_t machine_flags; /* 0x0388 */ -- uint64_t ftrace_func; /* 0x0390 */ -- uint64_t gmap; /* 0x0398 */ -- uint8_t pad_0x03a0[0x0400-0x03a0]; /* 0x03a0 */ -- -- /* Interrupt response block. */ -- uint8_t irb[64]; /* 0x0400 */ -- -- /* Per cpu primary space access list */ -- uint32_t paste[16]; /* 0x0440 */ -- -- uint8_t pad_0x0480[0x0e00-0x0480]; /* 0x0480 */ -- -- /* -- * 0xe00 contains the address of the IPL Parameter Information -- * block. Dump tools need IPIB for IPL after dump. -- * Note: do not change the position of any fields in 0x0e00-0x0f00 -- */ -- uint64_t ipib; /* 0x0e00 */ -- uint32_t ipib_checksum; /* 0x0e08 */ -- uint64_t vmcore_info; /* 0x0e0c */ -- uint8_t pad_0x0e14[0x0e18-0x0e14]; /* 0x0e14 */ -- uint64_t os_info; /* 0x0e18 */ -- uint8_t pad_0x0e20[0x0f00-0x0e20]; /* 0x0e20 */ -- -- /* Extended facility list */ -- uint64_t stfle_fac_list[32]; /* 0x0f00 */ -- uint8_t pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ -- uint64_t vector_save_area_addr; /* 0x11b0 */ -- /* 64 bit extparam used for pfault/diag 250: defined by architecture */ -- uint64_t ext_params2; /* 0x11B8 */ -- uint8_t pad_0x11c0[0x1200-0x11C0]; /* 0x11C0 */ -- -- /* CPU register save area: defined by architecture */ -- uint64_t floating_pt_save_area[16]; /* 0x1200 */ -- uint64_t gpregs_save_area[16]; /* 0x1280 */ -- struct psw_t psw_save_area; /* 0x1300 */ -- uint8_t pad_0x1310[0x1318-0x1310]; /* 0x1310 */ -- uint32_t prefixreg_save_area; /* 0x1318 */ -- uint32_t fpt_creg_save_area; /* 0x131c */ -- uint8_t pad_0x1320[0x1324-0x1320]; /* 0x1320 */ -- uint32_t tod_progreg_save_area; /* 0x1324 */ -- uint32_t cpu_timer_save_area[2]; /* 0x1328 */ -- uint32_t clock_comp_save_area[2]; /* 0x1330 */ -- uint8_t pad_0x1338[0x1340-0x1338]; /* 0x1338 */ -- uint32_t access_regs_save_area[16]; /* 0x1340 */ -- uint64_t cregs_save_area[16]; /* 0x1380 */ -- uint8_t pad_0x1400[0x1800-0x1400]; /* 0x1400 */ -- -- /* Transaction abort diagnostic block */ -- uint8_t pgm_tdb[256]; /* 0x1800 */ -- -- /* align to the top of the prefix area */ -- uint8_t pad_0x1900[0x2000-0x1900]; /* 0x1900 */ --} __packed __aligned(8192); -- --#define S390_lowcore (*((struct _lowcore *) 0)) -- --#define __LC_IPLDEV 0x0c6c --#define __LC_OS_INFO 0x0e18 -- --#define PAGE_SIZE 4096 -- --void panic_notify(unsigned long reason); -- --#define panic(reason, x...) \ --do { \ -- printf(x); \ -- panic_notify(reason); \ -- libc_stop(reason); \ --} while (0) -- --static __always_inline int page_is_valid(unsigned long addr) --{ -- unsigned long tmp; -- int rc; -- -- asm volatile( -- "0: ic %1,%2\n" -- "1: lhi %0,1\n" -- "2:\n" -- ".pushsection .fixup, \"ax\"\n" -- "3: xr %0,%0\n" -- " jg 2b\n" -- ".popsection\n" -- EX_TABLE(0b, 3b) EX_TABLE(1b, 3b) -- : "=d" (rc), "=d" (tmp) -- : "Q" (*(unsigned long *) addr) -- : "cc"); -- return rc; --} -- --static __always_inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) --{ -- register unsigned long reg2 asm("2") = (unsigned long) buf; -- register unsigned long reg3 asm("3") = (unsigned long) len; -- -- asm volatile( -- "0: cksm %0,%1\n" /* do checksum on longs */ -- " jo 0b\n" -- : "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory"); -- return sum; --} -- --#define __ctl_store(array, low, high) ({ \ -- typedef struct { char _[sizeof(array)]; } __may_alias addrtype;\ -- asm volatile( \ -- " stctg %1,%2,%0\n" \ -- : "=Q" (*(addrtype *)(&array)) \ -- : "i" (low), "i" (high)); \ --}) -- --#define __ctl_load(array, low, high) ({ \ -- typedef struct { char _[sizeof(array)]; } __may_alias addrtype; \ -- asm volatile( \ -- " lctlg %1,%2,%0\n" \ -- : : "Q" (*(addrtype *)(&array)), \ -- "i" (low), "i" (high)); \ --}) -- --static __always_inline void __ctl_set_bit(unsigned int cr, unsigned int bit) --{ -- unsigned long reg; -- -- __ctl_store(reg, cr, cr); -- reg |= 1UL << bit; -- __ctl_load(reg, cr, cr); --} -- --/* -- * DIAG 308 support -- */ --enum diag308_subcode { -- DIAG308_REL_HSA = 2, -- DIAG308_IPL = 3, -- DIAG308_DUMP = 4, -- DIAG308_SET = 5, -- DIAG308_STORE = 6, --}; -- --static __always_inline int diag308(unsigned long subcode, void *addr) --{ -- register unsigned long _addr asm("0") = (unsigned long) addr; -- register unsigned long _rc asm("1") = 0; -- -- asm volatile( -- " diag %0,%2,0x308\n" -- "0:\n" -- : "+d" (_addr), "+d" (_rc) -- : "d" (subcode) : "cc", "memory"); -- return _rc; --} -- --/* -- * Store CPU address -- */ --static __always_inline unsigned short stap(void) --{ -- unsigned short cpu_address; -- -- asm volatile("stap %0" : "=m" (cpu_address)); -- return cpu_address; --} -- --/* -- * Program the clock comparator -- */ --static __always_inline void set_clock_comparator(uint64_t time) --{ -- asm volatile("sckc %0" : : "Q" (time)); --} -- --/* -- * Program the CPU timer -- */ --static __always_inline void set_cpu_timer(uint64_t timer) --{ -- asm volatile("spt %0" : : "Q" (timer)); --} -- --/* -- * Get current time (store clock) -- */ --static __always_inline unsigned long long get_tod_clock(void) --{ -- unsigned long long clk; -- -- asm volatile("stck %0" : "=Q" (clk) : : "cc"); -- return clk; --} -- --/* -- * Get ID of current CPU -- */ --struct cpuid { -- unsigned int version:8; -- unsigned int ident:24; -- unsigned int machine:16; -- unsigned int unused:16; --} __packed __aligned(8); -- --static __always_inline void get_cpu_id(struct cpuid *ptr) --{ -- asm volatile("stidp %0" : "=Q" (*ptr)); --} -- --/* -- * Check if we run under z/VM -- */ --static __always_inline int is_zvm(void) --{ -- struct cpuid cpuid; -- -- get_cpu_id(&cpuid); -- return cpuid.version == 0xff; --} -- --/* -- * Vector register definition -- */ --typedef struct { -- uint32_t u[4]; --} __vector128; -- --/* -- * Save vector registers -- */ --static __always_inline void save_vx_regs(__vector128 *vxrs) --{ -- typedef struct { __vector128 _[32]; } addrtype; -- -- asm volatile( -- " la 1,%0\n" -- " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ -- " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ -- : "=Q" (*(addrtype *) vxrs) : : "1"); --} -- --/* -- * Save vector registers safe -- */ --static __always_inline void save_vx_regs_safe(__vector128 *vxrs) --{ -- unsigned long cr0; -- -- __ctl_store(cr0, 0, 0); -- __ctl_set_bit(0, 17); -- __ctl_set_bit(0, 18); /* AFP-register-control */ -- save_vx_regs(vxrs); -- __ctl_load(cr0, 0, 0); --} -- --#define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ -- --static __always_inline int __test_facility(unsigned long nr, void *facilities) --{ -- unsigned char *ptr; -- -- if (nr >= MAX_FACILITY_BIT) -- return 0; -- ptr = (unsigned char *) facilities + (nr >> 3); -- return (*ptr & (0x80 >> (nr & 7))) != 0; --} -- --/* -- * The test_facility function uses the bit odering where the MSB is bit 0. -- * That makes it easier to query facility bits with the bit number as -- * documented in the Principles of Operation. -- */ --static __always_inline int test_facility(unsigned long nr) --{ -- return __test_facility(nr, &S390_lowcore.stfle_fac_list); --} -- --static __always_inline unsigned long __stfle_asm(uint64_t *stfle_fac_list, int size) --{ -- register unsigned long reg0 asm("0") = size - 1; -- -- asm volatile( -- ".insn s,0xb2b00000,0(%1)" /* stfle */ -- : "+d" (reg0) -- : "a" (stfle_fac_list) -- : "memory", "cc"); -- return reg0; --} -- --/** -- * stfle - Store facility list extended -- * @stfle_fac_list: array where facility list can be stored -- * @size: size of passed in array in double words -- */ --static __always_inline void stfle(uint64_t *stfle_fac_list, int size) --{ -- unsigned long nr; -- -- asm volatile( -- " .insn s,0xb2b10000,0(0)\n" /* stfl */ -- "0:\n" -- EX_TABLE(0b, 0b) -- : "+m" (S390_lowcore.stfl_fac_list)); -- nr = 4; /* bytes stored by stfl */ -- memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); -- if (S390_lowcore.stfl_fac_list & 0x01000000) { -- /* More facility bits available with stfle */ -- nr = __stfle_asm(stfle_fac_list, size); -- nr = MIN((nr + 1) * 8, size * 8UL); -- } -- memset((char *) stfle_fac_list + nr, 0, size * 8 - nr); --} -- --#endif /* S390_H */ -diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds -deleted file mode 100644 -index a9f17fb..0000000 ---- a/zipl/boot/stage3.lds -+++ /dev/null -@@ -1,43 +0,0 @@ --/* -- * 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 -- * 0xf000-0xffff Stack -- */ -- --SECTIONS --{ -- . = 0x0; -- -- . = 0x2000; -- __heap_start = .; -- . = 0x6000; -- __heap_stop = .; -- -- -- /* stage 3 parameter */ -- . = 0x9000; -- _stage3_parms = .; -- -- . = 0xa000; -- .text.start : { *(.text.start) } -- .text : { *(.text) } -- __ex_table_start = .; -- .ex_table : { *(.ex_table) } -- __ex_table_stop = .; -- .eh_frame : { *(.eh_frame) } -- -- __bss_start = .; -- .bss : { *(.bss) } -- __bss_stop = .; -- .rodata : {*(.rodata) } -- .data : { *(.data) } --} --- -2.21.3 - diff --git a/SOURCES/s390-tools-zipl-bls-loadaddr.patch b/SOURCES/s390-tools-zipl-bls-loadaddr.patch deleted file mode 100644 index 032d84e..0000000 --- a/SOURCES/s390-tools-zipl-bls-loadaddr.patch +++ /dev/null @@ -1,146 +0,0 @@ -From ecf5a4ecb909bfd91306678d0c460ab2f2837a33 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 18 Nov 2019 04:10:06 -0500 -Subject: [PATCH 1/3] zipl: drop redundant string duplication -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Dan Horák ---- - zipl/src/scan.c | 9 +-------- - 1 file changed, 1 insertion(+), 8 deletions(-) - -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index 38fa5454..0ea37efa 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -1575,7 +1575,6 @@ scan_check_bls(struct scan_token *scan) - int i, rc; - char *target_value = NULL; - char *img_value = NULL; -- char *buffer = NULL; - /* - * In the BLS case, each BLS section heading inherits a keyword - * assignment target= from zipl.conf, and they are all the same. -@@ -1609,14 +1608,8 @@ scan_check_bls(struct scan_token *scan) - scan[i].content.keyword.value); - return rc; - } -- buffer = (char *) -- misc_malloc(strlen(img_value) + 1); -- if (buffer == NULL) -- return -1; -- memcpy(buffer, img_value, strlen(img_value)); -- buffer[strlen(img_value)] = 0; - free(scan[i].content.keyword.value); -- scan[i].content.keyword.value = buffer; -+ scan[i].content.keyword.value = img_value; - } - } - } - -From 05f83569960e2774e819fe0942da1f92d0cce35b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 18 Nov 2019 04:29:04 -0500 -Subject: [PATCH 2/3] zipl: set reason not text for failed check -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Dan Horák ---- - zipl/src/scan.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index 0ea37efa..0f01cac9 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -1603,7 +1603,7 @@ scan_check_bls(struct scan_token *scan) - scan[i].content.keyword.value); - rc = misc_check_readable_file(img_value); - if (rc) { -- error_text( -+ error_reason( - "Image file '%s' is not accessible", - scan[i].content.keyword.value); - return rc; - -From 8ab552b430f109d80966d0c56bed0d204d917d30 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 18 Nov 2019 11:45:50 -0500 -Subject: [PATCH 3/3] zipl: fix handling of values with load address in BLS -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Various keywords (like image or ramdisk) allow specifying a load address -as an optional argument. Adapt the logic for checking the presence of -the files to take this into the account. - -Fixes: https://github.com/ibm-s390-tools/s390-tools/commit/d71628326d80e623fc9f008fe4ea93edb5592b2e -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1772054 - -Signed-off-by: Dan Horák ---- - zipl/src/scan.c | 32 ++++++++++++++++++++++++++------ - 1 file changed, 26 insertions(+), 6 deletions(-) - -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index 0f01cac9..a34edf62 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -1575,6 +1575,8 @@ scan_check_bls(struct scan_token *scan) - int i, rc; - char *target_value = NULL; - char *img_value = NULL; -+ char *file = NULL; -+ char *tmp, *value; - /* - * In the BLS case, each BLS section heading inherits a keyword - * assignment target= from zipl.conf, and they are all the same. -@@ -1595,19 +1597,37 @@ scan_check_bls(struct scan_token *scan) - if (scan[i].content.keyword.keyword == scan_keyword_image || - scan[i].content.keyword.keyword == scan_keyword_ramdisk) { - -- rc = misc_check_readable_file( -- scan[i].content.keyword.value); -+ value = scan[i].content.keyword.value; -+ /* -+ * put the filename only into the file var before -+ * checking its presence -+ */ -+ if (contains_address(value)) { -+ tmp = strrchr(value, ','); -+ file = strndup(value, tmp - value); -+ } else { -+ file = value; -+ } -+ rc = misc_check_readable_file(file); - if (rc) { - misc_asprintf(&img_value, "%s%s", -- target_value, -- scan[i].content.keyword.value); -+ target_value, file); - rc = misc_check_readable_file(img_value); - if (rc) { - error_reason( -- "Image file '%s' is not accessible", -- scan[i].content.keyword.value); -+ "File '%s' not accessible", file); - return rc; - } -+ /* -+ * when file has stripped the load address part, -+ * do generate a prefixed value -+ */ -+ if (file != value) { -+ free(file); -+ free(img_value); -+ misc_asprintf(&img_value, "%s%s", -+ target_value, value); -+ } - free(scan[i].content.keyword.value); - scan[i].content.keyword.value = img_value; - } diff --git a/SOURCES/s390-tools-zipl-sort-like-rpm.patch b/SOURCES/s390-tools-zipl-blscfg-rpm-nvr-sort.patch similarity index 76% rename from SOURCES/s390-tools-zipl-sort-like-rpm.patch rename to SOURCES/s390-tools-zipl-blscfg-rpm-nvr-sort.patch index 4595255..bb8117d 100644 --- a/SOURCES/s390-tools-zipl-sort-like-rpm.patch +++ b/SOURCES/s390-tools-zipl-blscfg-rpm-nvr-sort.patch @@ -1,18 +1,18 @@ -From 8ec7b75204f3c7bf691e14b89c73c5dd28d2a824 Mon Sep 17 00:00:00 2001 -From: Peter Jones -Date: Mon, 15 Oct 2018 13:54:16 -0400 -Subject: [PATCH] blscfg: sort like rpm nvr, not like a single version - -Signed-off-by: Peter Jones ---- - zipl/src/scan.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++- - zipl/src/Makefile | 3 +- - 2 files changed, 96 insertions(+), 3 deletions(-) - -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index fe72e9ab13d..63186222783 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c +diff -up s390-tools-2.9.0/zipl/src/Makefile.blscfg-rpm-nvr-sort s390-tools-2.9.0/zipl/src/Makefile +--- s390-tools-2.9.0/zipl/src/Makefile.blscfg-rpm-nvr-sort 2019-05-22 08:16:17.317273801 -0400 ++++ s390-tools-2.9.0/zipl/src/Makefile 2019-05-22 08:18:02.947273801 -0400 +@@ -7,7 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \ + -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) + ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) + +-libs = $(rootdir)/libutil/libutil.a ++libs = $(rootdir)/libutil/libutil.a -lrpm + + objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ + install.o zipl.o $(rootdir)/zipl/boot/data.o +diff -up s390-tools-2.9.0/zipl/src/scan.c.blscfg-rpm-nvr-sort s390-tools-2.9.0/zipl/src/scan.c +--- s390-tools-2.9.0/zipl/src/scan.c.blscfg-rpm-nvr-sort 2019-05-21 09:13:36.000000000 -0400 ++++ s390-tools-2.9.0/zipl/src/scan.c 2019-05-22 08:16:17.317273801 -0400 @@ -33,6 +33,8 @@ #include "lib/util_base.h" @@ -22,7 +22,7 @@ index fe72e9ab13d..63186222783 100644 #include "boot.h" #include "error.h" #include "misc.h" -@@ -652,14 +654,104 @@ bls_filter(const struct dirent *ent) +@@ -653,13 +655,103 @@ bls_filter(const struct dirent *ent) return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0; } @@ -63,8 +63,9 @@ index fe72e9ab13d..63186222783 100644 + *release = NULL; + } +} -+ -+static int + + static int +-bls_sort(const struct dirent **ent_a, const struct dirent **ent_b) +split_cmp(char *nvr0, char *nvr1, int has_name) +{ + int ret = 0; @@ -95,7 +96,8 @@ index fe72e9ab13d..63186222783 100644 +/* 0: filename0 and filename1 are the same version */ +/* -1: filename1 is newer than filename0 */ +static int bls_cmp(const char *filename0, const char *filename1) -+{ + { +- return strverscmp((*ent_a)->d_name, (*ent_b)->d_name); + char *id0, *id1; + int l, r; + @@ -116,32 +118,13 @@ index fe72e9ab13d..63186222783 100644 + free(id1); + + return r; + } + ++static int ++bls_sort(const struct dirent **ent_a, const struct dirent **ent_b) ++{ ++ return bls_cmp((*ent_a)->d_name, (*ent_b)->d_name); +} - static int - bls_sort(const struct dirent **ent_a, const struct dirent **ent_b) - { -- return strverscmp((*ent_a)->d_name, (*ent_b)->d_name); -+ return bls_cmp((*ent_a)->d_name, (*ent_b)->d_name); - } - -- static int scan_append_section_heading(struct scan_token* scan, int* index, char* name); - static int -diff --git a/zipl/src/Makefile b/zipl/src/Makefile -index 1634c0d5121..bc797990652 100644 ---- a/zipl/src/Makefile -+++ b/zipl/src/Makefile -@@ -7,7 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \ - -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) - ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) - --libs = $(rootdir)/libutil/libutil.a -+libs = $(rootdir)/libutil/libutil.a -lrpm - - objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ - install.o zipl.o $(rootdir)/zipl/boot/data.o --- -2.17.1 - diff --git a/SOURCES/s390utils-2.15.1-rhel.patch b/SOURCES/s390utils-2.15.1-rhel.patch new file mode 100644 index 0000000..86f0a77 --- /dev/null +++ b/SOURCES/s390utils-2.15.1-rhel.patch @@ -0,0 +1,3478 @@ +From b67ac1d584e00c8c36c885a2fd36709a16809d86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 7 Dec 2020 14:26:26 +0100 +Subject: [PATCH 1/9] zdev/lsdasd: Add FC Endpoint Security information + (#1723844) + +Summary: zdev/lsdasd: Add FC Endpoint Security information +Description: Provide the status of the FC Endpoint Security information via the + long output of lsdasd for online Base and Alias devices. +Upstream-ID: d619b492e997cef0ddba8073f2a3073c7c2ecc5c +Upstream-ID: 79e4798061a89152b967a0761f88f59742722b06 +--- + zconf/lsdasd | 7 +++++-- + zdev/src/dasd.c | 17 +++++++++++++++++ + 2 files changed, 22 insertions(+), 2 deletions(-) + +diff --git a/zconf/lsdasd b/zconf/lsdasd +index 792efc0..795b33b 100755 +--- a/zconf/lsdasd ++++ b/zconf/lsdasd +@@ -387,6 +387,7 @@ function extended() + read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size + read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity + read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated ++ read FC_SEC 2> /dev/null < $DEVPATH/fc_security + + # convert to hexadecimal values + PIM=0x$PIM +@@ -521,7 +522,7 @@ function extended() + elif [[ "$ALIAS" == 1 ]]; then + if [[ "$BASEONLY" == "false" ]]; then + ACTIVE="alias" +- printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ ++ printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" \ + "$ACTIVE" \ +@@ -532,6 +533,7 @@ function extended() + "$ERP" \ + "$HPF" \ + "$DEV_UID" \ ++ "$FC_SEC" \ + "${INSTALLED_PATHS[@]}" \ + "${USED_PATHS[@]}" \ + "${NP_PATHS[@]}" \ +@@ -563,7 +565,7 @@ function extended() + DISCIPLINE="${DISCIPLINE} (ESE)" + fi + +- printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ ++ printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ + "$SORTKEYLEN" "$SORTKEY" \ + "$BUSID" \ + "$BLOCKNAME" \ +@@ -584,6 +586,7 @@ function extended() + "$ERP" \ + "$HPF" \ + "$DEV_UID" \ ++ "$FC_SEC" \ + "${INSTALLED_PATHS[@]}" \ + "${USED_PATHS[@]}" \ + "${NP_PATHS[@]}" \ +diff --git a/zdev/src/dasd.c b/zdev/src/dasd.c +index 645e8b0..63b49e3 100644 +--- a/zdev/src/dasd.c ++++ b/zdev/src/dasd.c +@@ -313,6 +313,22 @@ static struct attrib dasd_attr_safe_offline = { + .writeonly = 1, + }; + ++static struct attrib dasd_attr_fc_security = { ++ .name = "fc_security", ++ .title = "Show FC Endpoint Security state of DASD device", ++ .desc = ++ "This read-only attribute shows the Fibre Channel Endpoint Security\n" ++ "status of the connection to the DASD device:\n" ++ " Unsupported : The DASD device does not support Fibre Channel\n" ++ " Endpoint Security\n" ++ " Inconsistent : The operational channel paths of the DASD device\n" ++ " report inconsistent Fibre Channel Endpoint\n" ++ " Security status\n" ++ " Authentication: The connection has been authenticated\n" ++ " Encryption : The connection is encrypted\n", ++ .readonly = 1, ++}; ++ + /* + * DASD subtype methods. + */ +@@ -617,6 +633,7 @@ struct subtype dasd_subtype_eckd = { + &dasd_attr_reservation_policy, + &dasd_attr_last_known_reservation_state, + &dasd_attr_safe_offline, ++ &dasd_attr_fc_security, + &internal_attr_early, + ), + .unknown_dev_attribs = 1, +-- +2.26.2 + + +From 5f1a4aa22f2c294cd29d1e896c2629969c840dde Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Mon, 2 Nov 2020 14:30:58 +0100 +Subject: [PATCH 2/9] genprotimg: abort if one of the recursive targets is + failing (#1845925) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Abort compilation as soon as one of the recursive targets is failing. + +Fixes: 65b9fc442c1a ("genprotimg: introduce new tool for the creation of PV images") +Signed-off-by: Marc Hartmayer +Signed-off-by: Jan Höppner +(cherry picked from commit 6db7fbe0187042f44a63a5c7dbeb9f116909d02e) +--- + genprotimg/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/genprotimg/Makefile b/genprotimg/Makefile +index 127bde2..4e81a63 100644 +--- a/genprotimg/Makefile ++++ b/genprotimg/Makefile +@@ -21,7 +21,7 @@ clean: clean-recursive + $(RECURSIVE_TARGETS): + @target=`echo $@ |sed s/-recursive//`; \ + for d in $(SUBDIRS); do \ +- $(MAKE) -C $$d $$target; \ ++ $(MAKE) -C $$d $$target || exit 1; \ + done + + .PHONY: all install clean $(RECURSIVE_TARGETS) +-- +2.26.2 + + +From 9bbcbb300168b1e246b5b02605e359e5bbe8dceb Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Mon, 2 Nov 2020 15:55:46 +0100 +Subject: [PATCH 3/9] genprotimg: fix two memory leaks (#1845925) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +==1005844== HEAP SUMMARY: +==1005844== in use at exit: 18,907 bytes in 14 blocks +==1005844== total heap usage: 82 allocs, 68 frees, 32,529 bytes allocated +==1005844== +==1005844== 136 (104 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 12 of 14 +==1005844== at 0x483885A: calloc (vg_replace_malloc.c:760) +==1005844== by 0x48C950D: g_malloc0 (gmem.c:132) +==1005844== by 0x100EC41: pv_args_new (pv_args.c:364) +==1005844== by 0x100587F: main (genprotimg.c:122) +==1005844== +==1005844== LEAK SUMMARY: +==1005844== definitely lost: 104 bytes in 1 blocks +==1005844== indirectly lost: 32 bytes in 1 blocks +==1005844== possibly lost: 0 bytes in 0 blocks +==1005844== still reachable: 18,771 bytes in 12 blocks +==1005844== suppressed: 0 bytes in 0 blocks +==1005844== Reachable blocks (those to which a pointer was found) are not shown. +==1005844== To see them, rerun with: --leak-check=full --show-leak-kinds=all +==1005844== +==1005844== For lists of detected and suppressed errors, rerun with: -s +==1005844== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) + +Signed-off-by: Marc Hartmayer +Signed-off-by: Jan Höppner +(cherry picked from commit db6f272607842a6279fee589fb101f3a1f6148f3) +--- + genprotimg/src/genprotimg.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c +index 0d82550..c6de381 100644 +--- a/genprotimg/src/genprotimg.c ++++ b/genprotimg/src/genprotimg.c +@@ -177,5 +177,7 @@ error: + rmdir_recursive(tmp_dir, NULL); + remove_signal_handler(signals, G_N_ELEMENTS(signals)); + g_free(tmp_dir); ++ g_clear_pointer(&img, pv_img_free); ++ g_clear_pointer(&args, pv_args_free); + exit(ret); + } +-- +2.26.2 + + +From 977d553fd81dbc5924f9b783231e1bacfe73ccd2 Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Wed, 11 Nov 2020 22:30:12 +0100 +Subject: [PATCH 4/9] genprotimg: require argument for 'ramdisk' and 'parmfile' + options (#1845925) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +A argument is required for the optional options 'ramdisk' and +'parmfile'. + +Fixes: 65b9fc442c1a ("genprotimg: introduce new tool for the creation of PV images") +Reviewed-by: Bjoern Walk +Signed-off-by: Marc Hartmayer +Signed-off-by: Jan Höppner +(cherry picked from commit 895a88b2f8d775e45ab1251f0b4bb275efd44a64) +--- + genprotimg/src/pv/pv_args.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c +index 9fb7298..4bb78b5 100644 +--- a/genprotimg/src/pv/pv_args.c ++++ b/genprotimg/src/pv/pv_args.c +@@ -227,7 +227,7 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], + .arg_description = _("IMAGE") }, + { .long_name = "ramdisk", + .short_name = 'r', +- .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, ++ .flags = G_OPTION_FLAG_FILENAME, + .arg = G_OPTION_ARG_CALLBACK, + .arg_data = cb_add_component, + .description = _("Use RAMDISK as the initial RAM disk\n" INDENT +@@ -235,7 +235,7 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], + .arg_description = _("RAMDISK") }, + { .long_name = "parmfile", + .short_name = 'p', +- .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, ++ .flags = G_OPTION_FLAG_FILENAME, + .arg = G_OPTION_ARG_CALLBACK, + .arg_data = cb_add_component, + .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT +-- +2.26.2 + + +From c17cdcf28fabb373884f51c08b93502999910035 Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Mon, 2 Nov 2020 15:19:06 +0100 +Subject: [PATCH 5/9] genprotimg: add host-key document verification support + (#1845925) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add host-key document verification support to genprotimg. This ensures +that a host-key document is genuine and provided by IBM. For this the +user must provide the IBM Z signing key, the intermediate CA +certificate (signed by the root CA used) so a chain of trust starting +from the host-key document and ending in the root CA can be +established. + +By default, genprotimg tries to download all revocation lists needed +by looking up in the corresponding certificate on how CRL information +can be obtained (see https://tools.ietf.org/html/rfc5280#section-4.2.1.13 +for details). + +Acked-by: Patrick Steuer +Signed-off-by: Marc Hartmayer +Signed-off-by: Jan Höppner +(cherry picked from commit 074de1e14ed785c18f55ecf9762ac3f5de3465b4) +--- + README.md | 2 +- + genprotimg/man/genprotimg.8 | 49 +- + genprotimg/src/Makefile | 17 +- + genprotimg/src/genprotimg.c | 17 + + genprotimg/src/include/pv_crypto_def.h | 18 + + genprotimg/src/pv/pv_args.c | 57 +- + genprotimg/src/pv/pv_args.h | 6 + + genprotimg/src/pv/pv_error.h | 27 + + genprotimg/src/pv/pv_image.c | 203 +++- + genprotimg/src/utils/crypto.c | 1379 +++++++++++++++++++++++- + genprotimg/src/utils/crypto.h | 75 +- + genprotimg/src/utils/curl.c | 121 +++ + genprotimg/src/utils/curl.h | 25 + + 13 files changed, 1927 insertions(+), 69 deletions(-) + create mode 100644 genprotimg/src/utils/curl.c + create mode 100644 genprotimg/src/utils/curl.h + +diff --git a/README.md b/README.md +index aa2188a..6fb6831 100644 +--- a/README.md ++++ b/README.md +@@ -277,7 +277,7 @@ build options: + | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | + | json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb | + | glib2 | `HAVE_GLIB2` | genprotimg | +-| libcurl | `HAVE_LIBCURL` | libekmfweb | ++| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb | + + This table lists additional build or install options: + +diff --git a/genprotimg/man/genprotimg.8 b/genprotimg/man/genprotimg.8 +index 597106e..c08336b 100644 +--- a/genprotimg/man/genprotimg.8 ++++ b/genprotimg/man/genprotimg.8 +@@ -2,7 +2,7 @@ + .\" 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 GENPROTIMG 8 "March 2020" "s390-tools" ++.TH GENPROTIMG 8 "November 2020" "s390-tools" + .SH NAME + genprotimg \- Create a protected virtualization image + +@@ -10,6 +10,7 @@ genprotimg \- Create a protected virtualization image + .SY + .B genprotimg + \fB\-k\fR \fIHOST_KEY_DOCUMENT\fR... ++\fB\-C\fR \fICERTIFICATE\fR... + \fB\-i\fR \fIVMLINUZ\fR + [\fB\-r\fR \fIRAMDISK\fR] + [\fB\-p\fR \fIPARMFILE\fR] +@@ -21,15 +22,19 @@ genprotimg \- Create a protected virtualization image + .PP + Use \fBgenprotimg\fR to generate a single bootable image file with + encrypted and integrity-protected parts. The command requires a kernel +-image, a host-key document, and an output file name. Optionally, +-specify an initial RAM filesystem, and a file containing the kernel +-parameters. Should special circumstances require it, you can ++image, a host-key document, certificates for the host-key document ++verification, and an output file name. Optionally, specify an initial ++RAM filesystem, and a file containing the kernel parameters. If the ++command should be run offline, use the \fB\-\-offline\fR option and ++specify the certificate revocation lists (CRLs) by using the ++\fB\-\-crl\fR option. Should special circumstances require it, you can + optionally specify your own keys for the encryption by using the +-experimental options. In the resulting image file, a plain text boot +-loader, the encrypted components for kernel, initial RAM disk, kernel +-parameters, and the encrypted and integrity-protected header are +-concatenated. The header contains metadata necessary for running the +-guest in protected mode. ++experimental options. For all certificates, CRLs, and host-key ++documents, both the PEM and DER input formats are supported. In the ++resulting image file, a plain text boot loader, the encrypted ++components for kernel, initial RAM disk, kernel parameters, and the ++encrypted and integrity-protected header are concatenated. The header ++contains metadata necessary for running the guest in protected mode. + .PP + Use this image file as a kernel image for zipl or for a direct kernel + boot using QEMU. +@@ -53,6 +58,12 @@ Specifies a host-key document. At least one is required. Specify this + option multiple times to enable the image to run on more than one + host. + .TP ++\fB\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR ++Specifies the certificate that is used to establish a chain of trust ++for the verification of the host-key documents. Specify this option ++twice to specify the IBM Z signing key and the intermediate CA ++certificate (signed by the root CA). Required. ++.TP + \fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR + Specifies the output file. Required. + .TP +@@ -65,6 +76,20 @@ Specifies the RAM disk image. Optional. + \fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR + Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional. + .TP ++\fB\-\-crl\fR=\fI\,FILE\/\fR ++Specifies the revocation list that is used to check whether a ++certificate of the chain of trust is revoked. Specify this option ++multiple times to use multiple CRLs. Optional. ++.TP ++\fB\-\-offline\fR ++Specifies offline mode, in which no attempt is made to download ++CRLs. Optional. ++.TP ++\fB\-\-root\-ca\fR=\fI\,FILE\/\fR ++Specifies the root CA certificate for the verification. If omitted, ++the DigiCert root CA certificate installed on the system is used. Use ++this only if you trust the specified certificate. Optional. ++.TP + \fB\-\-no-verify\fR + Do not require the host-key documents to be valid. For testing + purposes, do not use for a production image. Optional. +@@ -77,11 +102,13 @@ Prints version information, then exits. + Generate a protected virtualization image in + \fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, + the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in +-\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR: ++\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, ++the IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the ++host-key document in \fI\,host_key.crt\/\fR: + .PP + .Vb 1 + .EX +-\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-o \fI\,/boot/vmlinuz.pv\/\fR ++\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR + .EE + .Ve + .PP +diff --git a/genprotimg/src/Makefile b/genprotimg/src/Makefile +index 1adeac3..2da15da 100644 +--- a/genprotimg/src/Makefile ++++ b/genprotimg/src/Makefile +@@ -23,16 +23,16 @@ WARNINGS := -Wall -Wextra -Wshadow \ + $(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \ + pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \ + pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \ +- pv/pv_opt_item.c \ ++ pv/pv_opt_item.c utils/curl.c \ + $(NULL) + $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o) + + ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \ +- $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \ ++ $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \ + $(WARNINGS) \ + $(NULL) + ALL_CPPFLAGS += $(INCLUDE_PARMS) +-LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) ++LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS) + + + ifneq ($(shell sh -c 'command -v pkg-config'),) +@@ -40,21 +40,27 @@ GLIB2_CFLAGS := $(shell pkg-config --silence-errors --cflags glib-2.0) + GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0) + LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto) + LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto) ++LIBCURL_CFLAGS := $(shell pkg-config --silence-errors --cflags libcurl) ++LIBCURL_LIBS := $(shell pkg-config --silence-errors --libs libcurl) + else + GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include + GLIB2_LIBS := -lglib-2.0 + LIBCRYPTO_CFLAGS := + LIBCRYPTO_LIBS := -lcrypto ++LIBCURL_CFLAGS := ++LIBCURL_LIBS := -lcurl + endif + + BUILD_TARGETS := skip-$(bin_PROGRAM) + INSTALL_TARGETS := skip-$(bin_PROGRAM) + ifneq (${HAVE_OPENSSL},0) + ifneq (${HAVE_GLIB2},0) ++ifneq (${HAVE_LIBCURL},0) + BUILD_TARGETS := $(bin_PROGRAM) + INSTALL_TARGETS := install-$(bin_PROGRAM) + endif + endif ++endif + + all: $(BUILD_TARGETS) + +@@ -98,4 +104,9 @@ $($(bin_PROGRAM)_OBJS): .check-dep-$(bin_PROGRAM) + "openssl-devel / libssl-dev version >= 1.1.0", \ + "HAVE_OPENSSL=0", \ + "-I.") ++ $(call check_dep, \ ++ "$(bin_PROGRAM)", \ ++ "curl/curl.h", \ ++ "libcurl-devel", \ ++ "HAVE_LIBCURL=0") + touch $@ +diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c +index c6de381..2041fa2 100644 +--- a/genprotimg/src/genprotimg.c ++++ b/genprotimg/src/genprotimg.c +@@ -18,6 +18,8 @@ + #include "common.h" + #include "pv/pv_args.h" + #include "pv/pv_image.h" ++#include "utils/crypto.h" ++#include "utils/curl.h" + + enum { + LOG_LEVEL_CRITICAL = 0, +@@ -117,6 +119,8 @@ static void remove_signal_handler(const gint *signals, const gsize signals_n) + signal(signals[i], SIG_DFL); + } + ++static void __attribute__((constructor)) __init(void); ++static void __attribute__((destructor)) __cleanup(void); + gint main(gint argc, gchar *argv[]) + { + g_autoptr(PvArgs) args = pv_args_new(); +@@ -181,3 +185,16 @@ error: + g_clear_pointer(&args, pv_args_free); + exit(ret); + } ++ ++static void __init(void) ++{ ++ pv_crypto_init(); ++ if (curl_init() != 0) ++ g_abort(); ++} ++ ++static void __cleanup(void) ++{ ++ curl_cleanup(); ++ pv_crypto_cleanup(); ++} +diff --git a/genprotimg/src/include/pv_crypto_def.h b/genprotimg/src/include/pv_crypto_def.h +index ddb8652..53984a3 100644 +--- a/genprotimg/src/include/pv_crypto_def.h ++++ b/genprotimg/src/include/pv_crypto_def.h +@@ -14,6 +14,24 @@ + + #include "lib/zt_common.h" + ++/* IBM signing key subject */ ++#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation" ++#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US" ++#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie" ++#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service" ++#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation" ++#define PV_IBM_Z_SUBJECT_STATE "New York" ++#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6 ++ ++/* Minimum security level for the keys/certificates used to establish a chain of ++ * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html ++ * for details). ++ */ ++#define PV_CERTS_SECURITY_LEVEL 2 ++ ++/* SKID for DigiCert Assured ID Root CA */ ++#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F" ++ + union ecdh_pub_key { + struct { + uint8_t x[80]; +diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c +index 4bb78b5..3ba1f94 100644 +--- a/genprotimg/src/pv/pv_args.c ++++ b/genprotimg/src/pv/pv_args.c +@@ -18,7 +18,9 @@ + + static gchar summary[] = + "Use genprotimg to create a protected virtualization kernel image file,\n" +- "which can be loaded using zipl or QEMU."; ++ "which can be loaded using zipl or QEMU. For all certificates, revocation\n" ++ "lists, and host-key documents, both the PEM and DER input formats are\n" ++ "supported."; + + static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2) + { +@@ -97,9 +99,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err) + return -1; + } + +- if (!args->no_verify) { +- g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, +- _("Use the option '--no-verify' as the verification support is not available yet.")); ++ if (!args->no_verify && ++ (!args->untrusted_cert_paths || ++ g_strv_length(args->untrusted_cert_paths) == 0)) { ++ g_set_error( ++ err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, ++ _("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n" ++ "by using the '--cert' option, or use the '--no-verify' flag to disable the\n" ++ "host-key document verification completely (at your own risk).")); + return -1; + } + +@@ -141,6 +148,8 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value, + { + gchar **args_option = NULL; + ++ if (g_str_equal(option, "--root-ca")) ++ args_option = &args->root_ca_path; + if (g_str_equal(option, "-o") || g_str_equal(option, "--output")) + args_option = &args->output_path; + if (g_str_equal(option, "--x-comp-key")) +@@ -211,6 +220,18 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], + _("FILE specifies a host-key document. At least\n" INDENT + "one is required."), + .arg_description = _("FILE") }, ++ { .long_name = "cert", ++ .short_name = 'C', ++ .flags = G_OPTION_FLAG_NONE, ++ .arg = G_OPTION_ARG_FILENAME_ARRAY, ++ .arg_data = &args->untrusted_cert_paths, ++ .description = _( ++ "FILE contains a certificate that is used to\n" INDENT ++ "establish a chain of trust for the verification\n" INDENT ++ "of the host-key documents. The IBM Z signing\n" INDENT ++ "key and intermediate CA certificate (signed\n" INDENT ++ "by the root CA) are required."), ++ .arg_description = _("FILE") }, + { .long_name = "output", + .short_name = 'o', + .flags = G_OPTION_FLAG_FILENAME, +@@ -241,6 +262,31 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], + .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT + "(optional)."), + .arg_description = _("PARMFILE") }, ++ { .long_name = "crl", ++ .short_name = 0, ++ .flags = G_OPTION_FLAG_NONE, ++ .arg = G_OPTION_ARG_FILENAME_ARRAY, ++ .arg_data = &args->crl_paths, ++ .description = _( ++ "FILE contains a certificate revocation list\n" INDENT ++ "(optional)."), ++ .arg_description = _("FILE") }, ++ { .long_name = "offline", ++ .short_name = 0, ++ .flags = G_OPTION_FLAG_NONE, ++ .arg = G_OPTION_ARG_NONE, ++ .arg_data = &args->offline, ++ .description = _("Don't download CRLs (optional)."), ++ .arg_description = NULL }, ++ { .long_name = "root-ca", ++ .short_name = 0, ++ .flags = G_OPTION_FLAG_FILENAME, ++ .arg = G_OPTION_ARG_CALLBACK, ++ .arg_data = cb_set_string_option, ++ .description = _( ++ "Set FILE as the trusted root CA and don't use the\n" INDENT ++ "root CAs that are installed on the system (optional)."), ++ .arg_description = _("FILE") }, + { .long_name = "no-verify", + .short_name = 0, + .flags = G_OPTION_FLAG_NONE, +@@ -378,6 +424,9 @@ void pv_args_free(PvArgs *args) + g_free(args->cust_root_key_path); + g_free(args->cust_comm_key_path); + g_free(args->gcm_iv_path); ++ g_free(args->root_ca_path); ++ g_strfreev(args->crl_paths); ++ g_strfreev(args->untrusted_cert_paths); + g_strfreev(args->host_keys); + g_free(args->xts_key_path); + g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free); +diff --git a/genprotimg/src/pv/pv_args.h b/genprotimg/src/pv/pv_args.h +index f17e7b8..8939232 100644 +--- a/genprotimg/src/pv/pv_args.h ++++ b/genprotimg/src/pv/pv_args.h +@@ -25,6 +25,7 @@ void pv_arg_free(PvArg *arg); + typedef struct { + gint log_level; + gint no_verify; ++ gboolean offline; + gchar *pcf; + gchar *scf; + gchar *psw_addr; /* PSW address which will be used for the start of +@@ -34,6 +35,11 @@ typedef struct { + gchar *cust_comm_key_path; + gchar *gcm_iv_path; + gchar **host_keys; ++ gchar *root_ca_path; /* Trusted root CA used for the verification of the ++ * chain of trust (if specified). ++ */ ++ gchar **untrusted_cert_paths; ++ gchar **crl_paths; + gchar *xts_key_path; + GSList *comps; + gchar *output_path; +diff --git a/genprotimg/src/pv/pv_error.h b/genprotimg/src/pv/pv_error.h +index 1dd24fc..abe47ae 100644 +--- a/genprotimg/src/pv/pv_error.h ++++ b/genprotimg/src/pv/pv_error.h +@@ -28,6 +28,8 @@ typedef enum { + PV_ERROR_IPIB_SIZE, + PV_ERROR_PV_HDR_SIZE, + PV_ERROR_INTERNAL, ++ PV_ERROR_CURL_INIT_FAILED, ++ PV_ERROR_DOWNLOAD_FAILED, + } PvErrors; + + typedef enum { +@@ -57,6 +59,31 @@ typedef enum { + PV_CRYPTO_ERROR_RANDOMIZATION, + PV_CRYPTO_ERROR_INVALID_PARM, + PV_CRYPTO_ERROR_INVALID_KEY_SIZE, ++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, ++ PV_CRYPTO_ERROR_EXPIRED, ++ PV_CRYPTO_ERROR_NOT_VALID_YET, ++ PV_CRYPTO_ERROR_LOAD_CRL, ++ PV_CRYPTO_ERROR_NO_PUBLIC_KEY, ++ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, ++ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, ++ PV_CRYPTO_ERROR_INVALID_URI, ++ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, ++ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, ++ PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, ++ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, ++ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, ++ PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, ++ PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, ++ PV_CRYPTO_ERROR_NO_CRL, ++ PV_CRYPTO_ERROR_LOAD_ROOT_CA, ++ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, ++ PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, ++ PV_CRYPTO_ERROR_WRONG_CA_USED, ++ PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, ++ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, ++ PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, ++ PV_CRYPTO_ERROR_NO_CRLDP, ++ PV_CRYPTO_ERROR_CERT_REVOKED, + } PvCryptoErrors; + + #endif +diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c +index 7ec5fe9..59eca5e 100644 +--- a/genprotimg/src/pv/pv_image.c ++++ b/genprotimg/src/pv/pv_image.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -138,22 +139,18 @@ static EVP_PKEY *pv_img_get_cust_pub_priv_key(gint nid, GError **err) + return generate_ec_key(nid, err); + } + +-static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths, +- X509_STORE *store, gint nid, ++static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid, + GError **err) + { + g_autoslist(EVP_PKEY) ret = NULL; + +- g_assert(host_cert_paths); +- +- for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL; +- iterator++) { ++ for (GSList *iterator = host_keys_with_path; iterator; ++ iterator = iterator->next) { ++ x509_with_path *cert_with_path = iterator->data; + g_autoptr(EVP_PKEY) host_key = NULL; +- const gchar *path = *iterator; +- +- g_assert(path); ++ X509 *cert = cert_with_path->cert; + +- host_key = read_ec_pubkey_cert(store, nid, path, err); ++ host_key = read_ec_pubkey_cert(cert, nid, err); + if (!host_key) + return NULL; + +@@ -253,10 +250,172 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s, + return 0; + } + ++static gint pv_img_hostkey_verify(GSList *host_key_certs, ++ const gchar *root_ca_path, ++ const gchar *const *crl_paths, ++ const gchar *const *untrusted_cert_paths, ++ gboolean offline, GError **err) ++{ ++ g_autoslist(x509_with_path) untrusted_certs_with_path = NULL; ++ g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; ++ g_autoptr(STACK_OF_X509) untrusted_certs = NULL; ++ g_autoslist(x509_pair) ibm_z_pairs = NULL; ++ g_autoptr(X509_STORE) trusted = NULL; ++ gint ibm_signing_certs_count; ++ ++ /* Load trusted root CAs of the system if and only if @root_ca_path is ++ * NULL, otherwise use the root CA specified by @root_ca_path. ++ */ ++ trusted = store_setup(root_ca_path, crl_paths, err); ++ if (!trusted) ++ goto error; ++ ++ if (!offline) { ++ g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL; ++ ++ /* Set up the download routine for the lookup of CRLs. */ ++ store_setup_crl_download(trusted); ++ ++ /* Try to download the CRLs of the IBM Z signing certificates ++ * specified in the host-key documents. Ignore download errors ++ * as it's still possible that a CRL is specified via command ++ * line. ++ */ ++ downloaded_ibm_signing_crls = try_load_crls_by_certs(host_key_certs); ++ ++ /* Add the downloaded CRLs to the store so they can be used for ++ * the verification later. ++ */ ++ for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) { ++ X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i); ++ ++ if (X509_STORE_add_crl(trusted, crl) != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INTERNAL, ++ _("failed to load CRL")); ++ goto error; ++ } ++ } ++ } ++ ++ /* Load all untrusted certificates (e.g. IBM Z signing key and ++ * DigiCert intermediate CA) that are required to establish a chain of ++ * trust starting from the host-key document up to the root CA (if not ++ * otherwise specified that's the DigiCert Assured ID Root CA). ++ */ ++ untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err); ++ if (!untrusted_certs_with_path) ++ goto error; ++ ++ /* Convert to STACK_OF(X509) */ ++ untrusted_certs = get_x509_stack(untrusted_certs_with_path); ++ ++ /* Find all IBM Z signing keys and remove them from the chain as we ++ * have to verify that they're valid. The last step of the chain of ++ * trust verification must be done manually, as the IBM Z signing keys ++ * are not marked as (intermediate) CA and therefore the standard ++ * `X509_verify_cert` function of OpenSSL cannot be used to verify the ++ * actual host-key documents. ++ */ ++ ibm_signing_certs = delete_ibm_signing_certs(untrusted_certs); ++ ibm_signing_certs_count = sk_X509_num(ibm_signing_certs); ++ if (ibm_signing_certs_count < 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, ++ _("please specify at least one IBM Z signing key")); ++ goto error; ++ } else if (ibm_signing_certs_count > 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, ++ _("please specify only one IBM Z signing key")); ++ goto error; ++ } ++ ++ if (store_set_verify_param(trusted, err) < 0) ++ goto error; ++ ++ /* Verify that the IBM Z signing keys are trustable. ++ * For this we must check: ++ * ++ * 1. Can a chain of trust be established ending in a root CA ++ * 2. Is the correct root CA ued? It has either to be the ++ * 'DigiCert Assured ID Root CA' or the root CA specified via ++ * command line. ++ */ ++ for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) { ++ X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i); ++ g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL; ++ g_autoptr(X509_STORE_CTX) ctx = NULL; ++ x509_pair *pair = NULL; ++ ++ g_assert(ibm_signing_cert); ++ ++ /* Create the verification context and set the trusted ++ * and chain parameters. ++ */ ++ ctx = create_store_ctx(trusted, untrusted_certs, err); ++ if (!ctx) ++ goto error; ++ ++ /* Verify the IBM Z signing key */ ++ if (verify_cert(ibm_signing_cert, ctx, err) < 0) ++ goto error; ++ ++ /* Verify the build chain of trust chain. If the user passes a ++ * trusted root CA on the command line then the check for the ++ * Subject Key Identifier (SKID) is skipped, otherwise let's ++ * check if the SKID meets our expectation. ++ */ ++ if (!root_ca_path && ++ check_chain_parameters(X509_STORE_CTX_get0_chain(ctx), ++ get_digicert_assured_id_root_ca_skid(), ++ err) < 0) { ++ goto error; ++ } ++ ++ ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err); ++ if (!ibm_signing_crls) { ++ g_prefix_error(err, _("IBM Z signing key: ")); ++ goto error; ++ } ++ ++ /* Increment reference counter of @ibm_signing_cert as the ++ * certificate will now also be owned by @ibm_z_pairs. ++ */ ++ if (X509_up_ref(ibm_signing_cert) != 1) ++ g_abort(); ++ ++ pair = x509_pair_new(&ibm_signing_cert, &ibm_signing_crls); ++ ibm_z_pairs = g_slist_append(ibm_z_pairs, pair); ++ g_assert(!ibm_signing_cert); ++ g_assert(!ibm_signing_crls); ++ } ++ ++ /* Verify host-key documents by using the IBM Z signing ++ * certificates and the corresponding certificate revocation ++ * lists. ++ */ ++ for (GSList *iterator = host_key_certs; iterator; iterator = iterator->next) { ++ x509_with_path *host_key_with_path = iterator->data; ++ const gchar *host_key_path = host_key_with_path->path; ++ X509 *host_key = host_key_with_path->cert; ++ gint flags = X509_V_FLAG_CRL_CHECK; ++ ++ if (verify_host_key(host_key, ibm_z_pairs, flags, ++ PV_CERTS_SECURITY_LEVEL, err) < 0) { ++ g_prefix_error(err, "'%s': ", host_key_path); ++ goto error; ++ } ++ } ++ ++ return 0; ++error: ++ g_prefix_error(err, _("Failed to verify host-key document: ")); ++ return -1; ++} ++ + /* read in the keys or auto-generate them */ + static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) + { +- g_autoptr(X509_STORE) store = NULL; ++ g_autoslist(x509_with_path) host_key_certs = NULL; + + g_assert(img->xts_cipher); + g_assert(img->cust_comm_cipher); +@@ -285,8 +444,25 @@ static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) + if (!img->cust_pub_priv_key) + return -1; + ++ /* Load all host-key documents specified on the command line */ ++ host_key_certs = load_certificates((const gchar **)args->host_keys, ++ err); ++ if (!host_key_certs) ++ return -1; ++ ++ if (!args->no_verify && ++ pv_img_hostkey_verify(host_key_certs, args->root_ca_path, ++ (const gchar * const *)args->crl_paths, ++ (const gchar * const *)args->untrusted_cert_paths, ++ args->offline, err) < 0) { ++ return -1; ++ } ++ ++ /* Loads the public keys stored in the host-key documents and verify ++ * that the correct elliptic curve is used. ++ */ + img->host_pub_keys = +- pv_img_get_host_keys(args->host_keys, store, img->nid, err); ++ pv_img_get_host_keys(host_key_certs, img->nid, err); + if (!img->host_pub_keys) + return -1; + +@@ -406,6 +582,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err) + if (args->no_verify) + g_warning(_("host-key document verification is disabled. Your workload is not secured.")); + ++ if (args->root_ca_path) ++ g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted.")); ++ + ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err); + if (!ret->comps) + return NULL; +diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c +index b0c4aa9..0f774eb 100644 +--- a/genprotimg/src/utils/crypto.c ++++ b/genprotimg/src/utils/crypto.c +@@ -16,6 +16,11 @@ + #include + #include + #include ++#include ++#include ++#include ++#include ++#include + #include + #include + +@@ -25,8 +30,49 @@ + #include "pv/pv_error.h" + + #include "buffer.h" ++#include "curl.h" + #include "crypto.h" + ++#define DEFINE_GSLIST_MAP(t2, t1) \ ++ typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, \ ++ GError **err); \ ++ G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1(const GSList *list, \ ++ g_slist_map_func_##t2##_##t1 func, \ ++ GError **err) \ ++ { \ ++ g_autoslist(t1) ret = NULL; \ ++ for (const GSList *iterator = list; iterator; \ ++ iterator = iterator->next) { \ ++ const t2 *value = iterator->data; \ ++ t1 *new_value = NULL; \ ++ g_assert(value); \ ++ new_value = func(value, err); \ ++ if (!new_value) \ ++ return NULL; \ ++ ret = g_slist_append(ret, g_steal_pointer(&new_value)); \ ++ } \ ++ return g_steal_pointer(&ret); \ ++ } ++ ++#define DEFINE_GSLIST_TO_STACK(t1) \ ++ G_GNUC_UNUSED static STACK_OF(t1) *g_slist_to_stack_of_##t1(GSList **list) \ ++ { \ ++ g_assert(list); \ ++ g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \ ++ if (!ret) \ ++ g_abort(); \ ++ for (GSList *iterator = *list; iterator; \ ++ iterator = iterator->next) { \ ++ if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \ ++ g_abort(); \ ++ } \ ++ g_clear_pointer(list, g_slist_free); \ ++ return g_steal_pointer(&ret); \ ++ } ++ ++DEFINE_GSLIST_MAP(x509_with_path, X509) ++DEFINE_GSLIST_TO_STACK(X509) ++ + EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) + { + g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); +@@ -359,79 +405,1340 @@ static gboolean certificate_uses_correct_curve(EVP_PKEY *key, gint nid, + return TRUE; + } + +-static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err) ++/* Verify that the used public key algorithm matches the subject signature ++ * algorithm ++ */ ++static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject, ++ GError **err) + { +- g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new(); +- if (!csc) +- g_abort(); ++ gint pkey_nid; + +- if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) { +- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT, +- _("Failed to initialize X.509 store")); +- return FALSE; ++ if (!pkey) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_PUBLIC_KEY, ++ _("no public key")); ++ return -1; + } + +- if (X509_verify_cert(csc) != 1) { +- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, +- _("Failed to verify host-key document")); +- return FALSE; ++ if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL, ++ &pkey_nid) != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, ++ _("unsupported signature algorithm")); ++ return -1; + } + +- return TRUE; ++ if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, ++ _("signature algorithm mismatch")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static X509_CRL *load_crl_from_bio(BIO *bio) ++{ ++ g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL); ++ if (crl) ++ return g_steal_pointer(&crl); ++ ERR_clear_error(); ++ BIO_reset(bio); ++ ++ /* maybe the CRL is stored in DER format */ ++ crl = d2i_X509_CRL_bio(bio, NULL); ++ if (crl) ++ return g_steal_pointer(&crl); ++ return NULL; ++} ++ ++static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data) ++{ ++ g_autoptr(X509_CRL) ret = NULL; ++ g_autoptr(BIO) bio = NULL; ++ ++ g_assert(data); ++ ++ if (data->len > INT_MAX) ++ return NULL; ++ ++ bio = BIO_new_mem_buf(data->data, (int)data->len); ++ if (!bio) ++ g_abort(); ++ ++ ret = load_crl_from_bio(bio); ++ if (!ret) ++ return NULL; ++ ++ return g_steal_pointer(&ret); ++} ++ ++static gint load_crl_from_web(const gchar *url, X509_CRL **crl, GError **err) ++{ ++ g_autoptr(X509_CRL) tmp_crl = NULL; ++ g_autoptr(GByteArray) data = NULL; ++ g_assert(crl); ++ ++ data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS, ++ CRL_DOWNLOAD_MAX_SIZE, err); ++ if (!data) { ++ g_prefix_error(err, _("unable to download CRL: ")); ++ return -1; ++ } ++ tmp_crl = GByteArray_to_X509_CRL(data); ++ if (!tmp_crl) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, ++ _("unable to load CRL from '%s'"), url); ++ return -1; ++ } ++ *crl = g_steal_pointer(&tmp_crl); ++ return 0; ++} ++ ++static BIO *bio_read_from_file(const char *path) ++{ ++ g_autoptr(BIO) bio = BIO_new_file(path, "r"); ++ ++ if (!bio) ++ return NULL; ++ ++ return g_steal_pointer(&bio); + } + +-static X509 *load_certificate(const gchar *path, GError **err) ++/* This function reads in only the first certificate and ignores all other. This ++ * is only relevant for the PEM file format. For the host-key document and the ++ * root CA this behavior is expected. ++ */ ++X509 *load_cert_from_file(const char *path, GError **err) + { +- g_autoptr(X509) ret = NULL; +- g_autoptr(BIO) bio = BIO_new_file(path, "rb"); ++ g_autoptr(BIO) bio = bio_read_from_file(path); ++ g_autoptr(X509) cert = NULL; + + if (!bio) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_READ_CERTIFICATE, +- _("Failed to read host-key document: '%s'"), path); ++ _("unable to read certificate: '%s'"), path); + return NULL; + } + +- ret = PEM_read_bio_X509(bio, NULL, 0, NULL); +- if (!ret) { +- g_set_error(err, PV_CRYPTO_ERROR, +- PV_CRYPTO_ERROR_READ_CERTIFICATE, +- _("Failed to load host-key document: '%s'"), path); ++ cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); ++ if (cert) ++ return g_steal_pointer(&cert); ++ ERR_clear_error(); ++ BIO_reset(bio); ++ ++ /* maybe the certificate is stored in DER format */ ++ cert = d2i_X509_bio(bio, NULL); ++ if (cert) ++ return g_steal_pointer(&cert); ++ ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, ++ _("unable to load certificate: '%s'"), path); ++ return NULL; ++} ++ ++/* @crl_paths is allowed to be NULL */ ++static int load_crls_to_store(X509_STORE *store, const gchar *const *crl_paths, ++ gboolean err_out_empty_crls, GError **err) ++{ ++ for (const gchar *const *iterator = crl_paths; ++ iterator != NULL && *iterator != NULL; iterator++) { ++ const gchar *crl_path = *iterator; ++ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); ++ int count; ++ ++ g_assert(crl_path); ++ ++ if (!lookup) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("X509 store initialization failed")); ++ return -1; ++ } ++ ++ /* support *.pem files containing multiple CRLs */ ++ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM); ++ if (count > 0) ++ continue; ++ ++ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1); ++ if (count == 1) ++ continue; ++ ++ if (err_out_empty_crls) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_LOAD_CRL, ++ _("unable to load CRL from: '%s'"), crl_path); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* returns ++ * 0 when the certificate is valid, ++ * -1 when not yet valid, ++ * 1 when expired ++ */ ++static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after) ++{ ++ if (X509_cmp_current_time(not_before) != -1) ++ return -1; ++ ++ if (X509_cmp_current_time(not_after) != 1) ++ return 1; ++ ++ return 0; ++} ++ ++static gint x509_name_entry_get_data0(X509_NAME_ENTRY *entry, const guchar **data, ++ gsize *data_len) ++{ ++ const ASN1_STRING *asn1_str; ++ gint tmp_data_len; ++ ++ g_assert(data); ++ g_assert(data_len); ++ ++ asn1_str = X509_NAME_ENTRY_get_data(entry); ++ if (!asn1_str) ++ return -1; ++ ++ tmp_data_len = ASN1_STRING_length(asn1_str); ++ if (tmp_data_len < 0) ++ return -1; ++ ++ *data = ASN1_STRING_get0_data(asn1_str); ++ *data_len = (gsize)tmp_data_len; ++ return 0; ++} ++ ++/* The caller must not free *data! */ ++static gint x509_name_get_data0_by_NID(X509_NAME *name, gint nid, ++ const guchar **data, gsize *data_len) ++{ ++ ++ X509_NAME_ENTRY *entry = NULL; ++ gint lastpos = -1; ++ ++ lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos); ++ if (lastpos == -1) ++ return -1; ++ ++ entry = X509_NAME_get_entry(name, lastpos); ++ if (!entry) ++ return -1; ++ ++ if (x509_name_entry_get_data0(entry, data, data_len) < 0) ++ return -1; ++ ++ return 0; ++} ++ ++/* @y must be a NULL-terminated string */ ++static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid, ++ const gchar *y) ++{ ++ const guchar *data = NULL; ++ gsize y_len = strlen(y); ++ gsize data_len; ++ ++ if (x509_name_get_data0_by_NID(name, nid, &data, &data_len) < 0) ++ return FALSE; ++ ++ if (data_len != y_len) ++ return FALSE; ++ ++ return memcmp(data, y, data_len) == 0; ++} ++ ++static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x, ++ const X509_NAME_ENTRY *y) ++{ ++ const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x); ++ const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x); ++ const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y); ++ const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y); ++ gint x_len = ASN1_STRING_length(x_data); ++ gint y_len = ASN1_STRING_length(y_data); ++ ++ if (x_len < 0 || x_len != y_len) ++ return FALSE; ++ ++ /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also ++ * compares the type, which is sometimes different. ++ */ ++ return OBJ_cmp(x_obj, y_obj) == 0 && ++ memcmp(ASN1_STRING_get0_data(x_data), ++ ASN1_STRING_get0_data(y_data), ++ (unsigned long)x_len) == 0; ++} ++ ++static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y) ++{ ++ gint x_count = X509_NAME_entry_count(x); ++ gint y_count = X509_NAME_entry_count(y); ++ ++ if (x != y && (!x || !y)) ++ return FALSE; ++ ++ if (x_count != y_count) ++ return FALSE; ++ ++ for (gint i = 0; i < x_count; i++) { ++ const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i); ++ gboolean entry_found = FALSE; ++ ++ for (gint j = 0; j < y_count; j++) { ++ const X509_NAME_ENTRY *entry_j = ++ X509_NAME_get_entry(y, j); ++ ++ if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) { ++ entry_found = TRUE; ++ break; ++ } ++ } ++ ++ if (!entry_found) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* Checks whether the subject of @cert is a IBM signing key subject. For this we ++ * must check that the subject is equal to: 'C = US, ST = New York, L = ++ * Poughkeepsie, O = International Business Machines Corporation, CN = ++ * International Business Machines Corporation' and the organization unit (OUT) ++ * must end with the suffix ' Key Signing Service'. ++ */ ++static gboolean has_ibm_signing_subject(X509 *cert) ++{ ++ X509_NAME *subject = X509_get_subject_name(cert); ++ /* X509_NAME_entry_count is safe to be used with NULL */ ++ gint entry_count = X509_NAME_entry_count(subject); ++ g_autofree gchar *data_str = NULL; ++ const guchar *data; ++ gsize data_len; ++ ++ if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT) ++ return FALSE; ++ ++ if (!x509_name_data_by_nid_equal(subject, NID_countryName, ++ PV_IBM_Z_SUBJECT_COUNTRY_NAME)) ++ return FALSE; ++ ++ if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, ++ PV_IBM_Z_SUBJECT_STATE)) ++ return FALSE; ++ ++ if (!x509_name_data_by_nid_equal(subject, NID_localityName, ++ PV_IBM_Z_SUBJECT_LOCALITY_NAME)) ++ return FALSE; ++ ++ if (!x509_name_data_by_nid_equal(subject, NID_organizationName, ++ PV_IBM_Z_SUBJECT_ORGANIZATION_NAME)) ++ return FALSE; ++ ++ if (!x509_name_data_by_nid_equal(subject, NID_commonName, ++ PV_IBM_Z_SUBJECT_COMMON_NAME)) ++ return FALSE; ++ ++ if (x509_name_get_data0_by_NID(subject, NID_organizationalUnitName, ++ &data, &data_len) < 0) ++ return FALSE; ++ ++ /* Make sure that data_str is null-terminated as in general it cannot be ++ * assumed that @data is null-terminated. ++ */ ++ data_str = g_strndup((const gchar *)data, data_len); ++ if (!g_str_has_suffix(data_str, ++ PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint nids[], ++ gsize nids_len) ++{ ++ gint entry_count = X509_NAME_entry_count(name); ++ g_autoptr(X509_NAME) ret = NULL; ++ ++ if (entry_count < 0) ++ return NULL; ++ ++ if (nids_len != (gsize) entry_count) + return NULL; ++ ++ ret = X509_NAME_new(); ++ if (!ret) ++ g_abort(); ++ ++ for (gsize i = 0; i < nids_len; i++) { ++ const X509_NAME_ENTRY *entry = NULL; ++ gint nid = nids[i]; ++ gint lastpos = -1; ++ ++ lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos); ++ if (lastpos == -1) ++ return NULL; ++ ++ entry = X509_NAME_get_entry(name, lastpos); ++ if (!entry) ++ return NULL; ++ ++ if (X509_NAME_add_entry(ret, entry, -1, 0) != 1) ++ return NULL; + } + + return g_steal_pointer(&ret); + } + +-EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, +- GError **err) ++/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory ++ * ordered. The problem is that our certificates are not consistent in the order ++ * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details). ++ * ++ * This function converts a correct X509_NAME into the broken one. The caller is ++ * responsible to free the returned value. ++ */ ++X509_NAME *c2b_name(const X509_NAME *name) + { +- g_autoptr(EVP_PKEY) ret = NULL; +- g_autoptr(X509) cert = NULL; ++ gint nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName, ++ NID_localityName, NID_stateOrProvinceName, NID_commonName }; ++ g_autoptr(X509_NAME) broken_name = NULL; + +- cert = load_certificate(path, err); +- if (!cert) ++ g_assert(name); ++ ++ /* Try to reorder the attributes */ ++ broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids)); ++ if (broken_name) ++ return g_steal_pointer(&broken_name); ++ return X509_NAME_dup((X509_NAME *)name); ++} ++ ++/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */ ++static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err) ++{ ++ const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl); ++ const X509_NAME *issuer_subject = X509_get_subject_name(issuer); ++ AUTHORITY_KEYID *akid = NULL; ++ ++ if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) { ++ g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject, ++ NULL, 0); ++ g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0); ++ ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, ++ _("issuer mismatch:\n%s\n%s"), ++ issuer_subject_str, crl_issuer_str); ++ return -1; ++ } ++ ++ /* If AKID(@crl) is specified it must match with SKID(@issuer) */ ++ akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL); ++ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, ++ _("AKID mismatch")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Verify whether a revocation list @crl is valid and is issued by @cert. For ++ * this multiple steps must be done: ++ * ++ * 1. verify issuer of the CRL matches with the suject name of @cert ++ * 2. verify the validity period of the CRL ++ * 3. verify the signature of the CRL ++ * ++ * Important: This function doesn't verify whether @cert is allowed to issue a ++ * CRL. Returns 0 if @crl is valid and issued by @cert, otherwise -1. ++ */ ++gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, ++ gint verify_flags, GError **err) ++{ ++ EVP_PKEY *pkey = X509_get0_pubkey(cert); ++ ++ g_assert(crl); ++ ++ if (!pkey) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("failed to retrieve public key from the certificate")); ++ return -1; ++ } ++ ++ /* check that the @crl issuer matches with the subject name of @cert*/ ++ if (check_crl_issuer(crl, cert, err) < 0) ++ return -1; ++ ++ /* verify the validity period of the CRL */ ++ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { ++ const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl); ++ const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl); ++ ++ if (!last || !next || check_validity_period(last, next)) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, ++ _("validity period is not valid")); ++ return -1; ++ } ++ } else { ++ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; ++ } ++ ++ /* verify the signature */ ++ if (X509_CRL_verify(crl, pkey) != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, ++ _("signature is not valid")); ++ return -1; ++ } ++ g_assert(verify_flags == 0); ++ return 0; ++} ++ ++/* Given a certificate @cert try to find valid revocation lists in @ctx. If no ++ * valid CRL was found NULL is returned. ++ */ ++STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, ++ GError **err) ++{ ++ g_autoptr(STACK_OF_X509_CRL) ret = NULL; ++ const gint verify_flags = 0; ++ X509_NAME *subject = NULL; ++ ++ subject = X509_get_subject_name(cert); ++ if (!subject) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, ++ _("certificate is malformed")); + return NULL; ++ } + +- if (store && !verify_certificate(store, cert, err)) { +- g_prefix_error(err, +- _("Failed to load host-key document: '%s': "), +- path); ++ ret = X509_STORE_CTX_get1_crls(ctx, subject); ++ if (!ret) { ++ /* Workaround to fix the mismatch between issuer name of the ++ * IBM Z signing CRLs and the IBM Z signing key subject name. ++ */ ++ g_autoptr(X509_NAME) broken_subject = c2b_name(subject); ++ ++ ret = X509_STORE_CTX_get1_crls(ctx, broken_subject); ++ if (!ret) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, ++ _("no CRL found")); ++ return NULL; ++ } ++ } ++ ++ /* Filter out non-valid CRLs for @cert */ ++ for (gint i = 0; i < sk_X509_CRL_num(ret); i++) { ++ X509_CRL *crl = sk_X509_CRL_value(ret, i); ++ ++ g_assert(crl); ++ ++ /* If @crl is not valid remove it from the array and log a ++ * warning. ++ */ ++ if (check_crl_valid_for_cert(crl, cert, verify_flags, err) < 0) { ++ g_assert(err); ++ g_warning(_("CRL is not valid: %s"), (*err)->message); ++ g_clear_error(err); ++ ++ /* Remove this certificate from the list and change i-- as the ++ * array has changed - this is not beautfiul, but right now the ++ * easiest solution I came up with ++ */ ++ if (sk_X509_CRL_delete(ret, i--) != crl) ++ g_abort(); ++ ++ g_clear_pointer(&crl, X509_CRL_free); ++ } ++ } ++ ++ if (sk_X509_CRL_num(ret) < 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, ++ _("no valid CRL found")); + return NULL; + } ++ return g_steal_pointer(&ret); ++} ++ ++/* Return a list of all IBM Z signing key certificates in @certs and remove them ++ * from the chain. Return empty stack if no IBM Z signing key is found. ++ */ ++STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs) ++{ ++ g_autoptr(STACK_OF_X509) ret = sk_X509_new_null(); ++ ++ for (gint i = 0; i < sk_X509_num(certs); i++) { ++ X509 *cert = sk_X509_value(certs, i); ++ ++ g_assert(cert); ++ ++ if (!has_ibm_signing_subject(cert)) ++ continue; ++ ++ /* Remove this certificate from the list and change i-- as the ++ * array has changed - this is not beautfiul, but right now the ++ * easiest solution I came up with. ++ */ ++ if (sk_X509_delete(certs, i--) != cert) ++ g_abort(); ++ ++ if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0) ++ g_abort(); ++ } ++ ++ return g_steal_pointer(&ret); ++} ++ ++X509_STORE *store_setup(const gchar *root_ca_path, const gchar * const *crl_paths, ++ GError **err) ++{ ++ g_autoptr(X509_STORE) store = X509_STORE_new(); ++ ++ g_assert(store); ++ ++ /* if @root_ca_path != NULL use the specified root CA only, otherwise use the ++ * default root CAs found on the system ++ */ ++ if (root_ca_path) { ++ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); ++ int count; ++ ++ if (!lookup) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("X509 store initialization failed")); ++ return NULL; ++ } ++ ++ count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM); ++ if (count > 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_LOAD_ROOT_CA, ++ _("multiple certificates in one PEM file is not supported: '%s'"), ++ root_ca_path); ++ return NULL; ++ } else if (count < 1) { ++ count = X509_load_cert_file(lookup, root_ca_path, ++ X509_FILETYPE_ASN1); ++ if (count != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_LOAD_ROOT_CA, ++ _("failed to load root certificate from '%s'"), ++ root_ca_path); ++ return NULL; ++ } ++ } ++ } else { ++ /* Load certificates into @store from the hardcoded OpenSSL ++ * default paths ++ */ ++ if (X509_STORE_set_default_paths(store) != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, ++ _("failed to load system root certificates")); ++ return NULL; ++ } ++ } ++ ++ /* Error out if a CRL file was provided that has not at least one CRL*/ ++ if (load_crls_to_store(store, crl_paths, TRUE, err) < 0) ++ return NULL; ++ ++ return g_steal_pointer(&store); ++} ++ ++int store_set_verify_param(X509_STORE *store, GError **err) ++{ ++ g_autoptr(X509_VERIFY_PARAM) param = NULL; ++ unsigned long flags = X509_V_FLAG_CRL_CHECK | ++ X509_V_FLAG_CRL_CHECK_ALL | ++ X509_V_FLAG_TRUSTED_FIRST | ++ X509_V_FLAG_CHECK_SS_SIGNATURE | ++ X509_V_FLAG_X509_STRICT | ++ X509_V_FLAG_POLICY_CHECK; ++ ++ /* Create a X509_VERIFY_PARAM structure, which specifies which checks ++ * should be done by the certificate verification operation ++ */ ++ param = X509_VERIFY_PARAM_new(); ++ if (!param) ++ g_abort(); ++ ++ /* The maximum depth level of the chain of trust for the verification of ++ * the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert) ++ * intermediate CA -> (DigiCert) root CA ++ */ ++ X509_VERIFY_PARAM_set_depth(param, 2); ++ ++ /* Set minimum allowed security level to at least 112 bits. */ ++ X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL); ++ ++ /* Set verification purpose to 'Any Purpose' and specify that the ++ * associated trust setting of the default purpose should be used. ++ */ ++ if (X509_VERIFY_PARAM_set_purpose(param, ++ X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1) ++ goto error; ++ ++ /* Each certificate from the chain of trust must be checked against a ++ * CRL to see if it has been revoked. In addition, use trusted ++ * certificates first mode, check signature of the last certificate, ++ * strict mode, and verify the policies. ++ */ ++ if (X509_VERIFY_PARAM_set_flags(param, flags) != 1) ++ goto error; ++ ++ if (X509_STORE_set1_param(store, param) != 1) ++ goto error; ++ ++ return 0; ++ ++error: ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("X509 store initialization failed")); ++ return -1; ++} ++ ++/* @cert_paths must contain at least one element, otherwise an error is ++ * reported. ++ */ ++GSList *load_certificates(const gchar *const *cert_paths, GError **err) ++{ ++ g_autoslist(x509_with_path) ret = NULL; ++ ++ for (const gchar *const *iterator = cert_paths; ++ iterator != NULL && *iterator != NULL; iterator++) { ++ const gchar *cert_path = *iterator; ++ g_autoptr(X509) cert = NULL; ++ ++ g_assert(cert_path); ++ ++ cert = load_cert_from_file(cert_path, err); ++ if (!cert) ++ return NULL; ++ ++ ret = g_slist_append(ret, x509_with_path_new(cert, cert_path)); ++ } ++ if (!ret) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, ++ _("no certificates specified")); ++ return NULL; ++ } ++ ++ return g_steal_pointer(&ret); ++} ++ ++static X509 *get_cert(const x509_with_path *cert_with_path, G_GNUC_UNUSED GError **err) ++{ ++ g_autoptr(X509) cert = NULL; ++ ++ g_assert(cert_with_path && cert_with_path->cert); ++ ++ cert = cert_with_path->cert; ++ if (X509_up_ref(cert) != 1) ++ g_abort(); ++ return g_steal_pointer(&cert); ++} ++ ++STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list) ++{ ++ g_autoslist(X509) certs = NULL; ++ g_autoptr(GError) err = NULL; ++ ++ certs = g_slist_map_x509_with_path_X509(x509_with_path_list, ++ get_cert, &err); ++ g_assert_null(err); ++ return g_slist_to_stack_of_X509(&certs); ++} ++ ++x509_with_path *x509_with_path_new(X509 *cert, const gchar *path) ++{ ++ g_autoptr(x509_with_path) ret = g_new(x509_with_path, 1); ++ ++ g_assert(cert && path); ++ ++ if (X509_up_ref(cert) != 1) ++ g_abort(); ++ ret->cert = cert; ++ ret->path = g_strdup(path); ++ return g_steal_pointer(&ret); ++} ++ ++void x509_with_path_free(x509_with_path *cert) ++{ ++ if (!cert) ++ return; ++ ++ X509_free(cert->cert); ++ g_free((gchar *)cert->path); ++ g_free(cert); ++} ++ ++x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls) ++{ ++ g_autoptr(x509_pair) ret = g_new0(x509_pair, 1); ++ ++ g_assert(cert); ++ g_assert(crls); ++ ++ ret->cert = g_steal_pointer(cert); ++ ret->crls = g_steal_pointer(crls); ++ return g_steal_pointer(&ret); ++} ++ ++void x509_pair_free(x509_pair *pair) ++{ ++ if (!pair) ++ return; ++ ++ sk_X509_CRL_pop_free(pair->crls, X509_CRL_free); ++ X509_free(pair->cert); ++ g_free(pair); ++} ++ ++X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, ++ GError **err) ++{ ++ g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new(); ++ ++ if (!ctx || !X509_STORE_CTX_init(ctx, trusted, NULL, chain)) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("X509 store initialization failed: %s"), ++ X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); ++ return NULL; ++ } ++ ++ return g_steal_pointer(&ctx); ++} ++ ++gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err) ++{ ++ gint rc; ++ ++ X509_STORE_CTX_set_cert(ctx, cert); ++ rc = X509_verify_cert(ctx); ++ if (rc != 1) { ++ X509 *tmp_cert = NULL; ++ ++ tmp_cert = X509_STORE_CTX_get_current_cert(ctx); ++ if (tmp_cert) { ++ g_autofree char *subj_name = X509_NAME_oneline( ++ X509_get_subject_name(tmp_cert), NULL, 0); ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INTERNAL, ++ _("failed to verify certificate '%s': %s"), ++ subj_name, ++ X509_verify_cert_error_string( ++ X509_STORE_CTX_get_error(ctx))); ++ } else { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INTERNAL, ++ _("failed to verify certificate: %s"), ++ X509_verify_cert_error_string( ++ X509_STORE_CTX_get_error(ctx))); ++ } ++ ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int security_level_to_bits(int level) ++{ ++ static int security_bits[] = { 0, 80, 112, 128, 192, 256 }; ++ ++ g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits)); ++ ++ return security_bits[level]; ++} ++ ++static ASN1_OCTET_STRING *digicert_assured_id_root_ca; ++ ++const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void) ++{ ++ pv_crypto_init(); ++ return digicert_assured_id_root_ca; ++} ++ ++/* Used for the caching of the downloaded CRLs */ ++static GHashTable *cached_crls; ++ ++void pv_crypto_init(void) ++{ ++ if (digicert_assured_id_root_ca) ++ return; ++ ++ cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, ++ (GDestroyNotify)X509_CRL_free); ++ digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING( ++ NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID); ++} ++ ++void pv_crypto_cleanup(void) ++{ ++ if (!digicert_assured_id_root_ca) ++ return; ++ g_clear_pointer(&cached_crls, g_hash_table_destroy); ++ g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free); ++} ++ ++gint check_chain_parameters(const STACK_OF_X509 *chain, ++ const ASN1_OCTET_STRING *skid, GError **err) ++{ ++ const ASN1_OCTET_STRING *ca_skid = NULL; ++ gint len = sk_X509_num(chain); ++ X509 *ca = NULL; ++ ++ g_assert(skid); ++ /* at least one root and one leaf certificate must be defined */ ++ g_assert(len >= 2); ++ ++ /* get the root certificate of the chain of trust */ ++ ca = sk_X509_value(chain, len - 1); ++ if (!ca) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("no root certificate found")); ++ return -1; ++ } ++ ++ ca_skid = X509_get0_subject_key_id(ca); ++ if (!ca_skid) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, ++ _("malformed root certificate")); ++ return -1; ++ } ++ ++ if (ASN1_STRING_cmp(ca_skid, skid) != 0) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED, ++ _("expecting DigiCert root CA to be used")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* It's almost the same as X509_check_issed from OpenSSL does except that we ++ * don't check the key usage of the potential issuer. This means we check: ++ * 1. issuer_name(cert) == subject_name(issuer) ++ * 2. Check whether the akid(cert) (if available) matches the issuer skid ++ * 3. Check that the cert algrithm matches the subject algorithm ++ * 4. Verify the signature of certificate @cert is using the public key of ++ * @issuer. ++ */ ++static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err) ++{ ++ const X509_NAME *issuer_subject = X509_get_subject_name(issuer); ++ const X509_NAME *cert_issuer = X509_get_issuer_name(cert); ++ AUTHORITY_KEYID *akid = NULL; ++ ++ /* We cannot use X509_NAME_cmp() because it considers the order of the ++ * X509_NAME_Entries. ++ */ ++ if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) { ++ g_autofree char *issuer_subject_str = ++ X509_NAME_oneline(issuer_subject, NULL, 0); ++ g_autofree char *cert_issuer_str = ++ X509_NAME_oneline(cert_issuer, NULL, 0); ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, ++ _("Subject issuer mismatch:\n'%s'\n'%s'"), ++ issuer_subject_str, cert_issuer_str); ++ return -1; ++ } ++ ++ akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); ++ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, ++ _("AKID mismatch")); ++ return -1; ++ } ++ ++ if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, err) < 0) ++ return -1; ++ ++ if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, ++ _("Signature verification failed")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl) ++{ ++ X509_REVOKED *revoked = NULL; ++ gint rc; ++ ++ if (!cert || !crl) ++ g_abort(); ++ ++ rc = X509_CRL_get0_by_serial(crl, &revoked, ++ (ASN1_INTEGER *)X509_get0_serialNumber(cert)); ++ if (rc == 0) ++ return FALSE; ++ ++ if (revoked) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++/* Get the first http[s] URL from a DIST_POINT */ ++static const char *get_first_dp_url(DIST_POINT *dp) ++{ ++ GENERAL_NAMES *general_names; ++ ++ g_assert(dp); ++ ++ if (!dp->distpoint || dp->distpoint->type != 0) ++ return NULL; ++ ++ general_names = dp->distpoint->name.fullname; ++ for (gint i = 0; i < sk_GENERAL_NAME_num(general_names); i++) { ++ GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i); ++ g_autofree const gchar *uri_str = NULL; ++ ASN1_STRING *uri_asn1; ++ const gchar *uri_data; ++ gint uri_data_len; ++ gint type; ++ ++ uri_asn1 = GENERAL_NAME_get0_value(name, &type); ++ if (type != GEN_URI) ++ continue; ++ uri_data_len = ASN1_STRING_length(uri_asn1); ++ if (uri_data_len < 0) ++ continue; ++ uri_data = (const gchar *)ASN1_STRING_get0_data(uri_asn1); ++ /* Make sure that uri_str is null-terminated as in general it ++ * cannot be assumed that @uri_data is null-terminated. ++ */ ++ uri_str = g_strndup(uri_data, ++ (gsize)uri_data_len); ++ if (g_str_has_prefix(uri_str, "http://")) ++ return uri_data; ++ if (g_str_has_prefix(uri_str, "https://")) ++ return uri_data; ++ } ++ return NULL; ++} ++ ++static gboolean insert_crl(X509_NAME *name, X509_CRL *crl) ++{ ++ g_autofree gchar *key = NULL; ++ ++ g_assert(name); ++ ++ key = X509_NAME_oneline(name, NULL, 0); ++ if (!key) ++ g_abort(); ++ if (X509_CRL_up_ref(crl) != 1) ++ g_abort(); ++ return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl); ++} ++ ++/* Caller is responsible for free'ing */ ++static X509_CRL *lookup_crl(X509_NAME *name) ++{ ++ g_autoptr(X509_CRL) crl = NULL; ++ g_autofree gchar *key = NULL; ++ ++ g_assert(name); ++ ++ key = X509_NAME_oneline(name, NULL, 0); ++ if (!key) ++ g_abort(); ++ crl = g_hash_table_lookup(cached_crls, key); ++ if (crl) { ++ if (X509_CRL_up_ref(crl) != 1) ++ g_abort(); ++ return g_steal_pointer(&crl); ++ } ++ return NULL; ++} ++ ++/* Returns empty stack if no CRL downloaded. */ ++static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm) ++{ ++ g_autoptr(STACK_OF_X509_CRL) crls = NULL; ++ g_autoptr(X509_CRL) crl = NULL; ++ /* must not be free'd */ ++ X509 *cert = NULL; ++ ++ crls = sk_X509_CRL_new_null(); ++ if (!crls) ++ g_abort(); ++ cert = X509_STORE_CTX_get_current_cert(ctx); ++ if (!cert) ++ g_steal_pointer(&crls); ++ g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0); ++ crl = lookup_crl(nm); ++ if (!crl) { ++ /* ignore error */ ++ crl = load_crl_by_cert(cert, NULL); ++ if (!crl) ++ return g_steal_pointer(&crls); ++ g_assert_true(insert_crl(nm, crl)); ++ } ++ if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0) ++ g_abort(); ++ return g_steal_pointer(&crls); ++} ++ ++void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack) ++{ ++ if (!stack) ++ return; ++ ++ sk_DIST_POINT_pop_free(stack, DIST_POINT_free); ++} ++ ++void STACK_OF_X509_free(STACK_OF_X509 *stack) ++{ ++ if (!stack) ++ return; ++ ++ sk_X509_pop_free(stack, X509_free); ++} ++ ++void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack) ++{ ++ if (!stack) ++ return; ++ ++ sk_X509_CRL_pop_free(stack, X509_CRL_free); ++} ++ ++/* Downloaded CRLs have a higher precedence than the CRLs specified on the ++ * command line. ++ */ ++static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm) ++{ ++ g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm); ++ ++ if (sk_X509_CRL_num(crls) > 0) ++ return g_steal_pointer(&crls); ++ return X509_STORE_CTX_get1_crls(ctx, nm); ++} ++ ++/* Set up CRL lookup with download support */ ++void store_setup_crl_download(X509_STORE *st) ++{ ++ X509_STORE_set_lookup_crls(st, crls_cb); ++} ++ ++/* Download a CRL using the URI specified in the distribution @crldp */ ++static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **err) ++{ ++ const gchar *uri = get_first_dp_url(crldp); ++ g_autoptr(X509_CRL) crl = NULL; ++ ++ if (!uri) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("no valid URL specified in distribution point")); ++ return NULL; ++ } ++ ++ if (load_crl_from_web(uri, &crl, err) < 0) ++ return NULL; ++ ++ return g_steal_pointer(&crl); ++} ++ ++/* This function returns the first X509_CRL found from the CRL distribution ++ * points specified in @cert. This function could be optimized by filtering ++ * duplicate certificates and/or filtering duplicated URIs. ++ */ ++X509_CRL *load_crl_by_cert(X509 *cert, GError **err) ++{ ++ g_autoptr(STACK_OF_DIST_POINT) crldps = NULL; ++ g_autoptr(X509_CRL) ret = NULL; ++ ++ g_assert(cert); ++ ++ crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); ++ if (!crldps || sk_DIST_POINT_num(crldps) == 0) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRLDP, ++ _("no distribution point found")); ++ return NULL; ++ } ++ ++ for (int i = 0; i < sk_DIST_POINT_num(crldps); i++) { ++ DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i); ++ ++ g_assert(crldp); ++ ++ /* ignore error */ ++ ret = load_crl_by_dist_point(crldp, NULL); ++ if (ret) ++ return g_steal_pointer(&ret); ++ } ++ ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, ++ _("failed to download CRL")); ++ return NULL; ++} ++ ++STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path) ++{ ++ g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null(); ++ if (!ret) ++ g_abort(); ++ ++ for (GSList *iterator = certs_with_path; iterator; ++ iterator = iterator->next) { ++ x509_with_path *cert_with_path = iterator->data; ++ X509 *cert = cert_with_path->cert; ++ g_autoptr(X509_CRL) crl = NULL; ++ ++ g_assert(cert); ++ ++ /* ignore error */ ++ crl = load_crl_by_cert(cert, NULL); ++ if (!crl) ++ continue; ++ ++ if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0) ++ g_abort(); ++ } ++ ++ return g_steal_pointer(&ret); ++} ++ ++/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z ++ * signing certificate/revocation list. This function verifies a host-key ++ * document. To do so multiple steps are required: ++ * ++ * 1. issuer(host_key) == subject(issuer_crt) ++ * 2. Signature verification ++ * 3. @host_key must not be expired ++ * 4. @host_key must not be revoked ++ */ ++gint verify_host_key(X509 *host_key, GSList *issuer_pairs, ++ gint verify_flags, int level, GError **err) ++{ ++ g_assert(host_key); ++ ++ const gint exp_security_bits = security_level_to_bits(level); ++ EVP_PKEY *pkey = X509_get0_pubkey(host_key); ++ gboolean successfully_checked = FALSE; ++ gint pkey_security_bits; ++ ++ if (!pkey) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, ++ _("failed to retrieve public key")); ++ return -1; ++ } ++ ++ /* check key level, if necessary */ ++ pkey_security_bits = EVP_PKEY_security_bits(pkey); ++ if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) { ++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, ++ _("not enough bits of security (%d, %d expected)"), ++ pkey_security_bits, exp_security_bits); ++ return -1; ++ } ++ ++ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { ++ const ASN1_TIME *last = X509_get_notBefore(host_key); ++ const ASN1_TIME *next = X509_get_notAfter(host_key); ++ ++ if (!last || !next || check_validity_period(last, next)) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, ++ _("validity period is not valid")); ++ return -1; ++ } ++ } else { ++ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; ++ } ++ ++ /* Verify that the host_key was issued by a certificate and that it ++ * wasn't revoked. ++ */ ++ for (GSList *iterator = issuer_pairs; iterator; ++ iterator = iterator->next) { ++ const x509_pair *pair = iterator->data; ++ STACK_OF_X509_CRL *issuer_crls = NULL; ++ X509 *issuer_cert = NULL; ++ ++ g_assert(pair); ++ ++ issuer_cert = pair->cert; ++ issuer_crls = pair->crls; ++ ++ g_assert(issuer_cert); ++ ++ /* Verify that the issuer(host_key) == subject(issuer_cert) and ++ * that the signature is valid ++ */ ++ if (check_host_key_issued(host_key, issuer_cert, NULL) < 0) ++ continue; ++ ++ /* Check against CRL */ ++ if (verify_flags & X509_V_FLAG_CRL_CHECK) { ++ gboolean crl_checked = FALSE; ++ ++ verify_flags &= ~X509_V_FLAG_CRL_CHECK; ++ for (gint i = 0; i < sk_X509_CRL_num(issuer_crls); i++) { ++ X509_CRL *issuer_crl = ++ sk_X509_CRL_value(issuer_crls, i); ++ ++ g_assert(issuer_crl); ++ ++ if (is_cert_revoked(host_key, issuer_crl)) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_CERT_REVOKED, ++ _("certificate revoked")); ++ return -1; ++ } ++ ++ crl_checked = TRUE; ++ } ++ ++ if (!crl_checked) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_INTERNAL, ++ _("no valid CRL found")); ++ return -1; ++ } ++ successfully_checked = TRUE; ++ break; ++ } ++ } ++ ++ if (!successfully_checked) { ++ g_set_error(err, PV_CRYPTO_ERROR, ++ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, ++ _("no IBM Z signing key that issued this host-key document found")); ++ return -1; ++ } ++ ++ /* were some unsupported flags specified? */ ++ g_assert(verify_flags == 0); ++ return 0; ++} ++ ++EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, ++ GError **err) ++{ ++ g_autoptr(EVP_PKEY) ret = NULL; + + ret = X509_get_pubkey(cert); + if (!ret) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, +- _("Failed to get public key from host-key document: '%s'"), +- path); ++ _("Failed to get public key from host-key document")); + return NULL; + } + + if (!certificate_uses_correct_curve(ret, nid, err)) { + g_prefix_error(err, +- _("Failed to load host-key document: '%s': "), +- path); ++ _("Host-key document doesn\'t use correct EC curve")); + return NULL; + } + +diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h +index 34418ed..286cf45 100644 +--- a/genprotimg/src/utils/crypto.h ++++ b/genprotimg/src/utils/crypto.h +@@ -11,14 +11,18 @@ + #define PV_UTILS_CRYPTO_H + + #include ++#include + #include + #include + #include + #include + #include ++#include + #include ++#include + #include + #include ++#include + #include + + #include "common.h" +@@ -33,6 +37,9 @@ + #define AES_256_XTS_TWEAK_SIZE 16 + #define AES_256_XTS_KEY_SIZE 64 + ++#define CRL_DOWNLOAD_TIMEOUT_MS 3000 ++#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */ ++ + enum PvCryptoMode { + PV_ENCRYPT, + PV_DECRYPT, +@@ -40,7 +47,34 @@ enum PvCryptoMode { + + typedef GSList HostKeyList; + ++/* play nice with g_autoptr */ ++typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT; ++typedef STACK_OF(X509) STACK_OF_X509; ++typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL; ++ ++void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack); ++void STACK_OF_X509_free(STACK_OF_X509 *stack); ++void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack); ++ ++typedef struct { ++ X509 *cert; ++ const gchar *path; ++} x509_with_path; ++ ++x509_with_path *x509_with_path_new(X509 *cert, const gchar *path); ++void x509_with_path_free(x509_with_path *cert); ++ ++typedef struct { ++ X509 *cert; ++ STACK_OF_X509_CRL *crls; ++} x509_pair; ++ ++x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls); ++void x509_pair_free(x509_pair *pair); ++ + /* Register auto cleanup functions */ ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) +@@ -51,10 +85,18 @@ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free); ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free); ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free); + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free) + + union cmp_index { + struct { +@@ -79,8 +121,37 @@ struct cipher_parms { + const Buffer *iv_or_tweak; + }; + +-EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, +- GError **err); ++int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, ++ gint verify_flags, GError **err); ++void pv_crypto_init(void); ++void pv_crypto_cleanup(void); ++const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void); ++gint verify_host_key(X509 *host_key, GSList *issuer_pairs, ++ gint verify_flags, int level, GError **err); ++X509 *load_cert_from_file(const char *path, GError **err); ++X509_CRL *load_crl_from_file(const gchar *path, GError **err); ++GSList *load_certificates(const gchar *const *cert_paths, GError **err); ++STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list); ++X509_STORE *store_setup(const gchar *root_ca_path, ++ const gchar * const *crl_paths, ++ GError **err); ++int store_set_verify_param(X509_STORE *store, GError **err); ++X509_CRL *load_crl_by_cert(X509 *cert, GError **err); ++STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path); ++gint check_chain_parameters(const STACK_OF_X509 *chain, ++ const ASN1_OCTET_STRING *skid, GError **err); ++X509_NAME *c2b_name(const X509_NAME *name); ++ ++STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs); ++STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, ++ GError **err); ++X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, ++ GError **err); ++gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err); ++X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err); ++void store_setup_crl_download(X509_STORE *st); ++EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err); ++ + Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); + Buffer *generate_aes_key(guint size, GError **err); + Buffer *generate_aes_iv(guint size, GError **err); +diff --git a/genprotimg/src/utils/curl.c b/genprotimg/src/utils/curl.c +new file mode 100644 +index 0000000..a8ef3f6 +--- /dev/null ++++ b/genprotimg/src/utils/curl.c +@@ -0,0 +1,121 @@ ++/* ++ * Libcurl utils ++ * ++ * Copyright IBM Corp. 2020 ++ * ++ * s390-tools is free software; you can redistribute it and/or modify ++ * it under the terms of the MIT license. See LICENSE for details. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "lib/zt_common.h" ++#include "pv/pv_error.h" ++ ++#include "curl.h" ++ ++struct UserData { ++ GByteArray *buffer; ++ guint max_size; ++}; ++ ++static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) ++{ ++ g_assert(userdata); ++ struct UserData *data = (struct UserData *)userdata; ++ GByteArray *buffer = data->buffer; ++ guint64 actual_size; ++ size_t err; ++ ++ g_assert(buffer); ++ ++ if (!g_uint64_checked_mul(&actual_size, size, nmemb)) ++ g_abort(); ++ ++ /* Signal an error condition by returning a amount that differs ++ * from the amount passed to the callback. This results in a ++ * CURLE_WRITE_ERROR. ++ */ ++ err = actual_size + 1; ++ ++ if (actual_size > G_MAXUINT) ++ return err; ++ ++ data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size); ++ if (data->buffer->len > data->max_size) ++ return err; ++ ++ return actual_size; ++} ++ ++gint curl_init(void) ++{ ++ if (curl_global_init(CURL_GLOBAL_ALL) != 0) ++ return -1; ++ return 0; ++} ++ ++void curl_cleanup(void) ++{ ++ curl_global_cleanup(); ++} ++ ++GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, ++ GError **err) ++{ ++ g_autoptr(GByteArray) ret = NULL; ++ g_autoptr(CURL) handle = NULL; ++ g_autofree gchar *agent = NULL; ++ struct UserData userdata; ++ CURLcode rc; ++ ++ /* set up curl session */ ++ handle = curl_easy_init(); ++ if (!handle) ++ g_abort(); ++ ++ /* follow redirection */ ++ rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING); ++ rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ ret = g_byte_array_new(); ++ userdata.buffer = ret; ++ userdata.max_size = max_size; ++ rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ rc = curl_easy_setopt(handle, CURLOPT_URL, url); ++ if (rc != CURLE_OK) ++ goto curl_err; ++ ++ rc = curl_easy_perform(handle); ++ if (rc != CURLE_OK) { ++ g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED, ++ _("download failed: %s"), curl_easy_strerror(rc)); ++ return NULL; ++ } ++ ++ return g_steal_pointer(&ret); ++curl_err: ++ g_set_error(err, PV_ERROR, ++ PV_ERROR_CURL_INIT_FAILED, ++ _("cURL initialization failed: %s"), ++ curl_easy_strerror(rc)); ++ return NULL; ++} +diff --git a/genprotimg/src/utils/curl.h b/genprotimg/src/utils/curl.h +new file mode 100644 +index 0000000..4ec1c11 +--- /dev/null ++++ b/genprotimg/src/utils/curl.h +@@ -0,0 +1,25 @@ ++/* ++ * Libcurl utils ++ * ++ * Copyright IBM Corp. 2020 ++ * ++ * 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 PV_UTILS_LIBCURL_H ++#define PV_UTILS_LIBCURL_H ++ ++#include ++#include ++ ++#include "common.h" ++ ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup) ++ ++GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, ++ GError **err); ++gint curl_init(void); ++void curl_cleanup(void); ++ ++#endif /* PV_UTILS_LIBCURL_H */ +-- +2.26.2 + + +From 4964155a921e38d971bc857dbe95ce60aaffef45 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 7 Dec 2020 14:34:51 +0100 +Subject: [PATCH 6/9] s390-tools: add hsci tool (#1847434) + +Summary: s390-tools: add hsci tool +Description: hsci is used to control and show HSCI (HiperSockets Converged + Interfaces) settings. A HiperSockets interface and an external + network interface are converged to an HSCI interface. +Upstream-ID: 0566a492aec764c5405a94185fd6117fcd520249 +--- + Makefile | 2 +- + README.md | 3 + + hsci/Makefile | 16 ++ + hsci/hsci | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++ + hsci/hsci.8 | 100 ++++++++++++ + 5 files changed, 561 insertions(+), 1 deletion(-) + create mode 100644 hsci/Makefile + create mode 100644 hsci/hsci + create mode 100644 hsci/hsci.8 + +diff --git a/Makefile b/Makefile +index cc277b8..cfbbd95 100644 +--- a/Makefile ++++ b/Makefile +@@ -9,7 +9,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ + 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 zpcictl \ +- genprotimg lsstp ++ genprotimg lsstp hsci + + SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) + +diff --git a/README.md b/README.md +index 6fb6831..5a2153c 100644 +--- a/README.md ++++ b/README.md +@@ -249,6 +249,9 @@ Package contents + Management Foundation - Web Edition, and is used to manage keys in an + enterprise. + ++ * hsci: ++ Manage HiperSockets Converged Interfaces (HSCI). ++ + For more information refer to the following publications: + + * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" +diff --git a/hsci/Makefile b/hsci/Makefile +new file mode 100644 +index 0000000..6f7474c +--- /dev/null ++++ b/hsci/Makefile +@@ -0,0 +1,16 @@ ++include ../common.mak ++ ++all: ++ ++install: hsci ++ $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ ++ < hsci >$(DESTDIR)$(BINDIR)/hsci; \ ++ chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ ++ chmod 755 $(DESTDIR)$(BINDIR)/hsci; \ ++ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hsci.8 \ ++ $(DESTDIR)$(MANDIR)/man8 ++ ++clean: ++ ++.PHONY: all install clean +diff --git a/hsci/hsci b/hsci/hsci +new file mode 100644 +index 0000000..9a56aa0 +--- /dev/null ++++ b/hsci/hsci +@@ -0,0 +1,441 @@ ++#!/bin/bash ++# ++# hsci - Tool to manage HiperSockets Converged Interfaces (HSCI) ++# ++# Copyright IBM Corp. 2020 ++# ++# s390-tools is free software; you can redistribute it and/or modify ++# it under the terms of the MIT license. See LICENSE for details. ++# ++ ++hsdev="" ++ndev="" ++hsci="" ++hsdev_mac="" ++hsif_pnetid="" ++netif_pnetid="" ++hsci_pnetid="" ++ ++function usage { ++cat <<-EOD ++Usage: hsci COMMAND [OPTION] ++ ++This tool is designed to control and show HSCI (HiperSockets Converged ++Interfaces) settings. A HiperSockets interface and an external network ++ ++COMMANDS ++ add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface ++ del HSCI_NAME Deletes an HSCI interface ++ show Lists the configured HSCI interfaces ++ ++OPTIONS: ++ -v, --version Prints the version number of the hsci tool and exits ++ -h, --help Displays the help information for the command ++EOD ++} ++ ++function prereqs_check { ++ if ! [ -x "$(command -v ip)" ]; then ++ echo "Error: No iproute2 installed on this system" >&2 ++ return 1 ++ fi ++} ++ ++function check_pnetids { ++ # get PNETID of the HS ++ local hsif_pnetids="" ++ local netif_pnetids="" ++ ++ if [ -e /sys/class/net/$hsdev/device/util_string ]; then ++ hsif_pnetids="$(cat /sys/class/net/$hsdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" ++ else ++ if [ -e /sys/class/net/$hsdev/device/chpid ]; then ++ chpid="$(cat /sys/class/net/$hsdev/device/chpid | tr [:upper:] [:lower:])" ++ hsif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" ++ fi ++ fi ++ if [ "$hsif_pnetids" != "" ]; then ++ port_hsif="$(cat /sys/class/net/$hsdev/dev_port)" ++ (( idx=16*$port_hsif+1 )) ++ (( end=$idx+15 )) ++ hsif_pnetid="$(echo "$hsif_pnetids" | cut -c $idx-$end | tr -d ' ')" ++ fi ++ ++ # get PNETID of the NET_DEV ++ if [ -e /sys/class/net/$ndev/device/util_string ]; then ++ netif_pnetids="$(cat /sys/class/net/$ndev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" ++ else ++ if [ -e /sys/class/net/$ndev/device/chpid ]; then ++ chpid="$(cat /sys/class/net/$ndev/device/chpid | tr [:upper:] [:lower:])" ++ netif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" ++ fi ++ fi ++ if [ "$netif_pnetids" != "" ]; then ++ port_netif="$(cat /sys/class/net/$ndev/dev_port)" ++ (( idx=16*$port_netif+1 )) ++ (( end=$idx+15 )) ++ netif_pnetid="$(echo "$netif_pnetids" | cut -c $idx-$end | tr -d ' ')" ++ fi ++ ++ #Check PNETIDs ++ if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then ++ echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2 ++ return 1 ++ fi ++ ++ if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" == "$hsif_pnetid" ]; then ++ hsci_pnetid=$hsif_pnetid ++ fi ++} ++ ++function verify_precon { ++ echo "Verifying net dev $ndev and HiperSockets dev $hsdev" ++ ++ if [ ! -e /sys/class/net/$hsdev ]; then ++ echo "Error: $hsdev does not exist" >&2 ++ return 1 ++ fi ++ if [ "$(cat /sys/class/net/$hsdev/device/card_type)" != "HiperSockets" ]; then ++ echo "Error: $hsdev is not a HiperSockets device" >&2 ++ return 1 ++ fi ++ if [ "$(cat /sys/class/net/$hsdev/device/layer2)" != "1" ]; then ++ echo "Error: $hsdev is not in layer 2 mode" >&2 ++ return 1 ++ fi ++ if [ ! -e /sys/class/net/$hsdev/device/vnicc/bridge_invisible ]; then ++ echo "Error: Missing vnic-characteristics support" >&2 ++ return 1 ++ fi ++ if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible)" == "n/a" ]; then ++ echo "Error: $hsdev does not support vnicc" >&2 ++ return 1 ++ fi ++ if [ $(ip link show $hsdev | grep UP | wc -l) -eq 0 ]; then ++ echo "Error: $hsdev is not in state UP" >&2 ++ return 1 ++ fi ++ if [ $(bridge -d link show dev $hsdev self | grep learning_sync | wc -l) -eq 0 ]; then ++ echo "Error: $hsdev does not support attribute learning_sync" >&2 ++ return 1 ++ fi ++ if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then ++ echo "Error: $hsdev is already a bridge port" >&2 ++ return 1 ++ fi ++ ++ #Pre-verify net_dev ++ if [ ! -e /sys/class/net/$ndev ]; then ++ echo "Error: $ndev does not exist" >&2 ++ return 1 ++ fi ++ if [ "$(cat /sys/class/net/$ndev/device/card_type)" == "HiperSockets" ]; then ++ echo "Error: $ndev is also a HiperSockets device" >&2 ++ return 1 ++ fi ++ if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then ++ echo "Error: $ndev is not in state UP" >&2 ++ return 1 ++ fi ++ if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then ++ echo "Error: $ndev is already a bridge port" >&2 ++ return 1 ++ fi ++ ++ #Check PNETIDs ++ check_pnetids ++ if [ $? -ne 0 ]; then ++ return $? ++ fi ++ ++ return 0 ++} ++ ++function clean_up { ++ bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 ++ echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1 ++ bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 ++ ip link del $hsci >/dev/null 2>&1 ++} ++ ++############################################################################## ++## add a new HSCI interface ++############################################################################## ++function add_hsci { ++ ++ if [ $# != 2 ]; then ++ echo "hsci: Invalid parameters" >&2 ++ echo "Use 'hsci --help' for more information" >&2 ++ return 1 ++ fi ++ hsdev=$1 ++ ndev=$2 ++ ++ #### Verify preconditions ++ verify_precon ++ if [ $? -ne 0 ]; then ++ return $? ++ fi ++ ++ hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)" ++ hsci=hsci$hsci_postfix ++ ++ echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev" ++ ++ #### Create bridge ++ ip link add name $hsci type bridge stp_state 0 >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Could not create a bridge" >&2 ++ return 1 ++ fi ++ ++ #### Prepare hsdev ++ # Set VNICC of hsdev to invisible ++ #(mandatory for co-existence with HS-OSA bridges!) ++ echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible ++ ++ #### Create bridge ports ++ ip link set dev $ndev master $hsci >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Could not set master for $ndev" >&2 ++ clean_up ++ return 1 ++ fi ++ ip link set dev $hsdev master $hsci >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Could not set master for $hsdev" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # no forwarding between ndev and hsdev -> isolated on ++ # ndev is default for outgoing unknown targets -> flood on ++ # no need to learn external LAN targets into fdb -> learning off ++ bridge link set dev $ndev isolated on learning off flood on mcast_flood on >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set bridge attributes on $ndev" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # no forwarding between ndev and hsdev -> isolated on ++ # fdb will be populated by dev-to-bridge-notification, no need to learn ++ # -> learning off ++ # only send to hsdev, if listed in fdb -> flood off ++ # don't send MC/BC on hsdev -> mcast_flood off ++ bridge link set dev $hsdev isolated on learning off flood off mcast_flood off >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set bridge attributes on $hsdev" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # NOTE: Although not required, BCs will be sent out on hsdev. ++ # NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS. ++ ++ hsdev_mac="$(cat /sys/class/net/$hsdev/address)" ++ echo "Set $hsdev MAC $hsdev_mac on $ndev and $hsci" ++ ++ # set HS MAC on OSA as secondary MAC ++ bridge fdb add $hsdev_mac dev $ndev >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set HS MAC on OSA as secondary MAC" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # set HS MAC (common MAC) on HSCI as primary MAC ++ ip link set address $hsdev_mac dev $hsci >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set HiperSockets MAC (common MAC) on HSCI as primary MAC" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # use hsdev MTU ++ if [ -e /sys/class/net/$hsdev/mtu ]; then ++ hs_mtu="$(cat /sys/class/net/$hsdev/mtu)" ++ ip link set dev $hsci mtu $hs_mtu >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set MTU for $hsci " >&2 ++ clean_up ++ return 1 ++ fi ++ fi ++ ip link set dev $hsci up >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to set $hsci up" >&2 ++ clean_up ++ return 1 ++ fi ++ ++ # Turn on device for bridge notification ++ bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to turn on device for bridge notification" >&2 ++ clean_up ++ return 1 ++ fi ++ echo "Successfully added HSCI interface $hsci" ++ return 0 ++} ++ ++############################################################################## ++## Delete HSCI ++############################################################################## ++ ++function del_hsci { ++ if [ $# != 1 ]; then ++ echo "hsci: invalid parameters" >&2 ++ echo "Use 'hsci --help' for more information" >&2 ++ return 1 ++ fi ++ hsci=$1 ++ if [ $(ip link show dev $hsci | wc -l) -eq 0 ]; then ++ echo "Error: $hsci does not exit" >&2 ++ return 1 ++ fi ++ if [ $(ip link show | grep "master $hsci" | wc -l) -eq 0 ]; then ++ echo "Error: $hsci is not an active HSCI interface" >&2 ++ return 1 ++ fi ++ ++ bports="$(ip link show | grep "master $hsci" | awk '{print $2}')" ++ for bport in $bports; do ++ if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then ++ hsdev=${bport%:} ++ else ++ ndev=${bport%:} ++ fi ++ done ++ if [ "$hsdev" == "" ]; then ++ echo "Error: $hsci has no active HiperSockets port" >&2 ++ return 1 ++ fi ++ echo "Deleting HSCI interface $hsci with the HiperSockets $hsdev and the external $ndev" ++ ++ bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to turn off learning_sync on $hsdev" >&2 ++ return 1 ++ fi ++ echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible ++ ++ hsdev_mac="$(cat /sys/class/net/$hsdev/address)" ++ echo "Deleting $hsev MAC $hsdev_mac on $ndev" ++ bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to delete $hsev MAC $hsdev_mac on $ndev" >&2 ++ return 1 ++ fi ++ ++ ip link del $hsci >/dev/null 2>&1 ++ if [ $? -ne 0 ]; then ++ echo "Error: Failed to delete $hsci" >&2 ++ return 1 ++ fi ++ echo "Successfully deleted device $hsci" ++ ++ return 0 ++} ++ ++############################################################################## ++## Show HSCI ++############################################################################## ++ ++function list_active { ++ hsdev=$1 ++ local ext="" ++ ++ hsci="$(ip link show dev $hsdev | awk '{for(x=1;x&2 ++ echo "Use 'hsci --help' for more information" >&2 ++ return 1 ++ fi ++ header=0 ++ ++ for hs_net_dev in $(ls -1 /sys/class/net/); do ++ list_one $hs_net_dev ++ done ++ ++ return 0 ++} ++ ++#============================================================================== ++ ++function print_version() ++{ ++ echo "hsci utility: version %S390_TOOLS_VERSION%" ++ echo "Copyright IBM Corp. 2020" ++} ++ ++############################################################################## ++##### Main ++############################################################################## ++prereqs_check ++ ++args="$(getopt -u -o hv -l help,version -- $*)" ++[ $? -ne 0 ] && exit 2 ++set -- $args ++while true; do ++ case $1 in ++ -v | --version) ++ print_version ++ exit 0 ++ ;; ++ -h | --help) ++ usage ++ exit 0 ++ ;; ++ --) ++ ;; ++ add) shift ++ add_hsci "$@" ++ exit $? ++ ;; ++ del) shift ++ del_hsci "$@" ++ exit $? ++ ;; ++ show) shift ++ show_hsci "$@" ++ exit $? ++ ;; ++ *) echo "hsci: Please specify a valid command or option" >&2 ++ echo "Use 'hsci --help' for more information" >&2 ++ exit 1 ++ esac ++ shift ++done ++ +diff --git a/hsci/hsci.8 b/hsci/hsci.8 +new file mode 100644 +index 0000000..fc17053 +--- /dev/null ++++ b/hsci/hsci.8 +@@ -0,0 +1,100 @@ ++.\" Copyright IBM Corp. 2020 ++ ++.TH HSCI 8 "November 2020" "s390-tools" "Linux Programmer's Manual" ++ ++ ++.SH NAME ++.B hsci ++\- control and show HSCI settings. ++ ++ ++.SH SYNOPSIS ++.B hsci add ++.I HSDEV ++.I NETDEV ++.br ++.B hsci del ++.I HSCINAME ++.br ++.B hsci show ++.br ++.B hsci [\-hv] ++ ++.SH DESCRIPTION ++.BI hsci ++is used to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. ++ ++.SH COMMANDS ++.TP ++.B add \fIHSDEV\fR \fINETDEV\fR ++.RS .4i ++.PP ++Adds an HSCI interface ++.PP ++.I HSDEV ++is the interface name of the HiperSockets device to be converged into the HSCI interface. ++.PP ++.I NETDEV ++is the interface name of the external network device to be converged into the HSCI interface. ++.RE ++ ++.TP ++.B del \fIHSCINAME\fR ++.RS .4i ++.PP ++Deletes an HSCI interface ++.PP ++.I HSCINAME ++is the name of the HSCI interface for the HiperSockets device and the external network device. ++.RE ++ ++.TP ++.B show ++.RS .4i ++.PP ++Lists the configured HSCI interfaces. ++.RE ++ ++.SH OPTIONS ++.TP ++.BR \-v ", " \-\-version ++Prints the version number of hsci and exits. ++.TP ++.BR \-h ", " \-\-help ++Displays the help information for the command. ++ ++.SH EXIT CODES ++.TP ++.BR "0" ++The hsci command ran successfully. ++ ++.TP ++.BR "1" ++An error occurred. ++ ++.SH EXAMPLE ++.BR "hsci show" ++.TP ++.RB ++Lists the configured HSCI interfaces: ++.RS 1.2i ++ ++HSCI PNET_ID HiperSockets External ++.br ++----------------------------------------- ++.br ++hsci8410 NET1 enc8410 encb040 ++ ++.RE ++ ++.SH SEE ALSO ++.nf ++ip(8), bridge(8) ++.fi ++ ++.SH AUTHOR ++.nf ++Written by Alexandra Winter ++ Wenjia Zhang ++.fi ++ +-- +2.26.2 + + +From 0b71c216d158a21c9905dad773a22c456d80b32d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 7 Dec 2020 14:36:11 +0100 +Subject: [PATCH 7/9] zcryptstats: Fix handling of partial results with many + domains (#1901962) + +Description: zcryptstats: Fix handling of partial results with many domains +Symptom: Running zcryptstats when many domains are available per cryto + card does not produce any output, and is hanging in a loop. +Problem: When many domains per card are available, then the results of + the SCDMD CHSC call may not fit into the output area, and a + partial result is returned. The further results must be + retrieved with another CHSC call. Fix the code to pass the + correct next-domain to the subsequent CHSC call of a partial + response. Otherwise the same set of domains 1 to n are retrieved + again, resulting in an infinite loop, because this will always + produce a partial result. +Solution: Fix the code to pass the correct next-domain to the subsequent + CHSC call of a partial response. +Reproduction: Run zcryptstats on a system with many domains per card. +Upstream-ID: cf2311f1f1de17435b49ba8c8697be91705ba031 +--- + zconf/zcrypt/zcryptstats.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c +index 3bb2078..2456e5d 100644 +--- a/zconf/zcrypt/zcryptstats.c ++++ b/zconf/zcrypt/zcryptstats.c +@@ -1178,8 +1178,14 @@ static int get_apqn_measurement_data(uint8_t card) + 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; ++ if (scdmd_area.response.p) { ++ scdmd_area.request.first_drid = ++ scdmd_area.response.crid; ++ } else { ++ 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; +@@ -1217,10 +1223,6 @@ static int get_apqn_measurement_data(uint8_t card) + 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; +-- +2.26.2 + + +From 42d4f61d02b268f6a4e04a18188905ffbd95c9bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 7 Dec 2020 14:37:26 +0100 +Subject: [PATCH 8/9] dasdfmt: Fix bad file descriptor error when running on + symlinks (#1901963) + +Description: dasdfmt: Fix bad file descriptor error when running on symlinks +Symptom: When calling dasdfmt on device node symlinks like + /dev/disk/by-id/ccw-0X9300, dasdfmt fails with "dasdfmt: the + ioctl to get the blocksize of the device failed (Bad file + descriptor)" +Problem: This is because before the actual formatting process starts, the + disk will be disabled calling the BIODASDDISABLE ioctl, + resulting in the removal of the symlink. Trying to open this + file later in the process to retrieve e.g. blocksize information + results in the mentioned error, as the file doesn't exist any + longer. +Solution: In order to fix this without modifying the behaviour of libdasd, + introduce the two global variables dev_node and dev_path. + dev_path is the original device path entered by the user. + dev_node on the other hand is the reliable device node under + /dev/block/ using the major and minor numbers and is determined + in get_device_name(). The dev_path is used for message output + only and the dev_node variable is used for the actual disk + operations. +Reproduction: Simply call dasdfmt on a device node symlink in /dev/disk/ such + as /dev/disk/by-id/ccw-0X9300 for example. +Upstream-ID: 148d3f9b64da599adf453baf65e7a8596e2e7d97 +Upstream-ID: cad450fdf9a9dd2562eec27157e4fd133a98813e +--- + dasdfmt/dasdfmt.c | 2 +- + dasdfmt/dasdfmt.h | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/dasdfmt/dasdfmt.c b/dasdfmt/dasdfmt.c +index a424f3c..5665f64 100644 +--- a/dasdfmt/dasdfmt.c ++++ b/dasdfmt/dasdfmt.c +@@ -1637,7 +1637,7 @@ int main(int argc, char *argv[]) + /* End of options string - start of devices list */ + break; + default: +- error("Try '%s --help' for more information."); ++ error("Try '%s --help' for more information.", prog_name); + } + + if (rc == -1) +diff --git a/dasdfmt/dasdfmt.h b/dasdfmt/dasdfmt.h +index 9d35eed..4d6fbb5 100644 +--- a/dasdfmt/dasdfmt.h ++++ b/dasdfmt/dasdfmt.h +@@ -41,6 +41,10 @@ static const char mode_str[3][10] = { + "Full", "Quick", "Expand" + }; + ++/* Report error, free memory, and exit */ ++static void error(const char *format, ...) ++ __attribute__((__noreturn__, __format__(__printf__, 1, 2))); ++ + #define DASD_PARTN_BITS 2 + #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + +-- +2.26.2 + + +From 2e86043c334aa5a48878eda656e292fb408dd912 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Mon, 7 Dec 2020 14:38:35 +0100 +Subject: [PATCH 9/9] zkey: Fix KMS plugin configuration to store APQNs + correctly. (#1901968) + +Description: zkey: Fix KMS plugin configuration to store APQNs correctly. +Symptom: When a KMS plugin is configured with APQNs, the set of + APQNs is stored per card type, i.e. the set of CCA APQNs and + the set of EP11 APQNs is stored separately in the KMS + plugin configuration file. Unfortunately, the names of the + configuration properties are swapped, so that CCA APQNs are + stored as EP11 APQNs, and vice versa. + This does not cause any malfunction as of today, however + if this is fixed later, while a KMS plugin configuration + already exists, then the KMS plugin will fail to work once + the fix is applied. A KMS plugin reconfiguration would then + be needed to make the plugin work again. +Problem: The KMS configuration property names to store the CCA and + EP11 APQNs are incorrect, i.e. swapped. +Solution: Correct the KMS configuration property names. +Reproduction: Configure a KMS plugin with APQNs and check the KMS config + file. +Upstream-ID: 07d181e29b484108bce5ea07c1561ffb62a1b56e +--- + zkey/kms.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/zkey/kms.c b/zkey/kms.c +index b2ce165..85e3ff2 100644 +--- a/zkey/kms.c ++++ b/zkey/kms.c +@@ -46,8 +46,8 @@ + #define KMS_CONFIG_PROP_KMS "kms" + #define KMS_CONFIG_PROP_KMS_CONFIG "config" + #define KMS_CONFIG_PROP_APQNS "apqns" +-#define KMS_CONFIG_PROP_CCA_APQNS "ep11_apqns" +-#define KMS_CONFIG_PROP_EP11_APQNS "cca_apqns" ++#define KMS_CONFIG_PROP_CCA_APQNS "cca_apqns" ++#define KMS_CONFIG_PROP_EP11_APQNS "ep11_apqns" + #define KMS_CONFIG_LOCAL "local" + + #define KMS_KEY_PROP_NAME "zkey-name" +-- +2.26.2 + diff --git a/SOURCES/zfcpconf.sh b/SOURCES/zfcpconf.sh index 45d10a1..8dc05ad 100644 --- a/SOURCES/zfcpconf.sh +++ b/SOURCES/zfcpconf.sh @@ -2,10 +2,12 @@ # config file syntax: # deviceno WWPN FCPLUN +# deviceno # allowed when auto LUN scan is enabled and port is in NPIV mode # # Example: # 0.0.4000 0x5005076300C213e9 0x5022000000000000 # 0.0.4001 0x5005076300c213e9 0x5023000000000000 +# 0.0.5000 # # # manual setup: @@ -21,6 +23,14 @@ CONFIG=/etc/zfcp.conf PATH=/bin:/sbin +set_online() +{ + DEVICE=$1 + + [ `cat /sys/bus/ccw/drivers/zfcp/${DEVICE}/online` = "0" ] \ + && echo 1 > /sys/bus/ccw/drivers/zfcp/${DEVICE}/online +} + if [ -f "$CONFIG" ]; then if [ ! -d /sys/bus/ccw/drivers/zfcp ]; then modprobe zfcp @@ -34,6 +44,17 @@ if [ -f "$CONFIG" ]; then *) [ -z "$line" ] && continue set $line + if [ $# -eq 1 ]; then + DEVICE=${1##*0x} + if [ `cat /sys/module/zfcp/parameters/allow_lun_scan` = "Y" ]; then + set_online ${DEVICE} + grep -q NPIV /sys/bus/ccw/devices/${DEVICE}/host*/fc_host/host*/port_type || \ + echo "Error: Only device ID (${DEVICE}) given, but port not in NPIV mode" + else + echo "Error: Only device ID (${DEVICE}) given, but LUN scan is disabled for the zfcp module" + fi + continue + fi if [ $# -eq 5 ]; then DEVICE=$1 SCSIID=$2 @@ -46,8 +67,7 @@ if [ -f "$CONFIG" ]; then WWPN=$2 FCPLUN=$3 fi - [ `cat /sys/bus/ccw/drivers/zfcp/${DEVICE}/online` = "0" ] \ - && echo 1 > /sys/bus/ccw/drivers/zfcp/${DEVICE}/online + set_online ${DEVICE} [ ! -d /sys/bus/ccw/drivers/zfcp/${DEVICE}/${WWPN}/${FCPLUN} ] \ && echo $FCPLUN > /sys/bus/ccw/drivers/zfcp/${DEVICE}/${WWPN}/unit_add ;; diff --git a/SPECS/s390utils.spec b/SPECS/s390utils.spec index b471ffe..af50a72 100644 --- a/SPECS/s390utils.spec +++ b/SPECS/s390utils.spec @@ -1,15 +1,15 @@ %define cmsfsver 1.1.8c %define vipaver 2.1.0 +# secure boot support is for RHEL only %if 0%{?rhel} >= 8 %global signzipl 1 %endif Name: s390utils Summary: Utilities and daemons for IBM z Systems -Group: System Environment/Base -Version: 2.6.0 -Release: 30%{?dist} +Version: 2.15.1 +Release: 4%{?dist} Epoch: 2 License: MIT ExclusiveArch: s390 s390x @@ -29,32 +29,30 @@ Source15: device_cio_free.service Source16: ccw_init Source17: ccw.udev Source21: normalize_dasd_arg -Source22: 00-zipl-prepare.install -Source23: 20-zipl-kernel.install -Source24: 52-zipl-rescue.install -Source25: 91-zipl.install +Source23: 00-zipl-prepare.install +Source24: 20-zipl-kernel.install +Source25: 52-zipl-rescue.install +Source26: 91-zipl.install -# for secure boot %if 0%{?signzipl} %define pesign_name redhatsecureboot302 %endif -# backported stuff for RHEL -Patch0: s390-tools-rhel.patch -# BLS support in zipl # change the defaults to match Fedora environment -Patch100: s390-tools-zipl-invert-script-options.patch -# https://bugzilla.redhat.com/show_bug.cgi?id=1640968 -Patch102: s390-tools-zipl-sort-like-rpm.patch -# https://bugzilla.redhat.com/show_bug.cgi?id=1772054 -Patch103: s390-tools-zipl-bls-loadaddr.patch +Patch0: s390-tools-zipl-invert-script-options.patch +Patch1: s390-tools-zipl-blscfg-rpm-nvr-sort.patch + +# backported fixes +Patch100: s390utils-2.15.1-rhel.patch Patch1000: cmsfs-1.1.8-warnings.patch Patch1001: cmsfs-1.1.8-kernel26.patch Patch1002: cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch +Patch1003: cmsfs-1.1.8-args.patch Patch2000: src_vipa-2.1.0-deprecate.patch +Requires: s390utils-core = %{epoch}:%{version}-%{release} Requires: s390utils-base = %{epoch}:%{version}-%{release} Requires: s390utils-osasnmpd = %{epoch}:%{version}-%{release} Requires: s390utils-cpuplugd = %{epoch}:%{version}-%{release} @@ -64,7 +62,6 @@ Requires: s390utils-ziomon = %{epoch}:%{version}-%{release} Requires: s390utils-cmsfs = %{epoch}:%{version}-%{release} BuildRequires: gcc-c++ -BuildRequires: rpm-devel %description This is a meta package for installing the default s390-tools sub packages. @@ -77,13 +74,11 @@ be used together with the zSeries (s390) Linux kernel and device drivers. %prep %setup -q -n s390-tools-%{version} -a 4 -a 6 -# Backported stuff for RHEL -%patch0 -p1 +# Fedora/RHEL changes +%patch0 -p1 -b .zipl-invert-script-options +%patch1 -p1 -b .blscfg-rpm-nvr-sort -# BLS support in zipl -%patch100 -p1 -b .zipl-invert-script-options -%patch102 -p1 -b .zipl-sort-like-rpm -%patch103 -p1 -b .zipl-bls-loadaddr +%patch100 -p1 # # cmsfs @@ -97,6 +92,9 @@ pushd cmsfs-%{cmsfsver} # use detected filesystem block size (#651012) %patch1002 -p1 -b .use-detected-block-size + +# fix args processing (#1866872) +%patch1003 -p1 -b .args popd # @@ -141,7 +139,7 @@ popd %install make install \ HAVE_DRACUT=1 \ - DESTDIR=$RPM_BUILD_ROOT \ + DESTDIR=%{buildroot} \ BINDIR=/usr/sbin \ SYSTEMDSYSTEMUNITDIR=%{_unitdir} \ DISTRELEASE=%{release} \ @@ -159,67 +157,155 @@ else fi %endif -mkdir -p $RPM_BUILD_ROOT{/boot,%{_udevrulesdir},%{_sysconfdir}/{profile.d,sysconfig},%{_prefix}/lib/modules-load.d} -install -p -m 644 zipl/boot/tape0.bin $RPM_BUILD_ROOT/boot/tape0 -install -p -m 755 %{SOURCE5} $RPM_BUILD_ROOT%{_sbindir} -install -p -m 755 %{SOURCE13} $RPM_BUILD_ROOT%{_sbindir} -install -p -m 755 %{SOURCE21} $RPM_BUILD_ROOT%{_sbindir} -install -p -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_udevrulesdir}/56-zfcp.rules -install -p -m 644 %{SOURCE12} $RPM_BUILD_ROOT%{_udevrulesdir}/56-dasd.rules +# move tools to searchable dir +mv %{buildroot}%{_datadir}/s390-tools/netboot/mk-s390image %{buildroot}%{_bindir} -touch $RPM_BUILD_ROOT%{_sysconfdir}/{zfcp.conf,dasd.conf} +mkdir -p %{buildroot}{/boot,%{_udevrulesdir},%{_sysconfdir}/{profile.d,sysconfig},%{_prefix}/lib/modules-load.d} +install -p -m 644 zipl/boot/tape0.bin %{buildroot}/boot/tape0 +install -p -m 755 %{SOURCE5} %{buildroot}%{_sbindir} +install -p -m 755 %{SOURCE13} %{buildroot}%{_sbindir} +install -p -m 755 %{SOURCE21} %{buildroot}%{_sbindir} +install -p -m 644 %{SOURCE7} %{buildroot}%{_udevrulesdir}/56-zfcp.rules +install -p -m 644 %{SOURCE12} %{buildroot}%{_udevrulesdir}/56-dasd.rules + +touch %{buildroot}%{_sysconfdir}/{zfcp.conf,dasd.conf} # upstream udev rules -install -Dp -m 644 etc/udev/rules.d/*.rules $RPM_BUILD_ROOT%{_udevrulesdir} +install -Dp -m 644 etc/udev/rules.d/*.rules %{buildroot}%{_udevrulesdir} # upstream modules config -install -Dp -m 644 etc/modules-load.d/*.conf $RPM_BUILD_ROOT%{_prefix}/lib/modules-load.d +install -Dp -m 644 etc/modules-load.d/*.conf %{buildroot}%{_prefix}/lib/modules-load.d # Install kernel-install scripts install -d -m 0755 %{buildroot}%{_prefix}/lib/kernel/install.d/ -install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE22} install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ zfcpdump/10-zfcpdump.install install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE23} install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE24} install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE25} +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE26} install -d -m 0755 %{buildroot}%{_sysconfdir}/kernel/install.d/ install -m 0644 /dev/null %{buildroot}%{_sysconfdir}/kernel/install.d/20-grubby.install # cmsfs tools must be available in /sbin for f in cat lst vol cp ck; do - install -p -m 755 cmsfs-%{cmsfsver}/cmsfs${f} $RPM_BUILD_ROOT%{_sbindir} - install -p -m 644 cmsfs-%{cmsfsver}/cmsfs${f}.8 $RPM_BUILD_ROOT%{_mandir}/man8 + install -p -m 755 cmsfs-%{cmsfsver}/cmsfs${f} %{buildroot}%{_sbindir} + install -p -m 644 cmsfs-%{cmsfsver}/cmsfs${f}.8 %{buildroot}%{_mandir}/man8 done # src_vipa pushd src_vipa-%{vipaver} -make install LIBDIR=%{_libdir} SBINDIR=%{_bindir} INSTROOT=$RPM_BUILD_ROOT LDCONFIG=/bin/true +make install LIBDIR=%{_libdir} SBINDIR=%{_bindir} INSTROOT=%{buildroot} LDCONFIG=/bin/true popd # install usefull headers for devel subpackage -mkdir -p $RPM_BUILD_ROOT%{_includedir}/%{name} -install -p -m 644 include/lib/vtoc.h $RPM_BUILD_ROOT%{_includedir}/%{name} +mkdir -p %{buildroot}%{_includedir}/%{name} +install -p -m 644 include/lib/vtoc.h %{buildroot}%{_includedir}/%{name} # device_cio_free -install -p -m 755 %{SOURCE14} $RPM_BUILD_ROOT%{_sbindir} -pushd $RPM_BUILD_ROOT%{_sbindir} +install -p -m 755 %{SOURCE14} %{buildroot}%{_sbindir} +pushd %{buildroot}%{_sbindir} for lnk in dasd zfcp znet; do ln -sf device_cio_free ${lnk}_cio_free done popd -install -p -m 644 %{SOURCE15} $RPM_BUILD_ROOT%{_unitdir} +install -p -m 644 %{SOURCE15} %{buildroot}%{_unitdir} # ccw -install -p -m 755 %{SOURCE16} $RPM_BUILD_ROOT/usr/lib/udev/ccw_init -install -p -m 644 %{SOURCE17} $RPM_BUILD_ROOT%{_udevrulesdir}/81-ccw.rules +install -p -m 755 %{SOURCE16} %{buildroot}/usr/lib/udev/ccw_init +install -p -m 644 %{SOURCE17} %{buildroot}%{_udevrulesdir}/81-ccw.rules # zipl.conf to be ghosted -touch $RPM_BUILD_ROOT%{_sysconfdir}/zipl.conf +touch %{buildroot}%{_sysconfdir}/zipl.conf %files %doc README.md +# ************************* s390-tools core package ************************* +# +%package core +License: MIT +Summary: S390 core tools +Requires: coreutils +%{?systemd_requires} +# BRs are covered via the base package + + +%description core +This package provides minimal set of tools needed to system to boot. + +%post core +%if 0 +# enable in F-31 +%systemd_post device_cio_free.service +%else +# explicit enable for upgrade patch from s390utils-base < 2.6.0-4 +systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : +%endif +%systemd_post cpi.service + +%preun core +%systemd_preun device_cio_free.service +%systemd_preun cpi.service + +%postun core +%systemd_postun_with_restart cpi.service + +%files core +%doc README.md zdev/src/chzdev_usage.txt +%doc LICENSE +%{_sbindir}/chreipl +%{_sbindir}/chzdev +%{_sbindir}/cio_ignore +%{_sbindir}/dasdfmt +%{_sbindir}/dasdinfo +%{_sbindir}/zipl +%dir /lib/s390-tools/ +/lib/s390-tools/{zipl,chreipl}_helper.* +/lib/s390-tools/cpictl +/lib/s390-tools/stage3.bin +/lib/s390-tools/zdev-root-update +/lib/s390-tools/zipl.conf +%ghost %config(noreplace) %{_sysconfdir}/zipl.conf +%{_unitdir}/cpi.service +%config(noreplace) %{_sysconfdir}/sysconfig/cpi +/usr/lib/dracut/modules.d/95zdev/ +%{_mandir}/man5/zipl.conf.5* +%{_mandir}/man8/chreipl.8* +%{_mandir}/man8/chzdev.8* +%{_mandir}/man8/cio_ignore.8* +%{_mandir}/man8/dasdfmt.8* +%{_mandir}/man8/dasdinfo.8* +%{_mandir}/man8/zipl.8* + +# Additional Fedora/RHEL specific stuff +%ghost %config(noreplace) %{_sysconfdir}/dasd.conf +%ghost %config(noreplace) %{_sysconfdir}/zfcp.conf +%{_sbindir}/dasdconf.sh +%{_sbindir}/normalize_dasd_arg +%{_sbindir}/zfcpconf.sh +%{_sbindir}/device_cio_free +%{_sbindir}/dasd_cio_free +%{_sbindir}/zfcp_cio_free +%{_sbindir}/znet_cio_free +%{_unitdir}/device_cio_free.service +/usr/lib/udev/ccw_init +%{_udevrulesdir}/40-z90crypt.rules +%{_udevrulesdir}/56-dasd.rules +%{_udevrulesdir}/56-zfcp.rules +%{_udevrulesdir}/59-dasd.rules +%{_udevrulesdir}/60-readahead.rules +%{_udevrulesdir}/81-ccw.rules +%{_udevrulesdir}/90-cpi.rules +%{_sysconfdir}/kernel/install.d/20-grubby.install +%{_prefix}/lib/kernel/install.d/00-zipl-prepare.install +%{_prefix}/lib/kernel/install.d/10-zfcpdump.install +%{_prefix}/lib/kernel/install.d/20-zipl-kernel.install +%{_prefix}/lib/kernel/install.d/52-zipl-rescue.install +%{_prefix}/lib/kernel/install.d/91-zipl.install +%{_prefix}/lib/modules-load.d/s390-pkey.conf + + # # ************************* s390-tools base package ************************* # @@ -227,18 +313,19 @@ touch $RPM_BUILD_ROOT%{_sysconfdir}/zipl.conf # src_vipa is CPL License: MIT and CPL Summary: S390 base tools -Group: System Environment/Base Requires: gawk sed coreutils Requires: sysfsutils Requires: sg3_utils Requires: ethtool +Requires: tar +Requires: s390utils-core = %{epoch}:%{version}-%{release} %{?systemd_requires} BuildRequires: perl-generators BuildRequires: ncurses-devel -BuildRequires: libpfm-devel BuildRequires: glibc-static BuildRequires: cryptsetup-devel >= 2.0.3 BuildRequires: json-c-devel +BuildRequires: rpm-devel BuildRequires: glib2-devel @@ -381,45 +468,30 @@ For more information refer to the following publications: getent group zkeyadm > /dev/null || groupadd -r zkeyadm %post base -%systemd_post cpi.service -%if 0 -# enable in F-31 -%systemd_post device_cio_free.service -%else -# explicit enable for upgrade patch from s390utils-base < 2.6.0-4 -systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : -%endif %systemd_post dumpconf.service %preun base -%systemd_preun cpi.service -%systemd_preun device_cio_free.service %systemd_preun dumpconf.service %postun base -%systemd_postun_with_restart cpi.service %systemd_postun_with_restart dumpconf.service %files base -%doc README.md zdev/src/*.txt -%doc LICENSE +%doc README.md zdev/src/lszdev_usage.txt %{_sbindir}/chccwdev %{_sbindir}/chchp %{_sbindir}/chcpumf -%{_sbindir}/chreipl %{_sbindir}/chshut %{_sbindir}/chzcrypt -%{_sbindir}/chzdev -%{_sbindir}/cio_ignore -%{_sbindir}/dasdfmt -%{_sbindir}/dasdinfo %{_sbindir}/dasdstat %{_sbindir}/dasdview %{_sbindir}/dbginfo.sh %{_sbindir}/fdasd +%{_sbindir}/hsci %{_sbindir}/hyptop %{_sbindir}/ip_watcher.pl %{_sbindir}/lschp +%{_sbindir}/lscpumf %{_sbindir}/lscss %{_sbindir}/lsdasd %{_sbindir}/lsqeth @@ -427,6 +499,7 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_sbindir}/lsreipl %{_sbindir}/lsscm %{_sbindir}/lsshut +%{_sbindir}/lsstp %{_sbindir}/lstape %{_sbindir}/lszcrypt %{_sbindir}/lszdev @@ -447,23 +520,25 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_sbindir}/zcryptstats %{_sbindir}/zfcpdbf %{_sbindir}/zgetdump -%{_sbindir}/zipl %{_sbindir}/zipl-switch-to-blscfg %{_sbindir}/znetconf %{_sbindir}/zpcictl -%{_bindir}/lscpumf %{_bindir}/dump2tar %{_bindir}/genprotimg +%{_bindir}/mk-s390image %{_bindir}/vmconvert %{_bindir}/zkey %{_bindir}/zkey-cryptsetup -%{_unitdir}/cpi.service %{_unitdir}/dumpconf.service %ghost %config(noreplace) %{_sysconfdir}/zipl.conf -%config(noreplace) %{_sysconfdir}/sysconfig/cpi %config(noreplace) %{_sysconfdir}/sysconfig/dumpconf -/lib/s390-tools/ -/usr/lib/dracut/modules.d/95zdev/ +/lib/s390-tools/dumpconf +/lib/s390-tools/lsznet.raw +%dir /lib/s390-tools/zfcpdump +/lib/s390-tools/zfcpdump/zfcpdump-initrd +/lib/s390-tools/znetcontrolunits +%{_libdir}/libekmfweb.so.* +%{_libdir}/zkey/zkey-ekmfweb.so %{_mandir}/man1/dbginfo.sh.1* %{_mandir}/man1/dump2tar.1* %{_mandir}/man1/lscpumf.1* @@ -472,23 +547,20 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_mandir}/man1/zipl-switch-to-blscfg.1* %{_mandir}/man1/zkey.1* %{_mandir}/man1/zkey-cryptsetup.1* +%{_mandir}/man1/zkey-ekmfweb.1* %{_mandir}/man4/prandom.4* %{_mandir}/man5/zipl.conf.5* %{_mandir}/man8/chccwdev.8* %{_mandir}/man8/chchp.8* %{_mandir}/man8/chcpumf.8* -%{_mandir}/man8/chreipl.8* %{_mandir}/man8/chshut.8* %{_mandir}/man8/chzcrypt.8* -%{_mandir}/man8/chzdev.8* -%{_mandir}/man8/cio_ignore.8* -%{_mandir}/man8/dasdfmt.8* -%{_mandir}/man8/dasdinfo.8* %{_mandir}/man8/dasdstat.8* %{_mandir}/man8/dasdview.8* %{_mandir}/man8/dumpconf.8* %{_mandir}/man8/fdasd.8* %{_mandir}/man8/genprotimg.8.* +%{_mandir}/man8/hsci.8* %{_mandir}/man8/hyptop.8* %{_mandir}/man8/lschp.8* %{_mandir}/man8/lscss.8* @@ -498,6 +570,7 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_mandir}/man8/lsreipl.8* %{_mandir}/man8/lsscm.8* %{_mandir}/man8/lsshut.8* +%{_mandir}/man8/lsstp.8* %{_mandir}/man8/lstape.8* %{_mandir}/man8/lszcrypt.8* %{_mandir}/man8/lszdev.8* @@ -514,43 +587,17 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_mandir}/man8/zcryptctl.8* %{_mandir}/man8/zcryptstats.8* %{_mandir}/man8/zgetdump.8* -%{_mandir}/man8/zipl.8* %{_mandir}/man8/znetconf.8* %{_mandir}/man8/zpcictl.8* %dir %{_datadir}/s390-tools/ -%{_datadir}/s390-tools/cpumf/ %{_datadir}/s390-tools/genprotimg/ %{_datadir}/s390-tools/netboot/ %dir %attr(0770,root,zkeyadm) %{_sysconfdir}/zkey %dir %attr(0770,root,zkeyadm) %{_sysconfdir}/zkey/repository +%config(noreplace) %attr(0660,root,zkeyadm)%{_sysconfdir}/zkey/kms-plugins.conf -# Additional Redhat specific stuff +# Additional Fedora/RHEL specific stuff /boot/tape0 -%ghost %config(noreplace) %{_sysconfdir}/dasd.conf -%ghost %config(noreplace) %{_sysconfdir}/zfcp.conf -%{_sbindir}/dasdconf.sh -%{_sbindir}/zfcpconf.sh -%{_sbindir}/dasd_cio_free -%{_sbindir}/device_cio_free -%{_sbindir}/zfcp_cio_free -%{_sbindir}/znet_cio_free -%{_sbindir}/normalize_dasd_arg -%{_unitdir}/device_cio_free.service -/usr/lib/udev/ccw_init -%{_udevrulesdir}/40-z90crypt.rules -%{_udevrulesdir}/56-zfcp.rules -%{_udevrulesdir}/56-dasd.rules -%{_udevrulesdir}/59-dasd.rules -%{_udevrulesdir}/60-readahead.rules -%{_udevrulesdir}/81-ccw.rules -%{_udevrulesdir}/90-cpi.rules -%{_sysconfdir}/kernel/install.d/20-grubby.install -%{_prefix}/lib/kernel/install.d/00-zipl-prepare.install -%{_prefix}/lib/kernel/install.d/10-zfcpdump.install -%{_prefix}/lib/kernel/install.d/20-zipl-kernel.install -%{_prefix}/lib/kernel/install.d/52-zipl-rescue.install -%{_prefix}/lib/kernel/install.d/91-zipl.install -%{_prefix}/lib/modules-load.d/s390-pkey.conf # src_vipa %{_bindir}/src_vipa.sh @@ -562,7 +609,6 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : # %package osasnmpd Summary: SNMP sub-agent for OSA-Express cards -Group: System Environment/Daemons Requires: net-snmp Requires: psmisc BuildRequires: net-snmp-devel @@ -582,7 +628,6 @@ ATM Ethernet LAN Emulation in QDIO mode. # %package mon_statd Summary: Monitoring daemons for Linux in z/VM -Group: System Environment/Daemons Requires: coreutils %{?systemd_requires} @@ -622,7 +667,6 @@ Monitoring daemons for Linux in z/VM: # %package cpuplugd Summary: Daemon that manages CPU and memory resources -Group: System Environment/Daemons %{?systemd_requires} BuildRequires: systemd @@ -652,7 +696,6 @@ memory can be increased or decreased exploiting the CMM1 feature. # %package ziomon Summary: S390 ziomon tools -Group: Applications/System Requires: blktrace Requires: coreutils Requires: device-mapper-multipath @@ -691,7 +734,6 @@ Tool set to collect data for zfcp performance analysis and report. # %package iucvterm Summary: z/VM IUCV terminal applications -Group: Applications/System Requires(pre): shadow-utils Requires(post): grep Requires(postun): grep @@ -754,7 +796,6 @@ fi %package cmsfs License: GPLv2 Summary: CMS file system tools -Group: System Environment/Base URL: http://www.casita.net/pub/cmsfs/cmsfs.html # Requires: @@ -778,7 +819,6 @@ This package contains the CMS file system tools. # %package cmsfs-fuse Summary: CMS file system based on FUSE -Group: System Environment/Base BuildRequires: fuse-devel Requires: fuse @@ -796,8 +836,8 @@ This package contains the CMS file system based on FUSE. # %package zdsfs Summary: z/OS data set access based on FUSE -Group: System Environment/Base BuildRequires: fuse-devel +BuildRequires: libcurl-devel Requires: fuse %description zdsfs @@ -812,7 +852,6 @@ This package contains the z/OS data set access based on FUSE. # %package hmcdrvfs Summary: HMC drive file system based on FUSE -Group: System Environment/Base BuildRequires: fuse-devel Requires: fuse @@ -831,7 +870,6 @@ to list files and directories. # %package cpacfstatsd Summary: Monitor and maintain CPACF activity counters -Group: System Environment/Base Requires(post): systemd Requires(preun): systemd Requires(postun): systemd @@ -866,16 +904,55 @@ getent group cpacfstats >/dev/null || groupadd -r cpacfstats # %package devel Summary: Development files -Group: Development/Libraries + +Requires: %{name}-base%{?_isa} = %{epoch}:%{version}-%{release} %description devel User-space development files for the s390/s390x architecture. %files devel -%{_includedir}/%{name} +%{_includedir}/%{name}/ +%{_includedir}/ekmfweb/ +%{_libdir}/libekmfweb.so %changelog +* Mon Dec 07 2020 Dan Horák - 2:2.15.1-4 +- zkey: Fix KMS plugin configuration to store APQNs correctly. (#1901968) +- dasdfmt: Fix bad file descriptor error when running on symlinks (#1901963) +- zcryptstats: Fix handling of partial results with many domains (#1901962) +- s390-tools: add hsci tool (#1847434) +- genprotimg: add host-key document verification support (#1845925) +- genprotimg: require argument for 'ramdisk' and 'parmfile' options (#1845925) +- genprotimg: fix two memory leaks (#1845925) +- genprotimg: abort if one of the recursive targets is failing (#1845925) +- zdev/lsdasd: Add FC Endpoint Security information (#1723844) +- Resolves: #1723844 #1845925 #1847434 #1901962 #1901963 #1901968 + +* Fri Nov 27 2020 Javier Martinez Canillas - 2:2.15.1-2 +- add again the 00-zipl-prepare.install script that got dropped (#1902273) + Resolves: #1902273 + +* Wed Nov 04 2020 Dan Horák - 2:2.15.1-2 +- rebased to 2.15.1 (#1851111) +- implement improved auto LUN scan (#1552697) +- fix crash when device is missing in cmsfslst (#1866872) +- introduce s390utils-core to avoid dependency on Perl (#1886201) +- Resolves: #1851111 #1552697 #1866872 #1886201 + +* Fri Jul 31 2020 Dan Horák - 2:2.6.0-33 +- cpacfstats: Bugfix to remove libpfm from cpacfstats (#1861779) +- Resolves: (#1861779) + +* Fri Jul 24 2020 Dan Horák - 2:2.6.0-32 +- vmcp: Change sequence of failed exit (#1858839) +- zipl: Fix KVM IPL without bootindex (#1858842, #1846960) +- Resolves: #1858839, #1846960 + +* Fri Jul 03 2020 Javier Martinez Canillas - 2:2.6.0-31 +- add a default entry in zipl.conf if there isn't one present (#1698363) +- Resolves: #1698363 + * Mon Jun 22 2020 Dan Horák - 2:2.6.0-30 - avoid dependency on network-scripts (#1847388) - Resolves: #1847388