mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b8114e6f | ||
|
|
c4e4b926b4 | ||
|
|
6324f62da7 | ||
|
|
3ade063ea2 | ||
|
|
92a3b30323 | ||
|
|
673ff375d9 | ||
|
|
4e2ebe0370 | ||
|
|
78b053326c | ||
|
|
ab06a5d88a | ||
|
|
a0a71efde0 | ||
|
|
b4b2202ff5 | ||
|
|
a26f61c4fe | ||
|
|
a28b396d9e | ||
|
|
0012eaf68e | ||
|
|
c22d282e67 | ||
|
|
071522f7d2 | ||
|
|
ae7217806b | ||
|
|
9f6150db34 | ||
|
|
b39bdfbf6e | ||
|
|
71fe58111c | ||
|
|
5e46632767 | ||
|
|
1bd64f5b0f | ||
|
|
d311506dc4 | ||
|
|
08e4520a4f | ||
|
|
46fd42af0c | ||
|
|
a8b0d7ace8 | ||
|
|
a29b3c8997 | ||
|
|
27dce3317a | ||
|
|
4382901daa | ||
|
|
bcbb6fcae6 | ||
|
|
b16a6d4fe1 | ||
|
|
0d15a07c0a | ||
|
|
5394cd363c | ||
|
|
0906293cd8 | ||
|
|
a9e13a2d69 | ||
|
|
ee2c6d4160 | ||
|
|
455ad953a9 | ||
|
|
02a0d12988 | ||
|
|
be47b51890 | ||
|
|
2677a4182d | ||
|
|
812df795c3 | ||
|
|
a0d6edf03c | ||
|
|
81920f7cfd | ||
|
|
40dd63e2ac | ||
|
|
a8579a0727 | ||
|
|
2ab27bdaf2 | ||
|
|
03fef264bd | ||
|
|
927a48e607 | ||
|
|
fbcb3f384a | ||
|
|
c7fe21b019 | ||
|
|
50a4740443 | ||
|
|
164d4817ec | ||
|
|
abec41f514 | ||
|
|
fcb503ea3c | ||
|
|
2eea614bbf | ||
|
|
3a13cb43f2 | ||
|
|
e8fca95592 | ||
|
|
b5604850ab |
@@ -11,3 +11,11 @@ tab_width = 8
|
||||
|
||||
[*.sh]
|
||||
shell_variant = bash # used by `shfmt`
|
||||
|
||||
[*.y{a,}ml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
@@ -5,7 +5,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Alexandra Winter
|
||||
- Alexey Ishchuk
|
||||
- Andreas Herrmann
|
||||
- Andre Wild
|
||||
- André Wild
|
||||
- Antoinette Kaschner
|
||||
- Arnd Bergmann
|
||||
- Axel Wirbser
|
||||
@@ -19,6 +19,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Clemens von Mann
|
||||
- Colin Walters
|
||||
- Dan Horak
|
||||
- Dan Horák
|
||||
- Despina Papadopoulou
|
||||
- Dimitri John Ledkov
|
||||
- Eberhard Pasch
|
||||
@@ -53,7 +54,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Ingo Franzki
|
||||
- Ingo Tuchscherer
|
||||
- Jan Glauber
|
||||
- Jan Hoeppner
|
||||
- Jan Höppner
|
||||
- Jan Willeke
|
||||
- Jason J. Herne
|
||||
- Javier Martinez Canillas
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,6 +1,39 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __v2.20.0 (2021-02-04)__
|
||||
* __v2.21.0 (2022-04-20)__
|
||||
|
||||
For Linux kernel version: 5.17
|
||||
|
||||
Add new tools / libraries:
|
||||
- libcpumf: Create library libcpumf for CPU Measurement functions
|
||||
|
||||
Changes of existing tools:
|
||||
- chreipl-fcp-mpath: bundle a pre-cooked version of the manpage for build
|
||||
environments without access to `pandoc`
|
||||
- dbginfo.sh: Add multipath info to map paths to FC addressing and prio group
|
||||
- dbginfo.sh: Collect config files of systemd-modules-load.service
|
||||
- dbginfo.sh: Sort list of environment variables for readability
|
||||
- dbginfo.sh: Replace "which" by builtin command "type"
|
||||
- dbginfo.sh: Rework script formatting (indents, order)
|
||||
- dbginfo.sh: Update sysfs collection (excludes, messages)
|
||||
- genprotimg: Add Protected Virtualization (PV) dump support
|
||||
- genprotimg: Remove DigiCert root CA pinning
|
||||
- lszcrypt: Add CEX8S support
|
||||
- zcryptctl: Add control domain handling
|
||||
- zcryptstats: Add CEX8 support
|
||||
- zipl: Allow optional entries that are left out when files are missing
|
||||
- zipl: make IPL sections defined with BLS to inherit a target field
|
||||
- zpcictl: Add option to trigger firmware reset
|
||||
|
||||
Bug Fixes:
|
||||
- cpictl: Handle excessive kernel version numbers
|
||||
- dbginfo.sh: Collect all places where modprobe.d config files could exist
|
||||
- fdasd: Fix endless menu loop on EOF
|
||||
- zdump/dfi: Fix segfault due to double free
|
||||
- zdump: Fix /dev/mem reading
|
||||
- zpcictl: Fix race of SCLP reset and Linux recovery
|
||||
|
||||
* __v2.20.0 (2022-02-04)__
|
||||
|
||||
For Linux kernel version: 5.16
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -10,7 +10,7 @@ include common.mak
|
||||
#
|
||||
BASELIB_DIRS = libutil libseckey
|
||||
LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
libkmipclient
|
||||
libkmipclient libcpumf
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
|
||||
@@ -478,5 +478,6 @@ the different tools are provided:
|
||||
Summarized: chreipl-fcp-mpath requires GNU Bash, GNU Core Utilities,
|
||||
util-linux, udev, and multipath-tools. When using `HAVE_DRACUT=1` with the
|
||||
make invocation, it also requires dracut. When using `ENABLE_DOC=1` with the
|
||||
make invocation to build a man page and render the README.md as HTML, make
|
||||
further requires pandoc and GNU awk for the build process.
|
||||
make invocation to build a fresh man page (instead of using the pre-cooked
|
||||
version) and render the README.md as HTML, make further requires pandoc and
|
||||
GNU awk for the build process.
|
||||
|
||||
1
chreipl-fcp-mpath/.chreipl-fcp-mpath.7.cksum
Normal file
1
chreipl-fcp-mpath/.chreipl-fcp-mpath.7.cksum
Normal file
@@ -0,0 +1 @@
|
||||
8e604dab39577678bc9f24bfe5e2ee35354b2f4b1759edd16093157b1ecce9d3 README.md
|
||||
1
chreipl-fcp-mpath/.gitignore
vendored
1
chreipl-fcp-mpath/.gitignore
vendored
@@ -9,4 +9,3 @@
|
||||
/README.html
|
||||
/README.pdf
|
||||
/chreipl-fcp-mpath.md
|
||||
/chreipl-fcp-mpath.7
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
# - chreipl-fcp-mpath.mak
|
||||
# bash:
|
||||
# - bash
|
||||
# GNU coreutils:
|
||||
# - sha256sum
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# GNU awk:
|
||||
# - gawk
|
||||
@@ -100,20 +102,41 @@ chreiplzfcpmp-doc-man-meta = \
|
||||
-V footer="s390-tools $(S390_TOOLS_RELEASE)"
|
||||
|
||||
chreipl-fcp-mpath.7: PANDOCFLAGS += $(chreiplzfcpmp-doc-man-meta)
|
||||
chreipl-fcp-mpath.7: .chreipl-fcp-mpath.7.cksum
|
||||
|
||||
.INTERMEDIATE: chreipl-fcp-mpath.md
|
||||
chreipl-fcp-mpath.md: README.md
|
||||
gawk -- '/NOT-IN-MAN \{/,/NOT-IN-MAN \}/ { next } { print }' $(<) > $(@)
|
||||
|
||||
.chreipl-fcp-mpath.7.cksum: README.md
|
||||
sha256sum $(<) > $(@)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-doc-clean
|
||||
chreipl-fcp-mpath-doc-clean:
|
||||
rm -f README.html README.pdf chreipl-fcp-mpath.md chreipl-fcp-mpath.7
|
||||
rm -f README.html README.pdf chreipl-fcp-mpath.md
|
||||
|
||||
chreipl-fcp-mpath: chreipl-fcp-mpath-doc
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-doc-clean
|
||||
|
||||
else # $(ENABLE_DOC) != 1
|
||||
|
||||
# We bundle a pre-cooked man page with the source-code so that distributions
|
||||
# don't need `pandoc` in order to be able to ship the man page. As of this
|
||||
# writing multiple distributions don't have a packaged version of it.
|
||||
#
|
||||
# In order to remember to regenerate this pre-cooked version whenever the
|
||||
# README.md is changed, we also generate a checksum of the README.md, bundle
|
||||
# that as well, and compare that whenever `make` is called. This way, the
|
||||
# freshness of the man page can be checked, even if `pandoc` is not available,
|
||||
# or ENABLE_DOC disabled.
|
||||
|
||||
MANPAGE_FRESH := $(shell sha256sum --check .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0)
|
||||
ifeq ($(MANPAGE_FRESH),0)
|
||||
$(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`)
|
||||
endif
|
||||
|
||||
endif # $(ENABLE_DOC) == 1
|
||||
|
||||
#
|
||||
## Install
|
||||
#
|
||||
@@ -180,8 +203,6 @@ chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-dracut-config
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_DOC),1)
|
||||
|
||||
# chreipl-fcp-mpath: install man page
|
||||
INSTDIRS += $(MANDIR)
|
||||
$(DESTDIR)$(MANDIR)/man7: install_dirs
|
||||
@@ -194,8 +215,6 @@ chreipl-fcp-mpath-install-man-page: chreipl-fcp-mpath.7
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
## Utility
|
||||
#
|
||||
|
||||
@@ -120,18 +120,20 @@ in place, but the toolset has some software dependencies besides the
|
||||
requirements in section [Requirements](#requirements):
|
||||
|
||||
- GNU Bash;
|
||||
- GNU Core Utilities (mktemp, readlink, sync, truncate);
|
||||
- GNU Core Utilities (mktemp, readlink, sync, truncate, sha256sum);
|
||||
- util-linux (flock, hexdump, logger);
|
||||
- udev / systemd-udev;
|
||||
- multipath-tools.
|
||||
|
||||
To make use of the optional dracut configuration you need: dracut.
|
||||
|
||||
To build and install the documentation (man page) you need:
|
||||
To build a fresh version of the documentation (man page) you need:
|
||||
|
||||
- pandoc;
|
||||
- GNU Core Utilities (date);
|
||||
- GNU awk.
|
||||
- GNU awk;
|
||||
|
||||
otherwise the pre-cooked version shipped with the source will be used.
|
||||
|
||||
INSTALLATION
|
||||
============
|
||||
@@ -160,7 +162,7 @@ steps and copies the resulting components to their final destination.
|
||||
| Option | Values | Default | Effect
|
||||
| :----- | :----: | :-----: | :-----
|
||||
| HAVE_DRACUT | 0, 1 | 0 | Install a dracut configuration file that includes **chreipl-fcp-mpath** in the initial ramdisks built with **dracut**.
|
||||
| ENABLE_DOC | 0, 1 | 0 | Build and install a man page for **chreipl-fcp-mpath**.
|
||||
| ENABLE_DOC | 0, 1 | 0 | Build a fresh version of the man page for **chreipl-fcp-mpath**.
|
||||
|
||||
Specify any options as arguments for both the **make** and **make install**
|
||||
command as shown in the following example:
|
||||
@@ -195,8 +197,8 @@ the boot record to find the new initial ramdisk.
|
||||
|
||||
~ # zipl
|
||||
|
||||
With dracut and documentation enabled, **make install** deploys the following
|
||||
files to these default locations:
|
||||
With dracut enabled, **make install** deploys the following files to these
|
||||
default locations:
|
||||
|
||||
/usr/lib/chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh
|
||||
/usr/lib/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
|
||||
183
chreipl-fcp-mpath/chreipl-fcp-mpath.7
Normal file
183
chreipl-fcp-mpath/chreipl-fcp-mpath.7
Normal file
@@ -0,0 +1,183 @@
|
||||
.\" Automatically generated by Pandoc 2.16.1
|
||||
.\"
|
||||
.TH "CHREIPL-FCP-MPATH" "7" "2022-02-24" "s390-tools 2.20.0-build-20220224" "Administrator Manual"
|
||||
.hy
|
||||
.SH NAME
|
||||
.PP
|
||||
chreipl-fcp-mpath - use multipath information for re-IPL path failover
|
||||
on a running Linux instance
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI
|
||||
volume uses exactly one path to the volume.
|
||||
If this path is unavailable, the IPL fails.
|
||||
.PP
|
||||
The \f[B]chreipl-fcp-mpath\f[R] toolset monitors \f[B]udev\f[R] events
|
||||
about paths to the re-IPL volume.
|
||||
If the currently configured re-IPL path becomes unavailable, the toolset
|
||||
checks for operational paths to the same volume.
|
||||
If available, it reconfigures the re-IPL settings to use an operational
|
||||
path.
|
||||
.PP
|
||||
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite
|
||||
path failures on a running Linux instance if at least one path to the
|
||||
re-IPL volume remains operational.
|
||||
.PP
|
||||
\f[B]Chreipl-fcp-mpath\f[R] requires \f[B]udev\f[R],
|
||||
\f[B]multipathd\f[R] and \f[B]dm-multipath\f[R].
|
||||
Once installed, the toolset runs automatically and autonomously.
|
||||
No user intervention is possible or required.
|
||||
.PP
|
||||
Other than installing the toolset, there is no user interface for
|
||||
\f[B]chreipl-fcp-mpath\f[R].
|
||||
.SS Requirements
|
||||
.PP
|
||||
The \f[B]chreipl-fcp-mpath\f[R] tool has the following requirements on
|
||||
the Linux instance that is being monitored:
|
||||
.IP \[bu] 2
|
||||
The Linux instance must have started successfully, during IPL.
|
||||
.IP \[bu] 2
|
||||
The running Linux instance must use \f[B]dm-multipath\f[R] and
|
||||
\f[B]multipathd\f[R] for the configured re-IPL volume - a volume that
|
||||
contains a zipl boot record and has one of its paths used in the re-IPL
|
||||
configuration.
|
||||
.IP \[bu] 2
|
||||
\f[B]udev\f[R] must run.
|
||||
.IP \[bu] 2
|
||||
The toolset must observe at least one event about the configured re-IPL
|
||||
path.
|
||||
Examples for such events are: the SCSI disk comes online, or a path of
|
||||
the corresponding multipath device goes down or comes back online.
|
||||
.RS 2
|
||||
.IP \[bu] 2
|
||||
The WWID of the re-IPL volume must not change while the Linux instance
|
||||
is running.
|
||||
.RE
|
||||
.IP \[bu] 2
|
||||
When the configured re-IPL path becomes unavailable while the Linux
|
||||
instance is running, at least one operational path to the re-IPL volume
|
||||
must be available, or must become available.
|
||||
If no such path is available when the Linux instance is rebooted, the
|
||||
re-IPL path is not changed.
|
||||
.IP \[bu] 2
|
||||
The tool assumes that any manually reconfigured re-IPL device is valid
|
||||
and operational.
|
||||
.RS 2
|
||||
.PP
|
||||
The tool treats a newly configured re-IPL device like the initially
|
||||
configured re-IPL device.
|
||||
In particular, if the newly configured re-IPL device fulfills the
|
||||
requirements of the tool, re-IPL path failover takes place if the
|
||||
configured re-IPL path becomes unavailable.
|
||||
.RE
|
||||
.SS Caution with Manual Changes to the Configured re-IPL Target
|
||||
.PP
|
||||
\f[B]chreipl-fcp-mpath\f[R] is designed to accept operator-inititated
|
||||
changes of the re-IPL device.
|
||||
However, concurrent changes by the operator and tool driven changes can
|
||||
result in the operator change being overwritten.
|
||||
.PP
|
||||
To avoid this problem, change the re-IPL device only during steady-state
|
||||
operations, when no path events happen.
|
||||
Alternatively, make sure that no events are processed while you change
|
||||
the device.
|
||||
See EXAMPLES for one way to suspend event processing.
|
||||
.SH MESSAGES
|
||||
.PP
|
||||
During monitoring and event processing, \f[B]chreipl-fcp-mpath\f[R]
|
||||
writes messages to the syslog.
|
||||
.PP
|
||||
When the configured re-IPL path is changed to a different path to the
|
||||
same volume (priority \f[I]daemon.notice\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
Changed re-IPL path to: <device-bus-id>:<wwpn>:<lun>.
|
||||
.RE
|
||||
.PP
|
||||
When a path event indicates that the last available path has become
|
||||
non-operational (priority \f[I]daemon.alert\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
The re-IPL device cannot be changed because no operational path to the
|
||||
re-IPL volume remains.
|
||||
The next re-IPL might fail unless you re-attach or enable at least one
|
||||
valid path to the re-IPL volume.
|
||||
.RE
|
||||
.PP
|
||||
When changing the configured re-IPL device failed because of an error
|
||||
with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
Changing the re-IPL device failed.
|
||||
The current re-IPL settings might be inconsistent.
|
||||
Check and correct the settings (see the README.md of chreipl-fcp-mpath)
|
||||
to make sure that the current re-IPL device is valid.
|
||||
.RE
|
||||
.PP
|
||||
A failure to change the re-IPL device can indicate an inconsistent
|
||||
setting that cannot be corrected automatically by
|
||||
\f[B]chreipl-fcp-mpath\f[R].
|
||||
As a result, the next re-IPL might fail or might not use the intended
|
||||
re-IPL device.
|
||||
.PP
|
||||
You can use the following tools to check and correct the current
|
||||
settings:
|
||||
.IP \[bu] 2
|
||||
\f[B]lsreipl\f[R] to confirm that the intended re-IPL device is
|
||||
configured;
|
||||
.IP \[bu] 2
|
||||
\f[B]chreipl\f[R] to change the re-IPL device;
|
||||
.IP \[bu] 2
|
||||
\f[B]lszfcp\f[R] to inspect the state of available paths to the re-IPL
|
||||
device.
|
||||
.SH EXAMPLES
|
||||
.SS Manual Changes to the Configured re-IPL Device
|
||||
.PP
|
||||
As outlined in DESCRIPTION, be cautious when manually changing the
|
||||
configured re-IPL device.
|
||||
Assure that your reconfiguration actions do not collide with concurrent
|
||||
automatic event processing by \f[B]chreipl-fcp-mpath\f[R].
|
||||
You can avoid such collisions, by stopping event processing, making your
|
||||
changes, and then re-enabling event processing.
|
||||
You need \f[I]root\f[R] privileges for running the commands in the
|
||||
following example:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # udevadm settle
|
||||
\[ti] # udevadm control --stop-exec-queue
|
||||
\[ti] # chreipl ...
|
||||
\[ti] # udevadm control --start-exec-queue
|
||||
\f[R]
|
||||
.fi
|
||||
.SS Listing messages with journalctl
|
||||
.PP
|
||||
If your Linux instance includes \f[B]journalctl\f[R], use the following
|
||||
command to list all messages that are issued by
|
||||
\f[B]chreipl-fcp-mpath\f[R]:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # journalctl -t chreipl-fcp-mpath
|
||||
\f[R]
|
||||
.fi
|
||||
.PP
|
||||
To list only messages that were issued since the last IPL, use this
|
||||
command:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # journalctl -t chreipl-fcp-mpath -b
|
||||
\f[R]
|
||||
.fi
|
||||
.SH REPORTING BUGS
|
||||
.PP
|
||||
Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in
|
||||
\f[B]chreipl-fcp-mpath\f[R]: s390-tools
|
||||
Issues (https://github.com/ibm-s390-linux/s390-tools/issues).
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1),
|
||||
\f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8),
|
||||
\f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8),
|
||||
\f[B]zipl\f[R](8)
|
||||
20
common.mak
20
common.mak
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 20
|
||||
RELEASE = 21
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -59,11 +59,8 @@ define cmd_define
|
||||
endef
|
||||
|
||||
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
|
||||
$(eval $(call cmd_define, LINK," LINK ",$(CROSS_COMPILE)gcc))
|
||||
$(eval $(call cmd_define, LD," LD ",$(CROSS_COMPILE)ld))
|
||||
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
||||
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
|
||||
$(eval $(call cmd_define, LINKXX," LINKXX ",$(CROSS_COMPILE)g++))
|
||||
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
|
||||
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
|
||||
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
|
||||
@@ -84,6 +81,8 @@ CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
|
||||
SKIP = echo " SKIP $(call reldir) due to"
|
||||
|
||||
INSTALL = install
|
||||
LINK = $(CC)
|
||||
LINKXX = $(CXX)
|
||||
CP = cp
|
||||
ifneq ("${V}","1")
|
||||
MAKEFLAGS += --quiet
|
||||
@@ -92,13 +91,12 @@ ifneq ("${V}","1")
|
||||
else
|
||||
echocmd=
|
||||
endif
|
||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
|
||||
ifeq ("${W}","1")
|
||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security -Wextra
|
||||
else
|
||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
|
||||
DEFAULT_CFLAGS += -Wextra -Wshadow -Wundef -Wuninitialized -Wdouble-promotion -Wconversion
|
||||
endif
|
||||
ifeq ("${D}","1")
|
||||
DEFAULT_CFLAGS += -Og
|
||||
DEFAULT_CFLAGS += -Og -g3 -ggdb3
|
||||
else
|
||||
DEFAULT_CFLAGS += -O3
|
||||
endif
|
||||
@@ -252,11 +250,9 @@ export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS CXXFLAGS \
|
||||
|
||||
ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
||||
NO_PIE_CFLAGS := -fno-pie
|
||||
NO_PIE_LINKFLAGS := -no-pie
|
||||
NO_PIE_LDFLAGS := -no-pie
|
||||
else
|
||||
NO_PIE_CFLAGS :=
|
||||
NO_PIE_LINKFLAGS :=
|
||||
NO_PIE_LDFLAGS :=
|
||||
endif
|
||||
|
||||
@@ -371,6 +367,10 @@ $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp
|
||||
$(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a
|
||||
.PHONY: $(rootdir)/libvmcp
|
||||
|
||||
$(rootdir)/libcpumf/libcpumf.a: $(rootdir)/libcpumf
|
||||
$(MAKE) -C $(rootdir)/libcpumf/ libcpumf.a
|
||||
.PHONY: $(rootdir)/libcpumf
|
||||
|
||||
$(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb
|
||||
$(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so
|
||||
.PHONY: $(rootdir)/libekmfweb
|
||||
|
||||
@@ -5,7 +5,7 @@ MAN_FILES = lscpumf.1 chcpumf.8 lshwc.1
|
||||
|
||||
all: $(BIN_FILES)
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
libs = $(rootdir)/libutil/libutil.a $(rootdir)/libcpumf/libcpumf.a
|
||||
|
||||
lscpumf: lscpumf.o $(libs)
|
||||
chcpumf: chcpumf.o $(libs)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* chcpumf - Change CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
* 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.
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
static int verbose;
|
||||
static unsigned int verbose;
|
||||
static unsigned long min_sdb, max_sdb;
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
@@ -85,55 +85,30 @@ static long parse_buffersize(char *string)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static void read_sfb(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
unsigned long cur_min_sdb, cur_max_sdb;
|
||||
FILE *fp;
|
||||
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) {
|
||||
fclose(fp);
|
||||
errx(EXIT_FAILURE, "Can not parse file " PERF_SFB_SIZE);
|
||||
} else {
|
||||
if (*min == 0)
|
||||
*min = cur_min_sdb;
|
||||
if (*max == 0)
|
||||
*max = cur_max_sdb;
|
||||
}
|
||||
fclose(fp);
|
||||
if (*min >= *max)
|
||||
errx(EXIT_FAILURE,
|
||||
"The specified maximum must be greater than the minimum");
|
||||
}
|
||||
|
||||
static int write_sfb(unsigned long min, unsigned long max)
|
||||
static int write_sfb(unsigned int min, unsigned int max)
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
char text[64];
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "w");
|
||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "w");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
||||
err(EXIT_FAILURE, S390_CPUMSF_BUFFERSZ);
|
||||
snprintf(text, sizeof(text), "%u,%u", min, max);
|
||||
len = strlen(text) + 1;
|
||||
if (fwrite(text, 1, len, fp) != len) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (fclose(fp)) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (verbose && rc != EXIT_FAILURE)
|
||||
warnx("Sampling buffer sizes:\n"
|
||||
" Minimum:%7ld sample-data-blocks\n"
|
||||
" Maximum:%7ld sample-data-blocks\n",
|
||||
" Minimum:%7d sample-data-blocks\n"
|
||||
" Maximum:%7d sample-data-blocks\n",
|
||||
min, max);
|
||||
return rc;
|
||||
}
|
||||
@@ -183,15 +158,21 @@ static int parse_args(int argc, char **argv)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct stat sbuf;
|
||||
unsigned long my_min, my_max;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
parse_args(argc, argv);
|
||||
if (stat(PERF_PATH PERF_SF, &sbuf))
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
if (!libcpumf_have_sfb())
|
||||
errx(EXIT_FAILURE,
|
||||
"No CPU-measurement sampling facility detected");
|
||||
read_sfb(&min_sdb, &max_sdb);
|
||||
libcpumf_sfb_info(&my_min, &my_max);
|
||||
if (!min_sdb)
|
||||
min_sdb = my_min;
|
||||
if (!max_sdb)
|
||||
max_sdb = my_max;
|
||||
return write_sfb(min_sdb, max_sdb);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Defines for CPU Measurement Facility Characteristics
|
||||
*
|
||||
* 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 DEFINES_H
|
||||
#define DEFINES_H
|
||||
|
||||
#define PERF_SFB_SIZE "/sys/module/kernel/parameters/cpum_sfb_size"
|
||||
#define PERF_PATH "/sys/bus/event_source/devices/"
|
||||
#define PERF_SF "cpum_sf"
|
||||
#define PERF_CF "cpum_cf"
|
||||
|
||||
#endif
|
||||
195
cpumf/lscpumf.c
195
cpumf/lscpumf.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* lscpumf - Show CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
* 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.
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define ACTION_NONE 0
|
||||
#define ACTION_INFO 1
|
||||
@@ -45,9 +45,6 @@ static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
||||
#define PER_SDBT_SIZE 511
|
||||
|
||||
/* File names to read data from */
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
#define CPUMF_CF_TYPE "/sys/devices/cpum_cf/type"
|
||||
#define CPUMF_SF_TYPE "/sys/devices/cpum_sf/type"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
@@ -92,19 +89,18 @@ static char prefix[32]; /* Counter prefix */
|
||||
static bool show_names;
|
||||
|
||||
static struct cpumf_info {
|
||||
unsigned int first_vn; /* Counter facility first version nr */
|
||||
unsigned int second_vn; /* Counter facility second version nr */
|
||||
unsigned int authorization; /* Counter facility authorization */
|
||||
float version;
|
||||
int first_vn; /* Counter facility first version nr */
|
||||
int second_vn; /* Counter facility second version nr */
|
||||
int authorization; /* Counter facility authorization */
|
||||
unsigned long min_rate; /* Minimum sampling rate */
|
||||
unsigned long max_rate; /* Maximum sampling rate */
|
||||
unsigned long cpu_speed; /* CPU Cycles per micro second */
|
||||
unsigned int basic_sample_sz; /* # of Bytes per basic sample */
|
||||
unsigned int diag_sample_sz; /* # of bytes per diagnostic sample */
|
||||
unsigned char have_counter; /* CPUM counter facility detected */
|
||||
unsigned char have_samples; /* CPUM sampling facility detected */
|
||||
unsigned int min_sfb; /* Minimum sampling buffer size */
|
||||
unsigned int max_sfb; /* Maximum sampling buffer size */
|
||||
int basic_sample_sz; /* # of Bytes per basic sample */
|
||||
int diag_sample_sz; /* # of bytes per diagnostic sample */
|
||||
bool have_counter; /* CPUM counter facility detected */
|
||||
bool have_samples; /* CPUM sampling facility detected */
|
||||
unsigned long min_sfb; /* Minimum sampling buffer size */
|
||||
unsigned long max_sfb; /* Maximum sampling buffer size */
|
||||
unsigned short machine_type; /* Machine Type */
|
||||
} cpumf;
|
||||
|
||||
@@ -2565,26 +2561,12 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
* If perf_pmu_register() kernel function assigned any other (higher) type
|
||||
* number, set the prefix to <type-nr>:
|
||||
*/
|
||||
static int read_cpumf_type(const char *filename, const char *type)
|
||||
static void set_prefix(int nr)
|
||||
{
|
||||
int nr, rc = EXIT_FAILURE;
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
warnx("No CPU-measurement %s facility detected", type);
|
||||
return rc;
|
||||
}
|
||||
if (fscanf(fp, "%d", &nr) != 1) {
|
||||
warnx("Can not parse file %s", filename);
|
||||
} else {
|
||||
rc = EXIT_SUCCESS;
|
||||
if (nr == PERF_TYPE_RAW)
|
||||
strcat(prefix, "r");
|
||||
else
|
||||
snprintf(prefix, sizeof prefix, "%d:", nr);
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
if (nr == PERF_TYPE_RAW)
|
||||
strcat(prefix, "r");
|
||||
else
|
||||
snprintf(prefix, sizeof(prefix), "%d:", nr);
|
||||
}
|
||||
|
||||
/* Parse tool parameters. In case of --help or --version, print
|
||||
@@ -2661,8 +2643,6 @@ static unsigned long div_ceil(unsigned long a, unsigned long b)
|
||||
|
||||
static void show_info(struct cpumf_info *p, int details)
|
||||
{
|
||||
struct stat sbuf;
|
||||
|
||||
if (!p->have_counter && !p->have_samples) {
|
||||
warnx("No CPU-measurement facilities detected");
|
||||
return;
|
||||
@@ -2672,7 +2652,7 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
if (details) {
|
||||
printf("----------------------------------------------"
|
||||
"----------------------------\n");
|
||||
printf("Version: %3.1f\n\n", p->version);
|
||||
printf("Version: %d.%d\n\n", p->first_vn, p->second_vn);
|
||||
printf("Authorized counter sets:\n");
|
||||
if (!p->authorization)
|
||||
printf(" None\n");
|
||||
@@ -2689,8 +2669,7 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
if (0x8000 & p->authorization)
|
||||
printf(" Coprocessor Group counter Set\n");
|
||||
printf("\nLinux perf event support: %s\n\n",
|
||||
(stat(PERF_PATH PERF_CF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_CF ")");
|
||||
!p->have_counter ? "No" : "Yes (PMU: cpum_cf)");
|
||||
|
||||
}
|
||||
} else
|
||||
@@ -2718,19 +2697,18 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
p->diag_sample_sz);
|
||||
|
||||
printf("\nLinux perf event support: %s\n\n",
|
||||
(stat(PERF_PATH PERF_SF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_SF ")");
|
||||
!p->have_samples ? "No" : "Yes (PMU: cpum_sf)");
|
||||
|
||||
printf("Current sampling buffer settings for %s:\n",
|
||||
PERF_SF);
|
||||
printf("Current sampling buffer settings for"
|
||||
" cpum_sf:\n");
|
||||
printf(" Basic-sampling mode\n");
|
||||
total = p->min_sfb + div_ceil(p->min_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
printf(" Minimum: %6d"
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Minimum: %6ld"
|
||||
" sample-data-blocks (%6s)\n", p->min_sfb, text);
|
||||
total = p->max_sfb + div_ceil(p->max_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
printf(" Maximum: %6d"
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Maximum: %6ld"
|
||||
" sample-data-blocks (%6s)\n\n", p->max_sfb,
|
||||
text);
|
||||
|
||||
@@ -2739,13 +2717,13 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
fdiag = div_ceil(p->diag_sample_sz, p->basic_sample_sz);
|
||||
total = fdiag * p->min_sfb
|
||||
+ div_ceil(p->min_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Minimum: %6ld"
|
||||
" sample-data-blocks (%6s)\n",
|
||||
fdiag * p->min_sfb, text);
|
||||
total = fdiag * p->max_sfb
|
||||
+ div_ceil(p->max_sfb * fdiag, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Maximum: %6ld"
|
||||
" sample-data-blocks (%6s)\n", fdiag * p->max_sfb,
|
||||
text);
|
||||
@@ -2755,27 +2733,6 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
warnx("No CPU-measurement sampling facility detected");
|
||||
}
|
||||
|
||||
/* Read CPU Measurement sampling facility device driver minimum and maximum
|
||||
* buffer size
|
||||
*/
|
||||
static int read_sfb(struct cpumf_info *p)
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (!fp) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fscanf(fp, "%d,%d", &p->min_sfb, &p->max_sfb) != 2) {
|
||||
warnx("Can not parse %s", PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set the counter name for z15 counter numbered 265. It is either named
|
||||
* DFLT_CCERROR or DFLT_CCFINISH, depending on the linux version. The
|
||||
* counter was renamed from CCERROR to CCFINISH in linux version 5.8.
|
||||
@@ -2803,81 +2760,23 @@ static void read_ccerror(struct counters *cp, size_t cp_cnt)
|
||||
/* Read allnecessary information from /sysfs file /proc/service_levels */
|
||||
static int read_info(void)
|
||||
{
|
||||
char *linep = NULL;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *slp;
|
||||
int rc;
|
||||
int rc = EXIT_FAILURE;
|
||||
|
||||
memset(&cpumf, 0, sizeof cpumf);
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
rc = sscanf(linep, "CPU-MF: Counter facility:"
|
||||
" version=%f authorization=%x",
|
||||
&cpumf.version, &cpumf.authorization);
|
||||
if (rc != 2) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_counter = 1;
|
||||
cpumf.first_vn = (int)cpumf.version;
|
||||
cpumf.second_vn = ((int)(10 * cpumf.version) % 10);
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" min_rate=%ld max_rate=%ld cpu_speed=%ld",
|
||||
&cpumf.min_rate, &cpumf.max_rate,
|
||||
&cpumf.cpu_speed);
|
||||
if (rc != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_samples = 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=basic sample_size=%u",
|
||||
&cpumf.basic_sample_sz);
|
||||
if (rc != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=diagnostic sample_size=%u",
|
||||
&cpumf.diag_sample_sz);
|
||||
if (rc != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cpumf.have_samples) {
|
||||
rc = read_sfb(&cpumf);
|
||||
if (rc == EXIT_FAILURE)
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_counter = libcpumf_cpumcf_info(&cpumf.first_vn,
|
||||
&cpumf.second_vn,
|
||||
&cpumf.authorization);
|
||||
cpumf.have_samples = libcpumf_cpumsf_info(&cpumf.min_rate,
|
||||
&cpumf.max_rate,
|
||||
&cpumf.cpu_speed,
|
||||
&cpumf.basic_sample_sz,
|
||||
&cpumf.diag_sample_sz);
|
||||
if (cpumf.have_samples)
|
||||
libcpumf_sfb_info(&cpumf.min_sfb, &cpumf.max_sfb);
|
||||
cpumf.machine_type = util_arch_machine_type();
|
||||
if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN) {
|
||||
if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN)
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
rc = EXIT_SUCCESS;
|
||||
out:
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
|
||||
else
|
||||
rc = EXIT_SUCCESS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3051,14 +2950,18 @@ int main(int argc, char **argv)
|
||||
case ACTION_CNT:
|
||||
case ACTION_CNTALL:
|
||||
all = ret == ACTION_CNTALL;
|
||||
ret = read_cpumf_type(CPUMF_CF_TYPE, "counter");
|
||||
if (ret == EXIT_SUCCESS)
|
||||
ret = libcpumf_pmutype(S390_CPUMF_CF);
|
||||
if (ret >= EXIT_SUCCESS) {
|
||||
set_prefix(ret);
|
||||
show_counter(all);
|
||||
}
|
||||
break;
|
||||
case ACTION_SAMPLE:
|
||||
ret = read_cpumf_type(CPUMF_SF_TYPE, "sampling");
|
||||
if (ret == EXIT_SUCCESS)
|
||||
ret = libcpumf_pmutype(S390_CPUMF_SF);
|
||||
if (ret >= EXIT_SUCCESS) {
|
||||
set_prefix(ret);
|
||||
show_sample();
|
||||
}
|
||||
break;
|
||||
case ACTION_NONE:
|
||||
case ACTION_INFO:
|
||||
|
||||
112
cpumf/lshwc.c
112
cpumf/lshwc.c
@@ -38,17 +38,18 @@
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#include "lshwc.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||
#define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max"
|
||||
#define MAXCTRS 512
|
||||
#define IOCTLSLEEP 60U
|
||||
|
||||
static unsigned int read_interval = IOCTLSLEEP, cfvn, csvn, authorization;
|
||||
static unsigned int read_interval = IOCTLSLEEP;
|
||||
static int cfvn, csvn, authorization;
|
||||
static unsigned long loop_count = 1;
|
||||
static unsigned char *ioctlbuffer;
|
||||
static bool allcpu;
|
||||
@@ -151,20 +152,6 @@ static unsigned long getnumber(char *word, char stopchar)
|
||||
return no;
|
||||
}
|
||||
|
||||
/* Remove all whitespace from string. */
|
||||
static void kill_whitespace(char *s)
|
||||
{
|
||||
char *cp = s;
|
||||
|
||||
for (; *s != '\0'; ++s) {
|
||||
if (isspace(*s))
|
||||
continue;
|
||||
if (isprint(*s))
|
||||
*cp++ = *s;
|
||||
}
|
||||
*cp = '\0';
|
||||
}
|
||||
|
||||
/* Read file to get all online CPUs */
|
||||
static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||||
{
|
||||
@@ -242,29 +229,34 @@ static char *show_ctrset(unsigned long set)
|
||||
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
{
|
||||
uint64_t *words = start->cpumask;
|
||||
unsigned long i, no_a, no_b;
|
||||
char *cp, *tokens[16]; /* Used to parse command line params */
|
||||
char cpubuf[256];
|
||||
unsigned int i, no_a, no_b;
|
||||
cpu_set_t cpulist;
|
||||
int rc;
|
||||
|
||||
CPU_ZERO(&cpulist);
|
||||
start->data_bytes = 0;
|
||||
if (parm)
|
||||
kill_whitespace(parm);
|
||||
if (!parm || *parm == ':') {
|
||||
/* No CPU list or just counter sets */
|
||||
if (!get_cpus(CPUS_ONLINE, cpubuf, sizeof(cpubuf)))
|
||||
exit(EXIT_FAILURE);
|
||||
if (parm)
|
||||
strcat(cpubuf, parm);
|
||||
parm = cpubuf;
|
||||
start->counter_sets = S390_HWCTR_ALL; /* Default all counter sets */
|
||||
|
||||
if (parm) { /* CPU list with optional counter set */
|
||||
char *cp = strchr(parm, ':');
|
||||
|
||||
if (cp) { /* Handle counter set */
|
||||
*cp = '\0';
|
||||
start->counter_sets = parse_ctrset(++cp);
|
||||
}
|
||||
|
||||
if (strlen(parm) > 0) /* Handle CPU list */
|
||||
rc = libcpumf_cpuset(parm, &cpulist);
|
||||
else
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||
if (rc)
|
||||
errx(EXIT_FAILURE, "Cannot use CPU list %s", parm);
|
||||
} else { /* No CPU list and no counter sets */
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||
if (rc)
|
||||
err(EXIT_FAILURE, "Cannot read file " S390_CPUS_ONLINE);
|
||||
}
|
||||
|
||||
cp = strchr(parm, ':');
|
||||
if (cp) { /* Handle counter set */
|
||||
*cp = '\0';
|
||||
start->counter_sets = parse_ctrset(++cp);
|
||||
} else {
|
||||
start->counter_sets = S390_HWCTR_ALL;
|
||||
}
|
||||
/* Check with authorized counter sets */
|
||||
if ((start->counter_sets & authorization) != start->counter_sets) {
|
||||
unsigned int noton = ~(start->counter_sets & authorization);
|
||||
@@ -276,19 +268,10 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
show_ctrset(noton));
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||
++i, parm = NULL) {
|
||||
cp = strchr(tokens[i], '-'); /* Range character? */
|
||||
if (cp) {
|
||||
no_a = getnumber(tokens[i], *cp);
|
||||
no_b = getnumber(++cp, '\0');
|
||||
} else {
|
||||
no_b = getnumber(tokens[i], '\0');
|
||||
no_a = no_b;
|
||||
}
|
||||
if (!check_set(no_a, no_b, start->counter_sets))
|
||||
errx(EXIT_FAILURE, "Invalid CPU list %s", tokens[i]);
|
||||
}
|
||||
for (rc = 0; rc < CPU_SETSIZE; ++rc)
|
||||
if (CPU_ISSET(rc, &cpulist))
|
||||
if (!check_set(rc, rc, start->counter_sets))
|
||||
errx(EXIT_FAILURE, "Invalid CPU %d", rc);
|
||||
|
||||
/* Convert the CPU list to a bitmask for kernel cpumask_t */
|
||||
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
||||
@@ -632,37 +615,6 @@ static int do_it(char *s)
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Read counter first and second version number */
|
||||
static bool get_cvn(void)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool good = false;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *slp;
|
||||
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return false;
|
||||
}
|
||||
while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
int rc;
|
||||
|
||||
rc = sscanf(linep, "CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
||||
&cfvn, &csvn, &authorization);
|
||||
good = rc == 3;
|
||||
if (!good)
|
||||
warnx("Cannot parse line %s", linep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
return good;
|
||||
}
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
@@ -746,7 +698,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
have_support();
|
||||
if (!get_cvn())
|
||||
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
||||
return EXIT_FAILURE;
|
||||
if (!check_setpossible())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -408,8 +408,10 @@ static int read_line(void)
|
||||
{
|
||||
bzero(line_buffer, LINE_LENGTH);
|
||||
line_ptr = line_buffer;
|
||||
if (!fgets(line_buffer, LINE_LENGTH, stdin))
|
||||
if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
|
||||
clearerr(stdin);
|
||||
return 0;
|
||||
}
|
||||
while (*line_ptr && !isgraph(*line_ptr))
|
||||
line_ptr++;
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ stage3b_reloc.elf:
|
||||
|
||||
%.elf: %.o
|
||||
case $* in \
|
||||
stage3a) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
||||
stage3b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
||||
stage3b_reloc) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||
stage3a) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
||||
stage3b) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
||||
stage3b_reloc) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||
esac; \
|
||||
$(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@
|
||||
@chmod a-x $@
|
||||
|
||||
@@ -87,13 +87,23 @@ CRLs. Optional.
|
||||
.TP
|
||||
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
||||
Specifies the root CA certificate for the verification. If omitted,
|
||||
the DigiCert root CA certificate installed on the system is used. Use
|
||||
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
|
||||
Do not require the host-key documents to be valid. For testing
|
||||
purposes, do not use for a production image. Optional.
|
||||
.TP
|
||||
\fB\-\-comm\-key\fR=\fI\,FILE\/\fR
|
||||
Specifies the encryption key you want to use for the PV guest dump. 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. Optional.
|
||||
.TP
|
||||
\fB\-\-enable\-pckmo\fR
|
||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||
functions. This is the default. Optional.
|
||||
@@ -105,21 +115,25 @@ functions. Optional.
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Prints version information, then exits.
|
||||
|
||||
.SH EXAMPLE
|
||||
.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
|
||||
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:
|
||||
.PP
|
||||
.Vb 1
|
||||
.EX
|
||||
\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||
.EE
|
||||
.Ve
|
||||
.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
|
||||
|
||||
@@ -23,6 +23,7 @@ BODY_FILE=$(mktemp)
|
||||
ISSUER_DN_FILE=$(mktemp)
|
||||
SUBJECT_DN_FILE=$(mktemp)
|
||||
DEF_ISSUER_DN_FILE=$(mktemp)
|
||||
CANONICAL_ISSUER_DN_FILE=$(mktemp)
|
||||
CRL_SERIAL_FILE=$(mktemp)
|
||||
|
||||
# Cleanup on exit
|
||||
@@ -30,7 +31,7 @@ cleanup()
|
||||
{
|
||||
rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \
|
||||
$ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \
|
||||
$CRL_SERIAL_FILE
|
||||
$CANONICAL_ISSUER_DN_FILE $CRL_SERIAL_FILE
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -47,13 +48,21 @@ set -e
|
||||
usage()
|
||||
{
|
||||
cat <<-EOF
|
||||
Usage: `basename $1` host-key-doc signing-key-cert [-c CA-cert] [-r CRL]
|
||||
Usage: `basename $1` [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
|
||||
|
||||
Verify an IBM Secure Execution host key document against
|
||||
a signing key.
|
||||
|
||||
Options:
|
||||
-d disable default issuer check of host-key-doc
|
||||
-c CA-cert trusted CA certificate
|
||||
-r CRL list of revoked host-key-docs
|
||||
|
||||
Note that in order to have the full trust chain verified
|
||||
it is necessary to provide the issueing CA's certificate.
|
||||
it is necessary to provide the issuing CA's certificate.
|
||||
The default issuer check may be disabled if a non-default
|
||||
signing key certificate needs to be verified against the
|
||||
CA certificate.
|
||||
|
||||
EOF
|
||||
}
|
||||
@@ -121,20 +130,34 @@ default_issuer()
|
||||
commonName = International Business Machines Corporation
|
||||
countryName = US
|
||||
localityName = Poughkeepsie
|
||||
organizationalUnitName = IBM Z Host Key Signing Service
|
||||
organizationalUnitName = Key Signing Service
|
||||
organizationName = International Business Machines Corporation
|
||||
stateOrProvinceName = New York
|
||||
EOF
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
# As organizationalUnitName can have an arbitrary prefix but must
|
||||
# end with "Key Signing Service" let's normalize the OU name by
|
||||
# stripping off the prefix
|
||||
verify_default_issuer()
|
||||
{
|
||||
default_issuer > $DEF_ISSUER_DN_FILE
|
||||
|
||||
if ! diff $ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
|
||||
$ISSUER_DN_FILE > $CANONICAL_ISSUER_DN_FILE
|
||||
|
||||
if ! diff $CANONICAL_ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
then
|
||||
echo Incorrect default issuer >&2 && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
{
|
||||
if [ $1 -eq 1 ]
|
||||
then
|
||||
verify_default_issuer
|
||||
fi
|
||||
|
||||
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
||||
then
|
||||
@@ -196,14 +219,16 @@ check_file()
|
||||
# check args
|
||||
CRL_FILE=
|
||||
CA_FILE=
|
||||
CHECK_DEFAULT_ISSUER=1
|
||||
|
||||
args=$(getopt -qu "r:c:h" $*)
|
||||
args=$(getopt -qu "dr:c:h" $*)
|
||||
if [ $? = 0 ]
|
||||
then
|
||||
set -- $args
|
||||
while [ $1 != "" ]
|
||||
do
|
||||
case $1 in
|
||||
-d) CHECK_DEFAULT_ISSUER=0; shift;;
|
||||
-r) CRL_FILE=$2; shift 2;;
|
||||
-c) CA_FILE=$2; shift 2;;
|
||||
-h) usage $0; exit 0;;
|
||||
@@ -244,7 +269,7 @@ exit 1
|
||||
# Verify the issuer
|
||||
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
# Verify dates
|
||||
verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate)
|
||||
@@ -261,7 +286,7 @@ then
|
||||
echo -n "CRL "
|
||||
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL'
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
*/
|
||||
#define PV_CERTS_SECURITY_LEVEL 2
|
||||
|
||||
/* SKID for DigiCert Assured ID Root CA */
|
||||
#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F"
|
||||
|
||||
union ecdh_pub_key {
|
||||
struct {
|
||||
uint8_t x[80];
|
||||
|
||||
@@ -24,11 +24,15 @@
|
||||
#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_PCKM_ECC (1ULL << 5) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES (1ULL << 6) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA (1ULL << 7) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_NO_DECRYPTION (1ULL << 28) /* prevent Ultravisor decryption during unpack operation */
|
||||
#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 */
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -64,13 +64,17 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
{
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
if (args->pcf && args->allow_pckmo != PV_NOT_SET) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-pcf' and '--(enable|disable)-pckmo' options are mutually"
|
||||
" exclusive.\nUse 'genprotimg --help' for more information"));
|
||||
/* Check for mutually exclusive arguments */
|
||||
if (args->pcf && !(args->allow_pckmo == PV_NOT_SET &&
|
||||
args->allow_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;
|
||||
}
|
||||
|
||||
/* Check for unused arguments */
|
||||
if (args->unused_values->len > 0) {
|
||||
g_autofree gchar *unused = NULL;
|
||||
|
||||
@@ -88,6 +92,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for mandatory arguments */
|
||||
if (args->allow_dump == PV_TRUE && !args->cust_comm_key_path) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--allow-dump' 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"));
|
||||
@@ -111,7 +123,7 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n"
|
||||
_("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;
|
||||
@@ -155,14 +167,14 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
{
|
||||
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-comm-key"))
|
||||
args_option = &args->cust_comm_key_path;
|
||||
if (g_str_equal(option, "--x-header-key"))
|
||||
args_option = &args->cust_root_key_path;
|
||||
if (g_str_equal(option, "--x-pcf"))
|
||||
@@ -188,19 +200,6 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_enable_disable_flag(const gchar *option, const gchar *value G_GNUC_UNUSED,
|
||||
PvArgs *args, GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
if (g_str_equal(option, "--enable-pckmo"))
|
||||
args->allow_pckmo = PV_TRUE;
|
||||
else if (g_str_equal(option, "--disable-pckmo"))
|
||||
args->allow_pckmo = PV_FALSE;
|
||||
else
|
||||
g_assert_not_reached();
|
||||
|
||||
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)
|
||||
@@ -217,8 +216,51 @@ static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED,
|
||||
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->allow_##FLAG == PV_NOT_SET || \
|
||||
args->allow_##FLAG == VALUE)) { \
|
||||
g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \
|
||||
"'--enable-" #FLAG "' and '--disable-" #FLAG \
|
||||
"' are mutually exclusive"); \
|
||||
return FALSE; \
|
||||
} \
|
||||
args->allow_##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(FLAG, ENABLE_DESC, DISABLE_DESC) \
|
||||
{ \
|
||||
.long_name = "enable-" #FLAG, \
|
||||
.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-" #FLAG, \
|
||||
.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)
|
||||
|
||||
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
GError **err)
|
||||
{
|
||||
@@ -282,21 +324,28 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("PARMFILE") },
|
||||
{.long_name = "enable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||
"(optional).")},
|
||||
{.long_name = "disable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (optional).")},
|
||||
MUT_EXCL_BOOL_FLAG(
|
||||
dump,
|
||||
_("Enable PV guest dumps (optional). This option\n" INDENT
|
||||
"requires the '--comm-key' option."),
|
||||
_("Disable PV guest dumps (default) (optional).")),
|
||||
MUT_EXCL_BOOL_FLAG(
|
||||
pckmo,
|
||||
_("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||
"(optional)."),
|
||||
_("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 key with which you encrypt\n" INDENT
|
||||
"the PV guest dump (optional). Required by\n" INDENT
|
||||
"the '--enable-dump' option."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "crl",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
@@ -355,15 +404,6 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
};
|
||||
|
||||
GOptionEntry x_entries[] = {
|
||||
{ .long_name = "x-comm-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Use FILE as the customer communication key.\n" INDENT
|
||||
"Optional; default: auto-generated."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "x-comp-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
@@ -446,6 +486,7 @@ 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->allow_dump = PV_NOT_SET;
|
||||
args->allow_pckmo = PV_NOT_SET;
|
||||
return g_steal_pointer(&args);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef struct {
|
||||
gboolean offline;
|
||||
gchar *pcf;
|
||||
gchar *scf;
|
||||
PvTristate allow_dump;
|
||||
PvTristate allow_pckmo;
|
||||
gchar *psw_addr; /* PSW address which will be used for the start of
|
||||
* the actual component (e.g. Linux kernel)
|
||||
|
||||
@@ -229,7 +229,9 @@ static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s,
|
||||
}
|
||||
|
||||
static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
const gchar *scf_s, PvTristate allow_pckmo, GError **err)
|
||||
const gchar *scf_s,
|
||||
PvTristate allow_dump,
|
||||
PvTristate allow_pckmo, GError **err)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
@@ -247,6 +249,11 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
img->scf = flags;
|
||||
}
|
||||
|
||||
if (allow_dump == PV_TRUE)
|
||||
img->pcf |= PV_PCF_ALLOW_DUMPING;
|
||||
else if (allow_dump == PV_FALSE)
|
||||
img->pcf &= ~PV_PCF_ALLOW_DUMPING;
|
||||
|
||||
if (allow_pckmo == PV_TRUE)
|
||||
img->pcf |= PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA;
|
||||
else if (allow_pckmo == PV_FALSE)
|
||||
@@ -304,9 +311,10 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
}
|
||||
|
||||
/* Load all untrusted certificates (e.g. IBM Z signing key and
|
||||
* DigiCert intermediate CA) that are required to establish a chain of
|
||||
* trust starting from the host-key document up to the root CA (if not
|
||||
* otherwise specified that's the DigiCert Assured ID Root CA).
|
||||
* intermediate CA) that are required to establish a chain of trust
|
||||
* starting from the host-key document up to the root CA (if not
|
||||
* otherwise specified that can be one of the system wide installed
|
||||
* root CAs, e.g. DigiCert).
|
||||
*/
|
||||
untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err);
|
||||
if (!untrusted_certs_with_path)
|
||||
@@ -341,9 +349,8 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
* For this we must check:
|
||||
*
|
||||
* 1. Can a chain of trust be established ending in a root CA
|
||||
* 2. Is the correct root CA ued? It has either to be the
|
||||
* 'DigiCert Assured ID Root CA' or the root CA specified via
|
||||
* command line.
|
||||
* 2. Is the correct root CA used? It has either to be a system CA
|
||||
* or the root CA specified via command line.
|
||||
*/
|
||||
for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) {
|
||||
X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i);
|
||||
@@ -364,17 +371,12 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
if (verify_cert(ibm_signing_cert, ctx, err) < 0)
|
||||
goto error;
|
||||
|
||||
/* Verify the build chain of trust chain. If the user passes a
|
||||
* trusted root CA on the command line then the check for the
|
||||
* Subject Key Identifier (SKID) is skipped, otherwise let's
|
||||
* check if the SKID meets our expectation.
|
||||
/* If there is a chain of trust using either the provided root
|
||||
* CA on the command line or a system wide trusted root CA.
|
||||
*/
|
||||
if (!root_ca_path &&
|
||||
check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
||||
get_digicert_assured_id_root_ca_skid(),
|
||||
err) < 0) {
|
||||
if (check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
||||
err) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err);
|
||||
if (!ibm_signing_crls) {
|
||||
@@ -588,7 +590,7 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
||||
g_warning(_("host-key document verification is disabled. Your workload is not secured."));
|
||||
|
||||
if (args->root_ca_path)
|
||||
g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted."));
|
||||
g_warning(_("The root CA is selected through the command line. Ensure that this root CA is trusted."));
|
||||
|
||||
ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
|
||||
if (!ret->comps)
|
||||
@@ -608,7 +610,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
||||
return NULL;
|
||||
|
||||
/* set the control flags: PCF and SCF */
|
||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf, args->allow_pckmo, err) < 0)
|
||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf,
|
||||
args->allow_dump, args->allow_pckmo,
|
||||
err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* read in the keys */
|
||||
|
||||
@@ -1079,8 +1079,8 @@ int store_set_verify_param(X509_STORE *store, GError **err)
|
||||
g_abort();
|
||||
|
||||
/* The maximum depth level of the chain of trust for the verification of
|
||||
* the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert)
|
||||
* intermediate CA -> (DigiCert) root CA
|
||||
* the IBM Z signing key is 2, i.e. IBM Z signing key -> intermediate CA
|
||||
* -> root CA
|
||||
*/
|
||||
X509_VERIFY_PARAM_set_depth(param, 2);
|
||||
|
||||
@@ -1267,46 +1267,38 @@ static int security_level_to_bits(int level)
|
||||
return security_bits[level];
|
||||
}
|
||||
|
||||
static ASN1_OCTET_STRING *digicert_assured_id_root_ca;
|
||||
|
||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void)
|
||||
{
|
||||
pv_crypto_init();
|
||||
return digicert_assured_id_root_ca;
|
||||
}
|
||||
|
||||
/* Used for the caching of the downloaded CRLs */
|
||||
static GHashTable *cached_crls;
|
||||
|
||||
void pv_crypto_init(void)
|
||||
{
|
||||
if (digicert_assured_id_root_ca)
|
||||
if (cached_crls)
|
||||
return;
|
||||
|
||||
cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
(GDestroyNotify)X509_CRL_free);
|
||||
digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING(
|
||||
NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID);
|
||||
}
|
||||
|
||||
void pv_crypto_cleanup(void)
|
||||
{
|
||||
if (!digicert_assured_id_root_ca)
|
||||
if (!cached_crls)
|
||||
return;
|
||||
g_clear_pointer(&cached_crls, g_hash_table_destroy);
|
||||
g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free);
|
||||
}
|
||||
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
const ASN1_OCTET_STRING *skid, GError **err)
|
||||
GError **err)
|
||||
{
|
||||
const ASN1_OCTET_STRING *ca_skid = NULL;
|
||||
const X509_NAME *ca_x509_subject = NULL;
|
||||
g_autofree gchar *ca_subject = NULL;
|
||||
gint len = sk_X509_num(chain);
|
||||
X509 *ca = NULL;
|
||||
|
||||
g_assert(skid);
|
||||
/* at least one root and one leaf certificate must be defined */
|
||||
g_assert(len >= 2);
|
||||
if (len < 2) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("there must be at least on root and one leaf certificate in the chain of trust"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get the root certificate of the chain of trust */
|
||||
ca = sk_X509_value(chain, len - 1);
|
||||
@@ -1316,19 +1308,21 @@ gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
return -1;
|
||||
}
|
||||
|
||||
ca_skid = X509_get0_subject_key_id(ca);
|
||||
if (!ca_skid) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
|
||||
_("malformed root certificate"));
|
||||
ca_x509_subject = X509_get_subject_name(ca);
|
||||
if (!ca_x509_subject) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("subject of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ASN1_STRING_cmp(ca_skid, skid) != 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED,
|
||||
_("expecting DigiCert root CA to be used"));
|
||||
ca_subject = X509_NAME_oneline(ca_x509_subject, NULL, 0);
|
||||
if (!ca_subject) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("subject name of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_info("Root CA used: '%s'", ca_subject);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,6 @@ int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
gint verify_flags, GError **err);
|
||||
void pv_crypto_init(void);
|
||||
void pv_crypto_cleanup(void);
|
||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void);
|
||||
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
||||
gint verify_flags, int level, GError **err);
|
||||
X509 *load_cert_from_file(const char *path, GError **err);
|
||||
@@ -138,8 +137,7 @@ X509_STORE *store_setup(const gchar *root_ca_path,
|
||||
int store_set_verify_param(X509_STORE *store, GError **err);
|
||||
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
||||
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
const ASN1_OCTET_STRING *skid, GError **err);
|
||||
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);
|
||||
|
||||
106
include/lib/libcpumf.h
Normal file
106
include/lib/libcpumf.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* Copyright IBM Corp. 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 LIBCPUMF_H
|
||||
#define LIBCPUMF_H
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define S390_CPUMF_CF "/sys/devices/cpum_cf/"
|
||||
#define S390_CPUMF_CFDIAG "/sys/devices/cpum_cf_diag/"
|
||||
#define S390_CPUMF_SF "/sys/devices/cpum_sf/"
|
||||
#define S390_CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||
#define S390_CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||
#define S390_CPUMSF_BUFFERSZ "/sys/module/kernel/parameters/cpum_sfb_size"
|
||||
|
||||
/**
|
||||
* Read out the PMU type from a given file.
|
||||
*
|
||||
* Return the PMU type number assigned to this PMU by the kernel. This is
|
||||
* a non zero number.
|
||||
* If the PMU does not exist return -1 and set errno.
|
||||
*
|
||||
* @param[in] dirname Name of the event directory in sysfs
|
||||
*/
|
||||
int libcpumf_pmutype(const char *dirname);
|
||||
|
||||
/**
|
||||
* Read out the CPU list from a given file name, for example from files
|
||||
* /sys/devices/system/cpu/online or /sys/devices/system/cpu/possible.
|
||||
*
|
||||
* Return the cpu_set_t created from parsing the CPU list in the second
|
||||
* parameter.
|
||||
* Return code of zero indicates proper conversion and -1 indicates an
|
||||
* error.
|
||||
*
|
||||
* @param[in] buffer Comma separated string of a CPU list
|
||||
* @param[in] filename Name of a sysfs CPU list file name
|
||||
* @param[out] mask Converted buffer into cpu_set_t mask structure
|
||||
*/
|
||||
int libcpumf_cpuset(const char *buffer, cpu_set_t *mask);
|
||||
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Counting Facility hardware information
|
||||
*
|
||||
* Return true if CPU Measurement Counter facility information has been
|
||||
* retrieved and is valid.
|
||||
*
|
||||
* Return false if the information could not be extracted from the file.
|
||||
*
|
||||
* @param[out] cfvn Contains CPUMF counter first version number
|
||||
* @param[out] csvn Contains CPUMF counter second version number
|
||||
* @param[out] auth Contains CPUMF counter set authorization level
|
||||
*/
|
||||
bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Counter Facility is available.
|
||||
*/
|
||||
bool libcpumf_have_cpumcf(void);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Sampling Facility hardware information
|
||||
*
|
||||
* Read all necessary information from /sysfs file /proc/service_levels
|
||||
* to return CPU Measurement Counter Sampling facility information
|
||||
* characteristics.
|
||||
* Return true on success and false when the data can not be retrieved.
|
||||
*
|
||||
* @param[out] min Minimum supported sampling interval
|
||||
* @param[out] max Maximum supported sampling interval
|
||||
* @param[out] speed Current CPU speed, number of CPU cylces per
|
||||
* microsecond
|
||||
* @param[out] basic_sz Basic sample size in bytes
|
||||
* @param[out] diag_sz Diagnostic sample size in bytes
|
||||
*/
|
||||
bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max,
|
||||
unsigned long *speed, int *basic_sz, int *diag_sz);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Sampling Facility is available.
|
||||
*/
|
||||
bool libcpumf_have_cpumsf(void);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Sampling Facility buffer sizes are
|
||||
* available.
|
||||
*/
|
||||
bool libcpumf_have_sfb(void);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Sampling Facility supported sampling buffer sizes.
|
||||
*
|
||||
* Return the minimum and maximum CPU Measurement sampling facitity buffer
|
||||
* sizes supported.
|
||||
* Return true on success and false otherwise.
|
||||
*
|
||||
* @param[out] min Minimum supported sampling buffer size
|
||||
* @param[out] max Maximum supported sampling buffer size
|
||||
*/
|
||||
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max);
|
||||
#endif
|
||||
19
libcpumf/Makefile
Normal file
19
libcpumf/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
include ../common.mak
|
||||
|
||||
lib = libcpumf.a
|
||||
|
||||
examples = libcpumf_example
|
||||
|
||||
all: $(lib)
|
||||
examples: $(lib) $(examples)
|
||||
|
||||
objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
install: all
|
||||
|
||||
libcpumf_example: libcpumf_example.o $(lib)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(lib) $(examples)
|
||||
75
libcpumf/libcpumf_cpuset.c
Normal file
75
libcpumf/libcpumf_cpuset.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright IBM Corp. 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
||||
{
|
||||
char *cp, *buffer = strdup(parm);
|
||||
int to, from, rc;
|
||||
|
||||
if (!buffer) /* Errno set to ENOMEM */
|
||||
return -1;
|
||||
/* Check for invalid characters, such as 11.12 instead 11-12
|
||||
* but allow blanks and newline. Newline is appended
|
||||
* when the string is taken from sysfs files, for example
|
||||
* /sys/devices/system/cpu/online
|
||||
*/
|
||||
if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
CPU_ZERO(mask);
|
||||
for (; (cp = strtok(buffer, ",")); buffer = NULL) {
|
||||
char *dash = strchr(cp, '-'); /* Range character? */
|
||||
bool is_ok;
|
||||
|
||||
if (dash) {
|
||||
rc = sscanf(cp, "%d-%d", &from, &to);
|
||||
is_ok = rc == 2;
|
||||
} else {
|
||||
rc = sscanf(cp, "%d", &to);
|
||||
from = to;
|
||||
is_ok = rc == 1;
|
||||
}
|
||||
if (!is_ok) {
|
||||
errno = ERANGE;
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
for (; from <= to; ++from)
|
||||
CPU_SET(from, mask);
|
||||
}
|
||||
rc = 0;
|
||||
out:
|
||||
free(buffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask)
|
||||
{
|
||||
char *txt = NULL;
|
||||
ssize_t ret = -1;
|
||||
size_t len = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return ret;
|
||||
/* Read out file, one line expected */
|
||||
ret = getline(&txt, &len, fp);
|
||||
fclose(fp);
|
||||
if (ret > 0)
|
||||
ret = libcpumf_cpuset(txt, mask);
|
||||
free(txt);
|
||||
return ret;
|
||||
}
|
||||
76
libcpumf/libcpumf_example.c
Normal file
76
libcpumf/libcpumf_example.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Copyright IBM Corp. 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned long min, max, speed, sfb_min, sfb_max;
|
||||
int rc, pmu, cfvn, csvn, auth;
|
||||
cpu_set_t set;
|
||||
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_CF);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_CF, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_CF, errno);
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_SF);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_SF, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_SF, errno);
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_CFDIAG);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_CFDIAG, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_CFDIAG, errno);
|
||||
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &set);
|
||||
if (rc == 0) {
|
||||
puts("Online CPUs:");
|
||||
for (int i = 0; i < CPU_SETSIZE; ++i)
|
||||
if (CPU_ISSET(i, &set))
|
||||
printf("%d ", i);
|
||||
putchar('\n');
|
||||
}
|
||||
rc = libcpumf_cpuset("0-7,9,11-12 ,15", &set);
|
||||
if (rc == 0) {
|
||||
puts("String CPUs:");
|
||||
for (int i = 0; i < CPU_SETSIZE; ++i)
|
||||
if (CPU_ISSET(i, &set))
|
||||
printf("%d ", i);
|
||||
putchar('\n');
|
||||
} else {
|
||||
printf("libcpumf_cpuset input invalid %d\n", errno);
|
||||
}
|
||||
|
||||
printf("CPUMCF support %d\n", libcpumf_have_cpumcf());
|
||||
rc = libcpumf_cpumcf_info(&cfvn, &csvn, &auth);
|
||||
printf("libcpumf_cpumcf_info %d", rc);
|
||||
if (rc)
|
||||
printf(" cfvn %d csvn %d authorization %#x", cfvn, csvn, auth);
|
||||
putchar('\n');
|
||||
|
||||
printf("CPUMSF support %d\n", libcpumf_have_cpumsf());
|
||||
rc = libcpumf_cpumsf_info(&min, &max, &speed, &cfvn, &csvn);
|
||||
printf("libcpumf_cpumsf_info %d", rc);
|
||||
if (rc)
|
||||
printf(" min %ld max %ld speed %#lx basic %d diag %d", min,
|
||||
max, speed, cfvn, csvn);
|
||||
putchar('\n');
|
||||
|
||||
printf("CPUMSF have sfb %d\n", libcpumf_have_sfb());
|
||||
rc = libcpumf_sfb_info(&sfb_min, &sfb_max);
|
||||
printf("libcpumf_sfb_info %d", rc);
|
||||
if (rc)
|
||||
printf(" sfb_min %lu sfb_max %lu", sfb_min, sfb_max);
|
||||
putchar('\n');
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
33
libcpumf/libcpumf_pmutype.c
Normal file
33
libcpumf/libcpumf_pmutype.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright IBM Corp. 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int libcpumf_pmutype(const char *dirname)
|
||||
{
|
||||
FILE *file;
|
||||
char *fn;
|
||||
int ret;
|
||||
|
||||
ret = asprintf(&fn, "%s/type", dirname);
|
||||
if (ret == -1) /* No memory, errno set */
|
||||
return ret;
|
||||
file = fopen(fn, "r");
|
||||
free(fn);
|
||||
ret = -1; /* Errno set on file open error */
|
||||
if (file) {
|
||||
/* Read out a single number from that file */
|
||||
if (fscanf(file, "%u", &ret) != 1)
|
||||
/* Unexpected format error, set errno */
|
||||
errno = -ERANGE;
|
||||
fclose(file);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
141
libcpumf/libcpumf_support.c
Normal file
141
libcpumf/libcpumf_support.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright IBM Corp. 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
|
||||
bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool rc = false;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(SERVICELEVEL, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, SERVICELEVEL);
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
int cnt = sscanf(linep, "CPU-MF: Counter facility:"
|
||||
" version=%d.%d authorization=%x",
|
||||
cfvn, csvn, auth);
|
||||
if (cnt != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
rc = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
free(linep);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool libcpumf_have_cpumcf(void)
|
||||
{
|
||||
int cfvn, csvn, auth;
|
||||
|
||||
return libcpumf_cpumcf_info(&cfvn, &csvn, &auth);
|
||||
}
|
||||
|
||||
bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max,
|
||||
unsigned long *speed, int *basic_sz, int *diag_sz)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool rc = true;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
int hit = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(SERVICELEVEL, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, SERVICELEVEL);
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) {
|
||||
int ok;
|
||||
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" min_rate=%ld max_rate=%ld cpu_speed=%ld",
|
||||
min, max, speed);
|
||||
if (ok != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=basic sample_size=%u", basic_sz);
|
||||
if (ok != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=diagnostic sample_size=%u",
|
||||
diag_sz);
|
||||
if (ok != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
free(linep);
|
||||
if (hit != 3)
|
||||
rc = false;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool libcpumf_have_cpumsf(void)
|
||||
{
|
||||
unsigned long a, b, c;
|
||||
int basic_sz, diag_sz;
|
||||
|
||||
return libcpumf_cpumsf_info(&a, &b, &c, &basic_sz, &diag_sz);
|
||||
}
|
||||
|
||||
bool libcpumf_have_sfb(void)
|
||||
{
|
||||
unsigned long a, b;
|
||||
|
||||
return libcpumf_sfb_info(&a, &b);
|
||||
}
|
||||
|
||||
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
int rc = false;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, "%s", S390_CPUMSF_BUFFERSZ);
|
||||
if (fscanf(fp, "%lu,%lu", min, max) == 2)
|
||||
rc = true;
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
@@ -1549,6 +1549,59 @@ int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(blob, lrb.payload, lrb.pllen);
|
||||
|
||||
/* re-encipher MACed SPKI */
|
||||
rb.domain = domain;
|
||||
lrb.domain = domain;
|
||||
|
||||
resp_len = sizeof(resp);
|
||||
req_len = ep11.dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT,
|
||||
&rb, NULL, key_token + hdr->len,
|
||||
key_token_length - hdr->len);
|
||||
if (req_len < 0) {
|
||||
sk_debug(debug, "Failed to build XCP command block");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rv = ep11.dll_m_admin(resp, &resp_len, NULL, NULL, req, req_len, NULL,
|
||||
0, ep11_lib->target);
|
||||
if (rv != CKR_OK || resp_len == 0) {
|
||||
sk_debug(debug, "Command XCP_ADM_REENCRYPT failed. "
|
||||
"rc = 0x%lx, resp_len = %ld", rv, resp_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = ep11.dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv);
|
||||
if (rc != 0) {
|
||||
sk_debug(debug, "Failed to parse response. rc = %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rv != CKR_OK) {
|
||||
sk_debug(debug, "Failed to re-encrypt the EP11 secure key. "
|
||||
"rc = 0x%lx", rv);
|
||||
switch (rv) {
|
||||
case CKR_IBM_WKID_MISMATCH:
|
||||
sk_debug(debug, "The EP11 secure key is currently "
|
||||
"encrypted under a different master that does "
|
||||
"not match the master key in the CURRENT "
|
||||
"master key register of APQN %02X.%04X",
|
||||
card, domain);
|
||||
break;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (key_token_length - hdr->len != lrb.pllen) {
|
||||
sk_debug(debug, "Re-encrypted EP11 secure key size has "
|
||||
"changed: org-len: %lu, new-len: %lu",
|
||||
hdr->len - sizeof(*hdr), lrb.pllen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(key_token + hdr->len, lrb.payload, lrb.pllen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,11 @@ get_system_level()
|
||||
read -r d_major d_minor d_minor2 <<< "$(split_version "$ver_str" "._-" 3)"
|
||||
read -r k_ver k_patchlvl k_sublvl k_sublvl2 <<< "$(split_kver "$kver_str" 4)"
|
||||
|
||||
# Handle excessive sublevel numbers consistently
|
||||
if [[ "$k_sublvl2" -gt 65535 ]] ; then
|
||||
k_sublvl2=0
|
||||
fi
|
||||
|
||||
# Apply distro-specific logic
|
||||
case "$distro" in
|
||||
"rhel")
|
||||
|
||||
1275
scripts/dbginfo.sh
1275
scripts/dbginfo.sh
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
.\" lszcrypt.8
|
||||
.\"
|
||||
.\" Copyright 2019 IBM Corp.
|
||||
.\" Copyright IBM Corp. 2019, 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.
|
||||
.\"
|
||||
@@ -10,13 +10,14 @@
|
||||
.\" nroff -man lszcrypt.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH LSZCRYPT 8 "OCT 2020" "s390-tools"
|
||||
.TH LSZCRYPT 8 "FEB 2022" "s390-tools"
|
||||
.SH NAME
|
||||
lszcrypt \- display zcrypt device and configuration information
|
||||
.SH SYNOPSIS
|
||||
.TP 9
|
||||
.B lszcrypt
|
||||
.RB "[ " -V " ] "
|
||||
.RB "[" <filteroptions> "]"
|
||||
.RB "[" -V "]"
|
||||
[
|
||||
.I <device-id>
|
||||
[...]]
|
||||
@@ -31,7 +32,12 @@ lszcrypt \- display zcrypt device and configuration information
|
||||
.TP
|
||||
.B lszcrypt -h
|
||||
.TP
|
||||
.B lszcrypt -s
|
||||
.TP
|
||||
.B lszcrypt -v
|
||||
. TP
|
||||
.B <filteroptions>
|
||||
[--accelonly|--ccaonly|--ep11only] [--cardonly|--queueonly]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B lszcrypt
|
||||
@@ -109,8 +115,26 @@ B - indicate both (control and usage domain)
|
||||
.B -h, --help
|
||||
Displays help text and exits.
|
||||
.TP 8
|
||||
.B -s, --serial
|
||||
Shows the serial numbers for CCA and EP11 crypto cards.
|
||||
.TP 8
|
||||
.B -v, --version
|
||||
Displays version information and exits.
|
||||
.TP 8
|
||||
.B --accelonly
|
||||
Show only information for cards/queues in Accelerator mode.
|
||||
.TP 8
|
||||
.B --ccaonly
|
||||
Show only information for cards/queues in CCA-Coprocessor mode.
|
||||
.TP 8
|
||||
.B --ep11only
|
||||
Show only information for cards/queues in EP11-Coprocessor mode.
|
||||
.TP 8
|
||||
.B --cardonly
|
||||
Show only information for cards but no queue info.
|
||||
.TP 8
|
||||
.B --queueonly
|
||||
Show only information for queues but no card info.
|
||||
.SH LISTING DETAILS
|
||||
Here is an explanation of the columns displayed. Please note that some
|
||||
of the columns show up in verbose mode only.
|
||||
@@ -123,7 +147,7 @@ dot for a queue line.
|
||||
.B TYPE and HWTYPE
|
||||
The HWTYPE is a numeric value showing which type of hardware the zcrypt
|
||||
device driver presumes that this crypto card is. The currently known values
|
||||
are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6 and 13=CEX7.
|
||||
are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6, 13=CEX7 and 14=CEX8.
|
||||
.br
|
||||
The TYPE is a human readable value showing the hardware type and the basic
|
||||
function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So
|
||||
@@ -154,6 +178,9 @@ cryptographic operations. 'offline' is displayed when a card or queue
|
||||
is switched to (software) offline. If a card is 'deconfigured' via
|
||||
HMC, SE or chzcrypt the field shows 'deconfig'.
|
||||
.br
|
||||
A crypto card may also reach a 'checkstopped' state. lszcrypt shows
|
||||
this as 'chkstop'.
|
||||
.br
|
||||
If a queue is not bound to a device driver there is no detailed
|
||||
information available and thus the status shows only '-'.
|
||||
.br
|
||||
@@ -204,12 +231,17 @@ operations within the guests.
|
||||
.B DRIVER
|
||||
.br
|
||||
Shows which card or queue device driver currently handles this crypto
|
||||
resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX7
|
||||
resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX8
|
||||
hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware),
|
||||
cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved
|
||||
for use by kvm hypervisor for kvm guests and not accessible to host
|
||||
applications). It is also valid to have no driver handling a queue which is
|
||||
shown as a -no-driver- entry.
|
||||
.SH NOTES
|
||||
Use only one of the mode filtering options --accelonly, --ccaonly, --ep11only.
|
||||
Same with card/queue filtering: Use only one of --cardonly, --queueonly.
|
||||
However, one of the mode filtering options and one of the card/queue filtering
|
||||
can be combined.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B lszcrypt
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* lszcrypt - Display zcrypt devices and configuration settings
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2020
|
||||
* Copyright IBM Corp. 2008, 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.
|
||||
@@ -29,6 +29,11 @@
|
||||
*/
|
||||
static struct lszcrypt_l {
|
||||
int verbose;
|
||||
int showaccel;
|
||||
int showcca;
|
||||
int showep11;
|
||||
int showcard;
|
||||
int showqueue;
|
||||
} l;
|
||||
|
||||
/*
|
||||
@@ -39,6 +44,7 @@ static struct lszcrypt_l {
|
||||
#define CAP_CCA "CCA Secure Key"
|
||||
#define CAP_RNG "Long RNG"
|
||||
#define CAP_EP11 "EP11 Secure Key"
|
||||
#define CAP_APMMS "AP bus max message size limit %ld Kb"
|
||||
|
||||
/*
|
||||
* Card types
|
||||
@@ -95,6 +101,13 @@ static const struct util_prg prg = {
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
|
||||
#define OPT_ACCELONLY 0x81
|
||||
#define OPT_CCAONLY 0x82
|
||||
#define OPT_EP11ONLY 0x83
|
||||
#define OPT_CARDONLY 0x84
|
||||
#define OPT_QUEUEONLY 0x85
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.option = {"bus", 0, NULL, 'b'},
|
||||
@@ -113,6 +126,35 @@ static struct util_opt opt_vec[] = {
|
||||
.option = {"verbose", 0, NULL, 'V'},
|
||||
.desc = "Print verbose messages",
|
||||
},
|
||||
{
|
||||
.option = {"accelonly", 0, NULL, OPT_ACCELONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in Accelerator mode",
|
||||
},
|
||||
{
|
||||
.option = {"ccaonly", 0, NULL, OPT_CCAONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in CCA-Coprocessor mode",
|
||||
},
|
||||
{
|
||||
.option = {"ep11only", 0, NULL, OPT_EP11ONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in EP11-Coprocessor mode",
|
||||
},
|
||||
{
|
||||
.option = {"cardonly", 0, NULL, OPT_CARDONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards but no queue info",
|
||||
},
|
||||
{
|
||||
.option = {"queueonly", 0, NULL, OPT_QUEUEONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from queues but no card info",
|
||||
},
|
||||
{
|
||||
.option = {"serial", 0, NULL, 's'},
|
||||
.desc = "Show the serial numbers for CCA and EP11 crypto cards",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
@@ -247,13 +289,94 @@ static void show_domains(void)
|
||||
show_domains_util_rec(domain_array);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show serialnumbers
|
||||
*/
|
||||
static void show_serialnumbers(void)
|
||||
{
|
||||
struct util_rec *rec = util_rec_new_wide("-");
|
||||
struct dirent **dev_vec;
|
||||
int i, count;
|
||||
char *ap, *path, *device, *grp_dev, card[16], buf[256];
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
unsigned long facility;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
/* define the record */
|
||||
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 8, "CARD.DOM");
|
||||
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
|
||||
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
|
||||
util_rec_def(rec, "status", UTIL_REC_ALIGN_LEFT, 10, "STATUS");
|
||||
util_rec_def(rec, "serialnr", UTIL_REC_ALIGN_LEFT, 8, "SERIALNR");
|
||||
|
||||
/* scan the devices */
|
||||
path = util_path_sysfs("devices/ap/");
|
||||
count = util_scandir(&dev_vec, alphasort, path, "card[0-9a-fA-F]+");
|
||||
if (count < 1)
|
||||
errx(EXIT_FAILURE, "No crypto card devices found.");
|
||||
util_rec_print_hdr(rec);
|
||||
for (i = 0; i < count; i++) {
|
||||
device = dev_vec[i]->d_name;
|
||||
grp_dev = util_path_sysfs("devices/ap/%s", device);
|
||||
if (!util_path_is_dir(grp_dev))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device);
|
||||
if (!util_path_is_readable("%s/type", grp_dev) ||
|
||||
!util_path_is_readable("%s/online", grp_dev))
|
||||
goto next;
|
||||
strcpy(card, device + 4);
|
||||
util_rec_set(rec, "card", card);
|
||||
util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
|
||||
util_rec_set(rec, "type", buf);
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
if (facility & MASK_COPRO)
|
||||
util_rec_set(rec, "mode", "CCA-Coproc");
|
||||
else if (facility & MASK_EP11)
|
||||
util_rec_set(rec, "mode", "EP11-Coproc");
|
||||
else
|
||||
goto next;
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
if (util_path_is_readable("%s/chkstop", grp_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/chkstop", grp_dev);
|
||||
if (util_path_is_readable("%s/online", grp_dev))
|
||||
util_file_read_l(&online, 10, "%s/online", grp_dev);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
else
|
||||
util_rec_set(rec, "status", "-");
|
||||
}
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/serialnr", grp_dev))
|
||||
util_rec_set(rec, "serialnr", "-");
|
||||
else {
|
||||
buf[8] = '\0';
|
||||
util_rec_set(rec, "serialnr", buf);
|
||||
}
|
||||
util_rec_print(rec);
|
||||
next:
|
||||
free(grp_dev);
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show capability
|
||||
*/
|
||||
static void show_capability(const char *id_str)
|
||||
{
|
||||
unsigned long func_val;
|
||||
long hwtype, id;
|
||||
long hwtype, id, max_msg_size;
|
||||
char *p, *ap, *dev, card[16], cbuf[256];
|
||||
|
||||
/* check if ap driver is available */
|
||||
@@ -272,6 +395,9 @@ static void show_capability(const char *id_str)
|
||||
/* If sysfs attribute is missing, set functions to 0 */
|
||||
if (util_file_read_ul(&func_val, 16, "%s/ap_functions", dev))
|
||||
func_val = 0x00000000;
|
||||
/* try to read the ap bus max message size for this card */
|
||||
if (util_file_read_l(&max_msg_size, 10, "%s/max_msg_size", dev))
|
||||
max_msg_size = 0;
|
||||
/* Skip devices, which are not supported by zcrypt layer */
|
||||
if (!util_path_is_readable("%s/type", dev) ||
|
||||
!util_path_is_readable("%s/online", dev)) {
|
||||
@@ -289,9 +415,9 @@ static void show_capability(const char *id_str)
|
||||
case 6:
|
||||
case 8:
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
printf("%s\n", CAP_RSA2K);
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
@@ -300,37 +426,39 @@ static void show_capability(const char *id_str)
|
||||
printf("%s (%s)\n", CAP_CCA, cbuf);
|
||||
else
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
printf("%s\n", CAP_RNG);
|
||||
break;
|
||||
case 10: /* CEX4S */
|
||||
case 11: /* CEX5S */
|
||||
case 12: /* CEX6S */
|
||||
case 13: /* CEX7S */
|
||||
case 14: /* CEX8S */
|
||||
if (func_val & MASK_ACCEL) {
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
printf("%s\n", CAP_RSA2K);
|
||||
} else if (func_val & MASK_COPRO) {
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
if (cbuf[0])
|
||||
printf("%s (%s)\n", CAP_CCA, cbuf);
|
||||
else
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
printf("%s\n", CAP_RNG);
|
||||
} else if (func_val & MASK_EP11) {
|
||||
printf("%s", CAP_EP11);
|
||||
printf("%s\n", CAP_EP11);
|
||||
} else {
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.",
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.\n",
|
||||
card, hwtype);
|
||||
}
|
||||
if (max_msg_size > 0)
|
||||
printf(CAP_APMMS "\n", max_msg_size / 1024);
|
||||
break;
|
||||
default:
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.",
|
||||
card, hwtype);
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.\n",
|
||||
card, hwtype);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -375,7 +503,7 @@ static int read_driver(const char *dir, const char *subdir, char *buf, size_t bu
|
||||
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
long config = -1, online = -1;
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
char buf[256];
|
||||
unsigned long facility;
|
||||
|
||||
@@ -386,6 +514,8 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
|
||||
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev))
|
||||
util_file_read_l(&config, 10, "%s/%s/config", grp_dev, sub_dev);
|
||||
if (util_path_is_readable("%s/%s/chkstop", grp_dev, sub_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/%s/chkstop", grp_dev, sub_dev);
|
||||
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
|
||||
util_file_read_l(&online, 10, "%s/%s/online", grp_dev, sub_dev);
|
||||
|
||||
@@ -393,7 +523,9 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
if (online > 0)
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
@@ -489,6 +621,9 @@ static void show_subdevice(struct util_rec *rec, const char *grp_dev,
|
||||
!util_path_is_readable("%s/%s/online", grp_dev, sub_dev)))
|
||||
return;
|
||||
|
||||
if (!l.showqueue)
|
||||
return;
|
||||
|
||||
util_rec_set(rec, "card", sub_dev);
|
||||
read_subdev_rec_default(rec, grp_dev, sub_dev);
|
||||
read_subdev_rec_verbose(rec, grp_dev, sub_dev);
|
||||
@@ -516,7 +651,7 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev)
|
||||
*/
|
||||
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
long config = -1, online = -1;
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
char buf[256];
|
||||
unsigned long facility;
|
||||
|
||||
@@ -537,12 +672,16 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
if (util_path_is_readable("%s/chkstop", grp_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/chkstop", grp_dev);
|
||||
if (util_path_is_readable("%s/online", grp_dev))
|
||||
util_file_read_l(&online, 10, "%s/online", grp_dev);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
if (online > 0)
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
@@ -603,7 +742,7 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
|
||||
*/
|
||||
static void show_device(struct util_rec *rec, const char *device)
|
||||
{
|
||||
char *grp_dev, card[16];
|
||||
char *grp_dev, card[16], type[16], t = '\0';
|
||||
|
||||
strcpy(card, &device[4]);
|
||||
grp_dev = util_path_sysfs("devices/ap/%s", device);
|
||||
@@ -621,11 +760,21 @@ static void show_device(struct util_rec *rec, const char *device)
|
||||
}
|
||||
util_rec_set(rec, "card", card);
|
||||
|
||||
if (util_file_read_line(type, sizeof(type), "%s/type", grp_dev) == 0)
|
||||
t = type[strlen(type) - 1];
|
||||
|
||||
if ((t == 'A' && !l.showaccel) ||
|
||||
(t == 'C' && !l.showcca) ||
|
||||
(t == 'P' && !l.showep11))
|
||||
goto out_free;
|
||||
|
||||
read_rec_default(rec, grp_dev);
|
||||
read_rec_verbose(rec, grp_dev);
|
||||
|
||||
util_rec_print(rec);
|
||||
show_subdevices(rec, grp_dev);
|
||||
if (l.showcard)
|
||||
util_rec_print(rec);
|
||||
if (l.showqueue)
|
||||
show_subdevices(rec, grp_dev);
|
||||
out_free:
|
||||
free(grp_dev);
|
||||
}
|
||||
@@ -799,9 +948,27 @@ int main(int argc, char **argv)
|
||||
case 'd':
|
||||
show_domains();
|
||||
return EXIT_SUCCESS;
|
||||
case 's':
|
||||
show_serialnumbers();
|
||||
return EXIT_SUCCESS;
|
||||
case 'V':
|
||||
l.verbose++;
|
||||
break;
|
||||
case OPT_ACCELONLY:
|
||||
l.showaccel = 1;
|
||||
break;
|
||||
case OPT_CCAONLY:
|
||||
l.showcca = 1;
|
||||
break;
|
||||
case OPT_EP11ONLY:
|
||||
l.showep11 = 1;
|
||||
break;
|
||||
case OPT_CARDONLY:
|
||||
l.showcard = 1;
|
||||
break;
|
||||
case OPT_QUEUEONLY:
|
||||
l.showqueue = 1;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
@@ -815,6 +982,29 @@ int main(int argc, char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (l.showaccel + l.showcca + l.showep11) {
|
||||
case 0:
|
||||
l.showaccel = l.showcca = l.showep11 = 1;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
warnx("Only one of --accelonly or --ccaonly or --ep11only can be specified");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
switch (l.showcard + l.showqueue) {
|
||||
case 0:
|
||||
l.showcard = l.showqueue = 1;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
warnx("Only one of --cardonly or --queueonly can be specified");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (optind == argc)
|
||||
show_devices_all();
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" zcryptctl.8
|
||||
.\"
|
||||
.\" Copyright 2018 IBM Corp.
|
||||
.\" Copyright 2018, 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
@@ -10,7 +10,7 @@
|
||||
.\" nroff -man zcryptctl.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools"
|
||||
.TH ZCRYPTCTL 8 "JAN 2022" "s390-tools"
|
||||
.SH NAME
|
||||
zcryptctl \- display information and administrate zcrypt multiple device nodes
|
||||
.SH SYNOPSIS
|
||||
@@ -35,6 +35,11 @@ zcryptctl \- display information and administrate zcrypt multiple device nodes
|
||||
.B deldom
|
||||
.I node-name domain-nr
|
||||
.TP
|
||||
.B zcryptctl addctrl
|
||||
.R |
|
||||
.B delctrl
|
||||
.I node-name domain-nr
|
||||
.TP
|
||||
.B zcryptctl addioctl
|
||||
.R |
|
||||
.B delioctl
|
||||
@@ -57,6 +62,7 @@ driver. Each zcrypt device node can be restricted in terms of crypto
|
||||
cards, domains, and available ioctls. Such a device node can be used
|
||||
as a base for container solutions like Docker to control and restrict
|
||||
the access to crypto resources.
|
||||
See the NOTES section below for information on control domains.
|
||||
.SH COMMANDS
|
||||
.TP 8
|
||||
.B zcryptctl list
|
||||
@@ -96,6 +102,14 @@ Update the filter for the specified zcrypt device node and add or
|
||||
delete a domain to be accessible through this node. The symbol
|
||||
\fBALL\fP can be used to enable or disable all domains.
|
||||
.TP
|
||||
.B zcryptctl addctrl
|
||||
.R |
|
||||
.B delctrl
|
||||
.I node-name domain-nr
|
||||
Update the filter for the specified zcrypt device node and add or
|
||||
delete a control domain to be accessible through this node. The symbol
|
||||
\fBALL\fP can be used to enable or disable all domains.
|
||||
.TP
|
||||
.B zcryptctl addioctl
|
||||
.R |
|
||||
.B delioctl
|
||||
@@ -116,19 +130,20 @@ line and the settings are applied. Syntax is simple:
|
||||
.IP "node=<node-name>"
|
||||
.IP "aps=<list of ap numbers separated by space, tab or ','>"
|
||||
.IP "doms=<list of domain numbers separated by space, tab or ','>"
|
||||
.IP "ctrls=<list of control domain numbers separated by space, tab or ','>"
|
||||
.IP "ioctls=<list of ioctl as numeric or symbolic number separated by space, tab or ','>"
|
||||
.LP
|
||||
Empty lines are ignored and the '#' marks the rest of the
|
||||
line as comment.
|
||||
.LP
|
||||
The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP,
|
||||
\fBdoms=\fP and \fBioctls=\fP lines customize the previously created
|
||||
node. The symbol \fBALL\fP is also recognized for aps, doms, and
|
||||
ioctls.
|
||||
\fBdoms=\fP, \fBctrls=\fP and \fBioctls=\fP lines customize the
|
||||
previously created node. The symbol \fBALL\fP is also recognized for
|
||||
aps, doms, and ioctls.
|
||||
.LP
|
||||
Each action must fit into one line, spreading over multiple lines is
|
||||
not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP
|
||||
and \fBioctls=\fP lines to customize the very same node.
|
||||
not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP,
|
||||
\fBctrls=\fP and \fBioctls=\fP lines to customize the very same node.
|
||||
.LP
|
||||
Processing stops when a line cannot be parsed or the current action
|
||||
fails. In this case the exit status is non zero but the successful
|
||||
@@ -139,6 +154,17 @@ actions until the failure occurs are not rolled back.
|
||||
List the current configuration in a form suitable for input to the
|
||||
\fBzcryptctl config\fP command.
|
||||
.LP
|
||||
.SH NOTES
|
||||
|
||||
Control domain filtering is only supported for custom device
|
||||
<nodename> if the \fBadmask\fP file in sysfs under
|
||||
/sys/class/zcrypt/<nodename>/admask exists. If this file does not
|
||||
exist, the kernel is too old and does not support control command
|
||||
filtering. This is the same effect as setting the control domain mask
|
||||
to ALL. Note that, even though you can allow more than available to
|
||||
the system, you can only send control commands to control domains
|
||||
available to the system.
|
||||
|
||||
.SH EXIT STATUS
|
||||
On successful completion of the command the exit status is 0. A non
|
||||
zero return code (and some kind of failure message) is emitted if the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* zcryptctl - Maintain zcrypt multi device nodes.
|
||||
*
|
||||
* by Harald Freudenberger <freude@linux.ibm.com>
|
||||
* Copyright IBM Corp. 2018
|
||||
* Copyright IBM Corp. 2018, 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.
|
||||
@@ -58,6 +58,8 @@
|
||||
#define CMD_DEL_IOCTL 0x0009
|
||||
#define CMD_CONFIG 0x000A
|
||||
#define CMD_LISTCONFIG 0x000B
|
||||
#define CMD_ADD_CTRL 0x000C
|
||||
#define CMD_DEL_CTRL 0x000D
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
@@ -70,7 +72,7 @@ static const struct util_prg prg = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2018,
|
||||
.pub_last = 2018,
|
||||
.pub_last = 2022,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
@@ -345,6 +347,18 @@ static int cmd_list(int cmd,
|
||||
if (test_bit(i, buf))
|
||||
printf("%c%d", n++ == 0 ? tab : ',', i);
|
||||
putchar('\n');
|
||||
if (cmd == CMD_LISTCONFIG)
|
||||
printf(" ctrls =");
|
||||
else
|
||||
printf(" control domains:");
|
||||
if (read_dn_attr(de->d_name, "admask", buf, sizeof(buf)) != 0)
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not fetch admask attribute from sysfs for zcrypt node '%s'",
|
||||
de->d_name);
|
||||
for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++)
|
||||
if (test_bit(i, buf))
|
||||
printf("%c%d", n++ == 0 ? tab : ',', i);
|
||||
putchar('\n');
|
||||
if (cmd == CMD_LISTCONFIG)
|
||||
printf(" ioctls =");
|
||||
else
|
||||
@@ -526,6 +540,52 @@ static int cmd_add_del_dom(int cmd, const char *node, const char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_del_ctrl(int cmd, const char *node, int dom)
|
||||
{
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (cmd == CMD_ADD_CTRL)
|
||||
sprintf(buf, "+%d", dom);
|
||||
else
|
||||
sprintf(buf, "-%d", dom);
|
||||
rc = write_dn_attr(node, "admask", buf);
|
||||
if (rc != 0)
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not write into sysfs entry to %s domain %d for zdev node '%s'",
|
||||
cmd == CMD_ADD_CTRL ? "add" : "remove", dom, node);
|
||||
}
|
||||
|
||||
static int cmd_add_del_ctrl(int cmd, const char *node, const char *arg)
|
||||
{
|
||||
int dom, all = 0;
|
||||
|
||||
if (strcasecmp(arg, "ALL") == 0) {
|
||||
all = 1;
|
||||
} else {
|
||||
if (sscanf(arg, "%i", &dom) != 1)
|
||||
errx(EXIT_FAILURE,
|
||||
"Invalid domain argument '%s'", arg);
|
||||
if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT)
|
||||
errx(EXIT_FAILURE,
|
||||
"Domain argument '%s' out of range [0..%d]",
|
||||
arg, MAX_ZDEV_DOMAINS_EXT - 1);
|
||||
}
|
||||
|
||||
if (!all) {
|
||||
add_del_ctrl(cmd, node, dom);
|
||||
printf("Control domain %d %s\n", dom,
|
||||
(cmd == CMD_ADD_CTRL ? "added" : "removed"));
|
||||
} else {
|
||||
for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++)
|
||||
add_del_ctrl(cmd, node, dom);
|
||||
printf("All control domains %s\n",
|
||||
(cmd == CMD_ADD_CTRL ? "added" : "removed"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_del_ioctl(int cmd, const char *node, int ioctlnr)
|
||||
{
|
||||
int rc;
|
||||
@@ -694,12 +754,34 @@ static int cmd_config(int cmd _UNUSED_,
|
||||
break;
|
||||
if (!_match_string(&p, buf))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
|
||||
"Missing argument(s) for doms=... at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
cmd_add_del_dom(CMD_ADD_DOM, node, buf);
|
||||
while (isblank(*p) || *p == ',')
|
||||
p++;
|
||||
}
|
||||
} else if (_match_keyword(&p, "ctrls")) {
|
||||
if (!havenode)
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing node=... before processing any ctrls=... statements in line %d '%s'",
|
||||
nr, line);
|
||||
if (!_match_character(&p, '='))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing '=' at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
while (1) {
|
||||
while (isspace(*p))
|
||||
p++;
|
||||
if (*p == '\0' || *p == '#')
|
||||
break;
|
||||
if (!_match_string(&p, buf))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing argument(s) for ctrls=... at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
cmd_add_del_ctrl(CMD_ADD_CTRL, node, buf);
|
||||
while (isblank(*p) || *p == ',')
|
||||
p++;
|
||||
}
|
||||
} else if (_match_keyword(&p, "ioctls")) {
|
||||
if (!havenode)
|
||||
errx(EXIT_FAILURE,
|
||||
@@ -810,13 +892,35 @@ static struct zcryptctl_cmds_s {
|
||||
.cmd = CMD_DEL_DOM,
|
||||
.command = "deldom",
|
||||
.function = cmd_add_del_dom,
|
||||
.usage = "zcryptctl deldom <adapter>",
|
||||
.usage = "zcryptctl deldom <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"remove a crypto domain from the allowed domains list. The\n"
|
||||
"domain argument may be a number in the range 0-255 or the\n"
|
||||
"symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_ADD_CTRL,
|
||||
.command = "addctrl",
|
||||
.function = cmd_add_del_ctrl,
|
||||
.usage = "zcryptctl addctrl <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"add a crypto control domain to be accessible via this node.\n"
|
||||
"The domain argument may be a number in the range 0-255 or\n"
|
||||
"the symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_DEL_CTRL,
|
||||
.command = "delctrl",
|
||||
.function = cmd_add_del_ctrl,
|
||||
.usage = "zcryptctl delctrl <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"remove a crypto control domain from the allowed domains list.\n"
|
||||
"The domain argument may be a number in the range 0-255 or\n"
|
||||
"the symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_ADD_IOCTL,
|
||||
.command = "addioctl",
|
||||
@@ -1003,6 +1107,19 @@ int main(int argc, char *argv[])
|
||||
argv[optind + 1],
|
||||
argv[optind + 2]);
|
||||
break;
|
||||
case CMD_ADD_CTRL:
|
||||
case CMD_DEL_CTRL:
|
||||
if (optind + 1 >= argc)
|
||||
errx(EXIT_FAILURE, "Missing node name argument");
|
||||
if (optind + 2 >= argc)
|
||||
errx(EXIT_FAILURE, "Missing domain argument");
|
||||
if (check_nodename(argv[optind + 1]) != 0)
|
||||
errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
|
||||
argv[optind + 1]);
|
||||
rc = zcryptctl_cmds[cmdindex].function(c,
|
||||
argv[optind + 1],
|
||||
argv[optind + 2]);
|
||||
break;
|
||||
case CMD_ADD_IOCTL:
|
||||
case CMD_DEL_IOCTL:
|
||||
if (optind + 1 >= argc)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* zcryptstats - Show usage statistics of IBM Crypto Express adapters
|
||||
*
|
||||
* Copyright IBM Corp. 2019
|
||||
* Copyright IBM Corp. 2019, 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.
|
||||
@@ -148,8 +148,9 @@ struct chsc_cmb_area {
|
||||
#define CRYPTO_TYPE_CEX5S 11
|
||||
#define CRYPTO_TYPE_CEX6S 12
|
||||
#define CRYPTO_TYPE_CEX7S 13
|
||||
#define CRYPTO_TYPE_CEX8S 14
|
||||
|
||||
#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX7S
|
||||
#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX8S
|
||||
|
||||
struct crypto_counter {
|
||||
const char *name;
|
||||
@@ -236,8 +237,8 @@ static const struct crypto_mode mode_pcica[1] = {
|
||||
.counters = counter_pcica },
|
||||
};
|
||||
|
||||
#define NUM_CEX4567_MODES 11
|
||||
static const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = {
|
||||
#define NUM_CEX45678_MODES 11
|
||||
static const struct crypto_mode mode_cex45678[NUM_CEX45678_MODES] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
@@ -257,7 +258,7 @@ static const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = {
|
||||
.counters = counter_ep11 },
|
||||
};
|
||||
|
||||
#define NUM_CRYPTO_TYPES 14
|
||||
#define NUM_CRYPTO_TYPES 15
|
||||
static const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
@@ -276,14 +277,16 @@ static const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
|
||||
.modes = mode_accel },
|
||||
{ .name = "CEX3C", .num_modes = NUM_COPROC_MODES,
|
||||
.modes = mode_coproc },
|
||||
{ .name = "CEX4", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX5", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX6", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX7", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX4", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX5", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX6", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX7", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX8", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
};
|
||||
|
||||
|
||||
@@ -2424,4 +2427,3 @@ out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -804,17 +804,17 @@ void ccw_unblacklist_id_range(const char *range)
|
||||
|
||||
static char ***id_bitmap_new(void)
|
||||
{
|
||||
return misc_malloc(sizeof(char **) * CSSID_MAX);
|
||||
return misc_malloc(sizeof(char **) * (CSSID_MAX + 1));
|
||||
}
|
||||
|
||||
static void id_bitmap_free(char ***id_bitmap)
|
||||
{
|
||||
unsigned int cssid, ssid;
|
||||
|
||||
for (cssid = 0; cssid < CSSID_MAX; cssid++) {
|
||||
for (cssid = 0; cssid <= CSSID_MAX; cssid++) {
|
||||
if (!id_bitmap[cssid])
|
||||
continue;
|
||||
for (ssid = 0; ssid < SSID_MAX; ssid++)
|
||||
for (ssid = 0; ssid <= SSID_MAX; ssid++)
|
||||
free(id_bitmap[cssid][ssid]);
|
||||
free(id_bitmap[cssid]);
|
||||
}
|
||||
@@ -965,15 +965,15 @@ static struct util_list *cio_ignore_get_ranges(bool autoconf)
|
||||
ranges = strlist_new();
|
||||
|
||||
id_bitmap = id_bitmap_collect(autoconf);
|
||||
for (cssid = 0; cssid < CSSID_MAX; cssid++) {
|
||||
for (cssid = 0; cssid <= CSSID_MAX; cssid++) {
|
||||
if (!id_bitmap[cssid])
|
||||
continue;
|
||||
for (ssid = 0; ssid < SSID_MAX; ssid++) {
|
||||
for (ssid = 0; ssid <= SSID_MAX; ssid++) {
|
||||
if (!id_bitmap[cssid][ssid])
|
||||
continue;
|
||||
first = NULL;
|
||||
last = NULL;
|
||||
for (devno = 0; devno < DEVNO_MAX; devno++) {
|
||||
for (devno = 0; devno <= DEVNO_MAX; devno++) {
|
||||
if (!id_bitmap_get(id_bitmap, cssid, ssid,
|
||||
devno)) {
|
||||
if (first) {
|
||||
|
||||
@@ -556,7 +556,6 @@ static int open_dump(void)
|
||||
}
|
||||
if (mv_dumper_read() != 0)
|
||||
return -ENODEV;
|
||||
zg_close(g.fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
{
|
||||
off_t rc;
|
||||
|
||||
if (off >= zg_fh->sb.st_size)
|
||||
if (S_ISREG(zg_fh->sb.st_mode) && off >= zg_fh->sb.st_size)
|
||||
ERR_EXIT("Trying to seek past file end \"%s\"", zg_fh->path);
|
||||
|
||||
rc = lseek(zg_fh->fh, off, SEEK_SET);
|
||||
|
||||
@@ -68,11 +68,11 @@ stage3.exec: head.o stage3.o kdump3.o libc.o ebcdic.o ebcdic_conv.o sclp.o \
|
||||
}' \
|
||||
); \
|
||||
case $$STAGE in \
|
||||
0) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-Ttext,0";; \
|
||||
1) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-Ttext,0x18";; \
|
||||
1b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-Ttext,0xE000";; \
|
||||
2) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage2.lds";; \
|
||||
3) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3.lds";; \
|
||||
0) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-Ttext,0";; \
|
||||
1) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-Ttext,0x18";; \
|
||||
1b) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-Ttext,0xE000";; \
|
||||
2) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage2.lds";; \
|
||||
3) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage3.lds";; \
|
||||
esac; \
|
||||
$(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@
|
||||
|
||||
@@ -110,7 +110,7 @@ stage3.bin: stage3.exec
|
||||
$< $@
|
||||
|
||||
data.o: $(FILES)
|
||||
$(LD) $(NO_PIE_LDFLAGS) -r -b binary -o data.o $(FILES)
|
||||
$(LINK) $(NO_PIE_LDFLAGS) -static -nostdlib -Wl,--relocatable -Wl,--format,binary -o data.o $(FILES)
|
||||
|
||||
data.h: data.o
|
||||
rm -f data.h
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifndef JOB_H
|
||||
#define JOB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "disk.h"
|
||||
#include "zipl.h"
|
||||
|
||||
@@ -46,6 +48,8 @@ struct job_common_ipl_data {
|
||||
address_t image_addr;
|
||||
address_t parm_addr;
|
||||
address_t ramdisk_addr;
|
||||
bool optional;
|
||||
bool ignore;
|
||||
};
|
||||
|
||||
struct job_ipl_data {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
#define SCAN_SECTION_NUM 9
|
||||
#define SCAN_KEYWORD_NUM 22
|
||||
#define SCAN_KEYWORD_NUM 23
|
||||
#define SCAN_KEYWORD_ONLY_NUM 1
|
||||
#define SCAN_AUTOMENU_NAME "zipl-automatic-menu"
|
||||
|
||||
@@ -52,6 +52,7 @@ enum scan_keyword_id {
|
||||
scan_keyword_defaultauto = 19,
|
||||
scan_keyword_kdump = 20,
|
||||
scan_keyword_secure = 21,
|
||||
scan_keyword_optional = 22,
|
||||
};
|
||||
|
||||
enum scan_section_type {
|
||||
|
||||
@@ -447,6 +447,23 @@ This option cannot be used together with either
|
||||
.BR 'segment' .
|
||||
.PP
|
||||
|
||||
.B optional
|
||||
=
|
||||
.IR 0 / 1
|
||||
(configuration only)
|
||||
.IP
|
||||
.B Configuration section:
|
||||
.br
|
||||
If this option is set to 1 the configuration section will only be included in
|
||||
the boot menu if the referenced image file exists, and running
|
||||
.B zipl
|
||||
will not fail if the image file is missing.
|
||||
|
||||
The default value for
|
||||
.B 'optional'
|
||||
is 0.
|
||||
.PP
|
||||
|
||||
.B parameters
|
||||
=
|
||||
.I kernel\-parameters
|
||||
|
||||
@@ -394,6 +394,10 @@ store_stage2_menu(void* data, size_t size, struct job_data* job)
|
||||
return 0;
|
||||
/* Config texts */
|
||||
for (i = 0; i < job->data.menu.num; i++) {
|
||||
if (job->data.menu.entry[i].data.ipl.common.ignore) {
|
||||
params->config[job->data.menu.entry[i].pos] = 0;
|
||||
continue;
|
||||
}
|
||||
const char *kdump_str = "";
|
||||
if (job->data.menu.entry[i].data.ipl.is_kdump)
|
||||
kdump_str = " (kdump)";
|
||||
|
||||
@@ -1089,6 +1089,12 @@ build_program_table(int fd, char *filename, struct job_data *job,
|
||||
for (i=0; i < job->data.menu.num; i++) {
|
||||
switch (job->data.menu.entry[i].id) {
|
||||
case job_ipl:
|
||||
if (job->data.menu.entry[i].data.ipl.common.ignore) {
|
||||
printf("Skipping #%d: IPL section '%s' (missing files)\n",
|
||||
job->data.menu.entry[i].pos,
|
||||
job->data.menu.entry[i].name);
|
||||
break;
|
||||
}
|
||||
printf("Adding #%d: IPL section '%s'%s",
|
||||
job->data.menu.entry[i].pos,
|
||||
job->data.menu.entry[i].name,
|
||||
|
||||
@@ -758,7 +758,8 @@ static void error_text_section(const char *text, const char *section, const char
|
||||
|
||||
|
||||
static int
|
||||
check_common_ipl_data(struct job_common_ipl_data *common, const char *section)
|
||||
check_common_ipl_data(struct job_common_ipl_data *common, const char *section,
|
||||
bool may_ignore)
|
||||
{
|
||||
uint64_t max_parm_size, len;
|
||||
char *buffer = NULL;
|
||||
@@ -768,10 +769,16 @@ check_common_ipl_data(struct job_common_ipl_data *common, const char *section)
|
||||
if (common->image != NULL) {
|
||||
rc = misc_read_file(common->image, &buffer, &size, 0);
|
||||
if (rc) {
|
||||
error_text_section("Image file", section, common->image);
|
||||
if (may_ignore && common->optional) {
|
||||
printf("Optional section '%s': Missing image file '%s'\n",
|
||||
section, common->image);
|
||||
error_clear_reason();
|
||||
common->ignore = true;
|
||||
rc = 0;
|
||||
goto skip_image;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (size < MAX_COMMAND_LINE_SIZE + sizeof(uint64_t)) {
|
||||
error_text_section("Image file", section, common->image);
|
||||
return -1;
|
||||
@@ -789,12 +796,19 @@ check_common_ipl_data(struct job_common_ipl_data *common, const char *section)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
skip_image:
|
||||
if (common->ramdisk != NULL) {
|
||||
rc = misc_check_readable_file(common->ramdisk);
|
||||
if (rc) {
|
||||
error_text_section("Ramdisk file", section, common->ramdisk);
|
||||
return rc;
|
||||
if (common->optional) {
|
||||
printf("Optional section '%s': Missing ramdisk file '%s'\n",
|
||||
section, common->ramdisk);
|
||||
error_clear_reason();
|
||||
common->ignore = true;
|
||||
rc = 0;
|
||||
} else {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -802,13 +816,17 @@ check_common_ipl_data(struct job_common_ipl_data *common, const char *section)
|
||||
|
||||
static int
|
||||
check_job_ipl_data(struct job_ipl_data *ipl, char *name,
|
||||
struct job_envblk_data *envblk)
|
||||
struct job_envblk_data *envblk, bool may_ignore)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = check_common_ipl_data(&ipl->common, name);
|
||||
rc = check_common_ipl_data(&ipl->common, name, may_ignore);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (ipl->common.ignore)
|
||||
return 0;
|
||||
|
||||
return finalize_ipl_address_data(ipl, name, envblk);
|
||||
}
|
||||
|
||||
@@ -887,9 +905,15 @@ check_job_menu_data(struct job_menu_data *menu, struct job_envblk_data *envblk)
|
||||
case job_ipl:
|
||||
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
|
||||
menu->entry[i].name,
|
||||
envblk);
|
||||
envblk, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
/* default_pos is 1-indexed */
|
||||
if (menu->default_pos == i + 1 &&
|
||||
menu->entry[i].data.ipl.common.ignore) {
|
||||
error_text("Cannot ignore default entry");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case job_print_usage:
|
||||
case job_print_version:
|
||||
@@ -917,7 +941,7 @@ check_job_ipl_tape_data(struct job_ipl_tape_data *ipl, char* name)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
rc = check_common_ipl_data(&ipl->common, name);
|
||||
rc = check_common_ipl_data(&ipl->common, name, false);
|
||||
if (rc)
|
||||
return rc;
|
||||
return finalize_common_address_data(&ipl->common, name);
|
||||
@@ -1016,7 +1040,7 @@ check_job_data(struct job_data* job)
|
||||
break;
|
||||
case job_ipl:
|
||||
rc = check_job_ipl_data(&job->data.ipl, job->name,
|
||||
&job->envblk);
|
||||
&job->envblk, false);
|
||||
break;
|
||||
case job_menu:
|
||||
rc = check_job_menu_data(&job->data.menu, &job->envblk);
|
||||
@@ -1295,6 +1319,12 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
job->data.ipl.common.optional = false;
|
||||
if (data[(int) scan_keyword_optional] != NULL) {
|
||||
job->data.ipl.common.optional =
|
||||
atoi(data[(int) scan_keyword_optional]) == 1;
|
||||
}
|
||||
job->data.ipl.common.ignore = false;
|
||||
break;
|
||||
case section_ipl_tape:
|
||||
/* Tape IPL job */
|
||||
|
||||
278
zipl/src/scan.c
278
zipl/src/scan.c
@@ -47,45 +47,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = {
|
||||
* ult to tofs e mete file isk ent et pt out ultm dump
|
||||
* rs enu
|
||||
*
|
||||
* targ targ targ targ targ defa kdum secu
|
||||
* etba etty etge etbl etof ulta p re
|
||||
* targ targ targ targ targ defa kdum secu opti
|
||||
* etba etty etge etbl etof ulta p re onal
|
||||
* se pe omet ocks fset uto
|
||||
* ry ize
|
||||
*/
|
||||
/* default auto */
|
||||
{opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, opt, inv, opt},
|
||||
opt, opt, opt, opt, opt, opt, inv, opt, inv},
|
||||
/* default menu */
|
||||
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, opt},
|
||||
inv, inv, inv, inv, inv, inv, inv, opt, inv},
|
||||
/* default section */
|
||||
{req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, opt},
|
||||
inv, inv, inv, inv, inv, inv, inv, opt, inv},
|
||||
/* ipl */
|
||||
{inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, inv, opt, opt},
|
||||
opt, opt, opt, opt, opt, inv, opt, opt, opt},
|
||||
/* segment load */
|
||||
{inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* part dump */
|
||||
{inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* fs dump */
|
||||
{inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* ipl tape */
|
||||
{inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* multi volume dump */
|
||||
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req,
|
||||
inv, inv, inv, inv, inv, inv, inv, inv}
|
||||
inv, inv, inv, inv, inv, inv, inv, inv, inv}
|
||||
};
|
||||
|
||||
/* Determines which keyword may be present in a menu section */
|
||||
static enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = {
|
||||
/* menu section */
|
||||
opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, inv, inv, opt
|
||||
opt, opt, opt, opt, opt, inv, inv, opt, inv
|
||||
};
|
||||
|
||||
/* Mapping of keyword IDs to strings */
|
||||
@@ -114,6 +114,7 @@ static const struct {
|
||||
{ "tape", scan_keyword_tape},
|
||||
{ "kdump", scan_keyword_kdump},
|
||||
{ "secure", scan_keyword_secure},
|
||||
{ "optional", scan_keyword_optional},
|
||||
};
|
||||
|
||||
/* List of keywords that are used without an assignment */
|
||||
@@ -794,102 +795,6 @@ static int sort_bls_fields(struct misc_file_buffer *file, char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
scan_bls(const char* blsdir, struct scan_token** token, int scan_size)
|
||||
{
|
||||
int count = 0;
|
||||
int size, remaining = 0, n, current, rc = -1;
|
||||
struct scan_token* buffer;
|
||||
struct scan_token* array = *token;
|
||||
struct dirent** bls_entries;
|
||||
struct misc_file_buffer file;
|
||||
struct stat sb;
|
||||
char filename[PATH_MAX];
|
||||
|
||||
if (!(stat(blsdir, &sb) == 0 && S_ISDIR(sb.st_mode)))
|
||||
return 0;
|
||||
|
||||
n = scandir(blsdir, &bls_entries, bls_filter, bls_sort);
|
||||
if (n <= 0)
|
||||
return n;
|
||||
|
||||
while (array[count].id != 0)
|
||||
count++;
|
||||
|
||||
remaining = scan_size - count;
|
||||
|
||||
/* The array of scanned tokens is allocated when the zipl config file is
|
||||
* parsed. Its size is a multiple of INITIAL_ARRAY_LENGTH so it may have
|
||||
* enough space to scan all the tokens that are defined in the BLS files.
|
||||
* Calculate if is enough assuming that a BLS fragment can contain up to
|
||||
* 4 tokens: a section heading and 3 keywords (image, ramdisk, parameter).
|
||||
*/
|
||||
if (remaining < n * 4) {
|
||||
size = scan_size - remaining + (n * 4);
|
||||
buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token));
|
||||
if (!buffer)
|
||||
goto err;
|
||||
memset(buffer, 0, size * sizeof(struct scan_token));
|
||||
memcpy(buffer, array, count * sizeof(struct scan_token));
|
||||
} else {
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
while (n--) {
|
||||
sprintf(filename, "%s/%s", blsdir, bls_entries[n]->d_name);
|
||||
printf("Using BLS config file '%s'\n", filename);
|
||||
|
||||
rc = misc_get_file_buffer(filename, &file);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = sort_bls_fields(&file, filename);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
while ((size_t)file.pos < file.length) {
|
||||
current = misc_get_char(&file, 0);
|
||||
switch (current) {
|
||||
case '#':
|
||||
file.pos++;
|
||||
skip_line(&file);
|
||||
break;
|
||||
case EOF:
|
||||
break;
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\0':
|
||||
case ' ':
|
||||
file.pos++;
|
||||
break;
|
||||
default:
|
||||
rc = scan_bls_field(&file, buffer, &count);
|
||||
if (rc) {
|
||||
error_reason("Incorrect BLS field in "
|
||||
"config file %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
misc_free_file_buffer(&file);
|
||||
free(bls_entries[n]);
|
||||
}
|
||||
|
||||
*token = buffer;
|
||||
rc = 0;
|
||||
err:
|
||||
if (n > 0) {
|
||||
do {
|
||||
free(bls_entries[n]);
|
||||
} while (n-- > 0);
|
||||
}
|
||||
|
||||
free(bls_entries);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Search scanned tokens SCAN for a section/menu heading (according to
|
||||
* TYPE) of the given NAME, beginning at token OFFSET. Return the index of
|
||||
@@ -1358,6 +1263,163 @@ scan_get_section_keywords(struct scan_token* scan, int* index, char* name,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the default target defined either in the defaultboot section or the
|
||||
* menu section for the defaultmenu if there is a default menu defined.
|
||||
*/
|
||||
static char *scan_get_default_target(struct scan_token *scan)
|
||||
{
|
||||
int keyword_line[SCAN_KEYWORD_NUM];
|
||||
char *keyword[SCAN_KEYWORD_NUM];
|
||||
int num_line[BOOT_MENU_ENTRIES];
|
||||
char *num[BOOT_MENU_ENTRIES];
|
||||
int i, j, rc;
|
||||
|
||||
/* Find the defaultboot section */
|
||||
i = scan_find_section(scan, DEFAULTBOOT_SECTION,
|
||||
scan_id_section_heading, 0);
|
||||
if (i < 0) {
|
||||
error_reason("No '%s' section found and no section specified on command line",
|
||||
DEFAULTBOOT_SECTION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get keyword and number data */
|
||||
rc = scan_get_section_keywords(scan, &i, DEFAULTBOOT_SECTION, keyword,
|
||||
keyword_line, NULL, NULL);
|
||||
if (rc) {
|
||||
error_reason("Could not get keywords for '%s' section",
|
||||
DEFAULTBOOT_SECTION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if a defaultmenu is set */
|
||||
i = (int) scan_keyword_defaultmenu;
|
||||
if (keyword[i]) {
|
||||
j = scan_find_section(scan, keyword[i],
|
||||
scan_id_menu_heading, 0);
|
||||
if (j < 0) {
|
||||
error_reason("No '%s' section found", keyword[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get keyword and number data */
|
||||
rc = scan_get_section_keywords(scan, &j, keyword[i], keyword,
|
||||
keyword_line, num, num_line);
|
||||
if (rc) {
|
||||
error_reason("Could not get keywords for '%s' section",
|
||||
keyword[i]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return keyword[(int) scan_keyword_target];
|
||||
}
|
||||
|
||||
#define BLS_TOKEN_MAX 5 /* section heading, image, ramdisk, parameter, target */
|
||||
|
||||
int scan_bls(const char *blsdir, struct scan_token **token, int scan_size)
|
||||
{
|
||||
int size, remaining = 0, n, current, rc = -1, count = 0;
|
||||
struct scan_token *array = *token;
|
||||
struct misc_file_buffer file;
|
||||
struct dirent **bls_entries;
|
||||
struct scan_token *buffer;
|
||||
char filename[PATH_MAX];
|
||||
char *target = NULL;
|
||||
struct stat sb;
|
||||
|
||||
if (!(stat(blsdir, &sb) == 0 && S_ISDIR(sb.st_mode)))
|
||||
return 0;
|
||||
|
||||
n = scandir(blsdir, &bls_entries, bls_filter, bls_sort);
|
||||
if (n <= 0)
|
||||
return n;
|
||||
|
||||
while (array[count].id != 0)
|
||||
count++;
|
||||
|
||||
remaining = scan_size - count;
|
||||
|
||||
/* The array of scanned tokens is allocated when the zipl config file is
|
||||
* parsed. Its size is a multiple of INITIAL_ARRAY_LENGTH so it may have
|
||||
* enough space to scan all the tokens that are defined in the BLS files.
|
||||
* Calculate if is enough assuming that a BLS fragment can contain up to
|
||||
* BLS_TOKEN_MAX.
|
||||
*/
|
||||
if (remaining < n * BLS_TOKEN_MAX) {
|
||||
size = scan_size - remaining + (n * BLS_TOKEN_MAX);
|
||||
buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token));
|
||||
if (!buffer)
|
||||
goto err;
|
||||
memset(buffer, 0, size * sizeof(struct scan_token));
|
||||
memcpy(buffer, array, count * sizeof(struct scan_token));
|
||||
} else {
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
target = scan_get_default_target(array);
|
||||
while (n--) {
|
||||
sprintf(filename, "%s/%s", blsdir, bls_entries[n]->d_name);
|
||||
printf("Using BLS config file '%s'\n", filename);
|
||||
|
||||
rc = misc_get_file_buffer(filename, &file);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = sort_bls_fields(&file, filename);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
while ((size_t)file.pos < file.length) {
|
||||
current = misc_get_char(&file, 0);
|
||||
switch (current) {
|
||||
case '#':
|
||||
file.pos++;
|
||||
skip_line(&file);
|
||||
break;
|
||||
case EOF:
|
||||
break;
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\0':
|
||||
case ' ':
|
||||
file.pos++;
|
||||
break;
|
||||
default:
|
||||
rc = scan_bls_field(&file, buffer, &count);
|
||||
if (rc) {
|
||||
error_reason("Incorrect BLS field in config file %s\n",
|
||||
filename);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target != NULL)
|
||||
scan_append_keyword_assignment(buffer, &count,
|
||||
scan_keyword_target,
|
||||
target);
|
||||
|
||||
misc_free_file_buffer(&file);
|
||||
free(bls_entries[n]);
|
||||
}
|
||||
|
||||
*token = buffer;
|
||||
rc = 0;
|
||||
err:
|
||||
if (n > 0) {
|
||||
do {
|
||||
free(bls_entries[n]);
|
||||
} while (n-- > 0);
|
||||
}
|
||||
|
||||
free(bls_entries);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Check section at INDEX for compliance with config file rules. Upon success,
|
||||
* return zero and advance INDEX to point to the end of the section. Return
|
||||
* non-zero otherwise. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.\" Copyright IBM Corp. 2018
|
||||
.\" Copyright IBM Corp. 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.
|
||||
.\"
|
||||
@@ -17,7 +17,7 @@
|
||||
. PD
|
||||
..
|
||||
.
|
||||
.TH zpcictl 8 "Oct 2018" s390-tools zpcictl
|
||||
.TH zpcictl 8 "Mar 2022" s390-tools zpcictl
|
||||
.
|
||||
.SH NAME
|
||||
zpcictl - Manage PCI devices on IBM Z
|
||||
@@ -52,7 +52,45 @@ device (e.g.
|
||||
.SH OPTIONS
|
||||
.SS Error Handling Options
|
||||
.OD reset "" "DEVICE"
|
||||
Reset and re-initialize the PCI device.
|
||||
Reset and re-initialize the PCI device and report a device error to the Support
|
||||
Element (SE). The reset consists of a controlled shutdown and a subsequent
|
||||
re-enabling of the device from the shut off state. This process destroys and
|
||||
then re-creates higher level interfaces such as network interfaces and block
|
||||
devices. This reset is disruptive and often requires manual intervention on
|
||||
multiple layers. In particular, network interfaces that are part of a bonded
|
||||
interface must be re-added to the bond after the reset. Similarly, block
|
||||
devices backed by an NVMe that are part of a software RAID must be re-synced by
|
||||
re-adding to the RAID after resetting the NVMe.
|
||||
|
||||
Use this reset option only if the less disruptive automatic recovery mechanism
|
||||
is not supported by your kernel or it failed to restore the device's
|
||||
functionality. Unsuccessful automatic recovery can result in kernel messages
|
||||
indicating required manual intervention. If the device is malfunctioning
|
||||
without automatic recovery being triggered, consider using the \fB--reset-fw\fR
|
||||
option to trigger a less disruptive automatic recovery through
|
||||
a firmware-driven reset.
|
||||
.PP
|
||||
.
|
||||
.OD reset-fw "" "DEVICE"
|
||||
Reset the PCI device using a firmware-driven reset that also reports a device
|
||||
error on the Support Element (SE). If supported by your kernel, automatic recovery
|
||||
re-initializes the device after the firmware reports a successful device reset.
|
||||
|
||||
Use this option if the device is malfunctioning and automatic recovery is
|
||||
supported by the kernel but was not triggered. This condition can occur if the
|
||||
error is not detected by the low level PCI interfaces. A successful automatic
|
||||
recovery after the firmware-driven reset, is less disruptive than the full
|
||||
reset that is performed by the \fB--reset\fR option. Other than the full reset,
|
||||
the automatic recovery does not completely shut down the device and re-create
|
||||
it from the shut down state. Instead, it works with the device driver to
|
||||
restore the device in place. Thus, higher level interfaces such as network
|
||||
interfaces and block devices remain intact. In particular, with this type of
|
||||
reset high availability mechanisms like a bonded network interface or
|
||||
a software RAID can transparently re-integrate the recovered device. For
|
||||
example, after a failure and recovery, a software RAID can resync a stroage
|
||||
device or a network interface can be re-integrated in a bond. In contrast to
|
||||
a complete shut down, the device driver remains active and informs higher
|
||||
layers of both the occurence of an error state and the eventual recovery.
|
||||
.PP
|
||||
.
|
||||
.OD deconfigure "" "DEVICE"
|
||||
|
||||
@@ -46,12 +46,27 @@ static const struct util_prg prg = {
|
||||
#define OPT_RESET 128
|
||||
#define OPT_DECONF 129
|
||||
#define OPT_REPORT_ERR 130
|
||||
#define OPT_RESET_FW 131
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("ERROR HANDLING OPTIONS"),
|
||||
{
|
||||
.option = { "reset", no_argument, NULL, OPT_RESET },
|
||||
.desc = "Reset the device",
|
||||
.desc = "Reset the device and report an error to the Support Element (SE). "
|
||||
"The reset consists of a controlled shutdown and a subsequent "
|
||||
"re-enabling of the device. As a result, higher level interfaces such "
|
||||
"as network interfaces and block devices are destroyed and re-created.\n"
|
||||
"Manual configuration steps might be required to re-integrate the device, "
|
||||
"for example, in bonded interfaces or software RAIDs.\n"
|
||||
"Use this option only if the automatic recovery failed, or if it did "
|
||||
"not succeed to restore regular operations of the device and manual "
|
||||
"intervention is required.\n",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "reset-fw", no_argument, NULL, OPT_RESET_FW },
|
||||
.desc = "Reset the device through a firmware driven reset that triggers "
|
||||
"automatic recovery and reports an error to the Support Element (SE).\n",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
@@ -337,10 +352,19 @@ static void sclp_issue_action(struct zpci_device *pdev, int action)
|
||||
*/
|
||||
static void sclp_reset_device(struct zpci_device *pdev)
|
||||
{
|
||||
sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET);
|
||||
sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_REPORT_ERR);
|
||||
sysfs_write_value(pdev, "recover", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the PCI device via firmware and let auto recovery handle
|
||||
* re-initialization
|
||||
*/
|
||||
static void sclp_reset_device_fw(struct zpci_device *pdev)
|
||||
{
|
||||
sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET);
|
||||
}
|
||||
|
||||
/*
|
||||
* De-Configure/repair PCI device. Moves the device from configured
|
||||
* to reserved state.
|
||||
@@ -372,6 +396,9 @@ static void parse_cmdline(int argc, char *argv[], struct options *opts)
|
||||
case OPT_RESET:
|
||||
opts->reset = 1;
|
||||
break;
|
||||
case OPT_RESET_FW:
|
||||
opts->reset_fw = 1;
|
||||
break;
|
||||
case OPT_DECONF:
|
||||
opts->deconfigure = 1;
|
||||
break;
|
||||
@@ -411,6 +438,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (opts.reset)
|
||||
sclp_reset_device(&pdev);
|
||||
if (opts.reset_fw)
|
||||
sclp_reset_device_fw(&pdev);
|
||||
else if (opts.deconfigure)
|
||||
sclp_deconfigure(&pdev);
|
||||
else if (opts.report)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
struct options {
|
||||
unsigned int reset;
|
||||
unsigned int reset_fw;
|
||||
unsigned int deconfigure;
|
||||
unsigned int report;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user