mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
118 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
1bb9a9ec5b | ||
|
|
4a3957fab5 | ||
|
|
e506c94839 | ||
|
|
dfd9f52873 | ||
|
|
14ca7c5080 | ||
|
|
4fc1a92a8d | ||
|
|
d73d7f91ac | ||
|
|
800df6bef8 | ||
|
|
3c661da4ee | ||
|
|
bf5ca4367d | ||
|
|
faf26220a7 | ||
|
|
522252d18a | ||
|
|
42889edc0c | ||
|
|
13016ebc5a | ||
|
|
c4546daf34 | ||
|
|
1e18429f69 | ||
|
|
11e78cada5 | ||
|
|
0a7df9e030 | ||
|
|
7dd03eaeec | ||
|
|
663262c962 | ||
|
|
bf9482709f | ||
|
|
cdf0b5d66f | ||
|
|
4aafd6962a | ||
|
|
863e1c3fa4 | ||
|
|
7c47ea8e09 | ||
|
|
c239d99379 | ||
|
|
6c6e3a2b0e | ||
|
|
2dca5d193f | ||
|
|
75675ec627 | ||
|
|
18bf2cce06 | ||
|
|
8781dd3e7b | ||
|
|
6380e77f28 | ||
|
|
733b86c02a | ||
|
|
7827a791c9 |
26
.gitignore
vendored
26
.gitignore
vendored
@@ -5,6 +5,10 @@
|
||||
*.a
|
||||
*.o.d
|
||||
|
||||
# ctags files
|
||||
tags
|
||||
TAGS
|
||||
|
||||
#
|
||||
# Ignore generated executables and other generated files
|
||||
#
|
||||
@@ -13,6 +17,7 @@ cpacfstats/cpacfstats
|
||||
cpacfstats/cpacfstatsd
|
||||
cpumf/chcpumf
|
||||
cpumf/lscpumf
|
||||
cpumf/lshwc
|
||||
cpuplugd/cpuplugd
|
||||
dasdfmt/dasdfmt
|
||||
dasdinfo/dasdinfo
|
||||
@@ -20,6 +25,7 @@ dasdview/dasdview
|
||||
dump2tar/src/dump2tar
|
||||
fdasd/fdasd
|
||||
hmcdrvfs/hmcdrvfs
|
||||
hsavmcore/hsavmcore
|
||||
hyptop/hyptop
|
||||
ip_watcher/xcec-bridge
|
||||
ipl_tools/chreipl
|
||||
@@ -39,16 +45,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
|
||||
@@ -101,6 +105,8 @@ 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
|
||||
|
||||
10
AUTHORS.md
10
AUTHORS.md
@@ -9,12 +9,14 @@ List of all individuals having contributed content to s390-tools
|
||||
- Arnd Bergmann
|
||||
- Axel Wirbser
|
||||
- Benjamin Block
|
||||
- Brian C. Lane
|
||||
- Carsten Otte
|
||||
- Christian Borntraeger
|
||||
- Christian Ehrhardt
|
||||
- Christof Schmitt
|
||||
- Claudio Imbrenda
|
||||
- Clemens von Mann
|
||||
- Colin Walters
|
||||
- Dan Horak
|
||||
- Despina Papadopoulou
|
||||
- Dimitri John Ledkov
|
||||
@@ -25,6 +27,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
|
||||
@@ -55,6 +58,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Jean-Baptiste Joret
|
||||
- Jens Remus
|
||||
- Jochen Roehrig
|
||||
- Joern Siglen
|
||||
- Juergen Christ
|
||||
- Julian Wiedmann
|
||||
- Karsten Graul
|
||||
@@ -62,6 +66,7 @@ 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
|
||||
@@ -75,11 +80,13 @@ List of all individuals having contributed content to s390-tools
|
||||
- Michael Mueller
|
||||
- Mijo Safradin
|
||||
- Mikhail Zaslonko
|
||||
- Nikita Dubrovskii
|
||||
- Niklas Schnelle
|
||||
- Peter Oberparleiter
|
||||
- Peter Tiedemann
|
||||
- Philipp Kern
|
||||
- Philipp Rudo
|
||||
- Prashanth Sundararaman
|
||||
- Rafael Fonseca
|
||||
- Raimund Schroeder
|
||||
- Ralph Wuerthner
|
||||
@@ -109,7 +116,10 @@ List of all individuals having contributed content to s390-tools
|
||||
- Tuan Hoang
|
||||
- Ursula Braun
|
||||
- Utz Bacher
|
||||
- Vance Morris
|
||||
- Vasily Gorbik
|
||||
- Viktor Mihajlovski
|
||||
- Vineeth Vijayan
|
||||
- Volker Sameske
|
||||
- Wenjia Zhang
|
||||
- Wolfgang Taphorn
|
||||
|
||||
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,14 +1,63 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __v2.15.x (2020-xx-xx)__
|
||||
* __v2.17.0 (2021-07-07)__
|
||||
|
||||
For Linux kernel version: 5.x
|
||||
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:
|
||||
- hsci: New tool to manage HSCI interfaces
|
||||
- genprotimg: Add host-key document verification support
|
||||
- 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
|
||||
|
||||
Add new tool:
|
||||
- hsci: New tool to manage HSCI (HiperSockets Converged Interfaces)
|
||||
|
||||
Changes of existing tools:
|
||||
- genprotimg: Add host-key document verification support
|
||||
- genprotimg: boot: Make boot loader -march=z900 compatible
|
||||
- libekmfweb: Make install directory for shared libraries configurable
|
||||
- lsdasd: Add FC Endpoint Security information
|
||||
- make: Add address sanitizer support
|
||||
- netboot: Add version information to scripts
|
||||
- netboot: Bump busybox version in pxelinux.0 build
|
||||
- zdev: Add FC Endpoint Security information for DASD devices
|
||||
- zdev: Add build option to update initial RAM-disk by default
|
||||
- zkey-ekmfweb: Avoid sequence number clash when generating keys
|
||||
- zkey/zkey-ekmfweb: Install KMS plugins into configurable location
|
||||
- zkey: Add support to store LUKS2 dummy passphrase in key repository
|
||||
|
||||
Bug Fixes:
|
||||
- dasdfmt: Fix segfault when an incorrect option is specified
|
||||
- genprotimg: Fix several build issues
|
||||
- genprotimg: Require argument for 'ramdisk' and 'parmfile' options
|
||||
- zcryptstats: Fix handling of partial results with many domains
|
||||
- zfcpdbf: Deal with crash 7.2.9 change in caller name formatting
|
||||
- zipl/boot: Fix memory use after free in stage2
|
||||
- zipl/boot: Fix potential heap overflow in stage2
|
||||
- zipl: Fix reading 4k disk's geometry
|
||||
|
||||
* __v2.15.1 (2020-10-28)__
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ In the examples below we use this fictive identity:
|
||||
### Setup GitHub and local git
|
||||
|
||||
1. Create a fork of this repository by clicking the `Fork` button on the top
|
||||
right of the [s390-tools](https://github.com/ibm-s390-tools/s390-tools)
|
||||
right of the [s390-tools](https://github.com/ibm-s390-linux/s390-tools)
|
||||
main page
|
||||
|
||||
2. Clone your forked repository to your local development system
|
||||
@@ -84,7 +84,7 @@ In the examples below we use this fictive identity:
|
||||
s390-tools repository on GitHub
|
||||
```
|
||||
$ cd s390-tools
|
||||
~/s390-tools $ git remote add upstream https://github.com/ibm-s390-tools/s390-tools.git
|
||||
~/s390-tools $ git remote add upstream https://github.com/ibm-s390-linux/s390-tools.git
|
||||
```
|
||||
|
||||
4. Verify your remotes
|
||||
@@ -92,8 +92,8 @@ In the examples below we use this fictive identity:
|
||||
~/s390-tools $ git remote -v
|
||||
origin https://github.com/random-developer/s390-tools.git (fetch)
|
||||
origin https://github.com/random-developer/s390-tools.git (push)
|
||||
upstream https://github.com/ibm-s390-tools/s390-tools.git (fetch)
|
||||
upstream https://github.com/ibm-s390-tools/s390-tools.git (push)
|
||||
upstream https://github.com/ibm-s390-linux/s390-tools.git (fetch)
|
||||
upstream https://github.com/ibm-s390-linux/s390-tools.git (push)
|
||||
```
|
||||
You now have two remotes: The "origin" remote points to your fork
|
||||
and the "upstream" remote to the official s390-tools repository.
|
||||
|
||||
5
Makefile
5
Makefile
@@ -3,13 +3,14 @@ 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
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
libseckey libkmipclient
|
||||
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
|
||||
|
||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
|
||||
65
README.md
65
README.md
@@ -223,7 +223,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 +250,23 @@ 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.
|
||||
|
||||
For more information refer to the following publications:
|
||||
|
||||
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||
@@ -270,24 +285,30 @@ build options:
|
||||
|
||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||
|----------------|:------------------:|:-------------------------------------:|
|
||||
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump |
|
||||
| fuse | `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 |
|
||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||
|------------------|:----------------------------:|:--------------:|
|
||||
| dracut | `HAVE_DRACUT` | zdev |
|
||||
| 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.
|
||||
@@ -378,6 +399,17 @@ the different tools are provided:
|
||||
Distributors with different boot or RAM-disk mechanisms should provide
|
||||
a custom zdev-root-update helper script.
|
||||
|
||||
- `ZDEV_ALWAYS_UPDATE_INITRD=1` upon modification of any persistent device
|
||||
configuration, chzdev updates the initial RAM-disk by default, without any
|
||||
additional user interaction.
|
||||
|
||||
For some distributions, all the configuration attributes must be copied to
|
||||
the initial RAM-disk. Because the device configuration directives applied
|
||||
in the initial RAM-disk takes precedence over those stored in the root file-
|
||||
system. This copying is done usually by explicitly invoking a command. This
|
||||
build option makes it user-friendly and does this copying without any manual
|
||||
intervention.
|
||||
|
||||
Some functions of zdev require that the following programs are available:
|
||||
|
||||
- modprobe (kmod)
|
||||
@@ -415,3 +447,18 @@ 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 2.6 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.
|
||||
|
||||
@@ -299,7 +299,7 @@ static unsigned long dec_to_hex(unsigned long long num)
|
||||
{
|
||||
unsigned long res;
|
||||
|
||||
asm volatile("cvb %0,%1" : "=d" (res) : "m" (num));
|
||||
asm volatile("cvb %0,%1" : "=d" (res) : "Q" (num));
|
||||
return res & 0xffffffff;
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ static unsigned int hex_to_dec(unsigned int num)
|
||||
{
|
||||
unsigned long long res;
|
||||
|
||||
asm volatile("cvd %1,%0" : "=m" (res) : "d" (num));
|
||||
asm volatile("cvd %1,%0" : "=Q" (res) : "d" (num));
|
||||
return res & 0xffffffff;
|
||||
}
|
||||
|
||||
|
||||
42
common.mak
42
common.mak
@@ -5,8 +5,8 @@ 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 = 15
|
||||
PATCHLEVEL = 1
|
||||
RELEASE = 17
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
export S390_TOOLS_RELEASE
|
||||
@@ -106,6 +106,25 @@ endif
|
||||
DEFAULT_CPPFLAGS = -D_GNU_SOURCE
|
||||
DEFAULT_LDFLAGS = -rdynamic
|
||||
|
||||
ifeq ("${ASAN}","1")
|
||||
DEFAULT_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
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
|
||||
#
|
||||
@@ -174,12 +193,20 @@ ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump
|
||||
# for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system)
|
||||
SYSTEMDSYSTEMUNITDIR =
|
||||
USRINCLUDEDIR = $(INSTALLDIR)/usr/include
|
||||
ZKEYKMSPLUGINDIR = $(USRLIB64DIR)/zkey
|
||||
|
||||
ifeq ($(LIBDIR),$(INSTALLDIR)/lib)
|
||||
SOINSTALLDIR = $(USRLIB64DIR)
|
||||
else
|
||||
SOINSTALLDIR = $(LIBDIR)
|
||||
endif
|
||||
|
||||
INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||
$(SYSCONFDIR) $(SYSCONFDIR)/sysconfig \
|
||||
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||
$(USRLIB64DIR) $(USRINCLUDEDIR)
|
||||
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
||||
$(SOINSTALLDIR)
|
||||
OWNER = $(shell id -un)
|
||||
GROUP = $(shell id -gn)
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
@@ -264,6 +291,7 @@ help:
|
||||
@echo ' G=1 Build with gcov to collect code coverage data'
|
||||
@echo ' V=1 Generate verbose build output'
|
||||
@echo ' W=1 Build with higher warning level'
|
||||
@echo ' ASAN=1 Build with address sanitizer'
|
||||
@echo ''
|
||||
@echo 'EXAMPLES'
|
||||
@echo ' # make clean all D=1 W=1 -j'
|
||||
@@ -346,6 +374,14 @@ $(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,7 +1,7 @@
|
||||
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)
|
||||
|
||||
@@ -9,6 +9,7 @@ libs = $(rootdir)/libutil/libutil.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
|
||||
|
||||
@@ -85,25 +85,19 @@ static long parse_buffersize(char *string)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int read_sfb(unsigned long *min, unsigned long *max)
|
||||
static void 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;
|
||||
}
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
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;
|
||||
fclose(fp);
|
||||
errx(EXIT_FAILURE, "Can not parse file " PERF_SFB_SIZE);
|
||||
} else {
|
||||
if (*min == 0)
|
||||
*min = cur_min_sdb;
|
||||
@@ -111,7 +105,9 @@ static int read_sfb(unsigned long *min, unsigned long *max)
|
||||
*max = cur_max_sdb;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
if (*min >= *max)
|
||||
errx(EXIT_FAILURE,
|
||||
"The specified maximum must be greater than the minimum");
|
||||
}
|
||||
|
||||
static int write_sfb(unsigned long min, unsigned long max)
|
||||
@@ -122,25 +118,23 @@ static int write_sfb(unsigned long min, unsigned long max)
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "w");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
||||
len = strlen(text) + 1;
|
||||
if (fwrite(text, 1, len, fp) != len) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
warn(PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (fclose(fp)) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
warn(PERF_SFB_SIZE);
|
||||
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:%7ld sample-data-blocks\n"
|
||||
" Maximum:%7ld sample-data-blocks\n",
|
||||
min, max);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -160,61 +154,44 @@ 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;
|
||||
|
||||
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 (stat(PERF_PATH PERF_SF, &sbuf))
|
||||
errx(EXIT_FAILURE,
|
||||
"No CPU-measurement sampling facility detected");
|
||||
read_sfb(&min_sdb, &max_sdb);
|
||||
return write_sfb(min_sdb, max_sdb);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,4 @@
|
||||
#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
|
||||
|
||||
136
cpumf/lscpumf.c
136
cpumf/lscpumf.c
@@ -23,9 +23,10 @@
|
||||
|
||||
#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"
|
||||
|
||||
@@ -2558,25 +2559,6 @@ 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.
|
||||
@@ -2589,13 +2571,12 @@ static int read_cpumf_type(const char *filename, const char *type)
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "No CPU-measurement %s facility detected\n",
|
||||
type);
|
||||
warnx("No CPU-measurement %s facility detected", type);
|
||||
return rc;
|
||||
}
|
||||
if (fscanf(fp, "%d", &nr) != 1)
|
||||
fprintf(stderr, "Can not parse file %s\n", filename);
|
||||
else {
|
||||
if (fscanf(fp, "%d", &nr) != 1) {
|
||||
warnx("Can not parse file %s", filename);
|
||||
} else {
|
||||
rc = EXIT_SUCCESS;
|
||||
if (nr == PERF_TYPE_RAW)
|
||||
strcat(prefix, "r");
|
||||
@@ -2639,10 +2620,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);
|
||||
}
|
||||
}
|
||||
@@ -2685,7 +2664,7 @@ 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) {
|
||||
@@ -2710,13 +2689,12 @@ 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" :
|
||||
(stat(PERF_PATH PERF_CF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_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,7 +2718,7 @@ 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" :
|
||||
(stat(PERF_PATH PERF_SF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_SF ")");
|
||||
|
||||
printf("Current sampling buffer settings for %s:\n",
|
||||
@@ -2774,40 +2752,7 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
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;
|
||||
warnx("No CPU-measurement sampling facility detected");
|
||||
}
|
||||
|
||||
/* Read CPU Measurement sampling facility device driver minimum and maximum
|
||||
@@ -2815,16 +2760,16 @@ static int read_machine(unsigned short *mt)
|
||||
*/
|
||||
static int read_sfb(struct cpumf_info *p)
|
||||
{
|
||||
FILE *fp;
|
||||
int rc = EXIT_SUCCESS;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
if (!fp) {
|
||||
warn(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);
|
||||
warnx("Can not parse %s", PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
fclose(fp);
|
||||
@@ -2843,7 +2788,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";
|
||||
@@ -2866,8 +2811,8 @@ static int read_info(void)
|
||||
|
||||
memset(&cpumf, 0, sizeof cpumf);
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (slp == NULL) {
|
||||
linux_error(SERVICELEVEL);
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -2877,7 +2822,7 @@ static int read_info(void)
|
||||
" version=%f authorization=%x",
|
||||
&cpumf.version, &cpumf.authorization);
|
||||
if (rc != 2) {
|
||||
fprintf(stderr, "Can not parse line %s", linep);
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
@@ -2891,7 +2836,7 @@ static int read_info(void)
|
||||
&cpumf.min_rate, &cpumf.max_rate,
|
||||
&cpumf.cpu_speed);
|
||||
if (rc != 3) {
|
||||
fprintf(stderr, "Can not parse line %s", linep);
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
@@ -2902,7 +2847,7 @@ static int read_info(void)
|
||||
" mode=basic sample_size=%u",
|
||||
&cpumf.basic_sample_sz);
|
||||
if (rc != 1) {
|
||||
fprintf(stderr, "Can not parse line %s", linep);
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
@@ -2912,7 +2857,7 @@ static int read_info(void)
|
||||
" mode=diagnostic sample_size=%u",
|
||||
&cpumf.diag_sample_sz);
|
||||
if (rc != 1) {
|
||||
fprintf(stderr, "Can not parse line %s", linep);
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
@@ -2923,9 +2868,11 @@ static int read_info(void)
|
||||
if (rc == EXIT_FAILURE)
|
||||
goto out;
|
||||
}
|
||||
rc = read_machine(&cpumf.machine_type);
|
||||
if (rc == EXIT_FAILURE)
|
||||
cpumf.machine_type = util_arch_machine_type();
|
||||
if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN) {
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
rc = EXIT_SUCCESS;
|
||||
out:
|
||||
fclose(slp);
|
||||
@@ -3013,33 +2960,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 +3021,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);
|
||||
|
||||
775
cpumf/lshwc.c
Normal file
775
cpumf/lshwc.c
Normal file
@@ -0,0 +1,775 @@
|
||||
/* 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 <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 "lshwc.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||
#define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max"
|
||||
#define MAXCTRS 512
|
||||
|
||||
static const unsigned int ioctlsleep = 60;
|
||||
static unsigned int read_interval = ioctlsleep, 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 */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Remove all whitespace from string. */
|
||||
static void kill_whitespace(char *s)
|
||||
{
|
||||
char *cp = s;
|
||||
|
||||
for (; *s != '\0'; ++s) {
|
||||
if (isspace(*s))
|
||||
continue;
|
||||
if (isprint(*s))
|
||||
*cp++ = *s;
|
||||
}
|
||||
*cp = '\0';
|
||||
}
|
||||
|
||||
/* Read file to get all online CPUs */
|
||||
static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||||
{
|
||||
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)
|
||||
{
|
||||
__u64 *words = start->cpumask;
|
||||
unsigned long i, no_a, no_b;
|
||||
char *cp, *tokens[16]; /* Used to parse command line params */
|
||||
char cpubuf[256];
|
||||
|
||||
start->data_bytes = 0;
|
||||
if (parm)
|
||||
kill_whitespace(parm);
|
||||
if (!parm || *parm == ':') {
|
||||
/* No CPU list or just counter sets */
|
||||
if (!get_cpus(CPUS_ONLINE, cpubuf, sizeof(cpubuf)))
|
||||
exit(EXIT_FAILURE);
|
||||
if (parm)
|
||||
strcat(cpubuf, parm);
|
||||
parm = cpubuf;
|
||||
}
|
||||
|
||||
cp = strchr(parm, ':');
|
||||
if (cp) { /* Handle counter set */
|
||||
*cp = '\0';
|
||||
start->counter_sets = parse_ctrset(++cp);
|
||||
} else {
|
||||
start->counter_sets = S390_HWCTR_ALL;
|
||||
}
|
||||
/* Check with authorized counter sets */
|
||||
if ((start->counter_sets & authorization) != start->counter_sets) {
|
||||
unsigned int noton = ~(start->counter_sets & authorization);
|
||||
|
||||
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 (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")) != 0;
|
||||
++i, parm = 0) {
|
||||
cp = strchr(tokens[i], '-'); /* Range character? */
|
||||
if (cp) {
|
||||
no_a = getnumber(tokens[i], *cp);
|
||||
no_b = getnumber(++cp, '\0');
|
||||
} else {
|
||||
no_b = getnumber(tokens[i], '\0');
|
||||
no_a = no_b;
|
||||
}
|
||||
if (!check_set(no_a, no_b, start->counter_sets))
|
||||
errx(EXIT_FAILURE, "Invalid CPU list %s", tokens[i]);
|
||||
}
|
||||
|
||||
/* 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 % __BITS_PER_LONG;
|
||||
no_b = i / __BITS_PER_LONG;
|
||||
words[no_b] |= 1ULL << no_a;
|
||||
}
|
||||
}
|
||||
/* no_b is highest used index, swap array */
|
||||
start->cpumask_len = (no_b + 1) * 8;
|
||||
for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
|
||||
__u64 tmp = words[no_a];
|
||||
|
||||
words[no_a] = words[no_b];
|
||||
words[no_b] = tmp;
|
||||
}
|
||||
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 = 0) {
|
||||
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) {
|
||||
__u64 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(__u64);
|
||||
start.cpumask = alloca(max_possible_cpus / sizeof(__u64));
|
||||
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;
|
||||
}
|
||||
|
||||
/* Read counter first and second version number */
|
||||
static bool get_cvn(void)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool good = false;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *slp;
|
||||
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return false;
|
||||
}
|
||||
while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
int rc;
|
||||
|
||||
rc = sscanf(linep, "CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
||||
&cfvn, &csvn, &authorization);
|
||||
good = rc == 3;
|
||||
if (!good)
|
||||
warnx("Cannot parse line %s", linep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
return good;
|
||||
}
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.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 (!get_cvn())
|
||||
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;
|
||||
}
|
||||
92
cpumf/lshwc.h
Normal file
92
cpumf/lshwc.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* 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 <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 */
|
||||
__u64 version; /* Version of interface */
|
||||
__u64 data_bytes; /* # of bytes required */
|
||||
__u64 cpumask_len; /* Length of CPU mask in bytes */
|
||||
__u64 *cpumask; /* Pointer to CPU mask */
|
||||
__u64 counter_sets; /* Bit mask of counter set to get */
|
||||
};
|
||||
|
||||
struct s390_hwctr_setdata { /* Counter set data */
|
||||
__u32 set; /* Counter set number */
|
||||
__u32 no_cnts; /* # of counters stored in cv[] */
|
||||
__u64 cv[0]; /* Counter values (variable length) */
|
||||
};
|
||||
|
||||
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||||
__u32 cpu_nr; /* Counter set number */
|
||||
__u32 no_sets; /* # of counters sets in data[] */
|
||||
struct s390_hwctr_setdata data[0];
|
||||
};
|
||||
|
||||
struct s390_hwctr_read { /* Structure to get all ctr sets */
|
||||
__u64 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)
|
||||
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"
|
||||
|
||||
@@ -1232,7 +1232,7 @@ static void fdasd_reread_partition_table(fdasd_anchor_t *anc)
|
||||
if (!anc->silent)
|
||||
printf("rereading partition table...\n");
|
||||
|
||||
if (dasd_reread_partition_table(options.device, 1) != 0) {
|
||||
if (dasd_reread_partition_table(options.device, 5) != 0) {
|
||||
fdasd_error(anc, unable_to_ioctl, "Error while rereading "
|
||||
"partition table.\nPlease reboot!");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Entry code for stage 3a boot loader
|
||||
* Entry code for stage 3a and stage 3b boot loader
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
@@ -24,6 +24,8 @@ _start:
|
||||
sam64
|
||||
|
||||
/* Initialize stack */
|
||||
lgfi %r15, STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD
|
||||
basr %r13, 0
|
||||
.Lbase: llgf %r15, .Lstack - .Lbase(%r13)
|
||||
brasl %r14, initialize
|
||||
.Lstack: .long STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD
|
||||
.previous
|
||||
|
||||
@@ -21,6 +21,8 @@ _init:
|
||||
* kernel command line and the address and size of the
|
||||
* ramdisk. Simply ignore this by starting at 0x11000.
|
||||
*/
|
||||
lgfi %r1, STAGE3A_ENTRY
|
||||
basr %r13, 0
|
||||
.Lbase: llgf %r1, .Lstage3a_entry - .Lbase(%r13)
|
||||
br %r1
|
||||
.Lstage3a_entry: .long STAGE3A_ENTRY
|
||||
.previous
|
||||
|
||||
@@ -31,12 +31,12 @@ stage3b_reloc_start:
|
||||
sigp %r1, %r0, SIGP_SET_ARCHITECTURE
|
||||
sam64
|
||||
|
||||
.copy_stage3b:
|
||||
/* Location of stage3b in memory */
|
||||
larl %r8, stage3b_start
|
||||
|
||||
/* Destination for stage3b */
|
||||
lgfi %r9, STAGE3B_LOAD_ADDRESS
|
||||
basr %r13, 0
|
||||
.Lbase: llgf %r9, .Lstage3b_load_address - .Lbase(%r13)
|
||||
|
||||
/* Size of stage3b */
|
||||
lghi %r11, stage3b_end - stage3b_start
|
||||
@@ -45,8 +45,10 @@ stage3b_reloc_start:
|
||||
MEMCPY %r9, %r8, %r11
|
||||
|
||||
/* Branch to STAGE3B_ENTRY */
|
||||
lgfi %r9, STAGE3B_ENTRY
|
||||
llgf %r9, .Lstage3b_entry - .Lbase(%r13)
|
||||
br %r9
|
||||
.Lstage3b_load_address: .long STAGE3B_LOAD_ADDRESS
|
||||
.Lstage3b_entry: .long STAGE3B_ENTRY
|
||||
stage3b_start:
|
||||
.incbin "stage3b.bin"
|
||||
stage3b_end:
|
||||
|
||||
@@ -94,6 +94,14 @@ this only if you trust the specified certificate. Optional.
|
||||
Do not require the host-key documents to be valid. For testing
|
||||
purposes, do not use for a production image. 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.
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ INCLUDE_PARMS = $(addprefix -I,$(INCLUDE_PATHS))
|
||||
|
||||
WARNINGS := -Wall -Wextra -Wshadow \
|
||||
-Wcast-align -Wwrite-strings -Wmissing-prototypes \
|
||||
-Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline \
|
||||
-Wmissing-declarations -Wredundant-decls -Wnested-externs \
|
||||
-Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \
|
||||
-Wpointer-arith -Werror \
|
||||
-Wpointer-arith -Werror -Wno-error=inline \
|
||||
$(NULL)
|
||||
|
||||
$(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \
|
||||
@@ -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)
|
||||
|
||||
@@ -24,8 +24,11 @@
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_CFLAG_NO_DECRYPTION 0x10000000ULL
|
||||
/* Plaintext control flags */
|
||||
#define PV_PCF_PCKM_ECC (1ULL << 5) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES (1ULL << 6) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA (1ULL << 7) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_NO_DECRYPTION (1ULL << 28) /* prevent Ultravisor decryption during unpack operation */
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -64,6 +64,13 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
{
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
if (args->pcf && args->allow_pckmo != PV_NOT_SET) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-pcf' and '--(enable|disable)-pckmo' options are mutually"
|
||||
" exclusive.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args->unused_values->len > 0) {
|
||||
g_autofree gchar *unused = NULL;
|
||||
|
||||
@@ -181,6 +188,19 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_enable_disable_flag(const gchar *option, const gchar *value G_GNUC_UNUSED,
|
||||
PvArgs *args, GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
if (g_str_equal(option, "--enable-pckmo"))
|
||||
args->allow_pckmo = PV_TRUE;
|
||||
else if (g_str_equal(option, "--disable-pckmo"))
|
||||
args->allow_pckmo = PV_FALSE;
|
||||
else
|
||||
g_assert_not_reached();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_set_log_level(const gchar *option G_GNUC_UNUSED,
|
||||
const gchar *value G_GNUC_UNUSED, PvArgs *args,
|
||||
GError **err G_GNUC_UNUSED)
|
||||
@@ -262,6 +282,21 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("PARMFILE") },
|
||||
{.long_name = "enable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||
"(optional).")},
|
||||
{.long_name = "disable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (optional).")},
|
||||
{ .long_name = "crl",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
@@ -357,7 +392,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 +446,7 @@ PvArgs *pv_args_new(void)
|
||||
g_autoptr(PvArgs) args = g_new0(PvArgs, 1);
|
||||
|
||||
args->unused_values = g_ptr_array_new_with_free_func(g_free);
|
||||
args->allow_pckmo = PV_NOT_SET;
|
||||
return g_steal_pointer(&args);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,19 @@ 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_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;
|
||||
@@ -301,7 +301,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 +383,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 +396,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 +411,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 +428,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,7 @@ 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_pckmo, GError **err)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
@@ -247,6 +247,11 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
img->scf = flags;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -485,23 +490,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 +535,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;
|
||||
@@ -589,6 +594,7 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **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 +608,7 @@ 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_pckmo, err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* read in the keys */
|
||||
@@ -637,13 +643,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 +690,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 +714,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 +831,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 +851,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 +872,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 +893,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"),
|
||||
@@ -440,10 +441,14 @@ 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);
|
||||
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);
|
||||
@@ -1428,7 +1439,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 +1454,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 +1474,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,9 +1484,9 @@ 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)
|
||||
g_steal_pointer(&crls);
|
||||
return g_steal_pointer(&crls);
|
||||
g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
|
||||
crl = lookup_crl(nm);
|
||||
if (!crl) {
|
||||
@@ -1517,19 +1528,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 +1656,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 +1767,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 +1858,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 +1867,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 +1908,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 +1940,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 +2006,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 +2110,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,8 +117,8 @@ 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,
|
||||
@@ -152,24 +152,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
|
||||
86
hsavmcore/Makefile
Normal file
86
hsavmcore/Makefile
Normal file
@@ -0,0 +1,86 @@
|
||||
#
|
||||
# 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 fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
else
|
||||
FUSE_CFLAGS = -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
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", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
|
||||
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)
|
||||
|
||||
.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 fuse fuse-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 fuse fuse-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 fuse libfuse-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
|
||||
209
hsavmcore/overlay.c
Normal file
209
hsavmcore/overlay.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 26
|
||||
#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 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)
|
||||
{
|
||||
(void)offset;
|
||||
(void)fi;
|
||||
|
||||
if (strcmp(path, ROOT_DIR) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* We have only one file */
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0);
|
||||
|
||||
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
|
||||
23
hsci/hsci
23
hsci/hsci
@@ -22,6 +22,7 @@ Usage: hsci COMMAND [OPTION]
|
||||
|
||||
This tool is designed to control and show HSCI (HiperSockets Converged
|
||||
Interfaces) settings. A HiperSockets interface and an external network
|
||||
interface are converged into an HSCI interface.
|
||||
|
||||
COMMANDS
|
||||
add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface
|
||||
@@ -145,7 +146,7 @@ function verify_precon {
|
||||
#Check PNETIDs
|
||||
check_pnetids
|
||||
if [ $? -ne 0 ]; then
|
||||
return $?
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
@@ -174,7 +175,7 @@ function add_hsci {
|
||||
#### Verify preconditions
|
||||
verify_precon
|
||||
if [ $? -ne 0 ]; then
|
||||
return $?
|
||||
return 1
|
||||
fi
|
||||
|
||||
hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)"
|
||||
@@ -252,16 +253,6 @@ function add_hsci {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# use hsdev MTU
|
||||
if [ -e /sys/class/net/$hsdev/mtu ]; then
|
||||
hs_mtu="$(cat /sys/class/net/$hsdev/mtu)"
|
||||
ip link set dev $hsci mtu $hs_mtu >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set MTU for $hsci " >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
ip link set dev $hsci up >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set $hsci up" >&2
|
||||
@@ -302,10 +293,14 @@ function del_hsci {
|
||||
|
||||
bports="$(ip link show | grep "master $hsci" | awk '{print $2}')"
|
||||
for bport in $bports; do
|
||||
bport=${bport%:}
|
||||
if [[ $bport == *@* ]]; then
|
||||
bport=${bport%@*}
|
||||
fi
|
||||
if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then
|
||||
hsdev=${bport%:}
|
||||
hsdev=$bport
|
||||
else
|
||||
ndev=${bport%:}
|
||||
ndev=$bport
|
||||
fi
|
||||
done
|
||||
if [ "$hsdev" == "" ]; then
|
||||
|
||||
2066
include/kmipclient/kmipclient.h
Normal file
2066
include/kmipclient/kmipclient.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -115,7 +115,7 @@
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "vtoc.h"
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
|
||||
/**
|
||||
@@ -834,6 +834,11 @@ void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log);
|
||||
int lzds_dshandle_set_seekbuffer(struct dshandle *dsh,
|
||||
unsigned long long seek_buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Set iconv handle for codepage conversion.
|
||||
*/
|
||||
int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv);
|
||||
|
||||
/**
|
||||
* @brief Get the size of the data set in number of tracks (sum of all extents).
|
||||
*/
|
||||
|
||||
39
include/lib/util_arch.h
Normal file
39
include/lib/util_arch.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @defgroup util_arch_h util_arch: General architecture helpers
|
||||
* @{
|
||||
* @brief General architecture helpers
|
||||
*
|
||||
* 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 LIB_UTIL_ARCH_H
|
||||
#define LIB_UTIL_ARCH_H
|
||||
|
||||
enum util_arch_machine_type {
|
||||
UTIL_ARCH_MACHINE_TYPE_UNKNOWN = 0,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z10_EC = 2097,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z10_BC = 2098,
|
||||
UTIL_ARCH_MACHINE_TYPE_ZE_196 = 2817,
|
||||
UTIL_ARCH_MACHINE_TYPE_ZE_114 = 2818,
|
||||
UTIL_ARCH_MACHINE_TYPE_ZE_EC12 = 2827,
|
||||
UTIL_ARCH_MACHINE_TYPE_ZE_BC12 = 2828,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z13 = 2964,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z13_S = 2965,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z14 = 3906,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z14_ZR1 = 3907,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z15 = 8561,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z15_T02 = 8562,
|
||||
};
|
||||
|
||||
int util_arch_machine_type(void);
|
||||
|
||||
const char *util_arch_machine_type_str(void);
|
||||
|
||||
const char *util_arch_machine_type_to_str(int type);
|
||||
|
||||
unsigned long util_arch_hsa_maxsize(void);
|
||||
|
||||
#endif /** LIB_UTIL_ARCH_H @} */
|
||||
28
include/lib/util_log.h
Normal file
28
include/lib/util_log.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @defgroup util_log_h util_log: Multi-level message logging interface
|
||||
* @{
|
||||
* @brief Multi-level message logging
|
||||
*
|
||||
* 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 LIB_UTIL_LOG_H
|
||||
#define LIB_UTIL_LOG_H
|
||||
|
||||
enum util_log_level {
|
||||
UTIL_LOG_ERROR,
|
||||
UTIL_LOG_WARN,
|
||||
UTIL_LOG_INFO,
|
||||
UTIL_LOG_DEBUG,
|
||||
UTIL_LOG_TRACE,
|
||||
UTIL_LOG_NUM_LEVELS /* Must be the last one. */
|
||||
};
|
||||
|
||||
void util_log_set_level(int log_level);
|
||||
|
||||
void util_log_print(int log_level, const char *fmt, ...);
|
||||
|
||||
#endif /** LIB_UTIL_LOG_H @} */
|
||||
@@ -27,25 +27,10 @@ struct util_proc_dev_entry {
|
||||
char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for the fields of the output of /proc/mounts (man fstab)
|
||||
*/
|
||||
struct util_proc_mnt_entry {
|
||||
char *spec;
|
||||
char *file;
|
||||
char *vfstype;
|
||||
char *mntOpts;
|
||||
char *dump;
|
||||
char *passno;
|
||||
};
|
||||
|
||||
int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry);
|
||||
void util_proc_part_free_entry(struct util_proc_part_entry *entry);
|
||||
int util_proc_dev_get_entry(dev_t dev, int blockdev,
|
||||
struct util_proc_dev_entry *entry);
|
||||
void util_proc_dev_free_entry(struct util_proc_dev_entry *entry);
|
||||
int util_proc_mnt_get_entry(const char *file_name, const char *spec,
|
||||
struct util_proc_mnt_entry *entry);
|
||||
void util_proc_mnt_free_entry(struct util_proc_mnt_entry *entry);
|
||||
|
||||
#endif /* LIB_UTIL_PROC_H */
|
||||
|
||||
48
include/libseckey/sk_cca.h
Normal file
48
include/libseckey/sk_cca.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* libseckey - Secure key library
|
||||
*
|
||||
* 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 SK_CCA_H
|
||||
#define SK_CCA_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "libseckey/sk_openssl.h"
|
||||
|
||||
#define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500
|
||||
|
||||
int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib,
|
||||
int curve_nid, unsigned char *key_token,
|
||||
size_t *key_token_length, bool debug);
|
||||
|
||||
int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib,
|
||||
size_t modulus_bits, unsigned int pub_exp,
|
||||
unsigned char *key_token,
|
||||
size_t *key_token_length, bool debug);
|
||||
|
||||
int SK_CCA_get_key_type(const unsigned char *key_token, size_t key_token_length,
|
||||
int *pkey_type);
|
||||
|
||||
int SK_CCA_get_secure_key_as_pkey(const struct sk_ext_cca_lib *cca_lib,
|
||||
const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
bool rsa_pss, EVP_PKEY **pkey, bool debug);
|
||||
|
||||
int SK_CCA_get_public_from_secure_key(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
sk_pub_key_func_t pub_key_cb,
|
||||
void *private,
|
||||
bool debug);
|
||||
|
||||
int SK_CCA_reencipher_key(const struct sk_ext_cca_lib *cca_lib,
|
||||
unsigned char *key_token, size_t key_token_length,
|
||||
bool to_new, bool debug);
|
||||
|
||||
#endif
|
||||
177
include/libseckey/sk_ep11.h
Normal file
177
include/libseckey/sk_ep11.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* libseckey - Secure key library
|
||||
*
|
||||
* 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 SK_EP11_H
|
||||
#define SK_EP11_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "libseckey/sk_openssl.h"
|
||||
|
||||
#define EP11_MAX_KEY_TOKEN_SIZE 8192
|
||||
|
||||
int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
int curve_nid, unsigned char *key_token,
|
||||
size_t *key_token_length, bool debug);
|
||||
|
||||
int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
size_t modulus_bits, unsigned int pub_exp,
|
||||
bool x9_31, unsigned char *key_token,
|
||||
size_t *key_token_length, bool debug);
|
||||
|
||||
int SK_EP11_get_key_type(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
int *pkey_type);
|
||||
|
||||
const unsigned char *SK_EP11_get_key_blob(const unsigned char *key_token,
|
||||
size_t key_token_length);
|
||||
|
||||
size_t SK_EP11_get_key_blob_size(const unsigned char *key_token,
|
||||
size_t key_token_length);
|
||||
|
||||
int SK_EP11_get_secure_key_as_pkey(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
bool rsa_pss, EVP_PKEY **pkey, bool debug);
|
||||
|
||||
int SK_EP11_get_public_from_secure_key(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
sk_pub_key_func_t pub_key_cb,
|
||||
void *private,
|
||||
bool debug);
|
||||
|
||||
int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
unsigned char *key_token, size_t key_token_length,
|
||||
bool debug);
|
||||
|
||||
/* PKCS#11 definitions */
|
||||
|
||||
#define CK_PTR *
|
||||
|
||||
typedef unsigned char CK_BYTE;
|
||||
typedef CK_BYTE CK_CHAR;
|
||||
typedef CK_BYTE CK_UTF8CHAR;
|
||||
typedef CK_BYTE CK_BBOOL;
|
||||
typedef unsigned long CK_ULONG;
|
||||
typedef long CK_LONG;
|
||||
typedef CK_ULONG CK_FLAGS;
|
||||
typedef CK_ULONG CK_RV;
|
||||
typedef CK_ULONG CK_SLOT_ID;
|
||||
typedef CK_ULONG CK_MECHANISM_TYPE;
|
||||
typedef CK_ULONG CK_ATTRIBUTE_TYPE;
|
||||
typedef CK_ULONG CK_OBJECT_CLASS;
|
||||
typedef CK_ULONG CK_KEY_TYPE;
|
||||
typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
|
||||
typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
|
||||
|
||||
typedef CK_BYTE CK_PTR CK_BYTE_PTR;
|
||||
typedef CK_CHAR CK_PTR CK_CHAR_PTR;
|
||||
typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR;
|
||||
typedef CK_ULONG CK_PTR CK_ULONG_PTR;
|
||||
typedef void CK_PTR CK_VOID_PTR;
|
||||
typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
|
||||
typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
|
||||
typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
|
||||
|
||||
typedef struct CK_MECHANISM {
|
||||
CK_MECHANISM_TYPE mechanism;
|
||||
CK_VOID_PTR pParameter;
|
||||
CK_ULONG ulParameterLen;
|
||||
} CK_MECHANISM;
|
||||
|
||||
typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
|
||||
|
||||
typedef struct CK_ATTRIBUTE {
|
||||
CK_ATTRIBUTE_TYPE type;
|
||||
CK_VOID_PTR pValue;
|
||||
CK_ULONG ulValueLen;
|
||||
} CK_ATTRIBUTE;
|
||||
|
||||
typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
|
||||
|
||||
typedef struct CK_RSA_PKCS_PSS_PARAMS {
|
||||
CK_MECHANISM_TYPE hashAlg;
|
||||
CK_RSA_PKCS_MGF_TYPE mgf;
|
||||
CK_ULONG sLen;
|
||||
} CK_RSA_PKCS_PSS_PARAMS;
|
||||
|
||||
typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
|
||||
|
||||
typedef struct CK_RSA_PKCS_OAEP_PARAMS {
|
||||
CK_MECHANISM_TYPE hashAlg;
|
||||
CK_RSA_PKCS_MGF_TYPE mgf;
|
||||
CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
|
||||
CK_VOID_PTR pSourceData;
|
||||
CK_ULONG ulSourceDataLen;
|
||||
} CK_RSA_PKCS_OAEP_PARAMS;
|
||||
|
||||
typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
|
||||
|
||||
#define CKZ_DATA_SPECIFIED 0x00000001
|
||||
|
||||
#define CKG_MGF1_SHA1 0x00000001
|
||||
#define CKG_MGF1_SHA224 0x00000005
|
||||
#define CKG_MGF1_SHA256 0x00000002
|
||||
#define CKG_MGF1_SHA384 0x00000003
|
||||
#define CKG_MGF1_SHA512 0x00000004
|
||||
|
||||
#define CKG_VENDOR_DEFINED 0x80000000UL
|
||||
#define CKG_IBM_MGF1_SHA3_224 (CKG_VENDOR_DEFINED + 1)
|
||||
#define CKG_IBM_MGF1_SHA3_256 (CKG_VENDOR_DEFINED + 2)
|
||||
#define CKG_IBM_MGF1_SHA3_384 (CKG_VENDOR_DEFINED + 3)
|
||||
#define CKG_IBM_MGF1_SHA3_512 (CKG_VENDOR_DEFINED + 4)
|
||||
|
||||
#define CKR_OK 0x00000000
|
||||
#define CKR_VENDOR_DEFINED 0x80000000
|
||||
|
||||
#define CKO_PUBLIC_KEY 0x00000002
|
||||
#define CKO_PRIVATE_KEY 0x00000003
|
||||
|
||||
#define CKK_EC 0x00000003
|
||||
|
||||
#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000
|
||||
#define CKM_RSA_PKCS 0x00000001
|
||||
#define CKM_RSA_PKCS_OAEP 0x00000009
|
||||
#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000A
|
||||
#define CKM_RSA_X9_31 0x0000000B
|
||||
#define CKM_RSA_PKCS_PSS 0x0000000D
|
||||
#define CKM_SHA_1 0x00000220
|
||||
#define CKM_SHA256 0x00000250
|
||||
#define CKM_SHA224 0x00000255
|
||||
#define CKM_SHA384 0x00000260
|
||||
#define CKM_SHA512 0x00000270
|
||||
#define CKM_SHA512_224 0x00000048
|
||||
#define CKM_SHA512_256 0x0000004C
|
||||
#define CKM_EC_KEY_PAIR_GEN 0x00001040
|
||||
#define CKM_ECDSA 0x00001041
|
||||
|
||||
#define CKM_VENDOR_DEFINED 0x80000000
|
||||
#define CKM_IBM_SHA3_224 (CKM_VENDOR_DEFINED + 0x00010001)
|
||||
#define CKM_IBM_SHA3_256 (CKM_VENDOR_DEFINED + 0x00010002)
|
||||
#define CKM_IBM_SHA3_384 (CKM_VENDOR_DEFINED + 0x00010003)
|
||||
#define CKM_IBM_SHA3_512 (CKM_VENDOR_DEFINED + 0x00010004)
|
||||
|
||||
#define CKA_CLASS 0x00000000
|
||||
#define CKA_KEY_TYPE 0x00000100
|
||||
#define CKA_SENSITIVE 0x00000103
|
||||
#define CKA_ENCRYPT 0x00000104
|
||||
#define CKA_DECRYPT 0x00000105
|
||||
#define CKA_SIGN 0x00000108
|
||||
#define CKA_VERIFY 0x0000010A
|
||||
#define CKA_DERIVE 0x0000010C
|
||||
#define CKA_DECRYPT 0x00000105
|
||||
#define CKA_WRAP 0x00000106
|
||||
#define CKA_UNWRAP 0x00000107
|
||||
#define CKA_MODULUS_BITS 0x00000121
|
||||
#define CKA_PUBLIC_EXPONENT 0x00000122
|
||||
#define CKA_EC_PARAMS 0x00000180
|
||||
|
||||
#endif
|
||||
234
include/libseckey/sk_openssl.h
Normal file
234
include/libseckey/sk_openssl.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* libseckey - Secure key library
|
||||
*
|
||||
* 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 SK_OPENSSL_H
|
||||
#define SK_OPENSSL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
#ifndef OPENSSL_VERSION_PREREQ
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)
|
||||
#define OPENSSL_VERSION_PREREQ(maj, min) \
|
||||
((OPENSSL_VERSION_MAJOR << 16) + \
|
||||
OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))
|
||||
#else
|
||||
#define OPENSSL_VERSION_PREREQ(maj, min) \
|
||||
(OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \
|
||||
((min) << 20)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* External crypto library definitions
|
||||
*/
|
||||
|
||||
struct sk_ext_cca_lib {
|
||||
void *cca_lib; /* Handle of CCA host library loaded via dlopen */
|
||||
};
|
||||
|
||||
typedef uint64_t target_t;
|
||||
|
||||
struct sk_ext_ep11_lib {
|
||||
void *ep11_lib; /* Handle of EP11 host library loaded via dlopen */
|
||||
target_t target; /* single or group target handle */
|
||||
};
|
||||
|
||||
enum sk_ext_lib_type {
|
||||
SK_EXT_LIB_CCA = 1,
|
||||
SK_EXT_LIB_EP11 = 2,
|
||||
};
|
||||
|
||||
struct sk_ext_lib {
|
||||
enum sk_ext_lib_type type;
|
||||
union {
|
||||
struct sk_ext_cca_lib *cca; /* Used if type = EXT_LIB_CCA */
|
||||
struct sk_ext_ep11_lib *ep11; /* Used if type = EXT_LIB_EP11 */
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Secure key library initialization and termination functions
|
||||
*/
|
||||
|
||||
int SK_OPENSSL_init(bool debug);
|
||||
void SK_OPENSSL_term(void);
|
||||
|
||||
/*
|
||||
* Secure key generation and reenciphering definitions and functions
|
||||
*/
|
||||
|
||||
enum sk_key_type {
|
||||
SK_KEY_TYPE_EC = 1,
|
||||
SK_KEY_TYPE_RSA = 2,
|
||||
};
|
||||
|
||||
struct sk_key_gen_info {
|
||||
enum sk_key_type type;
|
||||
union {
|
||||
struct {
|
||||
int curve_nid;
|
||||
} ec;
|
||||
struct {
|
||||
size_t modulus_bits;
|
||||
unsigned int pub_exp;
|
||||
bool x9_31;
|
||||
} rsa;
|
||||
};
|
||||
};
|
||||
|
||||
int SK_OPENSSL_generate_secure_key(unsigned char *secure_key,
|
||||
size_t *secure_key_size,
|
||||
const struct sk_key_gen_info *info,
|
||||
const struct sk_ext_lib *ext_lib,
|
||||
bool debug);
|
||||
|
||||
int SK_OPENSSL_reencipher_secure_key(unsigned char *secure_key,
|
||||
size_t secure_key_size, bool to_new,
|
||||
const struct sk_ext_lib *ext_lib,
|
||||
bool debug);
|
||||
|
||||
/*
|
||||
* Get an OpenSSL PKEY from a secure key to be used with OpenSSL.
|
||||
*/
|
||||
int SK_OPENSSL_get_secure_key_as_pkey(const unsigned char *secure_key,
|
||||
size_t secure_key_size, bool rsa_pss,
|
||||
EVP_PKEY **pkey,
|
||||
const struct sk_ext_lib *ext_lib,
|
||||
bool debug);
|
||||
|
||||
/*
|
||||
* Get the public key parts from a secure key.
|
||||
*/
|
||||
struct sk_pub_key_info {
|
||||
enum sk_key_type type;
|
||||
union {
|
||||
struct {
|
||||
int curve_nid;
|
||||
size_t prime_len;
|
||||
const unsigned char *x;
|
||||
const unsigned char *y;
|
||||
} ec;
|
||||
struct {
|
||||
size_t modulus_len;
|
||||
const unsigned char *modulus;
|
||||
size_t pub_exp_len;
|
||||
const unsigned char *pub_exp;
|
||||
} rsa;
|
||||
};
|
||||
};
|
||||
|
||||
typedef int (*sk_pub_key_func_t)(const struct sk_pub_key_info *pub_key,
|
||||
void *private);
|
||||
|
||||
int SK_OPENSSL_get_public_from_secure_key(const unsigned char *secure_key,
|
||||
size_t secure_key_size,
|
||||
sk_pub_key_func_t pub_key_cb,
|
||||
void *private,
|
||||
const struct sk_ext_lib *ext_lib,
|
||||
bool debug);
|
||||
|
||||
/*
|
||||
* Helper functions to setup a secure key sign context and to generate
|
||||
* certificate signing requests or self signed certificates with the secure key
|
||||
*/
|
||||
|
||||
struct sk_rsa_pss_params {
|
||||
/*
|
||||
* salt length in bytes, or OpenSSL constants
|
||||
* RSA_PSS_SALTLEN_DIGEST (-1), RSA_PSS_SALTLEN_AUTO (-2), or
|
||||
* RSA_PSS_SALTLEN_MAX(-3)
|
||||
*/
|
||||
int salt_len;
|
||||
/*
|
||||
* OpenSSl digest nid, or NID_undef to use the same digest algorithm
|
||||
* as the signature algorithm
|
||||
*/
|
||||
int mgf_digest_nid;
|
||||
};
|
||||
|
||||
int SK_OPENSSL_setup_sign_context(EVP_PKEY *pkey, bool verify, int digest_nid,
|
||||
struct sk_rsa_pss_params *rsa_pss_params,
|
||||
EVP_MD_CTX **md_ctx, EVP_PKEY_CTX **pkey_ctx,
|
||||
bool debug);
|
||||
|
||||
int SK_OPENSSL_generate_csr(const unsigned char *secure_key,
|
||||
size_t secure_key_size,
|
||||
const char *subject_rdns[], size_t num_subject_rdns,
|
||||
bool subject_utf8, const X509 *renew_cert,
|
||||
const char *extensions[], size_t num_extensions,
|
||||
int digest_nid,
|
||||
struct sk_rsa_pss_params *rsa_pss_params,
|
||||
X509_REQ **csr,
|
||||
const struct sk_ext_lib *ext_lib, bool debug);
|
||||
|
||||
int SK_OPENSSL_generate_ss_cert(const unsigned char *secure_key,
|
||||
size_t secure_key_size,
|
||||
const char *subject_rdns[],
|
||||
size_t num_subject_rdns, bool subject_utf8,
|
||||
const X509 *renew_cert,
|
||||
const char *extensions[], size_t num_extensions,
|
||||
int validity_days, int digest_nid,
|
||||
struct sk_rsa_pss_params *rsa_pss_params,
|
||||
X509 **ss_cert,
|
||||
const struct sk_ext_lib *ext_lib, bool debug);
|
||||
|
||||
/*
|
||||
* Import secure keys as PKEY, or import clear public keys as PKEY
|
||||
*/
|
||||
|
||||
typedef int (*sk_rsa_sign_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length,
|
||||
unsigned char *sig, size_t *siglen,
|
||||
const unsigned char *tbs, size_t tbslen,
|
||||
int padding_type, int md_nid,
|
||||
void *private, bool debug);
|
||||
typedef int (*sk_rsa_pss_sign_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length, unsigned char *sig,
|
||||
size_t *siglen, const unsigned char *tbs,
|
||||
size_t tbslen, int md_nid, int mfgmd_nid,
|
||||
int saltlen, void *private, bool debug);
|
||||
typedef int (*sk_ecdsa_sign_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length, unsigned char *sig,
|
||||
size_t *siglen, const unsigned char *tbs,
|
||||
size_t tbslen, int md_nid, void *private,
|
||||
bool debug);
|
||||
typedef int (*sk_rsa_decrypt_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length,
|
||||
unsigned char *to, size_t *tolen,
|
||||
const unsigned char *from, size_t fromlen,
|
||||
int padding_type, void *private, bool debug);
|
||||
typedef int (*sk_rsa_decrypt_oaep_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length,
|
||||
unsigned char *to, size_t *tolen,
|
||||
const unsigned char *from, size_t fromlen,
|
||||
int oaep_md_nid, int mgfmd_nid,
|
||||
unsigned char *label, int label_len,
|
||||
void *private, bool debug);
|
||||
|
||||
struct sk_funcs {
|
||||
sk_rsa_sign_t rsa_sign;
|
||||
sk_rsa_pss_sign_t rsa_pss_sign;
|
||||
sk_ecdsa_sign_t ecdsa_sign;
|
||||
sk_rsa_decrypt_t rsa_decrypt;
|
||||
sk_rsa_decrypt_oaep_t rsa_decrypt_oaep;
|
||||
};
|
||||
|
||||
int SK_OPENSSL_get_pkey(const unsigned char *secure_key, size_t secure_key_size,
|
||||
const struct sk_pub_key_info *pub_key, bool rsa_pss,
|
||||
const struct sk_funcs *sk_funcs, const void *private,
|
||||
EVP_PKEY **pkey, bool debug);
|
||||
|
||||
int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey);
|
||||
|
||||
#endif
|
||||
89
include/libseckey/sk_utilities.h
Normal file
89
include/libseckey/sk_utilities.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* libseckey - Secure key library
|
||||
*
|
||||
* 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 SK_UTILITIES_H
|
||||
#define SK_UTILITIES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "libseckey/sk_openssl.h"
|
||||
|
||||
void SK_UTIL_warnx(const char *func, const char *fmt, ...);
|
||||
|
||||
#define sk_debug(debug, fmt...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
SK_UTIL_warnx(__func__, fmt); \
|
||||
} while (0)
|
||||
|
||||
/* EC curve information definitions and functions */
|
||||
struct sk_ec_curve_info {
|
||||
int curve_nid;
|
||||
enum {
|
||||
SK_EC_TYPE_PRIME = 0,
|
||||
SK_EC_TYPE_BRAINPOOL = 1,
|
||||
} type;
|
||||
size_t prime_bits;
|
||||
size_t prime_len;
|
||||
const unsigned char *der; /* DER encoded OID */
|
||||
size_t der_size;
|
||||
};
|
||||
|
||||
const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid);
|
||||
int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits);
|
||||
int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits);
|
||||
|
||||
int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len,
|
||||
const unsigned char *x, int y_bit,
|
||||
unsigned char *y);
|
||||
|
||||
/* Digest information definitions and functions */
|
||||
struct sk_digest_info {
|
||||
int digest_nid;
|
||||
size_t digest_size;
|
||||
const char *cca_keyword;
|
||||
const unsigned char *der; /* DER encoded SEQ of OID and OCT-STRING */
|
||||
size_t der_size;
|
||||
unsigned long pkcs11_mech;
|
||||
unsigned long pkcs11_mgf;
|
||||
unsigned char x9_31_md; /* X9.31 digest identifier */
|
||||
};
|
||||
|
||||
const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid);
|
||||
|
||||
/* Helper functions for certificate and CSR handling */
|
||||
int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[],
|
||||
size_t num_rdns, bool utf8);
|
||||
int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req,
|
||||
const char *exts[], size_t num_exts,
|
||||
const STACK_OF(X509_EXTENSION)
|
||||
*addl_exts);
|
||||
int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size);
|
||||
|
||||
int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig,
|
||||
size_t raw_sig_len,
|
||||
unsigned char *sig, size_t *sig_len);
|
||||
|
||||
/* Functions to read and write keys, certificates, requests, etc. */
|
||||
int SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert);
|
||||
int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert);
|
||||
int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req,
|
||||
bool new_hdr);
|
||||
int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob,
|
||||
size_t *key_blob_len);
|
||||
int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob,
|
||||
size_t key_blob_len);
|
||||
int SK_UTIL_read_public_key(const char *pem_filename, EVP_PKEY **pkey);
|
||||
int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey);
|
||||
|
||||
#endif
|
||||
@@ -91,6 +91,7 @@ int dasd_sys_ese(char *devnode)
|
||||
|
||||
rc = fgetc(fp) - '0';
|
||||
fclose(fp);
|
||||
free(path);
|
||||
|
||||
return (rc == 1) ? 1 : 0;
|
||||
}
|
||||
@@ -218,7 +219,8 @@ int dasd_get_host_access_count(char *device)
|
||||
return 0;
|
||||
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid);
|
||||
util_file_read_l(&value, 10, path);
|
||||
if (util_file_read_l(&value, 10, path))
|
||||
value = 0;
|
||||
free(path);
|
||||
|
||||
return value;
|
||||
|
||||
@@ -21,17 +21,31 @@ else
|
||||
INSTALL_TARGETS += skip-libekmfweb-openssl
|
||||
endif
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
libs = $(rootdir)/libseckey/libseckey.a
|
||||
|
||||
TMPFILE := $(shell mktemp)
|
||||
|
||||
detect-openssl-version.dep:
|
||||
echo "#include <openssl/opensslv.h>" > detect-openssl-version.dep
|
||||
echo "#include <openssl/evp.h>" >> detect-openssl-version.dep
|
||||
echo "#if OPENSSL_VERSION_NUMBER < 0x10101000L" >> detect-openssl-version.dep
|
||||
echo " #error openssl version 1.1.1 is required" >> detect-openssl-version.dep
|
||||
echo "#endif" >> detect-openssl-version.dep
|
||||
echo "static void __attribute__((unused)) test(void) {" >> detect-openssl-version.dep
|
||||
echo " EVP_PKEY_meth_remove(NULL);" >> detect-openssl-version.dep
|
||||
echo "}" >> detect-openssl-version.dep
|
||||
echo "#include <openssl/opensslv.h>" > $(TMPFILE)
|
||||
echo "#include <openssl/evp.h>" >> $(TMPFILE)
|
||||
echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE)
|
||||
echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE)
|
||||
echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE)
|
||||
echo " #else" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE)
|
||||
echo " ((min) << 20)))" >> $(TMPFILE)
|
||||
echo " #endif" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE)
|
||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
||||
echo "}" >> $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
check-dep-libekmfweb: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
@@ -39,7 +53,7 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
||||
"detect-openssl-version.dep", \
|
||||
"openssl-devel version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
-I. -lcrypto)
|
||||
-I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
$(call check_dep, \
|
||||
"libekmfweb", \
|
||||
"json-c/json.h", \
|
||||
@@ -49,7 +63,9 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
||||
"libekmfweb", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0")
|
||||
"HAVE_LIBCURL=0" \
|
||||
`curl-config --cflags` `curl-config --libs`)
|
||||
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
touch check-dep-libekmfweb
|
||||
|
||||
skip-libekmfweb-openssl:
|
||||
@@ -67,19 +83,19 @@ ekmfweb.o: check-dep-libekmfweb ekmfweb.c utilities.h cca.h $(rootdir)include/ek
|
||||
utilities.o: check-dep-libekmfweb utilities.c utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
|
||||
cca.o: check-dep-libekmfweb cca.c cca.h utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
|
||||
|
||||
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC
|
||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl -lcurl -ldl
|
||||
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `curl-config --cflags`
|
||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `curl-config --libs` -ldl
|
||||
libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \
|
||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM)
|
||||
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o
|
||||
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs)
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
ln -srf libekmfweb.so.$(VERSION) libekmfweb.so.$(VERM)
|
||||
ln -srf libekmfweb.so.$(VERSION) libekmfweb.so
|
||||
|
||||
install-libekmfweb.so.$(VERSION): libekmfweb.so.$(VERSION)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION)
|
||||
ln -srf $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERM)
|
||||
ln -srf $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERM)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so
|
||||
$(INSTALL) -d -m 770 $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/ekmfweb/ekmfweb.h $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb
|
||||
|
||||
|
||||
1337
libekmfweb/cca.c
1337
libekmfweb/cca.c
File diff suppressed because it is too large
Load Diff
@@ -19,21 +19,6 @@
|
||||
|
||||
#include "ekmfweb/ekmfweb.h"
|
||||
|
||||
/* CCA PKA Key Generate function */
|
||||
typedef void (*CSNDPKG_t)(long *return_code,
|
||||
long *reason_code,
|
||||
long *exit_data_length,
|
||||
unsigned char *exit_data,
|
||||
long *rule_array_count,
|
||||
unsigned char *rule_array,
|
||||
long *regeneration_data_length,
|
||||
unsigned char *regeneration_data,
|
||||
long *skeleton_key_token_length,
|
||||
unsigned char *skeleton_key_token,
|
||||
unsigned char *transport_key_identifier,
|
||||
long *generated_key_identifier_length,
|
||||
unsigned char *generated_key_identifier);
|
||||
|
||||
/* CCA PKA Key Token Build function */
|
||||
typedef void (*CSNDPKB_t)(long *return_code,
|
||||
long *reason_code,
|
||||
@@ -57,31 +42,6 @@ typedef void (*CSNDPKB_t)(long *return_code,
|
||||
unsigned char *reserved_5,
|
||||
long *token_length, unsigned char *token);
|
||||
|
||||
/* CCA PKA Key Token Change function */
|
||||
typedef void (*CSNDKTC_t)(long *return_code,
|
||||
long *reason_code,
|
||||
long *exit_data_length,
|
||||
unsigned char *exit_data,
|
||||
long *rule_array_count,
|
||||
unsigned char *rule_array,
|
||||
long *key_identifier_length,
|
||||
unsigned char *key_identifier);
|
||||
|
||||
/* CCA Digital Signature Generate function */
|
||||
typedef void (*CSNDDSG_t)(long *return_code,
|
||||
long *reason_code,
|
||||
long *exit_data_length,
|
||||
unsigned char *exit_data,
|
||||
long *rule_array_count,
|
||||
unsigned char *rule_array,
|
||||
long *PKA_private_key_identifier_length,
|
||||
unsigned char *PKA_private_key_identifier,
|
||||
long *hash_length,
|
||||
unsigned char *hash,
|
||||
long *signature_field_length,
|
||||
long *signature_bit_length,
|
||||
unsigned char *signature_field);
|
||||
|
||||
/* CCA Key Token Build2 function */
|
||||
typedef void (*CSNBKTB2_t)(long *return_code,
|
||||
long *reason_code,
|
||||
@@ -153,9 +113,6 @@ typedef void (*CSNDSYI2_t)(long *return_code,
|
||||
|
||||
struct cca_lib {
|
||||
CSNDPKB_t dll_CSNDPKB;
|
||||
CSNDPKG_t dll_CSNDPKG;
|
||||
CSNDKTC_t dll_CSNDKTC;
|
||||
CSNDDSG_t dll_CSNDDSG;
|
||||
CSNBKTB2_t dll_CSNBKTB2;
|
||||
CSNDEDH_t dll_CSNDEDH;
|
||||
CSNDSYI2_t dll_CSNDSYI2;
|
||||
@@ -164,34 +121,6 @@ struct cca_lib {
|
||||
#define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500
|
||||
#define CCA_MAX_SYM_KEY_TOKEN_SIZE 725
|
||||
|
||||
int cca_generate_ecc_key_pair(const struct ekmf_cca_lib *cca_lib,
|
||||
int curve_nid, unsigned char *key_token,
|
||||
size_t *key_token_length, bool verbose);
|
||||
|
||||
int cca_generate_rsa_key_pair(const struct ekmf_cca_lib *cca_lib,
|
||||
size_t modulus_bits, unsigned int pub_exp,
|
||||
unsigned char *key_token,
|
||||
size_t *key_token_length, bool verbose);
|
||||
|
||||
int cca_get_key_type(const unsigned char *key_token, size_t key_token_length,
|
||||
int *pkey_type);
|
||||
|
||||
int cca_reencipher_key(const struct ekmf_cca_lib *cca_lib,
|
||||
const unsigned char *key_token, size_t key_token_length,
|
||||
bool to_new, bool verbose);
|
||||
|
||||
int cca_get_ecc_pub_key_as_pkey(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
EVP_PKEY **pkey, bool verbose);
|
||||
|
||||
int cca_get_ecc_pub_key_as_json_web_key(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
json_object **jwk, bool verbose);
|
||||
|
||||
int cca_get_rsa_pub_key_as_pkey(const unsigned char *key_token,
|
||||
size_t key_token_length,
|
||||
int pkey_type, EVP_PKEY **pkey, bool verbose);
|
||||
|
||||
int cca_import_key_from_json_web_key(const struct ekmf_cca_lib *cca_lib,
|
||||
json_object *jwk, unsigned char *key_token,
|
||||
size_t *key_token_length, bool verbose);
|
||||
@@ -225,23 +154,4 @@ int cca_import_external_key(const struct ekmf_cca_lib *cca_lib,
|
||||
size_t *imported_key_token_length,
|
||||
bool verbose);
|
||||
|
||||
int cca_rsa_sign(const struct ekmf_cca_lib *cca_lib,
|
||||
const unsigned char *key_token, size_t key_token_length,
|
||||
unsigned char *sig, size_t *siglen,
|
||||
const unsigned char *tbs, size_t tbslen,
|
||||
int padding_type, int digest_nid, bool verbose);
|
||||
|
||||
int cca_rsa_pss_sign(const struct ekmf_cca_lib *cca_lib,
|
||||
const unsigned char *key_token, size_t key_token_length,
|
||||
unsigned char *sig, size_t *siglen,
|
||||
const unsigned char *tbs, size_t tbslen,
|
||||
int digest_nid, int mgf_digest_nid, int saltlen,
|
||||
bool verbose);
|
||||
|
||||
int cca_ecdsa_sign(const struct ekmf_cca_lib *cca_lib,
|
||||
const unsigned char *key_token, size_t key_token_length,
|
||||
unsigned char *sig, size_t *siglen,
|
||||
const unsigned char *tbs, size_t tbslen, int digest_nid,
|
||||
bool verbose);
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -81,26 +81,6 @@ void free_key_info(struct ekmf_key_info *key);
|
||||
|
||||
char *get_http_header_value(const struct curl_slist *headers, const char *name);
|
||||
|
||||
size_t ecc_get_curve_prime_bits(int curve_nid);
|
||||
size_t ecc_get_curve_prime_length(int curve_nid);
|
||||
const char *ecc_get_curve_id(int curve_nid);
|
||||
bool ecc_is_prime_curve(int curve_nid);
|
||||
bool ecc_is_brainpool_curve(int curve_nid);
|
||||
int ecc_get_curve_by_id(const char *curve_id);
|
||||
int ecc_get_prime_curve_by_prime_bits(size_t prime_bits);
|
||||
int ecc_get_brainpool_curve_by_prime_bits(size_t prime_bits);
|
||||
|
||||
int ecc_calculate_y_coordinate(int nid, size_t prime_len,
|
||||
const unsigned char *x, int y_bit,
|
||||
unsigned char *y);
|
||||
|
||||
int ecc_pub_key_as_pkey(int nid, size_t prime_len, const unsigned char *x,
|
||||
const unsigned char *y, EVP_PKEY **pkey);
|
||||
|
||||
int rsa_pub_key_as_pkey(const unsigned char *modulus, size_t modulus_length,
|
||||
const unsigned char *pub_exp, size_t pub_exp_length,
|
||||
int pkey_type, EVP_PKEY **pkey);
|
||||
|
||||
int json_web_key_as_pkey(json_object *jwk, int pkey_type, EVP_PKEY **pkey);
|
||||
|
||||
int write_key_blob(const char *filename, unsigned char *key_blob,
|
||||
@@ -119,38 +99,6 @@ int read_public_key(const char *pem_filename, EVP_PKEY **pkey);
|
||||
|
||||
int write_public_key(const char *pem_filename, EVP_PKEY *pkey);
|
||||
|
||||
typedef int (*rsa_sign_t)(const unsigned char *key_blob, size_t key_blob_length,
|
||||
unsigned char *sig, size_t *siglen,
|
||||
const unsigned char *tbs, size_t tbslen,
|
||||
int padding_type, int md_nid,
|
||||
void *private);
|
||||
typedef int (*rsa_pss_sign_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length, unsigned char *sig,
|
||||
size_t *siglen, const unsigned char *tbs,
|
||||
size_t tbslen, int md_nid, int mfgmd_nid,
|
||||
int saltlen, void *private);
|
||||
typedef int (*ecdsa_sign_t)(const unsigned char *key_blob,
|
||||
size_t key_blob_length, unsigned char *sig,
|
||||
size_t *siglen, const unsigned char *tbs,
|
||||
size_t tbslen, int md_nid, void *private);
|
||||
|
||||
struct sk_pkey_sign_func {
|
||||
rsa_sign_t rsa_sign;
|
||||
rsa_pss_sign_t rsa_pss_sign;
|
||||
ecdsa_sign_t ecdsa_sign;
|
||||
};
|
||||
|
||||
int setup_secure_key_pkey_method(int pkey_id);
|
||||
int cleanup_secure_key_pkey_method(int pkey_id);
|
||||
int setup_secure_key_pkey_context(EVP_PKEY_CTX *pkey_ctx,
|
||||
const unsigned char *key_blob,
|
||||
size_t key_blob_len,
|
||||
struct sk_pkey_sign_func *sign_funcs,
|
||||
void *private);
|
||||
|
||||
int setup_rsa_pss_pkey_context(EVP_PKEY_CTX *pkey_ctx,
|
||||
struct ekmf_rsa_pss_params *rsa_pss_params);
|
||||
|
||||
int build_subject_name(X509_NAME **name, const char *rdns[], size_t num_rdns,
|
||||
bool utf8);
|
||||
|
||||
|
||||
130
libkmipclient/Makefile
Normal file
130
libkmipclient/Makefile
Normal file
@@ -0,0 +1,130 @@
|
||||
include ../common.mak
|
||||
|
||||
VERSION = 1.0
|
||||
VERM = $(shell echo $(VERSION) | cut -d '.' -f 1)
|
||||
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_JSONC},0)
|
||||
ifneq (${HAVE_LIBXML2},0)
|
||||
ifneq (${HAVE_LIBCURL},0)
|
||||
BUILD_TARGETS += libkmipclient.so.$(VERSION)
|
||||
INSTALL_TARGETS += install-libkmipclient.so.$(VERSION)
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-curl
|
||||
INSTALL_TARGETS += skip-libkmipclient-curl
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-xml
|
||||
INSTALL_TARGETS += skip-libkmipclient-xml
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-jsonc
|
||||
INSTALL_TARGETS += skip-libkmipclient-jsonc
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-openssl
|
||||
INSTALL_TARGETS += skip-libkmipclient-openssl
|
||||
endif
|
||||
|
||||
TMPFILE := $(shell mktemp)
|
||||
|
||||
detect-openssl-version.dep:
|
||||
echo "#include <openssl/opensslv.h>" > $(TMPFILE)
|
||||
echo "#include <openssl/evp.h>" >> $(TMPFILE)
|
||||
echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE)
|
||||
echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE)
|
||||
echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE)
|
||||
echo " #else" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE)
|
||||
echo " ((min) << 20)))" >> $(TMPFILE)
|
||||
echo " #endif" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE)
|
||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
||||
echo "}" >> $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
check-dep-libkmipclient: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"detect-openssl-version.dep", \
|
||||
"openssl-devel version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
-I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"json-c/json.h", \
|
||||
"json-c-devel", \
|
||||
"HAVE_JSONC=0")
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"libxml/tree.h", \
|
||||
"libxml2-devel", \
|
||||
"HAVE_LIBXML2=0", \
|
||||
`xml2-config --cflags` `xml2-config --libs`)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`curl-config --cflags` `curl-config --libs`)
|
||||
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
touch check-dep-libkmipclient
|
||||
|
||||
skip-libkmipclient-openssl:
|
||||
echo " SKIP libkmipclient due to HAVE_OPENSSL=0"
|
||||
|
||||
skip-libkmipclient-jsonc:
|
||||
echo " SKIP libkmipclient due to HAVE_JSONC=0"
|
||||
|
||||
skip-libkmipclient-xml:
|
||||
echo " SKIP libkmipclient due to HAVE_LIBXML2=0"
|
||||
|
||||
skip-libkmipclient-curl:
|
||||
echo " SKIP libkmipclient due to HAVE_LIBCURL=0"
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
kmip.o: check-dep-libkmipclient kmip.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
request.o: check-dep-libkmipclient request.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
response.o: check-dep-libkmipclient response.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
attribute.o: check-dep-libkmipclient attribute.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
key.o: check-dep-libkmipclient key.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
ttlv.o: check-dep-libkmipclient ttlv.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
json.o: check-dep-libkmipclient json.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
xml.o: check-dep-libkmipclient xml.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
https.o: check-dep-libkmipclient https.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
names.o: check-dep-libkmipclient names.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
utils.o: check-dep-libkmipclient utils.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
|
||||
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `xml2-config --cflags` `curl-config --cflags`
|
||||
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `xml2-config --libs` `curl-config --libs`
|
||||
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
|
||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM)
|
||||
libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \
|
||||
xml.o https.o tls.o names.o utils.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
ln -srf libkmipclient.so.$(VERSION) libkmipclient.so.$(VERM)
|
||||
ln -srf libkmipclient.so.$(VERSION) libkmipclient.so
|
||||
|
||||
install-libkmipclient.so.$(VERSION): libkmipclient.so.$(VERSION)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERM)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so
|
||||
$(INSTALL) -d -m 770 $(DESTDIR)$(USRINCLUDEDIR)/kmipclient
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/kmipclient/kmipclient.h $(DESTDIR)$(USRINCLUDEDIR)/kmipclient
|
||||
|
||||
install: all $(INSTALL_TARGETS)
|
||||
|
||||
clean:
|
||||
rm -f *.o libkmipclient.so* check-dep-libkmipclient detect-openssl-version.dep
|
||||
|
||||
.PHONY: all install clean skip-libkmipclient-openssl skip-libkmipclient-jsonc \
|
||||
skip-libkmipclient-xml skip-libkmipclient-curl install-libkmipclient.so.$(VERSION)
|
||||
4011
libkmipclient/attribute.c
Normal file
4011
libkmipclient/attribute.c
Normal file
File diff suppressed because it is too large
Load Diff
848
libkmipclient/https.c
Normal file
848
libkmipclient/https.c
Normal file
@@ -0,0 +1,848 @@
|
||||
/*
|
||||
* libkmipclient - KMIP client library
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "kmip.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define HTTP_HDR_CONTENT_TYPE "Content-Type:"
|
||||
|
||||
#define CURL_ERROR_CHECK(rc, text, debug, label) \
|
||||
do { \
|
||||
if ((rc) != CURLE_OK) { \
|
||||
kmip_debug((debug), "%s: %s", (text), \
|
||||
curl_easy_strerror((rc))); \
|
||||
goto label; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct curl_sslctx_cb_data {
|
||||
const struct kmip_connection *conn;
|
||||
bool debug;
|
||||
};
|
||||
|
||||
struct curl_write_cb_data {
|
||||
const struct kmip_connection *conn;
|
||||
bool error;
|
||||
bool debug;
|
||||
union {
|
||||
struct {
|
||||
json_tokener *tok;
|
||||
json_object *resp_obj;
|
||||
} json;
|
||||
struct {
|
||||
xmlParserCtxtPtr ctx;
|
||||
} xml;
|
||||
struct {
|
||||
BIO *resp_mem_bio;
|
||||
} ttlv;
|
||||
};
|
||||
};
|
||||
|
||||
struct curl_header_cb_data {
|
||||
const struct kmip_connection *conn;
|
||||
bool error;
|
||||
bool debug;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new HTTPS connection to a KMIP server.
|
||||
*
|
||||
* @param connn The KMIP connection
|
||||
* @param debug if true, debug messages are printed
|
||||
*
|
||||
* @returns 0 in case of success, or a negative errno value
|
||||
*/
|
||||
int kmip_connection_https_init(struct kmip_connection *conn, bool debug)
|
||||
{
|
||||
const char *content_type, *accept, *server, *tok;
|
||||
const struct curl_tlssessioninfo *info = NULL;
|
||||
bool port_found = false;
|
||||
struct stat sb;
|
||||
int rc;
|
||||
|
||||
if (conn == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (strncmp(conn->config.server, "https://", 8) != 0) {
|
||||
kmip_debug(debug, "Server must start with 'https://'");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find port (if any) and beginning of uri */
|
||||
server = conn->config.server + 8;
|
||||
if (*server == '[') {
|
||||
/* IPv6 address enclosed in square brackets */
|
||||
tok = strchr(server, ']');
|
||||
if (tok == NULL) {
|
||||
kmip_debug(debug, "malformed IPv6 address");
|
||||
return -EINVAL;
|
||||
}
|
||||
tok++;
|
||||
port_found = (*tok == ':');
|
||||
} else {
|
||||
/* hostname or IPv4 address */
|
||||
tok = strchr(server, ':');
|
||||
port_found = (tok != NULL);
|
||||
}
|
||||
|
||||
conn->https.curl = curl_easy_init();
|
||||
if (conn->https.curl == NULL) {
|
||||
kmip_debug(debug, "curl_easy_init failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* The CURLOPT_SSL_CTX_FUNCTION callback only works with the OpenSSL
|
||||
* curl backend. Check that OpenSSL is the current curl backend.
|
||||
*/
|
||||
rc = curl_easy_getinfo(conn->https.curl, CURLINFO_TLS_SSL_PTR, &info);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_TLS_SSL_PTR", debug,
|
||||
out);
|
||||
if (info->backend != CURLSSLBACKEND_OPENSSL) {
|
||||
kmip_debug(debug, "libcurl is not using the OpenSSL backend");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_VERBOSE, debug ? 1 : 0);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_VERBOSE", debug, out);
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_URL,
|
||||
conn->config.server);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug, out);
|
||||
|
||||
if (!port_found) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_PORT,
|
||||
KMIP_DEFAULT_HTTPS_PORT_NUM);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug,
|
||||
out);
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYPEER,
|
||||
conn->config.tls_verify_peer ? 1L : 0L);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYPEER", debug,
|
||||
out);
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYHOST,
|
||||
conn->config.tls_verify_host ? 2L : 0L);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYHOST", debug,
|
||||
out);
|
||||
|
||||
if (conn->config.tls_ca != NULL) {
|
||||
if (stat(conn->config.tls_ca, &sb) != 0) {
|
||||
rc = -errno;
|
||||
kmip_debug(debug, "stat failed on '%s': %s",
|
||||
conn->config.tls_ca, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISDIR(sb.st_mode)) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAPATH,
|
||||
conn->config.tls_ca);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAPATH",
|
||||
debug, out);
|
||||
} else {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAINFO,
|
||||
conn->config.tls_ca);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAINFO",
|
||||
debug, out);
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->config.tls_issuer_cert != NULL) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_ISSUERCERT,
|
||||
conn->config.tls_issuer_cert);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ISSUERCERT",
|
||||
debug, out);
|
||||
}
|
||||
|
||||
if (conn->config.tls_pinned_pubkey != NULL) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_PINNEDPUBLICKEY,
|
||||
conn->config.tls_pinned_pubkey);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_PINNEDPUBLICKEY",
|
||||
debug, out);
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_FOLLOWLOCATION, 0L);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION",
|
||||
debug, out);
|
||||
|
||||
if (conn->config.tls_cipher_list != NULL) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CIPHER_LIST,
|
||||
conn->config.tls_cipher_list);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CIPHER_LIST",
|
||||
debug, out);
|
||||
}
|
||||
|
||||
if (conn->config.tls13_cipher_list != NULL) {
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_TLS13_CIPHERS,
|
||||
conn->config.tls13_cipher_list);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_TLS13_CIPHERS",
|
||||
debug, out);
|
||||
}
|
||||
|
||||
switch (conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
content_type = "Content-Type: application/octet-stream";
|
||||
accept = "Accept: application/octet-stream";
|
||||
break;
|
||||
case KMIP_ENCODING_JSON:
|
||||
content_type = "Content-Type: application/json;charset=UTF-8";
|
||||
accept = "Accept: application/json";
|
||||
break;
|
||||
case KMIP_ENCODING_XML:
|
||||
content_type = "Content-Type: text/xml;charset=UTF-8";
|
||||
accept = "Accept: text/xml";
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "invalid encoding: %d",
|
||||
conn->config.encoding);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn->https.headers = curl_slist_append(conn->https.headers,
|
||||
content_type);
|
||||
if (conn->https.headers == NULL) {
|
||||
kmip_debug(debug, "curl_slist_append failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn->https.headers = curl_slist_append(conn->https.headers, accept);
|
||||
if (conn->https.headers == NULL) {
|
||||
kmip_debug(debug, "curl_slist_append failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn->https.headers = curl_slist_append(conn->https.headers,
|
||||
"Accept-Charset: UTF-8");
|
||||
if (conn->https.headers == NULL) {
|
||||
kmip_debug(debug, "curl_slist_append failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Disable "Expect: 100-continue" */
|
||||
conn->https.headers = curl_slist_append(conn->https.headers, "Expect:");
|
||||
if (conn->https.headers == NULL) {
|
||||
kmip_debug(debug, "curl_slist_append failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* As per KMIP HTTPS profile: Cache-Control: no-cache */
|
||||
conn->https.headers = curl_slist_append(conn->https.headers,
|
||||
"Cache-Control: no-cache");
|
||||
if (conn->https.headers == NULL) {
|
||||
kmip_debug(debug, "curl_slist_append failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HTTPHEADER,
|
||||
conn->https.headers);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HTTPHEADER", debug,
|
||||
out);
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (rc != 0)
|
||||
kmip_connection_https_term(conn);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback called before the SSL handshake is performed.
|
||||
* It sets the client certificate and private key into the context.
|
||||
* It also adds a pinned server certificate to the SSL certificate store, so
|
||||
* that it is treated as trusted, although it might be self-signed.
|
||||
*/
|
||||
static CURLcode mkip_connection_https_sslctx_cb(CURL *UNUSED(curl),
|
||||
void *sslctx, void *parm)
|
||||
{
|
||||
struct curl_sslctx_cb_data *sslctx_cb = parm;
|
||||
SSL_CTX *ssl_ctx = (SSL_CTX *)sslctx;
|
||||
const struct kmip_connection *conn;
|
||||
X509_STORE *store;
|
||||
X509 *cert = NULL;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (ssl_ctx == NULL || sslctx_cb == NULL || sslctx_cb->conn == NULL)
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
conn = sslctx_cb->conn;
|
||||
|
||||
if (SSL_CTX_use_certificate_file(sslctx, conn->config.tls_client_cert,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
kmip_debug(sslctx_cb->debug, "Failed to load the client "
|
||||
"certificate '%s'", conn->config.tls_client_cert);
|
||||
if (sslctx_cb->debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey(ssl_ctx, conn->config.tls_client_key) != 1) {
|
||||
kmip_debug(sslctx_cb->debug, "Failed to set the client key");
|
||||
if (sslctx_cb->debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
if (conn->config.tls_server_cert == NULL)
|
||||
return CURLE_OK;
|
||||
|
||||
store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
if (store == NULL) {
|
||||
kmip_debug(sslctx_cb->debug, "Failed to get SSL Store");
|
||||
if (sslctx_cb->debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
fp = fopen(conn->config.tls_server_cert, "r");
|
||||
if (fp == NULL) {
|
||||
rc = -errno;
|
||||
kmip_debug(sslctx_cb->debug,
|
||||
"Failed to read server cert '%s': %s",
|
||||
conn->config.tls_server_cert, strerror(-rc));
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
fclose(fp);
|
||||
|
||||
if (cert == NULL) {
|
||||
kmip_debug(sslctx_cb->debug, "Failed to read the server "
|
||||
"certificate from file '%s'",
|
||||
conn->config.tls_server_cert);
|
||||
if (sslctx_cb->debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
if (sslctx_cb->debug) {
|
||||
kmip_debug(sslctx_cb->debug, "Pinned server certificate:");
|
||||
X509_print_ex_fp(stderr, cert, XN_FLAG_COMPAT,
|
||||
X509_FLAG_COMPAT);
|
||||
}
|
||||
|
||||
rc = X509_STORE_add_cert(store, cert);
|
||||
if (rc != 1) {
|
||||
kmip_debug(sslctx_cb->debug, "Failed to add server "
|
||||
"certificate to SSL Store");
|
||||
if (sslctx_cb->debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
X509_free(cert);
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback called during curl_easy_perform() to handle received headers.
|
||||
* Check for the expected response content type.
|
||||
*/
|
||||
static size_t mkip_connection_https_header_cb(void *contents, size_t size,
|
||||
size_t nmemb, void *userp)
|
||||
{
|
||||
struct curl_header_cb_data *cb = (struct curl_header_cb_data *)userp;
|
||||
size_t num = size * nmemb;
|
||||
const char *content_type;
|
||||
char *hdr = contents;
|
||||
size_t ofs;
|
||||
char *val;
|
||||
|
||||
if (num < strlen(HTTP_HDR_CONTENT_TYPE))
|
||||
goto out;
|
||||
|
||||
if (strncasecmp(hdr, HTTP_HDR_CONTENT_TYPE,
|
||||
strlen(HTTP_HDR_CONTENT_TYPE)) != 0)
|
||||
goto out;
|
||||
|
||||
ofs = strlen(HTTP_HDR_CONTENT_TYPE);
|
||||
val = hdr + ofs;
|
||||
while (*val == ' ' && ofs < num) {
|
||||
ofs++;
|
||||
val++;
|
||||
}
|
||||
if (ofs >= num)
|
||||
goto out;
|
||||
|
||||
switch (cb->conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
content_type = "application/octet-stream";
|
||||
break;
|
||||
case KMIP_ENCODING_JSON:
|
||||
content_type = "application/json";
|
||||
break;
|
||||
case KMIP_ENCODING_XML:
|
||||
content_type = "text/xml";
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num - ofs >= strlen(content_type) &&
|
||||
strncasecmp(val, content_type, strlen(content_type)) == 0)
|
||||
goto out;
|
||||
|
||||
cb->error = true;
|
||||
kmip_debug(cb->debug, "Unexpected response Content-Type: %.*s",
|
||||
(int)(num - ofs), val);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Callback called during curl_easy_perform() to handle received data.
|
||||
* Parse the (potentially partial) KMIP data.
|
||||
*/
|
||||
static size_t mkip_connection_https_write_cb(void *contents, size_t size,
|
||||
size_t nmemb, void *userp)
|
||||
{
|
||||
struct curl_write_cb_data *cb = (struct curl_write_cb_data *)userp;
|
||||
enum json_tokener_error jerr;
|
||||
size_t num = size * nmemb;
|
||||
int rc;
|
||||
|
||||
switch (cb->conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
kmip_debug(cb->debug, "Response Data (TTLV): %lu bytes", num);
|
||||
if (cb->debug)
|
||||
kmip_print_dump(__func__, (unsigned char *)contents,
|
||||
num, 2);
|
||||
|
||||
if (BIO_write(cb->ttlv.resp_mem_bio, contents, num) !=
|
||||
(int)num) {
|
||||
cb->error = true;
|
||||
kmip_debug(cb->debug, "BIO_write failed");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_JSON:
|
||||
kmip_debug(cb->debug, "Response Data (JSON):");
|
||||
kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents);
|
||||
|
||||
if (cb->json.resp_obj != NULL) {
|
||||
kmip_debug(cb->debug, "JSON data already complete, but "
|
||||
"additional data received");
|
||||
cb->error = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cb->json.resp_obj = json_tokener_parse_ex(cb->json.tok,
|
||||
(const char *)contents, num);
|
||||
|
||||
if (cb->json.resp_obj == NULL) {
|
||||
jerr = json_tokener_get_error(cb->json.tok);
|
||||
if (jerr == json_tokener_continue)
|
||||
goto out;
|
||||
|
||||
cb->error = true;
|
||||
kmip_debug(cb->debug, "json_tokener_parse_ex failed: %s",
|
||||
json_tokener_error_desc(jerr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_XML:
|
||||
kmip_debug(cb->debug, "Response Data (XML):");
|
||||
kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents);
|
||||
|
||||
rc = xmlParseChunk(cb->xml.ctx, (const char *)contents, num, 0);
|
||||
if (rc != XML_ERR_OK) {
|
||||
cb->error = true;
|
||||
kmip_debug(cb->debug, "xmlParseChunk failed: %d", rc);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform a request over the KMIP connection
|
||||
*
|
||||
* @param conn the KMIP connection
|
||||
* @param request the request to send
|
||||
* @param response On return: the received response. Must be freed by
|
||||
* the caller.
|
||||
*
|
||||
* @param debug if true, debug messages are printed
|
||||
*
|
||||
* @returns 0 in case of success, or a negative errno value
|
||||
*/
|
||||
int kmip_connection_https_perform(struct kmip_connection *conn,
|
||||
struct kmip_node *request,
|
||||
struct kmip_node **response,
|
||||
bool debug)
|
||||
{
|
||||
struct curl_sslctx_cb_data sslctx_cb = { 0 };
|
||||
struct curl_header_cb_data header_cb = { 0 };
|
||||
struct curl_write_cb_data write_cb = { 0 };
|
||||
char error_str[CURL_ERROR_SIZE] = { 0 };
|
||||
json_object *req_json_obj = NULL;
|
||||
xmlNode *req_xml_obj = NULL;
|
||||
xmlDoc *req_xml_doc = NULL;
|
||||
BIO *req_mem_bio = NULL;
|
||||
char *req_buff = NULL;
|
||||
int req_buff_size = 0;
|
||||
long status_code;
|
||||
size_t size;
|
||||
int rc;
|
||||
|
||||
if (conn == NULL || request == NULL || response == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*response = NULL;
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER,
|
||||
error_str);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ERRORBUFFER", debug,
|
||||
out);
|
||||
|
||||
/* Setup SSL Context callback */
|
||||
sslctx_cb.conn = conn;
|
||||
sslctx_cb.debug = debug;
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION,
|
||||
mkip_connection_https_sslctx_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt "
|
||||
"CURLOPT_SSL_CTX_FUNCTION", debug, out);
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA,
|
||||
&sslctx_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CTX_DATA",
|
||||
debug, out);
|
||||
|
||||
/* Setup write callback to handle received data */
|
||||
write_cb.conn = conn;
|
||||
write_cb.debug = debug;
|
||||
|
||||
switch (conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
write_cb.ttlv.resp_mem_bio = BIO_new(BIO_s_mem());
|
||||
if (write_cb.ttlv.resp_mem_bio == NULL) {
|
||||
kmip_debug(debug, "BIO_new failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_JSON:
|
||||
write_cb.json.tok = json_tokener_new();
|
||||
if (write_cb.json.tok == NULL) {
|
||||
kmip_debug(debug, "json_tokener_new failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_XML:
|
||||
write_cb.xml.ctx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0,
|
||||
NULL);
|
||||
if (write_cb.xml.ctx == NULL) {
|
||||
kmip_debug(debug, "xmlCreatePushParserCtxt failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION,
|
||||
mkip_connection_https_write_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEFUNCTION", debug,
|
||||
out);
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA,
|
||||
(void *)&write_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEDATA", debug,
|
||||
out);
|
||||
|
||||
/* Setup header callback to check content type */
|
||||
header_cb.conn = conn;
|
||||
header_cb.debug = debug;
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION,
|
||||
mkip_connection_https_header_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERFUNCTION", debug,
|
||||
out);
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA,
|
||||
(void *)&header_cb);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERDATA", debug,
|
||||
out);
|
||||
|
||||
/* Setup POST request and post data */
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CUSTOMREQUEST, "POST");
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CUSTOMREQUEST",
|
||||
debug, out);
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POST, 1L);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POST",
|
||||
debug, out);
|
||||
|
||||
switch (conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
req_mem_bio = BIO_new(BIO_s_mem());
|
||||
if (req_mem_bio == NULL) {
|
||||
kmip_debug(debug, "BIO_new failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = kmip_encode_ttlv(request, req_mem_bio, &size, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_ttlv failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
req_buff_size = BIO_get_mem_data(req_mem_bio, &req_buff);
|
||||
|
||||
kmip_debug(debug, "Request Data (TTLV): %d bytes",
|
||||
req_buff_size);
|
||||
if (debug)
|
||||
kmip_print_dump(__func__, (unsigned char *)req_buff,
|
||||
req_buff_size, 2);
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_JSON:
|
||||
rc = kmip_encode_json(request, &req_json_obj, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_json failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The memory returned by json_object_to_json_string_ext
|
||||
* is freed when the JSON object is freed.
|
||||
*/
|
||||
req_buff = (char *)json_object_to_json_string_ext(req_json_obj,
|
||||
JSON_C_TO_STRING_PLAIN |
|
||||
JSON_C_TO_STRING_NOSLASHESCAPE);
|
||||
if (req_buff == NULL) {
|
||||
kmip_debug(debug,
|
||||
"json_object_to_json_string_ext failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
req_buff_size = strlen(req_buff);
|
||||
|
||||
kmip_debug(debug, "Request Data (JSON):");
|
||||
kmip_debug(debug, " ->%*s<-", req_buff_size,
|
||||
req_buff);
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_XML:
|
||||
req_xml_doc = xmlNewDoc((xmlChar *)"1.0");
|
||||
if (req_xml_doc == NULL) {
|
||||
kmip_debug(debug, "xmlNewDoc failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = kmip_encode_xml(request, &req_xml_obj, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_xml failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
xmlDocSetRootElement(req_xml_doc, req_xml_obj);
|
||||
req_xml_obj = NULL;
|
||||
|
||||
xmlDocDumpFormatMemoryEnc(req_xml_doc, (xmlChar **)&req_buff,
|
||||
&req_buff_size, "UTF-8", 0);
|
||||
if (req_buff == NULL || req_buff_size == 0) {
|
||||
kmip_debug(debug, "xmlDocDumpFormatMemoryEnc failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
kmip_debug(debug, "Request Data (XML):");
|
||||
kmip_debug(debug, " ->%*s<-", req_buff_size,
|
||||
req_buff);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE,
|
||||
req_buff_size);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDSIZE",
|
||||
debug, out);
|
||||
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS,
|
||||
req_buff);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDS",
|
||||
debug, out);
|
||||
|
||||
/* Perform the request */
|
||||
rc = curl_easy_perform(conn->https.curl);
|
||||
if (rc != CURLE_OK) {
|
||||
kmip_debug(debug, "curl_easy_perform for '%s' failed: %s",
|
||||
conn->config.server, curl_easy_strerror(rc));
|
||||
kmip_debug(debug, "Error: %s", error_str);
|
||||
|
||||
if (header_cb.error) {
|
||||
kmip_debug(debug, "Unexpected Content-Type");
|
||||
rc = -EBADMSG;
|
||||
}
|
||||
if (write_cb.error) {
|
||||
kmip_debug(debug, "JSON/XML parsing failed");
|
||||
rc = -EBADMSG;
|
||||
}
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
rc = curl_easy_getinfo(conn->https.curl, CURLINFO_RESPONSE_CODE,
|
||||
&status_code);
|
||||
CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_RESPONSE_CODE",
|
||||
debug, out);
|
||||
kmip_debug(debug, "HTTP status code: %d", status_code);
|
||||
if (status_code != 200) {
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Process received data */
|
||||
switch (conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL,
|
||||
response, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_ttlv failed");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_JSON:
|
||||
if (write_cb.json.resp_obj == NULL) {
|
||||
kmip_debug(debug, "JSON content not wellformed");
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response,
|
||||
debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_json failed");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_ENCODING_XML:
|
||||
rc = xmlParseChunk(write_cb.xml.ctx, "", 0, 1);
|
||||
if (rc != XML_ERR_OK || !write_cb.xml.ctx->wellFormed ||
|
||||
write_cb.xml.ctx->myDoc == NULL) {
|
||||
kmip_debug(debug, "XML content not wellformed");
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = kmip_decode_xml(xmlDocGetRootElement(
|
||||
write_cb.xml.ctx->myDoc),
|
||||
NULL, response, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_xml failed");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
/* Cleanup */
|
||||
switch (conn->config.encoding) {
|
||||
case KMIP_ENCODING_TTLV:
|
||||
if (req_mem_bio != NULL)
|
||||
BIO_free(req_mem_bio);
|
||||
if (write_cb.ttlv.resp_mem_bio != NULL)
|
||||
BIO_free(write_cb.ttlv.resp_mem_bio);
|
||||
|
||||
break;
|
||||
case KMIP_ENCODING_JSON:
|
||||
if (write_cb.json.tok != NULL)
|
||||
json_tokener_free(write_cb.json.tok);
|
||||
if (write_cb.json.resp_obj != NULL)
|
||||
json_object_put(write_cb.json.resp_obj);
|
||||
if (req_json_obj != NULL)
|
||||
json_object_put(req_json_obj);
|
||||
break;
|
||||
case KMIP_ENCODING_XML:
|
||||
if (write_cb.xml.ctx != NULL) {
|
||||
xmlFreeDoc(write_cb.xml.ctx->myDoc);
|
||||
xmlFreeParserCtxt(write_cb.xml.ctx);
|
||||
}
|
||||
if (req_xml_doc != NULL)
|
||||
xmlFreeDoc(req_xml_doc);
|
||||
if (req_xml_obj != NULL)
|
||||
xmlFreeNode(req_xml_obj);
|
||||
if (req_buff != NULL)
|
||||
xmlFree(req_buff);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc != 0 && *response != NULL) {
|
||||
kmip_node_free(*response);
|
||||
*response = NULL;
|
||||
}
|
||||
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS, NULL);
|
||||
curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE, -1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates a HTTPS KMIP connection.
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
*/
|
||||
void kmip_connection_https_term(struct kmip_connection *conn)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return;
|
||||
|
||||
if (conn->https.curl != NULL)
|
||||
curl_easy_cleanup(conn->https.curl);
|
||||
conn->https.curl = NULL;
|
||||
|
||||
if (conn->https.headers != NULL)
|
||||
curl_slist_free_all(conn->https.headers);
|
||||
conn->https.headers = NULL;
|
||||
}
|
||||
649
libkmipclient/json.c
Normal file
649
libkmipclient/json.c
Normal file
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* libkmipclient - KMIP client library
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <endian.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kmip.h"
|
||||
#include "names.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define KMIP_JSON_TAG "tag"
|
||||
#define KMIP_JSON_NAME "name"
|
||||
#define KMIP_JSON_TYPE "type"
|
||||
#define KMIP_JSON_VALUE "value"
|
||||
|
||||
/**
|
||||
* Decode a KMIP node from the data in a JSON object using the JSON encoding.
|
||||
*
|
||||
* @param obj the JSON object to decode
|
||||
* @param parent the parent node or NULL if no parent exists.
|
||||
* @param node On return: the decoded node. The newly allocated
|
||||
* node has a reference count of 1.
|
||||
* @param debug if true, debug messages are printed
|
||||
*
|
||||
* @returns 0 in case of success, or a negative errno value
|
||||
*/
|
||||
int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
|
||||
struct kmip_node **node, bool debug)
|
||||
{
|
||||
json_object *tag_obj, *type_obj, *value_obj, *name_obj;
|
||||
enum kmip_tag tag, v1_attr_tag = 0;
|
||||
enum json_type value_type;
|
||||
struct kmip_node *n, *e;
|
||||
const char *str;
|
||||
int rc, num, i;
|
||||
int64_t int64;
|
||||
|
||||
if (obj == NULL || node == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_is_type(obj, json_type_object)) {
|
||||
kmip_debug(debug, "Object is not a JSON object");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
n = calloc(1, sizeof(struct kmip_node));
|
||||
if (n == NULL) {
|
||||
kmip_debug(debug, "calloc failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
n->ref_count = 1;
|
||||
|
||||
tag_obj = json_object_object_get(obj, KMIP_JSON_TAG);
|
||||
if (tag_obj == NULL ||
|
||||
!json_object_is_type(tag_obj, json_type_string)) {
|
||||
kmip_debug(debug, "Missing or invalid '%s' in JSON object",
|
||||
KMIP_JSON_TAG);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
str = json_object_get_string(tag_obj);
|
||||
n->tag = kmip_tag_by_name_or_hex(str);
|
||||
if (n->tag == 0) {
|
||||
kmip_debug(debug, "Unknown 'tag' in JSON object: '%s'", str);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
name_obj = json_object_object_get(obj, KMIP_JSON_NAME);
|
||||
if (name_obj != NULL) {
|
||||
if (!json_object_is_type(name_obj, json_type_string)) {
|
||||
kmip_debug(debug, "Invalid '%s' in JSON object",
|
||||
KMIP_JSON_NAME);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
n->name = strdup(json_object_get_string(tag_obj));
|
||||
}
|
||||
|
||||
type_obj = json_object_object_get(obj, KMIP_JSON_TYPE);
|
||||
if (type_obj == NULL) {
|
||||
n->type = KMIP_TYPE_STRUCTURE;
|
||||
} else {
|
||||
if (!json_object_is_type(type_obj, json_type_string)) {
|
||||
kmip_debug(debug,
|
||||
"Missing or invalid '%s' in JSON object",
|
||||
KMIP_JSON_TYPE);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
str = json_object_get_string(type_obj);
|
||||
n->type = kmip_type_by_name_or_hex(str);
|
||||
if (n->type == 0) {
|
||||
kmip_debug(debug, "Unknown 'type' in JSON object: '%s'",
|
||||
str);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
value_obj = json_object_object_get(obj, KMIP_JSON_VALUE);
|
||||
if (value_obj == NULL) {
|
||||
kmip_debug(debug, "Missing '%s' in JSON object",
|
||||
KMIP_JSON_VALUE);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
value_type = json_object_get_type(value_obj);
|
||||
|
||||
/*
|
||||
* KMIP v1.x attribute values may be Enumerations or Integer Masks.
|
||||
* To correctly decode them, we need to know the tag. This is contained
|
||||
* in a Attribute Name node, which is an element of our parent node.
|
||||
*/
|
||||
if (n->tag == KMIP_TAG_ATTRIBUTE_VALUE)
|
||||
v1_attr_tag = kmip_find_v1_attribute_name_tag(parent);
|
||||
tag = (v1_attr_tag != 0 ? v1_attr_tag : n->tag);
|
||||
|
||||
kmip_debug(debug, "tag: 0x%x type: 0x%x value_type: %d,", n->tag,
|
||||
n->type, value_type);
|
||||
|
||||
switch (n->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
switch (value_type) {
|
||||
case json_type_null:
|
||||
break;
|
||||
case json_type_array:
|
||||
num = json_object_array_length(value_obj);
|
||||
for (i = 0; i < num; i++) {
|
||||
rc = kmip_decode_json(
|
||||
json_object_array_get_idx(value_obj, i),
|
||||
n, &e, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to parse "
|
||||
"array element %d", i);
|
||||
goto out;
|
||||
}
|
||||
rc = kmip_node_add_structure_element(n, e);
|
||||
kmip_node_free(e);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"kmip_node_structure_add_element "
|
||||
"failed: rc: %d", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTEGER:
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
int64 = json_object_get_int64(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
if (n->type == KMIP_TYPE_INTEGER &&
|
||||
kmip_is_tag_mask(tag)) {
|
||||
rc = kmip_parse_mask(tag, str, '|', &int64);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to parse "
|
||||
"mask string '%s'", str);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
rc = kmip_parse_hex_int(str, &int64);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to parse "
|
||||
"hex string '%s'", str);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
if (n->type == KMIP_TYPE_INTEGER)
|
||||
n->integer_value = int64;
|
||||
else
|
||||
n->long_value = int64;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
n->interval_value = json_object_get_int64(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_hex_int(str, &int64);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to parse "
|
||||
"hex string '%s'", str);
|
||||
goto out;
|
||||
}
|
||||
n->interval_value = int64;
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
int64 = htobe64(json_object_get_int64(value_obj));
|
||||
rc = kmip_decode_bignum((const unsigned char *)&int64,
|
||||
sizeof(int64),
|
||||
&n->big_integer_value);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_bignum failed");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_bignum(str, true,
|
||||
&n->big_integer_value);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse bignum string '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
n->enumeration_value = json_object_get_int64(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_enum_value_by_tag_name_or_hex(tag, str,
|
||||
&n->enumeration_value);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse enumeration '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
switch (value_type) {
|
||||
case json_type_boolean:
|
||||
n->boolean_value = json_object_get_boolean(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_hex_int(str, &int64);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse hex string '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
n->boolean_value = (int64 != 0);
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
switch (value_type) {
|
||||
case json_type_string:
|
||||
n->text_value = strdup(
|
||||
json_object_get_string(value_obj));
|
||||
if (n->text_value == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
n->length = strlen(n->text_value);
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
switch (value_type) {
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_hex(str, false, &n->bytes_value,
|
||||
&n->length);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse hex string '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
n->date_time_value = json_object_get_int64(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_timestamp(str, &n->date_time_value);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse time stamp '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
switch (value_type) {
|
||||
case json_type_int:
|
||||
case json_type_double:
|
||||
n->date_time_ext_value =
|
||||
json_object_get_int64(value_obj);
|
||||
break;
|
||||
case json_type_string:
|
||||
str = json_object_get_string(value_obj);
|
||||
rc = kmip_parse_hex_int(str, &int64);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"Failed to parse hex string '%s'",
|
||||
str);
|
||||
goto out;
|
||||
}
|
||||
n->date_time_ext_value = int64;
|
||||
break;
|
||||
default:
|
||||
kmip_debug(debug, "Invalid JSON type %d for node type "
|
||||
"0x%x", value_type, n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
kmip_debug(debug, "unknown type: 0x%x", n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*node = n;
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (rc != 0)
|
||||
kmip_node_free(n);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a KMIP node into a JSON object using the JSON encoding.
|
||||
*
|
||||
* @param node the node to encode
|
||||
* @param obj On return: the JSON object
|
||||
* @param debug if true, debug messages are printed
|
||||
*
|
||||
* @returns 0 in case of success, or a negative errno value
|
||||
*/
|
||||
int kmip_encode_json(const struct kmip_node *node, json_object **obj,
|
||||
bool debug)
|
||||
{
|
||||
json_object *ret_obj = NULL, *memb_obj, *elem_obj;
|
||||
enum kmip_tag tag, v1_attr_tag = 0;
|
||||
struct kmip_node *element;
|
||||
char outstr[200] = { 0 };
|
||||
const char *str;
|
||||
int64_t int64;
|
||||
struct tm *tm;
|
||||
char *tmp;
|
||||
char *s;
|
||||
int rc;
|
||||
|
||||
if (node == NULL || obj == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
kmip_debug(debug, "tag: 0x%x type: 0x%x", node->tag, node->type);
|
||||
|
||||
ret_obj = json_object_new_object();
|
||||
if (ret_obj == NULL) {
|
||||
kmip_debug(debug, "Failed to allocate a JSON object");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memb_obj = json_object_new_string(
|
||||
kmip_tag_name_or_hex_by_tag(node->tag,
|
||||
outstr));
|
||||
if (memb_obj == NULL) {
|
||||
kmip_debug(debug, "Failed to build JSON object for tag");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = json_object_object_add(ret_obj, KMIP_JSON_TAG, memb_obj);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to add JSON object for tag");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (node->name != NULL) {
|
||||
memb_obj = json_object_new_string(node->name);
|
||||
if (memb_obj == NULL) {
|
||||
kmip_debug(debug,
|
||||
"Failed to build JSON object for name");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = json_object_object_add(ret_obj, KMIP_JSON_NAME, memb_obj);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to add JSON object for name");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->type != KMIP_TYPE_STRUCTURE) {
|
||||
str = kmip_type_name_by_type(node->type);
|
||||
if (str == NULL) {
|
||||
kmip_debug(debug, "unknown type 0x%x", node->type);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memb_obj = json_object_new_string(str);
|
||||
if (memb_obj == NULL) {
|
||||
kmip_debug(debug,
|
||||
"Failed to build JSON object for type");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = json_object_object_add(ret_obj, KMIP_JSON_TYPE, memb_obj);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to add JSON object for type");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* KMIP v1.x attribute values may be Enumerations or Integer Masks.
|
||||
* To correctly encode them, we need to know the tag. This is contained
|
||||
* in a Attribute Name node, which is an element of our parent node.
|
||||
*/
|
||||
if (node->tag == KMIP_TAG_ATTRIBUTE_VALUE)
|
||||
v1_attr_tag = kmip_find_v1_attribute_name_tag(node->parent);
|
||||
tag = (v1_attr_tag != 0 ? v1_attr_tag : node->tag);
|
||||
|
||||
switch (node->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
memb_obj = json_object_new_array();
|
||||
if (memb_obj == NULL) {
|
||||
kmip_debug(debug,
|
||||
"Failed to build JSON object for value array");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
element = node->structure_value;
|
||||
while (element != NULL) {
|
||||
rc = kmip_encode_json(element, &elem_obj, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_json failed");
|
||||
goto out;
|
||||
}
|
||||
rc = json_object_array_add(memb_obj, elem_obj);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"json_object_array_add failed");
|
||||
rc = EIO;
|
||||
goto out;
|
||||
}
|
||||
element = element->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTEGER:
|
||||
if (kmip_is_tag_mask(tag) && node->integer_value != 0) {
|
||||
rc = kmip_format_mask(tag, node->integer_value,
|
||||
'|', &tmp);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_format_mask failed");
|
||||
goto out;
|
||||
}
|
||||
memb_obj = json_object_new_string(tmp);
|
||||
free(tmp);
|
||||
} else {
|
||||
memb_obj = json_object_new_int(node->integer_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
memb_obj = json_object_new_int(node->interval_value);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
if (node->type == KMIP_TYPE_LONG_INTEGER)
|
||||
int64 = node->long_value;
|
||||
else
|
||||
int64 = node->date_time_ext_value;
|
||||
/* any values >= 2^52 must be represented as hex strings */
|
||||
if (int64 < 4503599627370496 &&
|
||||
int64 > -4503599627370496) {
|
||||
memb_obj = json_object_new_int64(int64);
|
||||
} else {
|
||||
rc = kmip_format_hex((const unsigned char *)&int64,
|
||||
sizeof(int64), true, &tmp);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_format_hex failed");
|
||||
goto out;
|
||||
}
|
||||
memb_obj = json_object_new_string(tmp);
|
||||
free(tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
rc = kmip_format_bignum(node->big_integer_value, true, &s);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_format_bignum failed");
|
||||
goto out;
|
||||
}
|
||||
memb_obj = json_object_new_string(s);
|
||||
free(s);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
str = kmip_enum_name_by_tag_value(tag, node->enumeration_value);
|
||||
if (str != NULL)
|
||||
memb_obj = json_object_new_string(str);
|
||||
else
|
||||
memb_obj = json_object_new_int(node->enumeration_value);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
memb_obj = json_object_new_boolean(node->boolean_value);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
memb_obj = json_object_new_string(node->text_value);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
rc = kmip_format_hex(node->bytes_value, node->length,
|
||||
false, &s);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_format_hex_long failed");
|
||||
goto out;
|
||||
}
|
||||
memb_obj = json_object_new_string(s);
|
||||
free(s);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
tm = gmtime((time_t *)&node->date_time_value);
|
||||
strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC,
|
||||
tm);
|
||||
memb_obj = json_object_new_string(outstr);
|
||||
break;
|
||||
|
||||
default:
|
||||
kmip_debug(debug, "unknown type: 0x%x", node->type);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memb_obj == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = json_object_object_add(ret_obj, KMIP_JSON_VALUE, memb_obj);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "Failed to add JSON object for value");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
*obj = ret_obj;
|
||||
|
||||
out:
|
||||
if (rc != 0)
|
||||
json_object_put(ret_obj);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
1439
libkmipclient/key.c
Normal file
1439
libkmipclient/key.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user