mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
793c037ded | ||
|
|
9f476217f5 | ||
|
|
49111d613b | ||
|
|
26d2e2d786 | ||
|
|
7bd159de1d | ||
|
|
605680d6fd | ||
|
|
f1a479a794 | ||
|
|
fa2d68e80c | ||
|
|
e1730585e9 | ||
|
|
ef55a5d677 | ||
|
|
bc4b7475f7 | ||
|
|
abcb93aacb | ||
|
|
b09a9b3490 | ||
|
|
caaf2b2116 | ||
|
|
382ec368fc | ||
|
|
9dd96144d4 | ||
|
|
efd41ca435 | ||
|
|
7bc12d0202 | ||
|
|
b49cd13d16 | ||
|
|
56e32dfcbb | ||
|
|
833a8e7309 | ||
|
|
a8eb2bd4e7 | ||
|
|
5276d408fd | ||
|
|
95bf7eb285 | ||
|
|
fdf66dc148 | ||
|
|
5ce79ea667 | ||
|
|
8c4b2872b8 | ||
|
|
7be364aa84 | ||
|
|
43858063ca | ||
|
|
739761f0d2 | ||
|
|
a8a3e7d49c | ||
|
|
ff04f76257 | ||
|
|
93216d916c | ||
|
|
256289a30a | ||
|
|
93da795520 | ||
|
|
a14f9d4edc | ||
|
|
fd024387d7 | ||
|
|
4af137f4fa | ||
|
|
01cd81ecf5 | ||
|
|
69eb06f39e | ||
|
|
d1636168b2 | ||
|
|
1e44ace41d | ||
|
|
a9b8caa411 | ||
|
|
6e48c5ebaa | ||
|
|
944581eaef | ||
|
|
3f6572e901 | ||
|
|
6520070a46 | ||
|
|
3b8fdcc892 | ||
|
|
352e2fe5d3 | ||
|
|
37b7b68f1b | ||
|
|
3db20bccef | ||
|
|
560b276f7e | ||
|
|
a19c43f613 | ||
|
|
5b6d7a467d | ||
|
|
b1fdbac5f9 | ||
|
|
cf51ac7860 | ||
|
|
cf5560a100 | ||
|
|
dff965465c | ||
|
|
79de37bbc8 | ||
|
|
e5c2fba0a8 | ||
|
|
04267470b1 | ||
|
|
cf7060ea08 | ||
|
|
f340a96fdf | ||
|
|
798bd752cf | ||
|
|
638f0aa4f9 | ||
|
|
d3b7581669 | ||
|
|
0cd063e40d | ||
|
|
4d97e15eb0 | ||
|
|
a32fe8641a | ||
|
|
c34adb9cab | ||
|
|
86b5df0d2d | ||
|
|
e0caed5534 | ||
|
|
8704c9c80e | ||
|
|
4ec33d0b76 | ||
|
|
3a4c20b443 | ||
|
|
c54a915a90 | ||
|
|
bc269ddb38 | ||
|
|
8c21f09570 | ||
|
|
4df0781989 | ||
|
|
195579cf0b | ||
|
|
849819cb77 | ||
|
|
72aa64495c | ||
|
|
d2de7f2808 | ||
|
|
f4cf4ae6eb | ||
|
|
f524b0b8dc | ||
|
|
f99719a695 | ||
|
|
7d81c1bbd4 | ||
|
|
2ed2c608ab | ||
|
|
d73f4dc22a | ||
|
|
ea297ecf84 | ||
|
|
7d41430e68 | ||
|
|
f095a892b9 | ||
|
|
646a389b53 | ||
|
|
66ef6b5945 | ||
|
|
6c75a06b12 | ||
|
|
b1ca60f5ba | ||
|
|
3d83100fa3 | ||
|
|
8c23e09a60 | ||
|
|
24e285ce02 | ||
|
|
ad709c1eaf | ||
|
|
b61596327b | ||
|
|
de18a72249 | ||
|
|
482bd93e2e | ||
|
|
026ecbafea | ||
|
|
40846bce49 | ||
|
|
b9879cbfc7 | ||
|
|
e61ed4f433 | ||
|
|
589090f79a | ||
|
|
57eef7c412 | ||
|
|
d864c55336 | ||
|
|
1b18ba3a03 | ||
|
|
938fe2a744 | ||
|
|
171432852b | ||
|
|
7608cf2de4 | ||
|
|
e480c4738d | ||
|
|
4cd7e8fd9a | ||
|
|
f1b94abefa | ||
|
|
5a54722848 | ||
|
|
cb0e119bed | ||
|
|
081a055115 | ||
|
|
6e27c2f2e9 | ||
|
|
d1d0bd39ba | ||
|
|
8d0c620257 | ||
|
|
d1884db236 | ||
|
|
73f211ecb1 | ||
|
|
8d2290977b | ||
|
|
25aac53d52 | ||
|
|
6b75568750 | ||
|
|
7cc131880c | ||
|
|
26465e37d7 | ||
|
|
697dcc0f6b | ||
|
|
ac7f866237 | ||
|
|
339ad5c2ed | ||
|
|
48f1e0da0a | ||
|
|
413adbe7bb | ||
|
|
667a8d714b | ||
|
|
2155b83c1f | ||
|
|
e6c17461a3 | ||
|
|
f68d3932c7 | ||
|
|
81f3845f34 | ||
|
|
75820cf62d | ||
|
|
3f0dc6b3af | ||
|
|
db6c500e6d | ||
|
|
578b77072a | ||
|
|
f94e9e9352 | ||
|
|
079ae1e24d | ||
|
|
2f3c189fda | ||
|
|
dec796d52f | ||
|
|
385ba6b51f | ||
|
|
050441922b | ||
|
|
567cbce8a8 | ||
|
|
eee29e0399 | ||
|
|
a0a8aa47a8 | ||
|
|
fe2946f76c | ||
|
|
17aba9322e | ||
|
|
53d803abf3 | ||
|
|
576a230341 | ||
|
|
bdaef6e76d | ||
|
|
e6c73c9974 | ||
|
|
d4316c155c | ||
|
|
a4f4337060 | ||
|
|
32dbfc4712 | ||
|
|
63142e3051 | ||
|
|
464d4d2842 | ||
|
|
b378ff788e | ||
|
|
20f1c0e2ea | ||
|
|
54e175a783 | ||
|
|
30dc1a5ddd | ||
|
|
47460e6e33 | ||
|
|
6fc13a0ff4 | ||
|
|
5751c08627 | ||
|
|
5e97205530 | ||
|
|
0495947604 | ||
|
|
516bd8c2cf | ||
|
|
d1b5f80fe5 | ||
|
|
e2ee24b3ac | ||
|
|
f23c207622 | ||
|
|
649f1bc084 | ||
|
|
17e575e350 | ||
|
|
57a32d98ef | ||
|
|
03340afe88 | ||
|
|
e7cef378e0 | ||
|
|
8da2f44ae2 | ||
|
|
a4a29d1e05 | ||
|
|
89f475cee1 | ||
|
|
96dbabae8d | ||
|
|
d30d272523 | ||
|
|
300f8d23b5 | ||
|
|
b7ac46b98b | ||
|
|
e91ad1b70e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -77,9 +77,8 @@ libzds/libzds.a
|
||||
lsstp/lsstp
|
||||
mon_tools/mon_fsstatd
|
||||
mon_tools/mon_procd
|
||||
opticsmon/opticsmon
|
||||
osasnmpd/osasnmpd
|
||||
pvattest/src/pvattest
|
||||
pvattest/tools/exchange_info
|
||||
qetharp/qetharp
|
||||
qethqoat/qethqoat
|
||||
systemd/cpacfstatsd.service
|
||||
|
||||
@@ -11,6 +11,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Axel Wirbser
|
||||
- Balint Reczey
|
||||
- Benjamin Block
|
||||
- Bjoern Walk
|
||||
- Brian C. Lane
|
||||
- Carsten Otte
|
||||
- Christian Borntraeger
|
||||
@@ -58,6 +59,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Horst Hummel
|
||||
- Ingo Franzki
|
||||
- Ingo Tuchscherer
|
||||
- Jakob Naucke
|
||||
- Jakub Čajka
|
||||
- Jan Glauber
|
||||
- Jan Höppner
|
||||
@@ -91,6 +93,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Michael Mueller
|
||||
- Mijo Safradin
|
||||
- Mikhail Zaslonko
|
||||
- Nihar Panda
|
||||
- Nikita Dubrovskii
|
||||
- Niklas Schnelle
|
||||
- Nikolay Gueorguiev
|
||||
@@ -107,6 +110,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Sa Liu
|
||||
- Sascha Silbe
|
||||
- Sebastian Ott
|
||||
- Sertonix
|
||||
- Seshagiri N. Ippili
|
||||
- Simon Sturm
|
||||
- Stefan Bader
|
||||
@@ -133,6 +137,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Ursula Braun
|
||||
- Utz Bacher
|
||||
- Vance Morris
|
||||
- VasiliyS
|
||||
- Vasily Gorbik
|
||||
- Viktor Mihajlovski
|
||||
- Vineeth Vijayan
|
||||
|
||||
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,6 +1,64 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __v2.37.0 (2025-02-07)__
|
||||
|
||||
For Linux kernel version: 6.13
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo.sh: Add details on CPU-measurement
|
||||
- dbginfo.sh: Add new crypto command
|
||||
- dbginfo.sh: Add overview commands and crypto update
|
||||
- dbginfo.sh: Adding kdump info
|
||||
- dbginfo.sh: Removing outdated email references
|
||||
- dbginfo.sh: Rework network section
|
||||
- dbginfo.sh: Update copyright 2nd year
|
||||
- pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
|
||||
- pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
|
||||
- pvimg: Add '--hdr-key' command line option to 'pvimg create'
|
||||
- pvimg: Rename '--key' into '--hdr-key' and use '--key' as an alias (for 'pvimg info')
|
||||
- pvsecret: Add support for retrievable secrets
|
||||
- ziorep_config: Add PCHID field to adapter report
|
||||
- ziorep_traffic: Add DEVBUSID column to traffic report
|
||||
- ziorep_utilization: Add --fcp-device parameter to print virtual adapter report
|
||||
- ziorep_utilization: Add PCHID column to physical adapter report
|
||||
- ziorep_utilization: Now prints only physical adapter report by default
|
||||
- ziorep_utilization: Swap Bus-ID and CHPID columns in virtual adapter report
|
||||
- zipl/boot: Increase section size for eckd_mv dumper
|
||||
- zkey: Add support for listing and importing protected virtualization secrets
|
||||
|
||||
Bug Fixes:
|
||||
- chpstat: Fix invalid utilization data on older kernels
|
||||
- opticsmon: Fix runaway loop in on_link_change()
|
||||
- zipl: Update inline assembly for GCC 15
|
||||
- zipl_helper.device-mapper: Add missed step in logical device resolution
|
||||
|
||||
* __v2.36.0 (2024-12-06)__
|
||||
|
||||
For Linux kernel version: 6.12
|
||||
|
||||
s390-tools: Define Rust MSRV as 1.75.0
|
||||
|
||||
Add new tools / libraries:
|
||||
- cpacfinfo: Tool to provide CPACF information
|
||||
- opticsmon: Tools to monitor optical modules for directly attached PCI based NICs
|
||||
- pvimg: Rust rewrite of genprotimg
|
||||
|
||||
Changes of existing tools:
|
||||
- chpstat: Add data bandwidth utilization column
|
||||
- chpstat: Add support for full CMCB
|
||||
- chpstat: Add support for new CMG types
|
||||
- dbginfo.sh: add overview commands and crypto update
|
||||
- hyptop: Support for structured output (json, json-seq, csv)
|
||||
- lszfcp: Add missing fallback marker for non-good fc_host port_state
|
||||
- lszfcp: Improve speed with many SCSI devices
|
||||
- pvattest: Add attestation policy check command
|
||||
- zipl: Add support of partitions of mirror md-devices
|
||||
|
||||
Bug Fixes:
|
||||
- lszcrypt: Fix wrong state showing up for removed AP queue within SE guest
|
||||
- lszfcp: Show device names line for zfcp_units without SCSI device
|
||||
|
||||
* __v2.35.0 (2024-10-01)__
|
||||
|
||||
For Linux kernel version: 6.11
|
||||
|
||||
6
Makefile
6
Makefile
@@ -9,18 +9,18 @@ include common.mak
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
BASELIB_DIRS = libutil libseckey
|
||||
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
||||
libkmipclient libcpumf libap libpv
|
||||
libkmipclient libcpumf libap libpv libzpci
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust
|
||||
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon
|
||||
|
||||
else
|
||||
BASELIB_DIRS =
|
||||
LIB_DIRS = libpv
|
||||
TOOL_DIRS = genprotimg rust
|
||||
TOOL_DIRS = rust
|
||||
endif
|
||||
|
||||
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
70
README.md
70
README.md
@@ -19,12 +19,17 @@ Package contents
|
||||
all s390-tools that are written in rust and require external crates.
|
||||
Disable the compilation of all tools in `rust/` using HAVE_CARGO=0
|
||||
See the `rust/README.md` for Details
|
||||
- cpacfinfo:
|
||||
Command line interface to get information about CP Assist for
|
||||
Cryptographic Functions (CPACF)
|
||||
- pvattest:
|
||||
Create, perform, and verify IBM Secure Execution attestation measurements.
|
||||
- pvapconfig:
|
||||
Automatic configure APQNs within an SE KVM guest
|
||||
- pvsecret:
|
||||
Manage secrets for IBM Secure Execution guests
|
||||
- pvimg:
|
||||
Create and inspect IBM Secure Execution images
|
||||
|
||||
* dasdfmt:
|
||||
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||
@@ -42,7 +47,8 @@ Package contents
|
||||
Display unique DASD ID, either UID or volser.
|
||||
|
||||
* genprotimg:
|
||||
Create a protected virtualization image.
|
||||
Create an IBM Secure Execution (protected virtualization) image. The
|
||||
genprotimg command is a symbolic link to the `pvimg create` command.
|
||||
|
||||
* udev rules:
|
||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||
@@ -304,26 +310,27 @@ HAVE_FUSE=0`".
|
||||
The following table provides an overview of the used libraries and
|
||||
build options:
|
||||
|
||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||
|----------------|:------------------:|:-------------------------------------:|
|
||||
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||
| | | hsavmcore |
|
||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||
| | | libkmipclient, pvattest, zgetdump, |
|
||||
| | | rust/pvsecret, |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest, zgetdump |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
|
||||
| | | pvattest, rust/pvsecret, |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||
|--------------|:------------------:|:--------------------------------------:|
|
||||
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump, |
|
||||
| | | hsavmcore |
|
||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | zkey, libekmfweb, libkmipclient, |
|
||||
| | | zgetdump, rust/pvattest, rust/pvimg, |
|
||||
| | | zgetdump/pvsecret, opticsmon |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | zgetdump |
|
||||
| libcurl | `HAVE_LIBCURL` | libekmfweb, libkmipclient, rust/pvimg, |
|
||||
| | | rust/pvattest, rust/pvsecret, |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||
| libnl3 | `HAVE_LIBNL3` | opticsmon |
|
||||
|
||||
This table lists additional build or install options:
|
||||
|
||||
@@ -355,13 +362,13 @@ the different tools are provided:
|
||||
* dbginfo.sh:
|
||||
The tar package is required to archive collected data.
|
||||
|
||||
* 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`.
|
||||
* rust/pvimg:
|
||||
For building pvimg you need OpenSSL version 1.1.1 or newer
|
||||
installed (openssl-devel.rpm). Also required is cargo and libcurl.
|
||||
Tip: you may skip the pvimg build by adding
|
||||
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
||||
|
||||
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
||||
The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl.
|
||||
|
||||
* rust/pvattest:
|
||||
For building pvattest you need OpenSSL version 1.1.1 or newer
|
||||
@@ -369,8 +376,13 @@ the different tools are provided:
|
||||
Tip: you may skip the pvattest build by adding
|
||||
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
||||
|
||||
The runtime requirements are: openssl-libs (>= 1.1.1) and
|
||||
glib2.56 or newer.
|
||||
The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl.
|
||||
|
||||
* opticsmon:
|
||||
For building opticsmon OpenSSL and the Netlink Library Suite (libnl3) are
|
||||
required.
|
||||
Tip: you may skip the opticsmon build by adding
|
||||
`HAVE_OPENSSL=0` or `HAVE_LIBNL3=0`
|
||||
|
||||
* osasnmpd:
|
||||
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||
|
||||
@@ -32,7 +32,7 @@ endif
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION := 2
|
||||
RELEASE := 35
|
||||
RELEASE := 37
|
||||
PATCHLEVEL := 0
|
||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -48,7 +48,7 @@ export S390_TEST_LIB_PATH=$(rootdir)/s390-tools-testsuite/lib
|
||||
# $ make HOST_ARCH=s390x CROSS_COMPILE="s390x-linux-gnu-"
|
||||
#
|
||||
|
||||
CROSS_COMPILE =
|
||||
CROSS_COMPILE ?=
|
||||
|
||||
#
|
||||
# Commands can be overwritten on the command line with "make <VAR>=<VALUE>":
|
||||
@@ -504,6 +504,10 @@ $(rootdir)/libpv/libpv.a: $(rootdir)/libpv
|
||||
$(MAKE) -C $(rootdir)/libpv libpv.a
|
||||
.PHONY: $(rootdir)/libpv
|
||||
|
||||
$(rootdir)/libzpci/libzpci.a: $(rootdir)/libzpci
|
||||
$(MAKE) -C $(rootdir)/libzpci libzpci.a
|
||||
.PHONY: $(rootdir)/libzpci
|
||||
|
||||
$(rootdir)/zipl/boot/.loaders:
|
||||
$(MAKE) -C $(rootdir)/zipl/boot/ .loaders
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ All counters are initially disabled and must be switched on to measure
|
||||
CPACF activities of the system. There is a slight performance penalty with
|
||||
CPACF counters enabled.
|
||||
|
||||
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
|
||||
counters are only available on LPARs and have to be authorized. If
|
||||
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
|
||||
counters are only available on LPARs and have to be authorized. If
|
||||
they are available, the counters
|
||||
.B des,
|
||||
.B aes,
|
||||
@@ -55,14 +55,131 @@ they are available, the counters
|
||||
.B rng,
|
||||
and
|
||||
.B ecc
|
||||
are made available. These counters can individually be activated,
|
||||
reset, printed, or deactivated. PAI counters are a lot more detailed.
|
||||
are made available. These counters can individually be activated,
|
||||
reset, printed, or deactivated. PAI counters are a lot more detailed.
|
||||
The user interface only offers the counters
|
||||
.B pai_user
|
||||
and
|
||||
.B pai_kernel
|
||||
to count CPACF usage in user-space or kernel-space. When printing
|
||||
these counters, detailed counters are shown. The detailed names are:
|
||||
to count CPACF usage in user-space or kernel-space. When printing
|
||||
these counters, detailed counters are shown.
|
||||
|
||||
A complete list of counters can be found at the end of this manpage.
|
||||
|
||||
Note that the counters starting with PCKMO and Reserved are only
|
||||
available in the pai_kernel set. Also note that the counters are
|
||||
designed to count successful operations. In the case of KMA this
|
||||
means only complete GCM operations including final hashing are
|
||||
counted.
|
||||
|
||||
Note: CPU-MF based CPACF performance counters are available on LPARs
|
||||
only. PAI counters are available on all hypervisors. For security
|
||||
reasons only members of the group \fIcpacfstats\fR are allowed to run
|
||||
the cpacfstats client application.
|
||||
|
||||
Example usage scenario:
|
||||
.P
|
||||
1. Start the cpacfstatsd daemon with root privileges.
|
||||
.P
|
||||
2. Check for successful startup by using the ps and syslog commands.
|
||||
.P
|
||||
3. Enable the CPACF counters of interest. For example, enable all counters
|
||||
by issuing cpacfstats -e.
|
||||
.P
|
||||
4. Run your applications.
|
||||
.P
|
||||
5. Display counter values by using the cpacfstats command. Reset the
|
||||
cryptographic counters as required. To reset, use, for example, cpacfstats
|
||||
-r.
|
||||
.P
|
||||
6. Disable all the CPACF measurements, for example, by using cpacfstats
|
||||
-d.
|
||||
.P
|
||||
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR or \fB\-\-help\fR
|
||||
Display help information for the command.
|
||||
.TP
|
||||
\fB\-v\fR or \fB\-\-version\fR
|
||||
Display version and copyright information for the command.
|
||||
.TP
|
||||
\fB\-e\fR or \fB\-\-enable\fR [counter]
|
||||
Enable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are enabled. Enabling a counter does not reset
|
||||
it. New events are added to the current counter value.
|
||||
.TP
|
||||
\fB\-d\fR or \fB\-\-disable\fR [counter]
|
||||
Disable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are disabled. Disabling a counter does not reset
|
||||
it. The counter value is preserved when a counter is disabled, and
|
||||
counting will resume using the preserved value when the counter is
|
||||
re-enabled.
|
||||
.TP
|
||||
\fB\-r\fR or \fB\-\-reset\fR [counter]
|
||||
Reset one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are reset to 0.
|
||||
.TP
|
||||
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
|
||||
Display the value of one or all CPACF performance counters. The
|
||||
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
|
||||
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
|
||||
\fBall\fR. If the counter argument is omitted or if there is no
|
||||
argument, all performance counters are displayed. If the optional
|
||||
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
|
||||
counters that have a non-zero value are printed.
|
||||
.TP
|
||||
\fB\-j\fR or \fB\-\-json\fR
|
||||
Display all activated counters in JSON format. The JSON contains an
|
||||
array of counter objects. Each object contains the property
|
||||
.B counter
|
||||
specifying either a CPU-MF counter of one of the detailed PAI counter.
|
||||
Additional properties include
|
||||
.B error
|
||||
an error number if the counter could not be read,
|
||||
.B value
|
||||
the counter value if the counter could be read,
|
||||
.B space
|
||||
for PAI counters to specify
|
||||
.B user
|
||||
or
|
||||
.B kernel
|
||||
space counter set, and
|
||||
.B counterid
|
||||
for PAI counters to specify the PAI counter number as specified in the
|
||||
Principles of Operation.
|
||||
.TP
|
||||
The default command is --print all.
|
||||
.
|
||||
.SH FILES
|
||||
.nf
|
||||
/run/cpacfstatsd_socket
|
||||
.fi
|
||||
.
|
||||
.SH RETURN VALUE
|
||||
.IP 0
|
||||
Successful program execution.
|
||||
.IP 1
|
||||
An error occurred, reasons include: invalid argument, cpacfstatsd could not
|
||||
be reached (check that the daemon is running), insufficient access rights,
|
||||
version mismatch between client and daemon, or the application is out of
|
||||
memory. The application prints a message with the details of the error and
|
||||
the errno value.
|
||||
.
|
||||
.SH NOTES
|
||||
ECC counters are only available since z15. cpacfstats will show the
|
||||
counters as \fIunsupported\fR if the hardware does not support ECC
|
||||
counters.
|
||||
.
|
||||
.SH APPENDIX
|
||||
The detailed pai counter names are:
|
||||
.RS
|
||||
.IP \(bu
|
||||
KM DES,
|
||||
@@ -376,118 +493,39 @@ PCKMO ECC Ed448,
|
||||
Reserved 1, and
|
||||
.IP \(bu
|
||||
Reserved 2.
|
||||
.IP \(bu
|
||||
KM AES-XTS (full) 128bit
|
||||
.IP \(bu
|
||||
KM AES-XTS (full) 256bit
|
||||
.IP \(bu
|
||||
KM AES-XTS (full) 128bit protected key
|
||||
.IP \(bu
|
||||
KM AES-XTS (full) 256bit protected key
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 224
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 256
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 384
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 512
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 224 protected key
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 256 protected key
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 384 protected key
|
||||
.IP \(bu
|
||||
KMAC HMAC SHA 512 protected key
|
||||
.IP \(bu
|
||||
PCKMO HMAC 512 protected key
|
||||
.IP \(bu
|
||||
PCKMO HMAC 1024 protected key
|
||||
.IP \(bu
|
||||
PCKMO AES-XTS 128bit double key protected key
|
||||
.IP \(bu
|
||||
PCKMO AES-XTS 256bit double key protected key
|
||||
.RE
|
||||
Note that the counters starting with PCKMO and Reserved are only
|
||||
available in the pai_kernel set. Also note that the counters are
|
||||
designed to count successful operations. In the case of KMA this
|
||||
means only complete GCM operations including final hashing are
|
||||
counted.
|
||||
|
||||
Note: CPU-MF based CPACF performance counters are available on LPARs
|
||||
only. PAI counters are available on all hypervisors. For security
|
||||
reasons only members of the group \fIcpacfstats\fR are allowed to run
|
||||
the cpacfstats client application.
|
||||
|
||||
Example usage scenario:
|
||||
.P
|
||||
1. Start the cpacfstatsd daemon with root privileges.
|
||||
.P
|
||||
2. Check for successful startup by using the ps and syslog commands.
|
||||
.P
|
||||
3. Enable the CPACF counters of interest. For example, enable all counters
|
||||
by issuing cpacfstats -e.
|
||||
.P
|
||||
4. Run your applications.
|
||||
.P
|
||||
5. Display counter values by using the cpacfstats command. Reset the
|
||||
cryptographic counters as required. To reset, use, for example, cpacfstats
|
||||
-r.
|
||||
.P
|
||||
6. Disable all the CPACF measurements, for example, by using cpacfstats
|
||||
-d.
|
||||
.P
|
||||
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR or \fB\-\-help\fR
|
||||
Display help information for the command.
|
||||
.TP
|
||||
\fB\-v\fR or \fB\-\-version\fR
|
||||
Display version and copyright information for the command.
|
||||
.TP
|
||||
\fB\-e\fR or \fB\-\-enable\fR [counter]
|
||||
Enable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are enabled. Enabling a counter does not reset
|
||||
it. New events are added to the current counter value.
|
||||
.TP
|
||||
\fB\-d\fR or \fB\-\-disable\fR [counter]
|
||||
Disable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are disabled. Disabling a counter does not reset
|
||||
it. The counter value is preserved when a counter is disabled, and
|
||||
counting will resume using the preserved value when the counter is
|
||||
re-enabled.
|
||||
.TP
|
||||
\fB\-r\fR or \fB\-\-reset\fR [counter]
|
||||
Reset one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are reset to 0.
|
||||
.TP
|
||||
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
|
||||
Display the value of one or all CPACF performance counters. The
|
||||
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
|
||||
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
|
||||
\fBall\fR. If the counter argument is omitted or if there is no
|
||||
argument, all performance counters are displayed. If the optional
|
||||
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
|
||||
counters that have a non-zero value are printed.
|
||||
.TP
|
||||
\fB\-j\fR or \fB\-\-json\fR
|
||||
Display all activated counters in JSON format. The JSON contains an
|
||||
array of counter objects. Each object contains the property
|
||||
.B counter
|
||||
specifying either a CPU-MF counter of one of the detailed PAI counter.
|
||||
Additional properties include
|
||||
.B error
|
||||
an error number if the counter could not be read,
|
||||
.B value
|
||||
the counter value if the counter could be read,
|
||||
.B space
|
||||
for PAI counters to specify
|
||||
.B user
|
||||
or
|
||||
.B kernel
|
||||
space counter set, and
|
||||
.B counterid
|
||||
for PAI counters to specify the PAI counter number as specified in the
|
||||
Principles of Operation.
|
||||
.TP
|
||||
The default command is --print all.
|
||||
.
|
||||
.SH FILES
|
||||
.nf
|
||||
/run/cpacfstatsd_socket
|
||||
.fi
|
||||
.
|
||||
.SH RETURN VALUE
|
||||
.IP 0
|
||||
Successful program execution.
|
||||
.IP 1
|
||||
An error occurred, reasons include: invalid argument, cpacfstatsd could not
|
||||
be reached (check that the daemon is running), insufficient access rights,
|
||||
version mismatch between client and daemon, or the application is out of
|
||||
memory. The application prints a message with the details of the error and
|
||||
the errno value.
|
||||
.
|
||||
.SH NOTES
|
||||
ECC counters are only available since z15. cpacfstats will show the
|
||||
counters as \fIunsupported\fR if the hardware does not support ECC
|
||||
counters.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR cpacfstatsd (8)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.SH NAME
|
||||
\*c \- extract CPU Measurement Facilities counter sets
|
||||
lshwc \- extract CPU Measurement Facilities counter sets
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.SH NAME
|
||||
\*c \- list Processor Activity Instrumentation (PAI) counters
|
||||
lspai \- list Processor Activity Instrumentation (PAI) counters
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
|
||||
.
|
||||
.SH NAME
|
||||
\*c \- record and report Processor Activity Instrumentation (PAI) counters
|
||||
pai \- record and report Processor Activity Instrumentation (PAI) counters
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
|
||||
5
genprotimg/.gitignore
vendored
5
genprotimg/.gitignore
vendored
@@ -1,5 +0,0 @@
|
||||
tags
|
||||
compile_commands.json
|
||||
src/.check-dep-genprotimg
|
||||
src/.detect-openssl.dep.c
|
||||
src/genprotimg
|
||||
@@ -1,25 +0,0 @@
|
||||
# Common definitions
|
||||
include ../common.mak
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
PKGDATADIR := "$(TOOLS_DATADIR)/genprotimg"
|
||||
TESTS :=
|
||||
SUBDIRS := boot src man
|
||||
RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
|
||||
all: all-recursive
|
||||
|
||||
install: install-recursive
|
||||
$(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(DESTDIR)$(PKGDATADIR)"
|
||||
|
||||
clean: clean-recursive
|
||||
|
||||
$(RECURSIVE_TARGETS):
|
||||
@target=`echo $@ |sed s/-recursive//`; \
|
||||
for d in $(SUBDIRS); do \
|
||||
$(MAKE) -C $$d $$target || exit 1; \
|
||||
done
|
||||
|
||||
.PHONY: all install clean $(RECURSIVE_TARGETS)
|
||||
@@ -1,85 +0,0 @@
|
||||
# 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 |
|
||||
@@ -1,12 +0,0 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
all:
|
||||
|
||||
install:
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
|
||||
$(INSTALL) -m 644 -c genprotimg.1 $(DESTDIR)$(MANDIR)/man1
|
||||
|
||||
clean:
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -1,159 +0,0 @@
|
||||
.\" 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 1 "May 2022" "s390-tools"
|
||||
.SH NAME
|
||||
genprotimg \- Create a protected virtualization image
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY
|
||||
.B genprotimg
|
||||
\fB\-k\fR \fIHOST_KEY_DOCUMENT\fR...
|
||||
\fB\-C\fR \fICERTIFICATE\fR...
|
||||
\fB\-i\fR \fIVMLINUZ\fR
|
||||
[\fB\-r\fR \fIRAMDISK\fR]
|
||||
[\fB\-p\fR \fIPARMFILE\fR]
|
||||
\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, certificates for the host-key document
|
||||
verification, and an output file name. Optionally, specify an initial
|
||||
RAM filesystem, and a file containing the kernel parameters. If the
|
||||
command should be run offline, use the \fB\-\-offline\fR option and
|
||||
specify the certificate revocation lists (CRLs) by using the
|
||||
\fB\-\-crl\fR option. Should special circumstances require it, you can
|
||||
optionally specify your own keys for the encryption by using the
|
||||
experimental options. For all certificates, CRLs, and host-key
|
||||
documents, both the PEM and DER input formats are supported. In the
|
||||
resulting image file, a plain text boot loader, the encrypted
|
||||
components for kernel, initial RAM disk, kernel parameters, and the
|
||||
encrypted and integrity-protected header are concatenated. The header
|
||||
contains metadata necessary for running the guest in protected mode.
|
||||
.PP
|
||||
Use this image file as a kernel image for zipl or for a direct kernel
|
||||
boot using QEMU.
|
||||
|
||||
.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\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR
|
||||
Specifies the certificate that is used to establish a chain of trust
|
||||
for the verification of the host-key documents. Specify this option
|
||||
twice to specify the IBM Z signing key and the intermediate CA
|
||||
certificate (signed by the root CA).
|
||||
Required. Ignored if \fB--no-verify\fP is specified.
|
||||
.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\-\-crl\fR=\fI\,FILE\/\fR
|
||||
Specifies the revocation list that is used to check whether a
|
||||
certificate of the chain of trust is revoked. Specify this option
|
||||
multiple times to use multiple CRLs. Optional.
|
||||
.TP
|
||||
\fB\-\-offline\fR
|
||||
Specifies offline mode, in which no attempt is made to download
|
||||
CRLs. Optional.
|
||||
.TP
|
||||
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
||||
Specifies the root CA certificate for the verification. If omitted,
|
||||
the system wide root CAs installed on the system is used. Use
|
||||
this only if you trust the specified certificate. Optional.
|
||||
.TP
|
||||
\fB\-\-no-verify\fR
|
||||
Does not require the host-key documents to be valid.
|
||||
Do not use for a production image unless you verified
|
||||
the host-key document before. Optional.
|
||||
.TP
|
||||
\fB\-\-comm\-key\fR=\fI\,FILE\/\fR
|
||||
Specifies the customer communication key (CCK). This key is used for the
|
||||
PV guest dump encryption and to derive the CCK-derived extension secret
|
||||
used for add-secret requests. Use a secure, random, plaintext AES-256
|
||||
GCM key. Optional.
|
||||
.TP
|
||||
\fB\-\-enable\-dump\fR
|
||||
Enable PV guest dumps. Requires the \fB\-\-comm\-key\fR option. Optional.
|
||||
.TP
|
||||
\fB\-\-disable\-dump\fR
|
||||
Disable PV guest dumps. This is the default.
|
||||
.TP
|
||||
\fB\-\-enable\-cck\-extension\-secret\fR
|
||||
Add-secret requests must provide an extension secret that matches the
|
||||
CCK-derived extension secret. Requires the \fB\-\-comm\-key\fR option.
|
||||
Optional.
|
||||
.TP
|
||||
\fB\-\-disable\-cck\-extension\-secret\fR
|
||||
Add-secret requests don't have to provide an extension secret. This is
|
||||
the default.
|
||||
.TP
|
||||
\fB\-\-enable\-pckmo\fR
|
||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||
functions. This is the default.
|
||||
.TP
|
||||
\fB\-\-disable\-pckmo\fR
|
||||
Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||
functions. Optional.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Prints version information, then exits.
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
These are examples to 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, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the
|
||||
IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
||||
host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in
|
||||
\fI\,comm-key\/\fR, which is used for the PV guest dump support in the second
|
||||
example.
|
||||
|
||||
Generate a protected virtualization image:
|
||||
|
||||
.PP
|
||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||
|
||||
Generate a protected virtualization image with PV guest dump support:
|
||||
.PP
|
||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
|
||||
.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)
|
||||
@@ -1,104 +0,0 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
bin_PROGRAM = genprotimg
|
||||
|
||||
PKGDATADIR ?= "$(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 \
|
||||
-Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \
|
||||
-Wpointer-arith -Wno-error=inline \
|
||||
$(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 utils/curl.c \
|
||||
$(NULL)
|
||||
$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
||||
|
||||
ALL_CFLAGS += -DPKGDATADIR=$(PKGDATADIR) \
|
||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
||||
-DOPENSSL_API_COMPAT=0x10100000L \
|
||||
$(WARNINGS) \
|
||||
$(NULL)
|
||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS)
|
||||
|
||||
|
||||
GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0)
|
||||
GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0)
|
||||
LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto)
|
||||
LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto)
|
||||
LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl)
|
||||
LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl)
|
||||
|
||||
BUILD_TARGETS := skip-$(bin_PROGRAM)
|
||||
INSTALL_TARGETS := skip-$(bin_PROGRAM)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
ifneq (${HAVE_LIBCURL},0)
|
||||
BUILD_TARGETS := $(bin_PROGRAM)
|
||||
INSTALL_TARGETS := install-$(bin_PROGRAM)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
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 <openssl/evp.h>" > $@
|
||||
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.")
|
||||
$(call check_dep, \
|
||||
"$(bin_PROGRAM)", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0")
|
||||
touch $@
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2020, 2022
|
||||
*
|
||||
* 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_H
|
||||
#define COMMON_H
|
||||
|
||||
#define GETTEXT_PACKAGE "genprotimg"
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "boot/linux_layout.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
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "pv/pv_args.h"
|
||||
#include "pv/pv_image.h"
|
||||
#include "utils/crypto.h"
|
||||
#include "utils/curl.h"
|
||||
|
||||
enum {
|
||||
LOG_LEVEL_CRITICAL = 0,
|
||||
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);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) __init(void);
|
||||
static void __attribute__((destructor)) __cleanup(void);
|
||||
gint main(gint argc, gchar *argv[])
|
||||
{
|
||||
g_autoptr(PvArgs) args = pv_args_new();
|
||||
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);
|
||||
g_clear_pointer(&img, pv_img_free);
|
||||
g_clear_pointer(&args, pv_args_free);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
static void __init(void)
|
||||
{
|
||||
pv_crypto_init();
|
||||
if (curl_init() != 0)
|
||||
g_abort();
|
||||
}
|
||||
|
||||
static void __cleanup(void)
|
||||
{
|
||||
curl_cleanup();
|
||||
pv_crypto_cleanup();
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/* IBM signing key subject */
|
||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
||||
#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6
|
||||
|
||||
/* Minimum security level for the keys/certificates used to establish a chain of
|
||||
* trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html
|
||||
* for details).
|
||||
*/
|
||||
#define PV_CERTS_SECURITY_LEVEL 2
|
||||
|
||||
union ecdh_pub_key {
|
||||
struct {
|
||||
uint8_t x[80];
|
||||
uint8_t y[80];
|
||||
};
|
||||
uint8_t data[160];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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 <openssl/sha.h>
|
||||
|
||||
#include "boot/psw.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
|
||||
|
||||
/* Internal helper macro */
|
||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||
|
||||
/* Plaintext control flags */
|
||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */
|
||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
|
||||
/* Secret control flags */
|
||||
#define PV_SCF_CCK_EXTENSION_SECRET_ENFORCMENT \
|
||||
__PV_BIT(1) /* All add-secret requests must provide an extension secret */
|
||||
|
||||
/* 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
|
||||
@@ -1,556 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
#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. For all certificates, revocation\n"
|
||||
"lists, and host-key documents, both the PEM and DER input formats are\n"
|
||||
"supported.";
|
||||
|
||||
static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2)
|
||||
{
|
||||
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)
|
||||
{
|
||||
const PvControlFlagsArgs *cf_args = &args->cf_args;
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
/* Check for mutually exclusive arguments */
|
||||
if (cf_args->pcf &&
|
||||
!(cf_args->enable_pckmo == PV_NOT_SET && cf_args->enable_dump == PV_NOT_SET)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-pcf' option cannot be used with the '--(enable|disable)-pckmo' or"
|
||||
" '--(enable|disable)-dump' flags.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cf_args->scf && !(cf_args->enable_cck_extension_secret_enforcement == PV_NOT_SET)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-scf' option cannot be used with the '--(enable|disable)-extension-secret-required' flags.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for unused arguments */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Check for mandatory arguments */
|
||||
if (cf_args->enable_dump == PV_TRUE && !args->cust_comm_key_path) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--enable-dump' requires the '--comm-key' option.\nUse 'genprotimg "
|
||||
"--help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
if (cf_args->enable_cck_extension_secret_enforcement == PV_TRUE &&
|
||||
!args->cust_comm_key_path) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--enable-cck-extension-secret' requires the '--comm-key' option.\nUse 'genprotimg "
|
||||
"--help' for more information"));
|
||||
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 &&
|
||||
(!args->untrusted_cert_paths ||
|
||||
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Either specify the IBM Z signing key and intermediate CA certificate\n"
|
||||
"by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
|
||||
"host-key document verification completely (at your own risk)."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, "--comm-key"))
|
||||
args_option = &args->cust_comm_key_path;
|
||||
if (g_str_equal(option, "--root-ca"))
|
||||
args_option = &args->root_ca_path;
|
||||
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
||||
args_option = &args->output_path;
|
||||
if (g_str_equal(option, "--x-comp-key"))
|
||||
args_option = &args->xts_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->cf_args.pcf;
|
||||
if (g_str_equal(option, "--x-psw"))
|
||||
args_option = &args->psw_addr;
|
||||
if (g_str_equal(option, "--x-scf"))
|
||||
args_option = &args->cf_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 MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE) (cb_##FLAG##_##VALUE)
|
||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, VALUE) \
|
||||
static gboolean MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE)(const gchar *option G_GNUC_UNUSED, \
|
||||
const gchar *value G_GNUC_UNUSED, \
|
||||
PvArgs *args, GError **err) \
|
||||
{ \
|
||||
if (!(args->cf_args.enable_##FLAG == PV_NOT_SET || \
|
||||
args->cf_args.enable_##FLAG == VALUE)) { \
|
||||
g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \
|
||||
"'--enable-" #FLAG "' and '--disable-" #FLAG \
|
||||
"' are mutually exclusive"); \
|
||||
return FALSE; \
|
||||
} \
|
||||
args->cf_args.enable_##FLAG = VALUE; \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CBS(FLAG) \
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_TRUE) \
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_FALSE)
|
||||
|
||||
#define MUT_EXCL_BOOL_FLAG(NAME, FLAG, ENABLE_DESC, DISABLE_DESC) \
|
||||
{ \
|
||||
.long_name = "enable-" #NAME, \
|
||||
.short_name = 0, \
|
||||
.flags = G_OPTION_FLAG_NO_ARG, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, \
|
||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_TRUE), \
|
||||
.description = ENABLE_DESC, \
|
||||
}, \
|
||||
{ \
|
||||
.long_name = "disable-" #NAME, .short_name = 0, .flags = G_OPTION_FLAG_NO_ARG, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, \
|
||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_FALSE), \
|
||||
.description = DISABLE_DESC, \
|
||||
}
|
||||
|
||||
#define INDENT " "
|
||||
|
||||
/* Define the callbacks for mutually exclusive command line flags */
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(dump);
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(pckmo);
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(cck_extension_secret_enforcement);
|
||||
|
||||
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. Specify this option multiple times\n" INDENT
|
||||
"to enable the image to run on more than one host."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "cert",
|
||||
.short_name = 'C',
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY,
|
||||
.arg_data = &args->untrusted_cert_paths,
|
||||
.description = _(
|
||||
"FILE contains a certificate that is used to\n" INDENT
|
||||
"establish a chain of trust for the verification\n" INDENT
|
||||
"of the host-key documents. The IBM Z signing\n" INDENT
|
||||
"key and intermediate CA certificate (signed\n" INDENT
|
||||
"by the root CA) are required."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "output",
|
||||
.short_name = 'o',
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.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_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_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") },
|
||||
MUT_EXCL_BOOL_FLAG(dump, dump,
|
||||
_("Enable PV guest dumps (optional). This option\n" INDENT
|
||||
"requires the '--comm-key' option."),
|
||||
_("Disable PV guest dumps (default).")),
|
||||
MUT_EXCL_BOOL_FLAG(
|
||||
cck-extension-secret, cck_extension_secret_enforcement,
|
||||
_("Add-secret requests must provide an extension\n" INDENT
|
||||
"secret that matches the CCK-derived extension\n" INDENT
|
||||
"secret (optional). This option requires the\n" INDENT
|
||||
"'--comm-key' option."),
|
||||
_("Add-secret requests don't have to provide\n" INDENT
|
||||
"the CCK-derived extension secret (default).")),
|
||||
MUT_EXCL_BOOL_FLAG(pckmo, pckmo,
|
||||
_("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)."),
|
||||
_("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (optional).")),
|
||||
{ .long_name = "comm-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"FILE contains the customer communication key\n" INDENT
|
||||
"(CCK) (optional)."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "crl",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY,
|
||||
.arg_data = &args->crl_paths,
|
||||
.description = _(
|
||||
"FILE contains a certificate revocation list\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "offline",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_NONE,
|
||||
.arg_data = &args->offline,
|
||||
.description = _("Don't download CRLs (optional)."),
|
||||
.arg_description = NULL },
|
||||
{ .long_name = "root-ca",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Set FILE as the trusted root CA and don't use the\n" INDENT
|
||||
"root CAs that are installed on the system (optional)."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "no-verify",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.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-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; mutually exclusive with\n" INDENT
|
||||
"'--(enable|disable)-pckmo'; default: '0xe0'."),
|
||||
.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; mutually exclusive with\n" INDENT
|
||||
"'--(enable|disable)-cck-extension-secret';\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);
|
||||
/* `args->cf_args` is implicitly initialized with zeros since
|
||||
* `g_new0` is used. So there is no reason to explicitly
|
||||
* initialize the values as PV_NOT_SET == 0.
|
||||
*/
|
||||
return g_steal_pointer(&args);
|
||||
}
|
||||
|
||||
void pv_args_free(PvArgs *args)
|
||||
{
|
||||
if (!args)
|
||||
return;
|
||||
|
||||
g_free(args->cf_args.pcf);
|
||||
g_free(args->cf_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_free(args->root_ca_path);
|
||||
g_strfreev(args->crl_paths);
|
||||
g_strfreev(args->untrusted_cert_paths);
|
||||
g_strfreev(args->host_keys);
|
||||
g_free(args->xts_key_path);
|
||||
g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free);
|
||||
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);
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
|
||||
#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 enum pv_tristate {
|
||||
PV_NOT_SET = 0,
|
||||
PV_TRUE,
|
||||
PV_FALSE,
|
||||
} PvTristate;
|
||||
/* The value of PV_NOT_SET is not allowed to be changed */
|
||||
STATIC_ASSERT(PV_NOT_SET == 0)
|
||||
|
||||
typedef struct {
|
||||
gchar *pcf;
|
||||
gchar *scf;
|
||||
/* Add-secret requests do require CCK-extension secrets */
|
||||
PvTristate enable_cck_extension_secret_enforcement;
|
||||
PvTristate enable_dump;
|
||||
PvTristate enable_pckmo;
|
||||
} PvControlFlagsArgs;
|
||||
|
||||
typedef struct {
|
||||
gint log_level;
|
||||
gint no_verify;
|
||||
gboolean offline;
|
||||
PvControlFlagsArgs cf_args;
|
||||
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 *root_ca_path; /* Trusted root CA used for the verification of the
|
||||
* chain of trust (if specified).
|
||||
*/
|
||||
gchar **untrusted_cert_paths;
|
||||
gchar **crl_paths;
|
||||
gchar *xts_key_path;
|
||||
GSList *comps;
|
||||
gchar *output_path;
|
||||
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
|
||||
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.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 PvBuffer *buf,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(buf);
|
||||
|
||||
g_autoptr(PvBuffer) dup_buf = pv_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:
|
||||
pv_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(PvBuffer) enc_buf = NULL;
|
||||
|
||||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
||||
g_autoptr(PvBuffer) new = NULL;
|
||||
|
||||
/* create a page aligned copy */
|
||||
new = pv_buffer_dup(component->buf, TRUE);
|
||||
pv_buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&new);
|
||||
}
|
||||
enc_buf = encrypt_buf(parms, component->buf, err);
|
||||
if (!enc_buf)
|
||||
return -1;
|
||||
|
||||
pv_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(PvBuffer) buf = NULL;
|
||||
|
||||
buf = pv_buffer_dup(component->buf, TRUE);
|
||||
pv_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();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 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 PvBuffer *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"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 PvBuffer *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();
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
PvBuffer *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 PvBuffer *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
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/psw.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, PvBuffer **pld_digest,
|
||||
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||||
uint64_t *nep, GError **err)
|
||||
{
|
||||
g_autoptr(PvBuffer) tmp_pld_digest = NULL;
|
||||
g_autoptr(PvBuffer) tmp_ald_digest = NULL;
|
||||
g_autoptr(PvBuffer) 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);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/psw.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, PvBuffer **pld_digest,
|
||||
PvBuffer **ald_digest, PvBuffer **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
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
|
||||
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,
|
||||
PV_ERROR_CURL_INIT_FAILED,
|
||||
PV_ERROR_DOWNLOAD_FAILED,
|
||||
} 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,
|
||||
PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD,
|
||||
PV_CRYPTO_ERROR_EXPIRED,
|
||||
PV_CRYPTO_ERROR_NOT_VALID_YET,
|
||||
PV_CRYPTO_ERROR_LOAD_CRL,
|
||||
PV_CRYPTO_ERROR_NO_PUBLIC_KEY,
|
||||
PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM,
|
||||
PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH,
|
||||
PV_CRYPTO_ERROR_INVALID_URI,
|
||||
PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED,
|
||||
PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID,
|
||||
PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID,
|
||||
PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
||||
PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
||||
PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE,
|
||||
PV_CRYPTO_ERROR_NO_CRL,
|
||||
PV_CRYPTO_ERROR_LOAD_ROOT_CA,
|
||||
PV_CRYPTO_ERROR_LOAD_DEFAULT_CA,
|
||||
PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
|
||||
PV_CRYPTO_ERROR_WRONG_CA_USED,
|
||||
PV_CRYPTO_ERROR_SKID_AKID_MISMATCH,
|
||||
PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND,
|
||||
PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL,
|
||||
PV_CRYPTO_ERROR_NO_CRLDP,
|
||||
PV_CRYPTO_ERROR_CERT_REVOKED,
|
||||
} PvCryptoErrors;
|
||||
|
||||
#endif
|
||||
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/psw.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_PCF_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, PvBuffer *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 PvBuffer aad_part = { .data = buf->data, .size = aad_len };
|
||||
PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||||
.size = enc_len };
|
||||
PvBuffer 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(PvBuffer) pld = NULL;
|
||||
g_autoptr(PvBuffer) ald = NULL;
|
||||
g_autoptr(PvBuffer) 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 PvBuffer *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));
|
||||
}
|
||||
}
|
||||
|
||||
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
{
|
||||
uint32_t hdr_size = pv_hdr_size(hdr);
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
|
||||
ret = pv_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);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <stdint.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);
|
||||
PvBuffer *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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/psw.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 */
|
||||
PvBuffer *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 */
|
||||
PvBuffer *cust_root_key;
|
||||
PvBuffer *gcm_iv;
|
||||
const EVP_CIPHER *gcm_cipher;
|
||||
/* Information for the IPIB and PV header */
|
||||
uint64_t pcf;
|
||||
uint64_t scf;
|
||||
PvBuffer *cust_comm_key;
|
||||
const EVP_CIPHER *cust_comm_cipher;
|
||||
PvBuffer *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, PvBuffer **pld, PvBuffer **ald,
|
||||
PvBuffer **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
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "boot/ipl.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 PvBuffer *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_PBT_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 PvBuffer *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);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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 PvBuffer *hdr, GError **err);
|
||||
void pv_ipib_free(IplParameterBlock *ipib);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 PvBuffer *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(PvBuffer) 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 = pv_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);
|
||||
}
|
||||
|
||||
PvBuffer *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(PvBuffer *loader, gsize loader_size,
|
||||
const PvBuffer *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(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err)
|
||||
{
|
||||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
||||
}
|
||||
|
||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||
{
|
||||
g_autoptr(PvBuffer) 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(PvBuffer *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);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/ipl.h"
|
||||
#include "boot/stage3b.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err);
|
||||
gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err);
|
||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err);
|
||||
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args);
|
||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
||||
|
||||
#endif
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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 "lib/zt_common.h"
|
||||
#include "boot/page.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
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "align.h"
|
||||
#include "buffer.h"
|
||||
#include "common.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
PvBuffer *pv_buffer_alloc(gsize size)
|
||||
{
|
||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
||||
|
||||
ret->data = g_malloc0(size);
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PvBuffer *pv_buffer_take(char *data, gsize size)
|
||||
{
|
||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
||||
|
||||
ret->data = data;
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
||||
{
|
||||
PvBuffer *ret;
|
||||
gsize size;
|
||||
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
size = buf->size;
|
||||
if (page_aligned)
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
ret = pv_buffer_alloc(size);
|
||||
|
||||
/* content will be 0-right-padded */
|
||||
memcpy(ret->data, buf->data, buf->size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err)
|
||||
{
|
||||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
||||
}
|
||||
|
||||
void pv_buffer_free(PvBuffer *buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
g_free(buf->data);
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
void pv_buffer_clear(PvBuffer **buf)
|
||||
{
|
||||
if (!buf || !*buf)
|
||||
return;
|
||||
|
||||
pv_buffer_free(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct PvBuffer {
|
||||
void *data;
|
||||
gsize size; /* in bytes */
|
||||
} PvBuffer;
|
||||
|
||||
PvBuffer *pv_buffer_alloc(gsize size);
|
||||
/* After this call @data belongs to the PvBuffer and must no longer be modified
|
||||
* by the caller.
|
||||
*/
|
||||
PvBuffer *pv_buffer_take(char *data, gsize size);
|
||||
void pv_buffer_free(PvBuffer *buf);
|
||||
void pv_buffer_clear(PvBuffer **buf);
|
||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free)
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdh.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ossl_typ.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/safestack.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
|
||||
#define CRL_DOWNLOAD_TIMEOUT_MS 3000
|
||||
#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */
|
||||
|
||||
enum PvCryptoMode {
|
||||
PV_ENCRYPT,
|
||||
PV_DECRYPT,
|
||||
};
|
||||
|
||||
typedef GSList HostKeyList;
|
||||
|
||||
/* play nice with g_autoptr */
|
||||
typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT;
|
||||
typedef STACK_OF(X509) STACK_OF_X509;
|
||||
typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL;
|
||||
|
||||
void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack);
|
||||
void STACK_OF_X509_free(STACK_OF_X509 *stack);
|
||||
void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack);
|
||||
|
||||
typedef struct {
|
||||
X509 *cert;
|
||||
const gchar *path;
|
||||
} x509_with_path;
|
||||
|
||||
x509_with_path *x509_with_path_new(X509 *cert, const gchar *path);
|
||||
void x509_with_path_free(x509_with_path *cert);
|
||||
|
||||
typedef struct {
|
||||
X509 *cert;
|
||||
STACK_OF_X509_CRL *crls;
|
||||
} x509_pair;
|
||||
|
||||
x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls);
|
||||
void x509_pair_free(x509_pair *pair);
|
||||
|
||||
/* Register auto cleanup functions */
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
|
||||
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(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free)
|
||||
|
||||
union cmp_index {
|
||||
struct {
|
||||
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 PvBuffer *key;
|
||||
const PvBuffer *iv_or_tweak;
|
||||
};
|
||||
|
||||
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
gint verify_flags, GError **err);
|
||||
void pv_crypto_init(void);
|
||||
void pv_crypto_cleanup(void);
|
||||
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
||||
gint verify_flags, int level, GError **err);
|
||||
X509 *load_cert_from_file(const char *path, GError **err);
|
||||
X509_CRL *load_crl_from_file(const gchar *path, GError **err);
|
||||
GSList *load_certificates(const gchar *const *cert_paths, GError **err);
|
||||
STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list);
|
||||
X509_STORE *store_setup(const gchar *root_ca_path,
|
||||
const gchar * const *crl_paths,
|
||||
GError **err);
|
||||
int store_set_verify_param(X509_STORE *store, GError **err);
|
||||
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
||||
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain, GError **err);
|
||||
X509_NAME *c2b_name(const X509_NAME *name);
|
||||
|
||||
STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
|
||||
STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert,
|
||||
GError **err);
|
||||
X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain,
|
||||
GError **err);
|
||||
gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err);
|
||||
X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
|
||||
void store_setup_crl_download(X509_STORE *st);
|
||||
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
||||
|
||||
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||||
PvBuffer *generate_aes_key(guint size, GError **err);
|
||||
PvBuffer *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);
|
||||
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||||
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err);
|
||||
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||
const struct cipher_parms *parms, PvBuffer *out,
|
||||
PvBuffer *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);
|
||||
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||
GError **err);
|
||||
G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms,
|
||||
const PvBuffer *in, GError **err);
|
||||
|
||||
#endif
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Libcurl utils
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "pv/pv_error.h"
|
||||
|
||||
#include "curl.h"
|
||||
|
||||
struct UserData {
|
||||
GByteArray *buffer;
|
||||
guint max_size;
|
||||
};
|
||||
|
||||
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
g_assert(userdata);
|
||||
struct UserData *data = (struct UserData *)userdata;
|
||||
GByteArray *buffer = data->buffer;
|
||||
guint64 actual_size;
|
||||
size_t err;
|
||||
|
||||
g_assert(buffer);
|
||||
|
||||
if (!g_uint64_checked_mul(&actual_size, size, nmemb))
|
||||
g_abort();
|
||||
|
||||
/* Signal an error condition by returning a amount that differs
|
||||
* from the amount passed to the callback. This results in a
|
||||
* CURLE_WRITE_ERROR.
|
||||
*/
|
||||
err = actual_size + 1;
|
||||
|
||||
if (actual_size > G_MAXUINT)
|
||||
return err;
|
||||
|
||||
data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size);
|
||||
if (data->buffer->len > data->max_size)
|
||||
return err;
|
||||
|
||||
return actual_size;
|
||||
}
|
||||
|
||||
gint curl_init(void)
|
||||
{
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void curl_cleanup(void)
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(GByteArray) ret = NULL;
|
||||
g_autoptr(CURL) handle = NULL;
|
||||
g_autofree gchar *agent = NULL;
|
||||
struct UserData userdata;
|
||||
CURLcode rc;
|
||||
|
||||
/* set up curl session */
|
||||
handle = curl_easy_init();
|
||||
if (!handle)
|
||||
g_abort();
|
||||
|
||||
/* follow redirection */
|
||||
rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING);
|
||||
rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
ret = g_byte_array_new();
|
||||
userdata.buffer = ret;
|
||||
userdata.max_size = max_size;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_URL, url);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
|
||||
rc = curl_easy_perform(handle);
|
||||
if (rc != CURLE_OK) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED,
|
||||
_("download failed: %s"), curl_easy_strerror(rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
curl_err:
|
||||
g_set_error(err, PV_ERROR,
|
||||
PV_ERROR_CURL_INIT_FAILED,
|
||||
_("cURL initialization failed: %s"),
|
||||
curl_easy_strerror(rc));
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Libcurl utils
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef PV_UTILS_LIBCURL_H
|
||||
#define PV_UTILS_LIBCURL_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup)
|
||||
|
||||
GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
|
||||
GError **err);
|
||||
gint curl_init(void);
|
||||
void curl_cleanup(void);
|
||||
|
||||
#endif /* PV_UTILS_LIBCURL_H */
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 PvBuffer *buf, uint64_t offset,
|
||||
GError **err)
|
||||
{
|
||||
if (file_seek(o, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (pv_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;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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 <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 PvBuffer *buf, uint64_t offset,
|
||||
GError **err);
|
||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||||
GError **err);
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* OpenSSL compatibility utils
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* 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_OPENSSL_COMPAT_H
|
||||
#define PV_UTILS_OPENSSL_COMPAT_H
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||
X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx))
|
||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||
X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm))
|
||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||
X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb))
|
||||
#else
|
||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||
X509_STORE_CTX_get_current_cert(ctx)
|
||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||
X509_STORE_CTX_get1_crls(ctx, nm)
|
||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||
X509_STORE_set_lookup_crls(st, cb)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include "helper.h"
|
||||
#include "hyptop.h"
|
||||
#include "sd.h"
|
||||
@@ -354,17 +357,6 @@ out:
|
||||
ht_reverse_off();
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert string to uppercase
|
||||
*/
|
||||
void ht_str_to_upper(char *str)
|
||||
{
|
||||
while (*str) {
|
||||
*str = toupper(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert ext TOD to microseconds
|
||||
*/
|
||||
@@ -412,3 +404,40 @@ s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_co
|
||||
|
||||
return smt_us;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add two new key value pairs containing the current time as UNIX epoch and formatted string to a
|
||||
* structured output object.
|
||||
*/
|
||||
void ht_fmt_time(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
char str[30];
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
tm = localtime(&tv.tv_sec);
|
||||
if (!tm)
|
||||
return;
|
||||
util_fmt_pair(FMT_PERSIST, "time_epoch", "%lld", mktime(tm));
|
||||
strftime(str, sizeof(str), "%F %T%z", tm);
|
||||
util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "time", "%s", str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new object for available CPU types to a structured output object.
|
||||
*/
|
||||
void ht_fmt_cpu_types(void)
|
||||
{
|
||||
struct sd_cpu_type *cpu_type;
|
||||
int i;
|
||||
|
||||
util_fmt_obj_start(FMT_DEFAULT, "cputypes");
|
||||
sd_cpu_type_iterate(cpu_type, i) {
|
||||
char *cpu_type_str = sd_cpu_type_id(cpu_type);
|
||||
util_str_tolower(cpu_type_str);
|
||||
util_fmt_pair(FMT_PERSIST, cpu_type_str, "%i",
|
||||
sd_cpu_type_cpu_cnt(cpu_type));
|
||||
}
|
||||
util_fmt_obj_end(); /* cpus{} */
|
||||
}
|
||||
|
||||
@@ -66,6 +66,12 @@ void ht_print_scroll_bar(int row_cnt, int row_start, int row_bar_start,
|
||||
int row_bar_bottom, int can_scroll_up,
|
||||
int can_scroll_down, int with_boder);
|
||||
|
||||
/*
|
||||
* util_fmt helper functions
|
||||
*/
|
||||
void ht_fmt_time(void);
|
||||
void ht_fmt_cpu_types(void);
|
||||
|
||||
/*
|
||||
* Error Macros
|
||||
*/
|
||||
|
||||
243
hyptop/hyptop.8
243
hyptop/hyptop.8
@@ -71,6 +71,51 @@ Use batch mode (no curses). This can be useful for sending output from hyptop
|
||||
to another program, a file, or a line mode terminal.
|
||||
In this mode no user input is accepted.
|
||||
.TP
|
||||
.BR "\-\-format=<FORMAT>"
|
||||
.RS
|
||||
Use this option to show output in a machine-readable format. FORMAT can be
|
||||
one of:
|
||||
|
||||
.IP \(bu 3
|
||||
.B json:
|
||||
Single JavaScript Object Notation (JSON) data structure
|
||||
|
||||
Data for all iterations is formatted as one JSON data structure in multiple
|
||||
lines to make them more readable by humans.
|
||||
.BR
|
||||
|
||||
See section "OUTPUT FORMAT" for more details.
|
||||
.BR
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B json\-seq:
|
||||
Sequence of JSON data structures
|
||||
|
||||
Data for each iteration is formatted as a separate JSON data structure prefixed
|
||||
with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line
|
||||
Feed character (0x0a) in accordance with RFC7464.
|
||||
.BR
|
||||
|
||||
See section "OUTPUT FORMAT" for more details.
|
||||
.BR
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B pairs:
|
||||
Textual key=value pairs
|
||||
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B csv:
|
||||
Comma-separated-value (CSV) list
|
||||
.BR
|
||||
|
||||
All values are enclosed in double quotation marks and separated by commas. The
|
||||
first line of output contains a list of headings. Subsequent lines each
|
||||
represent data for one system in one iteration.
|
||||
.PP
|
||||
This option implies the "\-\-batch_mode" option.
|
||||
.RE
|
||||
.TP
|
||||
.BR "\-d <SECONDS>" " or " "\-\-delay=<SECONDS>"
|
||||
Specifies the delay between screen updates.
|
||||
.TP
|
||||
@@ -216,6 +261,196 @@ For the online time field, the value is the time during which at least
|
||||
one CPU has been operational.
|
||||
Operating CPUs are shown with CPU identifier "0" and stopped CPUs with "1".
|
||||
|
||||
.SH OUTPUT FORMATS
|
||||
This section contains additional information for some of the supported
|
||||
output formats.
|
||||
|
||||
.SS json
|
||||
|
||||
JSON output consists of a top-level object with the following properties
|
||||
(key-value pairs):
|
||||
|
||||
.IP \(bu 3
|
||||
.BR meta :
|
||||
Tool meta-data including API level, version, host name, and time of invocation
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR hyptop :
|
||||
Hypervisor performance data
|
||||
.PP
|
||||
|
||||
Performance data is stored as an array of iteration objects under the "hyptop"
|
||||
property in the top-level object. Iteration objects contain the following properties:
|
||||
|
||||
.IP \(bu 3
|
||||
.BR iteration :
|
||||
sequential count of the current iteration
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR "time" " and " "time_epoch" :
|
||||
time when the iteration was recorded, in human-readable format and as Unix
|
||||
epoch.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR cputypes :
|
||||
contains the number of CPUs hyptop detected, per CPU type. Possible properties
|
||||
are
|
||||
.BR "cp" ", " "ifl" " and " "un".
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR systems :
|
||||
when the "sys_list" window is selected, contains an array of system objects
|
||||
that contain the performance data per system. Each system object contains a
|
||||
"system" property and properties for its performance data (see below).
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR cpus :
|
||||
when the "sys" window is selected, contains an array of CPU objects
|
||||
that contain the performance data per CPU of the selected system. Each CPU
|
||||
object contains a "coreid" property and properties for its performance data
|
||||
(see below).
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.BR summary :
|
||||
contains aggregated performance data over all systems in the "systems" or
|
||||
"cpus" array.
|
||||
.PP
|
||||
|
||||
All performance data available for the platform (LPAR, z/VM) hyptop is executed
|
||||
on is shown. Selecting specific fields with \-\-fields is not supported but it
|
||||
is possible to change the units of fields. Properties for performance data are
|
||||
labeled after their respective columns.
|
||||
|
||||
Example JSON output for a single iteration:
|
||||
.br
|
||||
|
||||
.RS
|
||||
{
|
||||
.br
|
||||
"meta": {
|
||||
.br
|
||||
"api_level": 1,
|
||||
.br
|
||||
"version": "2.35.0",
|
||||
.br
|
||||
"host": "mylpar.local",
|
||||
.br
|
||||
"time_epoch": 1730787834,
|
||||
.br
|
||||
"time": "2024-11-05 07:23:54+0100"
|
||||
.br
|
||||
},
|
||||
.br
|
||||
"hyptop": [
|
||||
.br
|
||||
{
|
||||
.br
|
||||
"iteration": 0,
|
||||
.br
|
||||
"time_epoch": 1730787834,
|
||||
.br
|
||||
"time": "2024-11-05 07:23:54+0100",
|
||||
.br
|
||||
"cputypes": {
|
||||
.br
|
||||
"ifl": 124,
|
||||
.br
|
||||
"cp": 1,
|
||||
.br
|
||||
"un": 0
|
||||
.br
|
||||
},
|
||||
.br
|
||||
"systems": [
|
||||
.br
|
||||
{
|
||||
.br
|
||||
"system": "MYLPAR",
|
||||
.br
|
||||
"#core": 30,
|
||||
.br
|
||||
"#the": 60,
|
||||
.br
|
||||
"core": 379.57,
|
||||
.br
|
||||
"the": 382.43,
|
||||
.br
|
||||
"smt": 294.73,
|
||||
.br
|
||||
"mgm": 2.09,
|
||||
.br
|
||||
"core+": "1503:13",
|
||||
.br
|
||||
"the+": "2060:45",
|
||||
.br
|
||||
"mgm+": "22:19",
|
||||
.br
|
||||
"online": "18:18:09"
|
||||
.br
|
||||
}
|
||||
.br
|
||||
],
|
||||
.br
|
||||
"summary": {
|
||||
.br
|
||||
"#core": 30,
|
||||
.br
|
||||
"#the": 60,
|
||||
.br
|
||||
"core": 379.57,
|
||||
.br
|
||||
"the": 382.43,
|
||||
.br
|
||||
"smt": 294.73,
|
||||
.br
|
||||
"mgm": 2.09,
|
||||
.br
|
||||
"core+": "1503:13",
|
||||
.br
|
||||
"the+": "2060:45",
|
||||
.br
|
||||
"mgm+": "22:19",
|
||||
.br
|
||||
"online": "18:18:09"
|
||||
.br
|
||||
}
|
||||
.br
|
||||
}
|
||||
.br
|
||||
]
|
||||
.br
|
||||
}
|
||||
.br
|
||||
.RE
|
||||
|
||||
.SS json\-seq
|
||||
|
||||
The json\-seq output format is a variation of the JSON output format described
|
||||
above with the following differences:
|
||||
|
||||
.IP \(bu 3
|
||||
Output consists of a sequence of top-level JSON objects, each contained in
|
||||
single line with no indentation
|
||||
.br
|
||||
|
||||
.IP \(bu 3
|
||||
Each top-level object is prefixed by an ASCII Record Separator character (0x1e)
|
||||
and suffixed with an ASCII Line Feed character (0x0a) in accordance with
|
||||
RFC7464
|
||||
.br
|
||||
.PP
|
||||
|
||||
.IP \(bu 3
|
||||
The first object contains tool meta-data properties defined in the previous
|
||||
section
|
||||
.br
|
||||
.PP
|
||||
|
||||
.IP \(bu 3
|
||||
Subsequent objects each represent performance data for one iteration
|
||||
.br
|
||||
.PP
|
||||
|
||||
.SH EXAMPLES
|
||||
To start hyptop with the "sys_list" window in interactive mode, enter:
|
||||
.br
|
||||
@@ -258,6 +493,14 @@ for CPU time calculation, enter:
|
||||
|
||||
# hyptop -t ifl,cp
|
||||
|
||||
.br
|
||||
To show a single iteration of performance data as a formatted JSON object for
|
||||
system "MYLPAR" and with fields CPU time, thread time, management time and
|
||||
online time in microseconds, enter:
|
||||
.br
|
||||
|
||||
# hyptop -f C:us,E:us,M:us,o:us --format json -n 1 -s MYLPAR
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B TERM
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "lib/util_fmt.h"
|
||||
|
||||
#include "dg_debugfs.h"
|
||||
#include "helper.h"
|
||||
#include "hyptop.h"
|
||||
@@ -218,6 +220,22 @@ static void l_term_check(void)
|
||||
" try \"--batch_mode\"\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Init util_fmt if --format is specified on the command line.
|
||||
*/
|
||||
static void l_fmt_init(void)
|
||||
{
|
||||
unsigned int flags = FMT_WARN;
|
||||
|
||||
if (!g.o.format_specified)
|
||||
return;
|
||||
if (g.o.format == FMT_CSV)
|
||||
flags |= FMT_QUOTEALL;
|
||||
if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ)
|
||||
flags |= FMT_HANDLEINT;
|
||||
util_fmt_init(stdout, g.o.format, flags, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init curses
|
||||
*/
|
||||
@@ -296,7 +314,6 @@ static void l_update_term_curses(void)
|
||||
static void l_update_term_batch(void)
|
||||
{
|
||||
g.w.cur->update_term(g.w.cur);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -360,6 +377,7 @@ void __noreturn hyptop_exit(int rc)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
opts_parse(argc, argv);
|
||||
l_fmt_init();
|
||||
hyptop_helper_init();
|
||||
sd_init();
|
||||
l_dg_init();
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "lib/util_fmt.h"
|
||||
|
||||
#include "helper.h"
|
||||
#include "nav_desc.h"
|
||||
#include "table.h"
|
||||
@@ -52,6 +54,8 @@ struct hyptop_win_opts {
|
||||
struct hyptop_opts {
|
||||
unsigned int win_specified;
|
||||
unsigned int batch_mode_specified;
|
||||
unsigned int format_specified;
|
||||
enum util_fmt_t format;
|
||||
unsigned int iterations_specified;
|
||||
unsigned int iterations;
|
||||
unsigned int iterations_act;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "getopt.h"
|
||||
@@ -19,6 +21,7 @@
|
||||
#include "hyptop.h"
|
||||
#include "opts.h"
|
||||
#include "sd.h"
|
||||
#include "table.h"
|
||||
|
||||
static const char l_copyright_str[] = "Copyright IBM Corp. 2010, 2017";
|
||||
|
||||
@@ -38,10 +41,16 @@ static char HELP_TEXT[] =
|
||||
"-S, --sort LETTER Sort field for current window\n"
|
||||
"-t, --cpu_types TYPE[,..] CPU types used for time calculations\n"
|
||||
"-b, --batch_mode Use batch mode (no curses)\n"
|
||||
" --format FORMAT Output format (" FMT_TYPE_NAMES "), implies -b\n"
|
||||
"-d, --delay SECONDS Delay time between screen updates\n"
|
||||
"-m, --smt_factor FACTOR Machine generation dependent SMT speedup factor.\n"
|
||||
"-n, --iterations NUMBER Number of iterations before ending\n";
|
||||
|
||||
/*
|
||||
* Options with long-name only
|
||||
*/
|
||||
#define OPT_FORMAT 256 /* --format */
|
||||
|
||||
/*
|
||||
* Initialize default settings
|
||||
*/
|
||||
@@ -287,6 +296,23 @@ static void l_batch_mode_set(void)
|
||||
g.o.batch_mode_specified = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the "--format" option
|
||||
*/
|
||||
static void l_format_set(const char *str)
|
||||
{
|
||||
enum util_fmt_t fmt;
|
||||
|
||||
if (!util_fmt_name_to_type(str, &fmt)) {
|
||||
ERR_EXIT("Unknown format '%s', supported formats: "
|
||||
FMT_TYPE_NAMES "\n", str);
|
||||
}
|
||||
|
||||
l_batch_mode_set();
|
||||
g.o.format_specified = 1;
|
||||
g.o.format = fmt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make option consisteny checks at end of command line parsing
|
||||
*/
|
||||
@@ -322,6 +348,7 @@ void opts_parse(int argc, char *argv[])
|
||||
{ "fields", required_argument, NULL, 'f'},
|
||||
{ "sort_field", required_argument, NULL, 'S'},
|
||||
{ "cpu_types", required_argument, NULL, 't'},
|
||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
static const char option_string[] = "vhbd:m:w:s:n:f:t:S:";
|
||||
@@ -366,6 +393,9 @@ void opts_parse(int argc, char *argv[])
|
||||
case 'S':
|
||||
l_sort_field_set(optarg);
|
||||
break;
|
||||
case OPT_FORMAT:
|
||||
l_format_set(optarg);
|
||||
break;
|
||||
default:
|
||||
l_std_usage_exit();
|
||||
}
|
||||
@@ -404,7 +434,7 @@ static void l_verify_systems(struct hyptop_win *win)
|
||||
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i]))
|
||||
continue;
|
||||
sys_name = ht_strdup(win->opts.sys.vec[i]);
|
||||
ht_str_to_upper(win->opts.sys.vec[i]);
|
||||
util_str_toupper(win->opts.sys.vec[i]);
|
||||
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) {
|
||||
ht_free(sys_name);
|
||||
continue;
|
||||
@@ -431,11 +461,11 @@ void opts_iterations_next(void)
|
||||
{
|
||||
if (g.o.iterations_specified) {
|
||||
g.o.iterations_act++;
|
||||
if (g.o.iterations_act >= g.o.iterations)
|
||||
if (g.o.iterations_act >= g.o.iterations) {
|
||||
if (g.o.format_specified)
|
||||
table_fmt_end();
|
||||
hyptop_exit(0);
|
||||
}
|
||||
}
|
||||
if (g.o.batch_mode_specified)
|
||||
printf("---------------------------------------------------"
|
||||
"----------------------------\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include "helper.h"
|
||||
#include "hyptop.h"
|
||||
#include "table.h"
|
||||
@@ -196,8 +199,8 @@ static void l_col_headline_init(struct table *t, struct table_col *col)
|
||||
col->p->head_char[0] = col->hotkey;
|
||||
strcpy(col->p->head_last, ++ptr);
|
||||
if (!t->attr_sorted_table) {
|
||||
ht_str_to_upper(col->p->head_first);
|
||||
ht_str_to_upper(col->p->head_last);
|
||||
util_str_toupper(col->p->head_first);
|
||||
util_str_toupper(col->p->head_last);
|
||||
col->p->head_char[0] = toupper(col->p->head_char[0]);
|
||||
}
|
||||
}
|
||||
@@ -948,6 +951,76 @@ static void l_table_print_all(struct table *t)
|
||||
hyptop_print_nl();
|
||||
}
|
||||
l_row_print(t, t->row_last);
|
||||
hyptop_print_nl();
|
||||
hyptop_printf("------------------------------------------------------"
|
||||
"-------------------------\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print one table row as structured output
|
||||
*
|
||||
* Note: column filtering and sorting is explicitly ignored because the
|
||||
* assumption is that these operations can be trivially performed by the
|
||||
* consumer.
|
||||
*/
|
||||
static void l_row_print_formatted(struct table *t, struct table_row *row)
|
||||
{
|
||||
struct table_col *col;
|
||||
int col_nr;
|
||||
|
||||
table_col_iterate(t, col, col_nr) {
|
||||
unsigned int flags = 0;
|
||||
struct table_entry *e = &row->entries[col_nr];
|
||||
|
||||
if (row == t->row_last && col_nr == 0)
|
||||
continue;
|
||||
if (table_col_needs_quotes(col))
|
||||
flags = FMT_QUOTE;
|
||||
util_fmt_pair(flags, col->head, "%s", e->str);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print table as structured output
|
||||
*/
|
||||
static void l_table_print_all_formatted(struct table *t)
|
||||
{
|
||||
struct table_row *row;
|
||||
|
||||
util_fmt_obj_start(FMT_ROW, "iteration");
|
||||
util_fmt_pair(FMT_PERSIST, "iteration", "%u", g.o.iterations_act);
|
||||
ht_fmt_time();
|
||||
ht_fmt_cpu_types();
|
||||
if (strcmp(g.o.cur_win->id, "sys_list") == 0)
|
||||
util_fmt_obj_start(FMT_LIST, "systems");
|
||||
else
|
||||
util_fmt_obj_start(FMT_LIST, "cpus");
|
||||
util_list_iterate(&t->row_list, row) {
|
||||
util_fmt_obj_start(FMT_ROW, "entry");
|
||||
l_row_print_formatted(t, row);
|
||||
util_fmt_obj_end(); /* entry */
|
||||
}
|
||||
util_fmt_obj_end(); /* systems[] */
|
||||
util_fmt_obj_start(FMT_DEFAULT, "summary");
|
||||
l_row_print_formatted(t, t->row_last);
|
||||
util_fmt_obj_end(); /* summary{} */
|
||||
util_fmt_obj_end(); /* iteration */
|
||||
}
|
||||
|
||||
void table_fmt_start(void)
|
||||
{
|
||||
if (!g.o.format_specified)
|
||||
return;
|
||||
if (g.o.format != FMT_JSONSEQ)
|
||||
util_fmt_obj_start(FMT_LIST, "hyptop");
|
||||
}
|
||||
|
||||
void table_fmt_end(void)
|
||||
{
|
||||
if (!g.o.format_specified)
|
||||
return;
|
||||
if (g.o.format != FMT_JSONSEQ)
|
||||
util_fmt_obj_end(); /* hyptop[] */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -955,10 +1028,14 @@ static void l_table_print_all(struct table *t)
|
||||
*/
|
||||
void table_print(struct table *t)
|
||||
{
|
||||
if (g.o.batch_mode_specified)
|
||||
l_table_print_all(t);
|
||||
else
|
||||
if (g.o.batch_mode_specified) {
|
||||
if (!g.o.format_specified)
|
||||
l_table_print_all(t);
|
||||
else
|
||||
l_table_print_all_formatted(t);
|
||||
} else {
|
||||
l_table_print_curses(t);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -106,6 +106,7 @@ struct table_col_priv {
|
||||
char head_char[2];
|
||||
char head_last[TABLE_HEADING_SIZE];
|
||||
int rsort;
|
||||
int needs_quotes;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -135,6 +136,11 @@ static inline int table_col_enabled(struct table_col *col)
|
||||
return col->p->enabled;
|
||||
}
|
||||
|
||||
static inline int table_col_needs_quotes(struct table_col *col)
|
||||
{
|
||||
return col->p->needs_quotes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table Column Constructor Macros
|
||||
*/
|
||||
@@ -384,6 +390,9 @@ extern struct table_row *table_row_alloc(struct table *t);
|
||||
extern void table_scroll_down(struct table *t, enum table_scroll_unit unit);
|
||||
extern void table_scroll_up(struct table *t, enum table_scroll_unit unit);
|
||||
|
||||
extern void table_fmt_start(void);
|
||||
extern void table_fmt_end(void);
|
||||
|
||||
/*
|
||||
* Entry add functions
|
||||
*/
|
||||
|
||||
@@ -63,7 +63,7 @@ static int l_unit_raw(struct table_col *col, struct table_entry *e)
|
||||
*/
|
||||
static int l_str(struct table_col *col, struct table_entry *e)
|
||||
{
|
||||
(void) col;
|
||||
col->p->needs_quotes = 1;
|
||||
return strlen(e->str);
|
||||
}
|
||||
|
||||
@@ -226,6 +226,8 @@ static int l_unit_hm_u64(char *str, u64 v1, int negative)
|
||||
|
||||
static int l_unit_hm(struct table_col *col, struct table_entry *e)
|
||||
{
|
||||
col->p->needs_quotes = 1;
|
||||
|
||||
if (!e->set)
|
||||
return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR);
|
||||
|
||||
@@ -272,6 +274,8 @@ static int l_unit_dhm_u64(char *str, u64 v1, int negative)
|
||||
|
||||
static int l_unit_dhm(struct table_col *col, struct table_entry *e)
|
||||
{
|
||||
col->p->needs_quotes = 1;
|
||||
|
||||
if (!e->set)
|
||||
return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR);
|
||||
|
||||
|
||||
@@ -162,7 +162,8 @@ static void l_cpu_add(struct sd_cpu *cpu)
|
||||
continue;
|
||||
l_cpu_item_add(table_row, cpu, item);
|
||||
}
|
||||
l_cpu_add_visual(table_row, cpu);
|
||||
if (!g.o.format_specified)
|
||||
l_cpu_add_visual(table_row, cpu);
|
||||
table_row_add(l_t, table_row);
|
||||
}
|
||||
|
||||
@@ -194,7 +195,8 @@ static void l_table_update_term(struct hyptop_win *win)
|
||||
{
|
||||
(void) win;
|
||||
|
||||
ht_print_head(l_sys_id);
|
||||
if (!g.o.format_specified)
|
||||
ht_print_head(l_sys_id);
|
||||
table_print(l_t);
|
||||
}
|
||||
|
||||
@@ -300,6 +302,7 @@ static void l_run(struct hyptop_win *win)
|
||||
|
||||
/* Reformat table when entering window */
|
||||
table_rebuild(l_t);
|
||||
table_fmt_start();
|
||||
while (1) {
|
||||
if (l_table_create()) {
|
||||
if (g.o.batch_mode_specified)
|
||||
@@ -363,9 +366,11 @@ void win_sys_init(void)
|
||||
col_vec[i] = col;
|
||||
col_desc_vec[i] = item->desc;
|
||||
}
|
||||
col_vec[i] = &l_vis_col;
|
||||
col_desc_vec[i] = vis_str;
|
||||
table_col_add(l_t, &l_vis_col);
|
||||
if (!g.o.format_specified) {
|
||||
col_vec[i] = &l_vis_col;
|
||||
col_desc_vec[i] = vis_str;
|
||||
table_col_add(l_t, &l_vis_col);
|
||||
}
|
||||
|
||||
/* Enable fields */
|
||||
if (win_sys.opts.fields.specified)
|
||||
|
||||
@@ -206,7 +206,8 @@ static void l_table_update_term(struct hyptop_win *win)
|
||||
{
|
||||
(void) win;
|
||||
|
||||
ht_print_head(NULL);
|
||||
if (!g.o.format_specified)
|
||||
ht_print_head(NULL);
|
||||
table_print(l_t);
|
||||
}
|
||||
|
||||
@@ -310,6 +311,7 @@ static void l_run(struct hyptop_win *win)
|
||||
|
||||
/* Reformat table when entering window */
|
||||
table_rebuild(l_t);
|
||||
table_fmt_start();
|
||||
while (1) {
|
||||
l_table_create();
|
||||
hyptop_update_term();
|
||||
|
||||
@@ -21,18 +21,20 @@
|
||||
|
||||
#define STAGE2_DESC _AC(0x78, UL)
|
||||
#define STAGE2_ENTRY _AC(0x2018, UL)
|
||||
#define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x6000, UL)
|
||||
#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL)
|
||||
#define ECKD2DUMP_SV_HEAP_ADDRESS _AC(0xb000, UL)
|
||||
#define ECKD2DUMP_HEAP_ADDRESS _AC(0xb000, UL)
|
||||
#define STAGE2_HEAP_SIZE _AC(0x3000, UL)
|
||||
#define STAGE2_STACK_ADDRESS _AC(0xe400, UL)
|
||||
#define STAGE2_STACK_SIZE _AC(0x1c00, UL)
|
||||
#define ECKD2DUMP_SV_STACK_ADDRESS _AC(0xe000, UL)
|
||||
#define ECKD2DUMP_SV_STACK_SIZE _AC(0x2000, UL)
|
||||
#define ECKD2DUMP_STACK_ADDRESS _AC(0xe000, UL)
|
||||
#define ECKD2DUMP_STACK_SIZE _AC(0x2000, UL)
|
||||
#define STAGE2_MAX_SIZE _AC(0x3000, UL)
|
||||
|
||||
#define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL)
|
||||
|
||||
#define STAGE3_ENTRY _AC(0xa000, UL)
|
||||
|
||||
99
include/lib/pci_list.h
Normal file
99
include/lib/pci_list.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @defgroup pci_list_h libzpci: zPCI device handling
|
||||
* @{
|
||||
* @brief Work with zPCI devices
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef LIB_ZPCI_PCI_LIST_H
|
||||
#define LIB_ZPCI_PCI_LIST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util_list.h"
|
||||
|
||||
enum zpci_pft {
|
||||
ZPCI_PFT_UNCLASSIFIED = 0x00,
|
||||
|
||||
ZPCI_PFT_ROCE_EXPRESS = 0x02,
|
||||
ZPCI_PFT_ROCE_EXPRESS2 = 0x0a,
|
||||
ZPCI_PFT_CNW = 0x0d,
|
||||
ZPCI_PFT_NETH = 0x0c,
|
||||
ZPCI_PFT_NETD = 0x0f,
|
||||
|
||||
ZPCI_PFT_NVME = 0x0b,
|
||||
ZPCI_PFT_ISM = 0x05
|
||||
};
|
||||
|
||||
/*
|
||||
* Follows RFC 2863 operational states with the
|
||||
* numeric values from IF_OPER_* in linux/if.h:
|
||||
*/
|
||||
typedef uint8_t operstate_t;
|
||||
|
||||
struct zpci_netdev {
|
||||
char *name;
|
||||
operstate_t operstate;
|
||||
};
|
||||
|
||||
struct zpci_dev {
|
||||
struct util_list_node entry;
|
||||
/* PCI Domain */
|
||||
uint32_t domain_nr;
|
||||
/* PCI Bus (8 bits), Device (5 bits), Function (3 bits) */
|
||||
union {
|
||||
uint16_t val;
|
||||
struct {
|
||||
uint16_t bus : 8;
|
||||
uint16_t dev : 5;
|
||||
uint16_t fn : 3;
|
||||
};
|
||||
} bdf;
|
||||
|
||||
/* Function attributes (see linux/Documentation/arch/s390/pci.rst) */
|
||||
uint32_t fid;
|
||||
uint32_t uid;
|
||||
uint16_t pchid;
|
||||
uint16_t vfn;
|
||||
uint8_t port;
|
||||
enum zpci_pft pft;
|
||||
bool uid_is_unique;
|
||||
/* Configuration state 0 - Standby, 1 Configured */
|
||||
bool conf;
|
||||
|
||||
/* Associated netdevs if any */
|
||||
int num_netdevs;
|
||||
struct zpci_netdev *netdevs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get if a PCI device is a PCI Virtual Function
|
||||
*
|
||||
* @param[in] zdev The device in question
|
||||
*
|
||||
* @return true if the device is a VF false otherwise
|
||||
*/
|
||||
static inline bool zpci_is_vf(struct zpci_dev *zdev)
|
||||
{
|
||||
return !!zdev->vfn;
|
||||
}
|
||||
|
||||
struct util_list *zpci_dev_list(void);
|
||||
void zpci_free_dev_list(struct util_list *zpci_list);
|
||||
void zpci_free_dev(struct zpci_dev *zdev);
|
||||
|
||||
char *zpci_pci_addr(struct zpci_dev *zdev);
|
||||
const char *zpci_pft_str(struct zpci_dev *zdev);
|
||||
|
||||
const char *zpci_operstate_str(operstate_t state);
|
||||
operstate_t zpci_operstate_from_str(const char *oper_str);
|
||||
|
||||
struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||
struct zpci_netdev **netdev);
|
||||
|
||||
#endif /* LIB_ZPCI_PCI_LIST_H */
|
||||
56
include/lib/pci_sclp.h
Normal file
56
include/lib/pci_sclp.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @defgroup pci_sclp_h libzpci: zPCI device handling
|
||||
* @{
|
||||
* @brief Issue SCLPs for zPCI devices
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef LIB_ZPCI_PCI_SCLP_H
|
||||
#define LIB_ZPCI_PCI_SCLP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define SCLP_ERRNOTIFY_AQ_RESET 0
|
||||
#define SCLP_ERRNOTIFY_AQ_DECONF 1
|
||||
#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2
|
||||
#define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3
|
||||
|
||||
#define SCLP_ERRNOTIFY_ID_ZPCICTL 0x4713
|
||||
#define SCLP_ERRNOTIFY_ID_OPTICSMON 0x4714
|
||||
|
||||
#define SCLP_ERRNOTIFY_DATA_SIZE 4054
|
||||
|
||||
struct zpci_report_error_header {
|
||||
__u8 version; /* Interface version byte */
|
||||
__u8 action; /* Action qualifier byte
|
||||
* 0: Adapter Reset Request
|
||||
* 1: Deconfigure and repair action requested
|
||||
* 2: Informational Report
|
||||
* 3: Optics Data
|
||||
*/
|
||||
__u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */
|
||||
} __packed;
|
||||
|
||||
struct zpci_report_error_data {
|
||||
__u64 timestamp;
|
||||
__u64 err_log_id;
|
||||
/* We cannot exceed a total of 4074 bytes (header + data) */
|
||||
char log_data[SCLP_ERRNOTIFY_DATA_SIZE];
|
||||
} __packed;
|
||||
|
||||
struct zpci_report_error {
|
||||
struct zpci_report_error_header header;
|
||||
struct zpci_report_error_data data;
|
||||
} __packed;
|
||||
|
||||
int zpci_sclp_issue_action(char *pci_addr, int action,
|
||||
char *data, size_t length, u64 err_log_id);
|
||||
|
||||
#endif /* LIB_ZPCI_PCI_SCLP_H */
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef LIB_UTIL_FILE_H
|
||||
#define LIB_UTIL_FILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/util_exit_code.h"
|
||||
|
||||
int util_file_read_line(char *str, size_t size, const char *fmt, ...);
|
||||
|
||||
@@ -129,6 +129,7 @@ int __util_vsprintf(const char *func, const char *file, int line,
|
||||
char *util_strcat_realloc(char *str1, const char *str2);
|
||||
void util_concatf(char **str1, const char *fmt, ...);
|
||||
void util_str_toupper(char *str);
|
||||
void util_str_tolower(char *str);
|
||||
|
||||
char *util_strstrip(char *s);
|
||||
size_t util_strlcpy(char *dest, const char *src, size_t size);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
struct util_proc_part_entry {
|
||||
dev_t device;
|
||||
size_t blockcount;
|
||||
@@ -27,6 +26,12 @@ struct util_proc_dev_entry {
|
||||
char *name;
|
||||
};
|
||||
|
||||
#define UTIL_PROC_DEV_ENTRY_DASD "dasd"
|
||||
#define UTIL_PROC_DEV_ENTRY_VIRTBLK "virtblk"
|
||||
#define UTIL_PROC_DEV_ENTRY_SD "sd"
|
||||
#define UTIL_PROC_DEV_ENTRY_BLKEXT "blkext"
|
||||
#define UTIL_PROC_DEV_ENTRY_MD "md"
|
||||
|
||||
int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry);
|
||||
void util_proc_part_free_entry(struct util_proc_part_entry *entry);
|
||||
int util_proc_dev_get_entry(dev_t dev, int blockdev,
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_arch.h"
|
||||
|
||||
#define PROC_SYSINFO "/proc/sysinfo"
|
||||
@@ -107,13 +109,44 @@ const char *util_arch_machine_type_to_str(int type)
|
||||
*/
|
||||
unsigned long util_arch_hsa_maxsize(void)
|
||||
{
|
||||
switch (util_arch_machine_type()) {
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||
return HSA_SIZE_512M;
|
||||
default:
|
||||
return HSA_SIZE_32M;
|
||||
unsigned long hsa_size = 0;
|
||||
char *path;
|
||||
int rc;
|
||||
|
||||
path = util_path_sysfs("firmware/dump/dump_area_size");
|
||||
if (util_path_exists(path)) {
|
||||
rc = util_file_read_ul(&hsa_size, 10, path);
|
||||
if (rc)
|
||||
hsa_size = 0;
|
||||
}
|
||||
free(path);
|
||||
|
||||
/*
|
||||
* Fall back in case of failed attempt to obtain dump area size
|
||||
* from sysfs for some reason (e.g. no kernel support of
|
||||
* the sysfs attribute /sys/firmware/dump/dump_area_size).
|
||||
* For all machine types starting with z15 we can safely assume
|
||||
* at least 512M of dump area size, otherwise, only 32M can be
|
||||
* safely assumed.
|
||||
*/
|
||||
if (!hsa_size) {
|
||||
switch (util_arch_machine_type()) {
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z10_EC:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z10_BC:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_196:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_114:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_EC12:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_BC12:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z13:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z13_S:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z14:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1:
|
||||
hsa_size = HSA_SIZE_32M;
|
||||
break;
|
||||
default:
|
||||
hsa_size = HSA_SIZE_512M;
|
||||
}
|
||||
}
|
||||
|
||||
return hsa_size;
|
||||
}
|
||||
|
||||
@@ -174,6 +174,21 @@ void util_str_toupper(char *str)
|
||||
str[i] = toupper(str[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to lowercase
|
||||
*
|
||||
* String \a str is converted to lowercase
|
||||
*
|
||||
* @param[in,out] str String to convert
|
||||
*/
|
||||
void util_str_tolower(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; str[i] != '\0'; i++)
|
||||
str[i] = tolower(str[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print to newly allocated string or exit in case of failure
|
||||
*/
|
||||
|
||||
20
libzpci/Makefile
Normal file
20
libzpci/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
include ../common.mak
|
||||
|
||||
lib = libzpci.a
|
||||
|
||||
all: $(lib)
|
||||
|
||||
objects = pci_list.o pci_sclp.o
|
||||
|
||||
examples := $(patsubst %.c,%,$(wildcard *_example.c))
|
||||
|
||||
examples: $(examples)
|
||||
$(examples): %: %.o $(lib) $(rootdir)/libutil/libutil.a
|
||||
|
||||
$(lib): ALL_CFLAGS += -fPIC -std=c11
|
||||
$(lib): $(objects)
|
||||
|
||||
install: all
|
||||
|
||||
clean:
|
||||
rm -f *.o $(lib) $(examples)
|
||||
48
libzpci/libzpci_example.c
Normal file
48
libzpci/libzpci_example.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/pci_list.h"
|
||||
|
||||
static void zpci_print(struct zpci_dev *zdev)
|
||||
{
|
||||
char *pci_addr = zpci_pci_addr(zdev);
|
||||
int i;
|
||||
|
||||
if (!zdev->conf) {
|
||||
printf("fid: %8x address: %s\n", zdev->fid, pci_addr);
|
||||
} else {
|
||||
printf("fid: %8x address: %s uid: %4x%s pchid: %4x vfn: %4d port: %1d pft: %s ",
|
||||
zdev->fid, pci_addr, zdev->uid, (zdev->uid_is_unique) ? " (unique)" : "",
|
||||
zdev->pchid, zdev->vfn, zdev->port, zpci_pft_str(zdev));
|
||||
if (zdev->num_netdevs) {
|
||||
printf("netdevs: ");
|
||||
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
printf("%s (%s)", zdev->netdevs[i].name,
|
||||
zpci_operstate_str(zdev->netdevs[i].operstate));
|
||||
if (i + 1 < zdev->num_netdevs)
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
free(pci_addr);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct util_list *zpci_list;
|
||||
struct zpci_dev *zdev;
|
||||
|
||||
zpci_list = zpci_dev_list();
|
||||
if (!zpci_list)
|
||||
errx(EXIT_FAILURE, "Error getting list of zPCI devices");
|
||||
|
||||
util_list_iterate(zpci_list, zdev)
|
||||
zpci_print(zdev);
|
||||
|
||||
zpci_free_dev_list(zpci_list);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
389
libzpci/pci_list.c
Normal file
389
libzpci/pci_list.c
Normal file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
* libzpci - Functions to handle zPCI devices and their properties
|
||||
*
|
||||
* Copyright IBM Corp. 2023
|
||||
*
|
||||
* 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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <linux/if.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/pci_list.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
|
||||
/**
|
||||
* Get the function type name for the given device
|
||||
*
|
||||
* The device type name is suitable for presentation to a user.
|
||||
*
|
||||
* @param[in] zdev The device in question
|
||||
*
|
||||
* @return a string representing the PCI device type
|
||||
*/
|
||||
const char *zpci_pft_str(struct zpci_dev *zdev)
|
||||
{
|
||||
switch (zdev->pft) {
|
||||
case ZPCI_PFT_UNCLASSIFIED:
|
||||
return "unclassified";
|
||||
case ZPCI_PFT_ROCE_EXPRESS:
|
||||
return "RoCE Express";
|
||||
case ZPCI_PFT_ROCE_EXPRESS2:
|
||||
return "RoCE Express-2";
|
||||
case ZPCI_PFT_CNW:
|
||||
return "Cloud Network Adapter";
|
||||
case ZPCI_PFT_NETH:
|
||||
return "Network Express Hybrid";
|
||||
case ZPCI_PFT_NETD:
|
||||
return "Network Express Dedicated";
|
||||
case ZPCI_PFT_NVME:
|
||||
return "NVMe";
|
||||
case ZPCI_PFT_ISM:
|
||||
return "ISM";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a textual representation of the device's PCI address
|
||||
*
|
||||
* The representation has extended "DDDD:bb:dd.f" format used
|
||||
* by Linux tooling such as lspci.
|
||||
*
|
||||
* @param[in] zdev The device in question
|
||||
*
|
||||
* @return the string representing the PCI address
|
||||
*/
|
||||
char *zpci_pci_addr(struct zpci_dev *zdev)
|
||||
{
|
||||
uint8_t bus = zdev->bdf.bus;
|
||||
uint8_t dev = zdev->bdf.dev;
|
||||
uint8_t fn = zdev->bdf.fn;
|
||||
char *pci_addr;
|
||||
|
||||
util_asprintf(&pci_addr, "%04x:%02x:%02x.%x", zdev->domain_nr, bus, dev, fn);
|
||||
return pci_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an operationanl state value from its state name
|
||||
*
|
||||
* The state names follow RFC 2863 and the values match
|
||||
* IF_OPER_* in linux/if.h.
|
||||
*
|
||||
* @param[in] oper_str The name of the operational state
|
||||
*
|
||||
* @return the operational state value
|
||||
*/
|
||||
operstate_t zpci_operstate_from_str(const char *oper_str)
|
||||
{
|
||||
if (!strcmp(oper_str, "notpresent"))
|
||||
return IF_OPER_NOTPRESENT;
|
||||
else if (!strcmp(oper_str, "down"))
|
||||
return IF_OPER_DOWN;
|
||||
else if (!strcmp(oper_str, "lowerlayerdown"))
|
||||
return IF_OPER_LOWERLAYERDOWN;
|
||||
else if (!strcmp(oper_str, "testing"))
|
||||
return IF_OPER_TESTING;
|
||||
else if (!strcmp(oper_str, "dormant"))
|
||||
return IF_OPER_DORMANT;
|
||||
else if (!strcmp(oper_str, "up"))
|
||||
return IF_OPER_UP;
|
||||
else
|
||||
return IF_OPER_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an operationanl state name from its value
|
||||
*
|
||||
* The state names follow RFC 2863 and the values match
|
||||
* IF_OPER_* in linux/if.h.
|
||||
*
|
||||
* @param[in] state The value of the operational state
|
||||
*
|
||||
* @return the operational state name string representation
|
||||
*/
|
||||
const char *zpci_operstate_str(operstate_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case IF_OPER_NOTPRESENT:
|
||||
return "notpresent";
|
||||
case IF_OPER_DOWN:
|
||||
return "down";
|
||||
case IF_OPER_LOWERLAYERDOWN:
|
||||
return "lowerlayerdown";
|
||||
case IF_OPER_TESTING:
|
||||
return "testing";
|
||||
case IF_OPER_DORMANT:
|
||||
return "dormant";
|
||||
case IF_OPER_UP:
|
||||
return "up";
|
||||
case IF_OPER_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
};
|
||||
}
|
||||
|
||||
static int zpci_populate_from_slot_dir(struct zpci_dev *zdev, const char *slot_dir,
|
||||
const char *slot_name)
|
||||
{
|
||||
char buf_addr[11]; /* "dddd:bb:dd\0" */
|
||||
uint8_t bus, df;
|
||||
uint32_t domain;
|
||||
int val, rc;
|
||||
|
||||
rc = sscanf(slot_name, "%x", &zdev->fid);
|
||||
if (rc != 1)
|
||||
return -EINVAL;
|
||||
|
||||
rc = util_file_read_line(buf_addr, sizeof(buf_addr), "%s/%s/address", slot_dir, slot_name);
|
||||
if (rc) {
|
||||
warn("Reading address from slot %s/%s", slot_dir, slot_name);
|
||||
return rc;
|
||||
}
|
||||
rc = sscanf(buf_addr, "%04x:%02hhx:%02hhx", &domain, &bus, &df);
|
||||
if (rc != 3)
|
||||
return -EINVAL;
|
||||
zdev->domain_nr = domain;
|
||||
zdev->bdf.val = (((uint16_t)bus) << 8) | df;
|
||||
|
||||
rc = util_file_read_i(&val, 10, "%s/%s/power", slot_dir, slot_name);
|
||||
if (rc) {
|
||||
warn("Reading power from slot %s/%s", slot_dir, slot_name);
|
||||
return rc;
|
||||
}
|
||||
zdev->conf = val > 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zpci_populate_netdevices(struct zpci_dev *zdev, const char *dev_dir)
|
||||
{
|
||||
const char *netdev_patt = "en.*";
|
||||
struct dirent **de_vec;
|
||||
int count, i, rc = 0;
|
||||
char *net_dir;
|
||||
char buf[16]; /* "lowerlayerdown" */
|
||||
|
||||
util_asprintf(&net_dir, "%s/net", dev_dir);
|
||||
count = util_scandir(&de_vec, alphasort, net_dir, netdev_patt);
|
||||
if (count == -1) {
|
||||
warn("Reading netdevice information for %s/net failed", dev_dir);
|
||||
rc = -EINVAL;
|
||||
goto out_net_dir;
|
||||
}
|
||||
/* A directory per netdev */
|
||||
for (i = 0; i < count; i++) {
|
||||
if (de_vec[i]->d_type != DT_DIR) {
|
||||
rc = -EINVAL;
|
||||
goto out_scan_dir;
|
||||
}
|
||||
}
|
||||
zdev->num_netdevs = count;
|
||||
if (!count)
|
||||
goto out_scan_dir;
|
||||
zdev->netdevs = util_zalloc(sizeof(struct zpci_netdev) * zdev->num_netdevs);
|
||||
for (i = 0; i < count; i++) {
|
||||
zdev->netdevs[i].name = util_strdup(de_vec[i]->d_name);
|
||||
rc = util_file_read_line(buf, sizeof(buf), "%s/%s/operstate", net_dir,
|
||||
zdev->netdevs[i].name);
|
||||
if (rc) {
|
||||
/* If operstate is not readable just set to unknown */
|
||||
zdev->netdevs[i].operstate = IF_OPER_UNKNOWN;
|
||||
rc = 0;
|
||||
continue;
|
||||
}
|
||||
zdev->netdevs[i].operstate = zpci_operstate_from_str(buf);
|
||||
}
|
||||
|
||||
out_scan_dir:
|
||||
util_scandir_free(de_vec, count);
|
||||
out_net_dir:
|
||||
free(net_dir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int zpci_populate_from_dev_dir(struct zpci_dev *zdev)
|
||||
{
|
||||
char *pci_addr = zpci_pci_addr(zdev);
|
||||
int rc, val;
|
||||
char *path;
|
||||
|
||||
path = util_path_sysfs("bus/pci/devices/%s", pci_addr);
|
||||
if (!path) {
|
||||
rc = -EINVAL;
|
||||
goto out_pci_addr;
|
||||
}
|
||||
if (!util_path_exists(path)) {
|
||||
rc = -ENODEV;
|
||||
goto out_path;
|
||||
}
|
||||
|
||||
rc = util_file_read_i(&val, 16, "%s/uid", path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
zdev->uid = val;
|
||||
|
||||
/* In old Linux versions uid_is_unique doesn't exist
|
||||
* so don't treat this as an error.
|
||||
*/
|
||||
rc = util_file_read_i(&val, 10, "%s/uid_is_unique", path);
|
||||
if (!rc)
|
||||
zdev->uid_is_unique = !!val;
|
||||
|
||||
rc = util_file_read_i(&val, 16, "%s/pchid", path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
zdev->pchid = val;
|
||||
|
||||
rc = util_file_read_i(&val, 16, "%s/vfn", path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
zdev->vfn = val;
|
||||
|
||||
rc = util_file_read_i(&val, 10, "%s/port", path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
zdev->port = val;
|
||||
|
||||
rc = util_file_read_i(&val, 16, "%s/pft", path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
zdev->pft = val;
|
||||
|
||||
if (util_path_is_readable("%s/net", path)) {
|
||||
rc = zpci_populate_netdevices(zdev, path);
|
||||
if (rc)
|
||||
goto out_path;
|
||||
}
|
||||
out_path:
|
||||
free(path);
|
||||
out_pci_addr:
|
||||
free(pci_addr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all configured and standby PCI devices
|
||||
*
|
||||
* @return a list of struct zpci_dev in case of success,
|
||||
* NULL in case of failure
|
||||
*/
|
||||
struct util_list *zpci_dev_list(void)
|
||||
{
|
||||
char *path = util_path_sysfs("bus/pci/slots/");
|
||||
const char *zpci_slot_patt = "[0-9a-f]{8}";
|
||||
struct util_list *zpci_list = NULL;
|
||||
struct dirent **de_vec;
|
||||
struct zpci_dev *zdev;
|
||||
int count, i, rc;
|
||||
|
||||
count = util_scandir(&de_vec, alphasort, path, zpci_slot_patt);
|
||||
if (count == -1) {
|
||||
warn("util_scandir failed");
|
||||
goto error_path;
|
||||
}
|
||||
zpci_list = util_list_new(struct zpci_dev, entry);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (de_vec[i]->d_type != DT_DIR)
|
||||
continue;
|
||||
zdev = util_zalloc(sizeof(*zdev));
|
||||
rc = zpci_populate_from_slot_dir(zdev, path, de_vec[i]->d_name);
|
||||
if (rc) {
|
||||
free(zdev);
|
||||
continue;
|
||||
}
|
||||
if (zdev->conf) {
|
||||
rc = zpci_populate_from_dev_dir(zdev);
|
||||
if (rc) {
|
||||
free(zdev);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
util_list_add_tail(zpci_list, zdev);
|
||||
}
|
||||
|
||||
util_scandir_free(de_vec, count);
|
||||
error_path:
|
||||
free(path);
|
||||
return zpci_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a PCI device struct
|
||||
*
|
||||
* This frees both the struct zpci_dev and its associated netdevs array
|
||||
*
|
||||
* @param[in] zdev The device struct to free
|
||||
*/
|
||||
void zpci_free_dev(struct zpci_dev *zdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (zdev->num_netdevs) {
|
||||
for (i = 0; i < zdev->num_netdevs; i++)
|
||||
free(zdev->netdevs[i].name);
|
||||
free(zdev->netdevs);
|
||||
}
|
||||
free(zdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a PCI device list
|
||||
*
|
||||
* This frees all elements in the list
|
||||
*
|
||||
* @param[in] zpci_list The device list to free
|
||||
*/
|
||||
void zpci_free_dev_list(struct util_list *zpci_list)
|
||||
{
|
||||
struct zpci_dev *zdev, *tmp;
|
||||
|
||||
util_list_iterate_safe(zpci_list, zdev, tmp) {
|
||||
util_list_remove(zpci_list, zdev);
|
||||
zpci_free_dev(zdev);
|
||||
}
|
||||
util_list_free(zpci_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a PCI device given the name of a netdev
|
||||
*
|
||||
* This function allows finding a PCI device when only the name of one
|
||||
* of its netdevs is known.
|
||||
*
|
||||
* @param[in] zpci_list The device list to search
|
||||
* @param[in] netdev_name The name of the netdev
|
||||
* @param[out] netdev Pointer to store the netdev or NULL if
|
||||
* only the PCI device is needed
|
||||
*
|
||||
* @return The PCI device if one is found NULL otherwise
|
||||
*/
|
||||
struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||
struct zpci_netdev **netdev)
|
||||
{
|
||||
struct zpci_dev *zdev = NULL;
|
||||
int i;
|
||||
|
||||
util_list_iterate(zpci_list, zdev) {
|
||||
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
if (!strcmp(zdev->netdevs[i].name, netdev_name)) {
|
||||
if (netdev)
|
||||
*netdev = &zdev->netdevs[i];
|
||||
return zdev;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
61
libzpci/pci_sclp.c
Normal file
61
libzpci/pci_sclp.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "lib/pci_sclp.h"
|
||||
#include "lib/util_path.h"
|
||||
|
||||
static int zpci_sclp_report(char *pci_addr, struct zpci_report_error *report)
|
||||
{
|
||||
size_t r_size = sizeof(*report);
|
||||
char *path;
|
||||
FILE *fp;
|
||||
|
||||
path = util_path_sysfs("bus/pci/devices/%s/report_error", pci_addr);
|
||||
fp = fopen(path, "w");
|
||||
free(path);
|
||||
if (!fp)
|
||||
return -ENODEV;
|
||||
if (fwrite(report, 1, r_size, fp) != r_size)
|
||||
return -EIO;
|
||||
if (fclose(fp))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an SCLP Adapter Error Notification event with a specific action
|
||||
* qualifier and optional log data.
|
||||
*
|
||||
* The logged data is truncated if needed.
|
||||
*
|
||||
* @return the number of bytes of the data which were actually logged
|
||||
* or a negative value on error.
|
||||
*/
|
||||
int zpci_sclp_issue_action(char *pci_addr, int action,
|
||||
char *data, size_t length, u64 err_log_id)
|
||||
{
|
||||
struct zpci_report_error report = {0};
|
||||
size_t copy_length = 0;
|
||||
int ret;
|
||||
|
||||
/* Data is truncated to fit in the report */
|
||||
if (data)
|
||||
copy_length = MIN(length, sizeof(report.data.log_data));
|
||||
report.header.version = 1;
|
||||
report.header.action = action;
|
||||
report.header.length = offsetof(struct zpci_report_error_data, log_data) + copy_length;
|
||||
report.data.timestamp = (__u64)time(NULL);
|
||||
report.data.err_log_id = err_log_id;
|
||||
|
||||
if (data)
|
||||
memcpy(report.data.log_data, data, copy_length);
|
||||
ret = zpci_sclp_report(pci_addr, &report);
|
||||
if (ret)
|
||||
return ret;
|
||||
return copy_length;
|
||||
}
|
||||
@@ -171,8 +171,6 @@ static const char help_text[] =
|
||||
"-h, --help Print this help, then exit\n"
|
||||
"-v, --version Print version information, then exit\n"
|
||||
"-a, --attach Run in foreground\n"
|
||||
"-i, --interval=<seconds> Sample interval\n"
|
||||
"\n"
|
||||
"Please report bugs to: linux390@de.ibm.com\n";
|
||||
"-i, --interval=<seconds> Sample interval\n";
|
||||
#endif
|
||||
|
||||
|
||||
83
opticsmon/Makefile
Normal file
83
opticsmon/Makefile
Normal file
@@ -0,0 +1,83 @@
|
||||
include ../common.mak
|
||||
|
||||
TESTS := tests/
|
||||
|
||||
libs =$(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a
|
||||
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
check_dep_openssl:
|
||||
$(call check_dep, \
|
||||
"opticsmon", \
|
||||
"openssl/evp.h", \
|
||||
"openssl-devel", \
|
||||
"HAVE_OPENSSL=0")
|
||||
BUILDTARGET += check_dep_openssl
|
||||
endif # HAVE_OPENSSL
|
||||
|
||||
ifneq (${HAVE_LIBNL3},0)
|
||||
check_dep_libnl3:
|
||||
$(call check_dep, \
|
||||
"opticsmon", \
|
||||
"netlink/socket.h", \
|
||||
"libnl3-devel", \
|
||||
"HAVE_LIBNL3=0")
|
||||
BUILDTARGET += check_dep_libnl3
|
||||
endif # HAVE_LIBNL3
|
||||
|
||||
ifeq (${HAVE_OPENSSL},0)
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_OPENSSL=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_OPENSSL=0
|
||||
|
||||
else ifeq (${HAVE_LIBNL3},0)
|
||||
all:
|
||||
$(SKIP) HAVE_LIBNL3=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_LIBNL3=0
|
||||
|
||||
else
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-3.0)
|
||||
LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-genl-3.0)
|
||||
LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-route-3.0)
|
||||
|
||||
LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libcrypto)
|
||||
|
||||
LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-3.0)
|
||||
LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-genl-3.0)
|
||||
LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-route-3.0)
|
||||
|
||||
LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libcrypto)
|
||||
else
|
||||
LIB_CFLAGS += -I /usr/include/libnl3/
|
||||
LIB_LFLAGS += -lnl-route-3 -lnl-genl-3 -lnl-3
|
||||
|
||||
LIB_CFLAGS += -I /usr/include/openssl/
|
||||
LIB_LFLAGS += -lcrypto
|
||||
endif
|
||||
|
||||
ALL_CPPFLAGS += $(LIB_CFLAGS)
|
||||
LDLIBS += $(LIB_LFLAGS)
|
||||
|
||||
BUILDTARGET += opticsmon
|
||||
|
||||
all: ${BUILDTARGET}
|
||||
|
||||
opticsmon: opticsmon.o optics_info.o optics_sclp.o ethtool.o link_mon.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 opticsmon $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 opticsmon.8 \
|
||||
$(DESTDIR)$(MANDIR)/man8
|
||||
endif # HAVE_OPENSSL3=0 or HAVE_LIBNL3=0
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ opticsmon core
|
||||
|
||||
.PHONY: all install clean
|
||||
279
opticsmon/ethtool.c
Normal file
279
opticsmon/ethtool.c
Normal file
@@ -0,0 +1,279 @@
|
||||
#include <execinfo.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include <netlink/socket.h>
|
||||
#include <netlink/msg.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <netlink/handlers.h>
|
||||
#include <netlink/attr.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include "ethtool.h"
|
||||
|
||||
static int ethtool_nl_cb(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nlattr *attrs[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
|
||||
struct nlmsghdr *hdr = nlmsg_hdr(msg);
|
||||
struct optics **oi = arg;
|
||||
int rc = 0;
|
||||
size_t len;
|
||||
|
||||
rc = genlmsg_parse(hdr, 0, attrs, ETHTOOL_A_MODULE_EEPROM_DATA, NULL);
|
||||
if (rc) {
|
||||
nl_perror(rc, "genlmsg parse");
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
len = nla_len(attrs[ETHTOOL_A_MODULE_EEPROM_DATA]);
|
||||
|
||||
/* Extend optics info*/
|
||||
if (!(*oi)->raw)
|
||||
(*oi)->raw = util_malloc(len);
|
||||
else
|
||||
(*oi)->raw = util_realloc((*oi)->raw, (*oi)->size + len);
|
||||
memcpy((*oi)->raw + (*oi)->size, nla_data(attrs[ETHTOOL_A_MODULE_EEPROM_DATA]), len);
|
||||
(*oi)->size += len;
|
||||
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
int ethtool_nl_connect(struct ethtool_nl_ctx *ctx)
|
||||
{
|
||||
struct nl_sock *sk;
|
||||
int ethtool_id;
|
||||
int rc = 0;
|
||||
|
||||
sk = nl_socket_alloc();
|
||||
if (!sk) {
|
||||
nl_perror(NLE_NOMEM, "alloc");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = genl_connect(sk);
|
||||
if (rc) {
|
||||
nl_perror(rc, "connect");
|
||||
rc = EXIT_FAILURE;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ethtool_id = genl_ctrl_resolve(sk, ETHTOOL_GENL_NAME);
|
||||
if (ethtool_id < 0) {
|
||||
if (ethtool_id == -NLE_OBJ_NOTFOUND)
|
||||
fprintf(stderr, "Ethtool netlink family not found\n");
|
||||
else
|
||||
nl_perror(ethtool_id, "ctrl resolve");
|
||||
rc = EXIT_FAILURE;
|
||||
goto err_close;
|
||||
}
|
||||
ctx->sk = sk;
|
||||
ctx->ethtool_id = ethtool_id;
|
||||
return rc;
|
||||
|
||||
err_close:
|
||||
nl_close(sk);
|
||||
err_free:
|
||||
nl_socket_free(sk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ethtool_nl_close(struct ethtool_nl_ctx *ctx)
|
||||
{
|
||||
nl_close(ctx->sk);
|
||||
nl_socket_free(ctx->sk);
|
||||
}
|
||||
|
||||
static int ethtool_nl_put_req_hdr(struct ethtool_nl_ctx *ctx, struct nl_msg *msg, uint8_t cmd,
|
||||
const char *netdev)
|
||||
{
|
||||
struct nlattr *opts;
|
||||
void *user_hdr;
|
||||
int rc = 0;
|
||||
|
||||
user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->ethtool_id, 0,
|
||||
NLM_F_REQUEST | NLM_F_ACK, cmd, ETHTOOL_GENL_VERSION);
|
||||
if (!user_hdr) {
|
||||
fprintf(stderr, "genlmsg put failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
opts = nla_nest_start(msg, ETHTOOL_A_MODULE_EEPROM_HEADER);
|
||||
if (!opts) {
|
||||
fprintf(stderr, "nla nest for start failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
NLA_PUT_STRING(msg, ETHTOOL_A_HEADER_DEV_NAME, netdev);
|
||||
nla_nest_end(msg, opts);
|
||||
return rc;
|
||||
|
||||
nla_put_failure:
|
||||
nla_nest_cancel(msg, opts);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int ethtool_nl_put_eeprom_get_attrs(struct nl_msg *msg, uint8_t addr, uint8_t page,
|
||||
uint32_t offset)
|
||||
{
|
||||
NLA_PUT_U32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, SFF8636_PAGE_SIZE);
|
||||
NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, page);
|
||||
NLA_PUT_U32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, offset);
|
||||
NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, 0);
|
||||
NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, addr);
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int ethtool_nl_get_page(struct ethtool_nl_ctx *ctx, const char *netdev, uint8_t addr,
|
||||
uint8_t page, uint32_t offset)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
int rc = 0;
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg) {
|
||||
nl_perror(NLE_NOMEM, "nlmsg alloc");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ethtool_nl_put_req_hdr(ctx, msg, ETHTOOL_MSG_MODULE_EEPROM_GET, netdev);
|
||||
ethtool_nl_put_eeprom_get_attrs(msg, addr, page, offset);
|
||||
rc = nl_send_auto(ctx->sk, msg);
|
||||
if (rc < 0) {
|
||||
nl_perror(rc, "Failed to send netlink message");
|
||||
rc = -EIO;
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
rc = nl_recvmsgs_default(ctx->sk);
|
||||
if (rc < 0) {
|
||||
if (rc == -NLE_NODEV) {
|
||||
rc = -ENODEV;
|
||||
} else {
|
||||
nl_perror(rc, "Failed to receive netlink message");
|
||||
rc = -EIO;
|
||||
}
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
/* Ethtool netlink sends ACKs need to pick them up */
|
||||
rc = nl_wait_for_ack(ctx->sk);
|
||||
if (rc < 0) {
|
||||
nl_perror(rc, "Failed to wait for netlink ack");
|
||||
rc = -EIO;
|
||||
goto free_msg;
|
||||
}
|
||||
free_msg:
|
||||
nlmsg_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ethtool_nl_get_sfp(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics *oi)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Page A0h upper */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, SFF8636_PAGE_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* If page A2h is not present we're done */
|
||||
if (!(oi->raw[SFF8472_DIAGNOSTICS_TYPE_OFFSET] & SFF8472_DIAGNOSTICS_TYPE_MASK))
|
||||
return 0;
|
||||
|
||||
/* Page A2h lower */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_HIGH, 0x0, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Page A2h upper */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_HIGH, 0x0, SFF8636_PAGE_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethtool_nl_get_qsfp(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics *oi)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Page 00h upper */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, SFF8636_PAGE_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Page 01h */
|
||||
if (oi->raw[SFF8636_PAGE_OFFSET] & SFF8636_P01H) {
|
||||
/* Page 01h upper only */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x1,
|
||||
SFF8636_PAGE_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Page 02h */
|
||||
if (oi->raw[SFF8636_PAGE_OFFSET] & SFF8636_P02H) {
|
||||
/* Page 02h upper only */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x2,
|
||||
SFF8636_PAGE_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Page 03h is present if flatmem is not set */
|
||||
if (!(oi->raw[SFF8636_STATUS_2_OFFSET] & SFF8636_STATUS_FLAT_MEM)) {
|
||||
/* Page 03h upper only */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x3,
|
||||
SFF8636_PAGE_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethtool_nl_get_optics(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics **oi)
|
||||
{
|
||||
int rc = 0;
|
||||
int type;
|
||||
|
||||
*oi = util_zalloc(sizeof(**oi));
|
||||
nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, ethtool_nl_cb, oi);
|
||||
|
||||
/* Page 00h lower */
|
||||
rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, 0);
|
||||
if (rc < 0)
|
||||
goto out_err_free_oi;
|
||||
|
||||
type = optics_type(*oi);
|
||||
switch (type) {
|
||||
case OPTICS_TYPE_SFP:
|
||||
rc = ethtool_nl_get_sfp(ctx, netdev, *oi);
|
||||
break;
|
||||
case OPTICS_TYPE_QSFP28:
|
||||
rc = ethtool_nl_get_qsfp(ctx, netdev, *oi);
|
||||
break;
|
||||
};
|
||||
if (rc < 0)
|
||||
goto out_err_free_oi;
|
||||
|
||||
return rc;
|
||||
|
||||
out_err_free_oi:
|
||||
free(*oi);
|
||||
*oi = NULL;
|
||||
return rc;
|
||||
}
|
||||
11
opticsmon/ethtool.h
Normal file
11
opticsmon/ethtool.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "optics_info.h"
|
||||
|
||||
struct ethtool_nl_ctx {
|
||||
struct nl_sock *sk;
|
||||
int ethtool_id;
|
||||
};
|
||||
|
||||
int ethtool_nl_connect(struct ethtool_nl_ctx *ctx);
|
||||
void ethtool_nl_close(struct ethtool_nl_ctx *ctx);
|
||||
int ethtool_nl_get_optics(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics **oi);
|
||||
96
opticsmon/link_mon.c
Normal file
96
opticsmon/link_mon.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <signal.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include <netlink/route/link.h>
|
||||
#include <netlink/netlink.h>
|
||||
|
||||
#include "link_mon.h"
|
||||
|
||||
#define MAX_EVENTS 32
|
||||
|
||||
static void nl_obj_parsed_cb(struct nl_object *obj, void *arg)
|
||||
{
|
||||
struct link_mon_nl_ctx *ctx = arg;
|
||||
struct rtnl_link *link;
|
||||
struct zpci_netdev netdev;
|
||||
|
||||
if (strcmp(nl_object_get_type(obj), "route/link") != 0)
|
||||
return;
|
||||
link = (struct rtnl_link *)obj;
|
||||
netdev.name = rtnl_link_get_name(link);
|
||||
netdev.operstate = rtnl_link_get_operstate(link);
|
||||
ctx->cb(&netdev, ctx->arg);
|
||||
}
|
||||
|
||||
static int nl_rtnl_lnkgrp_cb(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
if (nl_msg_parse(msg, &nl_obj_parsed_cb, arg) < 0)
|
||||
fprintf(stderr, "<<EVENT>> Unknown message type\n");
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
void link_mon_nl_waitfd_read(struct link_mon_nl_ctx *ctx)
|
||||
{
|
||||
nl_recvmsgs_default(ctx->sk);
|
||||
}
|
||||
|
||||
int link_mon_nl_waitfd_getfd(struct link_mon_nl_ctx *ctx)
|
||||
{
|
||||
return nl_socket_get_fd(ctx->sk);
|
||||
}
|
||||
|
||||
int link_mon_nl_waitfd_create(struct link_mon_nl_ctx *ctx, link_mon_nl_cb cb, void *arg)
|
||||
{
|
||||
int ret = 0, rc = 0;
|
||||
|
||||
ctx->sk = nl_socket_alloc();
|
||||
if (!ctx->sk)
|
||||
return -ENOMEM;
|
||||
ctx->cb = cb;
|
||||
ctx->arg = arg;
|
||||
nl_socket_disable_seq_check(ctx->sk);
|
||||
nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, nl_rtnl_lnkgrp_cb, ctx);
|
||||
|
||||
rc = nl_connect(ctx->sk, NETLINK_ROUTE);
|
||||
if (rc < 0) {
|
||||
ret = rc;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rc = nl_socket_add_membership(ctx->sk, RTNLGRP_LINK);
|
||||
if (rc < 0) {
|
||||
ret = rc;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
rc = rtnl_link_alloc_cache(ctx->sk, AF_UNSPEC, &ctx->cache);
|
||||
if (rc < 0) {
|
||||
ret = rc;
|
||||
goto err_close;
|
||||
}
|
||||
nl_cache_mngt_provide(ctx->cache);
|
||||
return 0;
|
||||
|
||||
err_close:
|
||||
nl_close(ctx->sk);
|
||||
err_free:
|
||||
nl_socket_free(ctx->sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void link_mon_nl_waitfd_destroy(struct link_mon_nl_ctx *ctx)
|
||||
{
|
||||
nl_cache_free(ctx->cache);
|
||||
nl_close(ctx->sk);
|
||||
nl_socket_free(ctx->sk);
|
||||
}
|
||||
27
opticsmon/link_mon.h
Normal file
27
opticsmon/link_mon.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include <linux/if.h>
|
||||
|
||||
#include "lib/pci_list.h"
|
||||
|
||||
typedef void (*link_mon_nl_cb)(struct zpci_netdev *, void *arg);
|
||||
|
||||
struct link_mon_nl_ctx {
|
||||
/* private fields */
|
||||
struct nl_sock *sk;
|
||||
struct nl_cache *cache;
|
||||
link_mon_nl_cb cb;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
int link_mon_nl_waitfd_create(struct link_mon_nl_ctx *ctx, link_mon_nl_cb cb, void *arg);
|
||||
void link_mon_nl_waitfd_read(struct link_mon_nl_ctx *ctx);
|
||||
void link_mon_nl_waitfd_destroy(struct link_mon_nl_ctx *ctx);
|
||||
int link_mon_nl_waitfd_getfd(struct link_mon_nl_ctx *ctx);
|
||||
171
opticsmon/optics_info.c
Normal file
171
opticsmon/optics_info.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "optics_info.h"
|
||||
|
||||
#define OPTICS_TYPE_OFFSET 0x0
|
||||
|
||||
#define OPTICS_SFP_LOS_IMPLEMENTED_OFFSET 0x41
|
||||
#define OPTICS_SFP_LOS_IMPLEMENTED_MASK 0x2
|
||||
|
||||
#define OPTICS_SFP_A2H_OFFSET 0x100
|
||||
#define OPTICS_SFP_LOS_OFFSET (OPTICS_SFP_A2H_OFFSET + 0x6e)
|
||||
#define OPTICS_SFP_DATA_NOT_READY_MASK 0x1
|
||||
#define OPTICS_SFP_TX_FAULT_MASK 0x4
|
||||
#define OPTICS_SFP_RX_LOS_MASK 0x2
|
||||
|
||||
#define OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET 0xC3
|
||||
#define OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK 0x2
|
||||
#define OPTICS_QSFP28_TX_FAULT_IMPLEMENTED_MASK 0x8
|
||||
|
||||
#define OPTICS_QSFP28_LOS_OFFSET 0x3
|
||||
#define OPTICS_QSFP28_LOS_MASK 0xf
|
||||
#define OPTICS_QSFP28_TX_LOS_MASK 0xf0
|
||||
#define OPTICS_QSFP28_TX_LOS_SHIFT 0x4
|
||||
|
||||
#define OPTICS_QSFP28_TX_FAULT_OFFSET 0x4
|
||||
#define OPTICS_QSFP28_TX_FAULT_MASK 0xf
|
||||
|
||||
const char *optics_type_str(enum optics_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case OPTICS_TYPE_UNKNOWN:
|
||||
return "unknown";
|
||||
case OPTICS_TYPE_SFP:
|
||||
return "SFP/SFP+/SFP28";
|
||||
case OPTICS_TYPE_QSFP28:
|
||||
return "QSFP28";
|
||||
};
|
||||
return "n.a.";
|
||||
}
|
||||
|
||||
enum optics_type optics_type(struct optics *oi)
|
||||
{
|
||||
if (!oi || !oi->raw || oi->size < OPTICS_TYPE_OFFSET + 1)
|
||||
return OPTICS_TYPE_UNKNOWN;
|
||||
|
||||
switch (oi->raw[OPTICS_TYPE_OFFSET]) {
|
||||
case (uint8_t)OPTICS_TYPE_SFP:
|
||||
return OPTICS_TYPE_SFP;
|
||||
case (uint8_t)OPTICS_TYPE_QSFP28:
|
||||
return OPTICS_TYPE_QSFP28;
|
||||
default:
|
||||
return OPTICS_TYPE_UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
bool optics_los_implemented(struct optics *oi)
|
||||
{
|
||||
enum optics_type type = optics_type(oi);
|
||||
uint8_t implemented;
|
||||
|
||||
if (type == OPTICS_TYPE_SFP) {
|
||||
if (oi->size < OPTICS_SFP_LOS_IMPLEMENTED_OFFSET + 1)
|
||||
return false;
|
||||
implemented = oi->raw[OPTICS_SFP_LOS_IMPLEMENTED_OFFSET];
|
||||
return !!(implemented & OPTICS_SFP_LOS_IMPLEMENTED_MASK);
|
||||
} else if (type == OPTICS_TYPE_QSFP28) {
|
||||
if (oi->size < OPTICS_QSFP28_LOS_OFFSET + 1)
|
||||
return false;
|
||||
if (oi->size < OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET)
|
||||
return false;
|
||||
implemented = oi->raw[OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET];
|
||||
/*
|
||||
* No RX LoS implemented flag take TX LOS implemented like
|
||||
* ethtool
|
||||
*/
|
||||
return !!(implemented & OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum optics_los optics_rx_los(struct optics *oi)
|
||||
{
|
||||
enum optics_los los = OPTICS_UNKNOWN_LOS;
|
||||
enum optics_type type = optics_type(oi);
|
||||
|
||||
if (!optics_los_implemented(oi))
|
||||
return los;
|
||||
|
||||
if (type == OPTICS_TYPE_SFP) {
|
||||
los = oi->raw[OPTICS_SFP_LOS_OFFSET];
|
||||
if (los & OPTICS_SFP_DATA_NOT_READY_MASK)
|
||||
return OPTICS_UNKNOWN_LOS;
|
||||
if (los & OPTICS_SFP_RX_LOS_MASK)
|
||||
return OPTICS_LOS;
|
||||
else
|
||||
return OPTICS_NO_LOS;
|
||||
} else if (type == OPTICS_TYPE_QSFP28) {
|
||||
los = oi->raw[OPTICS_QSFP28_LOS_OFFSET];
|
||||
if (los & OPTICS_QSFP28_LOS_MASK)
|
||||
los = OPTICS_LOS;
|
||||
else
|
||||
los = OPTICS_NO_LOS;
|
||||
}
|
||||
return los;
|
||||
}
|
||||
|
||||
const char *optics_los_str(enum optics_los los)
|
||||
{
|
||||
switch (los) {
|
||||
case OPTICS_LOS:
|
||||
return "yes";
|
||||
case OPTICS_NO_LOS:
|
||||
return "no";
|
||||
case OPTICS_UNAVAILABLE_LOS:
|
||||
return "unavailable";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
enum optics_los optics_tx_fault(struct optics *oi)
|
||||
{
|
||||
enum optics_los los = OPTICS_UNKNOWN_LOS;
|
||||
enum optics_type type = optics_type(oi);
|
||||
|
||||
if (!optics_los_implemented(oi))
|
||||
return los;
|
||||
|
||||
if (type == OPTICS_TYPE_SFP) {
|
||||
los = oi->raw[OPTICS_SFP_LOS_OFFSET];
|
||||
if (los & OPTICS_SFP_DATA_NOT_READY_MASK)
|
||||
return OPTICS_UNKNOWN_LOS;
|
||||
if (los & OPTICS_SFP_TX_FAULT_MASK)
|
||||
return OPTICS_LOS;
|
||||
else
|
||||
return OPTICS_NO_LOS;
|
||||
} else if (type == OPTICS_TYPE_QSFP28) {
|
||||
los = oi->raw[OPTICS_QSFP28_TX_FAULT_OFFSET];
|
||||
if (los & OPTICS_QSFP28_TX_FAULT_MASK)
|
||||
los = OPTICS_LOS;
|
||||
else
|
||||
los = OPTICS_NO_LOS;
|
||||
}
|
||||
return los;
|
||||
}
|
||||
|
||||
enum optics_los optics_tx_los(struct optics *oi)
|
||||
{
|
||||
enum optics_los los = OPTICS_UNKNOWN_LOS;
|
||||
enum optics_type type = optics_type(oi);
|
||||
|
||||
if (!optics_los_implemented(oi))
|
||||
return los;
|
||||
|
||||
if (type == OPTICS_TYPE_SFP) {
|
||||
return OPTICS_UNAVAILABLE_LOS;
|
||||
} else if (type == OPTICS_TYPE_QSFP28) {
|
||||
los = oi->raw[OPTICS_QSFP28_LOS_OFFSET];
|
||||
if (los & OPTICS_QSFP28_TX_LOS_MASK)
|
||||
los = OPTICS_LOS;
|
||||
else
|
||||
los = OPTICS_NO_LOS;
|
||||
}
|
||||
return los;
|
||||
}
|
||||
|
||||
void optics_free(struct optics *oi)
|
||||
{
|
||||
free(oi->raw);
|
||||
free(oi);
|
||||
}
|
||||
49
opticsmon/optics_info.h
Normal file
49
opticsmon/optics_info.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define SFF8079_I2C_ADDRESS_LOW 0x50
|
||||
#define SFF8079_I2C_ADDRESS_HIGH 0x51
|
||||
|
||||
#define SFF8472_DIAGNOSTICS_TYPE_OFFSET 0x5C
|
||||
#define SFF8472_DIAGNOSTICS_TYPE_MASK (1 << 6)
|
||||
|
||||
#define SFF8636_PAGE_SIZE 0x80
|
||||
#define SFF8636_QSFP28_LENGTH 0x100
|
||||
|
||||
#define SFF8636_STATUS_2_OFFSET 0x02
|
||||
#define SFF8636_STATUS_FLAT_MEM (1 << 2)
|
||||
|
||||
#define SFF8636_PAGE_OFFSET 0xC3
|
||||
#define SFF8636_P01H (1 << 6)
|
||||
#define SFF8636_P02H (1 << 7)
|
||||
|
||||
enum optics_type {
|
||||
OPTICS_TYPE_UNKNOWN = 0x0, /* Unknown or unsupported */
|
||||
OPTICS_TYPE_SFP = 0x3, /* SFP/SFP+/SFP28 and later with SFF-8472 management interface */
|
||||
OPTICS_TYPE_QSFP28 = 0x11 /* QSFP28 (SFF-8665 et al.)*/
|
||||
};
|
||||
|
||||
enum optics_los {
|
||||
OPTICS_NO_LOS = 0x0,
|
||||
OPTICS_LOS = 0x1,
|
||||
OPTICS_UNKNOWN_LOS = 0x2,
|
||||
OPTICS_UNAVAILABLE_LOS = 0x3,
|
||||
};
|
||||
|
||||
struct optics {
|
||||
size_t size;
|
||||
uint8_t *raw;
|
||||
};
|
||||
|
||||
enum optics_type optics_type(struct optics *oi);
|
||||
const char *optics_type_str(enum optics_type type);
|
||||
const char *optics_los_str(enum optics_los los);
|
||||
|
||||
enum optics_los optics_rx_los(struct optics *oi);
|
||||
enum optics_los optics_tx_los(struct optics *oi);
|
||||
enum optics_los optics_tx_fault(struct optics *oi);
|
||||
|
||||
void optics_free(struct optics *oi);
|
||||
50
opticsmon/optics_sclp.c
Normal file
50
opticsmon/optics_sclp.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/pci_sclp.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include "optics_sclp.h"
|
||||
|
||||
static struct sclp_optics_data *init_sclp_optics_data(struct optics *oi, size_t *length)
|
||||
{
|
||||
struct sclp_optics_data *od;
|
||||
|
||||
*length = sizeof(*od) + oi->size;
|
||||
od = util_zalloc(*length);
|
||||
od->module_present = optics_type(oi) != OPTICS_TYPE_UNKNOWN;
|
||||
od->rx_los = optics_rx_los(oi) == OPTICS_LOS;
|
||||
od->tx_fault = optics_tx_fault(oi) == OPTICS_LOS;
|
||||
switch (optics_type(oi)) {
|
||||
case OPTICS_TYPE_SFP:
|
||||
od->data_identifier = 1;
|
||||
break;
|
||||
case OPTICS_TYPE_QSFP28:
|
||||
od->data_identifier = 2;
|
||||
break;
|
||||
default:
|
||||
od->data_identifier = 0;
|
||||
}
|
||||
memcpy(od->data, oi->raw, oi->size);
|
||||
|
||||
return od;
|
||||
}
|
||||
|
||||
int sclp_issue_optics_report(struct zpci_dev *zdev, struct optics *oi)
|
||||
{
|
||||
struct sclp_optics_data *od;
|
||||
size_t length;
|
||||
char *pci_addr;
|
||||
int rc;
|
||||
|
||||
if (zdev->pft != ZPCI_PFT_NETD)
|
||||
return -ENOTSUP;
|
||||
od = init_sclp_optics_data(oi, &length);
|
||||
pci_addr = zpci_pci_addr(zdev);
|
||||
rc = zpci_sclp_issue_action(pci_addr, SCLP_ERRNOTIFY_AQ_OPTICS_DATA,
|
||||
(char *)od, length, SCLP_ERRNOTIFY_ID_OPTICSMON);
|
||||
free(pci_addr);
|
||||
free(od);
|
||||
return rc;
|
||||
}
|
||||
21
opticsmon/optics_sclp.h
Normal file
21
opticsmon/optics_sclp.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/pci_list.h"
|
||||
|
||||
#include "optics_info.h"
|
||||
|
||||
struct sclp_optics_data {
|
||||
/* Status */
|
||||
uint32_t module_present : 1;
|
||||
uint32_t rx_los : 1;
|
||||
uint32_t tx_fault : 1;
|
||||
uint32_t reserved_status : 29;
|
||||
/* Data Identifier */
|
||||
uint32_t data_identifier;
|
||||
/* Reserved */
|
||||
uint64_t reserved[3];
|
||||
/* Additional Log Data */
|
||||
uint8_t data[];
|
||||
} __packed;
|
||||
|
||||
int sclp_issue_optics_report(struct zpci_dev *zdev, struct optics *oi);
|
||||
83
opticsmon/opticsmon.8
Normal file
83
opticsmon/opticsmon.8
Normal file
@@ -0,0 +1,83 @@
|
||||
.\" Copyright IBM Corp. 2024
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.\" Macro for inserting an option description prologue.
|
||||
.\" .OD <long> [<short>] [args]
|
||||
.de OD
|
||||
. ds args "
|
||||
. if !'\\$3'' .as args \fI\\$3\fP
|
||||
. if !'\\$4'' .as args \\$4
|
||||
. if !'\\$5'' .as args \fI\\$5\fP
|
||||
. if !'\\$6'' .as args \\$6
|
||||
. if !'\\$7'' .as args \fI\\$7\fP
|
||||
. PD 0
|
||||
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
|
||||
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
||||
. PD
|
||||
..
|
||||
.
|
||||
.TH opticsmon 8 "Oct 2024" s390-tools zpcictl
|
||||
.
|
||||
.SH NAME
|
||||
opticsmon - Monitor optical modules for directly attached PCI based NICs
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B "opticsmon"
|
||||
.I "OPTIONS"
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
Use
|
||||
.B opticsmon
|
||||
to monitor the health of the optical modules of directly attached PCI based
|
||||
NICs. When executed without the \fB--daemon\fR option it will collect optical
|
||||
module data from all available PCI network interface physical functions and
|
||||
print a summary in JSON format. Add the \fB--send-report\fR option to report
|
||||
this data to the support element.
|
||||
|
||||
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.SS Operation Options
|
||||
.OD daemon "d"
|
||||
Run continuously and report on link state changes and periodically
|
||||
.PP
|
||||
.
|
||||
.OD send-report "r"
|
||||
Report the optics health data to the Support Element (SE)
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q"
|
||||
Be quiet and don't print optics health summary
|
||||
.PP
|
||||
.
|
||||
.OD interval "i" "seconds"
|
||||
Interval in seconds at which to collect monitoring data in the absence of link
|
||||
state changes. A value larger than 24 hours (86400 seconds) is clamped down to
|
||||
24 hours.
|
||||
.PP
|
||||
.
|
||||
.OD module-info ""
|
||||
Include a base64 encoded binary dump of the module's SFF-8636/8472/8024
|
||||
standard data for each netdev. This matches "ethtool --module-info <netdev> raw
|
||||
on".
|
||||
|
||||
.B Example: Extract module information for the first adapter
|
||||
|
||||
.CL
|
||||
opticsmon --module-info | jq -r '.adapters[0].netdevs[0].optics.module_info' | base64 -d | hexdump -C
|
||||
.PP
|
||||
.PP
|
||||
.
|
||||
.SS General Options
|
||||
.OD help "h" ""
|
||||
Print usage information, then exit.
|
||||
.PP
|
||||
.
|
||||
.OD version "v" ""
|
||||
Print version information, then exit.
|
||||
.PP
|
||||
|
||||
444
opticsmon/opticsmon.c
Normal file
444
opticsmon/opticsmon.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* opticsmon - Report optics monitoring data to firmware
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/pci_list.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "optics_info.h"
|
||||
#include "optics_sclp.h"
|
||||
#include "ethtool.h"
|
||||
#include "link_mon.h"
|
||||
|
||||
#define API_LEVEL 1
|
||||
|
||||
struct options {
|
||||
bool monitor;
|
||||
bool report;
|
||||
bool module_info;
|
||||
bool quiet;
|
||||
|
||||
uint32_t interval_seconds;
|
||||
};
|
||||
|
||||
struct opticsmon_ctx {
|
||||
struct options opts;
|
||||
struct ethtool_nl_ctx ethtool_ctx;
|
||||
struct link_mon_nl_ctx lctx;
|
||||
struct util_list *zpci_list;
|
||||
};
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "Use opticsmon to monitor the health of the optical modules\n"
|
||||
"of directly attached PCI based NICs",
|
||||
.copyright_vec = { {
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2024,
|
||||
.pub_last = 2024,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END }
|
||||
};
|
||||
|
||||
#define OPT_DUMP 128
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPERATION OPTIONS"),
|
||||
{
|
||||
.option = { "monitor", no_argument, NULL, 'm' },
|
||||
.desc = "Run continuously and report on link state changes "
|
||||
"collecting optics health data when a change is detected",
|
||||
},
|
||||
{
|
||||
.option = { "send-report", no_argument, NULL, 'r' },
|
||||
.desc = "Report the optics health data to the Support Element",
|
||||
},
|
||||
{
|
||||
.option = { "quiet", no_argument, NULL, 'q' },
|
||||
.desc = "Be quiet and don't print optics health summary",
|
||||
},
|
||||
{
|
||||
.option = { "module-info", no_argument, NULL, OPT_DUMP },
|
||||
.desc = "Include a base64 encoded binary dump of the module's "
|
||||
"SFF-8636/8472/8024 standard data for each netdev. "
|
||||
"This matches \"ethtool --module-info <netdev> raw on\"",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "seconds",
|
||||
.desc = "Interval in seconds at which to collect monitoring data "
|
||||
"in the absence of link state changes. A value larger than "
|
||||
"24 hours (86400 seconds) is clamped down to 24 hours.",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static void parse_cmdline(int argc, char *argv[], struct options *opts)
|
||||
{
|
||||
uint32_t seconds;
|
||||
int cmd, ret;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
do {
|
||||
cmd = util_opt_getopt_long(argc, argv);
|
||||
|
||||
switch (cmd) {
|
||||
case 'm':
|
||||
opts->monitor = true;
|
||||
break;
|
||||
case 'r':
|
||||
opts->report = true;
|
||||
break;
|
||||
case 'q':
|
||||
opts->quiet = true;
|
||||
break;
|
||||
case OPT_DUMP:
|
||||
opts->module_info = true;
|
||||
break;
|
||||
case 'i':
|
||||
ret = sscanf(optarg, "%u", &seconds);
|
||||
if (ret != 1) {
|
||||
fprintf(stderr,
|
||||
"Failed to parse interval argument \"%s\" as seconds\n",
|
||||
optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (seconds < 86400)
|
||||
opts->interval_seconds = seconds;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case -1:
|
||||
/* End of options string */
|
||||
break;
|
||||
}
|
||||
} while (cmd != -1);
|
||||
}
|
||||
|
||||
static int module_info_pair(struct optics *oi)
|
||||
{
|
||||
size_t b64_calclen, b64len;
|
||||
int rc = EXIT_SUCCESS;
|
||||
char *b64;
|
||||
|
||||
b64_calclen = (oi->size / 3) * 4;
|
||||
if (oi->size % 3 > 0)
|
||||
b64_calclen += 4;
|
||||
|
||||
b64 = util_zalloc(b64_calclen + 1); /* adds NUL byte */
|
||||
b64len = EVP_EncodeBlock((unsigned char *)b64, oi->raw, oi->size);
|
||||
if (b64len != b64_calclen) {
|
||||
fprintf(stderr, "encoding base64 via openssl failed\n");
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
util_fmt_pair(FMT_QUOTE, "module_info", b64);
|
||||
out:
|
||||
free(b64);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void optics_json_print(struct opticsmon_ctx *ctx, struct zpci_netdev *nd, struct optics *oi)
|
||||
{
|
||||
util_fmt_obj_start(FMT_DEFAULT, "netdev");
|
||||
util_fmt_pair(FMT_QUOTE, "name", nd->name);
|
||||
util_fmt_pair(FMT_QUOTE, "operstate", zpci_operstate_str(nd->operstate));
|
||||
util_fmt_obj_start(FMT_DEFAULT, "optics");
|
||||
util_fmt_pair(FMT_QUOTE, "type", optics_type_str(optics_type(oi)));
|
||||
util_fmt_pair(FMT_QUOTE, "rx_los", optics_los_str(optics_rx_los(oi)));
|
||||
util_fmt_pair(FMT_QUOTE, "tx_los", optics_los_str(optics_tx_los(oi)));
|
||||
util_fmt_pair(FMT_QUOTE, "tx_fault", optics_los_str(optics_rx_los(oi)));
|
||||
if (ctx->opts.module_info)
|
||||
module_info_pair(oi);
|
||||
util_fmt_obj_end();
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
|
||||
static int dump_adapter_data(struct opticsmon_ctx *ctx, struct zpci_dev *zdev)
|
||||
{
|
||||
struct optics **ois;
|
||||
int num_ois = 0;
|
||||
char *pci_addr;
|
||||
int i, rc;
|
||||
|
||||
ois = util_zalloc(sizeof(ois[0]) * zdev->num_netdevs);
|
||||
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
rc = ethtool_nl_get_optics(&ctx->ethtool_ctx, zdev->netdevs[i].name, &ois[i]);
|
||||
if (rc)
|
||||
goto free_ois;
|
||||
num_ois++;
|
||||
}
|
||||
if (!ctx->opts.quiet) {
|
||||
util_fmt_obj_start(FMT_DEFAULT, "adapter");
|
||||
util_fmt_pair(FMT_QUOTE, "pft", zpci_pft_str(zdev));
|
||||
util_fmt_obj_start(FMT_DEFAULT, "ids");
|
||||
util_fmt_pair(FMT_QUOTE, "fid", "0x%0x", zdev->fid);
|
||||
if (zdev->uid_is_unique)
|
||||
util_fmt_pair(FMT_QUOTE, "uid", "0x%0x", zdev->uid);
|
||||
pci_addr = zpci_pci_addr(zdev);
|
||||
util_fmt_pair(FMT_QUOTE, "pci_address", pci_addr);
|
||||
free(pci_addr);
|
||||
util_fmt_obj_end();
|
||||
util_fmt_obj_start(FMT_LIST, "netdevs");
|
||||
for (i = 0; i < zdev->num_netdevs; i++)
|
||||
optics_json_print(ctx, &zdev->netdevs[i], ois[i]);
|
||||
util_fmt_obj_end(); /* netdevs list */
|
||||
util_fmt_obj_end(); /* adapter */
|
||||
fflush(stdout);
|
||||
}
|
||||
if (ctx->opts.report) {
|
||||
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
rc = sclp_issue_optics_report(zdev, ois[i]);
|
||||
if (rc == -ENOTSUP) {
|
||||
fprintf(stderr, "Skipping %s which does not support reporting\n",
|
||||
zdev->netdevs[i].name);
|
||||
} else if (rc < 0) {
|
||||
fprintf(stderr, "Error issuing SCLP for optics data failed: %s\n",
|
||||
strerror(-rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
free_ois:
|
||||
for (i = 0; i < num_ois; i++)
|
||||
optics_free(ois[i]);
|
||||
free(ois);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void zpci_list_reload(struct util_list **zpci_list)
|
||||
{
|
||||
if (*zpci_list)
|
||||
zpci_free_dev_list(*zpci_list);
|
||||
*zpci_list = zpci_dev_list();
|
||||
}
|
||||
|
||||
static void dump_all_adapter_data(struct opticsmon_ctx *ctx)
|
||||
{
|
||||
struct zpci_dev *zdev;
|
||||
|
||||
zpci_list_reload(&ctx->zpci_list);
|
||||
util_list_iterate(ctx->zpci_list, zdev) {
|
||||
/* Filter non-NIC devices and VFs */
|
||||
if (zpci_is_vf(zdev) || !zdev->num_netdevs)
|
||||
continue;
|
||||
dump_adapter_data(ctx, zdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int oneshot_mode(struct opticsmon_ctx *ctx)
|
||||
{
|
||||
util_fmt_init(stdout, FMT_JSON, FMT_DEFAULT, API_LEVEL);
|
||||
if (!ctx->opts.quiet)
|
||||
util_fmt_obj_start(FMT_LIST, "adapters");
|
||||
dump_all_adapter_data(ctx);
|
||||
if (!ctx->opts.quiet)
|
||||
util_fmt_obj_end();
|
||||
util_fmt_exit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||
{
|
||||
struct opticsmon_ctx *ctx = arg;
|
||||
struct zpci_netdev *found_netdev;
|
||||
struct zpci_dev *zdev = NULL;
|
||||
int reloads = 1;
|
||||
|
||||
do {
|
||||
if (ctx->zpci_list) {
|
||||
zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
|
||||
if (zdev) {
|
||||
/* Skip data collection if operational state is
|
||||
* unchanged
|
||||
*/
|
||||
if (found_netdev->operstate == netdev->operstate)
|
||||
return;
|
||||
/* Update operation state for VFs even though
|
||||
* they are skipped just for a consistent view
|
||||
*/
|
||||
found_netdev->operstate = netdev->operstate;
|
||||
/* Only collect optics data for PFs */
|
||||
if (!zpci_is_vf(zdev))
|
||||
dump_adapter_data(ctx, zdev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Could be uninitalized list or a new device, retry after reload */
|
||||
zpci_list_reload(&ctx->zpci_list);
|
||||
reloads--;
|
||||
} while (reloads > 0);
|
||||
}
|
||||
|
||||
#define MAX_EVENTS 8
|
||||
|
||||
static int monitor_wait_loop(struct opticsmon_ctx *ctx, int sigfd, int timerfd)
|
||||
{
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
struct signalfd_siginfo fdsi;
|
||||
int i, nlfd, epfd, nfds;
|
||||
struct epoll_event ev;
|
||||
uint64_t expirations;
|
||||
ssize_t sread;
|
||||
|
||||
epfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = sigfd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &ev) == -1)
|
||||
return -EIO;
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = timerfd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev) == -1)
|
||||
return -EIO;
|
||||
|
||||
nlfd = link_mon_nl_waitfd_getfd(&ctx->lctx);
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = nlfd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, nlfd, &ev) == -1)
|
||||
return -EIO;
|
||||
|
||||
while (1) {
|
||||
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
|
||||
if (nfds < 0)
|
||||
return nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
/* signal fd */
|
||||
if (events[i].data.fd == sigfd) {
|
||||
sread = read(sigfd, &fdsi, sizeof(fdsi));
|
||||
if (sread != sizeof(fdsi))
|
||||
return -EIO;
|
||||
switch (fdsi.ssi_signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGQUIT:
|
||||
return 0;
|
||||
/* Unexpected signal */
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
/* timer fd */
|
||||
} else if (events[i].data.fd == timerfd) {
|
||||
sread = read(timerfd, &expirations, sizeof(uint64_t));
|
||||
if (sread != sizeof(uint64_t))
|
||||
return -EIO;
|
||||
if (!expirations)
|
||||
continue;
|
||||
dump_all_adapter_data(ctx);
|
||||
/* netlink fd */
|
||||
} else if (events[i].data.fd == nlfd) {
|
||||
link_mon_nl_waitfd_read(&ctx->lctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int monitor_mode(struct opticsmon_ctx *ctx)
|
||||
{
|
||||
struct itimerspec timerspec;
|
||||
int sigfd, timerfd, ret;
|
||||
sigset_t mask;
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGQUIT);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
return -EIO;
|
||||
|
||||
sigfd = signalfd(-1, &mask, 0);
|
||||
if (sigfd == -1) {
|
||||
fprintf(stderr, "Failed to create signalfd\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
if (timerfd == -1) {
|
||||
fprintf(stderr, "Failed to create timerfd\n");
|
||||
ret = -EIO;
|
||||
goto close_signalfd;
|
||||
}
|
||||
|
||||
/* Set initial expiration to 1 ns so we gather optics data at startup */
|
||||
timerspec.it_value.tv_sec = 0;
|
||||
timerspec.it_value.tv_nsec = 1;
|
||||
timerspec.it_interval.tv_sec = ctx->opts.interval_seconds;
|
||||
timerspec.it_interval.tv_nsec = 0;
|
||||
ret = timerfd_settime(timerfd, 0, &timerspec, NULL);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Failed to arm timer\n");
|
||||
goto close_timerfd;
|
||||
}
|
||||
|
||||
util_fmt_init(stdout, FMT_JSONSEQ, FMT_DEFAULT, API_LEVEL);
|
||||
ret = link_mon_nl_waitfd_create(&ctx->lctx, on_link_change, ctx);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Failed to create link monitoring socket\n");
|
||||
goto close_timerfd;
|
||||
}
|
||||
|
||||
monitor_wait_loop(ctx, sigfd, timerfd);
|
||||
|
||||
link_mon_nl_waitfd_destroy(&ctx->lctx);
|
||||
util_fmt_exit();
|
||||
close_signalfd:
|
||||
close(sigfd);
|
||||
close_timerfd:
|
||||
close(timerfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct opticsmon_ctx ctx = { .opts = { .interval_seconds = 86400 } };
|
||||
int ret;
|
||||
|
||||
parse_cmdline(argc, argv, &ctx.opts);
|
||||
ethtool_nl_connect(&ctx.ethtool_ctx);
|
||||
if (ctx.opts.monitor)
|
||||
ret = monitor_mode(&ctx);
|
||||
else
|
||||
ret = oneshot_mode(&ctx);
|
||||
ethtool_nl_close(&ctx.ethtool_ctx);
|
||||
|
||||
if (ctx.zpci_list)
|
||||
zpci_free_dev_list(ctx.zpci_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
848
rust/Cargo.lock
generated
848
rust/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"cpacfinfo",
|
||||
"pv",
|
||||
"pv_core",
|
||||
"pvapconfig",
|
||||
"pvattest",
|
||||
"pvimg",
|
||||
"pvsecret",
|
||||
"utils",
|
||||
]
|
||||
@@ -12,6 +14,15 @@ resolver = "2"
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.75.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
[workspace.lints.rust]
|
||||
missing_docs = { level = "deny", priority = 1 }
|
||||
missing_debug_implementations = "warn"
|
||||
non_ascii_idents = "warn"
|
||||
nonstandard-style = "warn"
|
||||
trivial_numeric_casts = "warn"
|
||||
unstable_features = "warn"
|
||||
unused = "warn"
|
||||
unused_import_braces = "warn"
|
||||
unused_qualifications = "warn"
|
||||
|
||||
@@ -17,9 +17,15 @@ ifneq (${HAVE_CARGO},0)
|
||||
INSTALL_TARGETS := install-rust-tools install-man
|
||||
CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS))
|
||||
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
CARGO_TARGETS += cpacfinfo
|
||||
else
|
||||
BUILD_TARGETS += skip-cpacfinfo
|
||||
endif #HOSTARCH
|
||||
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_LIBCURL},0)
|
||||
PV_TARGETS := pvsecret pvattest
|
||||
PV_TARGETS := pvsecret pvattest pvimg
|
||||
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
PV_TARGETS += pvapconfig
|
||||
@@ -34,7 +40,14 @@ endif #OPENSSL
|
||||
TEST_TARGETS := $(addsuffix _build,$(CARGO_TEST_TARGETS))
|
||||
endif #CARGO
|
||||
|
||||
BUILD_TARGETS += $(PV_BUILD_TARGETS)
|
||||
pvimg-bootloaders:
|
||||
$(MAKE) -C pvimg/boot/
|
||||
.PHONY: pvimg-bootloaders
|
||||
|
||||
PVIMG_PKGDATADIR := $(TOOLS_DATADIR)/pvimg
|
||||
export PVIMG_PKGDATADIR
|
||||
BUILD_TARGETS += $(PV_BUILD_TARGETS) pvimg-bootloaders
|
||||
INSTALL_TARGETS += pvimg-bootloaders
|
||||
|
||||
# build release targets by default
|
||||
ifeq ("${D}","0")
|
||||
@@ -65,10 +78,17 @@ skip-pv-build:
|
||||
skip-pvapconfig:
|
||||
echo " SKIP pvapconfig due to unsupported architecture (s390x only)"
|
||||
|
||||
skip-cpacfinfo:
|
||||
echo " SKIP cpacfinfo due to unsupported architecture (s390x only)"
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
install: $(INSTALL_TARGETS)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr "$(DESTDIR)$(USRBINDIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr $(DESTDIR)$(USRBINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvimg/tools/check_hostkeydoc $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||
$(MAKE) -C pvimg/boot install
|
||||
ln -sf pvimg $(DESTDIR)$(USRBINDIR)/genprotimg
|
||||
|
||||
print-rust-targets:
|
||||
echo $(BUILD_TARGETS)
|
||||
@@ -77,6 +97,7 @@ clean:
|
||||
ifneq (${HAVE_CARGO},0)
|
||||
$(CARGO_CLEAN) ${ALL_CARGOFLAGS}
|
||||
endif # CARGO
|
||||
$(MAKE) -C pvimg/boot/ clean
|
||||
$(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo
|
||||
|
||||
rust-test: $(CARGO_TEST_TARGETS)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
Please refer to the official documentation to set up a working rust environment:
|
||||
https://www.rust-lang.org/learn/get-started
|
||||
|
||||
The minimum supported Rust version (MSRV) is 1.75.
|
||||
|
||||
## Building rust code
|
||||
### s390-tools build system
|
||||
If `cargo` is installed a simple `make` should do the job. Note that,
|
||||
@@ -35,10 +37,6 @@ Tip: You can use `make version` to get the version string.
|
||||
* reexports ann symbols from __pv_core__
|
||||
* if no encryption utilities required, use __pv_core__
|
||||
|
||||
## Tools
|
||||
* __pvsecret__ _Manage secrets for IBM Secure Execution guests_
|
||||
* __pvapconfig__ _automatic configure APQNs within an SE KVM guest_
|
||||
|
||||
## Writing new tools
|
||||
We encourage to use Rust for new tools. However, for some use cases it makes
|
||||
sense to use C and C is still allowed to be used for a new tool/library.
|
||||
@@ -56,6 +54,8 @@ is a start, but can change over time.
|
||||
|
||||
* [anyhow](https://crates.io/crates/anyhow)
|
||||
* Flexible concrete Error type built on std::error::Error
|
||||
* [base64](https://crates.io/crates/base64)
|
||||
* Encodes and decodes base64 as bytes or utf8
|
||||
* [byteorder](https://crates.io/crates/byteorder)
|
||||
* Library for reading/writing numbers in big-endian and little-endian.
|
||||
* [cfg-if](https://crates.io/crates/cfg-if)
|
||||
@@ -66,6 +66,8 @@ is a start, but can change over time.
|
||||
* A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
* [curl](https://crates.io/crates/curl)
|
||||
* Rust bindings to libcurl for making HTTP requests
|
||||
* [deku](https://crates.io/crates/deku)
|
||||
* Bit level serialization/deserialization proc-macro for structs
|
||||
* [libc](https://crates.io/crates/libc)
|
||||
* Raw FFI bindings to platform libraries like libc.
|
||||
* [log](https://crates.io/crates/log)
|
||||
@@ -74,6 +76,8 @@ is a start, but can change over time.
|
||||
* OpenSSL bindings
|
||||
* [serde](https://crates.io/crates/serde)
|
||||
* A generic serialization/deserialization framework
|
||||
* [serde_jsonl](https://crates.io/crates/serde_json)
|
||||
* A JSON serialization file format
|
||||
* [serde_yaml](https://crates.io/crates/serde_yaml)
|
||||
* YAML data format for Serde
|
||||
* [thiserror](https://crates.io/crates/thiserror)
|
||||
|
||||
20
rust/cpacfinfo/Cargo.toml
Normal file
20
rust/cpacfinfo/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "cpacfinfo"
|
||||
version = "0.11.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.95"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
libc = "0.2"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
utils = { path = "../utils" }
|
||||
zerocopy = { version="0.7", features = ["derive"] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.2"
|
||||
clap = { version ="4.5", features = ["derive"]}
|
||||
lazy_static = "1.5"
|
||||
14
rust/cpacfinfo/build.rs
Normal file
14
rust/cpacfinfo/build.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_arch = "s390x")]
|
||||
cc::Build::new().file("src/stfle.c").compile("stfle");
|
||||
#[cfg(not(target_arch = "s390x"))]
|
||||
{
|
||||
println!("cargo:warning=cpacfinfo will have no functionality on non s390x architectures!");
|
||||
cc::Build::new().file("src/noop.c").compile("stfle");
|
||||
}
|
||||
println!("cargo:rerun-if-changed=src/stfle.c")
|
||||
}
|
||||
215
rust/cpacfinfo/man/cpacfinfo.1
Normal file
215
rust/cpacfinfo/man/cpacfinfo.1
Normal file
@@ -0,0 +1,215 @@
|
||||
.\" Copyright 2024, 2024 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 CPACFINFO 1 "AUG 2024" "s390-tools"
|
||||
.SH NAME
|
||||
.B cpacfinfo
|
||||
\- tool to provide information about CPACF
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B cpacfinfo
|
||||
[OPTIONS] [<INS>,...] [--format <FORMAT>]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B cpacfinfo
|
||||
is able to provide information about the MSA levels supported by the system as well as which
|
||||
CPACF instructions and functions are available.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.B \-m/\-\-msa
|
||||
Displays which MSA levels are enabled and how many functions of the ones introduced by
|
||||
this level are available. Can be combined with
|
||||
.B \-f/\-\-functions
|
||||
to list all functions under the corresponding MSA level. Can be combined with
|
||||
.B \-i/\-\-instructions
|
||||
to provide only specific instructions. Can be combined with
|
||||
.B \-a/\-\-available -n/\-\-not-available
|
||||
to filter the displayed function. Ignores option
|
||||
.B \-q/\-\-quiet.
|
||||
Can be combined with
|
||||
.B \-\-format
|
||||
to convert to JSON format.
|
||||
|
||||
.TP 8
|
||||
.B \-f/\-\-functions
|
||||
Displays subfunctions of CPACF instructions. Can be combined with
|
||||
.B \-a/\-\-available
|
||||
and
|
||||
.B \-n/\-\-not-available
|
||||
to filter what functions are displayed. By default
|
||||
.B \-f/\-\-functions
|
||||
will only provide information about available functions. Therefore
|
||||
.B cpacfinfo \-f/\-\-functions
|
||||
and
|
||||
.B cpacfinfo \-f/\-\-functions \-\-available
|
||||
show the same output. Use options
|
||||
.B \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available
|
||||
together to show information about every function known by the tool. Additionally
|
||||
.B cpacfinfo
|
||||
shows available functions not known to the tool as "UNKNOWN".
|
||||
|
||||
.TP 8
|
||||
.B \-i/\-\-instructions <INS>,...
|
||||
Restricts output of CPACF instructions to be listed.
|
||||
Multiple instructions can be supplied separated by "," to only show the supplied
|
||||
instructions in the output. Other options like
|
||||
.B \-\-quiet \-f/\-\-functions \-m/\-\-msa
|
||||
or filters like
|
||||
.B \-a/\-\-available \-n/\-\-not-available
|
||||
can still be used to further limit output.
|
||||
|
||||
.nr PI 2n
|
||||
Possible values for
|
||||
.B <INS>
|
||||
:
|
||||
.RS
|
||||
.IP \[bu] 2
|
||||
.B km
|
||||
: introduced with MSA
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kmc
|
||||
: introduced with MSA
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kimd
|
||||
: introduced with MSA
|
||||
|
||||
.IP \[bu] 2
|
||||
.B klmd
|
||||
: introduced with MSA
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kmac
|
||||
: introduced with MSA
|
||||
|
||||
.IP \[bu] 2
|
||||
.B pckmo
|
||||
: introduced with MSA 3
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kmf
|
||||
: introduced with MSA 4
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kmctr
|
||||
: introduced with MSA 4
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kmo
|
||||
: introduced with MSA 4
|
||||
|
||||
.IP \[bu] 2
|
||||
.B pcc
|
||||
: introduced with MSA 4
|
||||
|
||||
.IP \[bu] 2
|
||||
.B prno
|
||||
: introduced with MSA 5
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kma
|
||||
: introduced with MSA 8
|
||||
|
||||
.IP \[bu] 2
|
||||
.B kdsa
|
||||
: introduced with MSA 9
|
||||
.RE
|
||||
|
||||
.TP 8
|
||||
.B \-a/\-\-available
|
||||
Displays the CPACF subfunctions available on the system.
|
||||
.B cpacfinfo \-f/\-\-functions
|
||||
is the same as
|
||||
.B cpacfinfo \-f/\-\-functions \-a/\-\-available
|
||||
. If
|
||||
.B -n/\-\-not-available
|
||||
is specified no available functions are shown. To get available as well as functions not available use
|
||||
.B cpacfinfo \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available.
|
||||
|
||||
.TP 8
|
||||
.B \-n/\-\-not-available
|
||||
Displays the CPACF subfunctions known by the tool that are not-available on the system.
|
||||
Depending on the Hardware not all MSA levels might be available which can be checked
|
||||
with the
|
||||
.B \-m/\-\-msa
|
||||
option. In such cases it might not be obvious which functions of which instructions are available.
|
||||
This option together with the
|
||||
.B \-a/\-\-available
|
||||
option shows all possible functions.
|
||||
|
||||
.TP 8
|
||||
.B \-q/\-\-quiet
|
||||
Suppresses the output of the Query Authentication Information.
|
||||
By default
|
||||
.B cpacfinfo
|
||||
outputs the Query Authentication Information for every
|
||||
Instruction. To keep outputs of other options clean and minimal this can be disabled
|
||||
with this option.
|
||||
On machines that do not provide /sys/devices/system/cpu/cpacf/ output will always look like this
|
||||
option is supplied.
|
||||
|
||||
.TP 8
|
||||
.B \-\-format <FORMAT>
|
||||
Format output in the specified format. [default: 'human']
|
||||
|
||||
If option
|
||||
.B \-m/\-\-msa
|
||||
is specified the JSON output will equal to
|
||||
.B cpacfinfo -m
|
||||
without any other specified options. If
|
||||
.B \-m/\-\-msa
|
||||
is NOT specified the JSON output will equal to
|
||||
.B cpacfinfo -f
|
||||
without any other specified options.
|
||||
|
||||
.nr PI 2n
|
||||
Possible values for
|
||||
.B <FORMAT>
|
||||
:
|
||||
.RS
|
||||
.IP \[bu] 2
|
||||
.B human
|
||||
: Use human readable format.
|
||||
|
||||
.IP \[bu] 2
|
||||
.B json
|
||||
: Use JSON format.
|
||||
.RE
|
||||
|
||||
.TP 8
|
||||
.B \-v/\-\-version
|
||||
Print version information and exit.
|
||||
|
||||
.TP 8
|
||||
.B \-h/\-\-help
|
||||
Print help (see a summary with \-h')
|
||||
|
||||
.SH Query Authentication Information
|
||||
The Query Authentication Information is available since MSA 13 and if it is available
|
||||
.B cpacfinfo
|
||||
will display it by default. Query Authentication Information is available for every CPACF
|
||||
instruction and contains the following:
|
||||
.nr PI 2n
|
||||
.RS
|
||||
.IP \[bu] 2
|
||||
.B Format
|
||||
: This format specifies how the binary blob read from sysfs is to be parsed and says nothing
|
||||
about the Query Authentication Information itself.
|
||||
|
||||
.IP \[bu] 2
|
||||
.B Hash length
|
||||
: The hash length specifies the length of the hash in bytes.
|
||||
|
||||
.IP \[bu] 2
|
||||
.B IFCL version
|
||||
: The IFCL version specifies the instruction's firmware code level.
|
||||
|
||||
.IP \[bu] 2
|
||||
.B Hash
|
||||
: The hash of the firmware code level of the corresponding instruction.
|
||||
.RE
|
||||
|
||||
75
rust/cpacfinfo/src/cli.rs
Normal file
75
rust/cpacfinfo/src/cli.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use crate::msa::InstructionKind;
|
||||
|
||||
#[derive(Clone, PartialEq, clap::ValueEnum, Default)]
|
||||
pub enum Format {
|
||||
/// Human-focused, non-parsable output format
|
||||
#[default]
|
||||
Human,
|
||||
|
||||
/// Use JSON format
|
||||
Json,
|
||||
}
|
||||
|
||||
/// Command line interface to get information about CP Assist for Cryptographic Functions (CPACF)
|
||||
#[derive(Parser)]
|
||||
pub struct Cli {
|
||||
/// Print version information and exit
|
||||
#[arg(short, long, exclusive(true))]
|
||||
pub version: bool,
|
||||
|
||||
/// Provide information about the Message Security Assist (MSA)
|
||||
///
|
||||
/// Shows which MSA levels are available and how many functions of the ones introduced by
|
||||
/// this level are available.
|
||||
/// Compatible with option -f/--functions to list all functions under the corresponding MSA
|
||||
/// level.
|
||||
#[arg(short, long, conflicts_with("quiet"))]
|
||||
pub msa: bool,
|
||||
|
||||
/// Shows available functions sorted by instructions
|
||||
///
|
||||
/// Provides information about the subfunctions of an instruction.
|
||||
/// Functions not known to cpacfinfo are displayed as "UNKNOWN".
|
||||
#[arg(short, long)]
|
||||
pub functions: bool,
|
||||
|
||||
/// Filter instructions to provide in output
|
||||
///
|
||||
/// Multiple instructions can be supplied separated by "," to only show the supllied
|
||||
/// instructions in the output.
|
||||
#[arg(short, long, num_args = 1.., value_delimiter = ',')]
|
||||
pub instructions: Vec<InstructionKind>,
|
||||
|
||||
/// Shows available functions
|
||||
///
|
||||
/// Adds available functions to subfunction output.
|
||||
#[arg(short, long)]
|
||||
pub available: bool,
|
||||
|
||||
/// Shows functions that are not available
|
||||
///
|
||||
/// Adds non-available functions to subfunction output.
|
||||
#[arg(short, long = "not-available")]
|
||||
pub not_available: bool,
|
||||
|
||||
/// Suppresses the Query Authentication Information output of Instructions
|
||||
///
|
||||
/// By default cpacfinfo outputs the Query Authentication Information for every Instruction.
|
||||
/// To keep outputs of other options clean and minimal this can be disabled with this
|
||||
/// option.
|
||||
#[arg(short, long)]
|
||||
pub quiet: bool,
|
||||
|
||||
/// Converts human readable output to JSON format
|
||||
///
|
||||
/// Default is human to produce human readable output. When set to json will produce json
|
||||
/// output.
|
||||
#[arg(long, value_enum, default_value_t)]
|
||||
pub format: Format,
|
||||
}
|
||||
254
rust/cpacfinfo/src/main.rs
Normal file
254
rust/cpacfinfo/src/main.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
mod cli;
|
||||
mod msa;
|
||||
mod query;
|
||||
mod stfle;
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::Result as anyhowRes;
|
||||
use clap::Parser;
|
||||
use std::io::ErrorKind;
|
||||
use std::result::Result::Ok;
|
||||
use utils::print_version;
|
||||
|
||||
use crate::cli::{Cli, Format};
|
||||
use crate::msa::*;
|
||||
use crate::query::*;
|
||||
use crate::stfle::*;
|
||||
|
||||
/// producing -m/--msa output
|
||||
fn out_msa(args: &Cli, levels: &Vec<MsaLevel>, instructions: &Vec<Instruction>) {
|
||||
// produce json output
|
||||
if args.format == Format::Json {
|
||||
println!("{}", serde_json::to_string(levels).unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
// produce human readable output
|
||||
for lvl in levels {
|
||||
// print current level
|
||||
println!("{lvl}");
|
||||
|
||||
// if -f/--functions is not specified continue to next level
|
||||
if !args.functions {
|
||||
continue;
|
||||
}
|
||||
|
||||
// print all functions introduced by the current level sorted by instruction
|
||||
for ins in instructions {
|
||||
// skip instructions for which the current level introduces no new functions
|
||||
if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty()
|
||||
|| !ins.info.available
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter all functions that do not fit the command line arguments
|
||||
let funcs_to_be_printed = ins
|
||||
.funcs
|
||||
.iter()
|
||||
.filter(|func| {
|
||||
!(!func.available && !args.not_available
|
||||
|| func.available && !args.available && args.not_available)
|
||||
})
|
||||
.filter(|func| func.msa == lvl.msa_level);
|
||||
|
||||
// print all functions matching the command line arguments
|
||||
let mut ins_printed = false;
|
||||
for func in funcs_to_be_printed {
|
||||
if !ins_printed {
|
||||
println!("\t{ins}");
|
||||
ins_printed = true;
|
||||
}
|
||||
println!("\t\t{func}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// produces output for all cpacfinfo commands that do not contain the -m/--msa flag
|
||||
fn out_instructions(args: &Cli, instructions: &Vec<Instruction>) {
|
||||
// produce json output
|
||||
if args.format == Format::Json {
|
||||
println!("{}", serde_json::to_string(instructions).unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
// produce human readable output
|
||||
for ins in instructions {
|
||||
if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty()
|
||||
|| !ins.info.available
|
||||
{
|
||||
continue;
|
||||
}
|
||||
println!("{ins}");
|
||||
|
||||
// --no-auth-info/-n suppresses the Authentication Information output
|
||||
if !args.quiet && ins.info.qai_available {
|
||||
println!("{}", ins.info.qai);
|
||||
} else if !args.quiet {
|
||||
println!("Query Authentication Information not available for {ins} instruction! (potentially insufficient machine level)");
|
||||
}
|
||||
|
||||
// --functions/-f lists functions of instructions
|
||||
if args.functions {
|
||||
ins.funcs
|
||||
.iter()
|
||||
.filter(|func| {
|
||||
!(!func.available && !args.not_available
|
||||
|| func.available && !args.available && args.not_available)
|
||||
})
|
||||
.for_each(|func| println!("\t{func}"));
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> anyhowRes<()> {
|
||||
/* ---- PARSE COMMAND LINE ARGUMENTS ---- */
|
||||
let args: Cli = Cli::parse();
|
||||
|
||||
/* ---- PRINT VERSION STRING ---- */
|
||||
if args.version {
|
||||
print_version!("2024");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/* ---- SET CONSTANTS ---- */
|
||||
let mut instructions = Vec::new();
|
||||
init_instructions(&mut instructions);
|
||||
|
||||
let mut levels = Vec::new();
|
||||
for lvl in 0..MSA_LEVEL_COUNT {
|
||||
let temp = match num2msa(lvl) {
|
||||
Some(l) => l,
|
||||
None => panic!("programming error"),
|
||||
};
|
||||
let stfle_bit = msa2stfle(&temp);
|
||||
levels.push(MsaLevel::new(temp, stfle_bit));
|
||||
let idx_of_last_element = levels.len() - 1;
|
||||
update_msa_function_count(&args, &mut levels[idx_of_last_element], &instructions);
|
||||
}
|
||||
|
||||
/* ---- GET INFORMATION ---- */
|
||||
// get stfle bits
|
||||
let stfle_bits = Stfle::new()?;
|
||||
|
||||
// check stfle bits for available MSA levels
|
||||
for lvl in &mut levels {
|
||||
match lvl.stfle_bit {
|
||||
Some(bit) => lvl.enabled = stfle_bits.check_bit_in_stfle(bit),
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
|
||||
// check if SYSFS_PATH is available
|
||||
match check_sysfs() {
|
||||
true => (),
|
||||
false => return Ok(()),
|
||||
}
|
||||
|
||||
// run query function (fc 0) for every instruction to check available functions
|
||||
for ins in &mut instructions {
|
||||
if stfle_bits.check_bit_in_stfle(ins.info.stfle_bit) {
|
||||
ins.info.available = true;
|
||||
|
||||
// run query; save result in param
|
||||
let mut param = match query(&ins.kind, QUERY_FUNCTION_CODE) {
|
||||
Ok(pb) => match pb {
|
||||
Param::QueryParam(_) => pb,
|
||||
Param::QaiParam(_) => panic!("programming error"),
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
println!("Warning: Not able to retrieve subfunction information from sysfs for {ins} instruction");
|
||||
continue;
|
||||
}
|
||||
_ => return Err(e.into()),
|
||||
},
|
||||
};
|
||||
|
||||
// check if bit for functions of current instruction is set in param
|
||||
for func in &mut ins.funcs {
|
||||
if !param.check_bit_in_param(func.function_code as usize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
func.available = true;
|
||||
|
||||
// unset the bit in param to later see if any unsupported functions may be available
|
||||
param.unset_bit_in_param(func.function_code);
|
||||
|
||||
// check if qai is available
|
||||
if func.function_code == QAI_FUNCTION_CODE {
|
||||
ins.info.qai_available = true;
|
||||
}
|
||||
|
||||
// sync MsaLevel struct
|
||||
for lvl in &mut levels {
|
||||
if lvl.msa_level == func.msa {
|
||||
lvl.enabled = true;
|
||||
lvl.available_functions += 1;
|
||||
if args.instructions.is_empty() || args.instructions.contains(&ins.kind) {
|
||||
lvl.dynamic_available_functions += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look for any unsupported functions that my be available
|
||||
for i in 0..NUMBER_FUNC_CODES {
|
||||
// every bit in param that is 1 is an unsupported function
|
||||
if !param.check_bit_in_param(i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add function to instruction as UNKNOWN
|
||||
Instruction::add(ins, Function::new(i as u8, Msa::UNKNOWN, "UNKNOWN"));
|
||||
|
||||
// set function as available
|
||||
match ins.funcs.last_mut() {
|
||||
Some(ret) => ret.available = true,
|
||||
None => panic!("programming error"),
|
||||
}
|
||||
}
|
||||
|
||||
// if query authentication information (fc 127) available run query authentication
|
||||
// information
|
||||
if ins.info.qai_available {
|
||||
// get qai from sysfs
|
||||
let param = match query(&ins.kind, QAI_FUNCTION_CODE) {
|
||||
Ok(pb) => match pb {
|
||||
Param::QueryParam(_) => panic!("programming error"),
|
||||
Param::QaiParam(_) => pb,
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
println!("Warning: Not able to retrieve Query Authentication Information from sysfs for {ins} instruction");
|
||||
continue;
|
||||
}
|
||||
_ => return Err(e.into()),
|
||||
},
|
||||
};
|
||||
|
||||
// parse qai information into QueryAuthInfo struct
|
||||
match param.parse_qai_based_on_format(&mut ins.info.qai) {
|
||||
Ok(true) => (),
|
||||
Ok(false) => println!("WARNING: format {} in query authentication information of instruction {} is UNKNOWN", ins.info.qai.format, ins.kind),
|
||||
Err(e) => bail!(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- OUTPUT ---- */
|
||||
match args.msa {
|
||||
true => out_msa(&args, &levels, &instructions),
|
||||
false => out_instructions(&args, &instructions),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
575
rust/cpacfinfo/src/msa.rs
Normal file
575
rust/cpacfinfo/src/msa.rs
Normal file
@@ -0,0 +1,575 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use crate::cli::Cli;
|
||||
use core::fmt::{Display, Formatter, Result};
|
||||
use serde::{Serialize, Serializer};
|
||||
use utils::HexSlice;
|
||||
|
||||
/// Number of total function codes (0 to 127)
|
||||
pub const NUMBER_FUNC_CODES: usize = 128;
|
||||
|
||||
/// Number of MSA levels starting with MSA (0) - MSA 13
|
||||
pub const MSA_LEVEL_COUNT: u8 = 14;
|
||||
|
||||
/// enum of all supported instructions
|
||||
#[derive(PartialEq, Clone, clap::ValueEnum, Serialize)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum InstructionKind {
|
||||
/// introduced with MSA
|
||||
KM,
|
||||
/// introduced with MSA
|
||||
KMC,
|
||||
/// introduced with MSA
|
||||
KIMD,
|
||||
/// introduced with MSA
|
||||
KLMD,
|
||||
/// introduced with MSA
|
||||
KMAC,
|
||||
/// introduced with MSA 3
|
||||
PCKMO,
|
||||
/// introduced with MSA 4
|
||||
KMF,
|
||||
/// introduced with MSA 4
|
||||
KMCTR,
|
||||
/// introduced with MSA 4
|
||||
KMO,
|
||||
/// introduced with MSA 4
|
||||
PCC,
|
||||
/// introduced with MSA 5
|
||||
PRNO,
|
||||
/// introduced with MSA 8
|
||||
KMA,
|
||||
/// introduced with MSA 9
|
||||
KDSA,
|
||||
}
|
||||
|
||||
/// enum of all MSA levels
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum Msa {
|
||||
MSA,
|
||||
MSA1,
|
||||
MSA2,
|
||||
MSA3,
|
||||
MSA4,
|
||||
MSA5,
|
||||
MSA6,
|
||||
MSA7,
|
||||
MSA8,
|
||||
MSA9,
|
||||
MSA10,
|
||||
MSA11,
|
||||
MSA12,
|
||||
MSA13,
|
||||
#[default]
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl Serialize for Msa {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
Self::MSA => serializer.serialize_unit_variant("Msa", 0, "0"),
|
||||
Self::MSA1 => serializer.serialize_unit_variant("Msa", 1, "1"),
|
||||
Self::MSA2 => serializer.serialize_unit_variant("Msa", 2, "2"),
|
||||
Self::MSA3 => serializer.serialize_unit_variant("Msa", 3, "3"),
|
||||
Self::MSA4 => serializer.serialize_unit_variant("Msa", 4, "4"),
|
||||
Self::MSA5 => serializer.serialize_unit_variant("Msa", 5, "5"),
|
||||
Self::MSA6 => serializer.serialize_unit_variant("Msa", 6, "6"),
|
||||
Self::MSA7 => serializer.serialize_unit_variant("Msa", 7, "7"),
|
||||
Self::MSA8 => serializer.serialize_unit_variant("Msa", 8, "8"),
|
||||
Self::MSA9 => serializer.serialize_unit_variant("Msa", 9, "9"),
|
||||
Self::MSA10 => serializer.serialize_unit_variant("Msa", 10, "10"),
|
||||
Self::MSA11 => serializer.serialize_unit_variant("Msa", 11, "11"),
|
||||
Self::MSA12 => serializer.serialize_unit_variant("Msa", 12, "12"),
|
||||
Self::MSA13 => serializer.serialize_unit_variant("Msa", 13, "13"),
|
||||
Self::UNKNOWN => serializer.serialize_unit_variant("Msa", 14, "UNKNOWN"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Msa {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match *self {
|
||||
Self::MSA => write!(f, "MSA "),
|
||||
Self::MSA1 => write!(f, "MSA 1"),
|
||||
Self::MSA2 => write!(f, "MSA 2"),
|
||||
Self::MSA3 => write!(f, "MSA 3"),
|
||||
Self::MSA4 => write!(f, "MSA 4"),
|
||||
Self::MSA5 => write!(f, "MSA 5"),
|
||||
Self::MSA6 => write!(f, "MSA 6"),
|
||||
Self::MSA7 => write!(f, "MSA 7"),
|
||||
Self::MSA8 => write!(f, "MSA 8"),
|
||||
Self::MSA9 => write!(f, "MSA 9"),
|
||||
Self::MSA10 => write!(f, "MSA 10"),
|
||||
Self::MSA11 => write!(f, "MSA 11"),
|
||||
Self::MSA12 => write!(f, "MSA 12"),
|
||||
Self::MSA13 => write!(f, "MSA 13"),
|
||||
Self::UNKNOWN => write!(f, "UNKNOWN"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// converts Instruction enum to a string representation
|
||||
impl Display for InstructionKind {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match *self {
|
||||
Self::KM => write!(f, "KM"),
|
||||
Self::KMC => write!(f, "KMC"),
|
||||
Self::KIMD => write!(f, "KIMD"),
|
||||
Self::KLMD => write!(f, "KLMD"),
|
||||
Self::KMAC => write!(f, "KMAC"),
|
||||
Self::PCKMO => write!(f, "PCKMO"),
|
||||
Self::KMF => write!(f, "KMF"),
|
||||
Self::KMCTR => write!(f, "KMCTR"),
|
||||
Self::KMO => write!(f, "KMO"),
|
||||
Self::PCC => write!(f, "PCC"),
|
||||
Self::PRNO => write!(f, "PRNO"),
|
||||
Self::KMA => write!(f, "KMA"),
|
||||
Self::KDSA => write!(f, "KDSA"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Default)]
|
||||
pub struct MsaLevel {
|
||||
pub msa_level: Msa,
|
||||
total_functions: u8,
|
||||
pub available_functions: u8,
|
||||
#[serde(skip)]
|
||||
dynamic_total_functions: u8,
|
||||
#[serde(skip)]
|
||||
pub dynamic_available_functions: u8,
|
||||
pub stfle_bit: Option<u8>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl MsaLevel {
|
||||
pub fn new(msa_level: Msa, stfle_bit: Option<u8>) -> Self {
|
||||
Self {
|
||||
msa_level,
|
||||
stfle_bit,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MsaLevel {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "{} ", self.msa_level)?;
|
||||
match self.stfle_bit {
|
||||
Some(bit) => write!(f, "STFLE bit [ {:>3} ] : ", bit)?,
|
||||
None => write!(f, " : ")?,
|
||||
}
|
||||
match self.enabled {
|
||||
true => write!(f, " AVAILABLE")?,
|
||||
false => write!(f, "NOT AVAILABLE")?,
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
" ( {:>2} / {:<2} functions available )",
|
||||
self.dynamic_available_functions, self.dynamic_total_functions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Default)]
|
||||
pub struct Function {
|
||||
name: String,
|
||||
pub function_code: u8,
|
||||
pub available: bool,
|
||||
#[serde(skip)]
|
||||
pub msa: Msa,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(fc: u8, msa: Msa, name: &str) -> Self {
|
||||
Self {
|
||||
function_code: fc,
|
||||
name: name.to_string(),
|
||||
msa,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "({:3}) ", self.function_code)?;
|
||||
match self.available {
|
||||
true => write!(f, "[ AVAILABLE]")?,
|
||||
false => write!(f, "[NOT AVAILABLE]")?,
|
||||
}
|
||||
write!(f, " {}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Default)]
|
||||
pub struct QueryAuthInfo {
|
||||
pub format: u8,
|
||||
pub hash_len: u16,
|
||||
pub version: u32,
|
||||
// #[serde(with = "hex::serde")]
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Display for QueryAuthInfo {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, " Format: {}", self.format)?;
|
||||
if self.format != 0 {
|
||||
writeln!(f, " (unknown format)")?;
|
||||
return Ok(());
|
||||
}
|
||||
write!(f, "; Hash length: {}", self.hash_len)?;
|
||||
writeln!(f, "; IFCL version: {}", self.version)?;
|
||||
writeln!(f, " Hash:")?;
|
||||
|
||||
for chunk in self.hash.chunks(16) {
|
||||
writeln!(f, " {:-}", HexSlice::from(chunk))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Default)]
|
||||
pub struct InstructionInfo {
|
||||
pub name: String,
|
||||
pub available: bool,
|
||||
pub stfle_bit: u8,
|
||||
#[serde(skip)]
|
||||
pub qai_available: bool,
|
||||
pub qai: QueryAuthInfo,
|
||||
}
|
||||
|
||||
impl InstructionInfo {
|
||||
fn new(stfle_bit: u8, name: &str) -> Self {
|
||||
Self {
|
||||
stfle_bit,
|
||||
name: name.to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Instruction {
|
||||
pub kind: InstructionKind,
|
||||
pub info: InstructionInfo,
|
||||
pub funcs: Vec<Function>,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn new(instruction: InstructionKind, stfle_bit: u8, name: &str) -> Self {
|
||||
Self {
|
||||
kind: instruction,
|
||||
info: InstructionInfo::new(stfle_bit, name),
|
||||
funcs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, func: Function) {
|
||||
self.funcs.push(func);
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Instruction {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "{} ({})", self.info.name, self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
/// returns stfle bit based on given MSA level
|
||||
pub fn msa2stfle(msa_level: &Msa) -> Option<u8> {
|
||||
match msa_level {
|
||||
Msa::MSA => Some(17),
|
||||
Msa::MSA3 => Some(76),
|
||||
Msa::MSA4 => Some(77),
|
||||
Msa::MSA5 => Some(57),
|
||||
Msa::MSA8 => Some(146),
|
||||
Msa::MSA9 => Some(155),
|
||||
Msa::MSA12 => Some(86),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// returns MSA level based on given u8
|
||||
pub fn num2msa(num: u8) -> Option<Msa> {
|
||||
match num {
|
||||
0 => Some(Msa::MSA),
|
||||
1 => Some(Msa::MSA1),
|
||||
2 => Some(Msa::MSA2),
|
||||
3 => Some(Msa::MSA3),
|
||||
4 => Some(Msa::MSA4),
|
||||
5 => Some(Msa::MSA5),
|
||||
6 => Some(Msa::MSA6),
|
||||
7 => Some(Msa::MSA7),
|
||||
8 => Some(Msa::MSA8),
|
||||
9 => Some(Msa::MSA9),
|
||||
10 => Some(Msa::MSA10),
|
||||
11 => Some(Msa::MSA11),
|
||||
12 => Some(Msa::MSA12),
|
||||
13 => Some(Msa::MSA13),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes all functions known by cpacfinfo
|
||||
#[rustfmt::skip]
|
||||
pub fn init_instructions(instructions: &mut Vec<Instruction>) {
|
||||
let mut km = Instruction::new(InstructionKind::KM, 17, "Cipher Message");
|
||||
km.add(Function::new(0, Msa::MSA, "KM-Query"));
|
||||
km.add(Function::new(1, Msa::MSA, "KM-DEA"));
|
||||
km.add(Function::new(2, Msa::MSA, "KM-TDEA-128"));
|
||||
km.add(Function::new(3, Msa::MSA, "KM-TDEA-192"));
|
||||
km.add(Function::new(9, Msa::MSA3, "KM-Encrypted-DEA"));
|
||||
km.add(Function::new(10, Msa::MSA3, "KM-Encrypted-TDEA-128"));
|
||||
km.add(Function::new(11, Msa::MSA3, "KM-Encrypted-TDEA-192"));
|
||||
km.add(Function::new(18, Msa::MSA1, "KM-AES-128"));
|
||||
km.add(Function::new(19, Msa::MSA2, "KM-AES-192"));
|
||||
km.add(Function::new(20, Msa::MSA2, "KM-AES-256"));
|
||||
km.add(Function::new(26, Msa::MSA3, "KM-Encrypted-AES-128"));
|
||||
km.add(Function::new(27, Msa::MSA3, "KM-Encrypted-AES-192"));
|
||||
km.add(Function::new(28, Msa::MSA3, "KM-Encrypted-AES-256"));
|
||||
km.add(Function::new(50, Msa::MSA4, "KM-XTS-AES-128"));
|
||||
km.add(Function::new(52, Msa::MSA4, "KM-XTS-AES-256"));
|
||||
km.add(Function::new(58, Msa::MSA4, "KM-XTS-Encrypted-AES-128"));
|
||||
km.add(Function::new(60, Msa::MSA4, "KM-XTS-Encrypted-AES-256"));
|
||||
km.add(Function::new(82, Msa::MSA10, "KM-FULL-XTS-AES-128"));
|
||||
km.add(Function::new(84, Msa::MSA10, "KM-FULL-XTS-AES-256"));
|
||||
km.add(Function::new(90, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-128"));
|
||||
km.add(Function::new(92, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-256"));
|
||||
km.add(Function::new(127, Msa::MSA13, "KM-Query-Authentication-Information"));
|
||||
|
||||
let mut kmc = Instruction::new(InstructionKind::KMC, 17, "Cipher Message with Chaining");
|
||||
kmc.add(Function::new(0, Msa::MSA, "KMC-Query"));
|
||||
kmc.add(Function::new(1, Msa::MSA, "KMC-DEA"));
|
||||
kmc.add(Function::new(2, Msa::MSA, "KMC-TDEA-128"));
|
||||
kmc.add(Function::new(3, Msa::MSA, "KMC-TDEA-192"));
|
||||
kmc.add(Function::new(9, Msa::MSA3, "KMC-Encrypted-DEA"));
|
||||
kmc.add(Function::new(10, Msa::MSA3, "KMC-Encrypted-TDEA-128"));
|
||||
kmc.add(Function::new(11, Msa::MSA3, "KMC-Encrypted-TDEA-192"));
|
||||
kmc.add(Function::new(18, Msa::MSA1, "KMC-AES-128"));
|
||||
kmc.add(Function::new(19, Msa::MSA2, "KMC-AES-192"));
|
||||
kmc.add(Function::new(20, Msa::MSA2, "KMC-AES-256"));
|
||||
kmc.add(Function::new(26, Msa::MSA3, "KMC-Encrypted-AES-128"));
|
||||
kmc.add(Function::new(27, Msa::MSA3, "KMC-Encrypted-AES-192"));
|
||||
kmc.add(Function::new(28, Msa::MSA3, "KMC-Encrypted-AES-256"));
|
||||
kmc.add(Function::new(67, Msa::MSA1, "KMC-PRNG"));
|
||||
kmc.add(Function::new(127, Msa::MSA13, "KMC-Query-Authentication-Information"));
|
||||
|
||||
let mut kimd = Instruction::new(InstructionKind::KIMD, 17, "Compute Intermediate Message Digest");
|
||||
kimd.add(Function::new(0, Msa::MSA, "KIMD-Query"));
|
||||
kimd.add(Function::new(1, Msa::MSA, "KIMD-SHA-1"));
|
||||
kimd.add(Function::new(2, Msa::MSA1, "KIMD-SHA-256"));
|
||||
kimd.add(Function::new(3, Msa::MSA2, "KIMD-SHA-512"));
|
||||
kimd.add(Function::new(32, Msa::MSA6, "KIMD-SHA3-224"));
|
||||
kimd.add(Function::new(33, Msa::MSA6, "KIMD-SHA3-256"));
|
||||
kimd.add(Function::new(34, Msa::MSA6, "KIMD-SHA3-384"));
|
||||
kimd.add(Function::new(35, Msa::MSA6, "KIMD-SHA3-512"));
|
||||
kimd.add(Function::new(36, Msa::MSA6, "KIMD-SHAKE-128"));
|
||||
kimd.add(Function::new(37, Msa::MSA6, "KIMD-SHAKE-256"));
|
||||
kimd.add(Function::new(65, Msa::MSA4, "KIMD-GHASH"));
|
||||
kimd.add(Function::new(127, Msa::MSA13, "KIMD-Query-Authentication-Information"));
|
||||
|
||||
let mut klmd = Instruction::new(InstructionKind::KLMD, 17, "Compute Last Message Digest");
|
||||
klmd.add(Function::new(0, Msa::MSA, "KLMD-Query"));
|
||||
klmd.add(Function::new(1, Msa::MSA, "KLMD-SHA-1"));
|
||||
klmd.add(Function::new(2, Msa::MSA1, "KLMD-SHA-256"));
|
||||
klmd.add(Function::new(3, Msa::MSA2, "KLMD-SHA-512"));
|
||||
klmd.add(Function::new(32, Msa::MSA6, "KLMD-SHA3-224"));
|
||||
klmd.add(Function::new(33, Msa::MSA6, "KLMD-SHA3-256"));
|
||||
klmd.add(Function::new(34, Msa::MSA6, "KLMD-SHA3-384"));
|
||||
klmd.add(Function::new(35, Msa::MSA6, "KLMD-SHA3-512"));
|
||||
klmd.add(Function::new(36, Msa::MSA6, "KLMD-SHAKE-128"));
|
||||
klmd.add(Function::new(37, Msa::MSA6, "KLMD-SHAKE-256"));
|
||||
klmd.add(Function::new(127, Msa::MSA13, "KLMD-Query-Authentication-Information"));
|
||||
|
||||
let mut kmac = Instruction::new(InstructionKind::KMAC, 17, "Compute Message Authentication Code");
|
||||
kmac.add(Function::new(0, Msa::MSA, "KMAC-Query"));
|
||||
kmac.add(Function::new(1, Msa::MSA, "KMAC-DEA"));
|
||||
kmac.add(Function::new(2, Msa::MSA, "KMAC-TDEA-128"));
|
||||
kmac.add(Function::new(3, Msa::MSA, "KMAC-TDEA-192"));
|
||||
kmac.add(Function::new(9, Msa::MSA3, "KMAC-Encrypted-DEA"));
|
||||
kmac.add(Function::new(10, Msa::MSA3, "KMAC-Encrypted-TDEA-128"));
|
||||
kmac.add(Function::new(11, Msa::MSA3, "KMAC-Encrypted-TDEA-192"));
|
||||
kmac.add(Function::new(18, Msa::MSA4, "KMAC-AES-128"));
|
||||
kmac.add(Function::new(19, Msa::MSA4, "KMAC-AES-192"));
|
||||
kmac.add(Function::new(20, Msa::MSA4, "KMAC-AES-256"));
|
||||
kmac.add(Function::new(26, Msa::MSA4, "KMAC-Encrypted-AES-128"));
|
||||
kmac.add(Function::new(27, Msa::MSA4, "KMAC-Encrypted-AES-192"));
|
||||
kmac.add(Function::new(28, Msa::MSA4, "KMAC-Encrypted-AES-256"));
|
||||
kmac.add(Function::new(112, Msa::MSA11, "KMAC-HMAC-SHA-224"));
|
||||
kmac.add(Function::new(113, Msa::MSA11, "KMAC-HMAC-SHA-256"));
|
||||
kmac.add(Function::new(114, Msa::MSA11, "KMAC-HMAC-SHA-384"));
|
||||
kmac.add(Function::new(115, Msa::MSA11, "KMAC-HMAC-SHA-512"));
|
||||
kmac.add(Function::new(120, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-224"));
|
||||
kmac.add(Function::new(121, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-256"));
|
||||
kmac.add(Function::new(122, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-384"));
|
||||
kmac.add(Function::new(123, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-512"));
|
||||
kmac.add(Function::new(127, Msa::MSA13, "KMAC-Query-Authentication-Information"));
|
||||
|
||||
let mut pckmo = Instruction::new(InstructionKind::PCKMO, 76, "Perform Cryptographic Key Management Operation");
|
||||
pckmo.add(Function::new(0, Msa::MSA3, "PCKMO-Query"));
|
||||
pckmo.add(Function::new(1, Msa::MSA3, "PCKMO-Encrypt-DEA-Key"));
|
||||
pckmo.add(Function::new(2, Msa::MSA3, "PCKMO-Encrypt-TDEA-128-Key"));
|
||||
pckmo.add(Function::new(3, Msa::MSA3, "PCKMO-Encrypt-TDEA-192-Key"));
|
||||
pckmo.add(Function::new(18, Msa::MSA3, "PCKMO-Encrypt-AES-128-Key"));
|
||||
pckmo.add(Function::new(19, Msa::MSA3, "PCKMO-Encrypt-AES-192-Key"));
|
||||
pckmo.add(Function::new(20, Msa::MSA3, "PCKMO-Encrypt-AES-256-Key"));
|
||||
pckmo.add(Function::new(21, Msa::MSA10, "PCKMO-AES-XTS-128-Double"));
|
||||
pckmo.add(Function::new(22, Msa::MSA10, "PCKMO-AES-XTS-256-Double"));
|
||||
pckmo.add(Function::new(32, Msa::MSA9, "PCKMO-Encrypt-ECC-P256-Key"));
|
||||
pckmo.add(Function::new(33, Msa::MSA9, "PCKMO-Encrypt-ECC-P384-Key"));
|
||||
pckmo.add(Function::new(34, Msa::MSA9, "PCKMO-Encrypt-ECC-P521-Key"));
|
||||
pckmo.add(Function::new(40, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed25519-Key"));
|
||||
pckmo.add(Function::new(41, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed448-Key"));
|
||||
pckmo.add(Function::new(118, Msa::MSA11, "PCKMO-Encrypted-HMAC-512-KEY"));
|
||||
pckmo.add(Function::new(122, Msa::MSA11, "PCKMO-Encrypted-HMAC-1024-KEY"));
|
||||
pckmo.add(Function::new(127, Msa::MSA13, "PCKMO-Query-Authentication-Information"));
|
||||
|
||||
let mut kmf = Instruction::new(InstructionKind::KMF, 77, "Cipher Message with Cipher Feedback");
|
||||
kmf.add(Function::new(0, Msa::MSA4, "KMF-Query"));
|
||||
kmf.add(Function::new(1, Msa::MSA4, "KMF-DEA"));
|
||||
kmf.add(Function::new(2, Msa::MSA4, "KMF-TDEA-128"));
|
||||
kmf.add(Function::new(3, Msa::MSA4, "KMF-TDEA-192"));
|
||||
kmf.add(Function::new(9, Msa::MSA4, "KMF-Encrypted-DEA"));
|
||||
kmf.add(Function::new(10, Msa::MSA4, "KMF-Encrypted-TDEA-128"));
|
||||
kmf.add(Function::new(11, Msa::MSA4, "KMF-Encrypted-TDEA-192"));
|
||||
kmf.add(Function::new(18, Msa::MSA4, "KMF-AES-128"));
|
||||
kmf.add(Function::new(19, Msa::MSA4, "KMF-AES-192"));
|
||||
kmf.add(Function::new(20, Msa::MSA4, "KMF-AES-256"));
|
||||
kmf.add(Function::new(26, Msa::MSA4, "KMF-Encrypted-AES-128"));
|
||||
kmf.add(Function::new(27, Msa::MSA4, "KMF-Encrypted-AES-192"));
|
||||
kmf.add(Function::new(28, Msa::MSA4, "KMF-Encrypted-AES-256"));
|
||||
kmf.add(Function::new(127, Msa::MSA13, "KMF-Query-Authentication-Information"));
|
||||
|
||||
let mut kmctr = Instruction::new(InstructionKind::KMCTR, 77, "Cipher Message with Counter");
|
||||
kmctr.add(Function::new(0, Msa::MSA4, "KMCTR-Query"));
|
||||
kmctr.add(Function::new(1, Msa::MSA4, "KMCTR-DEA"));
|
||||
kmctr.add(Function::new(2, Msa::MSA4, "KMCTR-TDEA-128"));
|
||||
kmctr.add(Function::new(3, Msa::MSA4, "KMCTR-TDEA-192"));
|
||||
kmctr.add(Function::new(9, Msa::MSA4, "KMCTR-Encrypted-DEA"));
|
||||
kmctr.add(Function::new(10, Msa::MSA4, "KMCTR-Encrypted-TDEA-128"));
|
||||
kmctr.add(Function::new(11, Msa::MSA4, "KMCTR-Encrypted-TDEA-192"));
|
||||
kmctr.add(Function::new(18, Msa::MSA4, "KMCTR-AES-128"));
|
||||
kmctr.add(Function::new(19, Msa::MSA4, "KMCTR-AES-192"));
|
||||
kmctr.add(Function::new(20, Msa::MSA4, "KMCTR-AES-256"));
|
||||
kmctr.add(Function::new(26, Msa::MSA4, "KMCTR-Encrypted-AES-128"));
|
||||
kmctr.add(Function::new(27, Msa::MSA4, "KMCTR-Encrypted-AES-192"));
|
||||
kmctr.add(Function::new(28, Msa::MSA4, "KMCTR-Encrypted-AES-256"));
|
||||
kmctr.add(Function::new(127, Msa::MSA13, "KMCTR-Query-Authentication-Information"));
|
||||
|
||||
let mut kmo = Instruction::new(InstructionKind::KMO, 77, "Cipher Message with Output Feedback");
|
||||
kmo.add(Function::new(0, Msa::MSA4, "KMO-Query"));
|
||||
kmo.add(Function::new(1, Msa::MSA4, "KMO-DEA"));
|
||||
kmo.add(Function::new(2, Msa::MSA4, "KMO-TDEA-128"));
|
||||
kmo.add(Function::new(3, Msa::MSA4, "KMO-TDEA-192"));
|
||||
kmo.add(Function::new(9, Msa::MSA4, "KMO-Encrypted-DEA"));
|
||||
kmo.add(Function::new(10, Msa::MSA4, "KMO-Encrypted-TDEA-128"));
|
||||
kmo.add(Function::new(11, Msa::MSA4, "KMO-Encrypted-TDEA-192"));
|
||||
kmo.add(Function::new(18, Msa::MSA4, "KMO-AES-128"));
|
||||
kmo.add(Function::new(19, Msa::MSA4, "KMO-AES-192"));
|
||||
kmo.add(Function::new(20, Msa::MSA4, "KMO-AES-256"));
|
||||
kmo.add(Function::new(26, Msa::MSA4, "KMO-Encrypted-AES-128"));
|
||||
kmo.add(Function::new(27, Msa::MSA4, "KMO-Encrypted-AES-192"));
|
||||
kmo.add(Function::new(28, Msa::MSA4, "KMO-Encrypted-AES-256"));
|
||||
kmo.add(Function::new(127, Msa::MSA13, "KMO-Query-Authentication-Information"));
|
||||
|
||||
let mut pcc = Instruction::new(InstructionKind::PCC, 77, "Perform Cryptographic Computation");
|
||||
pcc.add(Function::new(0, Msa::MSA4, "PCC-Query"));
|
||||
pcc.add(Function::new(1, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-DEA"));
|
||||
pcc.add(Function::new(2, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-128"));
|
||||
pcc.add(Function::new(3, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-192"));
|
||||
pcc.add(Function::new(9, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-DEA"));
|
||||
pcc.add(Function::new(10, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-128"));
|
||||
pcc.add(Function::new(11, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-192"));
|
||||
pcc.add(Function::new(18, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-128"));
|
||||
pcc.add(Function::new(19, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-192"));
|
||||
pcc.add(Function::new(20, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-256"));
|
||||
pcc.add(Function::new(26, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-128"));
|
||||
pcc.add(Function::new(27, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-192"));
|
||||
pcc.add(Function::new(28, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-256"));
|
||||
pcc.add(Function::new(50, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-128"));
|
||||
pcc.add(Function::new(52, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-256"));
|
||||
pcc.add(Function::new(58, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-128"));
|
||||
pcc.add(Function::new(60, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-256"));
|
||||
pcc.add(Function::new(64, Msa::MSA9, "PCC-Scalar-Multiply-P256"));
|
||||
pcc.add(Function::new(65, Msa::MSA9, "PCC-Scalar-Multiply-P384"));
|
||||
pcc.add(Function::new(66, Msa::MSA9, "PCC-Scalar-Multiply-P521"));
|
||||
pcc.add(Function::new(72, Msa::MSA9, "PCC-Scalar-Multiply-Ed25519"));
|
||||
pcc.add(Function::new(73, Msa::MSA9, "PCC-Scalar-Multiply-Ed448"));
|
||||
pcc.add(Function::new(80, Msa::MSA9, "PCC-Scalar-Multiply-X25519"));
|
||||
pcc.add(Function::new(81, Msa::MSA9, "PCC-Scalar-Multiply-X448"));
|
||||
pcc.add(Function::new(127, Msa::MSA13, "PCC-Query-Authentication-Information"));
|
||||
|
||||
let mut prno = Instruction::new(InstructionKind::PRNO, 57, "Perform Random Number Operation");
|
||||
prno.add(Function::new(0, Msa::MSA5, "PRNO-Query"));
|
||||
prno.add(Function::new(3, Msa::MSA5, "PRNO-SHA-512-DRNG"));
|
||||
prno.add(Function::new(112, Msa::MSA7, "PRNO-TRNG-Query-Raw-to-Conditioned-Ratio"));
|
||||
prno.add(Function::new(114, Msa::MSA7, "PRNO-TRNG"));
|
||||
prno.add(Function::new(127, Msa::MSA13, "PRNO-Query-Authentication-Information"));
|
||||
|
||||
let mut kma = Instruction::new(InstructionKind::KMA, 146, "Cipher Message with Authentication");
|
||||
kma.add(Function::new(0, Msa::MSA8, "KMA-Query"));
|
||||
kma.add(Function::new(18, Msa::MSA8, "KMA-GCM-AES-128"));
|
||||
kma.add(Function::new(19, Msa::MSA8, "KMA-GCM-AES-192"));
|
||||
kma.add(Function::new(20, Msa::MSA8, "KMA-GCM-AES-256"));
|
||||
kma.add(Function::new(26, Msa::MSA8, "KMA-GCM-Encrypted-AES-128"));
|
||||
kma.add(Function::new(27, Msa::MSA8, "KMA-GCM-Encrypted-AES-192"));
|
||||
kma.add(Function::new(28, Msa::MSA8, "KMA-GCM-Encrypted-AES-256"));
|
||||
kma.add(Function::new(127, Msa::MSA13, "KMA-Query-Authentication-Information"));
|
||||
|
||||
let mut kdsa = Instruction::new(InstructionKind::KDSA, 155, "Compute Digital Signature Authentication");
|
||||
kdsa.add(Function::new(0, Msa::MSA9, "KDSA-Query"));
|
||||
kdsa.add(Function::new(1, Msa::MSA9, "KDSA-ECDSA-Verify-P256"));
|
||||
kdsa.add(Function::new(2, Msa::MSA9, "KDSA-ECDSA-Verify-P384"));
|
||||
kdsa.add(Function::new(3, Msa::MSA9, "KDSA-ECDSA-Verify-P521"));
|
||||
kdsa.add(Function::new(9, Msa::MSA9, "KDSA-ECDSA-Sign-P256"));
|
||||
kdsa.add(Function::new(10, Msa::MSA9, "KDSA-ECDSA-Sign-P384"));
|
||||
kdsa.add(Function::new(11, Msa::MSA9, "KDSA-ECDSA-Sign-P521"));
|
||||
kdsa.add(Function::new(17, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P256"));
|
||||
kdsa.add(Function::new(18, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P384"));
|
||||
kdsa.add(Function::new(19, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P521"));
|
||||
kdsa.add(Function::new(32, Msa::MSA9, "KDSA-EdDSA-Verify-Ed25519"));
|
||||
kdsa.add(Function::new(36, Msa::MSA9, "KDSA-EdDSA-Verify-Ed448"));
|
||||
kdsa.add(Function::new(40, Msa::MSA9, "KDSA-EdDSA-Sign-Ed25519"));
|
||||
kdsa.add(Function::new(44, Msa::MSA9, "KDSA-EdDSA-Sign-Ed448"));
|
||||
kdsa.add(Function::new(48, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed25519"));
|
||||
kdsa.add(Function::new(52, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed448"));
|
||||
kdsa.add(Function::new(127, Msa::MSA13, "KDSA-Query-Authentication-Information"));
|
||||
|
||||
instructions.push(km);
|
||||
instructions.push(kmc);
|
||||
instructions.push(kimd);
|
||||
instructions.push(klmd);
|
||||
instructions.push(kmac);
|
||||
instructions.push(pckmo);
|
||||
instructions.push(kmf);
|
||||
instructions.push(kmctr);
|
||||
instructions.push(kmo);
|
||||
instructions.push(pcc);
|
||||
instructions.push(prno);
|
||||
instructions.push(kma);
|
||||
instructions.push(kdsa);
|
||||
}
|
||||
|
||||
/// number of functions introduced by a level is dynamically counted to ease extension
|
||||
pub fn update_msa_function_count(args: &Cli, msa: &mut MsaLevel, ins: &Vec<Instruction>) {
|
||||
for i in ins {
|
||||
let num_of_funcs_in_level = i.funcs.iter().filter(|f| f.msa == msa.msa_level).count() as u8;
|
||||
|
||||
msa.total_functions += num_of_funcs_in_level;
|
||||
if args.instructions.is_empty() || args.instructions.contains(&i.kind) {
|
||||
msa.dynamic_total_functions += num_of_funcs_in_level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ser_hex<S: Serializer>(data: &Vec<u8>, ser: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
HexSlice::from(data).serialize(ser)
|
||||
}
|
||||
8
rust/cpacfinfo/src/noop.c
Normal file
8
rust/cpacfinfo/src/noop.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
uint32_t stfle(uint64_t __attribute__((unused)) * stfle_fac_list,
|
||||
uint32_t __attribute__((unused)) size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
261
rust/cpacfinfo/src/query.rs
Normal file
261
rust/cpacfinfo/src/query.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Error;
|
||||
use std::io::Read;
|
||||
use std::ops::Index;
|
||||
use std::result::Result;
|
||||
|
||||
use zerocopy::FromBytes;
|
||||
use zerocopy::FromZeroes;
|
||||
|
||||
use crate::msa::InstructionKind;
|
||||
use crate::msa::QueryAuthInfo;
|
||||
|
||||
/// Path to sysfs in which the query and qai informations are fetched from
|
||||
const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/";
|
||||
|
||||
/// Every Instruction has a Query function to get information about what functions are available
|
||||
pub const QUERY_FUNCTION_CODE: u8 = 0;
|
||||
|
||||
/// Number of bytes returned by this Query
|
||||
pub const QUERY_PARAM_SIZE_IN_BYTES: usize = 16;
|
||||
|
||||
/// Starting with MSA 13 every Instruction has a Query Authentication Information function to get
|
||||
/// information about the running firmware
|
||||
pub const QAI_FUNCTION_CODE: u8 = 127;
|
||||
|
||||
/// Number of bytes returned by this Query Authentication Information
|
||||
pub const QAI_PARAM_SIZE_IN_BYTES: usize = 256;
|
||||
|
||||
/// Query authentication information format identifier
|
||||
const FORMAT_0: u8 = 0;
|
||||
|
||||
#[derive(FromBytes, FromZeroes)]
|
||||
#[repr(C)]
|
||||
struct QaiFmt0 {
|
||||
res00: [u8; 6],
|
||||
hash_length: u16,
|
||||
res08: [u8; 4],
|
||||
version: u32,
|
||||
hash: [u8; 64],
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Param {
|
||||
QueryParam([u8; QUERY_PARAM_SIZE_IN_BYTES]),
|
||||
QaiParam([u8; QAI_PARAM_SIZE_IN_BYTES]),
|
||||
}
|
||||
|
||||
impl Index<u8> for Param {
|
||||
type Output = u8;
|
||||
|
||||
fn index(&self, index: u8) -> &Self::Output {
|
||||
match self {
|
||||
Self::QueryParam(p) => &p[index as usize],
|
||||
Self::QaiParam(p) => &p[index as usize],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Param {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::QueryParam(_) => QUERY_PARAM_SIZE_IN_BYTES,
|
||||
Self::QaiParam(_) => QAI_PARAM_SIZE_IN_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
/// check if a specific bit is 1 in param
|
||||
pub fn check_bit_in_param(&self, check_bit: usize) -> bool {
|
||||
// get correct byte of param
|
||||
let byte = check_bit / 8;
|
||||
|
||||
// get correct byte of param
|
||||
if byte >= self.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get correct bit of param
|
||||
let bit = 8 - ((check_bit % 8) + 1);
|
||||
|
||||
// return if specified bit is set
|
||||
match self {
|
||||
Self::QueryParam(param) => (param[byte] & (1 << bit)) > 0,
|
||||
Self::QaiParam(param) => (param[byte] & (1 << bit)) > 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// set given bit in param to 0
|
||||
pub fn unset_bit_in_param(&mut self, flip_bit: u8) {
|
||||
// get correct byte of param
|
||||
let byte = flip_bit / 8;
|
||||
if byte as usize >= self.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
// get correct bit of param
|
||||
let bit = 8 - ((flip_bit % 8) + 1);
|
||||
|
||||
// build template to logically AND against param byte
|
||||
// i.e. (flip_bit = 0) template = 1000 0000
|
||||
let mut template: u8 = 1 << bit;
|
||||
// flip all bits in template
|
||||
// i.e. (flip_bit = 0) template = 0111 1111
|
||||
template = !template;
|
||||
|
||||
// set bit to 0 while not changing any other bit
|
||||
// i.e. (flip_bit = 0) 0111 1111 & xxxx xxxx = 0xxx xxxx
|
||||
match self {
|
||||
Self::QueryParam(c) => c[byte as usize] &= template,
|
||||
Self::QaiParam(c) => c[byte as usize] &= template,
|
||||
}
|
||||
}
|
||||
|
||||
/// set all bytes of self to value
|
||||
#[cfg(test)]
|
||||
pub fn set_param_to(&mut self, value: u8) {
|
||||
match self {
|
||||
Self::QueryParam(ref mut content) => *content = [value; QUERY_PARAM_SIZE_IN_BYTES],
|
||||
Self::QaiParam(ref mut content) => *content = [value; QAI_PARAM_SIZE_IN_BYTES],
|
||||
}
|
||||
}
|
||||
|
||||
// Outsourced for potential future formats to be easily added in this match statement
|
||||
pub fn parse_qai_based_on_format(
|
||||
&self,
|
||||
qai: &mut QueryAuthInfo,
|
||||
) -> Result<bool, anyhow::Error> {
|
||||
match self {
|
||||
Self::QueryParam(_) => panic!("programming error"),
|
||||
Self::QaiParam(bin) => {
|
||||
// The third byte of the param block specifies which format to use to parse the rest
|
||||
qai.format = bin[3];
|
||||
|
||||
// for new formats add a match case here along with a parsing function
|
||||
match qai.format {
|
||||
FORMAT_0 => {
|
||||
parse_qai_format_0(qai, bin);
|
||||
Ok(true)
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if SYSFS_PATH exists
|
||||
pub fn check_sysfs() -> bool {
|
||||
match std::path::Path::new(SYSFS_PATH).exists() {
|
||||
true => true,
|
||||
false => {
|
||||
println!("Warning: There seems to be an insufficient kernel level running (sysfs interface {SYSFS_PATH} is missing)\nNo information can be fetched from sysfs, application exits early.");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// parsing the information supplied by sysfs into QueryAuthInfo struct
|
||||
///
|
||||
/// The following box shows the qai block with named fields each with a length in bytes.
|
||||
/// The length of field IFCL Hash depends on IFCL Hash Length and is either 32 or 64 bytes long.
|
||||
/// In case of a 32 bytes length the latter 32 bytes of the 64 bytes Hash are filled with zeros.
|
||||
///
|
||||
/// | BYTE | BYTE | BYTE | BYTE |
|
||||
/// -----------------------------------------------------------------
|
||||
/// | RESERVED (3) FORMAT (1) |
|
||||
/// | RESERVED (2) IFCL HASH LENGTH (2) |
|
||||
/// | RESERVED (4) |
|
||||
/// | IFCL VERSION (4) |
|
||||
/// | IFCL HASH (32 / 64) |
|
||||
/// | RESERVED (176) |
|
||||
/// -----------------------------------------------------------------
|
||||
fn parse_qai_format_0(qai: &mut QueryAuthInfo, param: &[u8]) {
|
||||
// parse param to temporary struct to ease further conversion
|
||||
let tmp = QaiFmt0::read_from_prefix(param).expect("programming error");
|
||||
|
||||
// parse from temporary struct
|
||||
qai.hash_len = tmp.hash_length;
|
||||
qai.version = tmp.version;
|
||||
|
||||
// depending on the parsed hash length the hash is parsed
|
||||
qai.hash = vec![0; qai.hash_len as usize];
|
||||
qai.hash
|
||||
.as_mut_slice()
|
||||
.copy_from_slice(&tmp.hash[..qai.hash_len as usize]);
|
||||
}
|
||||
|
||||
/// cpacfinfo does not execute the actual instruction with function code but uses information
|
||||
/// provided by the sysfs
|
||||
pub fn query(ins: &InstructionKind, fc: u8) -> Result<Param, Error> {
|
||||
// query dependent file names
|
||||
let auth_info;
|
||||
let mut param;
|
||||
match fc {
|
||||
QUERY_FUNCTION_CODE => {
|
||||
auth_info = "";
|
||||
param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]);
|
||||
}
|
||||
QAI_FUNCTION_CODE => {
|
||||
auth_info = "_auth_info";
|
||||
param = Param::QaiParam([0; QAI_PARAM_SIZE_IN_BYTES]);
|
||||
}
|
||||
_ => panic!("programming error"),
|
||||
};
|
||||
|
||||
// depending on which query is performed the bytes to be read from sysfs vary
|
||||
let bytes_to_be_read = param.len();
|
||||
|
||||
// build filepath
|
||||
let filepath = format!(
|
||||
"{SYSFS_PATH}{}_query{auth_info}_raw",
|
||||
ins.to_string().to_lowercase()
|
||||
);
|
||||
|
||||
// open file
|
||||
let mut f = File::open(filepath)?;
|
||||
|
||||
// read file
|
||||
let res = match param {
|
||||
Param::QueryParam(ref mut c) => read_file_to_buf(&mut f, c),
|
||||
Param::QaiParam(ref mut c) => read_file_to_buf(&mut f, c),
|
||||
};
|
||||
|
||||
let bytes_read = res?;
|
||||
|
||||
match bytes_read == bytes_to_be_read {
|
||||
true => Ok(param),
|
||||
false => Err(Error::new(std::io::ErrorKind::UnexpectedEof, "test")),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_file_to_buf(file: &mut File, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
file.read(buf)
|
||||
/* match file.read(buf) {
|
||||
Result::Ok(bytes_read) => Ok(bytes_read),
|
||||
Err(e) => Err(e),
|
||||
} */
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_param_funcs() {
|
||||
// initialize param with all ones
|
||||
let mut param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]);
|
||||
const NUMBER_OF_BITS: usize = 8 * QUERY_PARAM_SIZE_IN_BYTES;
|
||||
|
||||
for i in 0..NUMBER_OF_BITS {
|
||||
// reset param to all ones
|
||||
param.set_param_to(0xFF);
|
||||
assert!(param.check_bit_in_param(i));
|
||||
|
||||
// set one bit to zero
|
||||
param.unset_bit_in_param(i as u8);
|
||||
|
||||
// check if that bit is zero
|
||||
assert!(!param.check_bit_in_param(i));
|
||||
}
|
||||
}
|
||||
18
rust/cpacfinfo/src/stfle.c
Normal file
18
rust/cpacfinfo/src/stfle.c
Normal file
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t stfle(uint64_t *stfle_fac_list, uint32_t size)
|
||||
{
|
||||
uint32_t reg0 = size - 1;
|
||||
|
||||
asm volatile(" lgr %%r0,%[reg0]\n"
|
||||
" .insn s,0xb2b00000,%[list]\n" /* stfle */
|
||||
" lgr %[reg0],%%r0\n"
|
||||
: [reg0] "+&d"(reg0), [list] "+Q"(*stfle_fac_list)
|
||||
:
|
||||
: "memory", "cc", "r0");
|
||||
return reg0;
|
||||
}
|
||||
84
rust/cpacfinfo/src/stfle.rs
Normal file
84
rust/cpacfinfo/src/stfle.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use anyhow::Error;
|
||||
|
||||
/// Specifies the number of u64 values needed to store the stfle block
|
||||
pub const STFLE_LEN: usize = 3;
|
||||
|
||||
pub struct Stfle {
|
||||
data: [u64; STFLE_LEN],
|
||||
}
|
||||
|
||||
impl Stfle {
|
||||
/// Constructs a STFLE block, and saves the STFLE information in the structure.
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let mut ret = Self {
|
||||
data: [0; STFLE_LEN],
|
||||
};
|
||||
|
||||
// SAFETY: this call is safe because ret can store 64 bits * 3 which equals the required 192
|
||||
// bits.
|
||||
let rc = unsafe { stfle(&mut ret.data[0], STFLE_LEN as u32) };
|
||||
let rc = match rc {
|
||||
0 => {
|
||||
println!("Unable to fetch STFLE which is only available on s390x architecture");
|
||||
return Ok(ret);
|
||||
}
|
||||
rc if rc as usize >= STFLE_LEN => STFLE_LEN as u32,
|
||||
rc => rc + 1,
|
||||
};
|
||||
|
||||
if rc != STFLE_LEN as u32 {
|
||||
println!("Partial read of STFLE, information might be incomplete");
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// check specific bit in stfle (accounts for big-endianness of stfle)
|
||||
pub fn check_bit_in_stfle(&self, check_bit: u8) -> bool {
|
||||
// stfle is big endian while check_bit is little endian
|
||||
let byte = (check_bit / 64) as usize;
|
||||
if byte >= STFLE_LEN {
|
||||
return false;
|
||||
}
|
||||
// conversion from little endian check_bit to big endian
|
||||
let bit = ((check_bit / 64 + 1) * 64 - 1) - check_bit;
|
||||
self.data[byte] & 1 << bit > 0
|
||||
}
|
||||
}
|
||||
|
||||
// STFLE bits cannot be retrieved from the system but have to be fetched by running the STFLE
|
||||
// instruction of Z. This is done in linked C code
|
||||
extern "C" {
|
||||
/// Retrieve STFLE bits into list
|
||||
///
|
||||
/// List is in big-endian when returned.
|
||||
/// @list is to return the outcome of the stfle operation. Pointer must be able to store
|
||||
/// 192 bits.
|
||||
/// @doublewords specifies the length of the pointer @list as a number of elements behind
|
||||
/// the pointer.
|
||||
fn stfle(list: *mut u64, doublewords: u32) -> u32;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_check_bit_in_stfle() {
|
||||
let mut stfle = match Stfle::new() {
|
||||
Ok(ret) => ret,
|
||||
Err(e) => panic!("{e}"),
|
||||
};
|
||||
|
||||
for b in 0..(STFLE_LEN * 64) {
|
||||
// set bit
|
||||
stfle.data[b / 64] = u64::pow(2, (63 - (b - ((b / 64) * 64))) as u32);
|
||||
|
||||
// check if bit is set
|
||||
assert!(stfle.check_bit_in_stfle(b as u8));
|
||||
|
||||
// reset stfle
|
||||
stfle.data = [0; STFLE_LEN];
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,31 @@
|
||||
[package]
|
||||
name = "s390_pv"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "s390-tools IBM Secure Execution utilities"
|
||||
keywords = ["s390", "s390x", "IBM_Secure_Execution"]
|
||||
repository = "https://github.com/ibm-s390-linux/s390-tools/tree/master/rust"
|
||||
categories = ["hardware-support"]
|
||||
readme = "README.md"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.3"
|
||||
curl = "0.4.44"
|
||||
foreign-types = "0.3.1"
|
||||
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
|
||||
openssl = "0.10.57"
|
||||
openssl-sys = "0.9.92"
|
||||
serde = { version = "1.0.139", features = ["derive"] }
|
||||
thiserror = "1.0.33"
|
||||
byteorder = "1.5"
|
||||
curl = "0.4.47"
|
||||
enum_dispatch = "0.3.13"
|
||||
foreign-types = "0.3.2"
|
||||
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
||||
openssl = "0.10.70"
|
||||
openssl-sys = "0.9.105"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
thiserror = "2.0.11"
|
||||
zerocopy = { version="0.7", features = ["derive"] }
|
||||
|
||||
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.10.0" }
|
||||
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.11.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.139"
|
||||
serde_test = "1.0.177"
|
||||
|
||||
@@ -7,10 +7,11 @@ use std::{
|
||||
mem::size_of,
|
||||
};
|
||||
|
||||
use log::{debug, warn};
|
||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64};
|
||||
|
||||
// (SE) boot request control block aka SE header
|
||||
use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE};
|
||||
use log::debug;
|
||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64};
|
||||
|
||||
/// Struct containing all SE-header tags.
|
||||
///
|
||||
@@ -45,6 +46,81 @@ impl TryFrom<Vec<u8>> for BootHdrTags {
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct representing the Secure Execution boot image metadata
|
||||
#[allow(unused)]
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, Clone, FromBytes, FromZeroes, AsBytes, PartialEq, Eq)]
|
||||
pub struct SeImgMetaData {
|
||||
/// Magic value
|
||||
magic: [u8; 8],
|
||||
/// Secure Execution header offset in the image
|
||||
hdr_off: U64<BigEndian>,
|
||||
/// Version
|
||||
version: U32<BigEndian>,
|
||||
/// IPIB offset in the image
|
||||
ipib_off: U64<BigEndian>,
|
||||
}
|
||||
assert_size!(SeImgMetaData, 28);
|
||||
|
||||
impl SeImgMetaData {
|
||||
/// Address in the Secure Execution boot image
|
||||
pub const OFFSET: u64 = 0xc000;
|
||||
/// V1 of the Secure Execution boot image metadata
|
||||
const V1: u32 = 0x1;
|
||||
|
||||
/// Create v1 Secure Execution image metadata.
|
||||
pub fn new_v1(hdr_off: u64, ipib_off: u64) -> Self {
|
||||
Self {
|
||||
magic: Self::MAGIC,
|
||||
version: Self::V1.into(),
|
||||
hdr_off: hdr_off.into(),
|
||||
ipib_off: ipib_off.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn seek_start<R>(img: &mut R) -> Result<bool>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
const BUF_SIZE: i64 = 8;
|
||||
static_assert!(SeImgMetaData::MAGIC.len() == BUF_SIZE as usize);
|
||||
|
||||
let mut buf = [0; BUF_SIZE as usize];
|
||||
match img.seek(std::io::SeekFrom::Start(Self::OFFSET)) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
match img.read_exact(&mut buf) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
}
|
||||
|
||||
if Self::starts_with_magic(&buf) {
|
||||
// go back to the beginning of the metadata
|
||||
img.seek(Current(-BUF_SIZE))?;
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Gets the bytes of this value.
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
<Self as AsBytes>::as_bytes(self)
|
||||
}
|
||||
|
||||
/// Returns the version of this [`SeImgMetaData`].
|
||||
pub fn version(&self) -> u32 {
|
||||
self.version.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Magic value for the metadata of a Secure Execution boot image
|
||||
impl MagicValue<8> for SeImgMetaData {
|
||||
// ASCII `SeImgLnx`
|
||||
const MAGIC: [u8; 8] = [0x53, 0x65, 0x49, 0x6d, 0x67, 0x4c, 0x6e, 0x78];
|
||||
}
|
||||
|
||||
/// Magic value for a SE-(boot)header
|
||||
#[derive(Debug)]
|
||||
pub struct BootHdrMagic;
|
||||
@@ -52,6 +128,65 @@ impl MagicValue<8> for BootHdrMagic {
|
||||
const MAGIC: [u8; 8] = [0x49, 0x42, 0x4d, 0x53, 0x65, 0x63, 0x45, 0x78];
|
||||
}
|
||||
|
||||
/// Tries to seek to the start of the Secure Execution header.
|
||||
///
|
||||
/// Returns `false` if no Secure Execution header found, `true` otherwise.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// In the very unlikely case an IO error can appear when seeking to the
|
||||
/// beginning of the header.
|
||||
pub fn seek_se_hdr_start<R>(img: &mut R) -> Result<bool>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let max_iter: usize;
|
||||
const BUF_SIZE: i64 = 8;
|
||||
static_assert!(BootHdrMagic::MAGIC.len() == BUF_SIZE as usize);
|
||||
|
||||
let old_position = img.stream_position()?;
|
||||
if !SeImgMetaData::seek_start(img)? {
|
||||
// Search from the previous position.
|
||||
img.seek(std::io::SeekFrom::Start(old_position))?;
|
||||
max_iter = 0x15;
|
||||
} else {
|
||||
let mut img_metadata_bytes = vec![0u8; size_of::<SeImgMetaData>()];
|
||||
// read in the header
|
||||
img.read_exact(&mut img_metadata_bytes)?;
|
||||
// Cannot fail because the buffer has the same size as SeImgMetaData.
|
||||
let img_metadata = SeImgMetaData::ref_from(&img_metadata_bytes).unwrap();
|
||||
let img_metadata_version = img_metadata.version();
|
||||
if img_metadata_version != SeImgMetaData::V1 {
|
||||
warn!("Unknown Secure Execution boot image version {img_metadata_version}");
|
||||
}
|
||||
|
||||
img.seek(std::io::SeekFrom::Start(img_metadata.hdr_off.into()))?;
|
||||
max_iter = 1;
|
||||
}
|
||||
|
||||
let mut buf = [0; BUF_SIZE as usize];
|
||||
for _ in 0..max_iter {
|
||||
match img.read_exact(&mut buf) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
|
||||
if BootHdrMagic::starts_with_magic(&buf) {
|
||||
// go back to the beginning of the header
|
||||
img.seek(Current(-BUF_SIZE))?;
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
// goto next page start
|
||||
// or report invalid file format if file ends "early"
|
||||
match img.seek(Current(PAGESIZE as i64 - BUF_SIZE)) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
impl BootHdrTags {
|
||||
/// Returns a reference to the SE-header tag of this [`BootHdrTags`].
|
||||
pub fn tag(&self) -> &[u8; 16] {
|
||||
@@ -64,40 +199,6 @@ impl BootHdrTags {
|
||||
Self { ald, tld, pld, tag }
|
||||
}
|
||||
|
||||
/// Returns `false` if no SE-header found, `true` otherwise.
|
||||
/// In the very unlikely case an IO error can appear
|
||||
/// when seeking to the beginning of the header.
|
||||
fn seek_se_hdr_start<R>(img: &mut R) -> Result<bool>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
const MAX_ITER: usize = 0x15;
|
||||
const BUF_SIZE: i64 = 8;
|
||||
static_assert!(BootHdrMagic::MAGIC.len() == BUF_SIZE as usize);
|
||||
|
||||
let mut buf = [0; BUF_SIZE as usize];
|
||||
for _ in [0; MAX_ITER] {
|
||||
match img.read_exact(&mut buf) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
|
||||
if BootHdrMagic::starts_with_magic(&buf) {
|
||||
// go back to the beginning of the header
|
||||
img.seek(Current(-BUF_SIZE))?;
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
// goto next page start
|
||||
// or report invalid file format if file ends "early"
|
||||
match img.seek(Current(PAGESIZE as i64 - BUF_SIZE)) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Deserializes a (SE) boot header and extracts the tags.
|
||||
///
|
||||
/// Searches for the header; if found extracts the tags.
|
||||
@@ -110,7 +211,7 @@ impl BootHdrTags {
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
if !Self::seek_se_hdr_start(img)? {
|
||||
if !seek_se_hdr_start(img)? {
|
||||
debug!("No boot hdr found");
|
||||
return Err(Error::InvBootHdr);
|
||||
}
|
||||
@@ -149,7 +250,7 @@ impl BootHdrTags {
|
||||
let mut tag = [0u8; BootHdrHead::TAG_SIZE];
|
||||
img.read_exact(tag.as_mut_slice())?;
|
||||
|
||||
Ok(BootHdrTags {
|
||||
Ok(Self {
|
||||
pld: hdr_head.pld,
|
||||
ald: hdr_head.ald,
|
||||
tld: hdr_head.tld,
|
||||
@@ -186,8 +287,7 @@ mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use super::*;
|
||||
use crate::get_test_asset;
|
||||
use crate::Error;
|
||||
use crate::{get_test_asset, Error};
|
||||
|
||||
const EXP_HDR: BootHdrTags = BootHdrTags {
|
||||
pld: [
|
||||
@@ -279,4 +379,17 @@ mod tests {
|
||||
let der: Result<BootHdrTags> = ser.clone().try_into();
|
||||
assert!(matches!(der, Err(Error::InvBootHdrSize(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn se_img_metadata() {
|
||||
let metadata = SeImgMetaData::new_v1(0x14000, 0x16000);
|
||||
let data = [
|
||||
83, 101, 73, 109, 103, 76, 110, 120, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||
0, 1, 96, 0,
|
||||
];
|
||||
assert_eq!(metadata.as_bytes(), &data);
|
||||
assert_eq!(SeImgMetaData::ref_from(&data), Some(&metadata));
|
||||
|
||||
assert_eq!(metadata.version(), SeImgMetaData::V1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Trait for securely zeroizing memory.
|
||||
///
|
||||
/// To be used with [`Confidential`]
|
||||
pub trait Zeroize {
|
||||
/// Reliably overwrites the given buffer with zeros,
|
||||
fn zeroize(&mut self);
|
||||
}
|
||||
|
||||
// Automatically impl Zeroize for u8 arrays
|
||||
impl<const COUNT: usize> Zeroize for [u8; COUNT] {
|
||||
/// Reliably overwrites the given buffer with zeros,
|
||||
/// by performing a volatile write followed by a memory barrier
|
||||
fn zeroize(&mut self) {
|
||||
// SAFETY: given buffer(self) has the correct (compile time) size
|
||||
unsafe { std::ptr::write_volatile(self, [0u8; COUNT]) };
|
||||
std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for Vec<u8> {
|
||||
/// Reliably overwrites the given buffer with zeros,
|
||||
/// by overwriting the whole vector's capacity with zeros.
|
||||
fn zeroize(&mut self) {
|
||||
// TODO use `volatile_set_memory` when stabilized
|
||||
let mut dst = self.as_mut_ptr();
|
||||
for _ in 0..self.capacity() {
|
||||
// SAFETY:
|
||||
// * Vec allocated at least capacity elements continuously
|
||||
// * dst points always to a valid location
|
||||
unsafe {
|
||||
std::ptr::write_volatile(dst, 0);
|
||||
dst = dst.add(1);
|
||||
}
|
||||
}
|
||||
std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thin wrapper around an type implementing Zeroize.
|
||||
///
|
||||
/// A `Confidential` represents a confidential value that must be securely overwritten during drop.
|
||||
/// Will never leak its wrapped value during [`Debug`]
|
||||
///
|
||||
/// ```rust
|
||||
/// use s390_pv::request::Confidential;
|
||||
/// fn foo(value: Confidential<[u8; 2]>) {
|
||||
/// println!("value: {value:?}");
|
||||
/// }
|
||||
/// # fn main() {
|
||||
/// foo([1, 2].into());
|
||||
/// // prints:
|
||||
/// // in debug builds:
|
||||
/// // value: Confidential([1, 2])
|
||||
/// // in release builds:
|
||||
/// // value: Confidential(***)
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Default)]
|
||||
pub struct Confidential<C: Zeroize>(C);
|
||||
impl<C: Zeroize> Confidential<C> {
|
||||
/// Convert a type into a self overwriting one.
|
||||
///
|
||||
/// Prefer using [`Into`]
|
||||
pub fn new(v: C) -> Self {
|
||||
Confidential(v)
|
||||
}
|
||||
|
||||
/// Get a reference to the contained value
|
||||
pub fn value(&self) -> &C {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get an immutable reference to the contained value
|
||||
///
|
||||
/// NOTE that modifications to a mutable reference can trigger reallocation.
|
||||
/// e.g. a [`Vec`] might expand if more space needed. -> preallocate enough space
|
||||
/// or operate on slices. The old locations can and will **NOT** be zeroized.
|
||||
pub fn value_mut(&mut self) -> &mut C {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize + Debug> Debug for Confidential<C> {
|
||||
#[allow(unreachable_code)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// do NOT leak secrets in production builds
|
||||
#[cfg(not(debug_assertions))]
|
||||
return write!(f, "Confidential(***)");
|
||||
|
||||
let mut b = f.debug_tuple("Confidential");
|
||||
b.field(&self.0);
|
||||
b.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize> From<C> for Confidential<C> {
|
||||
fn from(v: C) -> Confidential<C> {
|
||||
Confidential(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize> Zeroize for Confidential<C> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize> Drop for Confidential<C> {
|
||||
fn drop(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user