diff --git a/SOURCES/s390-tools-rhel.patch b/SOURCES/s390-tools-rhel.patch index 1af7de1..5b50e8c 100644 --- a/SOURCES/s390-tools-rhel.patch +++ b/SOURCES/s390-tools-rhel.patch @@ -1,7 +1,7 @@ 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/43] drop LOADLIBES variable +Subject: [PATCH 01/56] drop LOADLIBES variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -128,13 +128,13 @@ index 778401b..61c2399 100644 install: all $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ -- -2.21.1 +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/43] zkey: Drop redundant include +Subject: [PATCH 02/56] zkey: Drop redundant include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -157,13 +157,13 @@ index 68f35cf..725cb3b 100644 detect-libcryptsetup.h: -- -2.21.1 +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/43] zkey: Be consistent when refering to libutil.a +Subject: [PATCH 03/56] zkey: Be consistent when refering to libutil.a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -200,13 +200,13 @@ index 725cb3b..7e2047a 100644 install-common: $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) -- -2.21.1 +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/43] zkey: Be explicit about linking the tools +Subject: [PATCH 04/56] zkey: Be explicit about linking the tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -237,13 +237,13 @@ index 7e2047a..901ddd4 100644 install-common: $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) -- -2.21.1 +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/43] zkey: Makefile: Avoid relink of modules during 'make +Subject: [PATCH 05/56] zkey: Makefile: Avoid relink of modules during 'make install' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -295,13 +295,13 @@ index 901ddd4..bc7bc33 100644 + zkey-cryptsetup-skip-jsonc install-common install-zkey \ + install-zkey-cryptsetup -- -2.21.1 +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/43] zkey: Makefile: Don't rebuild .o.d files on 'make +Subject: [PATCH 06/56] zkey: Makefile: Don't rebuild .o.d files on 'make install' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -366,13 +366,13 @@ index bc7bc33..a44b14b 100644 .PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \ -- -2.21.1 +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/43] zpcictl: Add tool to manage PCI devices (#1525409) +Subject: [PATCH 07/56] 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 @@ -976,13 +976,13 @@ index 0000000..5187e7c + +#endif /* ZPCICTL_H */ -- -2.21.1 +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/43] zpcictl: Read device link to obtain device address +Subject: [PATCH 08/56] zpcictl: Read device link to obtain device address (#1639220) Description: zpcictl: Read device link to obtain device address @@ -1183,13 +1183,13 @@ index 5f63b17..a9e38fb 100644 if (!pdev->device && pdev->class == PCI_CLASS_NVME) get_device_node(pdev); -- -2.21.1 +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/43] zpcictl: Change wording of man-page and help output +Subject: [PATCH 09/56] zpcictl: Change wording of man-page and help output (#1643451) Description: zpcictl: Change wording of man-page and help output @@ -1327,13 +1327,13 @@ index a9e38fb..7cfa0d3 100644 UTIL_OPT_VERSION, UTIL_OPT_END -- -2.21.1 +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/43] zdev: qeth BridgePort and VNICC attribute conflict +Subject: [PATCH 10/56] zdev: qeth BridgePort and VNICC attribute conflict (#1643452) Description: zdev: qeth BridgePort and VNICC attribute conflict @@ -1402,13 +1402,13 @@ index 46bc23d..6191ad1 100644 if (t == group_bridge && (!bridge || !bridge->specified)) bridge = s; -- -2.21.1 +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/43] qethqoat: add OSA-Express7S support (#1644384) +Subject: [PATCH 11/56] qethqoat: add OSA-Express7S support (#1644384) Description: qethqoat: add OSA-Express7S support Symptom: qethqoat fails to report HW generation and link speed for @@ -1468,13 +1468,13 @@ index dd7e992..e692937 100644 #define OAT_PORT_MEDIA_COPPER 0x01 #define OAT_PORT_MEDIA_MULTI_MODE 0x02 -- -2.21.1 +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/43] zcryptctl: add zcryptctl to manage multiple zcrypt +Subject: [PATCH 12/56] zcryptctl: add zcryptctl to manage multiple zcrypt nodes (#1646354) Summary: zcryptctl: add zcryptctl to manage multiple zcrypt nodes @@ -2713,13 +2713,13 @@ index 0000000..8326a08 + return rc; +} -- -2.21.1 +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/43] lszcrypt: support for alternate zcrypt device drivers +Subject: [PATCH 13/56] lszcrypt: support for alternate zcrypt device drivers (#1646355) Summary: lszcrypt: support for alternate zcrypt device drivers @@ -3066,13 +3066,13 @@ index eb3cd6e..580407f 100644 /* -- -2.21.1 +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/43] zkey: Fails to run commands generated by 'zkey +Subject: [PATCH 14/56] zkey: Fails to run commands generated by 'zkey cryptsetup' (#1650628) Description: zkey: Fails to run commands generated by 'zkey cryptsetup' @@ -3102,13 +3102,13 @@ index 11f555c..a4ad634 100644 return rc; -- -2.21.1 +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/43] zkey: Enhance error message about missing CCA library +Subject: [PATCH 15/56] zkey: Enhance error message about missing CCA library (#1655134) Description: zkey: Enhance error message about missing CCA library. @@ -3196,13 +3196,13 @@ index c9f7906..0837b27 100644 .SS "Import existing AES secure keys into the secure key repository" . -- -2.21.1 +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/43] zfcpdump: add install script for zfcpdump kernel +Subject: [PATCH 16/56] zfcpdump: add install script for zfcpdump kernel (#1600480) Summary: zfcpdump: add install script for zfcpdump kernel @@ -3496,13 +3496,13 @@ index e6d2981..22d2549 100644 return -1; dump->ramdisk_addr = UNSPECIFIED_ADDRESS; -- -2.21.1 +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/43] pkey: Support autoloading kernel pkey module (#1664632) +Subject: [PATCH 17/56] pkey: Support autoloading kernel pkey module (#1664632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -3531,13 +3531,13 @@ index 0000000..972a099 +# Load protected key support module on s390 early at boot +pkey -- -2.21.1 +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/43] zipl: Secure Boot support for SCSI IPL (#1659401) +Subject: [PATCH 18/56] 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. @@ -5329,13 +5329,13 @@ index fe72e9a..e57361e 100644 /* targetbase= */ /* targetgeometry= */ -- -2.21.1 +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/43] zipl: update stage3 objcopy command for gcc9 (#1659401) +Subject: [PATCH 19/56] zipl: update stage3 objcopy command for gcc9 (#1659401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -5361,13 +5361,13 @@ index da7e95f..a049797 100644 --only-section=.stage2dump.tail \ --only-section=.eckd2dump_mv.tail \ -- -2.21.1 +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/43] s390-tools: Add zcryptstats tool (#1658756) +Subject: [PATCH 20/56] s390-tools: Add zcryptstats tool (#1658756) Description: The zcryptstats tool displays usage statistics of IBM Crypto Express adapters. It obtains cryptographic @@ -8358,13 +8358,13 @@ index 0000000..136d5ba +} + -- -2.21.1 +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/43] zpcictl: Check for regular directory (#1695001) +Subject: [PATCH 21/56] zpcictl: Check for regular directory (#1695001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -8402,13 +8402,13 @@ index 7cfa0d3..f2a55ad 100644 free(path); -- -2.21.1 +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/43] cpumf: Add support for CPU-Measurement Facility +Subject: [PATCH 22/56] cpumf: Add support for CPU-Measurement Facility counters SVN 6 (#1683276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -8528,13 +8528,13 @@ index a8a2846..5729b95 100644 + 8562 => 'cpum-cf-extended-z14.ctr', }; -- -2.21.1 +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/43] ziomon: fix utilization recording with multi-digit scsi +Subject: [PATCH 23/56] ziomon: fix utilization recording with multi-digit scsi hosts (#1731203) Description: ziomon: fix utilization recording with multi-digit scsi hosts @@ -8611,13 +8611,13 @@ index 87c4bc4..26fa269 100755 ddebug " adding ${checked_devs[${#checked_devs[@]}-1]}"; WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}"; -- -2.21.1 +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/43] zdev: Do not export inacceptable attribute values +Subject: [PATCH 24/56] zdev: Do not export inacceptable attribute values (#1731960) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -8669,13 +8669,13 @@ index f1acb8a..a66db0b 100644 /* All non-default values should be exported. */ return true; -- -2.21.1 +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/43] zipl: do not overwrite BOOT_IMAGE entry (#1728677) +Subject: [PATCH 25/56] zipl: do not overwrite BOOT_IMAGE entry (#1728677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -8765,13 +8765,13 @@ index 91477b1..3a02001 100644 #define STAGE3_FLAG_SCSI 0x0001000000000000ULL #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL -- -2.21.1 +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/43] fdasd: Fix exit status in error cases (#1734816) +Subject: [PATCH 26/56] fdasd: Fix exit status in error cases (#1734816) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -8814,13 +8814,13 @@ index 085d2be..5de7955 100644 + return EXIT_FAILURE; } -- -2.21.1 +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/43] zipl: fix zfcp dump image location (#1730707) +Subject: [PATCH 27/56] zipl: fix zfcp dump image location (#1730707) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -8886,13 +8886,13 @@ index 2d8de8f..e68d1b7 100644 if (rc) goto error; -- -2.21.1 +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/43] dasdfmt/lsdasd: Add Thin provisioning base support +Subject: [PATCH 28/56] dasdfmt/lsdasd: Add Thin provisioning base support (#1651733) Summary: dasdfmt/lsdasd: Add Thin provisioning base support @@ -10403,13 +10403,13 @@ index a6c4333..20b53f5 100644 rc = -1; goto out; -- -2.21.1 +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/43] zdsfs: add online vtoc refresh (#1685536) +Subject: [PATCH 29/56] 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. @@ -11065,13 +11065,13 @@ index 462fda7..d7a83ee 100644 static void zdsfs_process_device_file(const char *devfile) -- -2.21.1 +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/43] zdev: add zfcp dix parameter handling (#1723852) +Subject: [PATCH 30/56] 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 @@ -11135,13 +11135,13 @@ index 03aa252..82dac57 100644 &zfcp_tattr_no_auto_port_rescan, &zfcp_tattr_port_scan_ratelimit, -- -2.21.1 +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/43] zipl: Fix error message printed with --dumptofs +Subject: [PATCH 31/56] zipl: Fix error message printed with --dumptofs (#1750307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -11169,13 +11169,13 @@ index e68d1b7..5dcebaf 100644 break; case 'M': -- -2.21.1 +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/43] zipl: set correct secure IPL default value (#1750326) +Subject: [PATCH 32/56] zipl: set correct secure IPL default value (#1750326) Set secure IPL to auto as default value to match documented behavior. @@ -11197,13 +11197,13 @@ index 5dcebaf..1178a7d 100644 if (cmdline.help) { job->command_line = 1; -- -2.21.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/43] zkey: various enhancements (#1725881) +Subject: [PATCH 33/56] zkey: various enhancements (#1725881) Description: Enhancements to zkey in response to first (customer) experiences @@ -13070,13 +13070,13 @@ index 3a8909a..2ecbb90 100644 print_help(command); return EXIT_SUCCESS; -- -2.21.1 +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/43] zkey: check master key consistency (#1753153) +Subject: [PATCH 34/56] 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. @@ -16369,13 +16369,13 @@ index 2ecbb90..a0dbc0b 100644 close(g.pkey_fd); if (g.keystore) -- -2.21.1 +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/43] zkey: Add support for CCA AES CIPHER keys (#1719623) +Subject: [PATCH 35/56] 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 @@ -20981,13 +20981,13 @@ index a0dbc0b..3ae9a9b 100644 g.force = 1; break; -- -2.21.1 +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/43] zcrypt: CEX7S exploitation support (#1723837) +Subject: [PATCH 36/56] zcrypt: CEX7S exploitation support (#1723837) Description: CEX7S exploitation support to lszcrypt, chzcrypt and zcryptstats. @@ -21290,13 +21290,13 @@ index 136d5ba..3bb2078 100644 } pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE); -- -2.21.1 +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/43] lstape, lsluns: handle non-zfcp; lin_tape multiple +Subject: [PATCH 37/56] lstape, lsluns: handle non-zfcp; lin_tape multiple paths (#1766569) Symptom: lstape shows unexpected additional Device suffix numbers in @@ -21737,13 +21737,13 @@ index 544cb08..019b6e5 100644 +Show all SCSI tape or changer devices with maximum information. +.RE -- -2.21.1 +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/43] zipl: fix the scanned tokens array size calculation +Subject: [PATCH 38/56] zipl: fix the scanned tokens array size calculation (#1751587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -21810,13 +21810,13 @@ index e57361e..ee04251 100644 if (!buffer) goto err; -- -2.21.1 +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/43] zipl: use FIEMAP mapping ioctl if it exists (moved from +Subject: [PATCH 39/56] zipl: use FIEMAP mapping ioctl if it exists (moved from Patch101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -21932,13 +21932,13 @@ index 0d8e779..43092bf 100644 /* This is a hole in the file */ *physical = 0; -- -2.21.1 +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/43] zipl: use the BLS "title" field as the IPL section name +Subject: [PATCH 40/56] 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 @@ -21993,13 +21993,13 @@ index ee04251..b8fea13 100644 } -- -2.21.1 +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/43] zipl: config file handling improvements for CoreOS +Subject: [PATCH 41/56] zipl: config file handling improvements for CoreOS (#1764706) Use TOOLS_SYSCONFDIR for the default zipl.conf path - c01bcfa73a6d795f33df639ba36304a704d13561 @@ -22275,13 +22275,13 @@ index b8fea13..f4228d3 100644 static int scan_get_defaultboot_type(char* keyword[], int line[], int section_line, -- -2.21.1 +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/43] zkey: Fix display of clear key size for XTS keys +Subject: [PATCH 42/56] zkey: Fix display of clear key size for XTS keys (#1794375) Description: zkey: Fix display of clear key size for XTS keys @@ -22332,13 +22332,13 @@ index 462f9fe..640ff86 100644 *bitsize += cipherkey->pl - 384; } -- -2.21.1 +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/43] zkey: Fix listing of keys on file systems reporting +Subject: [PATCH 43/56] zkey: Fix listing of keys on file systems reporting DT_UNKNOWN (#1792957) Description: zkey: Fix listing of keys on file systems reporting DT_UNKNOWN. @@ -22381,5 +22381,18894 @@ index af67721..e6be3a4 100644 len = strlen(dirent->d_name); -- -2.21.1 +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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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/56] 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 diff --git a/SOURCES/src_vipa-2.1.0-deprecate.patch b/SOURCES/src_vipa-2.1.0-deprecate.patch new file mode 100644 index 0000000..3ff2e1b --- /dev/null +++ b/SOURCES/src_vipa-2.1.0-deprecate.patch @@ -0,0 +1,12 @@ +diff -up s390-tools-2.2.0/src_vipa-2.1.0/Makefile.orig s390-tools-2.2.0/src_vipa-2.1.0/Makefile +--- s390-tools-2.2.0/src_vipa-2.1.0/Makefile.orig 2020-02-21 13:51:23.502305796 +0100 ++++ s390-tools-2.2.0/src_vipa-2.1.0/Makefile 2020-02-21 13:53:51.353817181 +0100 +@@ -44,6 +44,8 @@ src_vipa.sh: + echo '#!/bin/bash' > src_vipa.sh + echo 'export LD_LIBRARY_PATH=$(LIBDIR):$$LD_LIBRARY_PATH' >> src_vipa.sh + echo 'export LD_PRELOAD=$(LIBDIR)/src_vipa.so' >> src_vipa.sh ++ echo 'echo "WARNING: The src_vipa (flexible source address selection) feature is DEPRECATED"' >> src_vipa.sh ++ echo 'echo "WARNING: It will be removed in the future."' >> src_vipa.sh + echo 'exec $$@' >> src_vipa.sh + chmod 755 src_vipa.sh + diff --git a/SPECS/s390utils.spec b/SPECS/s390utils.spec index bce995d..97c4e04 100644 --- a/SPECS/s390utils.spec +++ b/SPECS/s390utils.spec @@ -9,7 +9,7 @@ Name: s390utils Summary: Utilities and daemons for IBM z Systems Group: System Environment/Base Version: 2.6.0 -Release: 28%{?dist} +Release: 29%{?dist} Epoch: 2 License: MIT ExclusiveArch: s390 s390x @@ -53,6 +53,8 @@ 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 +Patch2000: src_vipa-2.1.0-deprecate.patch + Requires: s390utils-base = %{epoch}:%{version}-%{release} Requires: s390utils-osasnmpd = %{epoch}:%{version}-%{release} Requires: s390utils-cpuplugd = %{epoch}:%{version}-%{release} @@ -97,6 +99,13 @@ pushd cmsfs-%{cmsfsver} %patch1002 -p1 -b .use-detected-block-size popd +# +# src_vipa +# +pushd src_vipa-%{vipaver} +# mark as deprecated +%patch2000 -p2 +popd # remove --strip from install find . -name Makefile | xargs sed -i 's/$(INSTALL) -s/$(INSTALL)/g' @@ -231,6 +240,7 @@ BuildRequires: libpfm-devel BuildRequires: glibc-static BuildRequires: cryptsetup-devel >= 2.0.3 BuildRequires: json-c-devel +BuildRequires: glib2-devel %description base @@ -356,6 +366,13 @@ s390 base tools. This collection provides the following utilities: Allows to set the system and sysplex names from the Linux guest to the HMC/SE using the Control Program Identification feature. + * genprotimg: + Tool for the creation of PV images. The image consists of a concatenation of + a plain text boot loader, the encrypted components for kernel, initrd, and + cmdline, and the integrity-protected PV header, containing metadata necessary for + running the guest in PV mode. Protected VMs (PVM) are KVM VMs, where KVM can't + access the VM's state like guest memory and guest registers anymore. + For more information refer to the following publications: * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" * "Using the dump tools" @@ -437,6 +454,7 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_sbindir}/zpcictl %{_bindir}/lscpumf %{_bindir}/dump2tar +%{_bindir}/genprotimg %{_bindir}/vmconvert %{_bindir}/zkey %{_bindir}/zkey-cryptsetup @@ -471,6 +489,7 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_mandir}/man8/dasdview.8* %{_mandir}/man8/dumpconf.8* %{_mandir}/man8/fdasd.8* +%{_mandir}/man8/genprotimg.8.* %{_mandir}/man8/hyptop.8* %{_mandir}/man8/lschp.8* %{_mandir}/man8/lscss.8* @@ -501,6 +520,7 @@ systemctl --no-reload preset device_cio_free.service >/dev/null 2>&1 || : %{_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 @@ -857,6 +877,23 @@ User-space development files for the s390/s390x architecture. %changelog +* Thu May 28 2020 Dan Horák - 2:2.6.0-29 +- zkey: Add support for EP11 secure keys (#1723845) +- ipl-tools: Add nvme device support to zipl, lsreipl/chreipl (#1525178) +- zipl: Rebase to 2.13.0 (#1821250) +- ipl_tools: support clear attribute for FCP and CCW re-IPL (#1812983) +- lscpumf: New z15 CPU-MF counters not available (#1821591) +- dbginfo.sh: Extend data collection (#1814323) +- zpcictl: Initiate recover after reset (#1814303) +- zipl: fix secure boot config handling (#1814322) +- zkey: Fix display of clear key size for CCA-AESCIPHER keys (#1808492) +- zkey: Fix display of XTS attribute for validate command (#1808494) +- dasdview: Fix exit status in error cases (#1783288) +- zipl/libc: Fix potential buffer overflow in printf (#1807973) +- src_vipa feature is deprecated (#1780648) +- zdev: report FC Endpoint Security of zfcp device (#1723843) +- Resolves: #1723845 #1525178 #1821250 #1812983 #1821591 #1814323 #1814303 #1814322 #1808492 #1808494 #1783288 #1807973 #1780648 #1723843 + * Mon Jan 27 2020 Dan Horák - 2:2.6.0-28 - zkey: Fix listing of keys on file systems reporting DT_UNKNOWN (#1792957) - Resolves: #1792957