mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
119 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 | ||
|
|
18efac6306 | ||
|
|
18f8db2225 | ||
|
|
f1a13749df | ||
|
|
b5da337d1e | ||
|
|
a67888f36a | ||
|
|
e1f174ae22 | ||
|
|
7503d88b3b | ||
|
|
43039943cd | ||
|
|
b2e02d202a | ||
|
|
1faa5d2957 | ||
|
|
d62e075450 | ||
|
|
983233730c | ||
|
|
ad024c06e1 | ||
|
|
e2843232d5 | ||
|
|
b79d17d233 | ||
|
|
11b401b599 | ||
|
|
69c5ee2e52 | ||
|
|
3ee625d74f | ||
|
|
832268ecb0 | ||
|
|
5fb6434548 | ||
|
|
5894e3f700 | ||
|
|
e384f06014 | ||
|
|
1627c6a39f | ||
|
|
bb8f17a7da | ||
|
|
8b3d3dd4bd | ||
|
|
a5c04e5746 | ||
|
|
c5ae1a41da | ||
|
|
970c697983 | ||
|
|
cff4b0384f | ||
|
|
724f800dc5 | ||
|
|
4cdfe91ca9 | ||
|
|
a44db0f367 | ||
|
|
03ddcd6267 | ||
|
|
5c468a220e | ||
|
|
4ba65eaa74 | ||
|
|
58cae4fecd | ||
|
|
9b84de4b0a | ||
|
|
e679a88d88 | ||
|
|
6acf6ed76d | ||
|
|
e7abf256f6 | ||
|
|
0d407904dd | ||
|
|
2238eb820f | ||
|
|
81013f0c70 | ||
|
|
697c5dc405 | ||
|
|
e9deec4c62 | ||
|
|
d60b7770fd | ||
|
|
70925f9aff | ||
|
|
ef88fac874 | ||
|
|
4bd16ba8ea | ||
|
|
f6a7da141c | ||
|
|
1bbd34e500 | ||
|
|
c2f8988444 | ||
|
|
d2990db15f | ||
|
|
fd3d79335a | ||
|
|
07b3837e68 | ||
|
|
13a1227a02 | ||
|
|
52118a8d02 | ||
|
|
ee89b850fe | ||
|
|
5160f41079 | ||
|
|
5b0115fdb4 | ||
|
|
ae305b5158 |
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
# Check https://editorconfig.org for details
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
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
|
||||
@@ -38,6 +39,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Fritz Elfert
|
||||
- Gerald Schaefer
|
||||
- Gerhard Tonn
|
||||
- Graham Inggs
|
||||
- Guevenc Guelce
|
||||
- Hannes Reinecke
|
||||
- Hans-Joachim Picht
|
||||
@@ -52,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
|
||||
@@ -74,6 +76,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Martin Peschke
|
||||
- Martin Petermann
|
||||
- Martin Schwidefsky
|
||||
- Matthew Rosato
|
||||
- Maxim Shchetynin
|
||||
- Melissa Howland
|
||||
- Michael Ernst
|
||||
@@ -103,6 +106,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Stefan Raspl
|
||||
- Stefan Reimbold
|
||||
- Stefan Weinhuber
|
||||
- Steffen Eiden
|
||||
- Steffen Maier
|
||||
- Steffen Thoss
|
||||
- Susanne Wintenberger
|
||||
|
||||
56
CHANGELOG.md
56
CHANGELOG.md
@@ -1,5 +1,61 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __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
|
||||
|
||||
Add new tools / libraries:
|
||||
- Add EditorConfig configuration
|
||||
|
||||
Changes of existing tools:
|
||||
- s390-tools switches to Fuse 3 as Fuse 2 is deprecated.
|
||||
Affected tools: cmsfs, hmcdrvfs, hsavmcore, zdsfs, zdump
|
||||
- chreipl-fcp-mpath: don't compress the manpage before installing it
|
||||
- cpictl: Report extended version information
|
||||
- genprotimg: Add extended kernel command line support
|
||||
- zdev: modify the lsblk output parser in lszdev
|
||||
- zipl: Add support for longer kernel command lines (now supports up to 64k length)
|
||||
|
||||
Bug Fixes:
|
||||
- cpictl: Suppress messages for unwritable sysfs files
|
||||
- dbginfo.sh: Fix missing syslog for step create_package
|
||||
- lshwc: Fix CPU list parameter setup for device driver
|
||||
- zdev: Check for errors when removing a devtype setting
|
||||
- zdev: Fix path resolution for multi-mount point file systems
|
||||
|
||||
* __v2.19.0 (2021-11-10)__
|
||||
|
||||
For Linux kernel version: 5.15
|
||||
|
||||
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 \
|
||||
|
||||
17
README.md
17
README.md
@@ -292,7 +292,7 @@ build options:
|
||||
|
||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||
|----------------|:------------------:|:-------------------------------------:|
|
||||
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||
| | | hsavmcore |
|
||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||
@@ -355,12 +355,12 @@ the different tools are provided:
|
||||
|
||||
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
|
||||
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
|
||||
FUSE is provided by installing the fuse and libfuse packages and by a
|
||||
FUSE is provided by installing the fuse3 and libfuse3 packages and by a
|
||||
kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package
|
||||
the fuse-devel package is required.
|
||||
The cmsfs-fuse tool requires FUSE version 2.8.1 or newer for full
|
||||
the fuse3-devel package is required.
|
||||
The cmsfs-fuse tool requires FUSE version 3.0 or newer for full
|
||||
functionality.
|
||||
For further information about FUSE see: http://fuse.sourceforge.net
|
||||
For further information about FUSE see: https://github.com/libfuse/libfuse
|
||||
|
||||
* hyptop:
|
||||
The ncurses-devel package is required to build hyptop.
|
||||
@@ -456,7 +456,7 @@ the different tools are provided:
|
||||
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
* hsavmcore:
|
||||
For building the hsavmcore tool you need fuse version 2.6 and optionally
|
||||
For building the hsavmcore tool you need fuse version 3.0 and optionally
|
||||
systemd which is enabled by default, to disable systemd support,
|
||||
add `HAVE_SYSTEMD=0` to the make invocation.
|
||||
Tip: you may skip the hsavmcore build by adding `HAVE_FUSE=0`
|
||||
@@ -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, GNU awk, and GNU Gzip 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.
|
||||
|
||||
@@ -32,7 +32,7 @@ Usage: $cmd [-t LAST_GIT_TAG]
|
||||
Add all new authors since last release to the $authors_file file. Without
|
||||
the -t option the tool uses the last available git tag as last release.
|
||||
|
||||
The tool should be called form the s390-tools master branch.
|
||||
The tool runs on the s390-tools master branch.
|
||||
|
||||
OPTIONS
|
||||
-t, --tag LAST_GIT_TAG Use git tag LAST_GIT_TAG as starting point
|
||||
@@ -116,7 +116,7 @@ echo "$cmd: Adding new authors since tag: $release_tag_last"
|
||||
# Print authors list without header
|
||||
tail -n +4 $authors_file_path
|
||||
# Add the new authors
|
||||
git log --format="- %an" $release_tag_last..
|
||||
git log --format="- %an" $release_tag_last..master
|
||||
# Then sort everything and remove duplicates
|
||||
} | sort | uniq > $authors_file_tmp
|
||||
# Create new AUTHORS file with header ...
|
||||
|
||||
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
|
||||
2
chreipl-fcp-mpath/.gitignore
vendored
2
chreipl-fcp-mpath/.gitignore
vendored
@@ -9,5 +9,3 @@
|
||||
/README.html
|
||||
/README.pdf
|
||||
/chreipl-fcp-mpath.md
|
||||
/chreipl-fcp-mpath.7
|
||||
/chreipl-fcp-mpath.7.gz
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
# - chreipl-fcp-mpath.mak
|
||||
# bash:
|
||||
# - bash
|
||||
# GNU coreutils:
|
||||
# - sha256sum
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# GNU Gzip:
|
||||
# - gzip
|
||||
# GNU awk:
|
||||
# - gawk
|
||||
|
||||
@@ -102,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
|
||||
#
|
||||
@@ -133,6 +154,7 @@ install: chreipl-fcp-mpath-install
|
||||
|
||||
# install udev rules
|
||||
INSTDIRS += $(UDEVRULESDIR)
|
||||
$(DESTDIR)$(UDEVRULESDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-udev-rules
|
||||
chreipl-fcp-mpath-install-udev-rules: | $(DESTDIR)$(UDEVRULESDIR)
|
||||
@@ -144,6 +166,7 @@ chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-rules
|
||||
|
||||
# install udev helper programs
|
||||
INSTDIRS += $(UDEVDIR)
|
||||
$(DESTDIR)$(UDEVDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-udev-helper
|
||||
chreipl-fcp-mpath-install-udev-helper: | $(DESTDIR)$(UDEVDIR)
|
||||
@@ -154,6 +177,7 @@ chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-helper
|
||||
|
||||
# install common library files
|
||||
INSTDIRS += $(CHREIPLZFCPMPDIR)
|
||||
$(DESTDIR)$(CHREIPLZFCPMPDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-libfiles
|
||||
chreipl-fcp-mpath-install-libfiles: | $(DESTDIR)$(CHREIPLZFCPMPDIR)
|
||||
@@ -167,6 +191,7 @@ ifeq ($(HAVE_DRACUT),1)
|
||||
|
||||
# install dracut config files
|
||||
INSTDIRS += $(DRACUTCONFDIR)
|
||||
$(DESTDIR)$(DRACUTCONFDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-dracut-config
|
||||
chreipl-fcp-mpath-install-dracut-config: | $(DESTDIR)$(DRACUTCONFDIR)
|
||||
@@ -178,28 +203,18 @@ 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
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-man-page
|
||||
chreipl-fcp-mpath-install-man-page: | $(DESTDIR)$(MANDIR)/man7
|
||||
chreipl-fcp-mpath-install-man-page: chreipl-fcp-mpath.7
|
||||
$(GZIP) -fk --best chreipl-fcp-mpath.7
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \
|
||||
chreipl-fcp-mpath.7.gz
|
||||
chreipl-fcp-mpath.7
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-man-page-clean
|
||||
chreipl-fcp-mpath-install-man-page-clean:
|
||||
rm -f chreipl-fcp-mpath.7.gz
|
||||
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-install-man-page-clean
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
## Utility
|
||||
#
|
||||
|
||||
@@ -120,19 +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 Gzip.
|
||||
|
||||
otherwise the pre-cooked version shipped with the source will be used.
|
||||
|
||||
INSTALLATION
|
||||
============
|
||||
@@ -161,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:
|
||||
@@ -196,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
|
||||
@@ -207,7 +208,7 @@ files to these default locations:
|
||||
/usr/lib/udev/chreipl-fcp-mpath-record-volume-identifier
|
||||
/usr/lib/udev/chreipl-fcp-mpath-try-change-ipl-path
|
||||
/usr/lib/udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
/usr/share/man/man7/chreipl-fcp-mpath.7.gz
|
||||
/usr/share/man/man7/chreipl-fcp-mpath.7
|
||||
|
||||
UNINSTALL
|
||||
=========
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
|
||||
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)
|
||||
@@ -16,17 +16,18 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"cmsfs-fuse", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
|
||||
all: check_dep cmsfs-fuse
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) -lm
|
||||
|
||||
@@ -80,9 +80,6 @@ Allow access by other users
|
||||
\fB\-o\fR allow_root
|
||||
Allow access by root
|
||||
.TP
|
||||
\fB\-o\fR nonempty
|
||||
Allow mounts over non\-empty file/dir
|
||||
.TP
|
||||
\fB\-o\fR default_permissions
|
||||
Enable permission checking by kernel
|
||||
.TP
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -1516,7 +1516,7 @@ static void walk_dir_block(struct fst_entry *fst, struct walk_file *walk,
|
||||
decode_edf_name(file, fst->name, fst->type);
|
||||
if (!file_unlinked(file)) {
|
||||
cache_fst_addr(walk->addr, file);
|
||||
walk->filler(walk->buf, file, NULL, 0);
|
||||
walk->filler(walk->buf, file, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1728,8 +1728,11 @@ static off_t get_file_size_logical(struct fst_entry *fst)
|
||||
return total;
|
||||
}
|
||||
|
||||
static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
||||
static int cmsfs_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void) fi;
|
||||
|
||||
int mask = (cmsfs.allow_other) ? 0444 : 0440;
|
||||
struct fst_entry fst;
|
||||
|
||||
@@ -1783,13 +1786,15 @@ static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
||||
}
|
||||
|
||||
static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct walk_file walk;
|
||||
struct fst_entry fst;
|
||||
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
/*
|
||||
* Offset is ignored and 0 passed to the filler fn so the whole
|
||||
@@ -1800,8 +1805,8 @@ static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
|
||||
memset(&walk, 0, sizeof(walk));
|
||||
/* readdir is possible without open so fi->fh is not set */
|
||||
@@ -2683,13 +2688,16 @@ static int cmsfs_statfs(const char *path, struct statvfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmsfs_utimens(const char *path, const struct timespec ts[2])
|
||||
static int cmsfs_utimens(const char *path, const struct timespec ts[2],
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct fst_entry fst;
|
||||
off_t fst_addr;
|
||||
struct tm tm;
|
||||
int rc;
|
||||
|
||||
(void) fi;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EACCES;
|
||||
|
||||
@@ -2825,7 +2833,8 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cmsfs_rename(const char *path, const char *new_path)
|
||||
static int cmsfs_rename(const char *path, const char *new_path,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct fst_entry fst, fst_new;
|
||||
off_t fst_addr, fst_addr_new;
|
||||
@@ -2835,6 +2844,8 @@ static int cmsfs_rename(const char *path, const char *new_path)
|
||||
struct file *f;
|
||||
int rc;
|
||||
|
||||
(void) flags;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EACCES;
|
||||
|
||||
@@ -3232,13 +3243,16 @@ static void update_fst(struct file *f, off_t addr)
|
||||
unhide_null_blocks(f);
|
||||
}
|
||||
|
||||
static int cmsfs_truncate(const char *path, off_t size)
|
||||
static int cmsfs_truncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct fst_entry fst;
|
||||
off_t fst_addr, len;
|
||||
struct file *f;
|
||||
int rc = 0;
|
||||
|
||||
(void) fi;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EROFS;
|
||||
|
||||
|
||||
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 = 19
|
||||
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:
|
||||
|
||||
122
cpumf/lshwc.c
122
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) {
|
||||
@@ -298,14 +281,8 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
words[no_b] |= 1ULL << no_a;
|
||||
}
|
||||
}
|
||||
/* no_b is highest used index, swap array */
|
||||
start->cpumask_len = (no_b + 1) * 8;
|
||||
for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
|
||||
uint64_t tmp = words[no_a];
|
||||
|
||||
words[no_a] = words[no_b];
|
||||
words[no_b] = tmp;
|
||||
}
|
||||
/* no_b is highest used index */
|
||||
start->cpumask_len = (no_b + 1) * CHAR_BIT;
|
||||
start->version = S390_HWCTR_START_VERSION;
|
||||
}
|
||||
|
||||
@@ -638,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"),
|
||||
{
|
||||
@@ -752,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 $@
|
||||
|
||||
@@ -28,13 +28,23 @@ static inline void __noreturn load_psw(struct psw_t psw)
|
||||
;
|
||||
}
|
||||
|
||||
static unsigned long get_kernel_cmdline_size(void)
|
||||
{
|
||||
unsigned long size = *(volatile unsigned long *)MAX_COMMAND_LINE_SIZE;
|
||||
|
||||
if (size != 0)
|
||||
return size;
|
||||
|
||||
return LEGACY_COMMAND_LINE_SIZE;
|
||||
}
|
||||
|
||||
void __noreturn start(void)
|
||||
{
|
||||
volatile struct stage3b_args *args = &loader_parms;
|
||||
volatile struct memblob *kernel = &args->kernel;
|
||||
volatile struct memblob *cmdline = &args->cmdline;
|
||||
volatile struct memblob *initrd = &args->initrd;
|
||||
volatile struct psw_t psw = args->psw;
|
||||
struct psw_t psw = args->psw;
|
||||
|
||||
/* set up ASCII and line-mode */
|
||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||
@@ -42,14 +52,14 @@ void __noreturn start(void)
|
||||
if (kernel->size < IMAGE_LOAD_ADDRESS)
|
||||
panic(EINTERNAL, "Invalid kernel\n");
|
||||
|
||||
if (cmdline->size > COMMAND_LINE_SIZE)
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel and cut the kernel header */
|
||||
memmove((void *)IMAGE_LOAD_ADDRESS,
|
||||
(void *)(kernel->src + IMAGE_LOAD_ADDRESS),
|
||||
kernel->size - IMAGE_LOAD_ADDRESS);
|
||||
|
||||
if (cmdline->size > get_kernel_cmdline_size())
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel cmdline */
|
||||
memmove((void *)COMMAND_LINE,
|
||||
(void *)cmdline->src,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -248,19 +248,6 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Convert uint64_t address to byte array */
|
||||
static void uint64_to_uint8_buf(uint8_t dst[8], uint64_t addr)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)&addr;
|
||||
|
||||
g_assert(dst);
|
||||
|
||||
for (gint i = 0; i < 8; i++) {
|
||||
/* cppcheck-suppress objectIndex */
|
||||
dst[i] = p[i];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err)
|
||||
{
|
||||
@@ -273,11 +260,8 @@ int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
|
||||
do {
|
||||
uint64_t cur_be = GUINT64_TO_BE(cur);
|
||||
uint8_t addr_buf[8];
|
||||
|
||||
uint64_to_uint8_buf(addr_buf, cur_be);
|
||||
|
||||
if (EVP_DigestUpdate(ctx, addr_buf, sizeof(addr_buf)) != 1) {
|
||||
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -440,7 +440,7 @@ static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject,
|
||||
|
||||
static X509_CRL *load_crl_from_bio(BIO *bio)
|
||||
{
|
||||
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);
|
||||
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
|
||||
gint rc;
|
||||
|
||||
if (crl)
|
||||
@@ -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);
|
||||
|
||||
@@ -16,17 +16,17 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"hmcdrvfs", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
ALL_CFLAGS += -DFUSE_USE_VERSION=26 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
||||
ALL_CFLAGS += -DFUSE_USE_VERSION=30 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) -lpthread -lrt -ldl -lm
|
||||
|
||||
OBJECTS = hmcdrvfs.o
|
||||
|
||||
@@ -111,9 +111,6 @@ allow access by other users
|
||||
.B -o allow_root
|
||||
allow access by root
|
||||
.TP
|
||||
.B -o nonempty
|
||||
allow mounts over non-empty file/dir
|
||||
.TP
|
||||
.B -o default_permissions
|
||||
enable permission checking by kernel
|
||||
.TP
|
||||
|
||||
@@ -990,7 +990,7 @@ static int hmcdrv_cache_dir(const char *dir, fuse_fill_dir_t filler, void *buf)
|
||||
hmcdrv_cache_refresh(path, &st, symlink);
|
||||
|
||||
if ((filler != NULL) &&
|
||||
(filler(buf, fname, &st, 0) != 0))
|
||||
(filler(buf, fname, &st, 0, 0) != 0))
|
||||
filler = NULL; /* stop filling */
|
||||
#ifdef DEBUG
|
||||
strftime(symlink, sizeof(symlink),
|
||||
@@ -1109,7 +1109,8 @@ static struct hmcdrv_fuse_file *hmcdrv_file_get(const char *path)
|
||||
*
|
||||
* Note: The most important function which FUSE calls (very often).
|
||||
*/
|
||||
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
{
|
||||
struct hmcdrv_fuse_file *fp;
|
||||
int rc = 0;
|
||||
@@ -1175,12 +1176,13 @@ static int hmcdrv_fuse_opendir(const char *UNUSED(path),
|
||||
*/
|
||||
static int hmcdrv_fuse_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t UNUSED(offset),
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
struct fuse_file_info *UNUSED(fi),
|
||||
enum fuse_readdir_flags UNUSED(flags))
|
||||
{
|
||||
int ret;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
|
||||
pthread_mutex_lock(&hmcdrv_ctx.mutex);
|
||||
ret = hmcdrv_cache_dir(path, filler, buf);
|
||||
@@ -1232,7 +1234,8 @@ static int hmcdrv_fuse_read(const char *path, char *buf, size_t size,
|
||||
* Return: value to be passed in the private_data field of fuse_context to
|
||||
* all file operations and as a parameter to the destroy() method
|
||||
*/
|
||||
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn))
|
||||
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn),
|
||||
struct fuse_config *UNUSED(cfg))
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ else # HAVE_FUSE
|
||||
# FUSE
|
||||
#
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
|
||||
#
|
||||
@@ -67,8 +67,9 @@ check-dep-fuse:
|
||||
$(call check_dep, \
|
||||
"hsavmcore", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
touch check-dep-fuse
|
||||
|
||||
install: all
|
||||
|
||||
@@ -15,7 +15,7 @@ sudo reboot
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo dnf install -y fuse fuse-devel systemd-devel
|
||||
sudo dnf install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -18,7 +18,7 @@ sudo reboot
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo zypper install -y fuse fuse-devel systemd-devel
|
||||
sudo zypper install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y make gcc kdump-tools fuse libfuse-dev libsystemd-dev
|
||||
sudo apt-get install -y make gcc kdump-tools fuse3 libfuse3-dev libsystemd-dev
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
#include <fuse.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
@@ -31,8 +31,11 @@ struct vmcore_overlay {
|
||||
bool fuse_debug;
|
||||
};
|
||||
|
||||
static int vmcore_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
static int vmcore_fuse_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)fi;
|
||||
|
||||
struct vmcore_overlay *overlay = fuse_get_context()->private_data;
|
||||
int ret = 0;
|
||||
|
||||
@@ -54,18 +57,20 @@ static int vmcore_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
|
||||
static int vmcore_fuse_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t offset,
|
||||
struct fuse_file_info *fi)
|
||||
struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
(void)offset;
|
||||
(void)fi;
|
||||
(void)flags;
|
||||
|
||||
if (strcmp(path, ROOT_DIR) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* We have only one file */
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#define INITRD_SIZE _AC(0x10410, UL)
|
||||
#define OLDMEM_BASE _AC(0x10418, UL)
|
||||
#define OLDMEM_SIZE _AC(0x10420, UL)
|
||||
#define MAX_COMMAND_LINE_SIZE _AC(0x10430, UL)
|
||||
#define COMMAND_LINE _AC(0x10480, UL)
|
||||
|
||||
/* Parameter sizes */
|
||||
#define COMMAND_LINE_SIZE 896
|
||||
#define LEGACY_COMMAND_LINE_SIZE 896
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ int dasd_sys_ese(char *devnode)
|
||||
return (rc == 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
static int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
{
|
||||
unsigned int val;
|
||||
int count, i;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ install:
|
||||
sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' cpictl > \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chown $(OWNER):$(GROUP) $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chmod 775 $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chmod 755 $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
|
||||
@for i in $(MAN_PAGES); \
|
||||
do \
|
||||
|
||||
250
scripts/cpictl
250
scripts/cpictl
@@ -24,6 +24,9 @@ readonly SYSTEM_NAME_PATH="/sys/firmware/cpi/system_name"
|
||||
readonly SYSPLEX_NAME_PATH="/sys/firmware/cpi/sysplex_name"
|
||||
readonly CPI_SET="/sys/firmware/cpi/set"
|
||||
|
||||
# Location of os-release file - can be specified externally for testing purpose
|
||||
readonly OS_RELEASE=${CPI_OS_RELEASE:-"/etc/os-release"}
|
||||
|
||||
declare LEVEL
|
||||
declare TYPE
|
||||
declare NAME
|
||||
@@ -37,6 +40,16 @@ readonly EXIT_FAILURE=1
|
||||
readonly EXIT_ARG_TOO_LONG=3
|
||||
readonly EXIT_INVALID_CHARS=4
|
||||
|
||||
# Distro-IDs as supported by SE/HMC firmware
|
||||
readonly DISTRO_GENERIC=0
|
||||
readonly DISTRO_RHEL=1
|
||||
readonly DISTRO_SLES=2
|
||||
readonly DISTRO_UBUNTU=3
|
||||
readonly DISTRO_FEDORA=4
|
||||
readonly DISTRO_OPENSUSE=5
|
||||
readonly DISTRO_DEBIAN=6
|
||||
readonly DISTRO_RHCOS=7
|
||||
|
||||
print_help_and_exit()
|
||||
{
|
||||
cat <<EndHelp
|
||||
@@ -49,7 +62,8 @@ Configure the Control-Program-Information (CPI) settings.
|
||||
sysplex name with values taken from environment
|
||||
variables
|
||||
-h, --help Print this help, then exit
|
||||
-L, --level LEVEL Set and commit OS level to LEVEL (format xx.yy.zzABCD)
|
||||
-L, --level LEVEL Set and commit OS level to LEVEL. Format: 0x<level> or
|
||||
[[[flags:]distro_id:distro_version:]kernel_version]
|
||||
-N, --name SYSTEM Set and commit the system name to SYSTEM
|
||||
-S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
|
||||
-T, --type TYPE Set and commit OS type to TYPE
|
||||
@@ -109,7 +123,7 @@ fail_with()
|
||||
|
||||
cpi_commit()
|
||||
{
|
||||
echo 1 > "$CPI_SET"
|
||||
echo 1 > "$CPI_SET" 2> /dev/null
|
||||
}
|
||||
|
||||
do_length_check()
|
||||
@@ -129,15 +143,233 @@ cpi_set_bit()
|
||||
LEVEL=$(printf '0x%x' $((LEVEL | (1 << (63 - $1)) )) )
|
||||
}
|
||||
|
||||
#
|
||||
# split_version - Split generic version string into array of sub-versions
|
||||
#
|
||||
# @version: Version string
|
||||
# @delim: Characters that delimit sub-versions in version string
|
||||
# @num: Number of sub-versions
|
||||
#
|
||||
# Print @num sub-versions of @version where each sub-version is delimited by
|
||||
# any of the characters in @delim. Print 0 in place of non-decimal or missing
|
||||
# sub-versions.
|
||||
#
|
||||
# Examples:
|
||||
# version=10 delim=. num=3 => 10 0 0
|
||||
# version=4.8 delim=. num=2 => 4 8
|
||||
#
|
||||
split_version()
|
||||
{
|
||||
local version="$1" delim="$2" num="$3"
|
||||
local list i subver
|
||||
|
||||
IFS="$delim" read -r -a list <<< "$version"
|
||||
|
||||
for (( i=0; i<num; i++ )) ; do
|
||||
subver="${list[i]:-0}"
|
||||
# Handle non-number sub-versions
|
||||
[[ "$subver" =~ ^[0-9]+$ ]] || subver=0
|
||||
# Force decimal interpretation in case of leading zeroes
|
||||
subver=$(( 10#$subver ))
|
||||
printf "%s " "$subver"
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# split_kver - Split Linux kernel version string into array of sub-versions
|
||||
#
|
||||
# @version: Linux kernel version string
|
||||
# @num: Number of sub-versions
|
||||
#
|
||||
# Print @num sub-versions of the specified kernel @version. Print 0 in place
|
||||
# of non-decimal or missing sub-versions.
|
||||
#
|
||||
# Examples:
|
||||
# version=2.4-13 num=4 => 2 4 0 13
|
||||
# version=3.0.93_3.0.101-0.8.2_0.8.1 num=6 => 3 0 93 3 0 101
|
||||
# version=4.12.14-lp150.11.4 num=5 => 4 12 14 0 11
|
||||
#
|
||||
split_kver()
|
||||
{
|
||||
local version="$1" num="$2"
|
||||
local main extra
|
||||
|
||||
# Separate extra version to handle short main version (e.g. 2.4-13)
|
||||
IFS="-_" read -r main extra <<< "$version"
|
||||
|
||||
split_version "$main" "." $(( num > 3 ? 3 : num ))
|
||||
[[ "$num" -gt 3 ]] && split_version "$extra" ".-_" $(( num - 3 ))
|
||||
}
|
||||
|
||||
#
|
||||
# bytes_to_word - Convert byte array to hexadecimal word
|
||||
#
|
||||
# @bytes: List of numbers representing byte values
|
||||
#
|
||||
# Print a big-endian hexadecimal representation of the word that results from
|
||||
# combining the specified byte values.
|
||||
#
|
||||
bytes_to_word()
|
||||
{
|
||||
printf "0x"
|
||||
printf "%02x" "$@"
|
||||
}
|
||||
|
||||
#
|
||||
# get_system_level - Print system level word for specified distribution version
|
||||
#
|
||||
# @distro: Distro ID (ID value from /etc/os-release)
|
||||
# @ver_str: Distro version string (VERSION_ID from /etc/os-release)
|
||||
# @kver_str: Kernel version string (output of 'uname -r')
|
||||
# @flags: Optional statistics flags
|
||||
#
|
||||
# Print a 64 bit hexadecimal system level in a format as understood by firmware.
|
||||
#
|
||||
# The format is 0xabccddeeeeffgghh, where
|
||||
# - a=statistics flags
|
||||
# - b=distro id
|
||||
# - c=distro major version
|
||||
# - d=distro minor version(s)
|
||||
# - e=kernel sublevel 2
|
||||
# - f=kernel version
|
||||
# - g=kernel patchlevel
|
||||
# - h=kernel sublevel 1
|
||||
#
|
||||
get_system_level()
|
||||
{
|
||||
local distro="$1" ver_str="$2" kver_str="$3" flags="${4:-0}"
|
||||
local distro_id d_major d_minor d_minor2
|
||||
local k_ver k_patchlvl k_sublvl k_sublvl2 bytes=()
|
||||
|
||||
# Extract list of sub-version numbers from version strings
|
||||
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")
|
||||
distro_id=$DISTRO_RHEL
|
||||
;;
|
||||
|
||||
"sles")
|
||||
distro_id=$DISTRO_SLES
|
||||
;;
|
||||
|
||||
"ubuntu")
|
||||
distro_id=$DISTRO_UBUNTU
|
||||
|
||||
# Encode minor and update version numbers in minor field
|
||||
(( d_minor=((d_minor & 0xf) * 0x10) + (d_minor2 & 0xf) ))
|
||||
;;
|
||||
|
||||
"fedora")
|
||||
distro_id=$DISTRO_FEDORA
|
||||
;;
|
||||
|
||||
"opensuse-leap")
|
||||
distro_id=$DISTRO_OPENSUSE
|
||||
;;
|
||||
|
||||
"debian")
|
||||
distro_id=$DISTRO_DEBIAN
|
||||
;;
|
||||
|
||||
"rhcos")
|
||||
distro_id=$DISTRO_RHCOS
|
||||
;;
|
||||
|
||||
*)
|
||||
distro_id=$DISTRO_GENERIC
|
||||
|
||||
# Reset unsupported fields
|
||||
d_major=0
|
||||
d_minor=0
|
||||
k_sublvl2=0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Assemble byte data
|
||||
(( bytes[0] = (flags & 0xf) * 0x10 + distro_id ))
|
||||
(( bytes[1] = d_major ))
|
||||
(( bytes[2] = d_minor ))
|
||||
(( bytes[3] = (k_sublvl2 / 256) & 0xff ))
|
||||
(( bytes[4] = k_sublvl2 & 0xff ))
|
||||
(( bytes[5] = k_ver ))
|
||||
(( bytes[6] = k_patchlvl ))
|
||||
(( bytes[7] = k_sublvl ))
|
||||
|
||||
# Print as single hex word
|
||||
bytes_to_word "${bytes[@]}"
|
||||
}
|
||||
|
||||
get_distro()
|
||||
{
|
||||
local line ID="linux" VERSION_ID="0" VERSION="" update
|
||||
|
||||
[[ ! -e "$OS_RELEASE" ]] && return
|
||||
|
||||
# Only import required variables
|
||||
while read -r line ; do
|
||||
if [[ "$line" =~ ^ID= ]] || [[ "$line" =~ ^VERSION_ID= ]] ||
|
||||
[[ "$line" =~ ^VERSION= ]] ; then
|
||||
eval "$line"
|
||||
fi
|
||||
done <"$OS_RELEASE"
|
||||
|
||||
if [[ "$ID" == "ubuntu" ]] ; then
|
||||
# Extract update version number only found in VERSION, e.g.
|
||||
# VERSION_ID="18.04" VERSION="18.04.5 LTS"
|
||||
update="${VERSION/*$VERSION_ID/}"
|
||||
update="${update%% *}"
|
||||
VERSION_ID="$VERSION_ID$update"
|
||||
fi
|
||||
|
||||
echo "$ID:$VERSION_ID"
|
||||
}
|
||||
|
||||
cpi_set_oslevel()
|
||||
{
|
||||
local kver=$(echo "${1:-$(uname -r)}" | grep -E -o '^[0-9]+[.][0-9]+[.][0-9]+')
|
||||
local maj=$((${kver%%.*} % 256))
|
||||
local min=${kver#*.}
|
||||
min=$((${min%.*} % 256))
|
||||
local rev=$((${kver##*.} % 256))
|
||||
local hexlevel=$(printf '0x%02x%02x%02x' $maj $min $rev)
|
||||
LEVEL=$(printf '0x%016x' $(((LEVEL & 0xFFFFFFFFFF000000) | hexlevel)))
|
||||
local level="${1:-}"
|
||||
local flags list distro_id distro_ver kver id ver
|
||||
|
||||
if [[ "$level" =~ ^0x ]] && ! [[ "$level" =~ : ]] ; then
|
||||
# Format: level=0x<hex>
|
||||
printf -v LEVEL "0x%016x" "$level" 2>/dev/null ||
|
||||
fail_with "$PRG: Invalid hexadecimal number in $level" \
|
||||
$EXIT_INVALID_CHARS
|
||||
return
|
||||
fi
|
||||
|
||||
# Format: level=[[[flags:]distro_id:distro_ver:]kver]
|
||||
IFS=":" read -r -a list <<< "$level:"
|
||||
kver="${list[*]: -1: 1}"
|
||||
distro_ver="${list[*]: -2: 1}"
|
||||
distro_id="${list[*]: -3: 1}"
|
||||
flags="${list[*]: -4: 1}"
|
||||
|
||||
if [[ -z "$kver" ]] ; then
|
||||
# Use version of currently running kernel
|
||||
kver="$(uname -r)"
|
||||
fi
|
||||
|
||||
if [[ -z "$distro_ver" ]] || [[ -z "$distro_id" ]] ; then
|
||||
# Use distro ID and version from os-release file
|
||||
IFS=":" read -r id ver <<< "$(get_distro)"
|
||||
distro_id=${distro_id:-$id}
|
||||
distro_ver=${distro_ver:-$ver}
|
||||
fi
|
||||
|
||||
if [[ -z "$flags" ]] ; then
|
||||
# Keep statistics flags from current system level
|
||||
flags=$(( (LEVEL >> 60) & 0xf ))
|
||||
fi
|
||||
|
||||
LEVEL=$(get_system_level "$distro_id" "$distro_ver" "$kver" "$flags")
|
||||
}
|
||||
|
||||
cpi_set_type()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "misc.h"
|
||||
|
||||
#define LSBLK_CMDLINE "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINT,PKNAME 2>/dev/null"
|
||||
#define LSBLK_CMDLINE2 "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINTS,PKNAME 2>/dev/null"
|
||||
|
||||
struct blkinfo {
|
||||
struct devnode *devnode;
|
||||
@@ -82,6 +84,26 @@ void blkinfo_print(struct blkinfo *blkinfo, int level)
|
||||
printf("%*sparent=%s\n", level, "", blkinfo->parent);
|
||||
}
|
||||
|
||||
/* Convert each occurrence of '\xnn' in @str to character with hex code <nn>. */
|
||||
static void hex_unescape(char *str)
|
||||
{
|
||||
unsigned int c;
|
||||
|
||||
while ((str = strstr(str, "\\x"))) {
|
||||
if (isxdigit(str[2]) && isxdigit(str[3]) &&
|
||||
sscanf(str + 2, "%2x", &c) == 1) {
|
||||
str[0] = (char)c;
|
||||
|
||||
/* Move remainder of str including nul behind <c>. */
|
||||
memmove(str + /* <c> */ 1,
|
||||
str + /* '\xnn' */ 4,
|
||||
strlen(str + 4) + /* <nul> */ 1);
|
||||
}
|
||||
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
static char *isolate_keyword(char **line_ptr, const char *keyword)
|
||||
{
|
||||
char *start, *end;
|
||||
@@ -102,30 +124,58 @@ static char *isolate_keyword(char **line_ptr, const char *keyword)
|
||||
return start;
|
||||
}
|
||||
|
||||
static struct blkinfo *blkinfo_from_line(char *line)
|
||||
static void add_blkinfos_from_line(struct util_list *blkinfos,
|
||||
char *line)
|
||||
{
|
||||
char *name, *majmin, *fstype, *uuid, *mountpoint, *parent;
|
||||
char *name, *majmin, *fstype, *uuid, *mountpoint, *mountpoints, *parent;
|
||||
struct blkinfo *blkinfo;
|
||||
|
||||
name = isolate_keyword(&line, "NAME=\"");
|
||||
majmin = isolate_keyword(&line, "MAJ:MIN=\"");
|
||||
if (!majmin)
|
||||
majmin = isolate_keyword(&line, "MAJ_MIN=\"");
|
||||
fstype = isolate_keyword(&line, "FSTYPE=\"");
|
||||
uuid = isolate_keyword(&line, "UUID=\"");
|
||||
mountpoint = isolate_keyword(&line, "MOUNTPOINT=\"");
|
||||
mountpoints = isolate_keyword(&line, "MOUNTPOINTS=\"");
|
||||
parent = isolate_keyword(&line, "PKNAME=\"");
|
||||
|
||||
return blkinfo_new(name, majmin, fstype, uuid, mountpoint, parent);
|
||||
if (!mountpoints) {
|
||||
/* Handle old lsblk output format. */
|
||||
blkinfo = blkinfo_new(name, majmin, fstype, uuid, mountpoint,
|
||||
parent);
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restore newline mount point separator encoded as hex. */
|
||||
hex_unescape(mountpoints);
|
||||
|
||||
/* Represent each mount point as a separate blkinfo to support
|
||||
* resolution of multi-mount point file systems like btrfs
|
||||
* subvolumes. */
|
||||
while ((mountpoint = strsep(&mountpoints, "\n"))) {
|
||||
blkinfo = blkinfo_new(name, majmin, fstype, uuid, mountpoint,
|
||||
parent);
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
}
|
||||
}
|
||||
|
||||
static struct util_list *blkinfos_read(void)
|
||||
{
|
||||
char *output, *curr, *next;
|
||||
struct util_list *blkinfos;
|
||||
struct blkinfo *blkinfo;
|
||||
|
||||
if (cached_blkinfos)
|
||||
return cached_blkinfos;
|
||||
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE2, 0, 1);
|
||||
if (output && !*output) {
|
||||
/* No output might indicate no support for new lsblk command-
|
||||
* line format - fall back to old format. */
|
||||
free(output);
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
||||
}
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
@@ -134,9 +184,7 @@ static struct util_list *blkinfos_read(void)
|
||||
/* Iterate over each line. */
|
||||
next = output;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
blkinfo = blkinfo_from_line(curr);
|
||||
if (blkinfo)
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
add_blkinfos_from_line(blkinfos, curr);
|
||||
}
|
||||
|
||||
free(output);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -2088,12 +2088,18 @@ static exit_code_t devtype_remove_settings(struct devtype *dt, config_t config,
|
||||
found = strlist_new();
|
||||
notfound = strlist_new();
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
remove_settings(dt->active_settings, names, found, notfound, 1);
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
rc = remove_settings(dt->active_settings, names, found,
|
||||
notfound, 1);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
remove_settings(dt->persistent_settings, names, found,
|
||||
notfound, 0);
|
||||
rc = remove_settings(dt->persistent_settings, names, found,
|
||||
notfound, 0);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!util_list_is_empty(notfound)) {
|
||||
@@ -2104,6 +2110,7 @@ static exit_code_t devtype_remove_settings(struct devtype *dt, config_t config,
|
||||
rc = EXIT_SETTING_NOT_FOUND;
|
||||
}
|
||||
|
||||
out:
|
||||
strlist_free(found);
|
||||
strlist_free(notfound);
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"zdsfs", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
$(call check_dep, \
|
||||
"zdsfs", \
|
||||
"curl/curl.h", \
|
||||
@@ -39,13 +40,13 @@ check_dep:
|
||||
"HAVE_CURL=0")
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
CURL_CFLAGS = $(shell pkg-config --silence-errors --cflags libcurl)
|
||||
CURL_LDLIBS = $(shell pkg-config --silence-errors --libs libcurl)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
CURL_CFLAGS = -I/usr/include/s390x-linux-gnu/curl
|
||||
CURL_LDLIBS = -lcurl
|
||||
endif
|
||||
|
||||
@@ -227,9 +227,6 @@ Allow access by other users
|
||||
\fB\-o\fR allow_root
|
||||
Allow access by root
|
||||
.TP
|
||||
\fB\-o\fR nonempty
|
||||
Allow mounts over non\-empty file/dir
|
||||
.TP
|
||||
\fB\-o\fR default_permissions
|
||||
Enable permission checking by kernel
|
||||
.TP
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/* The fuse version define tells fuse that we want to use the new API */
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -312,7 +312,8 @@ void keepalive_start(void)
|
||||
setup_timer(zdsfsinfo.keepalive);
|
||||
}
|
||||
|
||||
static int zdsfs_getattr(const char *path, struct stat *stbuf)
|
||||
static int zdsfs_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
{
|
||||
char normds[MAXDSNAMELENGTH];
|
||||
size_t dssize;
|
||||
@@ -524,7 +525,8 @@ static int zdsfs_update_vtoc(void)
|
||||
}
|
||||
|
||||
static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi))
|
||||
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi),
|
||||
enum fuse_readdir_flags UNUSED(flags))
|
||||
{
|
||||
char normds[MAXDSNAMELENGTH];
|
||||
char *mbrname;
|
||||
@@ -544,9 +546,9 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
* type one: the root directory contains all data sets
|
||||
*/
|
||||
if (strcmp(path, "/") == 0) {
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, METADATAFILE, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, METADATAFILE, NULL, 0, 0);
|
||||
/* note that we do not need to distinguish between
|
||||
* normal files and directories here, that is done
|
||||
* in the rdf_getattr function
|
||||
@@ -558,7 +560,7 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
lzds_dataset_get_is_supported(ds, &issupported);
|
||||
if (issupported) {
|
||||
lzds_dataset_get_name(ds, &dsname);
|
||||
filler(buf, dsname, NULL, 0);
|
||||
filler(buf, dsname, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
lzds_dsiterator_free(dsit);
|
||||
@@ -572,14 +574,14 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
return -ENOENT;
|
||||
lzds_dataset_get_is_PDS(ds, &ispds);
|
||||
if (ispds) {
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
rc = lzds_dataset_alloc_memberiterator(ds, &it);
|
||||
if (rc)
|
||||
return -ENOMEM;
|
||||
while (!lzds_memberiterator_get_next_member(it, &member)) {
|
||||
lzds_pdsmember_get_name(member, &mbrname);
|
||||
filler(buf, mbrname, NULL, 0);
|
||||
filler(buf, mbrname, NULL, 0, 0);
|
||||
}
|
||||
lzds_memberiterator_free(it);
|
||||
} else
|
||||
|
||||
@@ -15,8 +15,9 @@ check_dep_fuse:
|
||||
$(call check_dep, \
|
||||
"zgetdump mount support", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
endif
|
||||
|
||||
#
|
||||
@@ -41,28 +42,28 @@ check_dep_zlib:
|
||||
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o \
|
||||
OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dfi.o dfi_mem_chunk.o dfi_vmcoreinfo.o \
|
||||
dfi_lkcd.o dfi_elf.o \
|
||||
dfi_s390.o dfi_s390_ext.o\
|
||||
dfi_s390mv.o dfi_s390mv_ext.o \
|
||||
dfi_s390tape.o dfi_kdump.o \
|
||||
dfi_devmem.o dfo.o \
|
||||
dfi_devmem.o dfo.o dfo_mem_chunk.o \
|
||||
dfo_elf.o dfo_s390.o \
|
||||
df_s390.o \
|
||||
df_elf.o df_s390.o \
|
||||
dt.o dt_s390sv.o dt_s390sv_ext.o \
|
||||
dt_s390mv.o dt_s390mv_ext.o \
|
||||
dt_scsi.o stdout.o \
|
||||
dt_scsi.o output.o
|
||||
|
||||
ifeq ("$(HAVE_FUSE)","0")
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
|
||||
FUSE_LDLIBS =
|
||||
else ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
LDLIBS += -lz $(FUSE_LDLIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS)
|
||||
|
||||
191
zdump/df_elf.c
Normal file
191
zdump/df_elf.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "df_elf.h"
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
{
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
ehdr->e_ident[EI_ABIVERSION] = 0;
|
||||
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_S390;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_entry = 0;
|
||||
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_shoff = 0;
|
||||
ehdr->e_flags = 0;
|
||||
ehdr->e_ehsize = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_phentsize = sizeof(Elf64_Phdr);
|
||||
ehdr->e_phnum = phnum;
|
||||
ehdr->e_shentsize = 0;
|
||||
ehdr->e_shnum = 0;
|
||||
ehdr->e_shstrndx = 0;
|
||||
return ehdr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name)
|
||||
{
|
||||
Elf64_Nhdr *note;
|
||||
u64 len;
|
||||
|
||||
note = (Elf64_Nhdr *)buf;
|
||||
note->n_namesz = strlen(name) + 1;
|
||||
note->n_descsz = d_len;
|
||||
note->n_type = type;
|
||||
len = sizeof(Elf64_Nhdr);
|
||||
|
||||
memcpy(buf + len, name, note->n_namesz);
|
||||
len = ROUNDUP(len + note->n_namesz, 4);
|
||||
|
||||
memcpy(buf + len, desc, note->n_descsz);
|
||||
len = ROUNDUP(len + note->n_descsz, 4);
|
||||
|
||||
return PTR_ADD(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
static int cpu_nr = 1;
|
||||
|
||||
memset(&nt_prstatus, 0, sizeof(nt_prstatus));
|
||||
memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs));
|
||||
memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw));
|
||||
memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs));
|
||||
nt_prstatus.pr_pid = cpu_nr;
|
||||
cpu_nr++;
|
||||
|
||||
return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
|
||||
memset(&nt_fpregset, 0, sizeof(nt_fpregset));
|
||||
memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc));
|
||||
memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs));
|
||||
|
||||
return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
||||
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
||||
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
||||
sizeof(cpu->prefix), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_prpsinfo(void *ptr)
|
||||
{
|
||||
struct nt_prpsinfo_64 prpsinfo;
|
||||
|
||||
memset(&prpsinfo, 0, sizeof(prpsinfo));
|
||||
prpsinfo.pr_state = 0;
|
||||
prpsinfo.pr_sname = 'R';
|
||||
prpsinfo.pr_zomb = 0;
|
||||
strcpy(prpsinfo.pr_fname, "vmlinux");
|
||||
|
||||
return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
{
|
||||
if (!vmcoreinfo)
|
||||
return ptr;
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
|
||||
NOTE_NAME_VMCOREINFO);
|
||||
}
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "dfo.h"
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
|
||||
/*
|
||||
* S390 CPU timer note (u64)
|
||||
@@ -116,4 +116,20 @@ static inline void df_elf_ensure_s390x(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum);
|
||||
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name);
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu);
|
||||
void *nt_prpsinfo(void *ptr);
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo);
|
||||
|
||||
#endif /* DF_ELF_H */
|
||||
|
||||
@@ -286,6 +286,7 @@ const char *dfi_arch_str(enum dfi_arch arch)
|
||||
return "unknown";
|
||||
}
|
||||
ABORT("dfi_arch_str: Invalid dfi arch: %d", arch);
|
||||
return NULL; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -784,6 +785,7 @@ int dfi_init(void)
|
||||
i++;
|
||||
}
|
||||
ERR_EXIT("No valid dump found on \"%s\"", g.opts.device);
|
||||
return -1; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -556,7 +556,6 @@ static int open_dump(void)
|
||||
}
|
||||
if (mv_dumper_read() != 0)
|
||||
return -ENODEV;
|
||||
zg_close(g.fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
119
zdump/dfo.c
119
zdump/dfo.c
@@ -9,16 +9,13 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "zg.h"
|
||||
#include "dfo_mem_chunk.h"
|
||||
#include "dfo.h"
|
||||
|
||||
#define dfo_chunk_iterate(dfo_chunk) \
|
||||
util_list_iterate(&l.dump.chunk_list, dfo_chunk)
|
||||
|
||||
/*
|
||||
* DFO vector
|
||||
*/
|
||||
@@ -28,70 +25,14 @@ static struct dfo *dfo_vec[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Dump (output) information
|
||||
*/
|
||||
struct dump {
|
||||
u64 off; /* Current file offset in dump */
|
||||
u64 size; /* Size of dump in bytes */
|
||||
unsigned int chunk_cnt; /* Number of dump chunks */
|
||||
struct util_list chunk_list; /* DFO chunk list */
|
||||
};
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
struct dump dump;
|
||||
u64 off; /* Current file offset in dump */
|
||||
struct dfo *dfo;
|
||||
} l;
|
||||
|
||||
/*
|
||||
* Add dump chunk
|
||||
*/
|
||||
void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn)
|
||||
{
|
||||
struct dfo_chunk *dfo_chunk;
|
||||
|
||||
dfo_chunk = zg_alloc(sizeof(*dfo_chunk));
|
||||
dfo_chunk->start = start;
|
||||
dfo_chunk->end = start + size - 1;
|
||||
dfo_chunk->data = data;
|
||||
dfo_chunk->read_fn = read_fn;
|
||||
util_list_add_head(&l.dump.chunk_list, dfo_chunk);
|
||||
l.dump.chunk_cnt++;
|
||||
l.dump.size = MAX(l.dump.size, dfo_chunk->end + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy zero pages for chunk
|
||||
*/
|
||||
void dfo_chunk_zero_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
(void) dfo_chunk;
|
||||
(void) off;
|
||||
|
||||
memset(buf, 0, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy given buffer for chunk
|
||||
*/
|
||||
void dfo_chunk_buf_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
memcpy(buf, dfo_chunk->data + off, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy given memory range for chunk
|
||||
*/
|
||||
void dfo_chunk_mem_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk = dfo_chunk->data;
|
||||
|
||||
mem_chunk->read_fn(mem_chunk, off, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get DFO name
|
||||
*/
|
||||
@@ -125,55 +66,17 @@ void dfo_init(void)
|
||||
{
|
||||
if (!l.dfo)
|
||||
ABORT("DFO not set");
|
||||
util_list_init(&l.dump.chunk_list, struct dfo_chunk, list);
|
||||
if (dfo_chunk_init())
|
||||
ABORT("DFO memory chunk init failed");
|
||||
l.dfo->init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Find dump chunk for offset "off"
|
||||
*
|
||||
* This function is a bit hacky. DFO chunks can overlap. If two DFO chunks
|
||||
* overlap, the last registered chunk wins. The dfo_chunk_find() function
|
||||
* reflects that by returning the first memory chunk that is found in
|
||||
* the dfo chunk list.
|
||||
*
|
||||
* In addition to that it calculates the "virtual end" of that chunk. An
|
||||
* overlapping chunk can limit the "virtual end" of an underlying chunk so
|
||||
* that the "virtual end" of that chunk is lower than the "real end".
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* chunk 1.: |------|
|
||||
* chunk 2.: |---------------------|
|
||||
* off.....: ^
|
||||
* virt end: ^
|
||||
* real end: ^
|
||||
*
|
||||
* In this case chunk 2 will be returned and "end" is set to the start of
|
||||
* chunk 1.
|
||||
*/
|
||||
static struct dfo_chunk *dfo_chunk_find(u64 off, u64 *end)
|
||||
{
|
||||
struct dfo_chunk *dfo_chunk;
|
||||
|
||||
*end = U64_MAX;
|
||||
dfo_chunk_iterate(dfo_chunk) {
|
||||
if (dfo_chunk->start <= off && dfo_chunk->end >= off) {
|
||||
*end = MIN(*end, dfo_chunk->end);
|
||||
return dfo_chunk;
|
||||
} else if (dfo_chunk->start > off) {
|
||||
*end = MIN(*end, dfo_chunk->start - 1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seek to output dump offset "off"
|
||||
*/
|
||||
void dfo_seek(u64 off)
|
||||
{
|
||||
l.dump.off = off;
|
||||
l.off = off;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -183,7 +86,7 @@ u64 dfo_read(void *buf, u64 cnt)
|
||||
{
|
||||
struct dfo_chunk *dfo_chunk;
|
||||
u64 copied = 0, end, size;
|
||||
u64 off = l.dump.off;
|
||||
u64 off = l.off;
|
||||
|
||||
while (copied != cnt) {
|
||||
dfo_chunk = dfo_chunk_find(off, &end);
|
||||
@@ -196,7 +99,7 @@ u64 dfo_read(void *buf, u64 cnt)
|
||||
off += size;
|
||||
}
|
||||
out:
|
||||
l.dump.off = off;
|
||||
l.off = off;
|
||||
return copied;
|
||||
}
|
||||
|
||||
@@ -205,5 +108,5 @@ out:
|
||||
*/
|
||||
u64 dfo_size(void)
|
||||
{
|
||||
return l.dump.size;
|
||||
return dfo_chunk_dump_size();
|
||||
}
|
||||
|
||||
37
zdump/dfo.h
37
zdump/dfo.h
@@ -12,37 +12,12 @@
|
||||
#ifndef DFO_H
|
||||
#define DFO_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
#include "zg.h"
|
||||
|
||||
struct dfo_chunk;
|
||||
|
||||
typedef void (*dfo_chunk_read_fn)(struct dfo_chunk *chunk, u64 off,
|
||||
void *buf, u64 cnt);
|
||||
|
||||
struct dfo_chunk {
|
||||
struct util_list_node list;
|
||||
u64 start;
|
||||
u64 end;
|
||||
dfo_chunk_read_fn read_fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
extern void dfo_chunk_zero_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
||||
u64 cnt);
|
||||
extern void dfo_chunk_buf_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
||||
u64 cnt);
|
||||
extern void dfo_chunk_mem_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
||||
u64 cnt);
|
||||
extern void dfo_chunk_add(u64 start, u64 size, void *data,
|
||||
dfo_chunk_read_fn read_fn);
|
||||
|
||||
extern u64 dfo_read(void *buf, u64 cnt);
|
||||
extern void dfo_seek(u64 addr);
|
||||
extern u64 dfo_size(void);
|
||||
extern const char *dfo_name(void);
|
||||
extern void dfo_init(void);
|
||||
extern int dfo_set(const char *dfo_name);
|
||||
u64 dfo_read(void *buf, u64 cnt);
|
||||
void dfo_seek(u64 addr);
|
||||
u64 dfo_size(void);
|
||||
const char *dfo_name(void);
|
||||
void dfo_init(void);
|
||||
int dfo_set(const char *dfo_name);
|
||||
|
||||
/*
|
||||
* DFO operations
|
||||
|
||||
237
zdump/dfo_elf.c
237
zdump/dfo_elf.c
@@ -19,6 +19,7 @@
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfo_mem_chunk.h"
|
||||
#include "dfi_vmcoreinfo.h"
|
||||
#include "dfo.h"
|
||||
|
||||
@@ -27,55 +28,19 @@
|
||||
#define HDR_BASE_SIZE 0x2000
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
* Initialize ELF loads program headers
|
||||
*/
|
||||
static struct {
|
||||
void *hdr;
|
||||
u32 hdr_size;
|
||||
} l;
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
static void *ehdr_init(Elf64_Ehdr *ehdr)
|
||||
{
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
ehdr->e_ident[EI_ABIVERSION] = 0;
|
||||
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_S390;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_entry = 0;
|
||||
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_shoff = 0;
|
||||
ehdr->e_flags = 0;
|
||||
ehdr->e_ehsize = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_phentsize = sizeof(Elf64_Phdr);
|
||||
ehdr->e_phnum = dfi_mem_chunk_cnt() + 1;
|
||||
ehdr->e_shentsize = 0;
|
||||
ehdr->e_shnum = 0;
|
||||
ehdr->e_shstrndx = 0;
|
||||
return ehdr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF loads
|
||||
*/
|
||||
static u64 loads_init(Elf64_Phdr *phdr, u64 loads_offset)
|
||||
static u64 load_phdrs_init(Elf64_Phdr *phdr, u64 elf_offset)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 mem_size = 0;
|
||||
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
phdr->p_type = PT_LOAD;
|
||||
phdr->p_offset = loads_offset;
|
||||
phdr->p_offset = elf_offset;
|
||||
phdr->p_vaddr = mem_chunk->start;
|
||||
phdr->p_paddr = mem_chunk->start;
|
||||
phdr->p_memsz = mem_chunk->end - mem_chunk->start + 1;
|
||||
phdr->p_paddr = phdr->p_vaddr;
|
||||
phdr->p_memsz = mem_chunk->size;
|
||||
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
||||
/* Zero memory chunk */
|
||||
phdr->p_filesz = 0;
|
||||
@@ -83,7 +48,7 @@ static u64 loads_init(Elf64_Phdr *phdr, u64 loads_offset)
|
||||
phdr->p_filesz = phdr->p_memsz;
|
||||
phdr->p_flags = PF_R | PF_W | PF_X;
|
||||
phdr->p_align = PAGE_SIZE;
|
||||
loads_offset += phdr->p_filesz;
|
||||
elf_offset += phdr->p_filesz;
|
||||
mem_size += phdr->p_memsz;
|
||||
phdr++;
|
||||
}
|
||||
@@ -91,160 +56,12 @@ static u64 loads_init(Elf64_Phdr *phdr, u64 loads_offset)
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
* Initialize the program header entries for the notes and the related segment
|
||||
* data.
|
||||
*/
|
||||
static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
|
||||
const char *name)
|
||||
static void *notes_init(Elf64_Phdr *phdr, void *segment_start, u64 elf_offset)
|
||||
{
|
||||
Elf64_Nhdr *note;
|
||||
u64 len;
|
||||
|
||||
note = (Elf64_Nhdr *)buf;
|
||||
note->n_namesz = strlen(name) + 1;
|
||||
note->n_descsz = d_len;
|
||||
note->n_type = type;
|
||||
len = sizeof(Elf64_Nhdr);
|
||||
|
||||
memcpy(buf + len, name, note->n_namesz);
|
||||
len = ROUNDUP(len + note->n_namesz, 4);
|
||||
|
||||
memcpy(buf + len, desc, note->n_descsz);
|
||||
len = ROUNDUP(len + note->n_descsz, 4);
|
||||
|
||||
return PTR_ADD(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
static void *nt_prstatus(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
static int cpu_nr = 1;
|
||||
|
||||
memset(&nt_prstatus, 0, sizeof(nt_prstatus));
|
||||
memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs));
|
||||
memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw));
|
||||
memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs));
|
||||
nt_prstatus.pr_pid = cpu_nr;
|
||||
cpu_nr++;
|
||||
|
||||
return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
|
||||
"CORE");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
static void *nt_fpregset(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
|
||||
memset(&nt_fpregset, 0, sizeof(nt_fpregset));
|
||||
memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc));
|
||||
memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs));
|
||||
|
||||
return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset),
|
||||
"CORE");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
static void *nt_s390_timer(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
||||
"LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
static void *nt_s390_tod_cmp(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
||||
sizeof(cpu->todcmp), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
static void *nt_s390_tod_preg(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
||||
sizeof(cpu->todpreg), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
static void *nt_s390_ctrs(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
||||
"LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
static void *nt_s390_prefix(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
||||
sizeof(cpu->prefix), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
static void *nt_s390_vxrs_low(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
static void *nt_s390_vxrs_high(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
static void *nt_prpsinfo(void *ptr)
|
||||
{
|
||||
struct nt_prpsinfo_64 prpsinfo;
|
||||
|
||||
memset(&prpsinfo, 0, sizeof(prpsinfo));
|
||||
prpsinfo.pr_state = 0;
|
||||
prpsinfo.pr_sname = 'R';
|
||||
prpsinfo.pr_zomb = 0;
|
||||
strcpy(prpsinfo.pr_fname, "vmlinux");
|
||||
|
||||
return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo), "CORE");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
static void *nt_vmcoreinfo(void *ptr)
|
||||
{
|
||||
char *vmcoreinfo = dfi_vmcoreinfo_get();
|
||||
|
||||
if (!vmcoreinfo)
|
||||
return ptr;
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo), "VMCOREINFO");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize notes
|
||||
*/
|
||||
static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
|
||||
{
|
||||
void *ptr_start = ptr;
|
||||
void *ptr = segment_start;
|
||||
struct dfi_cpu *cpu;
|
||||
|
||||
ptr = nt_prpsinfo(ptr);
|
||||
@@ -266,24 +83,24 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
|
||||
}
|
||||
}
|
||||
out:
|
||||
ptr = nt_vmcoreinfo(ptr);
|
||||
ptr = nt_vmcoreinfo(ptr, dfi_vmcoreinfo_get());
|
||||
memset(phdr, 0, sizeof(*phdr));
|
||||
phdr->p_type = PT_NOTE;
|
||||
phdr->p_offset = notes_offset;
|
||||
phdr->p_filesz = (unsigned long) PTR_SUB(ptr, ptr_start);
|
||||
phdr->p_offset = elf_offset;
|
||||
phdr->p_filesz = PTR_DIFF(ptr, segment_start);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup dump chunks
|
||||
*/
|
||||
static void dump_chunks_init(void)
|
||||
static void dump_chunks_init(void *hdr, u64 hdr_size)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 off = 0;
|
||||
|
||||
dfo_chunk_add(0, l.hdr_size, l.hdr, dfo_chunk_buf_fn);
|
||||
off = l.hdr_size;
|
||||
dfo_chunk_add(off, hdr_size, hdr, dfo_chunk_buf_fn);
|
||||
off += hdr_size;
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
||||
/* Zero memory chunk */
|
||||
@@ -312,31 +129,29 @@ static void dfo_elf_init(void)
|
||||
{
|
||||
Elf64_Phdr *phdr_notes, *phdr_loads;
|
||||
u32 alloc_size;
|
||||
void *buf, *ptr;
|
||||
u64 hdr_off;
|
||||
void *ptr;
|
||||
|
||||
ensure_s390x();
|
||||
alloc_size = HDR_BASE_SIZE +
|
||||
dfi_cpu_cnt() * HDR_PER_CPU_SIZE +
|
||||
dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
|
||||
l.hdr = zg_alloc(alloc_size);
|
||||
buf = zg_alloc(alloc_size);
|
||||
/* Init elf header */
|
||||
ptr = ehdr_init(l.hdr);
|
||||
ptr = ehdr_init(buf, dfi_mem_chunk_cnt() + 1);
|
||||
/* Init program headers */
|
||||
phdr_notes = ptr;
|
||||
ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr));
|
||||
phdr_loads = ptr;
|
||||
ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * dfi_mem_chunk_cnt());
|
||||
/* Init notes */
|
||||
hdr_off = PTR_DIFF(ptr, l.hdr);
|
||||
hdr_off = PTR_DIFF(ptr, buf);
|
||||
ptr = notes_init(phdr_notes, ptr, hdr_off);
|
||||
/* Init loads */
|
||||
hdr_off = PTR_DIFF(ptr, l.hdr);
|
||||
loads_init(phdr_loads, hdr_off);
|
||||
l.hdr_size = hdr_off;
|
||||
if (l.hdr_size > alloc_size)
|
||||
ABORT("hdr_size=%u alloc_size=%u", l.hdr_size, alloc_size);
|
||||
dump_chunks_init();
|
||||
hdr_off = PTR_DIFF(ptr, buf);
|
||||
load_phdrs_init(phdr_loads, hdr_off);
|
||||
if (hdr_off > alloc_size)
|
||||
ABORT("hdr_size=%llu alloc_size=%u", hdr_off, alloc_size);
|
||||
dump_chunks_init(buf, hdr_off);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
128
zdump/dfo_mem_chunk.c
Normal file
128
zdump/dfo_mem_chunk.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2017, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "zg.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfo_mem_chunk.h"
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
u64 size; /* Size of dump in bytes */
|
||||
unsigned int chunk_cnt; /* Number of dump chunks */
|
||||
struct util_list chunk_list; /* DFO chunk list */
|
||||
} l;
|
||||
|
||||
/*
|
||||
* Add dump chunk
|
||||
*/
|
||||
void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn)
|
||||
{
|
||||
struct dfo_chunk *dfo_chunk;
|
||||
|
||||
dfo_chunk = zg_alloc(sizeof(*dfo_chunk));
|
||||
dfo_chunk->start = start;
|
||||
dfo_chunk->end = start + size - 1;
|
||||
dfo_chunk->data = data;
|
||||
dfo_chunk->read_fn = read_fn;
|
||||
util_list_add_head(&l.chunk_list, dfo_chunk);
|
||||
l.chunk_cnt++;
|
||||
l.size = MAX(l.size, dfo_chunk->end + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy zero pages for chunk
|
||||
*/
|
||||
void dfo_chunk_zero_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
(void) dfo_chunk;
|
||||
(void) off;
|
||||
|
||||
memset(buf, 0, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy given buffer for chunk
|
||||
*/
|
||||
void dfo_chunk_buf_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
memcpy(buf, dfo_chunk->data + off, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump chunk function: Copy given memory range for chunk
|
||||
*/
|
||||
void dfo_chunk_mem_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk = dfo_chunk->data;
|
||||
|
||||
mem_chunk->read_fn(mem_chunk, off, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find dump chunk for offset "off"
|
||||
*
|
||||
* This function is a bit hacky. DFO chunks can overlap. If two DFO chunks
|
||||
* overlap, the last registered chunk wins. The dfo_chunk_find() function
|
||||
* reflects that by returning the first memory chunk that is found in
|
||||
* the dfo chunk list.
|
||||
*
|
||||
* In addition to that it calculates the "virtual end" of that chunk. An
|
||||
* overlapping chunk can limit the "virtual end" of an underlying chunk so
|
||||
* that the "virtual end" of that chunk is lower than the "real end".
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* chunk 1.: |------|
|
||||
* chunk 2.: |---------------------|
|
||||
* off.....: ^
|
||||
* virt end: ^
|
||||
* real end: ^
|
||||
*
|
||||
* In this case chunk 2 will be returned and "end" is set to the start of
|
||||
* chunk 1.
|
||||
*/
|
||||
struct dfo_chunk *dfo_chunk_find(u64 off, u64 *end)
|
||||
{
|
||||
struct dfo_chunk *dfo_chunk;
|
||||
|
||||
*end = U64_MAX;
|
||||
dfo_chunk_iterate(dfo_chunk) {
|
||||
if (dfo_chunk->start <= off && dfo_chunk->end >= off) {
|
||||
*end = MIN(*end, dfo_chunk->end);
|
||||
return dfo_chunk;
|
||||
} else if (dfo_chunk->start > off) {
|
||||
*end = MIN(*end, dfo_chunk->start - 1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct util_list *dfo_chunk_list(void)
|
||||
{
|
||||
return &l.chunk_list;
|
||||
}
|
||||
|
||||
u64 dfo_chunk_dump_size(void)
|
||||
{
|
||||
return l.size;
|
||||
}
|
||||
|
||||
int dfo_chunk_init(void)
|
||||
{
|
||||
util_list_init(&l.chunk_list, struct dfo_chunk, list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dfo_chunk_deinit(void)
|
||||
{
|
||||
memset(&l, 0, sizeof(l));
|
||||
}
|
||||
42
zdump/dfo_mem_chunk.h
Normal file
42
zdump/dfo_mem_chunk.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2017, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DFO_MEM_CHUNK_H
|
||||
#define DFO_MEM_CHUNK_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_list.h"
|
||||
|
||||
struct dfo_chunk;
|
||||
|
||||
typedef void (*dfo_chunk_read_fn)(struct dfo_chunk *chunk, u64 off,
|
||||
void *buf, u64 cnt);
|
||||
|
||||
struct dfo_chunk {
|
||||
struct util_list_node list;
|
||||
u64 start;
|
||||
u64 end;
|
||||
dfo_chunk_read_fn read_fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
void dfo_chunk_zero_fn(struct dfo_chunk *chunk, u64 off, void *buf, u64 cnt);
|
||||
void dfo_chunk_buf_fn(struct dfo_chunk *chunk, u64 off, void *buf, u64 cnt);
|
||||
void dfo_chunk_mem_fn(struct dfo_chunk *chunk, u64 off, void *buf, u64 cnt);
|
||||
void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn);
|
||||
struct dfo_chunk *dfo_chunk_find(u64 off, u64 *end);
|
||||
|
||||
struct util_list *dfo_chunk_list(void);
|
||||
#define dfo_chunk_iterate(dfo_chunk) \
|
||||
util_list_iterate(dfo_chunk_list(), dfo_chunk)
|
||||
|
||||
u64 dfo_chunk_dump_size(void);
|
||||
|
||||
int dfo_chunk_init(void);
|
||||
void dfo_chunk_deinit(void);
|
||||
|
||||
#endif /* DFO_MEM_CHUNK_H */
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "df_s390.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfo_mem_chunk.h"
|
||||
#include "dfo.h"
|
||||
|
||||
/*
|
||||
|
||||
18
zdump/opts.c
18
zdump/opts.c
@@ -40,6 +40,7 @@ static const char optstr[] = "hvVidmus:f:X";
|
||||
*/
|
||||
static const char help_text[] =
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
|
||||
" DUMP [-s SYS] [-f FMT] DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] DIR\n"
|
||||
" -i DUMP [-s SYS]\n"
|
||||
" -d DUMPDEV\n"
|
||||
@@ -86,6 +87,7 @@ static void init_defaults(struct options *opts)
|
||||
{
|
||||
opts->prog_name = "zgetdump";
|
||||
opts->action = ZG_ACTION_COPY;
|
||||
opts->output_path = NULL;
|
||||
#ifdef __s390x__
|
||||
opts->fmt = "elf";
|
||||
#else
|
||||
@@ -166,6 +168,14 @@ static void device_set(struct options *opts, const char *path)
|
||||
opts->device = zg_strdup(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output path
|
||||
*/
|
||||
static void output_set(struct options *opts, const char *path)
|
||||
{
|
||||
opts->output_path = zg_strdup(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set FUSE debug options
|
||||
*/
|
||||
@@ -229,6 +239,14 @@ static void parse_pos_args(struct options *opts, char *argv[], int argc)
|
||||
|
||||
switch (opts->action) {
|
||||
case ZG_ACTION_COPY:
|
||||
if (pos_args == 0)
|
||||
ERR_EXIT("No device or dump specified");
|
||||
if (pos_args > 2)
|
||||
ERR_EXIT("Too many positional parameters specified");
|
||||
device_set(opts, argv[optind]);
|
||||
if (pos_args > 1)
|
||||
output_set(opts, argv[optind + 1]);
|
||||
break;
|
||||
case ZG_ACTION_DUMP_INFO:
|
||||
case ZG_ACTION_DEVICE_INFO:
|
||||
if (pos_args == 0)
|
||||
|
||||
@@ -18,6 +18,8 @@ struct options {
|
||||
int action_specified;
|
||||
enum zg_action action;
|
||||
char *device;
|
||||
/* If `output_path == NULL` the output is written to `stdout` */
|
||||
const char *output_path;
|
||||
char *mount_point;
|
||||
int fmt_specified;
|
||||
const char *fmt;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Write dump to standard output (stdout)
|
||||
* Write dump to the file descriptor (fd)
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
@@ -12,9 +12,9 @@
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "stdout.h"
|
||||
#include "output.h"
|
||||
|
||||
int stdout_write_dump(void)
|
||||
int write_dump(FILE *stream)
|
||||
{
|
||||
const u64 output_size = dfo_size();
|
||||
char buf[8UL * PAGE_SIZE];
|
||||
@@ -28,15 +28,13 @@ int stdout_write_dump(void)
|
||||
STDERR("\n");
|
||||
zg_progress_init("Copying dump", output_size);
|
||||
while (written != output_size) {
|
||||
ssize_t rc;
|
||||
size_t rc;
|
||||
u64 cnt;
|
||||
|
||||
cnt = dfo_read(buf, sizeof(buf));
|
||||
rc = write(STDOUT_FILENO, buf, cnt);
|
||||
if (rc == -1)
|
||||
ERR_EXIT_ERRNO("Error: Write failed");
|
||||
if (rc != (ssize_t) cnt)
|
||||
ERR_EXIT("Error: Could not write full block");
|
||||
rc = fwrite(buf, cnt, 1, stream);
|
||||
if (rc != 1 && ferror(stream))
|
||||
ERR_EXIT("Error: Write failed");
|
||||
written += cnt;
|
||||
zg_progress(written);
|
||||
};
|
||||
@@ -5,9 +5,11 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef STDOUT_H
|
||||
#define STDOUT_H
|
||||
#ifndef OUTPUT_H
|
||||
#define OUTPUT_H
|
||||
|
||||
int stdout_write_dump(void);
|
||||
#include <stdio.h>
|
||||
|
||||
#endif /* STDOUT_H */
|
||||
int write_dump(FILE *stream);
|
||||
|
||||
#endif /* OUTPUT_H */
|
||||
@@ -9,7 +9,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 25
|
||||
#define FUSE_USE_VERSION 30
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -84,8 +84,11 @@ static void stat_dump_init(void)
|
||||
/*
|
||||
* FUSE callback: Getattr
|
||||
*/
|
||||
static int zfuse_getattr(const char *path, struct stat *stat)
|
||||
static int zfuse_getattr(const char *path, struct stat *stat,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void) fi;
|
||||
|
||||
if (strcmp(path, "/") == 0) {
|
||||
*stat = l.stat_root;
|
||||
return 0;
|
||||
@@ -101,17 +104,19 @@ static int zfuse_getattr(const char *path, struct stat *stat)
|
||||
* FUSE callback: Readdir - Return ".", ".." and dump file
|
||||
*/
|
||||
static int zfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, &l.path[1], NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, &l.path[1], NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -206,7 +211,7 @@ int zfuse_mount_dump(void)
|
||||
fuse_opt_add_arg(&args, "zgetdump");
|
||||
fuse_opt_add_arg(&args, "-s");
|
||||
snprintf(tmp_str, sizeof(tmp_str),
|
||||
"-ofsname=%s,ro,default_permissions,nonempty",
|
||||
"-ofsname=%s,ro,default_permissions",
|
||||
g.opts.device);
|
||||
fuse_opt_add_arg(&args, tmp_str);
|
||||
fuse_opt_add_arg(&args, g.opts.mount_point);
|
||||
@@ -214,7 +219,7 @@ int zfuse_mount_dump(void)
|
||||
stat_root_init();
|
||||
stat_dump_init();
|
||||
snprintf(l.path, sizeof(l.path), "/dump.%s", dfo_name());
|
||||
return fuse_main(args.argc, args.argv, &zfuse_ops);
|
||||
return fuse_main(args.argc, args.argv, &zfuse_ops, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
113
zdump/zg.h
113
zdump/zg.h
@@ -45,79 +45,50 @@
|
||||
/*
|
||||
* Memory functions
|
||||
*/
|
||||
extern void *zg_alloc(unsigned int size);
|
||||
extern void *zg_realloc(void *ptr, unsigned int size);
|
||||
extern void zg_free(void *ptr);
|
||||
extern char *zg_strdup(const char *str);
|
||||
void *zg_alloc(unsigned int size);
|
||||
void *zg_realloc(void *ptr, unsigned int size);
|
||||
void zg_free(void *ptr);
|
||||
char *zg_strdup(const char *str);
|
||||
|
||||
/*
|
||||
* At exit functions
|
||||
*/
|
||||
typedef void (*zg_atexit_fn_t)(void);
|
||||
extern void zg_atexit(zg_atexit_fn_t fn);
|
||||
extern void __noreturn zg_exit(int rc);
|
||||
void zg_atexit(zg_atexit_fn_t fn);
|
||||
void __noreturn zg_exit(int rc);
|
||||
|
||||
/*
|
||||
* Temporary device node functions
|
||||
*/
|
||||
extern char *zg_devnode_create(dev_t dev);
|
||||
char *zg_devnode_create(dev_t dev);
|
||||
|
||||
/*
|
||||
* Progress bar functions
|
||||
*/
|
||||
extern void zg_progress_init(const char *msg, u64 mem_size);
|
||||
extern void zg_progress(u64 addr);
|
||||
void zg_progress_init(const char *msg, u64 mem_size);
|
||||
void zg_progress(u64 addr);
|
||||
|
||||
/*
|
||||
* Error and print functions
|
||||
*/
|
||||
#define ERR(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s: ", "zgetdump"); \
|
||||
fprintf(stderr, x); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} while (0)
|
||||
|
||||
#define ERR_EXIT(x...) \
|
||||
do { \
|
||||
ERR(x); \
|
||||
zg_exit(1); \
|
||||
} while (0)
|
||||
void zg_err(const char *fmt, ...);
|
||||
void zg_err_exit(const char *fmt, ...);
|
||||
void zg_err_exit_errno(const char *fmt, ...);
|
||||
void zg_abort(const char *fmt, ...);
|
||||
|
||||
#define ABORT(x...) \
|
||||
do { \
|
||||
ERR("Internal Error: " x); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#define ERR(fmt, ...) zg_err(fmt, ## __VA_ARGS__)
|
||||
#define ERR_EXIT(fmt, ...) zg_err_exit(fmt, ## __VA_ARGS__)
|
||||
#define ERR_EXIT_ERRNO(fmt, ...) zg_err_exit_errno(fmt, ## __VA_ARGS__)
|
||||
#define ABORT(fmt, ...) zg_abort(fmt, ## __VA_ARGS__)
|
||||
|
||||
#define ERR_EXIT_ERRNO(x...) \
|
||||
do { \
|
||||
fflush(stdout); \
|
||||
fprintf(stderr, "%s: ", "zgetdump"); \
|
||||
fprintf(stderr, x); \
|
||||
fprintf(stderr, " (%s)", strerror(errno)); \
|
||||
fprintf(stderr, "\n"); \
|
||||
zg_exit(1); \
|
||||
} while (0)
|
||||
|
||||
#define STDERR(x...) \
|
||||
do { \
|
||||
fprintf(stderr, x); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
#define STDERR_PR(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "\r%s: ", "zgetdump"); \
|
||||
fprintf(stderr, x); \
|
||||
} while (0)
|
||||
|
||||
#define STDOUT(x...) \
|
||||
do { \
|
||||
fprintf(stdout, x); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
void zg_stderr(const char *fmt, ...);
|
||||
void zg_stderr_pr(const char *fmt, ...);
|
||||
void zg_stdout(const char *fmt, ...);
|
||||
|
||||
#define STDERR(fmt, ...) zg_stderr(fmt, ## __VA_ARGS__)
|
||||
#define STDERR_PR(fmt, ...) zg_stderr_pr(fmt, ## __VA_ARGS__)
|
||||
#define STDOUT(fmt, ...) zg_stdout(fmt, ## __VA_ARGS__)
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
@@ -140,9 +111,9 @@ static inline u32 zg_csum_partial(const void *buf, int len, u32 sum)
|
||||
/*
|
||||
* Pointer atrithmetic
|
||||
*/
|
||||
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
|
||||
#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
|
||||
#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
|
||||
#define PTR_SUB(x, y) ((void *)(((char *) (x)) - ((unsigned long) (y))))
|
||||
#define PTR_ADD(x, y) ((void *)(((char *) (x)) + ((unsigned long) (y))))
|
||||
#define PTR_DIFF(x, y) ((unsigned long)PTR_SUB(x, y))
|
||||
|
||||
/*
|
||||
* File functions
|
||||
@@ -167,22 +138,22 @@ enum zg_check {
|
||||
ZG_CHECK_NONE,
|
||||
};
|
||||
|
||||
extern const char *zg_path(struct zg_fh *zg_fh);
|
||||
extern const struct stat *zg_stat(struct zg_fh *zg_fh);
|
||||
extern struct zg_fh *zg_open(const char *path, int flags, enum zg_check check);
|
||||
extern void zg_close(struct zg_fh *zg_fh);
|
||||
extern ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
extern ssize_t zg_gets(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
extern u64 zg_size(struct zg_fh *zg_fh);
|
||||
extern off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check);
|
||||
extern off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
extern off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
extern off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
extern int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
||||
enum zg_check check);
|
||||
extern enum zg_type zg_type(struct zg_fh *zg_fh);
|
||||
const char *zg_path(struct zg_fh *zg_fh);
|
||||
const struct stat *zg_stat(struct zg_fh *zg_fh);
|
||||
struct zg_fh *zg_open(const char *path, int flags, enum zg_check check);
|
||||
void zg_close(struct zg_fh *zg_fh);
|
||||
ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
ssize_t zg_gets(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
u64 zg_size(struct zg_fh *zg_fh);
|
||||
off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check);
|
||||
off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
||||
enum zg_check check);
|
||||
enum zg_type zg_type(struct zg_fh *zg_fh);
|
||||
|
||||
/*
|
||||
* zgetdump actions
|
||||
|
||||
72
zdump/zg_error.c
Normal file
72
zdump/zg_error.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2017, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "zg.h"
|
||||
|
||||
static inline void _zg_err(const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "%s: ", "zgetdump");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static inline void _zg_err_errno(const char *fmt, va_list ap)
|
||||
{
|
||||
const int errno_backup = errno;
|
||||
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s: ", "zgetdump");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, " (%s)", strerror(errno_backup));
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void zg_err(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_err(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void zg_err_exit(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_err(fmt, ap);
|
||||
va_end(ap);
|
||||
zg_exit(1);
|
||||
}
|
||||
|
||||
void zg_err_exit_errno(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_err_errno(fmt, ap);
|
||||
va_end(ap);
|
||||
zg_exit(1);
|
||||
}
|
||||
|
||||
void zg_abort(const char *fmt, ...)
|
||||
{
|
||||
char *newfmt;
|
||||
va_list ap;
|
||||
|
||||
newfmt = util_strcat_realloc(util_strdup("Internal Error: "), fmt);
|
||||
va_start(ap, fmt);
|
||||
_zg_err(newfmt, ap);
|
||||
va_end(ap);
|
||||
free(newfmt);
|
||||
|
||||
abort();
|
||||
}
|
||||
55
zdump/zg_print.c
Normal file
55
zdump/zg_print.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2017, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "zg.h"
|
||||
|
||||
static inline void _zg_stderr(const char *fmt, va_list ap)
|
||||
{
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
static inline void _zg_stderr_pr(const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "\r%s: ", "zgetdump");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
|
||||
static inline void _zg_stdout(const char *fmt, va_list ap)
|
||||
{
|
||||
vfprintf(stdout, fmt, ap);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void zg_stderr(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_stderr(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void zg_stderr_pr(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_stderr_pr(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void zg_stdout(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_zg_stdout(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
@@ -8,6 +8,8 @@ zgetdump \- Tool for copying and converting System z dumps
|
||||
.SH SYNOPSIS
|
||||
|
||||
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] > DUMP_FILE
|
||||
.br
|
||||
DUMP [-s SYS] [-f FMT] DUMP_FILE
|
||||
.br
|
||||
-m DUMP [-s SYS] [-f FMT] DIR
|
||||
.br
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "dt.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "stdout.h"
|
||||
#include "output.h"
|
||||
#include "zfuse.h"
|
||||
|
||||
/*
|
||||
@@ -156,18 +156,46 @@ static int do_mount(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "copy to stdout" action
|
||||
*/
|
||||
static int do_copy(void)
|
||||
static FILE *open_file_for_writing(const char *output)
|
||||
{
|
||||
FILE *stream;
|
||||
int fd;
|
||||
|
||||
if (output) {
|
||||
fd = open(output,
|
||||
O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC,
|
||||
0600);
|
||||
if (fd == -1)
|
||||
ERR_EXIT_ERRNO("Could not open '%s' exclusively", output);
|
||||
} else {
|
||||
fd = dup(STDOUT_FILENO);
|
||||
if (fd == -1)
|
||||
ERR_EXIT_ERRNO("Could not dup() stdout");
|
||||
}
|
||||
|
||||
stream = fdopen(fd, "w");
|
||||
if (!stream)
|
||||
ERR_EXIT_ERRNO("Could not fdopen()");
|
||||
fd = -1;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "copy to output" action
|
||||
*/
|
||||
static int do_copy(const char *output)
|
||||
{
|
||||
FILE *stream;
|
||||
int rc;
|
||||
|
||||
if (dfi_init() != 0)
|
||||
ERR_EXIT("Dump cannot be processed (is not complete)");
|
||||
dfo_init();
|
||||
kdump_select_check();
|
||||
rc = stdout_write_dump();
|
||||
stream = open_file_for_writing(output);
|
||||
rc = write_dump(stream);
|
||||
fclose(stream);
|
||||
dfi_exit();
|
||||
return rc;
|
||||
}
|
||||
@@ -185,7 +213,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
switch (g.opts.action) {
|
||||
case ZG_ACTION_COPY:
|
||||
return do_copy();
|
||||
return do_copy(g.opts.output_path);
|
||||
case ZG_ACTION_DUMP_INFO:
|
||||
return do_dump_info();
|
||||
case ZG_ACTION_DEVICE_INFO:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,6 +97,33 @@ char *strcpy(char *dest, const char *src)
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy \a src to buffer \a dest of size \a size. At most size - 1
|
||||
* chars will be copied. \a dest will always be NUL terminated.
|
||||
*
|
||||
* Note: If the return value is greater than or equal to size truncation
|
||||
* occurred.
|
||||
*
|
||||
* @param[in] dest Destination buffer
|
||||
* @param[in] src Source string
|
||||
* @param[in] size Size of destination buffer
|
||||
*
|
||||
* @returns strlen Length of \a src string
|
||||
*/
|
||||
size_t strlcpy(char *dest, const char *src, size_t size)
|
||||
{
|
||||
size_t str_len = strlen(src);
|
||||
size_t len;
|
||||
|
||||
if (size) {
|
||||
len = MIN(size - 1, str_len);
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = '\0';
|
||||
}
|
||||
|
||||
return str_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return string length
|
||||
*/
|
||||
|
||||
@@ -54,13 +54,14 @@ char *strcat(char *, const char *);
|
||||
int strncmp(const char *, const char *, unsigned long);
|
||||
int strlen(const char *);
|
||||
char *strcpy(char *, const char *);
|
||||
size_t strlcpy(char *dest, const char *src, size_t size);
|
||||
char *strchr(const char *str, char c);
|
||||
unsigned int strhash(unsigned char *str, unsigned int hash_size);
|
||||
unsigned long get_zeroed_page(void);
|
||||
void free_page(unsigned long);
|
||||
void initialize(void);
|
||||
void libc_stop(unsigned long) __noreturn;
|
||||
void start(void);
|
||||
void __noreturn start(void);
|
||||
void pgm_check_handler(void);
|
||||
void pgm_check_handler_fn(void);
|
||||
void panic_notify(unsigned long reason);
|
||||
|
||||
@@ -188,7 +188,7 @@ boot:
|
||||
|
||||
/* append 'BOOT_IMAGE=<num>' to parmline */
|
||||
snprintf(endstring, sizeof(endstring), " BOOT_IMAGE=%u", value);
|
||||
if ((strlen(cmd_line_extra) + strlen(endstring)) < COMMAND_LINE_SIZE)
|
||||
if ((strlen(cmd_line_extra) + strlen(endstring)) < LEGACY_COMMAND_LINE_SIZE)
|
||||
strcat(cmd_line_extra, endstring);
|
||||
|
||||
sclp_setup(SCLP_DISABLE);
|
||||
|
||||
@@ -75,15 +75,16 @@ out_free_page:
|
||||
}
|
||||
|
||||
|
||||
static void execute(uint64_t psw)
|
||||
static __noreturn void execute(uint64_t psw)
|
||||
{
|
||||
asm volatile(
|
||||
" lpsw %[psw]\n"
|
||||
: : [psw] "Q" (psw) : "cc"
|
||||
);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void start(void)
|
||||
void __noreturn start(void)
|
||||
{
|
||||
struct stage2_descr stage2_descr;
|
||||
struct subchannel_id subchannel_id;
|
||||
|
||||
@@ -393,7 +393,7 @@ static void store_status(void)
|
||||
* Block followed by a valid checksum (as defined in lowcore.h and set
|
||||
* by ipl.c). In case of match use diag308 to IPL.
|
||||
*/
|
||||
static void dump_exit(unsigned long code)
|
||||
static __noreturn void dump_exit(unsigned long code)
|
||||
{
|
||||
struct ipib_info *ipib_info = (struct ipib_info *)&S390_lowcore.ipib;
|
||||
uint32_t ipib_len, csum;
|
||||
@@ -406,6 +406,7 @@ static void dump_exit(unsigned long code)
|
||||
libc_stop(code);
|
||||
diag308(DIAG308_SET, (void *) ipib_info->ipib);
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -420,7 +421,7 @@ void panic_notify(unsigned long code)
|
||||
/*
|
||||
* Create stand-alone dump
|
||||
*/
|
||||
void start(void)
|
||||
void __noreturn start(void)
|
||||
{
|
||||
init_early();
|
||||
dt_device_parm_setup();
|
||||
|
||||
@@ -159,8 +159,8 @@ static void hash_table_add(struct env_hash_entry *items,
|
||||
(*new_idx)++;
|
||||
}
|
||||
|
||||
struct env_hash_entry *hash_table_find(struct env_hash_entry **buckets,
|
||||
char *name)
|
||||
static struct env_hash_entry *hash_table_find(struct env_hash_entry **buckets,
|
||||
char *name)
|
||||
{
|
||||
struct env_hash_entry *item;
|
||||
|
||||
@@ -232,7 +232,8 @@ static void parse_envblk(struct env_hash_entry *items,
|
||||
* CMDL_LEN: length of the command line to be processed
|
||||
* BUCKETS: hash table of pairs (NAME, VALUE)
|
||||
*/
|
||||
void process_parm_line(struct env_hash_entry **buckets, unsigned int cmdl_len)
|
||||
void process_parm_line(struct env_hash_entry **buckets, unsigned int cmdl_len,
|
||||
unsigned int max_len)
|
||||
{
|
||||
struct env_hash_entry *item;
|
||||
char empty_str = 0;
|
||||
@@ -282,7 +283,7 @@ void process_parm_line(struct env_hash_entry **buckets, unsigned int cmdl_len)
|
||||
*/
|
||||
len = strlen(val);
|
||||
|
||||
if (cmdl_len + len - (end - start + 1) >= COMMAND_LINE_SIZE)
|
||||
if (cmdl_len + len - (end - start + 1) >= max_len)
|
||||
/* VALUE doesn't fit */
|
||||
break;
|
||||
/*
|
||||
@@ -306,7 +307,7 @@ void process_parm_line(struct env_hash_entry **buckets, unsigned int cmdl_len)
|
||||
/**
|
||||
* LEN: length of the command line to be processed
|
||||
*/
|
||||
static void handle_environment(unsigned int len)
|
||||
static void handle_environment(unsigned int len, unsigned int max_len)
|
||||
{
|
||||
struct env_hash_entry *buckets[STR_HASH_SIZE];
|
||||
struct env_hash_entry *items;
|
||||
@@ -326,30 +327,76 @@ static void handle_environment(unsigned int len)
|
||||
* find environment variables in the command line and
|
||||
* replace them with their values as found in the hash table
|
||||
*/
|
||||
process_parm_line(buckets, len);
|
||||
process_parm_line(buckets, len, max_len);
|
||||
free_page((unsigned long)items);
|
||||
}
|
||||
|
||||
void start(void)
|
||||
static void verify_secure_boot(void)
|
||||
{
|
||||
unsigned int subchannel_id;
|
||||
unsigned char *cextra = (unsigned char *)COMMAND_LINE_EXTRA;
|
||||
unsigned char *cmdline = (unsigned char *)COMMAND_LINE;
|
||||
unsigned int cmdline_len = 0, cextra_len = 0;
|
||||
|
||||
/*
|
||||
* IPL process is secure we have to use default IPL values and
|
||||
* check if the psw jump address is within at the start of a
|
||||
* verified component. If it is not IPL is aborted.
|
||||
*/
|
||||
if (secure_boot_enabled()) {
|
||||
if (_stage3_parms.image_addr != IMAGE_LOAD_ADDRESS ||
|
||||
_stage3_parms.load_psw != DEFAULT_PSW_LOAD)
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_inval);
|
||||
if (_stage3_parms.image_addr != IMAGE_LOAD_ADDRESS ||
|
||||
_stage3_parms.load_psw != DEFAULT_PSW_LOAD)
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_inval);
|
||||
|
||||
if (!is_verified_address(_stage3_parms.load_psw & PSW32_ADDR_MASK))
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_unverified);
|
||||
if (!is_verified_address(_stage3_parms.load_psw & PSW32_ADDR_MASK))
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_unverified);
|
||||
}
|
||||
|
||||
static void setup_cmdline(void)
|
||||
{
|
||||
char *cextra = (char *)COMMAND_LINE_EXTRA;
|
||||
char *cmdline = (char *)COMMAND_LINE;
|
||||
unsigned int cmdline_len = 0;
|
||||
unsigned int max_cmdline_len = *(unsigned long *)MAX_COMMAND_LINE_SIZE;
|
||||
|
||||
if (!max_cmdline_len)
|
||||
max_cmdline_len = LEGACY_COMMAND_LINE_SIZE;
|
||||
|
||||
/* if valid command line is given, copy it into new kernel space */
|
||||
if (_stage3_parms.parm_addr != UNSPECIFIED_ADDRESS) {
|
||||
strlcpy(cmdline, (void *)(unsigned long *)_stage3_parms.parm_addr,
|
||||
max_cmdline_len);
|
||||
}
|
||||
/* determine length of original parm line */
|
||||
cmdline_len = MIN((unsigned int)strlen(cmdline), max_cmdline_len - 1);
|
||||
|
||||
/* convert extra parameter to ascii */
|
||||
if (!_stage3_parms.extra_parm || !*cextra)
|
||||
goto noextra;
|
||||
|
||||
/* Handle extra kernel parameters specified in DASD boot menu. */
|
||||
ebcdic_to_ascii((unsigned char *)cextra, (unsigned char *)cextra, COMMAND_LINE_EXTRA_SIZE);
|
||||
|
||||
while (isspace(*cextra))
|
||||
cextra++;
|
||||
|
||||
/*
|
||||
* if extra parm string starts with '=' replace original string,
|
||||
* else append
|
||||
*/
|
||||
if (*cextra == 0x3d) {
|
||||
strlcpy(cmdline, cextra+1, max_cmdline_len);
|
||||
} else if (*cextra && cmdline_len + 1 <= max_cmdline_len - 1) {
|
||||
/* add blank */
|
||||
cmdline[cmdline_len++] = 0x20;
|
||||
strlcpy(cmdline + cmdline_len, cextra, max_cmdline_len - cmdline_len);
|
||||
}
|
||||
|
||||
noextra:
|
||||
handle_environment(strlen(cmdline), max_cmdline_len);
|
||||
}
|
||||
|
||||
void start(void)
|
||||
{
|
||||
unsigned int subchannel_id;
|
||||
|
||||
if (secure_boot_enabled())
|
||||
verify_secure_boot();
|
||||
|
||||
/*
|
||||
* cut the kernel header
|
||||
*/
|
||||
@@ -362,57 +409,7 @@ void start(void)
|
||||
*(unsigned int *)__LC_IPLDEV = subchannel_id;
|
||||
*(unsigned long long *)IPL_DEVICE = subchannel_id;
|
||||
|
||||
/* if valid command line is given, copy it into new kernel space */
|
||||
if (_stage3_parms.parm_addr != UNSPECIFIED_ADDRESS) {
|
||||
memcpy(cmdline,
|
||||
(void *)(unsigned long *)_stage3_parms.parm_addr,
|
||||
COMMAND_LINE_SIZE);
|
||||
/* terminate \0 */
|
||||
cmdline[COMMAND_LINE_SIZE - 1] = 0;
|
||||
}
|
||||
/* determine length of original parm line */
|
||||
cmdline_len = MIN(strlen((const char *)cmdline),
|
||||
COMMAND_LINE_SIZE - 1);
|
||||
|
||||
/* convert extra parameter to ascii */
|
||||
if (!_stage3_parms.extra_parm || !*cextra)
|
||||
goto noextra;
|
||||
|
||||
/* Handle extra kernel parameters specified in DASD boot menu. */
|
||||
ebcdic_to_ascii(cextra, cextra, COMMAND_LINE_SIZE);
|
||||
|
||||
/* determine length of extra parameter */
|
||||
cextra_len = MIN(strlen((const char *)cextra), COMMAND_LINE_SIZE - 1);
|
||||
|
||||
/* remove leading whitespace of extra parameter */
|
||||
while (cextra_len > 0 && *cextra == 0x20) {
|
||||
cextra++;
|
||||
cextra_len--;
|
||||
}
|
||||
/*
|
||||
* if extra parm string starts with '=' replace original string,
|
||||
* else append
|
||||
*/
|
||||
if (*cextra == 0x3d && cextra_len >= 1) {
|
||||
/* skip '=' */
|
||||
cextra++;
|
||||
cextra_len--;
|
||||
memcpy(cmdline, cextra, cextra_len);
|
||||
cmdline[cextra_len] = 0;
|
||||
} else if (cmdline_len + 1 <= COMMAND_LINE_SIZE - 1) {
|
||||
/* add blank */
|
||||
cmdline[cmdline_len] = 0x20;
|
||||
cmdline_len++;
|
||||
/* check if length is within max value */
|
||||
cextra_len = (cmdline_len + cextra_len <= COMMAND_LINE_SIZE - 1) ?
|
||||
cextra_len : (COMMAND_LINE_SIZE - 1 - cmdline_len);
|
||||
/* append string */
|
||||
memcpy(cmdline + cmdline_len, cextra, cextra_len);
|
||||
/* terminate 0 */
|
||||
cmdline[cmdline_len + cextra_len] = 0;
|
||||
}
|
||||
noextra:
|
||||
handle_environment(cmdline_len + cextra_len);
|
||||
setup_cmdline();
|
||||
|
||||
/* copy initrd start address and size intop new kernle space */
|
||||
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
|
||||
|
||||
@@ -218,12 +218,25 @@ iplstart:
|
||||
la %r2,_initrd_addr # store ramdisk address
|
||||
mvc INITRD_START-PARMAREA(8,%r12),0(%r2)
|
||||
|
||||
# copy kernel to final destination
|
||||
|
||||
l %r6,.Lkernsize
|
||||
sl %r6,.Lkernoff
|
||||
l %r2,.Lkernoff # 0x10000
|
||||
lr %r3,%r6
|
||||
l %r4,.Loffset # 0x4000
|
||||
a %r4,.Lkernoff
|
||||
lr %r5,%r6
|
||||
mvcl %r2,%r4
|
||||
l %r12,.Lparmarea
|
||||
|
||||
#
|
||||
# Load parameter file
|
||||
#
|
||||
|
||||
l %r4,_parm_addr+4
|
||||
chi %r4,-1
|
||||
be .Lnopf
|
||||
.Lagain1:
|
||||
lr %r2,%r4 # load parmfile
|
||||
bas %r14,.Lloader
|
||||
@@ -236,31 +249,35 @@ iplstart:
|
||||
clc 0(3,%r4),.L_eof # if it is EOFx
|
||||
bz .Lagain1 # skip dateset trailer
|
||||
|
||||
chi %r2,COMMAND_LINE_SIZE-1 # truncate if line too long
|
||||
lt %r3,MAX_COMMAND_LINE_SIZE-PARMAREA+4(%r12)
|
||||
jnz 0f
|
||||
lhi %r3,LEGACY_COMMAND_LINE_SIZE
|
||||
0: cr %r2,%r3 # truncate if line too long
|
||||
bnh .Lnotrunc
|
||||
la %r2,COMMAND_LINE_SIZE-1
|
||||
lr %r2,%r3
|
||||
.Lnotrunc:
|
||||
|
||||
la %r5,0(%r4,%r2) # search for EBCDIC chars
|
||||
lr %r3,%r2
|
||||
ahi %r5,-1
|
||||
lr %r6,%r2
|
||||
.Lidebc:
|
||||
tm 0(%r5),0x80 # high order bit set ?
|
||||
bo .Ldocv # yes -> convert from EBCDIC
|
||||
ahi %r5,-1
|
||||
bct %r3,.Lidebc
|
||||
bct %r6,.Lidebc
|
||||
b .Lnocv
|
||||
.Ldocv:
|
||||
la %r3,_ebcasc
|
||||
tr 0(256,%r4),0(%r3) # convert parameters to ascii
|
||||
tr 256(256,%r4),0(%r3)
|
||||
tr 512(256,%r4),0(%r3)
|
||||
tr 768(127,%r4),0(%r3)
|
||||
lr %r6,%r2
|
||||
la %r5,_ebcasc
|
||||
0: tr 0(1,%r4),0(%r5)
|
||||
ahi %r4,1
|
||||
brct %r6,0b
|
||||
l %r4,_parm_addr+4
|
||||
.Lnocv:
|
||||
la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line
|
||||
mvc 0(256,%r3),0(%r4)
|
||||
mvc 256(256,%r3),256(%r4)
|
||||
mvc 512(256,%r3),512(%r4)
|
||||
mvc 768(127,%r3),768(%r4)
|
||||
lr %r5,%r2
|
||||
ahi %r5,-1
|
||||
la %r6,COMMAND_LINE-PARMAREA(%r12)
|
||||
lr %r7,%r2
|
||||
mvcl %r6,%r4
|
||||
slr %r0,%r0
|
||||
b .Lcntlp
|
||||
.Ldelspc:
|
||||
@@ -284,6 +301,8 @@ iplstart:
|
||||
|
||||
.Lagain2:
|
||||
l %r2,INITRD_START-PARMAREA+4(%r12) # load ramdisk
|
||||
chi %r2,-1
|
||||
be .Lnoinitrd
|
||||
bas %r14,.Lloader
|
||||
|
||||
st %r2,INITRD_SIZE-PARMAREA+4(%r12) # store ramdisk size
|
||||
@@ -298,86 +317,14 @@ iplstart:
|
||||
clc 0(3,%r2),.L_eof
|
||||
bz .Lagain2
|
||||
|
||||
#
|
||||
# Copy kernel image to final position
|
||||
#
|
||||
|
||||
la %r2,_load_psw # prepare copy code
|
||||
la %r3,.Lcopypsw
|
||||
mvc 0(8,%r3),0(%r2) # copy load psw
|
||||
|
||||
l %r4,_initrd_addr+4
|
||||
ahi %r4,-0x100
|
||||
|
||||
la %r2,.Lcopystart # get copy code addr (from)
|
||||
mvc 0(.Lcopyend-.Lcopystart,%r4),0(%r2) # copy code
|
||||
|
||||
l %r1,.Loffset # temp kernel address (from)
|
||||
|
||||
l %r2,4(%r3) # get kernel addr from psw (to)
|
||||
n %r2,.Lpswmask
|
||||
s %r2,.Lkernoff
|
||||
|
||||
l %r3,.Lkernsize # (length)
|
||||
br %r4 # branch to copy routine
|
||||
|
||||
#
|
||||
# Copy kernel, then load PSW
|
||||
#
|
||||
# R1 = from
|
||||
# R2 = to
|
||||
# R3 = length
|
||||
#
|
||||
|
||||
.align 8
|
||||
.Lcopystart:
|
||||
clr %r1,%r2
|
||||
je .Lcopyfinish # kernel is already at dest
|
||||
jh .Lcopytolower # need move to lower address
|
||||
|
||||
# Copy last to first
|
||||
|
||||
ar %r1,%r3 # start from last long
|
||||
ar %r2,%r3
|
||||
lhi %r4,-4 # set addr increment to -4
|
||||
j .Lcopygo
|
||||
|
||||
# Copy first to last
|
||||
|
||||
.Lcopytolower:
|
||||
lhi %r4,4 # set addr increment to 4
|
||||
|
||||
.Lcopygo:
|
||||
ahi %r3,3 # get size in longs
|
||||
srl %r3,2
|
||||
|
||||
.Lcopyloop:
|
||||
l %r0,0(%r1) # copy longs
|
||||
st %r0,0(%r2)
|
||||
ar %r1,%r4
|
||||
ar %r2,%r4
|
||||
brct %r3,.Lcopyloop
|
||||
|
||||
.Lcopyfinish:
|
||||
bras %r13,.Lcopyload # set up base register
|
||||
.Lcopyload:
|
||||
sr %r0,%r0
|
||||
la %r1,.Lcopypsw-.Lcopyload(%r13)
|
||||
mvc 0(8,%r0),0(%r1) # copy load psw to 0
|
||||
|
||||
lm %r0,%r15,.Lvmparam-.Lcopyload(%r13) # load saved registers
|
||||
.Lnoinitrd:
|
||||
mvc 0(8,0),_load_psw # copy load psw
|
||||
lpsw 0 # start kernel
|
||||
|
||||
.align 8
|
||||
.Lcopypsw:
|
||||
.quad 0
|
||||
.Lvmparam:
|
||||
.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 # vm parameter space
|
||||
|
||||
.Lcopyend:
|
||||
|
||||
.Lpswmask:
|
||||
.long 0x7fffffff
|
||||
.Lkernsize:
|
||||
.long 0
|
||||
.Lkernoff:
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifndef JOB_H
|
||||
#define JOB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "disk.h"
|
||||
#include "zipl.h"
|
||||
|
||||
@@ -39,13 +41,19 @@ struct job_target_data {
|
||||
blocknum_t targetoffset;
|
||||
};
|
||||
|
||||
struct job_ipl_data {
|
||||
struct job_common_ipl_data {
|
||||
char* image;
|
||||
char* parmline;
|
||||
char* ramdisk;
|
||||
address_t image_addr;
|
||||
address_t parm_addr;
|
||||
address_t ramdisk_addr;
|
||||
bool optional;
|
||||
bool ignore;
|
||||
};
|
||||
|
||||
struct job_ipl_data {
|
||||
struct job_common_ipl_data common;
|
||||
address_t envblk_addr;
|
||||
int is_kdump;
|
||||
};
|
||||
@@ -61,13 +69,8 @@ struct job_segment_data {
|
||||
};
|
||||
|
||||
struct job_dump_data {
|
||||
struct job_common_ipl_data common;
|
||||
char* device;
|
||||
char* image;
|
||||
char* parmline;
|
||||
char* ramdisk;
|
||||
address_t image_addr;
|
||||
address_t parm_addr;
|
||||
address_t ramdisk_addr;
|
||||
uint64_t mem;
|
||||
};
|
||||
|
||||
@@ -80,13 +83,8 @@ struct job_mvdump_data {
|
||||
};
|
||||
|
||||
struct job_ipl_tape_data {
|
||||
struct job_common_ipl_data common;
|
||||
char* device;
|
||||
char* image;
|
||||
char* parmline;
|
||||
char* ramdisk;
|
||||
address_t image_addr;
|
||||
address_t parm_addr;
|
||||
address_t ramdisk_addr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
|
||||
#define ADDRESS_LIMIT 0x80000000UL
|
||||
#define UNSPECIFIED_ADDRESS -1UL
|
||||
#define MAXIMUM_PARMLINE_SIZE 0x380UL
|
||||
#define LEGACY_MAXIMUM_PARMLINE_SIZE 0x380UL
|
||||
#define MAXIMUM_PARMLINE_SIZE 0x10000UL
|
||||
#define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000UL
|
||||
|
||||
#define BOOTMAP_FILENAME "bootmap"
|
||||
|
||||
@@ -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)";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user