mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
305 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 | ||
|
|
04407d24fc | ||
|
|
bcb8ea09d1 | ||
|
|
88619b6dba | ||
|
|
e127b89624 | ||
|
|
3591b94c42 | ||
|
|
2a29a28f78 | ||
|
|
cb831aea44 | ||
|
|
04be704083 | ||
|
|
1bcfcd3253 | ||
|
|
2dbaf9f991 | ||
|
|
2ea20094d0 | ||
|
|
a80c14d72c | ||
|
|
10518da618 | ||
|
|
e9d1e168b2 | ||
|
|
7567506cca | ||
|
|
524017ac06 | ||
|
|
15774dbe32 | ||
|
|
f247850ff0 | ||
|
|
800ef7cb13 | ||
|
|
48cf3b3809 | ||
|
|
99c8c27302 | ||
|
|
a5c3af1361 | ||
|
|
353c23a157 | ||
|
|
d371ba0db8 | ||
|
|
8308143008 | ||
|
|
d8871197ea | ||
|
|
df338a3bac | ||
|
|
1005e7be7e | ||
|
|
afb14cba7a | ||
|
|
1e450e6f7b | ||
|
|
696a007b62 | ||
|
|
ea5f3f4a1c | ||
|
|
6afd5e97d1 | ||
|
|
b1da8bbbe9 | ||
|
|
6ece595532 | ||
|
|
40d4aa38e9 | ||
|
|
c58cfd1ec1 | ||
|
|
02086f26d2 | ||
|
|
7a2666d727 | ||
|
|
2df532c1fa | ||
|
|
969a439aaa | ||
|
|
f8331a4b8e | ||
|
|
895dc80580 | ||
|
|
d93645d690 | ||
|
|
c3bb6307b8 | ||
|
|
6208c0626a | ||
|
|
3949c62f6f | ||
|
|
8c6c2ef30e | ||
|
|
12c84469fd | ||
|
|
0a8e726d05 | ||
|
|
533df585fd | ||
|
|
c4eb2d5fba | ||
|
|
35029ad010 | ||
|
|
296079f70a | ||
|
|
4f1c73d592 | ||
|
|
1fb859729e | ||
|
|
bbeb0f8445 | ||
|
|
2ae44cb794 | ||
|
|
a0b8033088 | ||
|
|
9cc74839b0 | ||
|
|
b7b7002855 | ||
|
|
55e428a4d7 | ||
|
|
cb09223a56 | ||
|
|
28db3523d0 | ||
|
|
f15e32cdb1 | ||
|
|
094057e265 | ||
|
|
025a2198a4 | ||
|
|
2b938b78aa | ||
|
|
de36fc5259 | ||
|
|
e3e5b6422a | ||
|
|
38343be0fa | ||
|
|
957e612720 | ||
|
|
1d2316caef | ||
|
|
64539853ca | ||
|
|
686c331b69 | ||
|
|
01551f98d5 | ||
|
|
0e2c5907e0 | ||
|
|
68ff79e4c3 | ||
|
|
5b96d86538 | ||
|
|
4043137cd7 | ||
|
|
735d8f5493 | ||
|
|
28efa7d120 | ||
|
|
ebde75eeda | ||
|
|
a645ec0fc0 | ||
|
|
761a185938 | ||
|
|
890b15d158 | ||
|
|
07677668a3 | ||
|
|
fde914fd1c | ||
|
|
35c97a07aa | ||
|
|
45e3f016f4 | ||
|
|
38b520ab4c | ||
|
|
3b6920271e | ||
|
|
a3146409da | ||
|
|
76aa8e4a52 | ||
|
|
f5e6f4a8cb | ||
|
|
80cb1553a3 | ||
|
|
706f59b9d9 | ||
|
|
02cc902b90 | ||
|
|
ff2fb42868 | ||
|
|
451bf7fbc6 | ||
|
|
d2a4a8b0f3 | ||
|
|
fe5753d34d | ||
|
|
fe187eb3d3 | ||
|
|
4a8afd4ed6 | ||
|
|
74e6ebe1df | ||
|
|
ec83da3a39 | ||
|
|
c11b0cdcaa | ||
|
|
4f3cba406e | ||
|
|
c55ac2c016 | ||
|
|
4eab80ef44 | ||
|
|
41ddd35bc1 | ||
|
|
bdc44cafb6 | ||
|
|
46583b4db6 | ||
|
|
a2359dbe5f | ||
|
|
52b6e57743 | ||
|
|
b1997c7aac | ||
|
|
c56aea0fa9 | ||
|
|
e24629b977 | ||
|
|
268dcebe23 | ||
|
|
030c0054b1 | ||
|
|
154914ee7a | ||
|
|
56fecf1832 | ||
|
|
081499f355 | ||
|
|
26c34a49b1 | ||
|
|
e70cde2c5d | ||
|
|
fff83fc116 | ||
|
|
5a7d2a58c8 | ||
|
|
89d25559e0 | ||
|
|
49901079d4 | ||
|
|
317384b5c9 | ||
|
|
8db32a8cb9 | ||
|
|
4cf73238fc | ||
|
|
27120f2824 | ||
|
|
8f32a60c22 | ||
|
|
0fa2f9acf7 | ||
|
|
2ca7db75d3 | ||
|
|
02aaff72fa | ||
|
|
8723dbce04 | ||
|
|
c5d566a4da | ||
|
|
d90344a2d5 | ||
|
|
71a667fbf0 | ||
|
|
d2611b472b | ||
|
|
34482d67c0 | ||
|
|
b77523ab4d | ||
|
|
802e5f6607 | ||
|
|
5e5d49264f | ||
|
|
3e818c53b2 | ||
|
|
9100d6f40e | ||
|
|
8bcb93673e | ||
|
|
21fe08ad23 | ||
|
|
f8d3e5069a | ||
|
|
9fb2568134 | ||
|
|
a38d82e8f9 | ||
|
|
8c9cc6e12a | ||
|
|
ce8383e5ac | ||
|
|
9696b4c9b5 | ||
|
|
466ceb02a1 | ||
|
|
f6ab7f6cda | ||
|
|
568caa0501 | ||
|
|
2ece47ee1a | ||
|
|
0fafbcf3bb | ||
|
|
7244785279 | ||
|
|
cdf716a7a9 | ||
|
|
529ad4000e | ||
|
|
0772c0f01b | ||
|
|
27a562da0a | ||
|
|
cd532cb6cd | ||
|
|
80b1306102 | ||
|
|
4b0403a963 | ||
|
|
1d9e7b614c | ||
|
|
44de579311 | ||
|
|
4fb18a1e7b | ||
|
|
59206b88d8 | ||
|
|
7a64b88396 | ||
|
|
b7807d0195 | ||
|
|
2f436d6ee0 | ||
|
|
302cd4ed7e | ||
|
|
69526998f0 | ||
|
|
a65bc51cf4 | ||
|
|
eb1fd47a85 | ||
|
|
c4918fe713 | ||
|
|
d23558f1d1 | ||
|
|
3d79a542d4 | ||
|
|
b6bdd7744a | ||
|
|
3f3f063c98 | ||
|
|
78d63f2333 |
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
|
||||
33
.gitignore
vendored
33
.gitignore
vendored
@@ -5,6 +5,14 @@
|
||||
*.a
|
||||
*.o.d
|
||||
|
||||
# ctags files
|
||||
tags
|
||||
TAGS
|
||||
|
||||
# Ignore coverage data
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
#
|
||||
# Ignore generated executables and other generated files
|
||||
#
|
||||
@@ -13,6 +21,7 @@ cpacfstats/cpacfstats
|
||||
cpacfstats/cpacfstatsd
|
||||
cpumf/chcpumf
|
||||
cpumf/lscpumf
|
||||
cpumf/lshwc
|
||||
cpuplugd/cpuplugd
|
||||
dasdfmt/dasdfmt
|
||||
dasdinfo/dasdinfo
|
||||
@@ -20,6 +29,8 @@ dasdview/dasdview
|
||||
dump2tar/src/dump2tar
|
||||
fdasd/fdasd
|
||||
hmcdrvfs/hmcdrvfs
|
||||
hsavmcore/check-dep-fuse
|
||||
hsavmcore/hsavmcore
|
||||
hyptop/hyptop
|
||||
ip_watcher/xcec-bridge
|
||||
ipl_tools/chreipl
|
||||
@@ -39,16 +50,14 @@ libekmfweb/detect-openssl-version.dep
|
||||
libekmfweb/libekmfweb.so
|
||||
libekmfweb/libekmfweb.so.1
|
||||
libekmfweb/libekmfweb.so.1.0
|
||||
libutil/util_base_example
|
||||
libutil/util_file_example
|
||||
libutil/util_libc_example
|
||||
libutil/util_opt_command_example
|
||||
libutil/util_opt_example
|
||||
libutil/util_panic_example
|
||||
libutil/util_path_example
|
||||
libutil/util_prg_example
|
||||
libutil/util_rec_example
|
||||
libutil/util_scandir_example
|
||||
libkmipclient/check-dep-libkmipclient
|
||||
libkmipclient/detect-openssl-version.dep
|
||||
libkmipclient/libkmipclient.so
|
||||
libkmipclient/libkmipclient.so.1
|
||||
libkmipclient/libkmipclient.so.1.0
|
||||
libseckey/check-dep-libseckey
|
||||
libseckey/detect-openssl-version.dep
|
||||
libutil/*_example
|
||||
libvmcp/vmcp_example
|
||||
libzds/libzds.a
|
||||
lsstp/lsstp
|
||||
@@ -94,13 +103,17 @@ zipl/boot/*.bin
|
||||
zipl/boot/*.exec
|
||||
zipl/boot/data.h
|
||||
zipl/src/chreipl_helper.device-mapper
|
||||
zdev/src/zdev_id
|
||||
zipl/src/zipl
|
||||
zipl/src/zipl-editenv
|
||||
zipl/src/zipl_helper.device-mapper
|
||||
zkey/check-dep-zkey
|
||||
zkey/check-dep-zkey-cryptsetup
|
||||
zkey/detect-libcryptsetup.dep
|
||||
zkey/ekmfweb/libekmfweb.dep
|
||||
zkey/ekmfweb/zkey-ekmfweb.so
|
||||
zkey/kmip/libkmipclient.dep
|
||||
zkey/kmip/zkey-kmip.so
|
||||
zkey/zkey
|
||||
zkey/zkey-cryptsetup
|
||||
zpcictl/zpcictl
|
||||
|
||||
13
AUTHORS.md
13
AUTHORS.md
@@ -2,9 +2,10 @@ List of all individuals having contributed content to s390-tools
|
||||
----------------------------------------------------------------
|
||||
|
||||
- Alexander Egorenkov
|
||||
- Alexandra Winter
|
||||
- Alexey Ishchuk
|
||||
- Andreas Herrmann
|
||||
- Andre Wild
|
||||
- André Wild
|
||||
- Antoinette Kaschner
|
||||
- Arnd Bergmann
|
||||
- Axel Wirbser
|
||||
@@ -18,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
|
||||
@@ -27,6 +29,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Erwin Vicari
|
||||
- Eugene Crosser
|
||||
- Eugene Dvurechenski
|
||||
- Fabrice Fontaine
|
||||
- Farhan Ali
|
||||
- Fedor Loshakov
|
||||
- Felix Beck
|
||||
@@ -36,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
|
||||
@@ -50,13 +54,14 @@ 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
|
||||
- Jean-Baptiste Joret
|
||||
- Jens Remus
|
||||
- Jochen Roehrig
|
||||
- Joern Siglen
|
||||
- Juergen Christ
|
||||
- Julian Wiedmann
|
||||
- Karsten Graul
|
||||
@@ -64,12 +69,14 @@ List of all individuals having contributed content to s390-tools
|
||||
- Klaus-Dieter Wacker
|
||||
- Lakhvich Dmitriy
|
||||
- Marc Hartmayer
|
||||
- Mario Held
|
||||
- Mark Dettinger
|
||||
- Mark Post
|
||||
- Martin Kammerer
|
||||
- Martin Peschke
|
||||
- Martin Petermann
|
||||
- Martin Schwidefsky
|
||||
- Matthew Rosato
|
||||
- Maxim Shchetynin
|
||||
- Melissa Howland
|
||||
- Michael Ernst
|
||||
@@ -99,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
|
||||
@@ -107,6 +115,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Swen Schillig
|
||||
- Taraka R. Bodireddy
|
||||
- Thomas Heidrich
|
||||
- Thomas Huth
|
||||
- Thomas Richter
|
||||
- Thomas Spatzier
|
||||
- Thomas Weber
|
||||
|
||||
132
CHANGELOG.md
132
CHANGELOG.md
@@ -1,5 +1,137 @@
|
||||
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
|
||||
|
||||
Add new tools / libraries:
|
||||
- chreipl-fcp-mpath: New toolset that uses multipath information to change
|
||||
the configured FCP re-IPL path on detecting issues with the current path
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo.sh: Add retry timeout and remove possible blocking "blockdev --report"
|
||||
- dbginfo.sh: Collect config- and debug-data for chreipl-fcp-mpath
|
||||
- hsci: Add support for multiple MAC addresses
|
||||
|
||||
Bug Fixes:
|
||||
- lshwc: Fix compile error for gcc <8.1
|
||||
- zdump: Various clean-ups and fixes
|
||||
- ziomon: Correct throughput calculation in ziorep_printers
|
||||
- zipl: Fix segmentation fault when setting stage3_parms
|
||||
|
||||
* __v2.18.0 (2021-10-01)__
|
||||
|
||||
For Linux kernel version: 5.14
|
||||
|
||||
Add new tools:
|
||||
- scripts: Add tool for parsing sclp s390dbf logs
|
||||
- zdev: Add udev rule helper tool
|
||||
- zipl-editenv: Add tool to operate with zIPL environment installed in the boot record
|
||||
|
||||
Changes of existing tools:
|
||||
- Makefile: Fix order of build of libraries for parallel builds
|
||||
- dbginfo.sh: Add collection in area of timedate, coredump and --check option
|
||||
- dbginfo.sh: Add exception on dump2tar for /sys/kernel/mm/page_idle/bitmap
|
||||
- dbginfo.sh: Cleanup of outdated sections and general code rework
|
||||
- dbginfo.sh: Collect zipl boot menu entries from boot loader specification
|
||||
- lszcrypt: Add support for vfio-ap status field
|
||||
- lszcrypt: Improved output for deconfig cards and queues
|
||||
- lszfcp: Add linkdown case to host marker of extended output
|
||||
- zdev: Add auto-config for PCI and crypto devices
|
||||
- zdump: Introduce multi-level message logging
|
||||
- zipl: Add support for environment block interpretation
|
||||
- zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled
|
||||
|
||||
Bug Fixes:
|
||||
- hsavmcore: Avoid recompilation of overlay during install step
|
||||
- libkmipclient: Fix parsing of hex values for XML and JSON encoding
|
||||
- vmur/vmur.cpp: Fix error handling on transfer failure
|
||||
- zdump: Lots of smaller fixes across the board
|
||||
|
||||
* __v2.17.0 (2021-07-07)__
|
||||
|
||||
For Linux kernel version: 5.12 / 5.13
|
||||
|
||||
Add new tools / libraries:
|
||||
- hsavmcore: New utility to make the dump process with kdump more efficient
|
||||
- libkmipclient: Add KMIP client shared library
|
||||
- libseckey: Add a secure key library
|
||||
- lshwc: New tool to extract and list complete counter sets
|
||||
|
||||
Changes of existing tools:
|
||||
- genprotimg: Add '--(enable|disable)-pckmo' options
|
||||
- genprotimg: Add OpenSSL 3.0 support
|
||||
- genprotimg: Change plaintext control flags defaults so PCKMO functions are allowed
|
||||
- libutil: Introduce multi-level message logging (util_log)
|
||||
- libutil: Introduce util_arch module
|
||||
- udev/dasd: Change DASD udev-rule to set none scheduler
|
||||
- zdsfs: Add transparent codepage conversion
|
||||
- zkey: Add support for KMIP-based key management systems
|
||||
|
||||
Bug Fixes:
|
||||
- ttyrun-getty: Avoid conflicts with serial-getty@
|
||||
- dbginfo: add /proc/kallsyms - refresh zVM, lscpu - fix WORKARCHIVE handling
|
||||
- dbginfo: add KVM data collection for server and guest - fix lszdev
|
||||
- genprotimg: Add missing return values in error paths
|
||||
- zkey: Fix conversion of CCA DATA keys to CCA CIPHER keys
|
||||
- znetconf: avoid conflict with "chzdev -e"
|
||||
|
||||
* __v2.16.0 (2021-02-19)__
|
||||
|
||||
For Linux kernel version: 5.10 / 5.11
|
||||
|
||||
27
Makefile
27
Makefile
@@ -3,15 +3,22 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
||||
# Include common definitions
|
||||
include common.mak
|
||||
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb
|
||||
#
|
||||
# BASELIBS: Libraries that have no dependency to other libraries in s390-tools
|
||||
# LIBS: Libraries that can have a dependency to base libraries
|
||||
# TOOLS: Tools that can have a dependency to base libraries or libraries
|
||||
#
|
||||
BASELIB_DIRS = libutil libseckey
|
||||
LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
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 \
|
||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||
genprotimg lsstp hsci
|
||||
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath
|
||||
|
||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
||||
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
all: $(TOOL_DIRS)
|
||||
clean: $(TOOL_DIRS)
|
||||
@@ -25,9 +32,10 @@ MAKECMDGOALS = all
|
||||
endif
|
||||
|
||||
#
|
||||
# We have to build the libraries before the tools are built. Otherwise
|
||||
# the tools would trigger parallel "make -C" builds for libraries in
|
||||
# case of "make -j".
|
||||
# We have to build the base libraries before the other libraries are built,
|
||||
# and then build the other libraries before the tools are built. Otherwise the
|
||||
# other libraries and tools would trigger parallel "make -C" builds for the
|
||||
# base libraries and the other libraries in case of "make -j".
|
||||
#
|
||||
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
|
||||
# "foreach" to generate a ";" separated list of "make -C <target>".
|
||||
@@ -44,7 +52,12 @@ $(TOOL_DIRS): $(LIB_DIRS)
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(TOOL_DIRS)
|
||||
|
||||
$(LIB_DIRS):
|
||||
$(LIB_DIRS): $(BASELIB_DIRS)
|
||||
$(foreach goal,$(MAKECMDGOALS), \
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(LIB_DIRS)
|
||||
|
||||
$(BASELIB_DIRS):
|
||||
$(foreach goal,$(MAKECMDGOALS), \
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(BASELIB_DIRS)
|
||||
|
||||
82
README.md
82
README.md
@@ -109,6 +109,9 @@ Package contents
|
||||
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
||||
several traces, make it more readable etc.
|
||||
|
||||
* sclpdbf:
|
||||
Display debug data for the sclp kernel component.
|
||||
|
||||
* scsi_logging_level:
|
||||
Create, get or set the logging level for the SCSI logging facility.
|
||||
|
||||
@@ -223,7 +226,8 @@ Package contents
|
||||
* CPU-measurement facilities (CPU-MF) tools:
|
||||
Use the lscpumf tool to display information about the CPU-measurement
|
||||
counter and sampling facilities. Use the chcpumf tool to control the
|
||||
sampling facility support.
|
||||
sampling facility support. Use lshwc to extract complete counter sets from
|
||||
the CPU Measurement Facilities.
|
||||
|
||||
* cpacfstats:
|
||||
The cpacfstats tools provide a client/server application set to monitor
|
||||
@@ -249,9 +253,27 @@ Package contents
|
||||
Management Foundation - Web Edition, and is used to manage keys in an
|
||||
enterprise.
|
||||
|
||||
* libkmipclient:
|
||||
A shared library that provides an KMIP client to communicate with an KMIP
|
||||
server. KMIP stands for Key Management Interoperability Protocol, and is an
|
||||
extensible communication protocol that defines message formats for the
|
||||
manipulation of cryptographic keys on a key management server.
|
||||
|
||||
* hsci:
|
||||
Manage HiperSockets Converged Interfaces (HSCI).
|
||||
|
||||
* hsavmcore:
|
||||
hsavmcore is designed to make the dump process with kdump more efficient.
|
||||
With hsavmcore, the HSA memory that contains a part of the production
|
||||
kernel's memory can be released early in the process. Depending on the size
|
||||
of the production kernel's memory, writing the dump to persistent storage
|
||||
can be time consuming and prevent the HSA memory from being reused
|
||||
by other LPARs.
|
||||
|
||||
* chreipl-fcp-mpath:
|
||||
Use multipath information to change the configured FCP re-IPL path on
|
||||
detecting issues with the current path.
|
||||
|
||||
For more information refer to the following publications:
|
||||
|
||||
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||
@@ -270,25 +292,30 @@ 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 |
|
||||
| pfm | `HAVE_PFM` | cpacfstats |
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
|
||||
This table lists additional build or install options:
|
||||
|
||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||
|------------------|:----------------------------:|:--------------:|
|
||||
| dracut | `HAVE_DRACUT` | zdev |
|
||||
| initramfs-tools | `HAVE_INITRAMFS` | zdev |
|
||||
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||
|------------------|:----------------------------:|:-----------------------:|
|
||||
| dracut | `HAVE_DRACUT` | zdev, chreipl-fcp-mpath |
|
||||
| initramfs-tools | `HAVE_INITRAMFS` | zdev |
|
||||
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
||||
|
||||
The s390-tools build process uses "pkg-config" if available and hard-coded
|
||||
compiler and linker options otherwise.
|
||||
@@ -328,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.
|
||||
@@ -427,3 +454,30 @@ the different tools are provided:
|
||||
(libcurl-devel.rpm).
|
||||
Tip: you may skip the libekmfweb build by adding `HAVE_OPENSSL=0`,
|
||||
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
* hsavmcore:
|
||||
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`
|
||||
to the make invocation.
|
||||
|
||||
* libkmipclient:
|
||||
For building the libkmipclient shared library you need openssl version 1.1.1
|
||||
or newer installed (openssl-devel.rpm). Also required are json-c version 0.13
|
||||
or newer (json-c-devel.rpm), libxml2 version 2.9.10 or newer
|
||||
(libxml2-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm).
|
||||
Tip: you may skip the libkmipclient build by adding `HAVE_OPENSSL=0`,
|
||||
`HAVE_JSONC=0`, `HAVE_LIBXML2=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
* chreipl-fcp-mpath:
|
||||
For a complete list and documentation of the requirements, installation and
|
||||
uninstallation, please see
|
||||
[chreipl-fcp-mpath/README.md](chreipl-fcp-mpath/README.md).
|
||||
|
||||
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 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
|
||||
11
chreipl-fcp-mpath/.gitignore
vendored
Normal file
11
chreipl-fcp-mpath/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# build artifacts
|
||||
/chreipl-fcp-mpath-common.sh
|
||||
/chreipl-fcp-mpath-is-ipl-tgt
|
||||
/chreipl-fcp-mpath-is-ipl-vol
|
||||
/chreipl-fcp-mpath-is-reipl-zfcp
|
||||
/chreipl-fcp-mpath-record-volume-identifier
|
||||
/chreipl-fcp-mpath-try-change-ipl-path
|
||||
/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
/README.html
|
||||
/README.pdf
|
||||
/chreipl-fcp-mpath.md
|
||||
274
chreipl-fcp-mpath/Makefile
Normal file
274
chreipl-fcp-mpath/Makefile
Normal file
@@ -0,0 +1,274 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Utilities list in GNU Make Conventions:
|
||||
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||
# Those necessary for sourced Makefiles:
|
||||
# - ../common.mak
|
||||
# - chreipl-fcp-mpath.mak
|
||||
# bash:
|
||||
# - bash
|
||||
# GNU coreutils:
|
||||
# - sha256sum
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# GNU awk:
|
||||
# - gawk
|
||||
|
||||
override SHELL := /bin/bash
|
||||
override .SHELLFLAGS := -O globstar -O nullglob -O extglob -c
|
||||
|
||||
# Include common s390-tools definitions
|
||||
include ../common.mak
|
||||
|
||||
# Include common chreipl-fcp-mpath definitions
|
||||
include chreipl-fcp-mpath.mak
|
||||
|
||||
# Local setting: .make.config
|
||||
# You may create a file named like this in the same directory as this
|
||||
# Makefile, and customize the build this way (e.g. re-define variables
|
||||
# set in `chreipl-fcp-mpath.mak`, or define a `CHREIPLZFCPMP_POST_INSTALL`
|
||||
# that is automatically called after each installation)
|
||||
ifneq ($(wildcard .make.config),)
|
||||
include $(wildcard .make.config)
|
||||
endif
|
||||
|
||||
#
|
||||
## Build
|
||||
#
|
||||
|
||||
.PHONY: chreipl-fcp-mpath chreipl-fcp-mpath-clean
|
||||
chreipl-fcp-mpath:
|
||||
chreipl-fcp-mpath-clean:
|
||||
all: chreipl-fcp-mpath
|
||||
clean: chreipl-fcp-mpath-clean
|
||||
|
||||
# common function used in the helper scripts
|
||||
CHREIPL_FCP_MPATH_COMMON := \
|
||||
chreipl-fcp-mpath-common.sh
|
||||
CHREIPL_FCP_MPATH_UDEV_HELPER := \
|
||||
chreipl-fcp-mpath-is-ipl-tgt \
|
||||
chreipl-fcp-mpath-is-ipl-vol \
|
||||
chreipl-fcp-mpath-is-reipl-zfcp \
|
||||
chreipl-fcp-mpath-record-volume-identifier \
|
||||
chreipl-fcp-mpath-try-change-ipl-path
|
||||
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): $(MAKEFILE_LIST)
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): % : %.in
|
||||
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||
chmod a+x $(@)
|
||||
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER): $(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-udev-helper-clean
|
||||
chreipl-fcp-mpath-udev-helper-clean:
|
||||
rm -f $(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
udev/rules.d/70-chreipl-fcp-mpath.rules: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
|
||||
chreipl-fcp-mpath: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-udev-helper-clean
|
||||
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: $(MAKEFILE_LIST)
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: % : %.in
|
||||
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-dracut-clean
|
||||
chreipl-fcp-mpath-dracut-clean:
|
||||
rm -f dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
|
||||
chreipl-fcp-mpath: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-dracut-clean
|
||||
|
||||
ifeq ($(ENABLE_DOC),1)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-doc
|
||||
chreipl-fcp-mpath-doc: README.html chreipl-fcp-mpath.7
|
||||
|
||||
ifeq ($(ENABLE_DOC_PDF),1)
|
||||
chreipl-fcp-mpath-doc: README.pdf
|
||||
|
||||
README.pdf: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||
endif
|
||||
|
||||
README.html: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||
|
||||
chreiplzfcpmp-doc-man-meta = \
|
||||
-M title="CHREIPL-FCP-MPATH" \
|
||||
-V header="Administrator Manual" \
|
||||
-V section="7" \
|
||||
-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: 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
|
||||
#
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install
|
||||
# The content of `CHREIPLZFCPMP_POST_INSTALL` (bash script) is automatically
|
||||
# called *after* installing chreipl-fcp-mpath during `make install`. If not
|
||||
# defined (the default), nothing happens. You may define this on the make
|
||||
# command line, or by creating a `.make.config` and defining the variable in
|
||||
# there.
|
||||
chreipl-fcp-mpath-install:
|
||||
$(CHREIPLZFCPMP_POST_INSTALL)
|
||||
|
||||
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)
|
||||
chreipl-fcp-mpath-install-udev-rules: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(UDEVRULESDIR) \
|
||||
udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
|
||||
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)
|
||||
chreipl-fcp-mpath-install-udev-helper: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
$(INSTALL_EXEC) -t $(DESTDIR)$(UDEVDIR) $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
|
||||
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)
|
||||
chreipl-fcp-mpath-install-libfiles: $(CHREIPL_FCP_MPATH_COMMON)
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(CHREIPLZFCPMPDIR) \
|
||||
$(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-libfiles
|
||||
|
||||
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)
|
||||
chreipl-fcp-mpath-install-dracut-config: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(DRACUTCONFDIR) \
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-dracut-config
|
||||
|
||||
endif
|
||||
|
||||
# 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
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \
|
||||
chreipl-fcp-mpath.7
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||
|
||||
#
|
||||
## Utility
|
||||
#
|
||||
|
||||
# Utilities for the debug feature of chreipl-fcp-mpath-common.sh.
|
||||
#
|
||||
# When `chreipl-fcp-mpath` is built with D=1 (default is D=0), each run
|
||||
# of one of the helper scripts will create a debug log in $(DEBUGOUTDIR)
|
||||
# (default: /run/udev) which among other things contains the complete shell
|
||||
# trace of that script run, with some added information that would not be
|
||||
# inspectable otherwise with just the trace.
|
||||
#
|
||||
# There is currently no way of enabling/disabling this at runtime.
|
||||
#
|
||||
# chreipl-fcp-mpath-common.sh defines debug log file name as
|
||||
# "chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" where SEQNUM is a
|
||||
# udev rule environment variable and each X is replaced with some [[:alnum:]]
|
||||
# by mktemp. For a definition of `debug_trace_tag`, please see the comments
|
||||
# in the source.
|
||||
#
|
||||
# The following targets can be used for some simple access and filtering of the
|
||||
# logs during development.
|
||||
|
||||
DEBUG_LOG_GLOB = $(DEBUGOUTDIR)/chreiplzfcpmp-[[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]-+([[:digit:]]).[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||
|
||||
# display all debug log files on the system
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs
|
||||
chreipl-fcp-mpath-debug-logs:
|
||||
@ls -1d $(DEBUG_LOG_GLOB)
|
||||
|
||||
# display only debug log files of script runs that exited with status 0 (= good)
|
||||
#
|
||||
# XXX: `sed -n -e '$p'` is used instead of `tail -n1` to prevent an other
|
||||
# dependency just for that (both invocations do the same thing).
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-filter-good
|
||||
chreipl-fcp-mpath-debug-logs-filter-good:
|
||||
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||
| sed -n -e '$$p' \
|
||||
| grep -q -e '^+ exit 0$$' || continue; \
|
||||
ls -d "$${lg}"; \
|
||||
done
|
||||
|
||||
# display only debug log files of script runs that didn't exit with status 0
|
||||
# (= bad)
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-filter-bad
|
||||
chreipl-fcp-mpath-debug-logs-filter-bad:
|
||||
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||
| sed -n -e '$$p' \
|
||||
| grep -q -e '^+ exit 0$$' && continue; \
|
||||
ls -d "$${lg}"; \
|
||||
done
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-clean
|
||||
chreipl-fcp-mpath-debug-logs-clean:
|
||||
rm -f $(DEBUG_LOG_GLOB)
|
||||
270
chreipl-fcp-mpath/README.md
Normal file
270
chreipl-fcp-mpath/README.md
Normal file
@@ -0,0 +1,270 @@
|
||||
<!-- markdown documentation: https://github.github.com/gfm/ -->
|
||||
|
||||
NAME
|
||||
====
|
||||
|
||||
chreipl-fcp-mpath - use multipath information for re-IPL path failover on a
|
||||
running Linux instance
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
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.
|
||||
|
||||
The **chreipl-fcp-mpath** toolset monitors **udev** 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.
|
||||
|
||||
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.
|
||||
|
||||
**Chreipl-fcp-mpath** requires **udev**, **multipathd** and **dm-multipath**.
|
||||
Once installed, the toolset runs automatically and autonomously. No user
|
||||
intervention is possible or required.
|
||||
|
||||
Other than installing the toolset, there is no user interface for
|
||||
**chreipl-fcp-mpath**.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The **chreipl-fcp-mpath** tool has the following requirements on the
|
||||
Linux instance that is being monitored:
|
||||
|
||||
- The Linux instance must have started successfully, during IPL.
|
||||
|
||||
- The running Linux instance must use **dm-multipath** and **multipathd** 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.
|
||||
|
||||
- **udev** must run.
|
||||
|
||||
- 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.
|
||||
|
||||
- The WWID of the re-IPL volume must not change while the Linux instance is
|
||||
running.
|
||||
|
||||
- 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.
|
||||
|
||||
- The tool assumes that any manually reconfigured re-IPL device is valid and
|
||||
operational.
|
||||
|
||||
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.
|
||||
|
||||
Caution with Manual Changes to the Configured re-IPL Target
|
||||
-----------------------------------------------------------
|
||||
|
||||
**chreipl-fcp-mpath** 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.
|
||||
|
||||
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](#examples) for one
|
||||
way to suspend event processing.
|
||||
|
||||
MESSAGES
|
||||
========
|
||||
|
||||
During monitoring and event processing, **chreipl-fcp-mpath** writes messages
|
||||
to the syslog.
|
||||
|
||||
When the configured re-IPL path is changed to a different path to the same
|
||||
volume (priority *daemon.notice*):
|
||||
|
||||
> Changed re-IPL path to: \<device-bus-id\>:\<wwpn\>:\<lun\>.
|
||||
|
||||
When a path event indicates that the last available path has become
|
||||
non-operational (priority *daemon.alert*):
|
||||
|
||||
> 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.
|
||||
|
||||
When changing the configured re-IPL device failed because of an error with the
|
||||
used Linux kernel interface (priority *daemon.crit*):
|
||||
|
||||
> 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.
|
||||
|
||||
A failure to change the re-IPL device can indicate an inconsistent setting that
|
||||
cannot be corrected automatically by **chreipl-fcp-mpath**. As a result, the
|
||||
next re-IPL might fail or might not use the intended re-IPL device.
|
||||
|
||||
You can use the following tools to check and correct the current settings:
|
||||
|
||||
- **lsreipl** to confirm that the intended re-IPL device is configured;
|
||||
- **chreipl** to change the re-IPL device;
|
||||
- **lszfcp** to inspect the state of available paths to the re-IPL device.
|
||||
|
||||
<!-- NOT-IN-MAN { -->
|
||||
|
||||
SOFTWARE REQUIREMENTS
|
||||
=====================
|
||||
|
||||
**chreipl-fcp-mpath** integrates into s390-tools's build and install
|
||||
infrastructure. Use **make** to build it. No explicit dependency management is
|
||||
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, sha256sum);
|
||||
- util-linux (flock, hexdump, logger);
|
||||
- udev / systemd-udev;
|
||||
- multipath-tools.
|
||||
|
||||
To make use of the optional dracut configuration you need: dracut.
|
||||
|
||||
To build a fresh version of the documentation (man page) you need:
|
||||
|
||||
- pandoc;
|
||||
- GNU Core Utilities (date);
|
||||
- GNU awk;
|
||||
|
||||
otherwise the pre-cooked version shipped with the source will be used.
|
||||
|
||||
INSTALLATION
|
||||
============
|
||||
|
||||
If your distribution includes a packaged version of **chreipl-fcp-mpath**,
|
||||
either as a separate package or as part of a **s390-tools** package, install
|
||||
that package. Otherwise, you can either install it from source as part of
|
||||
**s390-tools** or separately.
|
||||
|
||||
To install **chreipl-fcp-mpath** as part of **s390-tools**, use **make** on the
|
||||
top-level directory of your **s390-tools** distribution. Installing the entire
|
||||
distribution might overwrite other already installed tools.
|
||||
|
||||
To install the tool separately, change into the **chreipl-fcp-mpath**
|
||||
directory, and use **make** there.
|
||||
|
||||
You need *root* privileges to install the tool into the root file system.
|
||||
|
||||
Calling **make** runs the build steps. Calling **make install** runs the build
|
||||
steps and copies the resulting components to their final destination.
|
||||
**s390-tools** offers more options and targets to customize the build (see
|
||||
**make help**).
|
||||
|
||||
**chreipl-fcp-mpath** has the following optional build options:
|
||||
|
||||
| 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 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:
|
||||
|
||||
~ # cd chreipl-fcp-mpath/
|
||||
~ # make HAVE_DRACUT=1 ENABLE_DOC=1
|
||||
~ # make HAVE_DRACUT=1 ENABLE_DOC=1 install
|
||||
|
||||
After the installation, reload the udev rules database:
|
||||
|
||||
~ # udevadm control --reload
|
||||
|
||||
*The toolset is now active on your running Linux instance.*
|
||||
|
||||
If you use the *HAVE_DRACUT=1* option, also rebuild your
|
||||
initial ramdisk, to immediately include the toolset instead of
|
||||
waiting for the next kernel update.
|
||||
|
||||
How to rebuild the initial ramdisk and the naming scheme for the
|
||||
resulting file or files depends on your distribution.
|
||||
The following example applies to Fedora and to Red Hat Enterprise Linux:
|
||||
|
||||
~ # dracut --force /boot/initramfs-"$(uname -r)".img "$(uname -r)"
|
||||
|
||||
For SUSE Linux Enterprise Server run for example:
|
||||
|
||||
~ # dracut --hostonly --force /boot/initrd-"$(uname -r)" "$(uname -r)"
|
||||
|
||||
These commands replace the initial ramdisk for the currently running kernel.
|
||||
If your distribution uses **zipl** as its boot loader, run **zipl** to refresh
|
||||
the boot record to find the new initial ramdisk.
|
||||
|
||||
~ # zipl
|
||||
|
||||
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
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-tgt
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-vol
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-reipl-zfcp
|
||||
/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
|
||||
|
||||
UNINSTALL
|
||||
=========
|
||||
|
||||
If your distribution includes a separately from **s390-tools** packaged version
|
||||
of **chreipl-fcp-mpath**, uninstall that package.
|
||||
|
||||
For installations without distribution packaging, you cannot uninstall
|
||||
**chreipl-fcp-mpath** with **make**.
|
||||
|
||||
Instead, remove the toolset by deleting the installed files as listed in
|
||||
[INSTALLATION](#installation)), reload the udev rules database, and rebuild all
|
||||
modified initial ramdisks as described in [INSTALLATION](#installation)).
|
||||
|
||||
<!-- NOT-IN-MAN } -->
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
Manual Changes to the Configured re-IPL Device
|
||||
----------------------------------------------
|
||||
|
||||
As outlined in [DESCRIPTION](#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 **chreipl-fcp-mpath**.
|
||||
You can avoid such collisions, by stopping event processing, making your
|
||||
changes, and then re-enabling event processing. You need *root* privileges for
|
||||
running the commands in the following example:
|
||||
|
||||
~ # udevadm settle
|
||||
~ # udevadm control --stop-exec-queue
|
||||
~ # chreipl ...
|
||||
~ # udevadm control --start-exec-queue
|
||||
|
||||
Listing messages with journalctl
|
||||
--------------------------------
|
||||
|
||||
If your Linux instance includes **journalctl**, use the following command to
|
||||
list all messages that are issued by **chreipl-fcp-mpath**:
|
||||
|
||||
~ # journalctl -t chreipl-fcp-mpath
|
||||
|
||||
To list only messages that were issued since the last IPL, use this command:
|
||||
|
||||
~ # journalctl -t chreipl-fcp-mpath -b
|
||||
|
||||
REPORTING BUGS
|
||||
==============
|
||||
|
||||
Use the **Issues** functionality on GitHub to report any bugs in
|
||||
**chreipl-fcp-mpath**:
|
||||
[s390-tools Issues](<https://github.com/ibm-s390-linux/s390-tools/issues> "Link to the s390-tools Issues page").
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
**chreipl**(8), **dracut**(8), **journalctl**(1), **lsreipl(8)**,
|
||||
**lszfcp**(8), **multipath**(8), **multipathd**(8), **udev**(7),
|
||||
**udevadm**(8), **zipl**(8)
|
||||
446
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
446
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
@@ -0,0 +1,446 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# GNU coreutils:
|
||||
# - mktemp
|
||||
# - readlink
|
||||
# - sync
|
||||
# util-linux:
|
||||
# - flock
|
||||
# - logger
|
||||
|
||||
# Makes use of udev event environment variables:
|
||||
# SEQNUM
|
||||
|
||||
# (1) expand failed globs to an empty string
|
||||
# (2) extended pattern matching to strip leading/trailing whitespaces
|
||||
shopt -s nullglob extglob
|
||||
# (1) don't overwrite existing files using redirects (e.g.: `>`)
|
||||
set -o noclobber
|
||||
|
||||
# make sure any state files created are only writeable by the owning user
|
||||
umask 027
|
||||
|
||||
# create log if DEBUG is enabled (with Make: D=1)
|
||||
#
|
||||
# Each script importing this library and expecting a debug log to be created
|
||||
# must declare a *trace tag* in a variable `debug_trace_tag`. This is used as
|
||||
# identifier in the log file name. The format is:
|
||||
#
|
||||
# [[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]
|
||||
# \ /\ /
|
||||
# --------\ /-------- -------------------\ /-------------------
|
||||
# \/ \/
|
||||
# relative position of some unique abbreviation for the script
|
||||
# execution in the name, excluding any common prefix
|
||||
# udev rules
|
||||
if '@DEBUG@' && [ -v debug_trace_tag ] && tlg="$(
|
||||
mktemp -p '@debugoutdir@' \
|
||||
"chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" \
|
||||
2>/dev/null)"
|
||||
then
|
||||
readonly tlg
|
||||
exec >|"${tlg}" 2>&1
|
||||
set -x
|
||||
set
|
||||
else
|
||||
unset tlg
|
||||
fi
|
||||
|
||||
declare -gr ID_FILE='@chreiplzfcpmp-id-file@'
|
||||
declare -gr FW_LOCK_FILE='@chreiplzfcpmp-fwlock-file@'
|
||||
|
||||
declare -gA TRAP_EXIT_FN=()
|
||||
declare -gf trap_exit 1>/dev/null
|
||||
function trap_exit() {
|
||||
local fn
|
||||
|
||||
for fn in "${TRAP_EXIT_FN[@]}"; do
|
||||
"${fn}"
|
||||
done
|
||||
|
||||
trap - EXIT
|
||||
}
|
||||
trap trap_exit EXIT
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_exclusive_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_exclusive_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# prevent concurrent file creation
|
||||
#
|
||||
# First, open the file defined in ${ID_FILE} for writing; this will
|
||||
# succeed and create the file only if it doesn't exist already. If the
|
||||
# file already exist, the first open attempt will fail and we fall
|
||||
# back to opening it only for reading; this will always succeed if the
|
||||
# file already exists (the reason why the first attempty failed). In
|
||||
# both cases store the corresponding file descriptor in
|
||||
# ${ID_FILE_LOCK}.
|
||||
#
|
||||
# XXX: This should be race free.
|
||||
# open() with O_EXCL... is atomic (we set `noclobber` as shell
|
||||
# option); at least as long as we talk about a local FS.
|
||||
if ! { exec {ID_FILE_LOCK}>"${ID_FILE}"; } 2>/dev/null; then
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null \
|
||||
|| return 1
|
||||
fi
|
||||
|
||||
declare -gf id_file_unlock_exclusive_create 1>/dev/null
|
||||
function id_file_unlock_exclusive_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
sync "${ID_FILE}" 2>/dev/null
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}>&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_exclusive_create]=id_file_unlock_exclusive_create
|
||||
)
|
||||
|
||||
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_exclusive_no_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_exclusive_no_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||
#
|
||||
# XXX: return code is used in `chreipl-fcp-mpath-try-change-ipl-path`
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf id_file_unlock_exclusive_no_create 1>/dev/null
|
||||
function id_file_unlock_exclusive_no_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
sync "${ID_FILE}" 2>/dev/null
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}<&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_no_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_exclusive_no_create]=id_file_unlock_exclusive_no_create
|
||||
)
|
||||
|
||||
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_shared_no_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_shared_no_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf id_file_unlock_shared_no_create 1>/dev/null
|
||||
function id_file_unlock_shared_no_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}<&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_shared_no_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_shared_no_create]=id_file_unlock_shared_no_create
|
||||
)
|
||||
|
||||
flock --shared --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# firmware_unlock_exclusive() - call to unlock when finished with critical section
|
||||
#
|
||||
# XXX: `firmware_lock_*` can't be taken recursively
|
||||
function firmware_lock_exclusive() {
|
||||
declare -g FIRMWARE_LOCK=""
|
||||
|
||||
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||
# whether this is a normal file or directory). This file descriptor
|
||||
# will only be used for locking - not for actual I/O.
|
||||
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf firmware_unlock_exclusive 1>/dev/null
|
||||
function firmware_unlock_exclusive() {
|
||||
if [ -v FIRMWARE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {FIRMWARE_LOCK}<&-
|
||||
unset FIRMWARE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[firmware_unlock_exclusive]"
|
||||
}
|
||||
TRAP_EXIT_FN+=([firmware_unlock_exclusive]=firmware_unlock_exclusive)
|
||||
|
||||
flock --exclusive --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# firmware_unlock_shared() - call to unlock when finished with critical section
|
||||
#
|
||||
# XXX: `firmware_lock_*` can't be taken recursively
|
||||
function firmware_lock_shared() {
|
||||
declare -g FIRMWARE_LOCK=""
|
||||
|
||||
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||
# whether this is a normal file or directory). This file descriptor
|
||||
# will only be used for locking - not for actual I/O.
|
||||
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf firmware_unlock_shared 1>/dev/null
|
||||
function firmware_unlock_shared() {
|
||||
if [ -v FIRMWARE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {FIRMWARE_LOCK}<&-
|
||||
unset FIRMWARE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[firmware_unlock_shared]"
|
||||
}
|
||||
TRAP_EXIT_FN+=([firmware_unlock_shared]=firmware_unlock_shared)
|
||||
|
||||
flock --shared --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# IPL_TYPE
|
||||
# IPL_BUSID
|
||||
# IPL_WWPN
|
||||
# IPL_LUN
|
||||
function firmware_get_ipl_information() {
|
||||
declare -g IPL_TYPE="" IPL_BUSID="" IPL_WWPN="" IPL_LUN=""
|
||||
|
||||
# Take lock so we don't see any intermediate state from other helpers
|
||||
# running in parallel
|
||||
firmware_lock_shared || return 5
|
||||
|
||||
{ read -r IPL_TYPE _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null \
|
||||
|| return 1
|
||||
{ read -r IPL_BUSID _ < /sys/firmware/reipl/fcp/device; } 2>/dev/null \
|
||||
|| return 2
|
||||
{ read -r IPL_WWPN _ < /sys/firmware/reipl/fcp/wwpn; } 2>/dev/null \
|
||||
|| return 3
|
||||
{ read -r IPL_LUN _ < /sys/firmware/reipl/fcp/lun; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
firmware_unlock_shared
|
||||
|
||||
# show read values in debug log if enabled
|
||||
if '@DEBUG@'; then
|
||||
declare -p IPL_TYPE IPL_BUSID IPL_WWPN IPL_LUN 1>&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: absolute canonical path to the scsi device in sysfs, e.g.:
|
||||
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||
# Output variables:
|
||||
# SDEV_LUN
|
||||
function sdev_get_lun() {
|
||||
local sdev="${1}" sdev_lun_str
|
||||
# bash uses `intmax_t` as width for integer variables, and glibc
|
||||
# defines this either as `long int` on 64 bit systems, or
|
||||
# `long long int` on other.
|
||||
local -i sdev_lun=0 fcp_lun=0
|
||||
|
||||
sdev_lun_str="${sdev##*:}"
|
||||
# e.g.: 1075789848
|
||||
[[ "${sdev_lun_str}" == +([[:digit:]]) ]] || return 1
|
||||
# "cast" to integer
|
||||
sdev_lun="${sdev_lun_str}"
|
||||
|
||||
# convert the Linux integer LUN format to the hexadecimal 64 bit T10
|
||||
# LUN representation format used by many s390x interfaces
|
||||
(( fcp_lun = (((sdev_lun >> 0) & 0xffff) << 48)
|
||||
| (((sdev_lun >> 16) & 0xffff) << 32)
|
||||
| (((sdev_lun >> 32) & 0xffff) << 16)
|
||||
| (((sdev_lun >> 48) & 0xffff) << 0) ))
|
||||
# the '0x' prefix is part of the length
|
||||
printf -v SDEV_LUN "%#018llx" "${fcp_lun}"
|
||||
|
||||
# show read values in debug log if enabled
|
||||
if '@DEBUG@'; then
|
||||
declare -p SDEV_LUN 1>&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs, e.g.:
|
||||
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||
# , or a symlink pointing to the scsi device, e.g.:
|
||||
# /sys/class/block/sds/device
|
||||
# Output variables:
|
||||
# SDEV_BUSID
|
||||
# SDEV_WWPN
|
||||
# SDEV_LUN
|
||||
function sdev_get_fcp_addressing() {
|
||||
local sdev="${1}" fcp_lun rport rport_wwpn zfcp_dev
|
||||
|
||||
declare -g SDEV_BUSID="" SDEV_WWPN="" SDEV_LUN=""
|
||||
|
||||
sdev="$(readlink -se "${sdev}")" || return 1
|
||||
|
||||
# get the LUN for this SDEV
|
||||
#
|
||||
# sets ${SDEV_LUN}
|
||||
sdev_get_lun "${sdev}" || return 2
|
||||
|
||||
# get the WWPN of the remote port this SDEV is attached to
|
||||
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||
[ "${rport}" != "" ] || return 3
|
||||
|
||||
# XXX: This works even if the rport is currently in a bad
|
||||
# state, so e.g. when it has just gone down because of a
|
||||
# cable pull.
|
||||
{ read -r rport_wwpn _ < "${rport}"/port_name; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
# The Linux kernel doesn't guarantee the same format as in
|
||||
# /sys/firmware/..., so make sure it is the one we expect.
|
||||
[[ "${rport_wwpn}" =~ ^0x[[:xdigit:]]{1,16}$ ]] || return 5
|
||||
# the '0x' prefix is part of the length
|
||||
printf -v rport_wwpn "%#018llx" "${rport_wwpn}"
|
||||
|
||||
# get the Device Bus-ID of the device via which this SDEV is attached
|
||||
zfcp_dev="$(readlink -se "${sdev}"/../../../..)" || return 6
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740
|
||||
zfcp_dev="${zfcp_dev##*/}"
|
||||
|
||||
# shellcheck disable=2034
|
||||
SDEV_BUSID="${zfcp_dev}"
|
||||
# shellcheck disable=2034
|
||||
SDEV_WWPN="${rport_wwpn}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs
|
||||
# Output variables:
|
||||
# SDEV_WWID
|
||||
function sdev_get_wwid() {
|
||||
local sdev="${1}"
|
||||
local -a wwid
|
||||
|
||||
declare -g SDEV_WWID=""
|
||||
|
||||
# read the volume identifier without stripping any content
|
||||
#
|
||||
# XXX: we can read the WWID file, even if the SDEV is currently not
|
||||
# operational (e.g.: due to the path has gone away), as long as
|
||||
# the VPD PG 83 is still cached in the kernel; and the page gets
|
||||
# only released on SDEV device release.
|
||||
{ readarray -d "" -t wwid < "${sdev}"/wwid; } 2>/dev/null \
|
||||
|| return 1
|
||||
if '@DEBUG@'; then declare -p wwid 1>&2; fi
|
||||
|
||||
# test whether we read something
|
||||
#
|
||||
# This strips all leading spaces from the beginning of the read WWID
|
||||
# (until the first non-space or NUL character), and checks whether the
|
||||
# result is empty.
|
||||
# Hence, we return early if the WWID consists of only whitespace.
|
||||
#
|
||||
# XXX: there could be unexpected characters in the returned ID.
|
||||
# `scsi_id` from the udev helpers sanitizes the strings it reads
|
||||
# from the devices, so they can be used in environment variables
|
||||
# without much danger.
|
||||
# But we don't export anything here, so it should be fine.
|
||||
[ "${wwid[0]/#*([[:space:]])}" != "" ] || return 2
|
||||
|
||||
# shellcheck disable=2034
|
||||
SDEV_WWID="${wwid[0]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs
|
||||
# Return Value:
|
||||
# == 0: SDEV referenced by `1` in good state
|
||||
# != 0: otherwise
|
||||
function sdev_test_path_state() {
|
||||
local sdev="${1}" state zfcp_failed zfcp_in_recovery rport port_state
|
||||
|
||||
sdev="$(readlink -se "${sdev}")" || return 1
|
||||
|
||||
{ read -r state _ < "${sdev}"/state; } 2>/dev/null || return 2
|
||||
{ read -r zfcp_failed _ < "${sdev}"/zfcp_failed; } 2>/dev/null \
|
||||
|| return 3
|
||||
{ read -r zfcp_in_recovery _ < "${sdev}"/zfcp_in_recovery; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||
[ "${rport}" != "" ] || return 5
|
||||
{ read -r port_state _ < "${rport}"/port_state; } 2>/dev/null \
|
||||
|| return 6
|
||||
|
||||
if '@DEBUG@'; then
|
||||
declare -p state zfcp_failed zfcp_in_recovery port_state 1>&2
|
||||
fi
|
||||
|
||||
[ "${state}" = "running" ] || return 7
|
||||
[ "${zfcp_failed}" = "0" ] || return 8
|
||||
[ "${zfcp_in_recovery}" = "0" ] || return 9
|
||||
{ [ "${port_state}" = "Online" ] \
|
||||
|| [ "${port_state}" = "Marginal" ]; } || return 10
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_note() {
|
||||
logger -p 'daemon.notice' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_crit() {
|
||||
logger -p 'daemon.crit' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_alert() {
|
||||
logger -p 'daemon.alert' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
56
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-tgt.in
Normal file
56
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-tgt.in
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether the device in udev event environment variable ${DEVPATH}
|
||||
# represents the device we want to re-IPL from. We do this by comparing
|
||||
# Device-Bus-ID/Target-WWPN/LUN of the individual SDEVs to the parameters set
|
||||
# in `/sys/firmware/reipl/fcp/`.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=05iilt
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
firmware_get_ipl_information || exit 1
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev
|
||||
|
||||
# depends on `nullglob` from `chreipl-fcp-mpath-common.sh`
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
sdev_get_fcp_addressing "${sdev}" || continue
|
||||
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || continue
|
||||
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || continue
|
||||
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || continue
|
||||
|
||||
exit 0
|
||||
done
|
||||
unset sdev
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_fcp_addressing /sys/"${DEVPATH}"/device || exit 2
|
||||
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || exit 3
|
||||
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || exit 4
|
||||
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || exit 5
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 6
|
||||
86
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-vol.in
Normal file
86
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-vol.in
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether the device in environment variable ${DEVPATH} represents the
|
||||
# _volume_ that we IPL'ed from. We do this by comparing its WWID to the one
|
||||
# recorded in `@chreiplzfcpmp-id-file@`.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=11iilv
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function id_file_read_ipl_information() {
|
||||
local -a records
|
||||
|
||||
declare -g REC_WWID="" REC_BUSID="" REC_WWPN="" REC_LUN=""
|
||||
|
||||
# lock file before reading ID, so we don't see any intermediate state
|
||||
id_file_lock_shared_no_create || return 1
|
||||
|
||||
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||
|| return 2
|
||||
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||
|
||||
id_file_unlock_shared_no_create
|
||||
|
||||
[ "${#records[@]}" = "4" ] || return 3
|
||||
# check that none of the array fields contains whitespace only
|
||||
[ "${records[0]/#*([[:space:]])}" != "" ] || return 4
|
||||
[ "${records[1]/#*([[:space:]])}" != "" ] || return 5
|
||||
[ "${records[2]/#*([[:space:]])}" != "" ] || return 6
|
||||
[ "${records[3]/#*([[:space:]])}" != "" ] || return 7
|
||||
|
||||
REC_WWID="${records[0]}"
|
||||
REC_BUSID="${records[1]}"
|
||||
REC_WWPN="${records[2]}"
|
||||
REC_LUN="${records[3]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
id_file_read_ipl_information || exit 1
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev found=false
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_get_wwid "${sdev}"; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
unset sdev
|
||||
|
||||
"${found}" || exit 2
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_wwid /sys/"${DEVPATH}"/device || exit 3
|
||||
fi
|
||||
|
||||
# set by `sdev_get_wwid` and `id_file_read_ipl_information`
|
||||
[ "${SDEV_WWID}" = "${REC_WWID}" ] || exit 4
|
||||
|
||||
firmware_get_ipl_information || exit 5
|
||||
# set by `firmware_get_ipl_information` and `id_file_read_ipl_information`
|
||||
[ "${IPL_BUSID}" = "${REC_BUSID}" ] || exit 6
|
||||
[ "${IPL_WWPN}" = "${REC_WWPN}" ] || exit 7
|
||||
[ "${IPL_LUN}" = "${REC_LUN}" ] || exit 8
|
||||
|
||||
exit 0
|
||||
25
chreipl-fcp-mpath/chreipl-fcp-mpath-is-reipl-zfcp.in
Normal file
25
chreipl-fcp-mpath/chreipl-fcp-mpath-is-reipl-zfcp.in
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether ReIPL is gonna happen from a SCSI volume attached via zFCP
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=00iriz
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
declare reipl_type
|
||||
|
||||
{ read -r reipl_type _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null || exit 1
|
||||
if '@DEBUG@'; then declare -p reipl_type 1>&2; fi
|
||||
|
||||
[ "${reipl_type}" = "fcp" ] || exit 2
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
# GNU coreutils:
|
||||
# - truncate
|
||||
# util-linux:
|
||||
# - hexdump
|
||||
|
||||
# Record the identification of the volume we want to re-IPL from
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=10rvid
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function id_file_record_ipl_information() {
|
||||
local sdev_wwid="${1}" ipl_busid="${2}" ipl_wwpn="${3}" ipl_lun="${4}"
|
||||
|
||||
# lock file before writing ID, so noone sees any intermediate state
|
||||
id_file_lock_exclusive_create || return 1
|
||||
|
||||
# reset ID without removing the file (necessary for the locking to work
|
||||
# properly, since the FD we use for locking is on this file/inode)
|
||||
truncate --no-create --size=0 "${ID_FILE}" || return 3
|
||||
echo -ne "${sdev_wwid}\x00${ipl_busid}\x00${ipl_wwpn}\x00${ipl_lun}\x00" \
|
||||
>>"${ID_FILE}" || return 4
|
||||
|
||||
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||
|
||||
id_file_unlock_exclusive_create
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_get_wwid "${sdev}"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
unset sdev
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_wwid /sys/"${DEVPATH}"/device
|
||||
fi
|
||||
# shellcheck disable=SC2153
|
||||
[ "${SDEV_WWID}" != "" ] || exit 1
|
||||
|
||||
firmware_get_ipl_information || exit 2
|
||||
# shellcheck disable=SC2153
|
||||
id_file_record_ipl_information \
|
||||
"${SDEV_WWID}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" \
|
||||
|| exit 3
|
||||
|
||||
exit 0
|
||||
158
chreipl-fcp-mpath/chreipl-fcp-mpath-try-change-ipl-path.in
Normal file
158
chreipl-fcp-mpath/chreipl-fcp-mpath-try-change-ipl-path.in
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
# GNU coreutils:
|
||||
# - truncate
|
||||
# util-linux:
|
||||
# - hexdump
|
||||
|
||||
# Try to change the current re-IPL target to a dfferent, operational path to
|
||||
# the same volume.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
# CHREIPL_FCP_MPATH_IS_TGT
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=15tcip
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function apply_ipl_information() {
|
||||
local sdev_wwid="${1}"
|
||||
local sdev_busid="${2}" sdev_wwpn="${3}" sdev_lun="${4}"
|
||||
local ipl_type="${5}" ipl_busid="${6}" ipl_wwpn="${7}" ipl_lun="${8}"
|
||||
local -a records
|
||||
local try_update_id_file=true
|
||||
|
||||
[ "${ipl_type}" = "fcp" ] || return 1
|
||||
|
||||
[[ "${sdev_busid}" =~ ^[[:xdigit:]]{1,3}\.[[:xdigit:]]\.[[:xdigit:]]{1,4}$ ]] \
|
||||
|| return 2
|
||||
[[ "${sdev_wwpn}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 3
|
||||
[[ "${sdev_lun}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 4
|
||||
|
||||
# After updating the firmware re-IPL information below we also try to
|
||||
# update the information stored in the ID file (necessary, so it
|
||||
# contains the correct Device-Bus-ID/WWPN/LUN after the update). For
|
||||
# the update of the ID file we try to grab an exclusive lock, so there
|
||||
# are no overlapping reads/writes.
|
||||
#
|
||||
# In case we can't get the lock because the ID file is missing, but we
|
||||
# have a direct TGT match, we may still try to change the re-IPL
|
||||
# information, but skip the ID file update.
|
||||
#
|
||||
# "direct match" means, the event subject is either the SDEV that is
|
||||
# currently set as re-IPL target, or it is the dm-multipath device that
|
||||
# currently contains the re-IPL target.
|
||||
if ! id_file_lock_exclusive_no_create; then
|
||||
# rc == 1 --> could not read ${ID_FILE}
|
||||
[ "${PIPESTATUS[0]}" -eq 1 ] || return 5
|
||||
# if true, we are dealing with a direct TGT match
|
||||
[ "${CHREIPL_FCP_MPATH_IS_TGT}" = "true" ] || return 6
|
||||
try_update_id_file=false
|
||||
fi
|
||||
|
||||
# If we have a direct match (see in the comment above), we know
|
||||
# that we have a path to the current re-IPL volume - no matter of the
|
||||
# WWID. Otherwise, we got here by comparing the WWID of the event
|
||||
# subject with the one recorded in the ID file; in this case we try to
|
||||
# make sure the information is still up-to-date.
|
||||
if [ "${CHREIPL_FCP_MPATH_IS_TGT}" != "true" ]; then
|
||||
# last bail to make sure we don't overwrite user choices..
|
||||
#
|
||||
# XXX: this will *NOT* prevent the race completely, but at least
|
||||
# make it less likely
|
||||
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||
|| return 7
|
||||
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||
|
||||
[ "${#records[@]}" = "4" ] || return 8
|
||||
[ "${records[0]}" = "${sdev_wwid}" ] || return 9
|
||||
[ "${records[1]}" = "${ipl_busid}" ] || return 10
|
||||
[ "${records[2]}" = "${ipl_wwpn}" ] || return 11
|
||||
[ "${records[3]}" = "${ipl_lun}" ] || return 12
|
||||
fi
|
||||
|
||||
# Take lock so we don't see any intermediate state from other helpers
|
||||
# running in parallel
|
||||
firmware_lock_exclusive || return 13
|
||||
|
||||
if ! { echo "${sdev_busid}" >| /sys/firmware/reipl/fcp/device \
|
||||
&& echo "${sdev_wwpn}" >| /sys/firmware/reipl/fcp/wwpn \
|
||||
&& echo "${sdev_lun}" >| /sys/firmware/reipl/fcp/lun; };
|
||||
then
|
||||
log_alert "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."
|
||||
return 14
|
||||
fi
|
||||
|
||||
firmware_unlock_exclusive
|
||||
|
||||
if [ "${sdev_busid}" != "${ipl_busid}" ] \
|
||||
|| [ "${sdev_wwpn}" != "${ipl_wwpn}" ] \
|
||||
|| [ "${sdev_lun}" != "${ipl_lun}" ]; then
|
||||
log_note "Changed re-IPL path to: ${sdev_busid}:${sdev_wwpn}:${sdev_lun}."
|
||||
fi
|
||||
|
||||
# Try to update the information in the ID file if we have gotten the
|
||||
# lock for it.
|
||||
if ${try_update_id_file}; then
|
||||
# reset ID without removing the file
|
||||
truncate --no-create --size=0 "${ID_FILE}" || return 15
|
||||
echo -ne "${sdev_wwid}\x00${sdev_busid}\x00${sdev_wwpn}\x00${sdev_lun}\x00" \
|
||||
>>"${ID_FILE}" || return 16
|
||||
|
||||
id_file_unlock_exclusive_no_create
|
||||
|
||||
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
declare -g SDEV=""
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_test_path_state "${sdev}"; then
|
||||
SDEV="${sdev}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# No path of the multipath-device that represents the IPL volume is
|
||||
# online.
|
||||
if [ "${SDEV}" = "" ]; then
|
||||
log_crit "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."
|
||||
fi
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
if sdev_test_path_state /sys/"${DEVPATH}"/device; then
|
||||
SDEV=/sys/"${DEVPATH}"/device
|
||||
fi
|
||||
|
||||
fi
|
||||
[ "${SDEV}" != "" ] || exit 0
|
||||
|
||||
sdev_get_wwid "${SDEV}" || exit 0
|
||||
sdev_get_fcp_addressing "${SDEV}" || exit 0
|
||||
firmware_get_ipl_information || exit 0
|
||||
|
||||
# shellcheck disable=SC2153
|
||||
apply_ipl_information \
|
||||
"${SDEV_WWID}" "${SDEV_BUSID}" "${SDEV_WWPN}" "${SDEV_LUN}" \
|
||||
"${IPL_TYPE}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" || exit 0
|
||||
|
||||
exit 0
|
||||
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)
|
||||
93
chreipl-fcp-mpath/chreipl-fcp-mpath.mak
Normal file
93
chreipl-fcp-mpath/chreipl-fcp-mpath.mak
Normal file
@@ -0,0 +1,93 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Utilities list in GNU Make Conventions:
|
||||
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||
# GNU coreutils:
|
||||
# - mktemp
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# Pandoc:
|
||||
# - pandoc
|
||||
# GNU coreutils:
|
||||
# - date
|
||||
|
||||
#
|
||||
## Paths and Build Variables
|
||||
#
|
||||
|
||||
# Install the configuration file for dracut, to automatically pull in the
|
||||
# toolset into the initial ramdisk, when built with it.
|
||||
HAVE_DRACUT = 0
|
||||
|
||||
# Build documentation; requires: Pandoc
|
||||
ENABLE_DOC = 0
|
||||
|
||||
# https://www.gnu.org/software/make/manual/make.html#Directory-Variables
|
||||
CHREIPLZFCPMPDIR = $(USRLIBDIR)/chreipl-fcp-mpath
|
||||
UDEVDIR = $(USRLIBDIR)/udev
|
||||
UDEVRULESDIR = $(UDEVDIR)/rules.d
|
||||
UDEVRUNDIR = /run/udev
|
||||
DRACUTDIR = $(USRLIBDIR)/dracut
|
||||
DRACUTCONFDIR = $(DRACUTDIR)/dracut.conf.d
|
||||
DEBUGOUTDIR = $(UDEVRUNDIR)
|
||||
|
||||
INSTALL_EXEC = $(INSTALL) -g $(GROUP) -o $(OWNER) --preserve-timestamps
|
||||
INSTALL_DATA = $(INSTALL_EXEC) --mode=0644
|
||||
|
||||
# used for data exchange and synchronization across the different helpers
|
||||
chreiplzfcpmp-id-file = $(UDEVRUNDIR)/chreiplzfcpmp-ipl-volume-id
|
||||
# file used to implement mutual exclusion when accessing firmware IPL info:
|
||||
# - this should be something that is (practically) always available, so we
|
||||
# dont have to worry about fallbacks or error-handling;
|
||||
# - at the same time, it should not be used by anything else with flock(2) to
|
||||
# hold a lock for long periods.
|
||||
chreiplzfcpmp-fwlock-file = /sys/firmware/reipl
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
# export build-time definitions to the scripts/built-components
|
||||
define chreiplzfcpmp-sed-buildvar-replace =
|
||||
tmpout=$$(mktemp -p ./ .make.tmp.XXXXXXXXXXXXXXXX) && { \
|
||||
$(SED) -E \
|
||||
-e 's|@DEBUG@|$(if $(filter 1,$(D)),true,false)|g' \
|
||||
-e 's|@chreiplzfcpmp-id-file@|$(chreiplzfcpmp-id-file)|g' \
|
||||
-e 's|@chreiplzfcpmp-fwlock-file@|$(chreiplzfcpmp-fwlock-file)|g' \
|
||||
-e 's|@chreiplzfcpmp-lib@|$(CHREIPLZFCPMPDIR)/chreipl-fcp-mpath-common.sh|g' \
|
||||
-e 's|@debugoutdir@|$(DEBUGOUTDIR)|g' \
|
||||
-e 's|@udevdir@|$(UDEVDIR)|g' \
|
||||
-e 's|@udevrulesdir@|$(UDEVRULESDIR)|g' \
|
||||
$(1) > $${tmpout} \
|
||||
&& mv $${tmpout} $(2) \
|
||||
|| { rm $${tmpout}; false; } \
|
||||
; }
|
||||
endef
|
||||
|
||||
.PHONY: clean-mk-temp
|
||||
clean: clean-mk-temp
|
||||
clean-mk-temp:
|
||||
rm -f .make.tmp.[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||
|
||||
# Definitions for generating documentation when $(ENABLE_DOC) is set to `1`
|
||||
|
||||
PANDOCFLAGS = --fail-if-warnings
|
||||
ALL_PANDOCFLAGS = --preserve-tabs --tab-stop=8 --strip-comments \
|
||||
--standalone --self-contained \
|
||||
-M date="$(shell date +'%Y-%m-%d')" \
|
||||
$(PANDOCFLAGS)
|
||||
|
||||
$(eval $(call cmd_define, PANDOC," PANDOC ",pandoc))
|
||||
|
||||
%.html : ALL_PANDOCFLAGS += -t html
|
||||
%.html : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
|
||||
%.pdf : ALL_PANDOCFLAGS += -t latex --toc
|
||||
%.pdf : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
|
||||
%.7 : ALL_PANDOCFLAGS += -t man
|
||||
%.7 : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
@@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
|
||||
add_dracutmodules+=" bash multipath udev-rules "
|
||||
|
||||
install_items+=" @udevrulesdir@/70-chreipl-fcp-mpath.rules "
|
||||
install_items+=" @chreiplzfcpmp-lib@ "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-tgt "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-vol "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-reipl-zfcp "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-record-volume-identifier "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-try-change-ipl-path "
|
||||
|
||||
install_items+=" flock hexdump logger mktemp readlink sync truncate "
|
||||
101
chreipl-fcp-mpath/udev/rules.d/70-chreipl-fcp-mpath.rules
Normal file
101
chreipl-fcp-mpath/udev/rules.d/70-chreipl-fcp-mpath.rules
Normal file
@@ -0,0 +1,101 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
|
||||
# Did the event affect a multipath or scsi disk device?
|
||||
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_FAILED", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_REINSTATED", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
ACTION=="add", KERNEL=="sd[a-z]*", SUBSYSTEM=="block", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
GOTO="chreipl_fcp_mpath_end"
|
||||
LABEL="chreipl_fcp_mpath_path_change"
|
||||
|
||||
# Is this system IPL'ed (IOW, are we on s390x)? And do we ReIPL via zFCP?
|
||||
#
|
||||
# udev(7): If no absolute path is given, the program is expected to live
|
||||
# in /usr/lib/udev; otherwise, the absolute path must be
|
||||
# specified.
|
||||
TEST!="/sys/firmware/ipl", GOTO="chreipl_fcp_mpath_end"
|
||||
PROGRAM!="chreipl-fcp-mpath-is-reipl-zfcp", GOTO="chreipl_fcp_mpath_end"
|
||||
|
||||
# Consider the following scenarios.
|
||||
# Either:
|
||||
#
|
||||
# (A) We recognized a new SCSI Disk. This might represent:
|
||||
# (a) the path we want to ReIPL from;
|
||||
# (b) an alternative path to the volume we want to ReIPL from;
|
||||
# (c) a path to some unrelated volume.
|
||||
#
|
||||
# Or:
|
||||
#
|
||||
# (B) We recognized a PATH_ event for a multipath device. This might represent:
|
||||
# the path we want to ReIPL from:
|
||||
# (a) went away;
|
||||
# (b) came back online;
|
||||
# an alternative path to the volume we want to ReIPL from:
|
||||
# (c) went away;
|
||||
# (d) came back online;
|
||||
# (e) some unrelated multipath device saw an event.
|
||||
|
||||
# Test whether the affected device is, or contains, the current IPL target.
|
||||
#
|
||||
# This covers scenarios:
|
||||
# (A) (a),
|
||||
# (B) (a)/(b)/(c)/(d)
|
||||
PROGRAM!="chreipl-fcp-mpath-is-ipl-tgt", \
|
||||
ENV{CHREIPL_FCP_MPATH_IS_TGT}="false", \
|
||||
GOTO="chreipl_fcp_mpath_not_direct_match"
|
||||
ENV{CHREIPL_FCP_MPATH_IS_TGT}="true"
|
||||
|
||||
# Record the WWID, Device-Bus-ID, Remote WWPN, and LUN of the ReIPL target
|
||||
# (see `chreipl-fcp-mpath-is-ipl-vol` for usecases). This information
|
||||
# might change, depending on whether the machine operator changes the ReIPL
|
||||
# target to a different volume.
|
||||
#
|
||||
# XXX: Because the kernel doesn't generate any events upon changing of
|
||||
# the ReIPL target, the chreipl-fcp-mpath toolset can't take any
|
||||
# actions until the next path event for the new target is
|
||||
# generated. Following that, we assume that when the machine
|
||||
# operator changes the ReIPL target, the new target is reachable
|
||||
# and in a good state at this point in time.
|
||||
PROGRAM!="chreipl-fcp-mpath-record-volume-identifier", \
|
||||
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
|
||||
# If the even subject is not a direct match (not the sdev that is the current
|
||||
# ReIPL target, and not a mpath device that contains the current ReIPL target)
|
||||
LABEL="chreipl_fcp_mpath_not_direct_match"
|
||||
|
||||
# While this sdev/mpath device doesn't directly correspond to the path
|
||||
# currently set as ReIPL target, it might still point to the same volume.
|
||||
#
|
||||
# For mpath devices this can happen if the original ReIPL target is completely
|
||||
# gone from the machine, and so there is no way we can successfully, directly
|
||||
# compare the ReIPL parameters to the sdevs of the mpath device.
|
||||
#
|
||||
# For cases like these we recorded the volume identifier, which we now can
|
||||
# compare, and so still decide whether we are addressing the correct volume.
|
||||
#
|
||||
# This covers scenarios:
|
||||
# (A) (b)/(c),
|
||||
# (B) (c)/(d)/(e)
|
||||
#
|
||||
# XXX: we recorded WWID, Device-Bus-ID, Remote WWPN, LUN of the ReIPL target at
|
||||
# the time; if the latter three don't match the current ReIPL setting
|
||||
# anymore, we have to assume that someone changed the ReIPL target
|
||||
# manually, and we cannot use the WWID anymore since we can't possibly
|
||||
# know whether that stayed the same when the change was done.
|
||||
PROGRAM!="chreipl-fcp-mpath-is-ipl-vol", GOTO="chreipl_fcp_mpath_end"
|
||||
|
||||
# We are here because of scenarios:
|
||||
# (A) (a)/(b),
|
||||
# (B) (a)/(b)/(c)/(d)
|
||||
LABEL="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
RUN{program}+="chreipl-fcp-mpath-try-change-ipl-path"
|
||||
|
||||
LABEL="chreipl_fcp_mpath_end"
|
||||
@@ -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;
|
||||
|
||||
|
||||
47
common.mak
47
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 = 16
|
||||
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
|
||||
@@ -111,6 +109,20 @@ ifeq ("${ASAN}","1")
|
||||
DEFAULT_LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
|
||||
#
|
||||
# Check for header prerequisite
|
||||
#
|
||||
# $1: Name of include file to check
|
||||
# $2: Additional compiler & linker options (optional)
|
||||
#
|
||||
# Returns "yes" on success and nothing otherwise
|
||||
#
|
||||
define check_header_prereq
|
||||
$(shell printf "#include <%s>\n int main(void) {return 0;}" $1 | \
|
||||
( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) \
|
||||
$2 -o /dev/null -xc - ) >/dev/null 2>&1 && echo -n yes)
|
||||
endef
|
||||
|
||||
#
|
||||
# Check for build dependency
|
||||
#
|
||||
@@ -168,6 +180,7 @@ USRSBINDIR = $(INSTALLDIR)/usr/sbin
|
||||
USRBINDIR = $(INSTALLDIR)/usr/bin
|
||||
BINDIR = $(INSTALLDIR)/sbin
|
||||
LIBDIR = $(INSTALLDIR)/lib
|
||||
USRLIBDIR = $(INSTALLDIR)/usr/lib
|
||||
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
||||
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||
MANDIR = $(INSTALLDIR)/usr/share/man
|
||||
@@ -192,10 +205,10 @@ INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
||||
$(SOINSTALLDIR)
|
||||
$(SOINSTALLDIR) $(USRLIBDIR)
|
||||
OWNER = $(shell id -un)
|
||||
GROUP = $(shell id -gn)
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
|
||||
# Special defines for zfcpdump
|
||||
ZFCPDUMP_IMAGE = zfcpdump-image
|
||||
@@ -237,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
|
||||
|
||||
@@ -356,10 +367,22 @@ $(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
|
||||
|
||||
$(rootdir)/libseckey/libseckey.a: $(rootdir)/libseckey
|
||||
$(MAKE) -C $(rootdir)/libseckey/ libseckey.a
|
||||
.PHONY: $(rootdir)/libseckey
|
||||
|
||||
$(rootdir)/libkmipclient/libkmipclient.so: $(rootdir)/libkmipclient
|
||||
$(MAKE) -C $(rootdir)/libkmipclient/ libkmipclient.so
|
||||
.PHONY: $(rootdir)/libkmipclient
|
||||
|
||||
$(rootdir)/zipl/boot/data.o:
|
||||
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
include ../common.mak
|
||||
|
||||
BIN_FILES = lscpumf chcpumf
|
||||
MAN_FILES = lscpumf.1 chcpumf.8
|
||||
BIN_FILES = lscpumf chcpumf lshwc
|
||||
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)
|
||||
lshwc: lshwc.o $(libs)
|
||||
|
||||
install: all install-man
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
112
cpumf/chcpumf.c
112
cpumf/chcpumf.c
@@ -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,62 +85,31 @@ static long parse_buffersize(char *string)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int read_sfb(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
unsigned long cur_min_sdb, cur_max_sdb;
|
||||
int rc = EXIT_SUCCESS;
|
||||
FILE *fp;
|
||||
|
||||
if (geteuid()) {
|
||||
fprintf(stderr, "Error: Must run as root\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) {
|
||||
fprintf(stderr, "Error: Can not parse file " PERF_SFB_SIZE
|
||||
"\n");
|
||||
rc = EXIT_FAILURE;
|
||||
} else {
|
||||
if (*min == 0)
|
||||
*min = cur_min_sdb;
|
||||
if (*max == 0)
|
||||
*max = cur_max_sdb;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "w");
|
||||
if (!fp)
|
||||
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) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (fclose(fp)) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (verbose && rc != EXIT_FAILURE)
|
||||
fprintf(stderr, "Sampling buffer sizes:\n"
|
||||
" Minimum:%7ld sample-data-blocks\n"
|
||||
" Maximum:%7ld sample-data-blocks\n",
|
||||
min, max);
|
||||
warnx("Sampling buffer sizes:\n"
|
||||
" Minimum:%7d sample-data-blocks\n"
|
||||
" Maximum:%7d sample-data-blocks\n",
|
||||
min, max);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -160,61 +129,50 @@ static int parse_args(int argc, char **argv)
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'x':
|
||||
new = parse_buffersize(optarg);
|
||||
if (new < 1) {
|
||||
fprintf(stderr, "The specified number(s)"
|
||||
" are not valid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (new < 1)
|
||||
errx(EXIT_FAILURE,
|
||||
"The specified number(s) are not valid");
|
||||
max_sdb = new;
|
||||
action = 1;
|
||||
break;
|
||||
case 'm':
|
||||
new = parse_buffersize(optarg);
|
||||
if (new < 1) {
|
||||
fprintf(stderr, "The specified number(s)"
|
||||
" are not valid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (new < 1)
|
||||
errx(EXIT_FAILURE,
|
||||
"The specified number(s) are not valid");
|
||||
min_sdb = new;
|
||||
action = 1;
|
||||
break;
|
||||
case 'V':
|
||||
verbose = 1;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "One or more options are not valid\n");
|
||||
fprintf(stderr, "Try 'chcpumf --help' for more"
|
||||
" information\n");
|
||||
default:
|
||||
util_opt_print_parse_error(opt, argv);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (!action) {
|
||||
fprintf(stderr, "You must specify a valid option\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!action)
|
||||
errx(EXIT_FAILURE, "You must specify a valid option");
|
||||
return action;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
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) != 0) {
|
||||
fprintf(stderr,
|
||||
"No CPU-measurement sampling facility detected\n");
|
||||
return ret;
|
||||
}
|
||||
if (read_sfb(&min_sdb, &max_sdb))
|
||||
return ret;
|
||||
if (min_sdb >= max_sdb) {
|
||||
fprintf(stderr, "The specified maximum must be greater "
|
||||
"than the minimum\n");
|
||||
return ret;
|
||||
}
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
if (!libcpumf_have_sfb())
|
||||
errx(EXIT_FAILURE,
|
||||
"No CPU-measurement sampling facility detected");
|
||||
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,23 +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"
|
||||
|
||||
static inline void linux_error(const char *message)
|
||||
{
|
||||
fprintf(stderr, "Error: %s: %s\n", message, strerror(errno));
|
||||
}
|
||||
|
||||
#endif
|
||||
295
cpumf/lscpumf.c
295
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.
|
||||
@@ -23,11 +23,12 @@
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "lib/util_arch.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define ACTION_NONE 0
|
||||
#define ACTION_INFO 1
|
||||
@@ -44,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"),
|
||||
@@ -91,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;
|
||||
|
||||
@@ -2558,52 +2555,18 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const char *machine_name(void)
|
||||
{
|
||||
switch (cpumf.machine_type) {
|
||||
case 2097: return "IBM System z10 EC";
|
||||
case 2098: return "IBM System z10 BC";
|
||||
case 2817: return "IBM zEnterprise 196";
|
||||
case 2818: return "IBM zEnterprise 114";
|
||||
case 2827: return "IBM zEnterprise EC12";
|
||||
case 2828: return "IBM zEnterprise BC12";
|
||||
case 2964: return "IBM z13";
|
||||
case 2965: return "IBM z13s";
|
||||
case 3906: return "IBM z14";
|
||||
case 3907: return "IBM z14 ZR1";
|
||||
case 8561: return "IBM z15";
|
||||
case 8562: return "IBM z15 Model T02";
|
||||
}
|
||||
return "Unknown hardware model";
|
||||
}
|
||||
|
||||
/* Return the type number of the CPU Measurement facility from the sysfs file.
|
||||
* If the type number is equal to PERF_TYPE_RAW, then the prefix is 'r' to
|
||||
* specify the raw counter number by the perf tool.
|
||||
* 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) {
|
||||
fprintf(stderr, "No CPU-measurement %s facility detected\n",
|
||||
type);
|
||||
return rc;
|
||||
}
|
||||
if (fscanf(fp, "%d", &nr) != 1)
|
||||
fprintf(stderr, "Can not parse file %s\n", 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
|
||||
@@ -2639,10 +2602,8 @@ static int parse_args(int argc, char **argv)
|
||||
case 'C':
|
||||
actions[ACTION_CNTALL] = true;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "One or more options are not valid\n");
|
||||
fprintf(stderr, "Try 'lscpumf --help' for more"
|
||||
" information.\n");
|
||||
default:
|
||||
util_opt_print_parse_error(opt, argv);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@@ -2682,10 +2643,8 @@ 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) {
|
||||
fprintf(stderr, "No CPU-measurement facilities detected\n");
|
||||
warnx("No CPU-measurement facilities detected");
|
||||
return;
|
||||
}
|
||||
if (p->have_counter) {
|
||||
@@ -2693,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");
|
||||
@@ -2710,13 +2669,11 @@ 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) != 0) ? "No" :
|
||||
"Yes (PMU: " PERF_CF ")");
|
||||
!p->have_counter ? "No" : "Yes (PMU: cpum_cf)");
|
||||
|
||||
}
|
||||
} else
|
||||
fprintf(stderr,
|
||||
"No CPU-measurement counter facility detected\n");
|
||||
warnx("No CPU-measurement counter facility detected");
|
||||
if (p->have_samples) {
|
||||
unsigned long total, fdiag;
|
||||
char text[32];
|
||||
@@ -2740,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) != 0) ? "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);
|
||||
|
||||
@@ -2761,74 +2717,20 @@ 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);
|
||||
printf(" Size factor: %2ld\n", fdiag);
|
||||
}
|
||||
} else
|
||||
fprintf(stderr,
|
||||
"No CPU-measurement sampling facility detected\n");
|
||||
}
|
||||
|
||||
/* Funktion to read machine type */
|
||||
#define SYSINFO "/proc/sysinfo"
|
||||
#define MACH_TYPE "Type:"
|
||||
|
||||
static int read_machine(unsigned short *mt)
|
||||
{
|
||||
int rc = EXIT_FAILURE;
|
||||
char *linep = NULL;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(SYSINFO, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(SYSINFO);
|
||||
return rc;
|
||||
}
|
||||
while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) {
|
||||
if (!strncmp(linep, MACH_TYPE, sizeof MACH_TYPE - 1)) {
|
||||
int rc_scan = sscanf(linep, MACH_TYPE "%hd", mt);
|
||||
if (rc_scan != 1)
|
||||
fprintf(stderr, "Can not parse line %s", linep);
|
||||
else
|
||||
rc = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(linep);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Read CPU Measurement sampling facility device driver minimum and maximum
|
||||
* buffer size
|
||||
*/
|
||||
static int read_sfb(struct cpumf_info *p)
|
||||
{
|
||||
FILE *fp;
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fscanf(fp, "%d,%d", &p->min_sfb, &p->max_sfb) != 2) {
|
||||
fprintf(stderr, "Can not parse %s\n", PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
warnx("No CPU-measurement sampling facility detected");
|
||||
}
|
||||
|
||||
/* Set the counter name for z15 counter numbered 265. It is either named
|
||||
@@ -2843,7 +2745,7 @@ static void read_ccerror(struct counters *cp, size_t cp_cnt)
|
||||
char *ctrname;
|
||||
size_t i = 0;
|
||||
|
||||
if (stat(CCERROR, &sbuf) == 0)
|
||||
if (!stat(CCERROR, &sbuf))
|
||||
ctrname = "DFLT_CCERROR";
|
||||
else
|
||||
ctrname = "DFLT_CCFINISH";
|
||||
@@ -2858,79 +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;
|
||||
|
||||
memset(&cpumf, 0, sizeof cpumf);
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (slp == NULL) {
|
||||
linux_error(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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "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;
|
||||
}
|
||||
rc = read_machine(&cpumf.machine_type);
|
||||
if (rc == EXIT_FAILURE)
|
||||
goto out;
|
||||
rc = EXIT_SUCCESS;
|
||||
out:
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
int rc = EXIT_FAILURE;
|
||||
|
||||
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)
|
||||
rc = EXIT_FAILURE;
|
||||
else
|
||||
rc = EXIT_SUCCESS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3013,33 +2859,33 @@ static struct counters *get_counter(int ctrset, size_t *len)
|
||||
break;
|
||||
case CPUMF_CTRSET_EXTENDED:
|
||||
switch (cpumf.machine_type) {
|
||||
case 2097:
|
||||
case 2098:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z10_EC:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z10_BC:
|
||||
cp = cpumcf_z10_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z10_counters);
|
||||
break;
|
||||
case 2817:
|
||||
case 2818:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_196:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_114:
|
||||
cp = cpumcf_z196_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z196_counters);
|
||||
break;
|
||||
case 2827:
|
||||
case 2828:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_EC12:
|
||||
case UTIL_ARCH_MACHINE_TYPE_ZE_BC12:
|
||||
cp = cpumcf_zec12_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_zec12_counters);
|
||||
break;
|
||||
case 2964:
|
||||
case 2965:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z13:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z13_S:
|
||||
cp = cpumcf_z13_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z13_counters);
|
||||
break;
|
||||
case 3906:
|
||||
case 3907:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z14:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1:
|
||||
cp = cpumcf_z14_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z14_counters);
|
||||
break;
|
||||
case 8561:
|
||||
case 8562:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
cp = cpumcf_z15_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z15_counters);
|
||||
read_ccerror(cp, *len);
|
||||
@@ -3074,7 +2920,8 @@ static void show_counter(bool all)
|
||||
struct counters *cp;
|
||||
size_t cp_cnt;
|
||||
|
||||
printf("perf event counter list for %s\n", machine_name());
|
||||
printf("perf event counter list for %s\n",
|
||||
util_arch_machine_type_str());
|
||||
show_hdr();
|
||||
/* Basic counter set */
|
||||
cp = get_counter(CPUMF_CTRSET_BASIC, &cp_cnt);
|
||||
@@ -3103,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:
|
||||
|
||||
722
cpumf/lshwc.c
Normal file
722
cpumf/lshwc.c
Normal file
@@ -0,0 +1,722 @@
|
||||
/* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
/* CPU Measurements counter facility counter sets can be extracted by a
|
||||
* device driver accessible by opening device /dev/hwctr.
|
||||
* This program extracts complete counter set using this device.
|
||||
* Counter sets are per CPU, the interface allows to specify counter sets
|
||||
* for individual CPUs. The supported flags are executed from left to
|
||||
* right, the first error encountered stops the execution of the program.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/user.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#include "lshwc.h"
|
||||
|
||||
#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;
|
||||
static int cfvn, csvn, authorization;
|
||||
static unsigned long loop_count = 1;
|
||||
static unsigned char *ioctlbuffer;
|
||||
static bool allcpu;
|
||||
|
||||
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||||
static struct ctrname { /* List of defined counters */
|
||||
char *name; /* Counter name */
|
||||
bool hitcnt; /* Counter number read from ioctl() */
|
||||
unsigned long total; /* Total counter value */
|
||||
unsigned long *ccv; /* Per CPU counter value */
|
||||
} ctrname[MAXCTRS];
|
||||
|
||||
/* Open file and extract counter number */
|
||||
static int read_counter(const char *p)
|
||||
{
|
||||
FILE *fp = fopen(p, "r");
|
||||
int rc = 0, ctr;
|
||||
|
||||
if (fp) {
|
||||
rc = fscanf(fp, "event=%x", &ctr);
|
||||
fclose(fp);
|
||||
}
|
||||
return rc == 1 ? ctr : -EINVAL;
|
||||
}
|
||||
|
||||
static int add_countername(char *name, int nr)
|
||||
{
|
||||
ctrname[nr].name = strdup(name);
|
||||
return ctrname[nr].name ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static bool read_counternames(void)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
int i, ctr = 0, count = 0;
|
||||
char *path, *ctrpath;
|
||||
|
||||
path = util_path_sysfs("/bus/event_source/devices/cpum_cf/events/");
|
||||
count = util_scandir(&namelist, alphasort, path, "[^.]");
|
||||
if (count <= 0) {
|
||||
warnx("Cannot open %s", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < count && ctr >= 0; i++) {
|
||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||
ctr = read_counter(ctrpath);
|
||||
free(ctrpath);
|
||||
if (ctr >= 0)
|
||||
ctr = add_countername(namelist[i]->d_name, ctr);
|
||||
}
|
||||
if (ctr < 0)
|
||||
warnx("Cannot parse %s", path);
|
||||
util_scandir_free(namelist, count);
|
||||
free(path);
|
||||
return ctr < 0 ? false : true;
|
||||
}
|
||||
|
||||
static void free_counternames(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||
free(ctrname[i].name);
|
||||
free(ctrname[i].ccv);
|
||||
}
|
||||
}
|
||||
|
||||
static struct check_result {
|
||||
bool cpu_pos; /* CPU Number possible */
|
||||
bool cpu_req; /* CPU Number requested */
|
||||
bool cpu_hit; /* CPU Number received */
|
||||
unsigned char sets_req; /* Counters sets requested */
|
||||
unsigned char sets_hit; /* Counters sets received */
|
||||
} *check;
|
||||
|
||||
static bool check_set(unsigned long a, unsigned long b, unsigned long sets)
|
||||
{
|
||||
if (a > b)
|
||||
return false;
|
||||
for (; a <= b; ++a) {
|
||||
if (a >= max_possible_cpus || !check[a].cpu_pos)
|
||||
return false;
|
||||
check[a].cpu_req = true;
|
||||
check[a].sets_req = sets;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions to parse command line parameters
|
||||
* Convert a number from ascii to int.
|
||||
*/
|
||||
static unsigned long getnumber(char *word, char stopchar)
|
||||
{
|
||||
unsigned long no;
|
||||
char *endp;
|
||||
|
||||
no = strtoul(word, &endp, 0);
|
||||
if (*endp != stopchar)
|
||||
errx(EXIT_FAILURE, "Invalid parameter %s", word);
|
||||
return no;
|
||||
}
|
||||
|
||||
/* Read file to get all online CPUs */
|
||||
static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||||
{
|
||||
char fmt[16];
|
||||
FILE *slp;
|
||||
int rc;
|
||||
|
||||
slp = fopen(file, "r");
|
||||
if (!slp) {
|
||||
warnx("Cannot open %s", file);
|
||||
return false;
|
||||
}
|
||||
snprintf(fmt, sizeof(fmt), "%%%zus", bufsz - 1);
|
||||
rc = fscanf(slp, fmt, buf);
|
||||
fclose(slp);
|
||||
if (rc != 1)
|
||||
warnx("Cannot parse %s", file);
|
||||
return rc == 1 ? true : false;
|
||||
}
|
||||
|
||||
/* Parse counter set specification */
|
||||
static unsigned long parse_ctrset(char *cp)
|
||||
{
|
||||
unsigned long x = 0;
|
||||
|
||||
for (; *cp; ++cp) {
|
||||
switch (tolower(*cp)) {
|
||||
case 'b':
|
||||
x |= S390_HWCTR_BASIC;
|
||||
break;
|
||||
case 'c':
|
||||
x |= S390_HWCTR_CRYPTO;
|
||||
break;
|
||||
case 'e':
|
||||
x |= S390_HWCTR_EXT;
|
||||
break;
|
||||
case 'm':
|
||||
x |= S390_HWCTR_MT_DIAG;
|
||||
break;
|
||||
case 'p':
|
||||
case 'u':
|
||||
x |= S390_HWCTR_USER;
|
||||
break;
|
||||
case 'a':
|
||||
x |= S390_HWCTR_ALL;
|
||||
break;
|
||||
default:
|
||||
errx(EXIT_FAILURE,
|
||||
"Invalid counter set specification '%c'", *cp);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static char *show_ctrset(unsigned long set)
|
||||
{
|
||||
static char text[16];
|
||||
int i = 0;
|
||||
|
||||
if (set & S390_HWCTR_BASIC)
|
||||
text[i++] = 'B';
|
||||
if (set & S390_HWCTR_CRYPTO)
|
||||
text[i++] = 'C';
|
||||
if (set & S390_HWCTR_EXT)
|
||||
text[i++] = 'E';
|
||||
if (set & S390_HWCTR_MT_DIAG)
|
||||
text[i++] = 'M';
|
||||
if (set & S390_HWCTR_USER)
|
||||
text[i++] = 'U';
|
||||
text[i] = '\0';
|
||||
return text;
|
||||
}
|
||||
|
||||
/* Parse CPU list and counter sets */
|
||||
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
{
|
||||
uint64_t *words = start->cpumask;
|
||||
unsigned int i, no_a, no_b;
|
||||
cpu_set_t cpulist;
|
||||
int rc;
|
||||
|
||||
CPU_ZERO(&cpulist);
|
||||
start->data_bytes = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Check with authorized counter sets */
|
||||
if ((start->counter_sets & authorization) != start->counter_sets) {
|
||||
unsigned int noton = ~(start->counter_sets & authorization);
|
||||
|
||||
start->counter_sets &= authorization;
|
||||
if (!start->counter_sets)
|
||||
errx(EXIT_FAILURE, "No counter sets are authorized");
|
||||
warnx("One or more counter sets are not authorized: %s",
|
||||
show_ctrset(noton));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (check[i].cpu_req) {
|
||||
no_a = i % LONG_BIT;
|
||||
no_b = i / LONG_BIT;
|
||||
words[no_b] |= 1ULL << no_a;
|
||||
}
|
||||
}
|
||||
/* no_b is highest used index */
|
||||
start->cpumask_len = (no_b + 1) * CHAR_BIT;
|
||||
start->version = S390_HWCTR_START_VERSION;
|
||||
}
|
||||
|
||||
static bool check_setpossible(void)
|
||||
{
|
||||
char *cp, *parm, *tokens[16]; /* Used to parse command line params */
|
||||
unsigned long i, no_a, no_b;
|
||||
char cpubuf[1024];
|
||||
|
||||
if (!get_cpus(CPUS_KERNELMAX, cpubuf, sizeof(cpubuf)))
|
||||
return false;
|
||||
max_possible_cpus = getnumber(cpubuf, '\0') + 1;
|
||||
check = calloc(max_possible_cpus, sizeof(*check));
|
||||
if (!check)
|
||||
err(EXIT_FAILURE, "Maximum CPUs %u", max_possible_cpus);
|
||||
if (!get_cpus(CPUS_POSSIBLE, cpubuf, sizeof(cpubuf))) {
|
||||
free(check);
|
||||
return false;
|
||||
}
|
||||
parm = cpubuf;
|
||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||
++i, parm = NULL) {
|
||||
cp = strchr(tokens[i], '-');
|
||||
if (cp) { /* Range */
|
||||
no_a = getnumber(tokens[i], *cp);
|
||||
no_b = getnumber(++cp, '\0');
|
||||
} else {
|
||||
no_b = getnumber(tokens[i], '\0');
|
||||
no_a = no_b;
|
||||
}
|
||||
for (; no_a <= no_b; ++no_a)
|
||||
check[no_a].cpu_pos = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void show_header(void)
|
||||
{
|
||||
static bool header;
|
||||
bool comma = false;
|
||||
|
||||
if (header)
|
||||
return; /* Printed already */
|
||||
printf("Date,Time,CPU,"); /* Print counter name and number */
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||
if (!ctrname[i].hitcnt)
|
||||
continue;
|
||||
if (comma)
|
||||
putchar(',');
|
||||
printf("%s(%ld)", ctrname[i].name ?: "Counter", i);
|
||||
comma = true;
|
||||
}
|
||||
putchar('\n');
|
||||
header = true;
|
||||
}
|
||||
|
||||
static void line(char *header)
|
||||
{
|
||||
bool comma;
|
||||
|
||||
show_header();
|
||||
if (allcpu) {
|
||||
for (unsigned int h = 0; h < max_possible_cpus; ++h) {
|
||||
char txt[16];
|
||||
|
||||
if (!check[h].cpu_hit)
|
||||
continue;
|
||||
comma = false;
|
||||
snprintf(txt, sizeof(txt), "CPU%d,", h);
|
||||
printf("%s%s", header, txt);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||
if (!ctrname[i].hitcnt)
|
||||
continue;
|
||||
if (comma)
|
||||
putchar(',');
|
||||
printf("%ld", ctrname[i].ccv[h]);
|
||||
comma = true;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* Print total count of all CPUs */
|
||||
printf("%sTotal,", header);
|
||||
comma = false;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||
if (!ctrname[i].hitcnt)
|
||||
continue;
|
||||
if (comma)
|
||||
putchar(',');
|
||||
printf("%ld", ctrname[i].total);
|
||||
comma = true;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void show(void)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm *now_tm;
|
||||
char now_text[32];
|
||||
|
||||
now_tm = localtime(&now);
|
||||
strftime(now_text, sizeof(now_text), "%F,%T,", now_tm);
|
||||
line(now_text);
|
||||
}
|
||||
|
||||
/* Return Counter set size numbers (in counters) */
|
||||
static unsigned int ctrset_size(int set)
|
||||
{
|
||||
switch (set) {
|
||||
case S390_HWCTR_BASIC:
|
||||
return 6;
|
||||
case S390_HWCTR_USER:
|
||||
return (cfvn == 1) ? 6 : 2;
|
||||
case S390_HWCTR_CRYPTO:
|
||||
return (csvn <= 5) ? 16 : 20;
|
||||
case S390_HWCTR_EXT:
|
||||
switch (csvn) {
|
||||
case 1: return 32;
|
||||
case 2: return 48;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: return 128;
|
||||
}
|
||||
return 160;
|
||||
case S390_HWCTR_MT_DIAG:
|
||||
switch (csvn) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: return 0;
|
||||
}
|
||||
return 48;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return counter set offset numbers */
|
||||
static int ctrset_offset(int set)
|
||||
{
|
||||
switch (set) {
|
||||
case S390_HWCTR_BASIC:
|
||||
return 0;
|
||||
case S390_HWCTR_USER:
|
||||
return 32;
|
||||
case S390_HWCTR_CRYPTO:
|
||||
return 64;
|
||||
case S390_HWCTR_EXT:
|
||||
return 128;
|
||||
case S390_HWCTR_MT_DIAG:
|
||||
return 448;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool set_and_size_ok(struct s390_hwctr_setdata *p)
|
||||
{
|
||||
switch (p->set) {
|
||||
case S390_HWCTR_BASIC:
|
||||
case S390_HWCTR_USER:
|
||||
case S390_HWCTR_CRYPTO:
|
||||
case S390_HWCTR_EXT:
|
||||
case S390_HWCTR_MT_DIAG:
|
||||
return p->no_cnts == ctrset_size(p->set);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
||||
{
|
||||
if (idx >= ARRAY_SIZE(ctrname)) {
|
||||
warnx("Invalid counter number %zu", idx);
|
||||
return false;
|
||||
}
|
||||
if (cpu >= max_possible_cpus) {
|
||||
warnx("Invalid CPU number %d", cpu);
|
||||
return false;
|
||||
}
|
||||
if (!ctrname[idx].ccv) /* Unknown counter */
|
||||
ctrname[idx].ccv = calloc(max_possible_cpus,
|
||||
sizeof(unsigned long));
|
||||
if (ctrname[idx].ccv)
|
||||
ctrname[idx].ccv[cpu] += value;
|
||||
ctrname[idx].total += value;
|
||||
ctrname[idx].hitcnt = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int test_read(struct s390_hwctr_read *read)
|
||||
{
|
||||
void *base = &read->data;
|
||||
size_t offset = 0;
|
||||
|
||||
/* Clear previous hit counters */
|
||||
for (unsigned int i = 0; i < max_possible_cpus; ++i) {
|
||||
check[i].sets_hit = 0;
|
||||
check[i].cpu_hit = false;
|
||||
}
|
||||
|
||||
/* Iterate over all CPUs */
|
||||
for (unsigned int i = 0; i < read->no_cpus; ++i) {
|
||||
struct s390_hwctr_cpudata *cp = base + offset;
|
||||
|
||||
check[cp->cpu_nr].cpu_hit = true;
|
||||
check[cp->cpu_nr].sets_hit = 0;
|
||||
|
||||
offset += sizeof(cp->cpu_nr) + sizeof(cp->no_sets);
|
||||
/* Iterate over all counter sets */
|
||||
for (unsigned int j = 0; j < cp->no_sets; ++j) {
|
||||
struct s390_hwctr_setdata *sp = base + offset;
|
||||
|
||||
check[cp->cpu_nr].sets_hit |= sp->set;
|
||||
offset += sizeof(sp->set) + sizeof(sp->no_cnts);
|
||||
if (!set_and_size_ok(sp)) {
|
||||
warnx("CPU %d inconsistent set %d size %d",
|
||||
cp->cpu_nr, sp->set, sp->no_cnts);
|
||||
return -1;
|
||||
}
|
||||
/* Iterate over all counters in each set */
|
||||
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
||||
uint64_t value;
|
||||
void *addr = base + offset;
|
||||
size_t idx = ctrset_offset(sp->set) + k;
|
||||
|
||||
memcpy(&value, addr, sizeof(value));
|
||||
offset += sizeof(value);
|
||||
if (!add_countervalue(idx, cp->cpu_nr, value))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
show();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_open(void)
|
||||
{
|
||||
int fd = open(S390_HWCTR_DEVICE, O_RDWR);
|
||||
|
||||
if (fd < 0)
|
||||
warn(S390_HWCTR_DEVICE);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int do_stop(int ioctlfd)
|
||||
{
|
||||
int rc = ioctl(ioctlfd, S390_HWCTR_STOP, 0);
|
||||
|
||||
if (rc < 0)
|
||||
warn("ioctl S390_HWCTR_STOP");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_start(int ioctlfd, struct s390_hwctr_start *start)
|
||||
{
|
||||
int rc = ioctl(ioctlfd, S390_HWCTR_START, start);
|
||||
|
||||
if (rc < 0)
|
||||
warn("ioctl S390_HWCTR_START");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_read(int ioctlfd)
|
||||
{
|
||||
size_t ioctlbuffer_len = PAGE_SIZE * max_possible_cpus +
|
||||
sizeof(struct s390_hwctr_read);
|
||||
struct s390_hwctr_read *read;
|
||||
int rc;
|
||||
|
||||
if (!ioctlbuffer) {
|
||||
ioctlbuffer = malloc(ioctlbuffer_len);
|
||||
if (!ioctlbuffer) {
|
||||
warn("ioctl S390_HWCTR_START");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
read = (struct s390_hwctr_read *)ioctlbuffer;
|
||||
rc = ioctl(ioctlfd, S390_HWCTR_READ, read);
|
||||
if (!rc)
|
||||
rc = test_read(read);
|
||||
else
|
||||
warn("ioctl S390_HWCTR_READ");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void do_sleep(void)
|
||||
{
|
||||
struct timespec req = {
|
||||
.tv_sec = read_interval,
|
||||
.tv_nsec = 0
|
||||
};
|
||||
|
||||
nanosleep(&req, NULL);
|
||||
}
|
||||
|
||||
/* Execute commands and report first error */
|
||||
static int do_it(char *s)
|
||||
{
|
||||
struct s390_hwctr_start start;
|
||||
int ioctlfd;
|
||||
int rc;
|
||||
|
||||
memset(&start, 0, sizeof(start));
|
||||
rc = max_possible_cpus / sizeof(uint64_t);
|
||||
start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t));
|
||||
memset(start.cpumask, 0, rc);
|
||||
parse_cpulist(s, &start);
|
||||
errno = 0;
|
||||
ioctlfd = do_open();
|
||||
if (ioctlfd < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
rc = do_start(ioctlfd, &start);
|
||||
if (rc < 0) {
|
||||
close(ioctlfd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; !rc && i < loop_count; ++i) {
|
||||
rc = do_read(ioctlfd);
|
||||
if (rc) {
|
||||
close(ioctlfd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (read_interval && i + 1 < loop_count)
|
||||
do_sleep();
|
||||
}
|
||||
rc = do_stop(ioctlfd);
|
||||
close(ioctlfd);
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a' },
|
||||
.desc = "Displays all CPUs in output"
|
||||
},
|
||||
{
|
||||
.option = { "loop", required_argument, NULL, 'l' },
|
||||
.argument = "NUMBER",
|
||||
.desc = "Specifies loop count for next read"
|
||||
},
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "NUMBER",
|
||||
.desc = "Specifies interval between read operations (seconds)"
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "Read CPU Measurement facility counter sets",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2021,
|
||||
.pub_last = 2021,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/* Check for hardware support and exit if not available */
|
||||
static void have_support(void)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(S390_HWCTR_DEVICE, &statbuf) == -1)
|
||||
errx(EXIT_FAILURE,
|
||||
"No support for CPU Measurement Counter set facility");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *slash;
|
||||
int ch;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
||||
switch (ch) {
|
||||
default:
|
||||
util_opt_print_parse_error(ch, argv);
|
||||
return EXIT_FAILURE;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 'l':
|
||||
errno = 0;
|
||||
loop_count = strtoul(optarg, &slash, 0);
|
||||
if (errno || *slash)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -%c",
|
||||
ch);
|
||||
break;
|
||||
case 'i':
|
||||
errno = 0;
|
||||
read_interval = (unsigned int)strtoul(optarg, &slash, 0);
|
||||
if (errno || *slash)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||
break;
|
||||
case 'a':
|
||||
allcpu = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
have_support();
|
||||
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
||||
return EXIT_FAILURE;
|
||||
if (!check_setpossible())
|
||||
return EXIT_FAILURE;
|
||||
if (!read_counternames()) {
|
||||
free(check);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
ch = do_it(NULL);
|
||||
} else {
|
||||
while (optind < argc) {
|
||||
ch = do_it(argv[optind++]);
|
||||
if (ch)
|
||||
break;
|
||||
}
|
||||
}
|
||||
free_counternames();
|
||||
free(check);
|
||||
return ch;
|
||||
}
|
||||
93
cpumf/lshwc.h
Normal file
93
cpumf/lshwc.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CPU Measurement counter facility application for device driver.
|
||||
*
|
||||
* Ioctl system call definitions.
|
||||
*/
|
||||
|
||||
#ifndef LSHWC_H
|
||||
#define LSHWC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
enum {
|
||||
S390_HWCTR_BASIC = 0x2, /* BASIC counter set */
|
||||
S390_HWCTR_USER = 0x4, /* Problem-State Counter Set */
|
||||
S390_HWCTR_CRYPTO = 0x8, /* Crypto-Activity Counter Set */
|
||||
S390_HWCTR_EXT = 0x1, /* Extended Counter Set */
|
||||
S390_HWCTR_MT_DIAG = 0x20, /* MT-diagnostic Counter Set */
|
||||
S390_HWCTR_ALL = S390_HWCTR_BASIC | S390_HWCTR_USER |
|
||||
S390_HWCTR_CRYPTO | S390_HWCTR_EXT |
|
||||
S390_HWCTR_MT_DIAG
|
||||
};
|
||||
|
||||
/* The ioctl(..., S390_HWCTR_READ, ...) is the only subcommand which returns
|
||||
* data. It requires member data_bytes to be positive and indicates the
|
||||
* maximum amount of data available to store counter set data. The other
|
||||
* ioctl() subcommands do not use this member and it should be set to zero.
|
||||
*
|
||||
* The cpuset data is flattened using the following scheme, stored in member
|
||||
* data:
|
||||
*
|
||||
* 0x0 0x8 0xc 0x10 0x14 0x18 0x20 0x28 0xU-1
|
||||
* +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||||
* | no_cpus | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||
* +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||||
*
|
||||
* 0xU 0xU+4 0xU+8 0xU+10 0xV-1
|
||||
* +-----+---------+-----+-----+------+------+
|
||||
* | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||
* +-----+---------+-----+-----+------+------+
|
||||
*
|
||||
* 0xV 0xV+4 0xV+8 0xV+c
|
||||
* +-----+---------+-----+---------+-----+-----+------+------+
|
||||
* | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||
* +-----+---------+-----+---------+-----+-----+------+------+
|
||||
*
|
||||
* U and V denote arbitrary hexadezimal addresses.
|
||||
* In fact the first int represents the number of CPUs data was extracted
|
||||
* from. This is followed by CPU number and number of counter sets extracted.
|
||||
* Both are two integer values. This is followed by the set number and number
|
||||
* of counters extracted. Both are two integer values. This is followed by
|
||||
* the counter values, each element is eight bytes in size.
|
||||
*/
|
||||
|
||||
struct s390_hwctr_start { /* Set CPUs to operate on */
|
||||
uint64_t version; /* Version of interface */
|
||||
uint64_t data_bytes; /* # of bytes required */
|
||||
uint64_t cpumask_len; /* Length of CPU mask in bytes */
|
||||
uint64_t *cpumask; /* Pointer to CPU mask */
|
||||
uint64_t counter_sets; /* Bit mask of counter set to get */
|
||||
};
|
||||
|
||||
struct s390_hwctr_setdata { /* Counter set data */
|
||||
uint32_t set; /* Counter set number */
|
||||
uint32_t no_cnts; /* # of counters stored in cv[] */
|
||||
uint64_t cv[0]; /* Counter values (variable length) */
|
||||
};
|
||||
|
||||
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||||
uint32_t cpu_nr; /* Counter set number */
|
||||
uint32_t no_sets; /* # of counters sets in data[] */
|
||||
struct s390_hwctr_setdata data[0];
|
||||
};
|
||||
|
||||
struct s390_hwctr_read { /* Structure to get all ctr sets */
|
||||
uint64_t no_cpus; /* Total # of CPUs data taken from */
|
||||
struct s390_hwctr_cpudata data[0];
|
||||
};
|
||||
|
||||
#define S390_HWCTR_MAGIC 'C' /* Random magic # for ioctls */
|
||||
#define S390_HWCTR_START _IOWR(S390_HWCTR_MAGIC, 1, struct s390_hwctr_start)
|
||||
#define S390_HWCTR_STOP _IO(S390_HWCTR_MAGIC, 2)
|
||||
#define S390_HWCTR_READ _IOWR(S390_HWCTR_MAGIC, 3, struct s390_hwctr_read)
|
||||
|
||||
#define S390_HWCTR_START_VERSION 1 /* Version # s390_hwctr_start */
|
||||
#define S390_HWCTR_DEVICE "/dev/hwctr" /* Device name */
|
||||
#endif
|
||||
134
cpumf/man/lshwc.1
Normal file
134
cpumf/man/lshwc.1
Normal file
@@ -0,0 +1,134 @@
|
||||
.\" lshwc.1
|
||||
.\"
|
||||
.\"
|
||||
.\" Copyright IBM Corp. 2021
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\" ----------------------------------------------------------------------
|
||||
.ds c \fBlshwc\fP
|
||||
.
|
||||
.TH \*c "1" "February 2021" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.SH NAME
|
||||
\*c \- extract CPU Measurement Facilities counter sets
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
.RB [ \-a ]
|
||||
.RB [ \-l
|
||||
.IR count ]
|
||||
.RB [ \-i
|
||||
.IR interval ]
|
||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||
.br
|
||||
\*c
|
||||
.BR \-h | \-\-help
|
||||
.br
|
||||
\*c
|
||||
.BR \-v | \-\-version
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
The \*c command extracts complete counter sets from the CPU
|
||||
Measurement Facilities for Linux on Z.
|
||||
Counter sets can be specified and extracted for individual CPUs.
|
||||
The output is a comma-separated values file.
|
||||
Each line starts with a timestamp and the CPU number,
|
||||
followed by the extracted counter values.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Displays help information, then exits.
|
||||
.
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
Displays version information, then exits.
|
||||
.
|
||||
.TP
|
||||
.BR \-a ", " \-\-allcpu
|
||||
Displays counter values from each CPU.
|
||||
The default is a total summary line of all counters from all CPUs.
|
||||
.
|
||||
.TP
|
||||
.BR \-i ", " \-\-interval \fI\ seconds\fP
|
||||
Specifies a time interval, in seconds,
|
||||
that the command waits between read operations.
|
||||
The default is 60 seconds.
|
||||
.
|
||||
.TP
|
||||
.BR \-l ", " \-\-loop \fI\ count\fP
|
||||
Performs the specified number of read operations.
|
||||
.
|
||||
.TP
|
||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||
A comma-separated list of CPUs.
|
||||
Each CPU can optionally be followed by characters that specify the counter set.
|
||||
See below for details.
|
||||
.
|
||||
.SS "CPU List and counter-set specification"
|
||||
In the comma-separated list of CPUs,
|
||||
each element is a CPU or a range of CPUs.
|
||||
By default, \*c lists all CPUs.
|
||||
.P
|
||||
The CPU list can be followed by an optional list
|
||||
of characters that specify the counter sets to be extracted,
|
||||
preceded by a colon.
|
||||
The characters can be upper or lower case.
|
||||
By default, all counter sets are used.
|
||||
.IP b
|
||||
Include the basic counter set.
|
||||
.IP c
|
||||
Include the crypto counter set.
|
||||
.IP e
|
||||
Include the extended counter set.
|
||||
.IP m
|
||||
Include the MT_Diagnostic counter set.
|
||||
.IP p|u
|
||||
Include the problem counter set.
|
||||
.IP a
|
||||
Include all known counter sets (default).
|
||||
.SH "Concurrency with perf tool"
|
||||
The \*c tool and the linux
|
||||
.B perf
|
||||
tool use the same hardware and cannot be used concurrently.
|
||||
Both tools print an error message and abort when they
|
||||
detect this situation.
|
||||
.SH "EXAMPLES"
|
||||
The first example enables the basic and problem counter sets on CPU 0 and 1.
|
||||
Two read operations are performed and a summary line is printed for each
|
||||
read operation.
|
||||
.sp 1
|
||||
.nf
|
||||
.ft CW
|
||||
# lshwc -l2 0-1:BP
|
||||
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
||||
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||
2021-04-01,11:50:32,Total,125422,39421,304,13953,454,
|
||||
97489,0,0
|
||||
2021-04-01,11:51:32,Total,68074231,16386850,194028,21382384,317227,
|
||||
104503489,777383,14198
|
||||
.ft
|
||||
.fi
|
||||
.sp 1
|
||||
This example shows the counter values of the problem state counter set
|
||||
per CPU. CPU 0 and CPU 1 is selected.
|
||||
.nf
|
||||
.ft CW
|
||||
.sp 1
|
||||
# lshwc -l3 -a 0-1:P
|
||||
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||
2021-04-01,11:54:47,CPU0,0,0
|
||||
2021-04-01,11:54:47,CPU1,0,0
|
||||
2021-04-01,11:54:47,Total,0,0
|
||||
2021-04-01,11:55:47,CPU0,818775,14198
|
||||
2021-04-01,11:55:47,CPU1,125689,1306
|
||||
2021-04-01,11:55:47,Total,944464,15504
|
||||
2021-04-01,11:56:47,CPU0,3207071426,1489122591
|
||||
2021-04-01,11:56:47,CPU1,3225092021,1489278312
|
||||
2021-04-01,11:56:47,Total,6432163447,2978400903
|
||||
.ft
|
||||
.fi
|
||||
.SH "SEE ALSO"
|
||||
.BR lscpumf (1)
|
||||
.BR chcpumf (8)
|
||||
@@ -110,6 +110,7 @@ static struct util_opt dump2tar_opts[] = {
|
||||
{
|
||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||
.desc = "Stop reading file after SEC seconds",
|
||||
.argument = "SEC",
|
||||
},
|
||||
{
|
||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||
|
||||
25
etc/hsavmcore.conf
Normal file
25
etc/hsavmcore.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
# Example configuration for hsavmcore
|
||||
# See hsavmcore.conf(8) for documentation
|
||||
|
||||
# 0 - ERROR
|
||||
# 1 - WARN
|
||||
# 2 - INFO
|
||||
# 3 - DEBUG
|
||||
# 4 - TRACE
|
||||
#verbose = 0
|
||||
|
||||
#workdir = /var/crash
|
||||
|
||||
#mount_debugfs = 0
|
||||
|
||||
#use_hsa_mem = 0
|
||||
|
||||
#hsa_size = -1
|
||||
#release_hsa = 1
|
||||
|
||||
#bind_mount_vmcore = 1
|
||||
|
||||
#swap = /dev/disk/by-uuid/3cf6630b-4c4d-49ac-a0ae-0f5484cb5721
|
||||
#swap = /swap.img
|
||||
|
||||
#fuse_debug = 0
|
||||
@@ -26,10 +26,10 @@ KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env
|
||||
|
||||
LABEL="dasd_symlinks_end"
|
||||
|
||||
# on device add set request queue scheduler to deadline
|
||||
# on device add set request queue scheduler to none
|
||||
SUBSYSTEM!="block", GOTO="sched_end"
|
||||
|
||||
ACTION!="change", GOTO="sched_end"
|
||||
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline"
|
||||
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none"
|
||||
|
||||
LABEL="sched_end"
|
||||
|
||||
@@ -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,31 +87,53 @@ 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.
|
||||
.TP
|
||||
\fB\-\-disable\-pckmo\fR
|
||||
Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||
functions. Optional.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Prints version information, then exits.
|
||||
|
||||
.SH 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,6 +29,7 @@ $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
||||
|
||||
ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
|
||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
||||
-DOPENSSL_API_COMPAT=0x10100000L \
|
||||
$(WARNINGS) \
|
||||
$(NULL)
|
||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
||||
|
||||
@@ -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,8 +24,15 @@
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_CFLAG_NO_DECRYPTION 0x10000000ULL
|
||||
/* Internal helper macro */
|
||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||
|
||||
/* Plaintext control flags */
|
||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */
|
||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -64,6 +64,17 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
{
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -81,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"));
|
||||
@@ -104,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;
|
||||
@@ -148,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"))
|
||||
@@ -197,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)
|
||||
{
|
||||
@@ -262,6 +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") },
|
||||
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,
|
||||
@@ -320,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,
|
||||
@@ -357,7 +432,8 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
.description =
|
||||
_("Specify the plaintext control flags\n" INDENT
|
||||
"as a hexadecimal value.\n" INDENT
|
||||
"Optional; default: '0x0'."),
|
||||
"Optional; mutually exclusive with\n" INDENT
|
||||
"'--(enable|disable)-pckmo'; default: '0xe0'."),
|
||||
.arg_description = _("VALUE") },
|
||||
{ .long_name = "x-psw",
|
||||
.short_name = 0,
|
||||
@@ -410,6 +486,8 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,20 @@ typedef struct pv_arg {
|
||||
PvArg *pv_arg_new(PvComponentType type, const gchar *path);
|
||||
void pv_arg_free(PvArg *arg);
|
||||
|
||||
typedef enum pv_tristate {
|
||||
PV_NOT_SET = 0,
|
||||
PV_TRUE,
|
||||
PV_FALSE,
|
||||
} PvTristate;
|
||||
|
||||
typedef struct {
|
||||
gint log_level;
|
||||
gint no_verify;
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -73,12 +73,12 @@ PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||||
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
||||
}
|
||||
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(buf);
|
||||
|
||||
g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE);
|
||||
g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE);
|
||||
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
||||
err);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ void pv_component_free(PvComponent *component)
|
||||
|
||||
switch ((PvComponentDataType)component->d_type) {
|
||||
case DATA_BUFFER:
|
||||
buffer_clear(&component->buf);
|
||||
pv_buffer_clear(&component->buf);
|
||||
break;
|
||||
case DATA_FILE:
|
||||
comp_file_free(component->file);
|
||||
@@ -162,21 +162,21 @@ gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_pat
|
||||
|
||||
switch ((PvComponentDataType)component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
g_autoptr(Buffer) enc_buf = NULL;
|
||||
g_autoptr(PvBuffer) enc_buf = NULL;
|
||||
|
||||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
||||
g_autoptr(Buffer) new = NULL;
|
||||
g_autoptr(PvBuffer) new = NULL;
|
||||
|
||||
/* create a page aligned copy */
|
||||
new = buffer_dup(component->buf, TRUE);
|
||||
buffer_clear(&component->buf);
|
||||
new = pv_buffer_dup(component->buf, TRUE);
|
||||
pv_buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&new);
|
||||
}
|
||||
enc_buf = encrypt_buf(parms, component->buf, err);
|
||||
if (!enc_buf)
|
||||
return -1;
|
||||
|
||||
buffer_clear(&component->buf);
|
||||
pv_buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&enc_buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -220,10 +220,10 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||||
|
||||
switch (component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
g_autoptr(Buffer) buf = NULL;
|
||||
g_autoptr(PvBuffer) buf = NULL;
|
||||
|
||||
buf = buffer_dup(component->buf, TRUE);
|
||||
buffer_clear(&component->buf);
|
||||
buf = pv_buffer_dup(component->buf, TRUE);
|
||||
pv_buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&buf);
|
||||
return 0;
|
||||
} break;
|
||||
@@ -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"));
|
||||
@@ -301,7 +285,7 @@ int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
|
||||
switch (comp->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
const Buffer *buf = comp->buf;
|
||||
const PvBuffer *buf = comp->buf;
|
||||
|
||||
g_assert(buf->size <= INT64_MAX);
|
||||
g_assert(buf->size == size);
|
||||
@@ -383,6 +367,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bin2bn failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) {
|
||||
@@ -395,6 +380,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bn2binpad failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) {
|
||||
@@ -409,6 +395,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_add_word failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
nep++;
|
||||
@@ -425,7 +412,7 @@ gint pv_component_write(const PvComponent *component, FILE *f, GError **err)
|
||||
|
||||
switch (component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
const Buffer *buf = component->buf;
|
||||
const PvBuffer *buf = component->buf;
|
||||
|
||||
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -41,7 +41,7 @@ typedef struct {
|
||||
gint d_type; /* PvComponentDataType */
|
||||
union {
|
||||
struct comp_file *file;
|
||||
Buffer *buf;
|
||||
PvBuffer *buf;
|
||||
void *data;
|
||||
};
|
||||
uint64_t src_addr;
|
||||
@@ -51,7 +51,7 @@ typedef struct {
|
||||
|
||||
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||||
GError **err);
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||||
GError **err);
|
||||
void pv_component_free(PvComponent *component);
|
||||
gint pv_component_type(const PvComponent *component);
|
||||
|
||||
@@ -210,13 +210,13 @@ GSList *pv_img_comps_get_comps(const PvImgComps *comps)
|
||||
return comps->comps;
|
||||
}
|
||||
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||||
Buffer **ald_digest, Buffer **tld_digest,
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||||
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||||
uint64_t *nep, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) tmp_pld_digest = NULL;
|
||||
g_autoptr(Buffer) tmp_ald_digest = NULL;
|
||||
g_autoptr(Buffer) tmp_tld_digest = NULL;
|
||||
g_autoptr(PvBuffer) tmp_pld_digest = NULL;
|
||||
g_autoptr(PvBuffer) tmp_ald_digest = NULL;
|
||||
g_autoptr(PvBuffer) tmp_tld_digest = NULL;
|
||||
|
||||
comps->finalized = TRUE;
|
||||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
||||
|
||||
@@ -32,8 +32,8 @@ gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp,
|
||||
GError **err);
|
||||
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n);
|
||||
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err);
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||||
Buffer **ald_digest, Buffer **tld_digest,
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||||
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||||
uint64_t *nep, GError **err);
|
||||
void pv_img_comps_free(PvImgComps *comps);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ uint32_t pv_hdr_size(const PvHdr *hdr)
|
||||
|
||||
gboolean pv_hdr_uses_encryption(const PvHdr *hdr)
|
||||
{
|
||||
return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_CFLAG_NO_DECRYPTION);
|
||||
return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_PCF_NO_DECRYPTION);
|
||||
}
|
||||
|
||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr)
|
||||
@@ -76,17 +76,17 @@ uint64_t pv_hdr_get_nks(const PvHdr *hdr)
|
||||
}
|
||||
|
||||
/* In-place modification of ``buf`` */
|
||||
static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf,
|
||||
static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, PvBuffer *buf,
|
||||
GError **err)
|
||||
{
|
||||
uint32_t hdr_len = pv_hdr_size(hdr);
|
||||
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
||||
guint tag_len = pv_hdr_tag_size(hdr);
|
||||
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
||||
const Buffer aad_part = { .data = buf->data, .size = aad_len };
|
||||
Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||||
const PvBuffer aad_part = { .data = buf->data, .size = aad_len };
|
||||
PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||||
.size = enc_len };
|
||||
Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||||
PvBuffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||||
.size = tag_len };
|
||||
struct cipher_parms parms;
|
||||
int64_t c_len;
|
||||
@@ -119,9 +119,9 @@ static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err)
|
||||
g_autofree union ecdh_pub_key *cust_pub_key = NULL;
|
||||
struct pv_hdr_key_slot *hdr_slot = hdr->slots;
|
||||
struct pv_hdr_head *head = &hdr->head;
|
||||
g_autoptr(Buffer) pld = NULL;
|
||||
g_autoptr(Buffer) ald = NULL;
|
||||
g_autoptr(Buffer) tld = NULL;
|
||||
g_autoptr(PvBuffer) pld = NULL;
|
||||
g_autoptr(PvBuffer) ald = NULL;
|
||||
g_autoptr(PvBuffer) tld = NULL;
|
||||
uint64_t nep = 0;
|
||||
|
||||
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
||||
@@ -250,7 +250,7 @@ PvHdr *pv_hdr_new(const PvImage *img, GError **err)
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
||||
static void pv_hdr_memcpy(const PvHdr *hdr, const PvBuffer *dst)
|
||||
{
|
||||
uint64_t nks = pv_hdr_get_nks(hdr);
|
||||
uint8_t *data;
|
||||
@@ -270,13 +270,13 @@ static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
{
|
||||
uint32_t hdr_size = pv_hdr_size(hdr);
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
|
||||
ret = buffer_alloc(hdr_size);
|
||||
ret = pv_buffer_alloc(hdr_size);
|
||||
pv_hdr_memcpy(hdr, ret);
|
||||
|
||||
if (mode == PV_ENCRYPT) {
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
PvHdr *pv_hdr_new(const PvImage *img, GError **err);
|
||||
void pv_hdr_free(PvHdr *hdr);
|
||||
G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr);
|
||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err);
|
||||
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err);
|
||||
uint32_t pv_hdr_size(const PvHdr *hdr);
|
||||
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
||||
|
||||
@@ -56,12 +56,12 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||||
GError **err)
|
||||
{
|
||||
struct cipher_parms parms = { 0 };
|
||||
g_autoptr(Buffer) tweak = NULL;
|
||||
g_autoptr(PvBuffer) tweak = NULL;
|
||||
prepare_func func = NULL;
|
||||
void *opaque = NULL;
|
||||
gint rc;
|
||||
|
||||
if (img->pcf & PV_CFLAG_NO_DECRYPTION) {
|
||||
if (img->pcf & PV_PCF_NO_DECRYPTION) {
|
||||
/* we only need to align the components */
|
||||
func = pv_component_align;
|
||||
opaque = NULL;
|
||||
@@ -76,7 +76,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||||
EVP_CIPHER_iv_length(cipher));
|
||||
g_assert(img->xts_key->size <= UINT_MAX);
|
||||
|
||||
tweak = buffer_alloc(sizeof(comp->tweak.data));
|
||||
tweak = pv_buffer_alloc(sizeof(comp->tweak.data));
|
||||
memcpy(tweak->data, comp->tweak.data, tweak->size);
|
||||
func = pv_component_align_and_encrypt;
|
||||
parms.cipher = cipher;
|
||||
@@ -93,11 +93,11 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
||||
GError **err)
|
||||
static PvBuffer *pv_img_read_key(const gchar *path, guint key_size,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) tmp_ret = NULL;
|
||||
Buffer *ret = NULL;
|
||||
g_autoptr(PvBuffer) tmp_ret = NULL;
|
||||
PvBuffer *ret = NULL;
|
||||
gsize bytes_read;
|
||||
FILE *f = NULL;
|
||||
gsize size;
|
||||
@@ -116,7 +116,7 @@ static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
tmp_ret = buffer_alloc(size);
|
||||
tmp_ret = pv_buffer_alloc(size);
|
||||
if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0)
|
||||
goto err;
|
||||
|
||||
@@ -160,8 +160,8 @@ static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid,
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
static PvBuffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
gint key_len = EVP_CIPHER_key_length(cipher);
|
||||
|
||||
@@ -173,8 +173,8 @@ static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||||
return generate_aes_key((guint)key_len, err);
|
||||
}
|
||||
|
||||
static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
static PvBuffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
gint iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
|
||||
@@ -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, GError **err)
|
||||
const gchar *scf_s,
|
||||
PvTristate allow_dump,
|
||||
PvTristate allow_pckmo, GError **err)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
@@ -247,6 +249,16 @@ 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)
|
||||
img->pcf &= ~(PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -299,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)
|
||||
@@ -336,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);
|
||||
@@ -359,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) {
|
||||
@@ -485,23 +492,23 @@ static void pv_hdr_key_slot_free(PvHdrKeySlot *slot)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free)
|
||||
|
||||
static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||||
const Buffer *cust_root_key,
|
||||
const PvBuffer *cust_root_key,
|
||||
EVP_PKEY *cust_key, EVP_PKEY *host_key,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1);
|
||||
g_autofree union ecdh_pub_key *pub = NULL;
|
||||
g_autoptr(Buffer) exchange_key = NULL;
|
||||
g_autoptr(Buffer) digest_key = NULL;
|
||||
g_autoptr(Buffer) iv = NULL;
|
||||
Buffer pub_buf;
|
||||
g_autoptr(PvBuffer) exchange_key = NULL;
|
||||
g_autoptr(PvBuffer) digest_key = NULL;
|
||||
g_autoptr(PvBuffer) iv = NULL;
|
||||
PvBuffer pub_buf;
|
||||
/* No AAD data is used */
|
||||
Buffer aad = { .data = NULL, .size = 0 };
|
||||
PvBuffer aad = { .data = NULL, .size = 0 };
|
||||
/* Set the output buffers for the encrypted data and the
|
||||
* generated GCM tag
|
||||
*/
|
||||
Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||||
Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||||
PvBuffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||||
PvBuffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||||
struct cipher_parms parms;
|
||||
int64_t c_len = 0;
|
||||
|
||||
@@ -530,7 +537,7 @@ static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||||
g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher));
|
||||
|
||||
/* create zero IV */
|
||||
iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||||
iv = pv_buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||||
parms.iv_or_tweak = iv;
|
||||
parms.key = exchange_key;
|
||||
parms.cipher = gcm_cipher;
|
||||
@@ -583,12 +590,13 @@ 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)
|
||||
return NULL;
|
||||
|
||||
ret->pcf = PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA | PV_PCF_PCKM_ECC;
|
||||
ret->cust_comm_cipher = EVP_aes_256_gcm();
|
||||
ret->gcm_cipher = EVP_aes_256_gcm();
|
||||
ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR;
|
||||
@@ -602,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, 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 */
|
||||
@@ -637,13 +647,13 @@ void pv_img_free(PvImage *img)
|
||||
g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free);
|
||||
g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free);
|
||||
EVP_PKEY_free(img->cust_pub_priv_key);
|
||||
buffer_clear(&img->stage3a);
|
||||
pv_buffer_clear(&img->stage3a);
|
||||
pv_img_comps_free(img->comps);
|
||||
g_free(img->tmp_dir);
|
||||
buffer_free(img->xts_key);
|
||||
buffer_free(img->cust_root_key);
|
||||
buffer_free(img->gcm_iv);
|
||||
buffer_free(img->cust_comm_key);
|
||||
pv_buffer_free(img->xts_key);
|
||||
pv_buffer_free(img->cust_root_key);
|
||||
pv_buffer_free(img->gcm_iv);
|
||||
pv_buffer_free(img->cust_comm_key);
|
||||
g_free(img);
|
||||
}
|
||||
|
||||
@@ -684,13 +694,13 @@ gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||||
Buffer **tld, uint64_t *nep, GError **err)
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||||
PvBuffer **tld, uint64_t *nep, GError **err)
|
||||
{
|
||||
return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err);
|
||||
}
|
||||
|
||||
static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
||||
static gint pv_img_build_stage3b(PvImage *img, PvBuffer *stage3b, GError **err)
|
||||
{
|
||||
g_autofree struct stage3b_args *args = NULL;
|
||||
|
||||
@@ -708,7 +718,7 @@ static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err)
|
||||
{
|
||||
g_autoptr(PvComponent) comp = NULL;
|
||||
g_autoptr(Buffer) stage3b = NULL;
|
||||
g_autoptr(PvBuffer) stage3b = NULL;
|
||||
|
||||
stage3b = stage3b_getblob(path, err);
|
||||
if (!stage3b)
|
||||
@@ -825,7 +835,7 @@ static gint get_stage3a_data_size(const PvImage *img, gsize *data_size,
|
||||
|
||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) stage3a = NULL;
|
||||
g_autoptr(PvBuffer) stage3a = NULL;
|
||||
gsize bin_size, data_size = 0;
|
||||
|
||||
if (get_stage3a_data_size(img, &data_size, err) < 0)
|
||||
@@ -845,8 +855,8 @@ gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||||
}
|
||||
|
||||
/* Creates the PV IPIB and sets the stage3a arguments */
|
||||
static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
||||
GSList *comps, const Buffer *hdr, GError **err)
|
||||
static gint pv_img_build_stage3a(PvBuffer *stage3a, gsize stage3a_bin_size,
|
||||
GSList *comps, const PvBuffer *hdr, GError **err)
|
||||
{
|
||||
g_autofree struct ipl_parameter_block *ipib = NULL;
|
||||
|
||||
@@ -866,9 +876,9 @@ static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
||||
}
|
||||
|
||||
/* Creates the actual PV header (serialized and AES-GCM encrypted) */
|
||||
static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||||
static PvBuffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) hdr_buf = NULL;
|
||||
g_autoptr(PvBuffer) hdr_buf = NULL;
|
||||
g_autoptr(PvHdr) hdr = NULL;
|
||||
|
||||
hdr = pv_hdr_new(img, err);
|
||||
@@ -887,7 +897,7 @@ static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||||
*/
|
||||
gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) hdr = NULL;
|
||||
g_autoptr(PvBuffer) hdr = NULL;
|
||||
|
||||
/* load stage3b template into memory and add it to the list of
|
||||
* components. This must be done before calling
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
typedef struct {
|
||||
gchar *tmp_dir; /* directory used for temporary files */
|
||||
Buffer *stage3a; /* stage3a containing IPIB and PV header */
|
||||
PvBuffer *stage3a; /* stage3a containing IPIB and PV header */
|
||||
gsize stage3a_bin_size; /* size of stage3a.bin */
|
||||
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
||||
* location 0 of the created image
|
||||
@@ -35,15 +35,15 @@ typedef struct {
|
||||
GSList *host_pub_keys; /* public host keys */
|
||||
gint nid; /* Elliptic Curve used for the key derivation */
|
||||
/* keys and cipher used for the AES-GCM encryption */
|
||||
Buffer *cust_root_key;
|
||||
Buffer *gcm_iv;
|
||||
PvBuffer *cust_root_key;
|
||||
PvBuffer *gcm_iv;
|
||||
const EVP_CIPHER *gcm_cipher;
|
||||
/* Information for the IPIB and PV header */
|
||||
uint64_t pcf;
|
||||
uint64_t scf;
|
||||
Buffer *cust_comm_key;
|
||||
PvBuffer *cust_comm_key;
|
||||
const EVP_CIPHER *cust_comm_cipher;
|
||||
Buffer *xts_key;
|
||||
PvBuffer *xts_key;
|
||||
const EVP_CIPHER *xts_cipher;
|
||||
GSList *key_slots;
|
||||
GSList *optional_items;
|
||||
@@ -54,8 +54,8 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err);
|
||||
void pv_img_free(PvImage *img);
|
||||
gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err);
|
||||
gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err);
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||||
Buffer **tld, uint64_t *nep, GError **err);
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||||
PvBuffer **tld, uint64_t *nep, GError **err);
|
||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
||||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
||||
|
||||
@@ -35,7 +35,7 @@ uint64_t pv_ipib_get_size(uint32_t num_comp)
|
||||
}
|
||||
|
||||
static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||||
const Buffer *hdr)
|
||||
const PvBuffer *hdr)
|
||||
{
|
||||
g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX);
|
||||
g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX);
|
||||
@@ -100,7 +100,7 @@ static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||||
return 0;
|
||||
}
|
||||
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err)
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err)
|
||||
{
|
||||
uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps));
|
||||
g_autoptr(IplParameterBlock) ret = NULL;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
typedef struct ipl_parameter_block IplParameterBlock;
|
||||
|
||||
uint64_t pv_ipib_get_size(uint32_t num_comp);
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err);
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err);
|
||||
void pv_ipib_free(IplParameterBlock *ipib);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \
|
||||
sizeof(struct stage3a_args)))
|
||||
|
||||
static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize args_size, gsize data_size,
|
||||
gboolean data_aligned, GError **err)
|
||||
static PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize args_size, gsize data_size,
|
||||
gboolean data_aligned, GError **err)
|
||||
{
|
||||
g_autoptr(GMappedFile) mapped_file = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
gsize size, tmp_loader_size;
|
||||
gchar *loader_data;
|
||||
|
||||
@@ -60,7 +60,7 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||
size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) +
|
||||
data_size;
|
||||
|
||||
ret = buffer_alloc(size);
|
||||
ret = pv_buffer_alloc(size);
|
||||
|
||||
/* copy the loader "template" */
|
||||
memcpy(ret->data, loader_data, tmp_loader_size);
|
||||
@@ -71,8 +71,8 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err)
|
||||
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err)
|
||||
{
|
||||
return loader_getblob(filename, loader_size,
|
||||
sizeof(struct stage3a_args), data_size, TRUE,
|
||||
@@ -83,8 +83,8 @@ Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
/* Set the right offsets and sizes in the stage3a template + add
|
||||
* the IPIB block with the PV header
|
||||
*/
|
||||
static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
||||
const Buffer *hdr, struct ipl_parameter_block *ipib,
|
||||
static gint stage3a_set_data(PvBuffer *loader, gsize loader_size,
|
||||
const PvBuffer *hdr, struct ipl_parameter_block *ipib,
|
||||
GError **err)
|
||||
{
|
||||
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
||||
@@ -126,15 +126,15 @@ static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr,
|
||||
gint build_stage3a(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err)
|
||||
{
|
||||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
||||
}
|
||||
|
||||
Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
gsize rb_size;
|
||||
|
||||
ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0,
|
||||
@@ -146,7 +146,7 @@ Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args)
|
||||
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args)
|
||||
{
|
||||
g_assert(stage3b->size > sizeof(*args));
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
#include "boot/stage3b.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err);
|
||||
gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr,
|
||||
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err);
|
||||
gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err);
|
||||
Buffer *stage3b_getblob(const gchar *filename, GError **err);
|
||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args);
|
||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err);
|
||||
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args);
|
||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,18 +17,18 @@
|
||||
#include "common.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
Buffer *buffer_alloc(gsize size)
|
||||
PvBuffer *pv_buffer_alloc(gsize size)
|
||||
{
|
||||
Buffer *ret = g_new0(Buffer, 1);
|
||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
||||
|
||||
ret->data = g_malloc0(size);
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
||||
{
|
||||
Buffer *ret;
|
||||
PvBuffer *ret;
|
||||
gsize size;
|
||||
|
||||
if (!buf)
|
||||
@@ -38,19 +38,19 @@ Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
||||
if (page_aligned)
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
ret = buffer_alloc(size);
|
||||
ret = pv_buffer_alloc(size);
|
||||
|
||||
/* content will be 0-right-padded */
|
||||
memcpy(ret->data, buf->data, buf->size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err)
|
||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err)
|
||||
{
|
||||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
||||
}
|
||||
|
||||
void buffer_free(Buffer *buf)
|
||||
void pv_buffer_free(PvBuffer *buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
@@ -59,11 +59,11 @@ void buffer_free(Buffer *buf)
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
void buffer_clear(Buffer **buf)
|
||||
void pv_buffer_clear(PvBuffer **buf)
|
||||
{
|
||||
if (!buf || !*buf)
|
||||
return;
|
||||
|
||||
buffer_free(*buf);
|
||||
pv_buffer_free(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct Buffer {
|
||||
typedef struct PvBuffer {
|
||||
void *data;
|
||||
gsize size; /* in bytes */
|
||||
} Buffer;
|
||||
} PvBuffer;
|
||||
|
||||
Buffer *buffer_alloc(gsize size);
|
||||
void buffer_free(Buffer *buf);
|
||||
void buffer_clear(Buffer **buf);
|
||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err);
|
||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned);
|
||||
PvBuffer *pv_buffer_alloc(gsize size);
|
||||
void pv_buffer_free(PvBuffer *buf);
|
||||
void pv_buffer_clear(PvBuffer **buf);
|
||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "buffer.h"
|
||||
#include "curl.h"
|
||||
#include "openssl_compat.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#define DEFINE_GSLIST_MAP(t2, t1) \
|
||||
@@ -89,15 +90,15 @@ EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err)
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||||
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||||
{
|
||||
gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
guint digest_size;
|
||||
|
||||
g_assert(md_size > 0);
|
||||
|
||||
ret = buffer_alloc((guint)md_size);
|
||||
ret = pv_buffer_alloc((guint)md_size);
|
||||
if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestFinal_ex failed"));
|
||||
@@ -110,10 +111,10 @@ Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||||
}
|
||||
|
||||
/* Returns the digest of @buf using the hash algorithm @md */
|
||||
static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
||||
static PvBuffer *digest_buffer(const EVP_MD *md, const PvBuffer *buf, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_MD_CTX) md_ctx = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
g_assert(buf);
|
||||
|
||||
md_ctx = digest_ctx_new(md, err);
|
||||
@@ -134,9 +135,9 @@ static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
||||
}
|
||||
|
||||
/* Returns the SHA256 digest of @buf */
|
||||
Buffer *sha256_buffer(const Buffer *buf, GError **err)
|
||||
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
|
||||
ret = digest_buffer(EVP_sha256(), buf, err);
|
||||
if (!ret)
|
||||
@@ -207,10 +208,10 @@ union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err)
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
static PvBuffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
gsize key_size;
|
||||
|
||||
ctx = EVP_PKEY_CTX_new(cust, NULL);
|
||||
@@ -236,7 +237,7 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = buffer_alloc(key_size);
|
||||
ret = pv_buffer_alloc(key_size);
|
||||
if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||
_("Key derivation failed"));
|
||||
@@ -247,11 +248,11 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) raw = buffer_alloc(70);
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(Buffer) key = NULL;
|
||||
g_autoptr(PvBuffer) raw = pv_buffer_alloc(70);
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) key = NULL;
|
||||
guchar *data;
|
||||
|
||||
key = derive_key(cust, host, err);
|
||||
@@ -290,10 +291,10 @@ gint generate_tweak(union tweak *tweak, uint16_t i, GError **err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
||||
GError **err)
|
||||
static PvBuffer *generate_rand_data(guint size, const gchar *err_msg,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) buf = buffer_alloc(size);
|
||||
g_autoptr(PvBuffer) buf = pv_buffer_alloc(size);
|
||||
|
||||
g_assert(size <= INT_MAX);
|
||||
|
||||
@@ -307,14 +308,14 @@ static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
||||
return g_steal_pointer(&buf);
|
||||
}
|
||||
|
||||
Buffer *generate_aes_iv(guint size, GError **err)
|
||||
PvBuffer *generate_aes_iv(guint size, GError **err)
|
||||
{
|
||||
return generate_rand_data(size,
|
||||
_("Generating a IV failed because the required amount of random data is not available"),
|
||||
err);
|
||||
}
|
||||
|
||||
Buffer *generate_aes_key(guint size, GError **err)
|
||||
PvBuffer *generate_aes_key(guint size, GError **err)
|
||||
{
|
||||
return generate_rand_data(size,
|
||||
_("Generating a key failed because the required amount of random data is not available"),
|
||||
@@ -439,11 +440,15 @@ 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)
|
||||
return g_steal_pointer(&crl);
|
||||
ERR_clear_error();
|
||||
BIO_reset(bio);
|
||||
rc = BIO_reset(bio);
|
||||
if (rc != 1 || (rc != 0 && BIO_method_type(bio) == BIO_TYPE_FILE))
|
||||
return NULL;
|
||||
|
||||
/* maybe the CRL is stored in DER format */
|
||||
crl = d2i_X509_CRL_bio(bio, NULL);
|
||||
@@ -514,6 +519,7 @@ X509 *load_cert_from_file(const char *path, GError **err)
|
||||
{
|
||||
g_autoptr(BIO) bio = bio_read_from_file(path);
|
||||
g_autoptr(X509) cert = NULL;
|
||||
gint rc;
|
||||
|
||||
if (!bio) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
@@ -526,7 +532,12 @@ X509 *load_cert_from_file(const char *path, GError **err)
|
||||
if (cert)
|
||||
return g_steal_pointer(&cert);
|
||||
ERR_clear_error();
|
||||
BIO_reset(bio);
|
||||
rc = BIO_reset(bio);
|
||||
if (rc != 1 || (rc != 0 && BIO_method_type(bio) == BIO_TYPE_FILE)) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
_("unable to load certificate: '%s'"), path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* maybe the certificate is stored in DER format */
|
||||
cert = d2i_X509_bio(bio, NULL);
|
||||
@@ -1068,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);
|
||||
|
||||
@@ -1256,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);
|
||||
@@ -1305,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;
|
||||
}
|
||||
|
||||
@@ -1428,7 +1433,7 @@ static const char *get_first_dp_url(DIST_POINT *dp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean insert_crl(X509_NAME *name, X509_CRL *crl)
|
||||
static gboolean insert_crl(const X509_NAME *name, X509_CRL *crl)
|
||||
{
|
||||
g_autofree gchar *key = NULL;
|
||||
|
||||
@@ -1443,7 +1448,7 @@ static gboolean insert_crl(X509_NAME *name, X509_CRL *crl)
|
||||
}
|
||||
|
||||
/* Caller is responsible for free'ing */
|
||||
static X509_CRL *lookup_crl(X509_NAME *name)
|
||||
static X509_CRL *lookup_crl(const X509_NAME *name)
|
||||
{
|
||||
g_autoptr(X509_CRL) crl = NULL;
|
||||
g_autofree gchar *key = NULL;
|
||||
@@ -1463,7 +1468,7 @@ static X509_CRL *lookup_crl(X509_NAME *name)
|
||||
}
|
||||
|
||||
/* Returns empty stack if no CRL downloaded. */
|
||||
static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
||||
static STACK_OF_X509_CRL *crls_download_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm)
|
||||
{
|
||||
g_autoptr(STACK_OF_X509_CRL) crls = NULL;
|
||||
g_autoptr(X509_CRL) crl = NULL;
|
||||
@@ -1473,7 +1478,7 @@ static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
||||
crls = sk_X509_CRL_new_null();
|
||||
if (!crls)
|
||||
g_abort();
|
||||
cert = X509_STORE_CTX_get_current_cert(ctx);
|
||||
cert = Pv_X509_STORE_CTX_get_current_cert(ctx);
|
||||
if (!cert)
|
||||
return g_steal_pointer(&crls);
|
||||
g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
|
||||
@@ -1517,19 +1522,19 @@ void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack)
|
||||
/* Downloaded CRLs have a higher precedence than the CRLs specified on the
|
||||
* command line.
|
||||
*/
|
||||
static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
||||
static STACK_OF_X509_CRL *crls_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm)
|
||||
{
|
||||
g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm);
|
||||
|
||||
if (sk_X509_CRL_num(crls) > 0)
|
||||
return g_steal_pointer(&crls);
|
||||
return X509_STORE_CTX_get1_crls(ctx, nm);
|
||||
return Pv_X509_STORE_CTX_get1_crls(ctx, nm);
|
||||
}
|
||||
|
||||
/* Set up CRL lookup with download support */
|
||||
void store_setup_crl_download(X509_STORE *st)
|
||||
{
|
||||
X509_STORE_set_lookup_crls(st, crls_cb);
|
||||
Pv_X509_STORE_set_lookup_crls(st, crls_cb);
|
||||
}
|
||||
|
||||
/* Download a CRL using the URI specified in the distribution @crldp */
|
||||
@@ -1645,8 +1650,8 @@ gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
||||
}
|
||||
|
||||
if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) {
|
||||
const ASN1_TIME *last = X509_get_notBefore(host_key);
|
||||
const ASN1_TIME *next = X509_get_notAfter(host_key);
|
||||
const ASN1_TIME *last = X509_get0_notBefore(host_key);
|
||||
const ASN1_TIME *next = X509_get0_notAfter(host_key);
|
||||
|
||||
if (!last || !next || check_validity_period(last, next)) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
@@ -1756,8 +1761,8 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
gint cipher_block_size = EVP_CIPHER_block_size(cipher);
|
||||
guchar in_buf[PAGE_SIZE],
|
||||
out_buf[PAGE_SIZE + (guint)cipher_block_size];
|
||||
const Buffer *key = parms->key;
|
||||
const Buffer *tweak = parms->iv_or_tweak;
|
||||
const PvBuffer *key = parms->key;
|
||||
const PvBuffer *tweak = parms->iv_or_tweak;
|
||||
g_autofree guchar *tmp_tweak = NULL;
|
||||
gint out_len, tweak_size;
|
||||
gsize tmp_size_in = 0, tmp_size_out = 0;
|
||||
@@ -1847,6 +1852,7 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_add_word failed"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(BN_num_bytes(tweak_num) > 0);
|
||||
g_assert(BN_num_bytes(tweak_num) <= tweak_size);
|
||||
@@ -1855,6 +1861,7 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bn2binpad failed"));
|
||||
return -1;
|
||||
};
|
||||
|
||||
/* set new tweak */
|
||||
@@ -1895,11 +1902,11 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||||
const Buffer *in, gboolean encrypt,
|
||||
GError **err)
|
||||
static PvBuffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||||
const PvBuffer *in, gboolean encrypt,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(PvBuffer) ret = NULL;
|
||||
g_autoptr(BIO) b_out = NULL;
|
||||
g_autoptr(BIO) b_in = NULL;
|
||||
gsize in_size, out_size;
|
||||
@@ -1927,19 +1934,19 @@ static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = buffer_alloc((unsigned long)data_size);
|
||||
ret = pv_buffer_alloc((unsigned long)data_size);
|
||||
memcpy(ret->data, data, ret->size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err)
|
||||
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_buffer(parms, in, TRUE, err);
|
||||
}
|
||||
|
||||
Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err)
|
||||
PvBuffer *decrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_buffer(parms, in, FALSE, err);
|
||||
}
|
||||
@@ -1993,16 +2000,16 @@ G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms,
|
||||
}
|
||||
|
||||
/* GCM mode uses (zero-)padding */
|
||||
static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
||||
static int64_t gcm_encrypt_decrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||
const struct cipher_parms *parms,
|
||||
Buffer *out, Buffer *tag,
|
||||
PvBuffer *out, PvBuffer *tag,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
||||
const EVP_CIPHER *cipher = parms->cipher;
|
||||
const Buffer *iv = parms->iv_or_tweak;
|
||||
const PvBuffer *iv = parms->iv_or_tweak;
|
||||
gboolean encrypt = mode == PV_ENCRYPT;
|
||||
const Buffer *key = parms->key;
|
||||
const PvBuffer *key = parms->key;
|
||||
int64_t ret = -1;
|
||||
gint len = -1;
|
||||
|
||||
@@ -2097,8 +2104,8 @@ static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||||
const struct cipher_parms *parms, Buffer *out, Buffer *tag,
|
||||
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||
const struct cipher_parms *parms, PvBuffer *out, PvBuffer *tag,
|
||||
GError **err)
|
||||
{
|
||||
return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err);
|
||||
|
||||
@@ -117,15 +117,14 @@ union tweak {
|
||||
|
||||
struct cipher_parms {
|
||||
const EVP_CIPHER *cipher;
|
||||
const Buffer *key;
|
||||
const Buffer *iv_or_tweak;
|
||||
const PvBuffer *key;
|
||||
const PvBuffer *iv_or_tweak;
|
||||
};
|
||||
|
||||
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
gint verify_flags, GError **err);
|
||||
void pv_crypto_init(void);
|
||||
void pv_crypto_cleanup(void);
|
||||
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);
|
||||
@@ -152,24 +150,24 @@ X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
|
||||
void store_setup_crl_download(X509_STORE *st);
|
||||
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
||||
|
||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||||
Buffer *generate_aes_key(guint size, GError **err);
|
||||
Buffer *generate_aes_iv(guint size, GError **err);
|
||||
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||||
PvBuffer *generate_aes_key(guint size, GError **err);
|
||||
PvBuffer *generate_aes_iv(guint size, GError **err);
|
||||
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
||||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
||||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
||||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||||
Buffer *sha256_buffer(const Buffer *buf, GError **err);
|
||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||||
const struct cipher_parms *parms, Buffer *out,
|
||||
Buffer *tag, GError **err);
|
||||
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||||
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err);
|
||||
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||
const struct cipher_parms *parms, PvBuffer *out,
|
||||
PvBuffer *tag, GError **err);
|
||||
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
||||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
||||
GError **err);
|
||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err);
|
||||
G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms,
|
||||
const Buffer *in, GError **err);
|
||||
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||
GError **err);
|
||||
G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms,
|
||||
const PvBuffer *in, GError **err);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -171,13 +171,13 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset,
|
||||
gint seek_and_write_buffer(FILE *o, const PvBuffer *buf, uint64_t offset,
|
||||
GError **err)
|
||||
{
|
||||
if (file_seek(o, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (buffer_write(buf, o, err) < 0)
|
||||
if (pv_buffer_write(buf, o, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -26,7 +26,7 @@ gint file_write(FILE *out, const void *ptr, gsize size, gsize count,
|
||||
gsize *count_written, GError **err);
|
||||
gint pad_file_right(const gchar *path_out, const gchar *path_in,
|
||||
gsize *size_out, guint padding, GError **err);
|
||||
gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset,
|
||||
gint seek_and_write_buffer(FILE *out, const PvBuffer *buf, uint64_t offset,
|
||||
GError **err);
|
||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||||
GError **err);
|
||||
|
||||
33
genprotimg/src/utils/openssl_compat.h
Normal file
33
genprotimg/src/utils/openssl_compat.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* OpenSSL compatibility utils
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef PV_UTILS_OPENSSL_COMPAT_H
|
||||
#define PV_UTILS_OPENSSL_COMPAT_H
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||
X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx))
|
||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||
X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm))
|
||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||
X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb))
|
||||
#else
|
||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||
X509_STORE_CTX_get_current_cert(ctx)
|
||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||
X509_STORE_CTX_get1_crls(ctx, nm)
|
||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||
X509_STORE_set_lookup_crls(st, cb)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
|
||||
88
hsavmcore/Makefile
Normal file
88
hsavmcore/Makefile
Normal file
@@ -0,0 +1,88 @@
|
||||
#
|
||||
# Copyright IBM Corp. 2021
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
|
||||
include ../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
|
||||
ifeq (${HAVE_FUSE},0)
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_FUSE=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_FUSE=0
|
||||
|
||||
else # HAVE_FUSE
|
||||
|
||||
#
|
||||
# FUSE
|
||||
#
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
|
||||
#
|
||||
# systemd
|
||||
#
|
||||
ifneq (${HAVE_SYSTEMD},0)
|
||||
ifeq ($(call check_header_prereq,"systemd/sd-daemon.h"),yes)
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
SYSTEMD_CFLAGS = $(shell pkg-config --silence-errors --cflags libsystemd)
|
||||
SYSTEMD_LDLIBS = $(shell pkg-config --silence-errors --libs libsystemd)
|
||||
else
|
||||
SYSTEMD_CFLAGS =
|
||||
SYSTEMD_LDLIBS = -lsystemd
|
||||
endif
|
||||
ALL_CPPFLAGS += -DHAVE_SYSTEMD
|
||||
else
|
||||
$(warning "systemd support disabled")
|
||||
endif
|
||||
endif
|
||||
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread
|
||||
|
||||
sources := $(wildcard *.c)
|
||||
objects := $(patsubst %.c,%.o,$(sources))
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
all: hsavmcore
|
||||
|
||||
hsavmcore: $(objects) $(libs)
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
overlay.o: check-dep-fuse overlay.c overlay.h
|
||||
|
||||
check-dep-fuse:
|
||||
$(call check_dep, \
|
||||
"hsavmcore", \
|
||||
"fuse.h", \
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
touch check-dep-fuse
|
||||
|
||||
install: all
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
|
||||
$(DESTDIR)$(USRSBINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.8 \
|
||||
$(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.conf.5 \
|
||||
$(DESTDIR)$(MANDIR)/man5
|
||||
|
||||
endif # HAVE_FUSE
|
||||
|
||||
clean:
|
||||
rm -f hsavmcore $(objects) check-dep-fuse
|
||||
|
||||
.PHONY: all install clean
|
||||
219
hsavmcore/cmdline_options.c
Normal file
219
hsavmcore/cmdline_options.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "cmdline_options.h"
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "hsavmcore is designed to make the dump process with kdump more "
|
||||
"efficient. The HSA memory contains a part of the production "
|
||||
"kernel's memory. Use hsavmcore to cache this information and "
|
||||
"release HSA memory early in the process.",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2021,
|
||||
.pub_last = 2021,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("CONFIGURATION"),
|
||||
{
|
||||
.option = { "config", required_argument, NULL, 'c' },
|
||||
.argument = "CONFIGFILE",
|
||||
.desc = "Path to the configuration file.\n"
|
||||
"Default: no configuration file is used",
|
||||
},
|
||||
{
|
||||
.option = { "vmcore", required_argument, NULL, 'C' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the vmcore file.\n"
|
||||
"Default: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "hsa", required_argument, NULL, 'H' },
|
||||
.argument = "ZCOREHSAFILE",
|
||||
.desc = "Path to the zcore HSA file.\n"
|
||||
"Default: " ZCORE_HSA,
|
||||
},
|
||||
{
|
||||
.option = { "workdir", required_argument, NULL, 'W' },
|
||||
.argument = "WORKDIR",
|
||||
.desc = "Path to the work directory where temporary files can be "
|
||||
"stored.\nDefault: " WORKDIR,
|
||||
},
|
||||
{
|
||||
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the target of the bind mount for the vmcore "
|
||||
"replacement.\nDefault: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "swap", required_argument, NULL, 'S' },
|
||||
.argument = "PATH",
|
||||
.desc = "Path to a swap device or file. The specified swap "
|
||||
"device or file must exist and have the proper swap "
|
||||
"format.\nDefault: no swap device or file is activated",
|
||||
},
|
||||
{
|
||||
.option = { "hsasize", required_argument, NULL, 'T' },
|
||||
.argument = "HSASIZE",
|
||||
.desc = "HSA size in bytes.\n"
|
||||
"Default: -1 (read from the zcore HSA file)",
|
||||
},
|
||||
{
|
||||
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
||||
.desc = "Mount the debug file system.\n"
|
||||
"Default: the debug file system is not mounted",
|
||||
},
|
||||
{
|
||||
.option = { "hsamem", no_argument, NULL, 'F' },
|
||||
.desc = "Cache the HSA memory in regular memory.\n"
|
||||
"Default: the HSA memory is cached as a file within "
|
||||
"WORKDIR",
|
||||
},
|
||||
{
|
||||
.option = { "norelhsa", no_argument, NULL, 'R' },
|
||||
.desc = "Do NOT release the HSA memory after caching.\n"
|
||||
"Default: the HSA memory is released",
|
||||
},
|
||||
{
|
||||
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
||||
.desc = "Do NOT replace the system's vmcore.\n"
|
||||
"Default: the system's vmcore is replaced",
|
||||
},
|
||||
UTIL_OPT_SECTION("LOGGING"),
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Print verbose messages to stdout. Repeat this option "
|
||||
"for increased verbosity from just error messages to "
|
||||
"also include warning, information, debug, and trace "
|
||||
"messages. This option is intended for debugging",
|
||||
},
|
||||
{
|
||||
.option = { "fusedbg", no_argument, NULL, 'G' },
|
||||
.desc = "Enable FUSE debugging.\n"
|
||||
"Default: FUSE debugging is disabled",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
void parse_cmdline_options(int argc, char *argv[], struct config *config)
|
||||
{
|
||||
int opt, ret;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
/* Parse given command-line config */
|
||||
while (1) {
|
||||
opt = util_opt_getopt_long(argc, argv);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'V':
|
||||
config->verbose++;
|
||||
util_log_set_level(config->verbose);
|
||||
break;
|
||||
case 'c':
|
||||
ret = update_config_from_file(optarg, config);
|
||||
if (ret < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
util_log_set_level(config->verbose);
|
||||
break;
|
||||
case 'C':
|
||||
strncpy(config->vmcore_path, optarg,
|
||||
sizeof(config->vmcore_path) - 1);
|
||||
/* Ensure null termination */
|
||||
config->vmcore_path[sizeof(config->vmcore_path) - 1] =
|
||||
'\0';
|
||||
break;
|
||||
case 'H':
|
||||
strncpy(config->zcore_hsa_path, optarg,
|
||||
sizeof(config->zcore_hsa_path) - 1);
|
||||
/* Ensure null termination */
|
||||
config->zcore_hsa_path[sizeof(config->zcore_hsa_path) -
|
||||
1] = '\0';
|
||||
break;
|
||||
case 'W':
|
||||
strncpy(config->workdir_path, optarg,
|
||||
sizeof(config->workdir_path) - 1);
|
||||
/* Ensure null termination */
|
||||
config->workdir_path[sizeof(config->workdir_path) - 1] =
|
||||
'\0';
|
||||
break;
|
||||
case 'B':
|
||||
strncpy(config->bind_mount_vmcore_path, optarg,
|
||||
sizeof(config->bind_mount_vmcore_path) - 1);
|
||||
/* Ensure null termination */
|
||||
config->bind_mount_vmcore_path
|
||||
[sizeof(config->bind_mount_vmcore_path) - 1] =
|
||||
'\0';
|
||||
break;
|
||||
case 'S':
|
||||
strncpy(config->swap, optarg, sizeof(config->swap) - 1);
|
||||
/* Ensure null termination */
|
||||
config->swap[sizeof(config->swap) - 1] = '\0';
|
||||
break;
|
||||
case 'T': {
|
||||
char *endptr;
|
||||
long hsa_size = strtol(optarg, &endptr, 0);
|
||||
|
||||
if (*endptr != '\0' || hsa_size < -1 ||
|
||||
hsa_size > INT_MAX) {
|
||||
fprintf(stderr,
|
||||
"The given HSA size is invalid.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config->hsa_size = hsa_size;
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
config->mount_debugfs = true;
|
||||
break;
|
||||
case 'F':
|
||||
config->use_hsa_mem = true;
|
||||
break;
|
||||
case 'R':
|
||||
config->release_hsa = false;
|
||||
break;
|
||||
case 'N':
|
||||
config->bind_mount_vmcore = false;
|
||||
break;
|
||||
case 'G':
|
||||
config->fuse_debug = true;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
util_opt_print_parse_error(opt, argv);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
hsavmcore/cmdline_options.h
Normal file
19
hsavmcore/cmdline_options.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_CMDLINE_OPTIONS_H
|
||||
#define _HSAVMCORE_CMDLINE_OPTIONS_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/*
|
||||
* Parses the given command-line options and adjusts the application's
|
||||
* configuration accordingly.
|
||||
*/
|
||||
void parse_cmdline_options(int argc, char *argv[], struct config *config);
|
||||
|
||||
#endif
|
||||
27
hsavmcore/common.h
Normal file
27
hsavmcore/common.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_COMMON_H
|
||||
#define _HSAVMCORE_COMMON_H
|
||||
|
||||
#define NAME "hsavmcore"
|
||||
|
||||
#define DEBUGFS_MOUNT_POINT "/sys/kernel/debug"
|
||||
|
||||
#define ZCORE_HSA DEBUGFS_MOUNT_POINT "/zcore/hsa"
|
||||
|
||||
#define VMCORE_FILE "vmcore"
|
||||
|
||||
#define PROC_VMCORE "/proc/" VMCORE_FILE
|
||||
|
||||
#define WORKDIR "/var/crash"
|
||||
|
||||
#define HSA_CACHE_FILE NAME "-hsa-cache.bin"
|
||||
|
||||
#define OVERLAY_MOUNT_POINT "/tmp/" NAME "-overlay/"
|
||||
|
||||
#endif
|
||||
242
hsavmcore/config.c
Normal file
242
hsavmcore/config.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define CONFIG_LINE_MAX_SIZE 1024
|
||||
|
||||
/*
|
||||
* Supported configuration parameters
|
||||
*/
|
||||
#define CONFIG_VERBOSE "verbose"
|
||||
#define CONFIG_WORKDIR "workdir"
|
||||
#define CONFIG_HSA_SIZE "hsa_size"
|
||||
#define CONFIG_MOUNT_DEBUGFS "mount_debugfs"
|
||||
#define CONFIG_USE_HSA_MEM "use_hsa_mem"
|
||||
#define CONFIG_RELEASE_HSA "release_hsa"
|
||||
#define CONFIG_BIND_MOUNT_VMCORE "bind_mount_vmcore"
|
||||
#define CONFIG_FUSE_DEBUG "fuse_debug"
|
||||
#define CONFIG_SWAP "swap"
|
||||
|
||||
static char *get_value_str(char *line)
|
||||
{
|
||||
char *ptr = strchr(line, '=');
|
||||
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
return util_strstrip(ptr + 1);
|
||||
}
|
||||
|
||||
static int parse_bool(char *line, int linenum, const char *name, bool *value)
|
||||
{
|
||||
const char *value_str;
|
||||
unsigned long value_num;
|
||||
char *endptr;
|
||||
|
||||
value_str = get_value_str(line);
|
||||
if (!value_str) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: value expected for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "config parse bool: %s\n", value_str);
|
||||
|
||||
value_num = strtoul(value_str, &endptr, 0);
|
||||
if (*endptr != '\0' || (value_num != 0 && value_num != 1)) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: invalid value for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = value_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_int(char *line, int linenum, const char *name, int min_value,
|
||||
int max_value, int *value)
|
||||
{
|
||||
const char *value_str;
|
||||
long value_num;
|
||||
char *endptr;
|
||||
|
||||
value_str = get_value_str(line);
|
||||
if (!value_str) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: value expected for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "config parse int: %s\n", value_str);
|
||||
|
||||
value_num = strtol(value_str, &endptr, 0);
|
||||
if (*endptr != '\0' || value_num < min_value || value_num > max_value) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: invalid value for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = value_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_str(char *line, int linenum, const char *name, int min_size,
|
||||
int max_size, char *value)
|
||||
{
|
||||
const char *value_str;
|
||||
int size;
|
||||
|
||||
value_str = get_value_str(line);
|
||||
if (!value_str) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: value expected for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "config parse string: %s\n", value_str);
|
||||
|
||||
size = strlen(value_str);
|
||||
if ((min_size >= 0 && size < min_size) || size > max_size) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: invalid value for %s\n",
|
||||
linenum, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(value, value_str, max_size);
|
||||
/* Ensure null termination */
|
||||
value[max_size] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_line(char *line, int linenum, struct config *config)
|
||||
{
|
||||
if (strncmp(line, CONFIG_VERBOSE, strlen(CONFIG_VERBOSE)) == 0) {
|
||||
if (parse_int(line, linenum, CONFIG_VERBOSE, UTIL_LOG_ERROR,
|
||||
UTIL_LOG_TRACE, &config->verbose))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_WORKDIR, strlen(CONFIG_WORKDIR)) == 0) {
|
||||
if (parse_str(line, linenum, CONFIG_WORKDIR, 0,
|
||||
sizeof(config->workdir_path) - 1,
|
||||
config->workdir_path))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_HSA_SIZE, strlen(CONFIG_HSA_SIZE)) ==
|
||||
0) {
|
||||
if (parse_int(line, linenum, CONFIG_HSA_SIZE, -1, INT_MAX,
|
||||
&config->hsa_size))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_MOUNT_DEBUGFS,
|
||||
strlen(CONFIG_MOUNT_DEBUGFS)) == 0) {
|
||||
if (parse_bool(line, linenum, CONFIG_MOUNT_DEBUGFS,
|
||||
&config->mount_debugfs))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_USE_HSA_MEM,
|
||||
strlen(CONFIG_USE_HSA_MEM)) == 0) {
|
||||
if (parse_bool(line, linenum, CONFIG_USE_HSA_MEM,
|
||||
&config->use_hsa_mem))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_RELEASE_HSA,
|
||||
strlen(CONFIG_RELEASE_HSA)) == 0) {
|
||||
if (parse_bool(line, linenum, CONFIG_RELEASE_HSA,
|
||||
&config->release_hsa))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_BIND_MOUNT_VMCORE,
|
||||
strlen(CONFIG_BIND_MOUNT_VMCORE)) == 0) {
|
||||
if (parse_bool(line, linenum, CONFIG_BIND_MOUNT_VMCORE,
|
||||
&config->bind_mount_vmcore))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_FUSE_DEBUG,
|
||||
strlen(CONFIG_FUSE_DEBUG)) == 0) {
|
||||
if (parse_bool(line, linenum, CONFIG_FUSE_DEBUG,
|
||||
&config->fuse_debug))
|
||||
return -1;
|
||||
} else if (strncmp(line, CONFIG_SWAP, strlen(CONFIG_SWAP)) == 0) {
|
||||
if (parse_str(line, linenum, CONFIG_SWAP, 0,
|
||||
sizeof(config->swap) - 1, config->swap))
|
||||
return -1;
|
||||
} else {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"config line %d: unknown configuration '%s'\n",
|
||||
linenum, line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_config(struct config *config)
|
||||
{
|
||||
memset(config, 0, sizeof(struct config));
|
||||
strncpy(config->vmcore_path, PROC_VMCORE,
|
||||
sizeof(config->vmcore_path) - 1);
|
||||
strncpy(config->zcore_hsa_path, ZCORE_HSA,
|
||||
sizeof(config->zcore_hsa_path) - 1);
|
||||
strncpy(config->workdir_path, WORKDIR,
|
||||
sizeof(config->workdir_path) - 1);
|
||||
strncpy(config->bind_mount_vmcore_path, PROC_VMCORE,
|
||||
sizeof(config->bind_mount_vmcore_path) - 1);
|
||||
config->hsa_size = -1;
|
||||
config->mount_debugfs = false;
|
||||
config->use_hsa_mem = false;
|
||||
config->release_hsa = true;
|
||||
config->bind_mount_vmcore = true;
|
||||
config->fuse_debug = false;
|
||||
}
|
||||
|
||||
int update_config_from_file(const char *config_path, struct config *config)
|
||||
{
|
||||
char line[CONFIG_LINE_MAX_SIZE], *ptr;
|
||||
int ret = 0, linenum;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(config_path, "r");
|
||||
if (!fp) {
|
||||
util_log_print(UTIL_LOG_ERROR, "Couldn't open config file %s\n",
|
||||
config_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the given configuration file linewise and parse parameters */
|
||||
linenum = 0;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
linenum++;
|
||||
|
||||
ptr = util_strstrip(line);
|
||||
/* Skip empty or comment lines */
|
||||
if (ptr[0] == '\0' || ptr[0] == '#')
|
||||
continue;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "config line %d: %s\n", linenum,
|
||||
ptr);
|
||||
|
||||
ret = parse_line(ptr, linenum, config);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
59
hsavmcore/config.h
Normal file
59
hsavmcore/config.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_CONFIG_H
|
||||
#define _HSAVMCORE_CONFIG_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* This represents the application's configuration.
|
||||
*/
|
||||
struct config {
|
||||
/* Log message level */
|
||||
int verbose;
|
||||
/* Path to the system's vmcore file */
|
||||
char vmcore_path[PATH_MAX];
|
||||
/* Path to the system's zcore hsa file */
|
||||
char zcore_hsa_path[PATH_MAX];
|
||||
/*
|
||||
* Path to a directory where the application could create temporary
|
||||
* files.
|
||||
*/
|
||||
char workdir_path[PATH_MAX];
|
||||
/* Path to a bind-mount target for vmcore Overlay */
|
||||
char bind_mount_vmcore_path[PATH_MAX];
|
||||
/* Path to a swap device/file */
|
||||
char swap[PATH_MAX];
|
||||
/* HSA memory size */
|
||||
int hsa_size;
|
||||
/* Indicates whether the debugfs shall be mounted */
|
||||
bool mount_debugfs;
|
||||
/* Indicates whether the HSA memory file reader shall be used */
|
||||
bool use_hsa_mem;
|
||||
/* Indicates whether the HSA memory shall be released after caching */
|
||||
bool release_hsa;
|
||||
/* Indicates whether a bind-mount of vmcore Proxy shall be enabled */
|
||||
bool bind_mount_vmcore;
|
||||
/* Indicates whether the FUSE debug messages shall be enabled */
|
||||
bool fuse_debug;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes the application's configuration to its default values.
|
||||
*/
|
||||
void init_config(struct config *config);
|
||||
|
||||
/*
|
||||
* Updates the application's configuration from the given configuration file.
|
||||
*/
|
||||
int update_config_from_file(const char *config_path, struct config *config);
|
||||
|
||||
#endif
|
||||
172
hsavmcore/hsa.c
Normal file
172
hsavmcore/hsa.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <elf.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "hsa.h"
|
||||
|
||||
long get_hsa_size(const char *zcore_hsa_path)
|
||||
{
|
||||
long size;
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "Reading HSA memory size from %s\n",
|
||||
zcore_hsa_path);
|
||||
|
||||
/* Read HSA size */
|
||||
ret = util_file_read_l(&size, 16, zcore_hsa_path);
|
||||
if (ret < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "File read failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
long get_hsa_vmcore_offset(const char *vmcore_path)
|
||||
{
|
||||
Elf64_Ehdr elf_hdr;
|
||||
int fd = -1, n, i;
|
||||
long offset = -1;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"Reading HSA memory offset from vmcore %s\n",
|
||||
vmcore_path);
|
||||
|
||||
/* Open vmcore file */
|
||||
fd = open(vmcore_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "Reading vmcore ELF header\n");
|
||||
|
||||
/* Read ELF header */
|
||||
n = read(fd, &elf_hdr, sizeof(Elf64_Ehdr));
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
} else if (n != sizeof(Elf64_Ehdr)) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall read less data than expected (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Verify ELF header */
|
||||
if ((memcmp(elf_hdr.e_ident, ELFMAG, SELFMAG) != 0) ||
|
||||
elf_hdr.e_type != ET_CORE || elf_hdr.e_machine != EM_S390 ||
|
||||
elf_hdr.e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
util_log_print(UTIL_LOG_ERROR, "Invalid vmcore ELF header\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"Reading vmcore ELF program header(s)\n");
|
||||
|
||||
/* Read ELF program header(s) */
|
||||
n = lseek(fd, elf_hdr.e_phoff, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
/*
|
||||
* Go through all ELF program headers and find one
|
||||
* that starts at physical/virtual address 0x0.
|
||||
*/
|
||||
for (i = 0; i < elf_hdr.e_phnum; i++) {
|
||||
Elf64_Phdr elf_phdr;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"Reading vmcore ELF program header #%d\n", i);
|
||||
|
||||
n = read(fd, &elf_phdr, sizeof(Elf64_Phdr));
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
} else if (n != sizeof(Elf64_Phdr)) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall read less data than expected (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"vmcore ELF program segment #%d: type=%lx vaddr=%lx paddr=%lx offset=%lx\n",
|
||||
i, elf_phdr.p_type, elf_phdr.p_vaddr,
|
||||
elf_phdr.p_paddr, elf_phdr.p_offset);
|
||||
|
||||
/* HSA memory starts at physical/virtual address 0x0 */
|
||||
if (elf_phdr.p_type == PT_LOAD && elf_phdr.p_vaddr == 0 &&
|
||||
elf_phdr.p_paddr == 0) {
|
||||
offset = elf_phdr.p_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"Couldn't find HSA memory offset in vmcore\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "HSA memory vmcore offset %lx\n",
|
||||
offset);
|
||||
|
||||
close(fd);
|
||||
|
||||
return offset;
|
||||
|
||||
fail:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int release_hsa(const char *zcore_hsa_path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Release HSA memory via %s\n",
|
||||
zcore_hsa_path);
|
||||
|
||||
/* Release HSA memory */
|
||||
ret = util_file_write_s("0", zcore_hsa_path);
|
||||
if (ret < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "File write failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify that HSA memory has been released */
|
||||
if (get_hsa_size(zcore_hsa_path) > 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "HSA memory release failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "HSA memory successfully released\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
75
hsavmcore/hsa.h
Normal file
75
hsavmcore/hsa.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_HSA_H
|
||||
#define _HSAVMCORE_HSA_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* The interface to a HSA memory reader.
|
||||
* This interface must be implemented by a concrete HSA memory reader.
|
||||
*/
|
||||
struct hsa_reader {
|
||||
/* Total HSA memory size */
|
||||
long hsa_size;
|
||||
/* Offset of HSA memory in /proc/vmcore */
|
||||
long hsa_vmcore_offset;
|
||||
/* Destroys a concrete HSA memory reader */
|
||||
void (*destroy)(struct hsa_reader *self);
|
||||
/* Reads a HSA memory block given by offset and size */
|
||||
int (*read_at)(struct hsa_reader *self, long offset, void *buf,
|
||||
int size);
|
||||
};
|
||||
|
||||
static inline long hsa_get_size(struct hsa_reader *self)
|
||||
{
|
||||
return self->hsa_size;
|
||||
}
|
||||
|
||||
static inline long hsa_get_vmcore_offset(struct hsa_reader *self)
|
||||
{
|
||||
return self->hsa_vmcore_offset;
|
||||
}
|
||||
|
||||
static inline void destroy_hsa_reader(struct hsa_reader *self)
|
||||
{
|
||||
self->destroy(self);
|
||||
}
|
||||
|
||||
static inline int read_hsa_at(struct hsa_reader *self, long offset, void *buf,
|
||||
int size)
|
||||
{
|
||||
return self->read_at(self, offset, buf, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads total HSA memory size from /sys/kernel/debug/zcore/hsa.
|
||||
*/
|
||||
long get_hsa_size(const char *zcore_hsa_path);
|
||||
|
||||
/*
|
||||
* Returns the offset of HSA memory in /proc/vmcore.
|
||||
*/
|
||||
long get_hsa_vmcore_offset(const char *vmcore_path);
|
||||
|
||||
/*
|
||||
* Releases HSA memory.
|
||||
*/
|
||||
int release_hsa(const char *zcore_hsa_path);
|
||||
|
||||
/*
|
||||
* Returns a pointer to the enclosing struct which contains
|
||||
* the variable pointed to by the given pointer as a member.
|
||||
*/
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const typeof(((type *)NULL)->member) *mptr = (ptr); \
|
||||
(type *)((char *)mptr - offsetof(type, member)); \
|
||||
})
|
||||
|
||||
#endif
|
||||
236
hsavmcore/hsa_file.c
Normal file
236
hsavmcore/hsa_file.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "hsa.h"
|
||||
#include "hsa_file.h"
|
||||
|
||||
struct hsa_file_reader {
|
||||
struct hsa_reader super;
|
||||
/* Temporary file containing a copy of the HSA memory */
|
||||
int fd;
|
||||
};
|
||||
|
||||
static void destroy(struct hsa_reader *super)
|
||||
{
|
||||
struct hsa_file_reader *self =
|
||||
container_of(super, struct hsa_file_reader, super);
|
||||
|
||||
close(self->fd);
|
||||
free(self);
|
||||
}
|
||||
|
||||
static int read_at(struct hsa_reader *super, long offset, void *buf, int size)
|
||||
{
|
||||
struct hsa_file_reader *self =
|
||||
container_of(super, struct hsa_file_reader, super);
|
||||
long n, nread = 0;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n",
|
||||
offset, size);
|
||||
|
||||
/* Validate given offset */
|
||||
if (offset >= super->hsa_size)
|
||||
return 0;
|
||||
|
||||
/* Validate given size */
|
||||
size = MIN(super->hsa_size - offset, size);
|
||||
|
||||
n = lseek(self->fd, offset, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
n = read(self->fd, buf + nread, size);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
nread += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
static int copy_hsa_to_file(const char *vmcore_path, const char *workdir_path,
|
||||
long size, long offset)
|
||||
{
|
||||
int fd_in = -1, fd_out = -1;
|
||||
char cache_file_path[PATH_MAX];
|
||||
long n;
|
||||
|
||||
snprintf(cache_file_path, sizeof(cache_file_path), "%s/%s",
|
||||
workdir_path, HSA_CACHE_FILE);
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"Copy HSA memory from vmcore %s to cache file %s\n",
|
||||
vmcore_path, cache_file_path);
|
||||
|
||||
/* Open vmcore file */
|
||||
fd_in = open(vmcore_path, O_RDONLY);
|
||||
if (fd_in < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open cache file */
|
||||
fd_out = open(cache_file_path, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd_out < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Unlink cache file to auto-delete it on close */
|
||||
n = unlink(cache_file_path);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "unlink syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy HSA memory to cache file */
|
||||
n = lseek(fd_in, offset, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy HSA memory chunkwise to the temporary file */
|
||||
while (size) {
|
||||
char buf[1024];
|
||||
long nread, nwrite;
|
||||
|
||||
/* Read a chunk from vmcore */
|
||||
nread = MIN((long)sizeof(buf), size);
|
||||
|
||||
n = read(fd_in, buf, nread);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
} else if (n == 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall read less data than expected\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Write a chunk to cache file */
|
||||
nwrite = n;
|
||||
n = write(fd_out, buf, nwrite);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"write syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
} else if (n != nwrite) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"write syscall wrote less data than expected\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size -= n;
|
||||
}
|
||||
|
||||
/* Reset cache file position */
|
||||
n = lseek(fd_out, 0, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
close(fd_in);
|
||||
|
||||
return fd_out;
|
||||
|
||||
fail:
|
||||
if (fd_in >= 0)
|
||||
close(fd_in);
|
||||
if (fd_out >= 0)
|
||||
close(fd_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path,
|
||||
const char *vmcore_path,
|
||||
const char *workdir_path, long hsa_size,
|
||||
bool release_hsa_flag)
|
||||
{
|
||||
struct hsa_file_reader *self;
|
||||
long hsa_vmcore_offset;
|
||||
int fd;
|
||||
|
||||
/* Calculate HSA size if not given by user */
|
||||
if (hsa_size < 0) {
|
||||
hsa_size = get_hsa_size(zcore_hsa_path);
|
||||
if (hsa_size <= 0)
|
||||
return NULL;
|
||||
}
|
||||
hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path);
|
||||
if (hsa_vmcore_offset < 0)
|
||||
return NULL;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n",
|
||||
hsa_size, hsa_vmcore_offset);
|
||||
|
||||
/*
|
||||
* Store the whole HSA memory from /proc/vmcore to a temporary file
|
||||
* before releasing HSA.
|
||||
*/
|
||||
fd = copy_hsa_to_file(vmcore_path, workdir_path, hsa_size,
|
||||
hsa_vmcore_offset);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
if (release_hsa_flag) {
|
||||
if (release_hsa(zcore_hsa_path)) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
self = malloc(sizeof(struct hsa_file_reader));
|
||||
if (!self) {
|
||||
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->super.hsa_size = hsa_size;
|
||||
self->super.hsa_vmcore_offset = hsa_vmcore_offset;
|
||||
self->super.destroy = destroy;
|
||||
self->super.read_at = read_at;
|
||||
self->fd = fd;
|
||||
|
||||
return &self->super;
|
||||
}
|
||||
27
hsavmcore/hsa_file.h
Normal file
27
hsavmcore/hsa_file.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_HSA_FILE_H
|
||||
#define _HSAVMCORE_HSA_FILE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hsa.h"
|
||||
|
||||
/*
|
||||
* This concrete HSA memory reader copies the whole HSA memory from /proc/vmcore
|
||||
* to a temporary file.
|
||||
* In order for it to work, the system must provide enough file storage.
|
||||
* The advantage of this reader is that it doesn't require extra memory for
|
||||
* caching.
|
||||
*/
|
||||
struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path,
|
||||
const char *vmcore_path,
|
||||
const char *workdir_path, long hsa_size,
|
||||
bool release_hsa_flag);
|
||||
|
||||
#endif
|
||||
151
hsavmcore/hsa_mem.c
Normal file
151
hsavmcore/hsa_mem.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "hsa.h"
|
||||
#include "hsa_mem.h"
|
||||
|
||||
struct hsa_mem_reader {
|
||||
struct hsa_reader super;
|
||||
unsigned char cache[];
|
||||
};
|
||||
|
||||
static void destroy(struct hsa_reader *super)
|
||||
{
|
||||
struct hsa_mem_reader *self =
|
||||
container_of(super, struct hsa_mem_reader, super);
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
static int read_at(struct hsa_reader *super, long offset, void *buf, int size)
|
||||
{
|
||||
struct hsa_mem_reader *self =
|
||||
container_of(super, struct hsa_mem_reader, super);
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n",
|
||||
offset, size);
|
||||
|
||||
/* Validate given offset */
|
||||
if (offset >= super->hsa_size)
|
||||
return 0;
|
||||
|
||||
/* Validate given size */
|
||||
size = MIN(super->hsa_size - offset, size);
|
||||
|
||||
memcpy(buf, self->cache + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int read_hsa(const char *vmcore_path, long offset, void *buf, int size)
|
||||
{
|
||||
long n, nread = 0;
|
||||
int fd = -1;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "Read HSA memory from vmcore %s\n",
|
||||
vmcore_path);
|
||||
|
||||
/* Open vmcore file */
|
||||
fd = open(vmcore_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = lseek(fd, offset, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Read HSA memory */
|
||||
while (size) {
|
||||
n = read(fd, buf + nread, size);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
} else if (n == 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall read less data than expected\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nread += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct hsa_reader *make_hsa_mem_reader(const char *zcore_hsa_path,
|
||||
const char *vmcore_path, long hsa_size,
|
||||
bool release_hsa_flag)
|
||||
{
|
||||
struct hsa_mem_reader *self;
|
||||
long hsa_vmcore_offset;
|
||||
|
||||
/* Calculate HSA size if not given by user */
|
||||
if (hsa_size < 0) {
|
||||
hsa_size = get_hsa_size(zcore_hsa_path);
|
||||
if (hsa_size <= 0)
|
||||
return NULL;
|
||||
}
|
||||
hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path);
|
||||
if (hsa_vmcore_offset < 0)
|
||||
return NULL;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n",
|
||||
hsa_size, hsa_vmcore_offset);
|
||||
|
||||
self = malloc(sizeof(struct hsa_mem_reader) + hsa_size);
|
||||
if (!self) {
|
||||
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Cache the whole HSA memory from /proc/vmcore before releasing HSA */
|
||||
if (read_hsa(vmcore_path, hsa_vmcore_offset, self->cache, hsa_size)) {
|
||||
free(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (release_hsa_flag) {
|
||||
if (release_hsa(zcore_hsa_path)) {
|
||||
free(self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
self->super.hsa_size = hsa_size;
|
||||
self->super.hsa_vmcore_offset = hsa_vmcore_offset;
|
||||
self->super.destroy = destroy;
|
||||
self->super.read_at = read_at;
|
||||
|
||||
return &self->super;
|
||||
}
|
||||
24
hsavmcore/hsa_mem.h
Normal file
24
hsavmcore/hsa_mem.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_HSA_MEM_H
|
||||
#define _HSAVMCORE_HSA_MEM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hsa.h"
|
||||
|
||||
/*
|
||||
* This concrete HSA memory reader reads the whole HSA memory from /proc/vmcore
|
||||
* and caches it all in an internal memory buffer.
|
||||
* In order for it to work, the system must provide enough memory or swap space.
|
||||
*/
|
||||
struct hsa_reader *make_hsa_mem_reader(const char *zcore_hsa_path,
|
||||
const char *vmcore_path, long hsa_size,
|
||||
bool release_hsa_flag);
|
||||
|
||||
#endif
|
||||
147
hsavmcore/initramfs/fedora-rhel/README.md
Normal file
147
hsavmcore/initramfs/fedora-rhel/README.md
Normal file
@@ -0,0 +1,147 @@
|
||||
|
||||
# Setup
|
||||
|
||||
## Configure crashkernel
|
||||
|
||||
```shell
|
||||
sudo grubby --args "crashkernel=512M" --update-kernel=ALL
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
## Production kernel's root file system
|
||||
|
||||
- kdump mounts the production kernel's root file system under **/sysroot**.
|
||||
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo dnf install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
```shell
|
||||
make -C s390-tools/hsavmcore
|
||||
```
|
||||
|
||||
## Install hsavmcore
|
||||
|
||||
```shell
|
||||
sudo cp s390-tools/hsavmcore/hsavmcore /usr/sbin/
|
||||
```
|
||||
|
||||
## Create swap file
|
||||
|
||||
```shell
|
||||
sudo dd if=/dev/zero of=/var/crash/swap.img bs=1M count=1024
|
||||
sudo mkswap /var/crash/swap.img
|
||||
```
|
||||
|
||||
## Install hsavmcore.conf
|
||||
|
||||
### Test configuration
|
||||
|
||||
- Doesn't require HSA support
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 1
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 1
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
swap = /sysroot/var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
### Production configuration
|
||||
|
||||
- Works only on s390x
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 1
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 1
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
swap = /sysroot/var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
## Install new dracut module
|
||||
|
||||
```shell
|
||||
sudo cp -r s390-tools/hsavmcore/initramfs/fedora-rhel/dracut/modules.d/99hsavmcore /lib/dracut/modules.d/
|
||||
```
|
||||
|
||||
## Add the new dracut module as a dependency to the dracut module *kdumpbase*
|
||||
|
||||
```shell
|
||||
sudo sed -e 's#local _dep="base shutdown"#local _dep="base shutdown hsavmcore"#' \
|
||||
-i /lib/dracut/modules.d/99kdumpbase/module-setup.sh
|
||||
```
|
||||
|
||||
## Rebuild kdump initramfs
|
||||
|
||||
```shell
|
||||
sudo kdumpctl rebuild
|
||||
```
|
||||
|
||||
## Enable swap LVM in kdump
|
||||
|
||||
- Required if you want to use a swap device in kdump
|
||||
|
||||
```shell
|
||||
sudo sed -e 's#^KDUMP_COMMANDLINE_APPEND="\(.*\)"$#KDUMP_COMMANDLINE_APPEND="\1 rd.lvm.lv=rhel/swap"#' \
|
||||
-i /etc/sysconfig/kdump
|
||||
```
|
||||
|
||||
## Reload kdump
|
||||
|
||||
```shell
|
||||
sudo kdumpctl reload
|
||||
```
|
||||
|
||||
# Test
|
||||
|
||||
```shell
|
||||
echo N | sudo tee /sys/module/kernel/parameters/crash_kexec_post_notifiers
|
||||
echo c | sudo tee /proc/sysrq-trigger
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=hsavmcore Service
|
||||
After=initrd.target initrd-parse-etc.service sysroot.mount
|
||||
After=dracut-initqueue.service dracut-pre-mount.service dracut-mount.service dracut-pre-pivot.service
|
||||
Before=kdump-capture.service
|
||||
Before=initrd-cleanup.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/sbin/hsavmcore -c /etc/hsavmcore.conf
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog+console
|
||||
37
hsavmcore/initramfs/fedora-rhel/dracut/modules.d/99hsavmcore/module-setup.sh
Executable file
37
hsavmcore/initramfs/fedora-rhel/dracut/modules.d/99hsavmcore/module-setup.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
. $dracutfunctions
|
||||
|
||||
if ! [[ -d "${initdir}/tmp" ]]; then
|
||||
mkdir -p "${initdir}/tmp"
|
||||
fi
|
||||
|
||||
check() {
|
||||
[[ $debug ]] && set -x
|
||||
#kdumpctl sets this explicitly
|
||||
if [ -z "$IN_KDUMP" ]
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
local _dep="base shutdown"
|
||||
echo $_dep
|
||||
return 0
|
||||
}
|
||||
|
||||
installkernel() {
|
||||
hostonly='' instmods fuse
|
||||
}
|
||||
|
||||
install() {
|
||||
inst "/usr/sbin/hsavmcore" "/usr/sbin/hsavmcore"
|
||||
inst "/etc/hsavmcore.conf" "/etc/hsavmcore.conf"
|
||||
inst "$moddir/hsavmcore.service" "$systemdsystemunitdir/hsavmcore.service"
|
||||
mkdir -p "$initdir/$systemdsystemunitdir/initrd.target.wants"
|
||||
ln_r "$systemdsystemunitdir/hsavmcore.service" "$systemdsystemunitdir/initrd.target.wants/hsavmcore.service"
|
||||
|
||||
inst_hook pre-mount 30 "$moddir/setup-fuse.sh"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
modprobe fuse
|
||||
141
hsavmcore/initramfs/sles/README.md
Normal file
141
hsavmcore/initramfs/sles/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
# Setup
|
||||
|
||||
## Configure crashkernel
|
||||
|
||||
```shell
|
||||
sudo vim /etc/default/grub
|
||||
sudo sed -e 's/GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/GRUB_CMDLINE_LINUX_DEFAULT="\1 crashkernel=512M"/' \
|
||||
-i /etc/default/grub
|
||||
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
## Production kernel's root file system
|
||||
|
||||
- kdump mounts the production kernel's root file system under **/kdump/mnt1**.
|
||||
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo zypper install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
```shell
|
||||
make -C s390-tools/hsavmcore
|
||||
```
|
||||
|
||||
## Install hsavmcore
|
||||
|
||||
```shell
|
||||
sudo cp s390-tools/hsavmcore/hsavmcore /usr/sbin/
|
||||
```
|
||||
|
||||
## Create swap file
|
||||
|
||||
```shell
|
||||
sudo dd if=/dev/zero of=/var/crash/swap.img bs=1M count=1024
|
||||
sudo mkswap /var/crash/swap.img
|
||||
```
|
||||
|
||||
## Install hsavmcore.conf
|
||||
|
||||
### Test configuration
|
||||
|
||||
- Doesn't require HSA support
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /kdump/mnt1/var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 1
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /kdump/mnt1/var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 1
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
swap = /kdump/mnt1/var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
### Production configuration
|
||||
|
||||
- Works only on s390x
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /kdump/mnt1/var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 1
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /kdump/mnt1/var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 1
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
swap = /kdump/mnt1/var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
## Install new dracut module
|
||||
|
||||
```shell
|
||||
sudo cp -r s390-tools/hsavmcore/initramfs/sles/dracut/modules.d/99hsavmcore /usr/lib/dracut/modules.d/
|
||||
```
|
||||
|
||||
## Add the new dracut module as a dependency to the dracut module *kdump*
|
||||
|
||||
```shell
|
||||
sudo sed -e 's/_modules\[drm\]=/_modules[drm]=\n _modules[hsavmcore]=/' \
|
||||
-i /usr/lib/dracut/modules.d/99kdump/module-setup.sh
|
||||
```
|
||||
|
||||
## Rebuild kdump initramfs
|
||||
|
||||
```shell
|
||||
sudo mkdumprd -f
|
||||
```
|
||||
|
||||
## Reload kdump
|
||||
|
||||
```shell
|
||||
systemctl enable kdump
|
||||
systemctl restart kdump
|
||||
```
|
||||
|
||||
# Test
|
||||
|
||||
```shell
|
||||
echo c | sudo tee /proc/sysrq-trigger
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# See systemd.special(7) for details
|
||||
|
||||
[Unit]
|
||||
Description=hsavmcore Service
|
||||
Before=kdump-save.service
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionPathExists=/proc/vmcore
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/sbin/hsavmcore -c /etc/hsavmcore.conf
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog+console
|
||||
47
hsavmcore/initramfs/sles/dracut/modules.d/99hsavmcore/module-setup.sh
Executable file
47
hsavmcore/initramfs/sles/dracut/modules.d/99hsavmcore/module-setup.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
. /lib/kdump/setup-kdump.functions
|
||||
|
||||
kdump_needed() {
|
||||
# Building a kdump initrd?
|
||||
if [[ " $dracutmodules $add_dracutmodules $force_add_dracutmodules" == *\ $_mod\ * ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Is FADUMP active?
|
||||
if [ "$KDUMP_FADUMP" = "yes" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Do not include kdump by default
|
||||
return 1
|
||||
}
|
||||
|
||||
check() {
|
||||
# Get configuration
|
||||
kdump_get_config || return 1
|
||||
|
||||
kdump_needed || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
echo "systemd"
|
||||
return 0
|
||||
}
|
||||
|
||||
installkernel() {
|
||||
hostonly='' instmods fuse
|
||||
}
|
||||
|
||||
install() {
|
||||
inst_simple /usr/sbin/hsavmcore
|
||||
inst_simple /etc/hsavmcore.conf
|
||||
|
||||
inst "$moddir/hsavmcore.service" "$systemdsystemunitdir/hsavmcore.service"
|
||||
mkdir -p "$initdir/$systemdsystemunitdir/initrd.target.wants"
|
||||
ln_r "$systemdsystemunitdir/hsavmcore.service" "$systemdsystemunitdir/initrd.target.wants/hsavmcore.service"
|
||||
|
||||
inst_hook pre-mount 30 "$moddir/setup-fuse.sh"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
modprobe fuse
|
||||
142
hsavmcore/initramfs/ubuntu/README.md
Normal file
142
hsavmcore/initramfs/ubuntu/README.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Setup
|
||||
|
||||
## Production kernel's root file system
|
||||
|
||||
- kdump mounts the production kernel's root file system under **/**.
|
||||
|
||||
## debugfs
|
||||
|
||||
- kdump mounts debugfs automatically.
|
||||
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y make gcc kdump-tools fuse3 libfuse3-dev libsystemd-dev
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
```shell
|
||||
make -C s390-tools/hsavmcore
|
||||
```
|
||||
|
||||
## Install hsavmcore
|
||||
|
||||
```shell
|
||||
sudo cp s390-tools/hsavmcore/hsavmcore /usr/sbin/
|
||||
```
|
||||
|
||||
## Create swap file
|
||||
|
||||
```shell
|
||||
sudo dd if=/dev/zero of=/var/crash/swap.img bs=1M count=1024
|
||||
sudo mkswap /var/crash/swap.img
|
||||
```
|
||||
|
||||
## Install hsavmcore.conf
|
||||
|
||||
### Test configuration
|
||||
|
||||
- Doesn't require HSA support
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 0
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 0
|
||||
hsa_size = 0x1ffff000
|
||||
release_hsa = 0
|
||||
bind_mount_vmcore = 1
|
||||
swap = /var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
### Production configuration
|
||||
|
||||
- Works only on s390x
|
||||
|
||||
#### HSA cache in file
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /var/crash
|
||||
use_hsa_mem = 0
|
||||
mount_debugfs = 0
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### HSA cache in memory
|
||||
|
||||
```shell
|
||||
cat <<EOF | sudo tee /etc/hsavmcore.conf
|
||||
verbose = 2
|
||||
workdir = /sysroot/var/crash
|
||||
use_hsa_mem = 1
|
||||
mount_debugfs = 0
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
bind_mount_vmcore = 1
|
||||
swap = /var/crash/swap.img
|
||||
EOF
|
||||
```
|
||||
|
||||
## Install new dracut module
|
||||
|
||||
```shell
|
||||
sudo cp s390-tools/hsavmcore/initramfs/ubuntu/hsavmcore.service /usr/lib/systemd/system/
|
||||
```
|
||||
|
||||
## Add the new systemd service as a dependency to the service *kdump-tools-dump*
|
||||
|
||||
```shell
|
||||
Wants=network-online.target dbus.socket systemd-resolved.service hsavmcore.service
|
||||
After=network-online.target dbus.socket systemd-resolved.service hsavmcore.service
|
||||
|
||||
sudo sed -e 's/Wants=\(.*\)$/Wants=\1 hsavmcore.service/' \
|
||||
-e 's/After=\(.*\)$/After=\1 hsavmcore.service/' \
|
||||
-i /usr/lib/systemd/system/kdump-tools-dump.service
|
||||
```
|
||||
|
||||
## Rebuild kdump initramfs
|
||||
|
||||
```shell
|
||||
sudo rm -rf /var/lib/kdump/initrd*
|
||||
sudo kdump-config unload
|
||||
sudo kdump-config load
|
||||
sudo systemctl restart kdump-tools
|
||||
```
|
||||
|
||||
## Reload kdump
|
||||
|
||||
```shell
|
||||
sudo kdump-config unload
|
||||
sudo kdump-config load
|
||||
```
|
||||
|
||||
# Test
|
||||
|
||||
```shell
|
||||
echo N | sudo tee /sys/module/kernel/parameters/crash_kexec_post_notifiers
|
||||
echo c | sudo tee /proc/sysrq-trigger
|
||||
```
|
||||
22
hsavmcore/initramfs/ubuntu/hsavmcore.service
Normal file
22
hsavmcore/initramfs/ubuntu/hsavmcore.service
Normal file
@@ -0,0 +1,22 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=hsavmcore Service
|
||||
Wants=network-online.target dbus.socket systemd-resolved.service
|
||||
After=network-online.target dbus.socket systemd-resolved.service
|
||||
Before=kdump-tools-dump.service
|
||||
|
||||
[Install]
|
||||
WantedBy=kdump-tools-dump.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/sbin/hsavmcore -c /etc/hsavmcore.conf
|
||||
StandardInput=null
|
||||
StandardOutput=syslog+console
|
||||
StandardError=syslog+console
|
||||
238
hsavmcore/main.c
Normal file
238
hsavmcore/main.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "cmdline_options.h"
|
||||
#include "mount.h"
|
||||
#include "swap.h"
|
||||
#include "hsa.h"
|
||||
#include "hsa_mem.h"
|
||||
#include "hsa_file.h"
|
||||
#include "proxy.h"
|
||||
#include "overlay.h"
|
||||
|
||||
#define MAX_WAIT_VMCORE_OVERLAY_SECS 5
|
||||
|
||||
static int bind_mount_vmcore(const char *src, const char *target,
|
||||
int max_wait_secs)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Wait %d secs for %s to appear\n",
|
||||
max_wait_secs, src);
|
||||
|
||||
while (max_wait_secs--) {
|
||||
if (!stat(src, &st))
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (stat(src, &st)) {
|
||||
util_log_print(UTIL_LOG_ERROR, "Timeout for appearance of %s\n",
|
||||
src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = bind_mount(src, target);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_all_signals(void)
|
||||
{
|
||||
sigset_t signal_set;
|
||||
|
||||
sigfillset(&signal_set);
|
||||
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
|
||||
}
|
||||
|
||||
static void unblock_all_signals(void)
|
||||
{
|
||||
sigset_t signal_set;
|
||||
|
||||
sigfillset(&signal_set);
|
||||
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
|
||||
}
|
||||
|
||||
static void *vmcore_overlay_server(void *arg)
|
||||
{
|
||||
struct vmcore_overlay *vmcore_overlay = (struct vmcore_overlay *)arg;
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "vmcore overlay thread: start\n");
|
||||
|
||||
/* Unblock all signals because vmcore overlay handles them */
|
||||
unblock_all_signals();
|
||||
|
||||
/* Blocks until a signal has been received or an error occurred */
|
||||
ret = serve_vmcore_overlay(vmcore_overlay);
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "vmcore overlay thread: end (%d)\n",
|
||||
ret);
|
||||
|
||||
return (void *)(long)ret;
|
||||
}
|
||||
|
||||
static void terminate_vmcore_overlay(pthread_t tid)
|
||||
{
|
||||
pthread_kill(tid, SIGINT);
|
||||
pthread_join(tid, NULL);
|
||||
}
|
||||
|
||||
static int wait_for_vmcore_overlay(pthread_t tid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pthread_join(tid, (void **)&ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct vmcore_overlay *vmcore_overlay;
|
||||
struct vmcore_proxy *vmcore_proxy;
|
||||
int exit_code = EXIT_SUCCESS, ret;
|
||||
struct hsa_reader *hsa_reader;
|
||||
pthread_t vmcore_overlay_tid;
|
||||
struct config config;
|
||||
|
||||
init_config(&config);
|
||||
|
||||
parse_cmdline_options(argc, argv, &config);
|
||||
|
||||
if (strlen(config.swap)) {
|
||||
ret = swap_on(config.swap);
|
||||
if (ret < 0) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.mount_debugfs) {
|
||||
ret = mount_debugfs(DEBUGFS_MOUNT_POINT);
|
||||
if (ret < 0) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto swap_off;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.use_hsa_mem)
|
||||
hsa_reader =
|
||||
make_hsa_mem_reader(config.zcore_hsa_path,
|
||||
config.vmcore_path, config.hsa_size,
|
||||
config.release_hsa);
|
||||
else
|
||||
hsa_reader = make_hsa_file_reader(config.zcore_hsa_path,
|
||||
config.vmcore_path,
|
||||
config.workdir_path,
|
||||
config.hsa_size,
|
||||
config.release_hsa);
|
||||
if (!hsa_reader) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto unmount_debugfs;
|
||||
}
|
||||
|
||||
vmcore_proxy = make_vmcore_proxy(config.vmcore_path, hsa_reader);
|
||||
if (!vmcore_proxy) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto destroy_hsa_reader;
|
||||
}
|
||||
|
||||
vmcore_overlay = make_vmcore_overlay(vmcore_proxy, OVERLAY_MOUNT_POINT,
|
||||
config.fuse_debug);
|
||||
if (!vmcore_overlay) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto destroy_vmcore_proxy;
|
||||
}
|
||||
|
||||
/* vmcore overlay thread handles all signals */
|
||||
block_all_signals();
|
||||
|
||||
/* Start vmcore overlay thread which handles file system calls */
|
||||
ret = pthread_create(&vmcore_overlay_tid, NULL, vmcore_overlay_server,
|
||||
vmcore_overlay);
|
||||
if (ret < 0) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto destroy_vmcore_overlay;
|
||||
}
|
||||
|
||||
/* Bind mount /proc/vmcore */
|
||||
if (config.bind_mount_vmcore) {
|
||||
ret = bind_mount_vmcore(OVERLAY_MOUNT_POINT "/" VMCORE_FILE,
|
||||
config.bind_mount_vmcore_path,
|
||||
MAX_WAIT_VMCORE_OVERLAY_SECS);
|
||||
if (ret < 0) {
|
||||
terminate_vmcore_overlay(vmcore_overlay_tid);
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto destroy_vmcore_overlay;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* Tell systemd that service is ready now */
|
||||
ret = sd_notify(0, "READY=1");
|
||||
if (ret <= 0)
|
||||
util_log_print(UTIL_LOG_WARN, "Failed to notify systemd (%d)\n",
|
||||
ret);
|
||||
#endif
|
||||
|
||||
ret = wait_for_vmcore_overlay(vmcore_overlay_tid);
|
||||
if (ret < 0)
|
||||
exit_code = EXIT_FAILURE;
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* Tell systemd that service is stopping now */
|
||||
ret = sd_notify(0, "STOPPING=1");
|
||||
if (ret <= 0)
|
||||
util_log_print(UTIL_LOG_WARN, "Failed to notify systemd (%d)\n",
|
||||
ret);
|
||||
#endif
|
||||
|
||||
unblock_all_signals();
|
||||
|
||||
if (config.bind_mount_vmcore)
|
||||
unmount_detach(config.bind_mount_vmcore_path);
|
||||
|
||||
destroy_vmcore_overlay:
|
||||
destroy_vmcore_overlay(vmcore_overlay);
|
||||
|
||||
destroy_vmcore_proxy:
|
||||
destroy_vmcore_proxy(vmcore_proxy);
|
||||
|
||||
destroy_hsa_reader:
|
||||
destroy_hsa_reader(hsa_reader);
|
||||
|
||||
unmount_debugfs:
|
||||
if (config.mount_debugfs)
|
||||
unmount_detach(DEBUGFS_MOUNT_POINT);
|
||||
|
||||
swap_off:
|
||||
if (strlen(config.swap))
|
||||
swap_off(config.swap);
|
||||
|
||||
done:
|
||||
return exit_code;
|
||||
}
|
||||
139
hsavmcore/man/hsavmcore.8
Normal file
139
hsavmcore/man/hsavmcore.8
Normal file
@@ -0,0 +1,139 @@
|
||||
.\" Copyright 2021 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH HSAVMCORE 8 "May 2021" "s390-tools"
|
||||
.
|
||||
.SH NAME
|
||||
hsavmcore - Enable kdump to release the HSA memory early in the dump process
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B hsavmcore
|
||||
.RI [ OPTIONS ]
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B hsavmcore
|
||||
is designed to make the dump process with kdump more efficient.
|
||||
The HSA memory contains a part of the production kernel's memory.
|
||||
Use hsavmcore to cache this information and release HSA memory early in the process.
|
||||
.PP
|
||||
Depending on the size of the production kernel's memory, writing the dump to persistent
|
||||
storage can be time consuming and prevent the HSA memory from being reused by other LPARs.
|
||||
.
|
||||
The
|
||||
.B hsavmcore
|
||||
tool performs these steps:
|
||||
.IP " 1)"
|
||||
Read the size of the HSA memory from
|
||||
.B /sys/kernel/debug/zcore/hsa.
|
||||
.IP " 2)"
|
||||
Cache the HSA memory content contained in
|
||||
.B /proc/vmcore
|
||||
either in regular memory or within the file system.
|
||||
.IP " 3)"
|
||||
Releases the HSA memory by writing to
|
||||
.B /sys/kernel/debug/zcore/hsa.
|
||||
.PP
|
||||
At this stage, the HSA memory region is unavailable to
|
||||
.B /proc/vmcore
|
||||
and cannot be used by kdump.
|
||||
.
|
||||
The
|
||||
.B hsavmcore
|
||||
tool now combines the cached HSA memory and the non-HSA memory from the original
|
||||
.B /proc/vmcore
|
||||
to create a replacement for
|
||||
.B /proc/vmcore.
|
||||
.
|
||||
The replacement
|
||||
.B /proc/vmcore
|
||||
can be processed as usual.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fP or \fB\-\-help\fP
|
||||
Print usage information and exit.
|
||||
.
|
||||
.TP
|
||||
\fB\-v\fP or \fB\-\-version\fP
|
||||
Print version information and exit.
|
||||
.
|
||||
.TP
|
||||
\fB\-V\fP or \fB\-\-verbose\fP
|
||||
Print verbose messages to stdout. Repeat this option for increased verbosity
|
||||
from just error messages to also include warning, information, debug, and trace messages.
|
||||
This option is intended for debugging.
|
||||
.
|
||||
.TP
|
||||
\fB\-c\fP or \fB\-\-config\fP \fICONFIGFILE\fP
|
||||
Path to the configuration file. By default, no configuration file is used.
|
||||
.
|
||||
.TP
|
||||
\fB\-C\fP or \fB\-\-vmcore\fP \fIVMCOREFILE\fP
|
||||
Path to the vmcore file. Default:
|
||||
.B /proc/vmcore.
|
||||
.
|
||||
.TP
|
||||
\fB\-H\fP or \fB\-\-hsa\fP \fIZCOREHSAFILE\fP
|
||||
Path to the zcore HSA file. Default:
|
||||
.B /sys/kernel/debug/zcore/hsa.
|
||||
.
|
||||
.TP
|
||||
\fB\-W\fP or \fB\-\-workdir\fP \fIWORKDIR\fP
|
||||
Path to the work directory where temporary files can be stored. Default:
|
||||
.B /var/crash.
|
||||
.
|
||||
.TP
|
||||
\fB\-B\fP or \fB\-\-bmvmcore\fP \fIVMCOREFILE\fP
|
||||
Path to the target of the bind mount for the replacement vmcore file. Default:
|
||||
.B /proc/vmcore.
|
||||
.
|
||||
.TP
|
||||
\fB\-S\fP or \fB\-\-swap\fP \fIPATH\fP
|
||||
Path to a swap device or file. The specified swap device or file must exist and have the proper
|
||||
swap format. Default: no swap device or file is activated.
|
||||
.
|
||||
.TP
|
||||
\fB\-T\fP or \fB\-\-hsasize\fP \fIHSASIZE\fP
|
||||
HSA size in bytes. Used for testing purposes. Default: -1 (read from the zcore HSA file).
|
||||
.
|
||||
.TP
|
||||
\fB\-D\fP or \fB\-\-dbgfsmnt\fP
|
||||
Mount the debug file system. Default: the debug file system is not mounted.
|
||||
.
|
||||
.TP
|
||||
\fB\-F\fP or \fB\-\-hsamem\fP
|
||||
Cache the HSA memory in regular memory. Default: the HSA memory is cached as a file
|
||||
within WORKDIR.
|
||||
.
|
||||
.TP
|
||||
\fB\-R\fP or \fB\-\-norelhsa\fP
|
||||
Do NOT release the HSA memory after caching. Default: the HSA memory is released.
|
||||
.
|
||||
.TP
|
||||
\fB\-N\fP or \fB\-\-nobindmnt\fP
|
||||
Do NOT replace the system's vmcore file. Default: the system's vmcore file is replaced.
|
||||
.
|
||||
.TP
|
||||
\fB\-G\fP or \fB\-\-fusedbg\fP
|
||||
Enable FUSE debugging. Default: FUSE debugging is disabled.
|
||||
.RE
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B To run hsavmcore on a kdump system during a stand-alone dump with default parameters:
|
||||
.RS 4
|
||||
hsavmcore
|
||||
.br
|
||||
makedumpfile \-d 31 /proc/vmcore test-dump.elf
|
||||
.RE
|
||||
.TP
|
||||
.B To test hsavmcore with a vmcore copy and without being in a kdump system (for debugging):
|
||||
.RS 4
|
||||
hsavmcore \-VVV \-T 0x1ffff000 \-C vmcore-dump.elf \-N \-R
|
||||
.br
|
||||
makedumpfile \-d 31 /tmp/hsavmcore-overlay/vmcore test-dump.elf
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR hsavmcore.conf (5)
|
||||
118
hsavmcore/man/hsavmcore.conf.5
Normal file
118
hsavmcore/man/hsavmcore.conf.5
Normal file
@@ -0,0 +1,118 @@
|
||||
.\" Copyright 2021 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH HSAVMCORE.CONF 5 "May 2021" "s390-tools"
|
||||
.SH NAME
|
||||
hsavmcore.conf \- Configuration file for the hsavmcore tool
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B hsavmcore.conf
|
||||
configuration file contains the configuration information for
|
||||
the
|
||||
.B hsavmcore
|
||||
tool.
|
||||
|
||||
All specifications in the configuration file are optional.
|
||||
The command defaults apply for omitted parameters.
|
||||
.
|
||||
.SS "verbose"
|
||||
This parameter sets the verbosity level of the output messages.
|
||||
The following pre-defined numeric values can be used:
|
||||
.
|
||||
.RS 2
|
||||
.IP "-" 2
|
||||
\fB0\fP - show only error messages (default)
|
||||
.IP "-" 2
|
||||
\fB1\fP - show error and warning messages
|
||||
.IP "-" 2
|
||||
\fB2\fP - show error, warning and information messages
|
||||
.IP "-" 2
|
||||
\fB3\fP - show error, warning, information and debug messages
|
||||
.IP "-" 2
|
||||
\fB4\fP - show error, warning, information, debug and trace messages
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.SS "mount_debugfs"
|
||||
Mount (1) or do not mount (0) debugfs. Use this configuration if the kdump kernel
|
||||
does not mount the debugfs during the boot process.
|
||||
.
|
||||
.SS "workdir"
|
||||
Specifies a work directory on the kdump system where the hsavmcore tool can create
|
||||
temporary files. This specification is required if
|
||||
.B use_hsa_mem
|
||||
is set to 0.
|
||||
.
|
||||
.SS "use_hsa_mem"
|
||||
Cache the HSA memory in regular memory (1) or in a file on a file system (0).
|
||||
.
|
||||
.SS "hsa_size"
|
||||
Specify a value, in bytes, for the HSA memory size instead of reading the size
|
||||
from
|
||||
.B /sys/kernel/debug/zcore/hsa.
|
||||
This parameter is intended only to test
|
||||
the
|
||||
.B hsavmcore
|
||||
tool without being in a kdump kernel. Specifying -1 falls back
|
||||
to reading the size from
|
||||
.B /sys/kernel/debug/zcore/hsa.
|
||||
.
|
||||
.SS "release_hsa"
|
||||
Release (1) or do not release (0) the HSA memory after it is cached by
|
||||
the
|
||||
.B hsavmcore
|
||||
tool.
|
||||
.
|
||||
.SS "bind_mount_vmcore"
|
||||
Replace (1) the original vmcore file with the new file created by the
|
||||
.B hsavmcore
|
||||
tool or keep the original file (0), which no longer contains the information
|
||||
from the HSA memory. Set this parameter to 1 if you intend to use kdump tools to
|
||||
create a core dump.
|
||||
.
|
||||
.SS "swap"
|
||||
Specify a swap device or file through its path in a kdump system.
|
||||
The specified swap device or file must exist and have the proper swap format.
|
||||
You might need a swap device because the amount of memory available in the kdump
|
||||
kernel during a stand-alone dump is limited to the size of the HSA memory.
|
||||
.
|
||||
.SS "fuse_debug"
|
||||
Enable (1) or disable (0) fuse debugging.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
A complete configuration file could look like this:
|
||||
|
||||
.nf
|
||||
------------------------------ config file start ------------------------------
|
||||
# Example configuration for hsavmcore
|
||||
|
||||
# 0 - ERROR
|
||||
# 1 - WARN
|
||||
# 2 - INFO
|
||||
# 3 - DEBUG
|
||||
# 4 - TRACE
|
||||
verbose = 3
|
||||
|
||||
workdir = /var/crash
|
||||
|
||||
mount_debugfs = 1
|
||||
|
||||
use_hsa_mem = 1
|
||||
|
||||
hsa_size = -1
|
||||
release_hsa = 1
|
||||
|
||||
bind_mount_vmcore = 1
|
||||
|
||||
swap = /dev/disk/by-uuid/3cf6630b-4c4d-49ac-a0ae-0f5484cb5721
|
||||
#swap = /swap.img
|
||||
|
||||
fuse_debug = 0
|
||||
------------------------------ config file end ------------------------------
|
||||
.fi
|
||||
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR hsavmcore (8)
|
||||
63
hsavmcore/mount.c
Normal file
63
hsavmcore/mount.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "mount.h"
|
||||
|
||||
int mount_debugfs(const char *target)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Mount debugfs on %s\n", target);
|
||||
|
||||
ret = mount("none", target, "debugfs", 0, NULL);
|
||||
if (ret) {
|
||||
util_log_print(UTIL_LOG_ERROR, "mount syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bind_mount(const char *src, const char *target)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Bind mount %s on %s\n", src, target);
|
||||
|
||||
ret = mount(src, target, "", MS_BIND, NULL);
|
||||
if (ret) {
|
||||
util_log_print(UTIL_LOG_ERROR, "mount syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unmount_detach(const char *target)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Unmount detach %s\n", target);
|
||||
|
||||
ret = umount2(target, MNT_DETACH);
|
||||
if (ret) {
|
||||
util_log_print(UTIL_LOG_ERROR, "umount2 syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
hsavmcore/mount.h
Normal file
17
hsavmcore/mount.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_MOUNT_H
|
||||
#define _HSAVMCORE_MOUNT_H
|
||||
|
||||
int mount_debugfs(const char *target);
|
||||
|
||||
int bind_mount(const char *src, const char *target);
|
||||
|
||||
int unmount_detach(const char *target);
|
||||
|
||||
#endif
|
||||
214
hsavmcore/overlay.c
Normal file
214
hsavmcore/overlay.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FUSE_USE_VERSION 30
|
||||
#include <fuse.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "overlay.h"
|
||||
|
||||
#define ROOT_DIR "/"
|
||||
|
||||
struct vmcore_overlay {
|
||||
struct vmcore_proxy *vmcore_proxy;
|
||||
char mount_point[PATH_MAX];
|
||||
bool fuse_debug;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
memset(stbuf, 0, sizeof(struct stat));
|
||||
|
||||
if (strcmp(path, ROOT_DIR) == 0) {
|
||||
stbuf->st_mode = S_IFDIR | 0755;
|
||||
stbuf->st_nlink = 2;
|
||||
} else if (strcmp(path + 1, VMCORE_FILE) == 0) {
|
||||
stbuf->st_mode = S_IFREG | 0444;
|
||||
stbuf->st_nlink = 1;
|
||||
stbuf->st_size = vmcore_proxy_size(overlay->vmcore_proxy);
|
||||
} else {
|
||||
ret = -ENOENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmcore_fuse_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t offset,
|
||||
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, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmcore_fuse_open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
if (strcmp(path + 1, VMCORE_FILE) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
if ((fi->flags & O_ACCMODE) != O_RDONLY)
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmcore_fuse_read(const char *path, char *buf, size_t size,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
(void)fi;
|
||||
|
||||
if (strcmp(path + 1, VMCORE_FILE) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
struct vmcore_overlay *overlay = fuse_get_context()->private_data;
|
||||
|
||||
return read_vmcore_proxy_at(overlay->vmcore_proxy, offset, buf, size);
|
||||
}
|
||||
|
||||
static int setup_fuse_args(struct fuse_args *args, const char *mount_point,
|
||||
bool debug)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fuse_opt_add_arg(args, NAME);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
/* Single-threaded */
|
||||
ret = fuse_opt_add_arg(args, "-s");
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
/* Foreground */
|
||||
ret = fuse_opt_add_arg(args, "-f");
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
/* Debugging */
|
||||
if (debug) {
|
||||
ret = fuse_opt_add_arg(args, "-d");
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = fuse_opt_add_arg(args, mount_point);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vmcore_overlay *make_vmcore_overlay(struct vmcore_proxy *vmcore_proxy,
|
||||
const char *mount_point,
|
||||
bool fuse_debug)
|
||||
{
|
||||
struct vmcore_overlay *overlay;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "vmcore overlay: mountpoint=%s\n",
|
||||
mount_point);
|
||||
|
||||
overlay = malloc(sizeof(struct vmcore_overlay));
|
||||
if (!overlay) {
|
||||
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
overlay->vmcore_proxy = vmcore_proxy;
|
||||
strncpy(overlay->mount_point, mount_point,
|
||||
sizeof(overlay->mount_point) - 1);
|
||||
/* Ensure null termination */
|
||||
overlay->mount_point[sizeof(overlay->mount_point) - 1] = '\0';
|
||||
overlay->fuse_debug = fuse_debug;
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
void destroy_vmcore_overlay(struct vmcore_overlay *overlay)
|
||||
{
|
||||
free(overlay);
|
||||
}
|
||||
|
||||
/*
|
||||
* FUSE file system operations
|
||||
*/
|
||||
static struct fuse_operations vmcore_fuse_ops = {
|
||||
.getattr = vmcore_fuse_getattr,
|
||||
.readdir = vmcore_fuse_readdir,
|
||||
.open = vmcore_fuse_open,
|
||||
.read = vmcore_fuse_read,
|
||||
};
|
||||
|
||||
int serve_vmcore_overlay(struct vmcore_overlay *overlay)
|
||||
{
|
||||
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "vmcore overlay: FUSE main\n");
|
||||
|
||||
ret = setup_fuse_args(&args, overlay->mount_point, overlay->fuse_debug);
|
||||
if (ret < 0)
|
||||
goto free_args;
|
||||
|
||||
/* Create mount point */
|
||||
ret = mkdir(overlay->mount_point, 0755);
|
||||
if (ret < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "mkdir syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run file system, blocks until a signal has been received or an error
|
||||
* occurred.
|
||||
*/
|
||||
fuse_main(args.argc, args.argv, &vmcore_fuse_ops, overlay);
|
||||
|
||||
/* Remove mount point */
|
||||
rmdir(overlay->mount_point);
|
||||
|
||||
ret = 0;
|
||||
|
||||
free_args:
|
||||
|
||||
fuse_opt_free_args(&args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
33
hsavmcore/overlay.h
Normal file
33
hsavmcore/overlay.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_OVERLAY_H
|
||||
#define _HSAVMCORE_OVERLAY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
/*
|
||||
* A vmcore Overlay exports a vmcore Proxy as a normal read-only file
|
||||
* that could be used, for instance, by *makedumpfile*.
|
||||
*/
|
||||
|
||||
struct vmcore_overlay;
|
||||
|
||||
struct vmcore_overlay *make_vmcore_overlay(struct vmcore_proxy *vmcore_proxy,
|
||||
const char *mount_point,
|
||||
bool fuse_debug);
|
||||
|
||||
void destroy_vmcore_overlay(struct vmcore_overlay *overlay);
|
||||
|
||||
/*
|
||||
* This method handles all file system calls and blocks until a signal arrives.
|
||||
*/
|
||||
int serve_vmcore_overlay(struct vmcore_overlay *overlay);
|
||||
|
||||
#endif
|
||||
198
hsavmcore/proxy.c
Normal file
198
hsavmcore/proxy.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
struct vmcore_proxy {
|
||||
int vmcore_fd;
|
||||
long vmcore_size;
|
||||
struct hsa_reader *hsa_reader;
|
||||
};
|
||||
|
||||
static int read_file_at(int fd, long offset, void *buf, int size)
|
||||
{
|
||||
long n, nread = 0;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"vmcore proxy vmcore read: offset=%lx size=%x\n", offset,
|
||||
size);
|
||||
|
||||
n = lseek(fd, offset, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
n = read(fd, buf + nread, size);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"read syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
nread += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
static long get_vmcore_size(int fd)
|
||||
{
|
||||
long n, size;
|
||||
|
||||
/* Get vmcore file size */
|
||||
n = lseek(fd, 0, SEEK_END);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = n;
|
||||
|
||||
/* Reset vmcore file position */
|
||||
n = lseek(fd, 0, SEEK_SET);
|
||||
if (n < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
struct vmcore_proxy *make_vmcore_proxy(const char *vmcore_path,
|
||||
struct hsa_reader *hsa_reader)
|
||||
{
|
||||
struct vmcore_proxy *proxy;
|
||||
int vmcore_fd;
|
||||
long vmcore_size;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "vmcore proxy: vmcore path=%s\n",
|
||||
vmcore_path);
|
||||
|
||||
/* Open vmcore file */
|
||||
vmcore_fd = open(vmcore_path, O_RDONLY);
|
||||
if (vmcore_fd < 0) {
|
||||
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vmcore_size = get_vmcore_size(vmcore_fd);
|
||||
if (!vmcore_size) {
|
||||
close(vmcore_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "vmcore proxy: vmcore size=%lx\n",
|
||||
vmcore_size);
|
||||
|
||||
proxy = malloc(sizeof(struct vmcore_proxy));
|
||||
if (!proxy) {
|
||||
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||
close(vmcore_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proxy->vmcore_fd = vmcore_fd;
|
||||
proxy->vmcore_size = vmcore_size;
|
||||
proxy->hsa_reader = hsa_reader;
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
void destroy_vmcore_proxy(struct vmcore_proxy *proxy)
|
||||
{
|
||||
close(proxy->vmcore_fd);
|
||||
free(proxy);
|
||||
}
|
||||
|
||||
long vmcore_proxy_size(struct vmcore_proxy *proxy)
|
||||
{
|
||||
return proxy->vmcore_size;
|
||||
}
|
||||
|
||||
int read_vmcore_proxy_at(struct vmcore_proxy *proxy, long offset, void *buf,
|
||||
int size)
|
||||
{
|
||||
const long hsa_size = hsa_get_size(proxy->hsa_reader);
|
||||
const long hsa_vmcore_offset = hsa_get_vmcore_offset(proxy->hsa_reader);
|
||||
long nread = 0;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"vmcore proxy read: offset=%lx size=%x\n", offset, size);
|
||||
|
||||
/*
|
||||
* The caller might try to read beyond the maximum length of vmcore.
|
||||
* This guarantees the termination of the loop below in that case.
|
||||
*/
|
||||
size = MIN(proxy->vmcore_size - offset, size);
|
||||
|
||||
/*
|
||||
* 0 HSA offset HSA offset + vmcore size
|
||||
* HSA size
|
||||
*
|
||||
* +---------------------+---------------------+-----------------------+
|
||||
* | | | |
|
||||
* | vmcore 1st part | HSA memory region | vmcore 2nd part |
|
||||
* | | | |
|
||||
* +---------------------+---------------------+-----------------------+
|
||||
*/
|
||||
|
||||
while (size) {
|
||||
long n, nbyte;
|
||||
|
||||
if (offset < hsa_vmcore_offset) {
|
||||
/* vmcore 1st part */
|
||||
nbyte = MIN(hsa_vmcore_offset - offset, size);
|
||||
n = read_file_at(proxy->vmcore_fd, offset, buf + nread,
|
||||
nbyte);
|
||||
} else if (offset >= hsa_vmcore_offset &&
|
||||
offset < (hsa_vmcore_offset + hsa_size)) {
|
||||
/* HSA memory region */
|
||||
nbyte = MIN(hsa_vmcore_offset + hsa_size - offset,
|
||||
size);
|
||||
n = read_hsa_at(proxy->hsa_reader,
|
||||
offset - hsa_vmcore_offset,
|
||||
buf + nread, nbyte);
|
||||
} else {
|
||||
/* vmcore 2nd part */
|
||||
nbyte = MIN(proxy->vmcore_size - offset, size);
|
||||
n = read_file_at(proxy->vmcore_fd, offset, buf + nread,
|
||||
nbyte);
|
||||
}
|
||||
|
||||
if (n != nbyte)
|
||||
return -1;
|
||||
|
||||
nread += n;
|
||||
size -= n;
|
||||
offset += n;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
35
hsavmcore/proxy.h
Normal file
35
hsavmcore/proxy.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_PROXY_H
|
||||
#define _HSAVMCORE_PROXY_H
|
||||
|
||||
#include "hsa.h"
|
||||
|
||||
/*
|
||||
* A vmcore Proxy combines the original /proc/vmcore file with a HSA memory
|
||||
* reader into a new interface which can be used to read vmcore data w/o being
|
||||
* aware that the HSA memory region is NOT contained in the file /proc/vmcore.
|
||||
*
|
||||
* After releasing the HSA memory, the original /proc/vmcore will contain
|
||||
* a *hole* where the HSA memory was located. The vmcore proxy hides this
|
||||
* inconvenience from the user of this interface.
|
||||
*/
|
||||
|
||||
struct vmcore_proxy;
|
||||
|
||||
struct vmcore_proxy *make_vmcore_proxy(const char *vmcore_path,
|
||||
struct hsa_reader *hsa_reader);
|
||||
|
||||
void destroy_vmcore_proxy(struct vmcore_proxy *proxy);
|
||||
|
||||
long vmcore_proxy_size(struct vmcore_proxy *proxy);
|
||||
|
||||
int read_vmcore_proxy_at(struct vmcore_proxy *proxy, long offset, void *buf,
|
||||
int size);
|
||||
|
||||
#endif
|
||||
51
hsavmcore/swap.c
Normal file
51
hsavmcore/swap.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/swap.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "swap.h"
|
||||
|
||||
int swap_on(const char *path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Swap on %s\n", path);
|
||||
|
||||
ret = swapon(path, 0);
|
||||
if (ret) {
|
||||
util_log_print(UTIL_LOG_ERROR, "swapon syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swap_off(const char *path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
util_log_print(UTIL_LOG_INFO, "Swap off %s\n", path);
|
||||
|
||||
ret = swapoff(path);
|
||||
if (ret) {
|
||||
util_log_print(UTIL_LOG_ERROR, "swapoff syscall failed (%s)\n",
|
||||
strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
hsavmcore/swap.h
Normal file
15
hsavmcore/swap.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _HSAVMCORE_SWAP_H
|
||||
#define _HSAVMCORE_SWAP_H
|
||||
|
||||
int swap_on(const char *path);
|
||||
|
||||
int swap_off(const char *path);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user