mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
258 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
686262fdcd | ||
|
|
2379fd8a58 | ||
|
|
3552a27ae6 | ||
|
|
39106ba760 | ||
|
|
f270ac99f0 | ||
|
|
f83af8e076 | ||
|
|
4d2c92f6d5 | ||
|
|
503e241db1 | ||
|
|
dea5f80215 | ||
|
|
c261db259b | ||
|
|
c382e7ef44 | ||
|
|
16610a211f | ||
|
|
4d57ff046d | ||
|
|
4a76efe6d5 | ||
|
|
87d43c7a32 | ||
|
|
f383278a5a | ||
|
|
a9d4b1e1b9 | ||
|
|
61c5d7d431 | ||
|
|
b7765993e2 | ||
|
|
e152b554f5 | ||
|
|
c46a066827 | ||
|
|
cf003379ac | ||
|
|
7b94783cb7 | ||
|
|
38600bb4e2 | ||
|
|
16875f7c6d | ||
|
|
381fecfc44 | ||
|
|
5648b924d6 | ||
|
|
636d2d571b | ||
|
|
d016ec129d | ||
|
|
cb77faeae7 | ||
|
|
38ea8fc3ee | ||
|
|
d8e5bc07aa | ||
|
|
3b26f79143 | ||
|
|
c366429e57 | ||
|
|
8ed478ce46 | ||
|
|
a97a8f1cba | ||
|
|
fb0b6263d1 | ||
|
|
76aaf3d4a8 | ||
|
|
08232d29b9 | ||
|
|
cfaccc5fad | ||
|
|
7da5a6b9ed | ||
|
|
7ed87bed8c | ||
|
|
46ec94d0ce | ||
|
|
6bc0b023aa | ||
|
|
13d673e0f7 | ||
|
|
c40c159173 | ||
|
|
202ded5d11 | ||
|
|
e3a698c382 | ||
|
|
3a60b4caa8 | ||
|
|
f57858b3a8 | ||
|
|
9e430b9010 | ||
|
|
ad2a42ce9e | ||
|
|
1c32635b3a | ||
|
|
558594fddf | ||
|
|
c8879322b0 | ||
|
|
d716c553c4 | ||
|
|
ab35922161 | ||
|
|
474c6adf8f | ||
|
|
f5f806af06 | ||
|
|
0a01719477 | ||
|
|
07cd9143da | ||
|
|
d064cb522f | ||
|
|
802c7db50e | ||
|
|
0f87acc2b3 | ||
|
|
998b61e5f3 | ||
|
|
c47071815b | ||
|
|
0ed6c1ccde | ||
|
|
d888a27f07 | ||
|
|
7c2ae3d2e8 | ||
|
|
c88a8b6130 | ||
|
|
32a0434bc3 | ||
|
|
a2e556858a | ||
|
|
9eab43994b | ||
|
|
c53dfa9754 | ||
|
|
81a1e13f3b | ||
|
|
8f89234f1a | ||
|
|
d757dbfbff | ||
|
|
0d9a6e45fb | ||
|
|
4d4666cff7 | ||
|
|
46092add29 | ||
|
|
1655c39ded | ||
|
|
a95dc09c6e | ||
|
|
f1f80a8a20 | ||
|
|
4c2bfb1d47 | ||
|
|
c0fc21efb3 | ||
|
|
93d3c44a1a | ||
|
|
9eea78b3ad | ||
|
|
0a3a556879 | ||
|
|
aba8900074 | ||
|
|
f5744b95db | ||
|
|
13d721afd3 | ||
|
|
90a2e6d70e | ||
|
|
bc9f8a8100 | ||
|
|
2b5e7b0491 | ||
|
|
d7c95265cd | ||
|
|
8751cfc409 | ||
|
|
01f96d30f6 | ||
|
|
fffbd93f12 | ||
|
|
ddcfbdc8d2 | ||
|
|
173fd7cdca | ||
|
|
d14e7593cc | ||
|
|
1a3d0b74f7 | ||
|
|
f6c6f0cc71 | ||
|
|
966e67a252 | ||
|
|
1c128c0d11 | ||
|
|
a3199d58db | ||
|
|
659483031e | ||
|
|
7dc2513205 | ||
|
|
0748d365a6 | ||
|
|
94a404ed10 | ||
|
|
ef1799f31f | ||
|
|
47b0960cc7 | ||
|
|
2288331a6f | ||
|
|
23e9156f43 | ||
|
|
8f99e7c4ea | ||
|
|
f3bcd94524 | ||
|
|
a2f8b19c2a | ||
|
|
588d720517 | ||
|
|
cd822cb770 | ||
|
|
b27b8e3cd3 | ||
|
|
7a2c5dc980 | ||
|
|
d9e3763d1c | ||
|
|
6f15ed3264 | ||
|
|
0d2b5af007 | ||
|
|
9e7a8f48e8 | ||
|
|
98f7a0569c | ||
|
|
551f66282e | ||
|
|
3d2ba5aaed | ||
|
|
94942a48ab | ||
|
|
ab8984a7a3 | ||
|
|
4990f643c1 | ||
|
|
34bef977e8 | ||
|
|
f36c34038b | ||
|
|
bfd0e12d22 | ||
|
|
1450f85ada | ||
|
|
2a0f1e6977 | ||
|
|
0f433b1142 | ||
|
|
ab6bcad263 | ||
|
|
e40a3e0621 | ||
|
|
e56acf4f14 | ||
|
|
17977eda30 | ||
|
|
459a257568 | ||
|
|
fb65b53b9b | ||
|
|
c8d4062f73 | ||
|
|
c8e0992814 | ||
|
|
c0a12b29d0 | ||
|
|
02dded11a5 | ||
|
|
b71279cda5 | ||
|
|
c70477f8c6 | ||
|
|
9b51b8b882 | ||
|
|
48539596ef | ||
|
|
cafa99774c | ||
|
|
b5f7ac95d8 | ||
|
|
e984b97db0 | ||
|
|
64d4e02b4f | ||
|
|
90ddef5a41 | ||
|
|
58ef99f76b | ||
|
|
43c34956fb | ||
|
|
d7dee1b9d3 | ||
|
|
94a38ebc3a | ||
|
|
0764460eaf | ||
|
|
6fd02279da | ||
|
|
6274294bc5 | ||
|
|
27708026d4 | ||
|
|
849aa5b105 | ||
|
|
0be83bfbba | ||
|
|
f8592be43d | ||
|
|
647ad51423 | ||
|
|
73f51e45a8 | ||
|
|
bc4f455151 | ||
|
|
b4b5e0b6aa | ||
|
|
9927023680 | ||
|
|
9b2fb1d4d2 | ||
|
|
689b894506 | ||
|
|
06a30ae529 | ||
|
|
ed106d7f28 | ||
|
|
9d08fd8c7e | ||
|
|
7e8126704b | ||
|
|
d96767ee45 | ||
|
|
63f31bf73e | ||
|
|
7ecfe2353f | ||
|
|
7bec672c7e | ||
|
|
5aac5deb75 | ||
|
|
54e016ae71 | ||
|
|
6b53378839 | ||
|
|
1266f86444 | ||
|
|
231c02cdeb | ||
|
|
454a8d9d7b | ||
|
|
bbe92b9cd3 | ||
|
|
7bb41732fb | ||
|
|
c217f6be6a | ||
|
|
21662d38e6 | ||
|
|
19f3842292 | ||
|
|
ae0cbf00b1 | ||
|
|
9019c6864a | ||
|
|
d1b61c37fa | ||
|
|
32b68a5fad | ||
|
|
55fdb17b18 | ||
|
|
af730c79a6 | ||
|
|
041e6131d1 | ||
|
|
5a7d7e05b8 | ||
|
|
71b93d55ef | ||
|
|
d2b5e1e2d6 | ||
|
|
a3cb877c54 | ||
|
|
6895a71cc4 | ||
|
|
d9034b01f1 | ||
|
|
488ac8c3f2 | ||
|
|
ecf36d53c8 | ||
|
|
893ad920c5 | ||
|
|
0695c79f4e | ||
|
|
8837ea24cb | ||
|
|
65222d03b9 | ||
|
|
54937495e2 | ||
|
|
8a783b81a4 | ||
|
|
093da2a5a7 | ||
|
|
b68ea5fc7d | ||
|
|
90c587408f | ||
|
|
90475fbaa5 | ||
|
|
07ff9e1da0 | ||
|
|
5637799c92 | ||
|
|
73c82441e7 | ||
|
|
6d06921276 | ||
|
|
2996b34ddf | ||
|
|
e5821301f6 | ||
|
|
f4d1874ac5 | ||
|
|
f3428929a2 | ||
|
|
263d6950a1 | ||
|
|
8024f8e31a | ||
|
|
3849b29594 | ||
|
|
0e4d4da0e5 | ||
|
|
ca3cd51f91 | ||
|
|
fda1e0d33d | ||
|
|
1a850392bc | ||
|
|
14a79eb142 | ||
|
|
271b809495 | ||
|
|
2363269c1c | ||
|
|
f1db473d11 | ||
|
|
e35d05a5e3 | ||
|
|
c61783546b | ||
|
|
c08794bdfb | ||
|
|
4905975f81 | ||
|
|
d53bfb9201 | ||
|
|
0dac47cb62 | ||
|
|
7b68552359 | ||
|
|
775495c7e7 | ||
|
|
1057f13cdc | ||
|
|
ce59a299cb | ||
|
|
8235e025d4 | ||
|
|
b301381f90 | ||
|
|
c62f930634 | ||
|
|
85eb44ac95 | ||
|
|
d5f8063900 | ||
|
|
ee66929465 | ||
|
|
1b044b8a40 | ||
|
|
f46f6d34d3 | ||
|
|
3a96e8826f | ||
|
|
84738668ca | ||
|
|
dbea311aa8 |
@@ -1,5 +1,3 @@
|
||||
parm
|
||||
parms
|
||||
crate
|
||||
ser
|
||||
deriver
|
||||
|
||||
@@ -29,5 +29,5 @@ indent_size = 4
|
||||
[{Makefile,*.mak}]
|
||||
indent_style = tab
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
[{COMMIT_EDITMSG,EDIT_DESCRIPTION}]
|
||||
max_line_length = 72
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,6 +32,7 @@ cpacfstats/cpacfstatsd
|
||||
cpumf/chcpumf
|
||||
cpumf/lscpumf
|
||||
cpumf/lshwc
|
||||
cpumf/lspai
|
||||
cpumf/pai
|
||||
cpuplugd/cpuplugd
|
||||
dasdfmt/dasdfmt
|
||||
@@ -91,6 +92,7 @@ vmcp/vmcp
|
||||
vmur/vmur
|
||||
zconf/chp/chchp
|
||||
zconf/chp/lschp
|
||||
zconf/chp/chpstat/chpstat
|
||||
zconf/css/lscss
|
||||
zconf/qeth/lsqeth
|
||||
zconf/scm/lsscm
|
||||
|
||||
35
.pre-commit-config.yaml
Normal file
35
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
exclude: \.(bin|crl|crt|key)$
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-shebang-scripts-are-executable
|
||||
exclude_types: ['rust']
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: git-clang-format
|
||||
name: git-clang-format
|
||||
description: Run git-clang-format
|
||||
entry: git
|
||||
args: [clang-format, --staged, --]
|
||||
pass_filenames: true
|
||||
language: system
|
||||
require_serial: true
|
||||
minimum_pre_commit_version: "2.9.0"
|
||||
types_or: [c++, c]
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.1
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude_types: ['rust']
|
||||
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||
rev: 3.0.0
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
args: ["--external-sources"]
|
||||
5
.shellcheckrc
Normal file
5
.shellcheckrc
Normal file
@@ -0,0 +1,5 @@
|
||||
# Search in the current script's directory by default (since 0.7.0)
|
||||
source-path=SCRIPTDIR
|
||||
|
||||
# Allow external-sources (since 0.8.0)
|
||||
external-sources=true
|
||||
@@ -27,6 +27,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Eberhard Pasch
|
||||
- Eduard Shishkin
|
||||
- Einar Lueck
|
||||
- Eric Farman
|
||||
- Eric Sandeen
|
||||
- Erwin Vicari
|
||||
- Eugene Crosser
|
||||
@@ -35,6 +36,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Farhan Ali
|
||||
- Fedor Loshakov
|
||||
- Felix Beck
|
||||
- Finn Callies
|
||||
- Frank Blaschka
|
||||
- Frank Heimes
|
||||
- Frank Munzert
|
||||
@@ -56,6 +58,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Horst Hummel
|
||||
- Ingo Franzki
|
||||
- Ingo Tuchscherer
|
||||
- Jakub Čajka
|
||||
- Jan Glauber
|
||||
- Jan Höppner
|
||||
- Jan Willeke
|
||||
@@ -124,6 +127,8 @@ List of all individuals having contributed content to s390-tools
|
||||
- Thomas Richter
|
||||
- Thomas Spatzier
|
||||
- Thomas Weber
|
||||
- Thorsten Winkler
|
||||
- Tobias Huschle
|
||||
- Tuan Hoang
|
||||
- Ursula Braun
|
||||
- Utz Bacher
|
||||
@@ -134,3 +139,4 @@ List of all individuals having contributed content to s390-tools
|
||||
- Volker Sameske
|
||||
- Wenjia Zhang
|
||||
- Wolfgang Taphorn
|
||||
- Yaakov Selkowitz
|
||||
|
||||
100
CHANGELOG.md
100
CHANGELOG.md
@@ -1,6 +1,106 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __v2.33.1 (2024-05-28)__
|
||||
|
||||
For Linux kernel version: 6.9
|
||||
|
||||
Bug Fixes:
|
||||
- s390-tools: Fix formatting and typos in README.md
|
||||
- s390-tools: Fix release string
|
||||
|
||||
* __v2.33.0 (2024-05-27)__
|
||||
|
||||
For Linux kernel version: 6.9
|
||||
|
||||
Add new tools / libraries:
|
||||
- chpstat: New tool for displaying channel path statistics
|
||||
- libutil: Add output format helpers(util_fmt: JSON, JSON-SEQ, CSV, text pairs)
|
||||
|
||||
Changes of existing tools / libraries:
|
||||
- chzdev: Add --is-owner to identify files created by zdev
|
||||
- dasdfmt: Change default mode to always use full-format (Note: affects ESE DASD)
|
||||
- libap: Significantly reduce delay time between file lock retries
|
||||
- pvattest: Rewrite from C to Rust
|
||||
- pvattest: Support additional data & user-data
|
||||
- rust/pv: Support for Attestation
|
||||
|
||||
Bug Fixes:
|
||||
- chreipl: Improve disk type detection when running under QEMU
|
||||
- dbginfo.sh: Use POSIX option with uname
|
||||
- s390-tools: Fix missing hyphen escapes in the man page for many tools
|
||||
- zipl/src: Fix bugs in disk_get_info() reproducible in corner cases
|
||||
|
||||
* __v2.32.0 (2024-04-03)__
|
||||
|
||||
For Linux kernel version: 6.8
|
||||
|
||||
Changes of existing tools:
|
||||
- cpumf/lscpumf: add support for machine type 3932
|
||||
- genprotimg, pvattest, and pvsecret accept IBM signing key with Armonk as
|
||||
subject locality
|
||||
- zdump/zipl: Support for List-Directed dump from ECKD DASD
|
||||
- zkey: Detect FIPS mode and generate PBKDF for luksFormat according to it
|
||||
|
||||
Bug Fixes:
|
||||
- dbginfo.sh: dash compatible copy sequence
|
||||
- rust/pv_core: Fix UvDeviceInfo::get() method
|
||||
- zipl/src: Fix leak of files if run with a broken configuration
|
||||
- zkey: Fix convert command to accept only keys of type CCA-AESDATA
|
||||
|
||||
* __v2.31.0 (2024-02-02)__
|
||||
|
||||
For Linux kernel version: 6.7
|
||||
|
||||
General:
|
||||
- common.mak: Set default C/C++ standard to gnu11/gnu++11
|
||||
|
||||
Add new tools / libraries:
|
||||
- pvapconfig: Tool to automatically configure APQNs in SE KVM guests
|
||||
- s390-tools: Provide pre-commit configuration
|
||||
|
||||
Changes of existing tools:
|
||||
- cpuplugd: Adjust to CPU 0 being no longer hotpluggable
|
||||
- dbginfo.sh: Check for Dynamic Partition Mode
|
||||
- dbginfo.sh: Update man page and copyright
|
||||
- rust/pv: Add user-data signing and verifying
|
||||
- rust/pvsecret: Add user defined signatures and verifications
|
||||
- zdev/dracut: Consolidate device configuration
|
||||
|
||||
Bug Fixes:
|
||||
- dbginfo.sh: Fix relative path on script copy
|
||||
- libkmipclient: Fix build with libxml2-2.12.0
|
||||
- pvsecret: Fix panic if empty file is used as host key document
|
||||
- rust/pv: Fix 'elided_lifetimes_in_associated_constant' warning
|
||||
|
||||
* __v2.30.0 (2023-12-01)__
|
||||
|
||||
For Linux kernel version: 6.6
|
||||
|
||||
Add new tools / libraries:
|
||||
- lspai: Tool to display PAI counter sets
|
||||
- s390-tools: Provide a ShellCheck configuration
|
||||
|
||||
Changes of existing tools / libraries:
|
||||
- cpumf/pai: Add command line option for realtime scheduling
|
||||
- dbginfo.sh: enhance ethtool collection for ROCE
|
||||
- libutil/util_lockfile: add routine to return owning pid of file lock
|
||||
- lszcrypt: Improve lszcrypt output on SE guests
|
||||
- rust: Use a single workspace for all rust tools
|
||||
- zdev: limit the derivation of ZDEV_SITE_ID
|
||||
- zdump/df_s390: Update 'zgetdump -i' output with zlib info
|
||||
- zdump/dfi_s390: Support reading compressed s390_ext dumps
|
||||
- zipl/boot: Integrate zlib compression to single volume DASD dumper
|
||||
- zipl/boot: compile the bootloaders only if HOST_ARCH is s390x
|
||||
- zipl: Add --no-compress option to zipl command
|
||||
- zkey: Also check for deconfigured and check-stopped cards
|
||||
- dbginfo.sh: fix relative path on script copy
|
||||
|
||||
Bug Fixes:
|
||||
- ap_tools/ap-check: handle get-attributes between pre and post event
|
||||
- libutil: fix util_file_read_*() using wrong format specifiers
|
||||
- rust/pv: fix Invalid write of size 1
|
||||
|
||||
* __v2.29.0 (2023-08-04)__
|
||||
|
||||
For Linux kernel version: 6.5
|
||||
|
||||
6
Makefile
6
Makefile
@@ -15,12 +15,12 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools pvattest \
|
||||
rust
|
||||
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust
|
||||
|
||||
else
|
||||
BASELIB_DIRS =
|
||||
LIB_DIRS = libpv
|
||||
TOOL_DIRS = genprotimg pvattest rust
|
||||
TOOL_DIRS = genprotimg rust
|
||||
endif
|
||||
|
||||
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
34
README.md
34
README.md
@@ -19,6 +19,12 @@ Package contents
|
||||
all s390-tools that are written in rust and require external crates.
|
||||
Disable the compilation of all tools in `rust/` using HAVE_CARGO=0
|
||||
See the `rust/README.md` for Details
|
||||
- pvattest:
|
||||
Create, perform, and verify IBM Secure Execution attestation measurements.
|
||||
- pvapconfig:
|
||||
Automatic configure APQNs within an SE KVM guest
|
||||
- pvsecret:
|
||||
Manage secrets for IBM Secure Execution guests
|
||||
|
||||
* dasdfmt:
|
||||
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||
@@ -38,9 +44,6 @@ Package contents
|
||||
* genprotimg:
|
||||
Create a protected virtualization image.
|
||||
|
||||
* pvattest:
|
||||
Create, perform, and verify protected virtualization attestation measurements.
|
||||
|
||||
* udev rules:
|
||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||
@@ -360,12 +363,11 @@ the different tools are provided:
|
||||
|
||||
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
||||
|
||||
* pvattest:
|
||||
* rust/pvattest:
|
||||
For building pvattest you need OpenSSL version 1.1.1 or newer
|
||||
installed (openssl-devel.rpm). Also required is glib2.56 or newer
|
||||
(glib2-devel.rpm) and libcurl.
|
||||
installed (openssl-devel.rpm). Also required is cargo and libcurl.
|
||||
Tip: you may skip the pvattest build by adding
|
||||
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_GLIB2=0`.
|
||||
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
||||
|
||||
The runtime requirements are: openssl-libs (>= 1.1.1) and
|
||||
glib2.56 or newer.
|
||||
@@ -386,6 +388,24 @@ the different tools are provided:
|
||||
- Packages: blktrace, multipath-tools, sg3-utils
|
||||
- Tools: rsync, tar, lsscsi
|
||||
|
||||
* zipl
|
||||
For CCW-type DASD dump, zlib compression can be used to compress the dump
|
||||
data before writing it to the DASD partition. It can benefit from
|
||||
s390 on-chip compression accelerator (DFLTCC) and provide a faster dumping
|
||||
process, hence lower system downtime.
|
||||
The zlib version integrated with zipl (zipl/boot/zlib) is based on the Linux
|
||||
kernel zlib (kernel version 6.3) which represents zlib version 1.1.3 with a
|
||||
limited number of functions and a number of updates on top including s390
|
||||
hardware compression (DFLTCC) support. Also, all memory allocations are
|
||||
performed in advance, which aligns with zipl requirements.
|
||||
The CCW-type standalone dumper is built as a single binary and must be
|
||||
loaded to stage2 during boot. Hence, all required zlib functions must be
|
||||
integrated into it, and its size is restricted. To limit the size, only
|
||||
deflate-related parts are integrated (no decompression is required during
|
||||
dumping).
|
||||
Removing the inflate modules and function prototypes are the only major
|
||||
modifications made to the kernel version of zlib.
|
||||
|
||||
* zgetdump
|
||||
For building zgetdump you need OpenSSL version 1.1.0 or newer
|
||||
installed (openssl-devel.rpm). Also required is glib2
|
||||
|
||||
@@ -798,12 +798,34 @@ static int ap_check_handle_get_attributes(struct ap_check_anchor *anc)
|
||||
FILE *f;
|
||||
int rc;
|
||||
|
||||
rc = ap_get_lock_callout();
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to acquire configuration lock %d\n", rc);
|
||||
return -1;
|
||||
/*
|
||||
* For the get-attributes callout, we are typically called without the
|
||||
* callout lock held. However, there is a particular scenario (define
|
||||
* of an active mdev) where we may or may not be called with the lock
|
||||
* already held on behalf of mdevctl, depending on the mdevctl version.
|
||||
* Let's test for lock ownership first and, if already owned by the
|
||||
* parent (mdevctl) proceed rather than waiting on the file lock.
|
||||
*/
|
||||
rc = ap_try_lock_callout();
|
||||
switch (rc) {
|
||||
case 0:
|
||||
/* Lock acquired */
|
||||
anc->cleanup_lock = true;
|
||||
break;
|
||||
case 1:
|
||||
/* Lock held by parent -- trust the lock will remain held */
|
||||
break;
|
||||
default:
|
||||
/* Lock not acquired or held by parent -- do a normal obtain */
|
||||
rc = ap_get_lock_callout();
|
||||
if (rc) {
|
||||
fprintf(stderr,
|
||||
"Failed to acquire configuration lock %d\n",
|
||||
rc);
|
||||
return -1;
|
||||
}
|
||||
anc->cleanup_lock = true;
|
||||
}
|
||||
anc->cleanup_lock = true;
|
||||
|
||||
/*
|
||||
* Read the 'matrix' and 'control_domains' attributes to get the
|
||||
|
||||
50
common.mak
50
common.mak
@@ -31,11 +31,11 @@ endif
|
||||
# Global definitions
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 29
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
VERSION := 2
|
||||
RELEASE := 33
|
||||
PATCHLEVEL := 1
|
||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
export S390_TOOLS_RELEASE
|
||||
|
||||
reldir = $(subst $(realpath $(dir $(filter %common.mak,$(MAKEFILE_LIST))))/,,$(CURDIR))
|
||||
@@ -93,19 +93,19 @@ define cmd_define_and_export
|
||||
endef
|
||||
|
||||
define define_toolchain_variables
|
||||
$(eval $(call cmd_define_and_export, AS$(1)," AS$(1) ",$(2)as))
|
||||
$(eval $(call cmd_define_and_export, CC$(1)," CC$(1) ",$(2)gcc))
|
||||
$(eval $(call cmd_define_and_export, LINK$(1)," LINK$(1) ",$$(CC$(1))))
|
||||
$(eval $(call cmd_define_and_export, CXX$(1)," CXX$(1) ",$(2)g++))
|
||||
$(eval $(call cmd_define_and_export, LINKXX$(1)," LINKXX$(1) ",$$(CXX$(1))))
|
||||
$(eval $(call cmd_define_and_export, CPP$(1)," CPP$(1) ",$(2)gcc -E))
|
||||
$(eval $(call cmd_define_and_export, AR$(1)," AR$(1) ",$(2)ar))
|
||||
$(eval $(call cmd_define_and_export, NM$(1)," NM$(1) ",$(2)nm))
|
||||
$(eval $(call cmd_define_and_export, STRIP$(1)," STRIP$(1) ",$(2)strip))
|
||||
$(eval $(call cmd_define_and_export,OBJCOPY$(1)," OBJCOPY$(1) ",$(2)objcopy))
|
||||
$(eval $(call cmd_define_and_export,OBJDUMP$(1)," OBJDUMP$(1) ",$(2)objdump))
|
||||
$(eval PKG_CONFIG$(1) = pkg-config)
|
||||
$(eval export PKG_CONFIG$(1))
|
||||
$(call cmd_define_and_export, AS$(1)," AS$(1) ",$(2)as)
|
||||
$(call cmd_define_and_export, CC$(1)," CC$(1) ",$(2)gcc)
|
||||
$(call cmd_define_and_export, LINK$(1)," LINK$(1) ",$$(CC$(1)))
|
||||
$(call cmd_define_and_export, CXX$(1)," CXX$(1) ",$(2)g++)
|
||||
$(call cmd_define_and_export, LINKXX$(1)," LINKXX$(1) ",$$(CXX$(1)))
|
||||
$(call cmd_define_and_export, CPP$(1)," CPP$(1) ",$(2)gcc -E)
|
||||
$(call cmd_define_and_export, AR$(1)," AR$(1) ",$(2)ar)
|
||||
$(call cmd_define_and_export, NM$(1)," NM$(1) ",$(2)nm)
|
||||
$(call cmd_define_and_export, STRIP$(1)," STRIP$(1) ",$(2)strip)
|
||||
$(call cmd_define_and_export,OBJCOPY$(1)," OBJCOPY$(1) ",$(2)objcopy)
|
||||
$(call cmd_define_and_export,OBJDUMP$(1)," OBJDUMP$(1) ",$(2)objdump)
|
||||
PKG_CONFIG$(1) = pkg-config
|
||||
export PKG_CONFIG$(1)
|
||||
endef
|
||||
|
||||
# If the host architecture is not the same as the build architecture
|
||||
@@ -119,8 +119,8 @@ ifneq ($(HOST_ARCH),$(BUILD_ARCH))
|
||||
endif
|
||||
endif
|
||||
|
||||
$(call define_toolchain_variables,_FOR_BUILD,)
|
||||
$(call define_toolchain_variables,,$(CROSS_COMPILE))
|
||||
$(eval $(call define_toolchain_variables,_FOR_BUILD,))
|
||||
$(eval $(call define_toolchain_variables,,$(CROSS_COMPILE)))
|
||||
|
||||
|
||||
$(eval $(call cmd_define, RUNTEST," RUNTEST ",$(S390_TEST_LIB_PATH)/s390_runtest))
|
||||
@@ -293,8 +293,8 @@ INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
||||
$(SOINSTALLDIR) $(USRLIBDIR)
|
||||
OWNER = $(shell id -un)
|
||||
GROUP = $(shell id -gn)
|
||||
OWNER := $(shell id -un)
|
||||
GROUP := $(shell id -gn)
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
|
||||
# Special defines for zfcpdump
|
||||
@@ -304,7 +304,7 @@ ZFCPDUMP_FLAVOR = zfcpdump
|
||||
export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_INITRD ZFCPDUMP_FLAVOR
|
||||
|
||||
CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||
CFLAGS_FOR_BUILD ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||
CFLAGS_FOR_BUILD ?= -std=gnu11 $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||
CPPFLAGS ?= $(DEFAULT_CPPFLAGS)
|
||||
LDFLAGS ?= $(DEFAULT_LDFLAGS)
|
||||
|
||||
@@ -313,14 +313,14 @@ ALL_CFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
|
||||
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
|
||||
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
|
||||
-DS390_TOOLS_BINDIR=$(BINDIR) \
|
||||
$(CFLAGS)
|
||||
-std=gnu11 $(CFLAGS)
|
||||
CXXFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||
ALL_CXXFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
|
||||
-DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \
|
||||
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
|
||||
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
|
||||
-DS390_TOOLS_BINDIR=$(BINDIR) \
|
||||
$(CXXFLAGS)
|
||||
-std=gnu++11 $(CXXFLAGS)
|
||||
ALL_CPPFLAGS = -I $(rootdir)include $(CPPFLAGS)
|
||||
ALL_LDFLAGS = $(LDFLAGS)
|
||||
|
||||
|
||||
@@ -21,10 +21,11 @@ ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
||||
|
||||
all: check_dep cpacfstats cpacfstatsd
|
||||
|
||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
|
||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
|
||||
|
||||
cpacfstats: cpacfstats.o stats_sock.o
|
||||
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
install: all
|
||||
|
||||
@@ -355,13 +355,13 @@ PCKMO DES,
|
||||
.IP \(bu
|
||||
PCKMO 2key TDES,
|
||||
.IP \(bu
|
||||
PCMKO TDES,
|
||||
PCKMO TDES,
|
||||
.IP \(bu
|
||||
PCKMO AES 128bit,
|
||||
.IP \(bu
|
||||
PCKMO AES 192bit,
|
||||
.IP \(bu
|
||||
PCMKO AES 256bit,
|
||||
PCKMO AES 256bit,
|
||||
.IP \(bu
|
||||
PCKMO ECC P256,
|
||||
.IP \(bu
|
||||
|
||||
@@ -55,168 +55,6 @@ static const char *const counter_str[] = {
|
||||
[PAI_KERNEL] = "pai_kernel"
|
||||
};
|
||||
|
||||
/* Strings for the pai counter details. Note that this is 0-based
|
||||
* while PoP is 1-based.
|
||||
*/
|
||||
static const char *const pai_str[] = {
|
||||
[ 0] = "KM DES",
|
||||
[ 1] = "KM 2key TDES",
|
||||
[ 2] = "KM TDES",
|
||||
[ 3] = "KM DES protected key",
|
||||
[ 4] = "KM 2key TDES protected key",
|
||||
[ 5] = "KM TDES protected key",
|
||||
[ 6] = "KM AES 128bit",
|
||||
[ 7] = "KM AES 192bit",
|
||||
[ 8] = "KM AES 256bit",
|
||||
[ 9] = "KM AES 128bit protected key",
|
||||
[ 10] = "KM AES 192bit protected key",
|
||||
[ 11] = "KM AES 256bit protected key",
|
||||
[ 12] = "KM AES-XTS 128bit",
|
||||
[ 13] = "KM AES-XTS 256bit",
|
||||
[ 14] = "KM AES-XTS 128bit protected key",
|
||||
[ 15] = "KM AES-XTS 256bit protected key",
|
||||
[ 16] = "KMC DES",
|
||||
[ 17] = "KMC 2key TDES",
|
||||
[ 18] = "KMC TDES",
|
||||
[ 19] = "KMC DES protected key",
|
||||
[ 20] = "KMC 2key TDES protected key",
|
||||
[ 21] = "KMC TDES protected key",
|
||||
[ 22] = "KMC AES 128bit",
|
||||
[ 23] = "KMC AES 192bit",
|
||||
[ 24] = "KMC AES 256bit",
|
||||
[ 25] = "KMC AES 128bit protected key",
|
||||
[ 26] = "KMC AES 192bit protected key",
|
||||
[ 27] = "KMC AES 256bit protected key",
|
||||
[ 28] = "KMC PRNG",
|
||||
[ 29] = "KMA AES 128bit",
|
||||
[ 30] = "KMA AES 192bit",
|
||||
[ 31] = "KMA AES 256bit",
|
||||
[ 32] = "KMA AES 128bit protected key",
|
||||
[ 33] = "KMA AES 192bit protected key",
|
||||
[ 34] = "KMA AES 256bit protected key",
|
||||
[ 35] = "KMF DES",
|
||||
[ 36] = "KMF 2key TDES",
|
||||
[ 37] = "KMF TDES",
|
||||
[ 38] = "KMF DES protected key",
|
||||
[ 39] = "KMF 2key TDES protected key",
|
||||
[ 40] = "KMF TDES protected key",
|
||||
[ 41] = "KMF AES 128bit",
|
||||
[ 42] = "KMF AES 192bit",
|
||||
[ 43] = "KMF AES 256bit",
|
||||
[ 44] = "KMF AES 128bit protected key",
|
||||
[ 45] = "KMF AES 192bit protected key",
|
||||
[ 46] = "KMF AES 256bit protected key",
|
||||
[ 47] = "KMCTR DES",
|
||||
[ 48] = "KMCTR 2key TDES",
|
||||
[ 49] = "KMCTR TDES",
|
||||
[ 50] = "KMCTR DES protected key",
|
||||
[ 51] = "KMCTR 2key TDES protected key",
|
||||
[ 52] = "KMCTR TDES protected key",
|
||||
[ 53] = "KMCTR AES 128bit",
|
||||
[ 54] = "KMCTR AES 192bit",
|
||||
[ 55] = "KMCTR AES 256bit",
|
||||
[ 56] = "KMCTR AES 128bit protected key",
|
||||
[ 57] = "KMCTR AES 192bit protected key",
|
||||
[ 58] = "KMCTR AES 256bit protected key",
|
||||
[ 59] = "KMO DES",
|
||||
[ 60] = "KMO 2key TDES",
|
||||
[ 61] = "KMO TDES",
|
||||
[ 62] = "KMO DES protected key",
|
||||
[ 63] = "KMO 2key TDES protected key",
|
||||
[ 64] = "KMO TDES protected key",
|
||||
[ 65] = "KMO AES 128bit",
|
||||
[ 66] = "KMO AES 192bit",
|
||||
[ 67] = "KMO AES 256bit",
|
||||
[ 68] = "KMO AES 128bit protected key",
|
||||
[ 69] = "KMO AES 192bit protected key",
|
||||
[ 70] = "KMO AES 256bit protected key",
|
||||
[ 71] = "KIMD SHA1",
|
||||
[ 72] = "KIMD SHA256",
|
||||
[ 73] = "KIMD SHA512",
|
||||
[ 74] = "KIMD SHA3-224",
|
||||
[ 75] = "KIMD SHA3-256",
|
||||
[ 76] = "KIMD SHA3-384",
|
||||
[ 77] = "KIMD SHA3-512",
|
||||
[ 78] = "KIMD SHAKE 128",
|
||||
[ 79] = "KIMD SHAKE 256",
|
||||
[ 80] = "KIMD GHASH",
|
||||
[ 81] = "KLMD SHA1",
|
||||
[ 82] = "KLMD SHA256",
|
||||
[ 83] = "KLMD SHA512",
|
||||
[ 84] = "KLMD SHA3-224",
|
||||
[ 85] = "KLMD SHA3-256",
|
||||
[ 86] = "KLMD SHA3-384",
|
||||
[ 87] = "KLMD SHA3-512",
|
||||
[ 88] = "KLMD SHAKE 128",
|
||||
[ 89] = "KLMD SHAKE 256",
|
||||
[ 90] = "KMAC DES",
|
||||
[ 91] = "KMAC 2key TDES",
|
||||
[ 92] = "KMAC TDES",
|
||||
[ 93] = "KMAC DES protected key",
|
||||
[ 94] = "KMAC 2key TDES protected key",
|
||||
[ 95] = "KMAC TDES protected key",
|
||||
[ 96] = "KMAC AES 128bit",
|
||||
[ 97] = "KMAC AES 192bit",
|
||||
[ 98] = "KMAC AES 256bit",
|
||||
[ 99] = "KMAC AES 128bit protected key",
|
||||
[100] = "KMAC AES 192bit protected key",
|
||||
[101] = "KMAC AES 256bit protected key",
|
||||
[102] = "PCC Last Block CMAC DES",
|
||||
[103] = "PCC Last Block CMAC 2key TDES",
|
||||
[104] = "PCC Last Block CMAC TDES",
|
||||
[105] = "PCC Last Block CMAC DES protected key",
|
||||
[106] = "PCC Last Block CMAC 2key TDES protected key",
|
||||
[107] = "PCC Last Block CMAC TDES protected key",
|
||||
[108] = "PCC Last Block CMAC AES 128bit",
|
||||
[109] = "PCC Last Block CMAC AES 192bit",
|
||||
[110] = "PCC Last Block CMAC AES 256bit",
|
||||
[111] = "PCC Last Block CMAC AES 128bit protected key",
|
||||
[112] = "PCC Last Block CMAC AES 192bit protected key",
|
||||
[113] = "PCC Last Block CMAC AES 256bit protected key",
|
||||
[114] = "PCC XTS Parameter AES 128bit",
|
||||
[115] = "PCC XTS Parameter AES 256bit",
|
||||
[116] = "PCC XTS Parameter AES 128bit protected key",
|
||||
[117] = "PCC XTS Parameter AES 256bit protected key",
|
||||
[118] = "PCC Scalar Mult P256",
|
||||
[119] = "PCC Scalar Mult P384",
|
||||
[120] = "PCC Scalar Mult P521",
|
||||
[121] = "PCC Scalar Mult Ed25519",
|
||||
[122] = "PCC Scalar Mult Ed448",
|
||||
[123] = "PCC Scalar Mult X25519",
|
||||
[124] = "PCC Scalar Mult X448",
|
||||
[125] = "PRNO SHA512 DRNG",
|
||||
[126] = "PRNO TRNG Query Ratio",
|
||||
[127] = "PRNO TRNG",
|
||||
[128] = "KDSA ECDSA Verify P256",
|
||||
[129] = "KDSA ECDSA Verify P384",
|
||||
[130] = "KDSA ECDSA Verify P521",
|
||||
[131] = "KDSA ECDSA Sign P256",
|
||||
[132] = "KDSA ECDSA Sign P384",
|
||||
[133] = "KDSA ECDSA Sign P521",
|
||||
[134] = "KDSA ECDSA Sign P256 protected key",
|
||||
[135] = "KDSA ECDSA Sign P384 protected key",
|
||||
[136] = "KDSA ECDSA Sign P521 protected key",
|
||||
[137] = "KDSA EdDSA Verify Ed25519",
|
||||
[138] = "KDSA EdDSA Verify Ed448",
|
||||
[139] = "KDSA EdDSA Sign Ed25519",
|
||||
[140] = "KDSA EdDSA Sign Ed448",
|
||||
[141] = "KDSA EdDSA Sign Ed25519 protected key",
|
||||
[142] = "KDSA EdDSA Sign Ed448 protected key",
|
||||
[143] = "PCKMO DES",
|
||||
[144] = "PCKMO 2key TDES",
|
||||
[145] = "PCMKO TDES",
|
||||
[146] = "PCKMO AES 128bit",
|
||||
[147] = "PCKMO AES 192bit",
|
||||
[148] = "PCMKO AES 256bit",
|
||||
[149] = "PCKMO ECC P256",
|
||||
[150] = "PCKMO ECC P384",
|
||||
[151] = "PCKMO ECC P521",
|
||||
[152] = "PCKMO ECC Ed25519",
|
||||
[153] = "PCKMO ECC Ed448",
|
||||
[154] = "Reserved 1",
|
||||
[155] = "Reserved 2"
|
||||
};
|
||||
|
||||
|
||||
static int paiprintnonzero;
|
||||
|
||||
@@ -275,7 +113,8 @@ static void json_print_virtual_counter_answer(int s, int ctr,
|
||||
int state, uint64_t value)
|
||||
{
|
||||
int paictr = 0, paistate = 0, ec;
|
||||
uint64_t i, paivalue = 0, maxnum;
|
||||
uint64_t i, paivalue = 0;
|
||||
unsigned int maxnum;
|
||||
const char *space;
|
||||
|
||||
switch (ctr) {
|
||||
@@ -288,11 +127,11 @@ static void json_print_virtual_counter_answer(int s, int ctr,
|
||||
printf("\"value\":%d}", !!value);
|
||||
return;
|
||||
case PAI_USER:
|
||||
maxnum = NUM_PAI_USER;
|
||||
maxnum = get_num_user_space_ctrs();
|
||||
space = "user";
|
||||
break;
|
||||
case PAI_KERNEL:
|
||||
maxnum = NUM_PAI_KERNEL;
|
||||
maxnum = MAX_NUM_PAI;
|
||||
space = "kernel";
|
||||
break;
|
||||
default:
|
||||
@@ -304,7 +143,7 @@ static void json_print_virtual_counter_answer(int s, int ctr,
|
||||
return;
|
||||
if (value > maxnum) {
|
||||
eprint("Incompatible versions detected!\n");
|
||||
eprint("Expected %"PRIu64" counter space for %s, but got %"PRIu64"\n",
|
||||
eprint("Expected %lu counter space for %s, but got %lu\n",
|
||||
maxnum, space, value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -315,18 +154,18 @@ static void json_print_virtual_counter_answer(int s, int ctr,
|
||||
/* No more data for this virtual event after error. */
|
||||
return;
|
||||
}
|
||||
if (paictr > NUM_PAI_KERNEL) {
|
||||
if (paictr > MAX_NUM_PAI) {
|
||||
eprint("Pai counter number too big: %d\n", paictr);
|
||||
} else {
|
||||
printjsonsep();
|
||||
printf("{\"counter\":\"%s\",\"space\":\"%s\",\"counterid\":%d,",
|
||||
pai_str[paictr], space, paictr + 1);
|
||||
get_ctr_name(paictr), space, paictr + 1);
|
||||
if (paistate < 0) {
|
||||
printf("\"error\":%d}", paistate);
|
||||
/* Protocol does not send further counters. */
|
||||
return;
|
||||
}
|
||||
printf("\"value\":%"PRIu64"}", paivalue);
|
||||
printf("\"value\":%lu}", paivalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,7 +180,8 @@ static void print_virtual_counter_answer(int s,
|
||||
[UNSUPPORTED] = "unsupported"
|
||||
};
|
||||
int paictr = 0, paistate = 0, ec;
|
||||
uint64_t i, paivalue = 0, maxnum;
|
||||
uint64_t i, paivalue = 0;
|
||||
unsigned int maxnum;
|
||||
const char *ctrstr;
|
||||
|
||||
switch (ctr) {
|
||||
@@ -350,11 +190,11 @@ static void print_virtual_counter_answer(int s,
|
||||
printf(" hotplug detected\n");
|
||||
return;
|
||||
case PAI_USER:
|
||||
maxnum = NUM_PAI_USER;
|
||||
maxnum = get_num_user_space_ctrs();
|
||||
ctrstr = "pai_user";
|
||||
break;
|
||||
case PAI_KERNEL:
|
||||
maxnum = NUM_PAI_KERNEL;
|
||||
maxnum = MAX_NUM_PAI;
|
||||
ctrstr = "pai_kernel";
|
||||
break;
|
||||
default:
|
||||
@@ -372,7 +212,7 @@ static void print_virtual_counter_answer(int s,
|
||||
return;
|
||||
if (value > maxnum) {
|
||||
eprint("Incompatible versions detected!\n");
|
||||
eprint("Expected %"PRIu64" counters for %s, but got %"PRIu64"\n",
|
||||
eprint("Expected %lu counters for %s, but got %lu\n",
|
||||
maxnum, ctrstr, value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -383,10 +223,11 @@ static void print_virtual_counter_answer(int s,
|
||||
/* No more data for this virtual event after error. */
|
||||
return;
|
||||
}
|
||||
if (paictr > NUM_PAI_KERNEL)
|
||||
if (paictr > MAX_NUM_PAI)
|
||||
eprint("Pai counter number too big: %d\n", paictr);
|
||||
else if (!paiprintnonzero || paivalue > 0)
|
||||
printf(" %-45s: %"PRIu64"\n", pai_str[paictr], paivalue);
|
||||
printf(" (%3d) %-45s: %lu\n", paictr + 1,
|
||||
get_ctr_name(paictr), paivalue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +243,7 @@ static void print_answer(int s, int ctr, int state, uint64_t value)
|
||||
else if (state == UNSUPPORTED)
|
||||
printf(" %s counter: unsupported\n", counter_str[ctr]);
|
||||
else
|
||||
printf(" %s counter: %"PRIu64"\n", counter_str[ctr], value);
|
||||
printf(" %s counter: %lu\n", counter_str[ctr], value);
|
||||
}
|
||||
|
||||
|
||||
@@ -417,7 +258,7 @@ static void json_print_answer(int s, int ctr, int state, uint64_t value)
|
||||
} else if (state == ENABLED) {
|
||||
printjsonsep();
|
||||
printf("{\"counter\":\"%s\",", counter_str[ctr]);
|
||||
printf("\"value\":%"PRIu64"}", value);
|
||||
printf("\"value\":%lu}", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,22 @@
|
||||
#define DEFAULT_RECV_TIMEOUT (30 * 1000)
|
||||
|
||||
/*
|
||||
* Number of PAI counters for user space. This excludes PCKMO since
|
||||
* this instruction is privileged.
|
||||
* Number of PAI counters. Contains all counters regardless of kernel or user
|
||||
* space
|
||||
*/
|
||||
#define NUM_PAI_USER 143
|
||||
/* Number of PAI counters for kernel space. Contains all counters. */
|
||||
#define NUM_PAI_KERNEL 156
|
||||
#define MAX_NUM_PAI 156
|
||||
|
||||
/*
|
||||
* This is the sysfs directory from which cpacfstatsd daemon application loads
|
||||
* the available PAI counters
|
||||
*/
|
||||
#define SYSFS_PAI_COUNTER "/sys/bus/event_source/devices/pai_crypto/events/"
|
||||
|
||||
/*
|
||||
* Note that this is the first kernel only counter in the 1-based list of the
|
||||
* architecture and NOT from the 0-based list in the cpacfstats code!
|
||||
*/
|
||||
#define FIRST_KERNEL_ONLY_COUNTER 144
|
||||
|
||||
int eprint(const char *format, ...);
|
||||
|
||||
@@ -67,6 +77,12 @@ enum state_e {
|
||||
UNSUPPORTED
|
||||
};
|
||||
|
||||
enum counter_type {
|
||||
SUPPRESS_COUNTER = 0,
|
||||
KERNEL_AND_USER_COUNTER,
|
||||
KERNEL_ONLY_COUNTER,
|
||||
};
|
||||
|
||||
/*
|
||||
* query send from client to daemon
|
||||
* Consist of:
|
||||
@@ -122,15 +138,23 @@ int recv_msg(int sfd, struct msg *m, int timeout);
|
||||
|
||||
/* perf_crypto.c */
|
||||
|
||||
int perf_init(void);
|
||||
int perf_init(unsigned int *supported_counters);
|
||||
void perf_stop(void);
|
||||
void perf_close(void);
|
||||
int perf_enable_ctr(enum ctr_e ctr);
|
||||
int perf_disable_ctr(enum ctr_e ctr);
|
||||
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value);
|
||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
|
||||
int perf_enable_ctr(enum ctr_e ctr, unsigned int *supported_counters);
|
||||
int perf_disable_ctr(enum ctr_e ctr, unsigned int *supported_counters);
|
||||
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
||||
*supported_counters);
|
||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
||||
*supported_counters);
|
||||
int perf_ecc_supported(void);
|
||||
int perf_ctr_state(enum ctr_e ctr);
|
||||
int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value);
|
||||
|
||||
/* cpacfstats_common.c */
|
||||
|
||||
enum counter_type is_user_space(unsigned int ctr);
|
||||
const char *get_ctr_name(unsigned int ctr);
|
||||
unsigned int get_num_user_space_ctrs(void);
|
||||
|
||||
#endif
|
||||
|
||||
233
cpacfstats/cpacfstats_common.c
Normal file
233
cpacfstats/cpacfstats_common.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* cpacfstats_common.c - shared code by daemon and client
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "cpacfstats.h"
|
||||
|
||||
struct pai_counter {
|
||||
const char *str;
|
||||
const unsigned int counter_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Strings for the pai counter details.
|
||||
* Integer indicating if kernel space is needed (0 for user, KERNEL_ONLY_COUNTER for kernel)
|
||||
* Note that this is 0-based while PoP is 1-based.
|
||||
*
|
||||
* When adding new items to this list add the counter number in the pai_idx
|
||||
* list in cpacfstatsd.c and increase the number of total counters in
|
||||
* cpacfstats.h.
|
||||
*/
|
||||
const struct pai_counter pai[] = {
|
||||
[ 0] = {"KM DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 1] = {"KM 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 2] = {"KM TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 3] = {"KM DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 4] = {"KM 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 5] = {"KM TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 6] = {"KM AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 7] = {"KM AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 8] = {"KM AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 9] = {"KM AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 10] = {"KM AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 11] = {"KM AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 12] = {"KM AES-XTS 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 13] = {"KM AES-XTS 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 14] = {"KM AES-XTS 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 15] = {"KM AES-XTS 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 16] = {"KMC DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 17] = {"KMC 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 18] = {"KMC TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 19] = {"KMC DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 20] = {"KMC 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 21] = {"KMC TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 22] = {"KMC AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 23] = {"KMC AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 24] = {"KMC AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 25] = {"KMC AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 26] = {"KMC AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 27] = {"KMC AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 28] = {"KMC PRNG", KERNEL_AND_USER_COUNTER},
|
||||
[ 29] = {"KMA AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 30] = {"KMA AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 31] = {"KMA AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 32] = {"KMA AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 33] = {"KMA AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 34] = {"KMA AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 35] = {"KMF DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 36] = {"KMF 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 37] = {"KMF TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 38] = {"KMF DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 39] = {"KMF 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 40] = {"KMF TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 41] = {"KMF AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 42] = {"KMF AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 43] = {"KMF AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 44] = {"KMF AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 45] = {"KMF AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 46] = {"KMF AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 47] = {"KMCTR DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 48] = {"KMCTR 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 49] = {"KMCTR TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 50] = {"KMCTR DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 51] = {"KMCTR 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 52] = {"KMCTR TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 53] = {"KMCTR AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 54] = {"KMCTR AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 55] = {"KMCTR AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 56] = {"KMCTR AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 57] = {"KMCTR AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 58] = {"KMCTR AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 59] = {"KMO DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 60] = {"KMO 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 61] = {"KMO TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 62] = {"KMO DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 63] = {"KMO 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 64] = {"KMO TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 65] = {"KMO AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 66] = {"KMO AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 67] = {"KMO AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 68] = {"KMO AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 69] = {"KMO AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 70] = {"KMO AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 71] = {"KIMD SHA1", KERNEL_AND_USER_COUNTER},
|
||||
[ 72] = {"KIMD SHA256", KERNEL_AND_USER_COUNTER},
|
||||
[ 73] = {"KIMD SHA512", KERNEL_AND_USER_COUNTER},
|
||||
[ 74] = {"KIMD SHA3-224", KERNEL_AND_USER_COUNTER},
|
||||
[ 75] = {"KIMD SHA3-256", KERNEL_AND_USER_COUNTER},
|
||||
[ 76] = {"KIMD SHA3-384", KERNEL_AND_USER_COUNTER},
|
||||
[ 77] = {"KIMD SHA3-512", KERNEL_AND_USER_COUNTER},
|
||||
[ 78] = {"KIMD SHAKE 128", KERNEL_AND_USER_COUNTER},
|
||||
[ 79] = {"KIMD SHAKE 256", KERNEL_AND_USER_COUNTER},
|
||||
[ 80] = {"KIMD GHASH", KERNEL_AND_USER_COUNTER},
|
||||
[ 81] = {"KLMD SHA1", KERNEL_AND_USER_COUNTER},
|
||||
[ 82] = {"KLMD SHA256", KERNEL_AND_USER_COUNTER},
|
||||
[ 83] = {"KLMD SHA512", KERNEL_AND_USER_COUNTER},
|
||||
[ 84] = {"KLMD SHA3-224", KERNEL_AND_USER_COUNTER},
|
||||
[ 85] = {"KLMD SHA3-256", KERNEL_AND_USER_COUNTER},
|
||||
[ 86] = {"KLMD SHA3-384", KERNEL_AND_USER_COUNTER},
|
||||
[ 87] = {"KLMD SHA3-512", KERNEL_AND_USER_COUNTER},
|
||||
[ 88] = {"KLMD SHAKE 128", KERNEL_AND_USER_COUNTER},
|
||||
[ 89] = {"KLMD SHAKE 256", KERNEL_AND_USER_COUNTER},
|
||||
[ 90] = {"KMAC DES", KERNEL_AND_USER_COUNTER},
|
||||
[ 91] = {"KMAC 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 92] = {"KMAC TDES", KERNEL_AND_USER_COUNTER},
|
||||
[ 93] = {"KMAC DES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 94] = {"KMAC 2key TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 95] = {"KMAC TDES protected key", KERNEL_AND_USER_COUNTER},
|
||||
[ 96] = {"KMAC AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 97] = {"KMAC AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 98] = {"KMAC AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[ 99] = {"KMAC AES 128bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[100] = {"KMAC AES 192bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[101] = {"KMAC AES 256bit protected key", KERNEL_AND_USER_COUNTER},
|
||||
[102] = {"PCC Last Block CMAC DES", KERNEL_AND_USER_COUNTER},
|
||||
[103] = {"PCC Last Block CMAC 2key TDES", KERNEL_AND_USER_COUNTER},
|
||||
[104] = {"PCC Last Block CMAC TDES", KERNEL_AND_USER_COUNTER},
|
||||
[105] = {"PCC Last Block CMAC DES protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[106] = {"PCC Last Block CMAC 2key TDES protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[107] = {"PCC Last Block CMAC TDES protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[108] = {"PCC Last Block CMAC AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[109] = {"PCC Last Block CMAC AES 192bit", KERNEL_AND_USER_COUNTER},
|
||||
[110] = {"PCC Last Block CMAC AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[111] = {"PCC Last Block CMAC AES 128bit protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[112] = {"PCC Last Block CMAC AES 192bit protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[113] = {"PCC Last Block CMAC AES 256bit protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[114] = {"PCC XTS Parameter AES 128bit", KERNEL_AND_USER_COUNTER},
|
||||
[115] = {"PCC XTS Parameter AES 256bit", KERNEL_AND_USER_COUNTER},
|
||||
[116] = {"PCC XTS Parameter AES 128bit protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[117] = {"PCC XTS Parameter AES 256bit protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[118] = {"PCC Scalar Mult P256", KERNEL_AND_USER_COUNTER},
|
||||
[119] = {"PCC Scalar Mult P384", KERNEL_AND_USER_COUNTER},
|
||||
[120] = {"PCC Scalar Mult P521", KERNEL_AND_USER_COUNTER},
|
||||
[121] = {"PCC Scalar Mult Ed25519", KERNEL_AND_USER_COUNTER},
|
||||
[122] = {"PCC Scalar Mult Ed448", KERNEL_AND_USER_COUNTER},
|
||||
[123] = {"PCC Scalar Mult X25519", KERNEL_AND_USER_COUNTER},
|
||||
[124] = {"PCC Scalar Mult X448", KERNEL_AND_USER_COUNTER},
|
||||
[125] = {"PRNO SHA512 DRNG", KERNEL_AND_USER_COUNTER},
|
||||
[126] = {"PRNO TRNG Query Ratio", KERNEL_AND_USER_COUNTER},
|
||||
[127] = {"PRNO TRNG", KERNEL_AND_USER_COUNTER},
|
||||
[128] = {"KDSA ECDSA Verify P256", KERNEL_AND_USER_COUNTER},
|
||||
[129] = {"KDSA ECDSA Verify P384", KERNEL_AND_USER_COUNTER},
|
||||
[130] = {"KDSA ECDSA Verify P521", KERNEL_AND_USER_COUNTER},
|
||||
[131] = {"KDSA ECDSA Sign P256", KERNEL_AND_USER_COUNTER},
|
||||
[132] = {"KDSA ECDSA Sign P384", KERNEL_AND_USER_COUNTER},
|
||||
[133] = {"KDSA ECDSA Sign P521", KERNEL_AND_USER_COUNTER},
|
||||
[134] = {"KDSA ECDSA Sign P256 protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[135] = {"KDSA ECDSA Sign P384 protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[136] = {"KDSA ECDSA Sign P521 protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[137] = {"KDSA EdDSA Verify Ed25519", KERNEL_AND_USER_COUNTER},
|
||||
[138] = {"KDSA EdDSA Verify Ed448", KERNEL_AND_USER_COUNTER},
|
||||
[139] = {"KDSA EdDSA Sign Ed25519", KERNEL_AND_USER_COUNTER},
|
||||
[140] = {"KDSA EdDSA Sign Ed448", KERNEL_AND_USER_COUNTER},
|
||||
[141] = {"KDSA EdDSA Sign Ed25519 protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[142] = {"KDSA EdDSA Sign Ed448 protected key",
|
||||
KERNEL_AND_USER_COUNTER},
|
||||
[143] = {"PCKMO DES", KERNEL_ONLY_COUNTER},
|
||||
[144] = {"PCKMO 2key TDES", KERNEL_ONLY_COUNTER},
|
||||
[145] = {"PCKMO TDES", KERNEL_ONLY_COUNTER},
|
||||
[146] = {"PCKMO AES 128bit", KERNEL_ONLY_COUNTER},
|
||||
[147] = {"PCKMO AES 192bit", KERNEL_ONLY_COUNTER},
|
||||
[148] = {"PCKMO AES 256bit", KERNEL_ONLY_COUNTER},
|
||||
[149] = {"PCKMO ECC P256", KERNEL_ONLY_COUNTER},
|
||||
[150] = {"PCKMO ECC P384", KERNEL_ONLY_COUNTER},
|
||||
[151] = {"PCKMO ECC P521", KERNEL_ONLY_COUNTER},
|
||||
[152] = {"PCKMO ECC Ed25519", KERNEL_ONLY_COUNTER},
|
||||
[153] = {"PCKMO ECC Ed448", KERNEL_ONLY_COUNTER},
|
||||
[154] = {"Reserved 1", KERNEL_ONLY_COUNTER},
|
||||
[155] = {"Reserved 2", KERNEL_ONLY_COUNTER}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns counter_type of pai_counter struct
|
||||
*
|
||||
* SUPPRESS_COUNTER
|
||||
* KERNEL_AND_USER_COUNTER
|
||||
* KERNEL_ONLY_COUNTER
|
||||
*/
|
||||
enum counter_type is_user_space(unsigned int ctr)
|
||||
{
|
||||
if (ctr >= MAX_NUM_PAI)
|
||||
return SUPPRESS_COUNTER;
|
||||
return pai[ctr].counter_type;
|
||||
}
|
||||
|
||||
const char *get_ctr_name(unsigned int ctr)
|
||||
{
|
||||
if (ctr >= MAX_NUM_PAI)
|
||||
return NULL;
|
||||
return pai[ctr].str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns number of PAI counters for which no kernel space is needed
|
||||
*/
|
||||
unsigned int get_num_user_space_ctrs(void)
|
||||
{
|
||||
unsigned int counter = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_NUM_PAI; i++) {
|
||||
if (is_user_space(i) == KERNEL_AND_USER_COUNTER)
|
||||
counter++;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
@@ -24,12 +24,52 @@
|
||||
#include <sys/stat.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "cpacfstats.h"
|
||||
|
||||
static volatile int stopsig;
|
||||
|
||||
/*
|
||||
* This list contains the counter numbers sorted by instruction
|
||||
*/
|
||||
static const unsigned int pai_idx[] = {
|
||||
// KM
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
// KMC
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
|
||||
// KMA
|
||||
29, 30, 31, 32, 33, 34,
|
||||
// KMF
|
||||
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
|
||||
// KMCTR
|
||||
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
|
||||
// KMO
|
||||
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
|
||||
// KIMD
|
||||
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
|
||||
// KLMD
|
||||
81, 82, 83, 84, 85, 86, 87, 88, 89,
|
||||
// KMAC
|
||||
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
|
||||
// PCC
|
||||
102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
|
||||
114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
|
||||
// PRNO
|
||||
125, 126, 127,
|
||||
// KDSA
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
|
||||
140, 141, 142,
|
||||
// PCKMO
|
||||
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
|
||||
// Reserved
|
||||
154, 155
|
||||
};
|
||||
|
||||
static const char *const name = "cpacfstatsd";
|
||||
|
||||
static const char *const usage =
|
||||
@@ -93,34 +133,35 @@ static int send_answer(int s, int ctr, int state, uint64_t value)
|
||||
* Note that the PAI counters are 0-based, not 1 based as in PoP!
|
||||
* Sending ends with the first error.
|
||||
*/
|
||||
static int do_send_pai(int s, int user)
|
||||
static int do_send_pai(int s, int user, unsigned int *counter)
|
||||
{
|
||||
int ctr, state, i, maxctr, rc = 0;
|
||||
int ctr, state, i, rc = 0;
|
||||
unsigned int current_ctr;
|
||||
uint64_t value;
|
||||
|
||||
if (user) {
|
||||
ctr = PAI_USER;
|
||||
maxctr = NUM_PAI_USER;
|
||||
} else {
|
||||
ctr = PAI_KERNEL;
|
||||
maxctr = NUM_PAI_KERNEL;
|
||||
}
|
||||
ctr = user ? PAI_USER : PAI_KERNEL;
|
||||
|
||||
state = perf_ctr_state(ctr);
|
||||
if (state != ENABLED)
|
||||
return rc;
|
||||
for (i = 0; i < maxctr; ++i) {
|
||||
rc = perf_read_pai_ctr(i, user, &value);
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
current_ctr = pai_idx[i];
|
||||
if ((user && is_user_space(current_ctr) != KERNEL_AND_USER_COUNTER) ||
|
||||
(!user && is_user_space(current_ctr) == SUPPRESS_COUNTER) ||
|
||||
counter[current_ctr] != 1)
|
||||
continue;
|
||||
rc = perf_read_pai_ctr(current_ctr, user, &value);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
send_answer(s, current_ctr, rc, 0);
|
||||
break;
|
||||
}
|
||||
send_answer(s, i, state, value);
|
||||
send_answer(s, current_ctr, state, value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int do_enable(int s, enum ctr_e ctr)
|
||||
static int do_enable(int s, enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
uint64_t value = 0;
|
||||
int i, rc = 0;
|
||||
@@ -132,7 +173,7 @@ static int do_enable(int s, enum ctr_e ctr)
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
state = perf_ctr_state(i);
|
||||
if (state == DISABLED) {
|
||||
rc = perf_enable_ctr(i);
|
||||
rc = perf_enable_ctr(i, supported_counters);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
@@ -140,7 +181,7 @@ static int do_enable(int s, enum ctr_e ctr)
|
||||
state = ENABLED;
|
||||
}
|
||||
if (state != UNSUPPORTED) {
|
||||
rc = perf_read_ctr(i, &value);
|
||||
rc = perf_read_ctr(i, &value, supported_counters);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
@@ -148,20 +189,20 @@ static int do_enable(int s, enum ctr_e ctr)
|
||||
}
|
||||
send_answer(s, i, state, value);
|
||||
if (i == PAI_USER)
|
||||
rc = do_send_pai(s, 1);
|
||||
rc = do_send_pai(s, 1, supported_counters);
|
||||
if (i == PAI_KERNEL)
|
||||
rc = do_send_pai(s, 0);
|
||||
rc = do_send_pai(s, 0, supported_counters);
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL);
|
||||
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int do_disable(int s, enum ctr_e ctr)
|
||||
static int do_disable(int s, enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
int i, rc = 0;
|
||||
uint64_t value;
|
||||
@@ -171,7 +212,7 @@ static int do_disable(int s, enum ctr_e ctr)
|
||||
continue;
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
if (perf_ctr_state(i) == ENABLED) {
|
||||
rc = perf_disable_ctr(i);
|
||||
rc = perf_disable_ctr(i, supported_counters);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
@@ -181,14 +222,14 @@ static int do_disable(int s, enum ctr_e ctr)
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL);
|
||||
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int do_reset(int s, enum ctr_e ctr)
|
||||
static int do_reset(int s, enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
int i, rc = 0, state;
|
||||
uint64_t value;
|
||||
@@ -199,7 +240,7 @@ static int do_reset(int s, enum ctr_e ctr)
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
state = perf_ctr_state(i);
|
||||
if (state == ENABLED) {
|
||||
rc = perf_reset_ctr(i, &value);
|
||||
rc = perf_reset_ctr(i, &value, supported_counters);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
@@ -207,20 +248,20 @@ static int do_reset(int s, enum ctr_e ctr)
|
||||
}
|
||||
send_answer(s, i, state, value);
|
||||
if (i == PAI_USER)
|
||||
rc = do_send_pai(s, 1);
|
||||
rc = do_send_pai(s, 1, supported_counters);
|
||||
if (i == PAI_KERNEL)
|
||||
rc = do_send_pai(s, 0);
|
||||
rc = do_send_pai(s, 0, supported_counters);
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL);
|
||||
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int do_print(int s, enum ctr_e ctr)
|
||||
static int do_print(int s, enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
int i, rc = 0, state;
|
||||
uint64_t value = 0;
|
||||
@@ -231,7 +272,7 @@ static int do_print(int s, enum ctr_e ctr)
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
state = perf_ctr_state(i);
|
||||
if (state == ENABLED) {
|
||||
rc = perf_read_ctr(i, &value);
|
||||
rc = perf_read_ctr(i, &value, supported_counters);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
@@ -239,13 +280,13 @@ static int do_print(int s, enum ctr_e ctr)
|
||||
}
|
||||
send_answer(s, i, state, value);
|
||||
if (i == PAI_USER)
|
||||
rc = do_send_pai(s, 1);
|
||||
rc = do_send_pai(s, 1, supported_counters);
|
||||
if (i == PAI_KERNEL)
|
||||
rc = do_send_pai(s, 0);
|
||||
rc = do_send_pai(s, 0, supported_counters);
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||
rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL);
|
||||
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||
}
|
||||
return rc;
|
||||
@@ -436,9 +477,44 @@ int eprint(const char *format, ...)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* returns -1 on error
|
||||
* returns X where X is the found counters in dir
|
||||
*
|
||||
* the supplied array supported_counters[] is filled in this function with the
|
||||
* available PAI counters found in SYSFS_PAI_COUNTER
|
||||
*/
|
||||
static void supported_functions(unsigned int supported_counters[])
|
||||
{
|
||||
const char *dir = SYSFS_PAI_COUNTER;
|
||||
struct dirent *dp = NULL;
|
||||
char filepath[PATH_MAX];
|
||||
unsigned int num;
|
||||
DIR *dfd = NULL;
|
||||
|
||||
dfd = opendir(dir);
|
||||
if (dfd == NULL)
|
||||
return;
|
||||
|
||||
while ((dp = readdir(dfd)) != NULL) {
|
||||
if ((strcmp(dp->d_name, ".") != 0) &&
|
||||
(strcmp(dp->d_name, "..") != 0)) {
|
||||
snprintf(filepath, sizeof(filepath), "%s%s", dir, dp->d_name);
|
||||
if (util_file_read_va(filepath, "event=0x10%x", &num) != 1)
|
||||
continue;
|
||||
if (num > 0 && num <= MAX_NUM_PAI)
|
||||
supported_counters[num - 1] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dfd);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc, sfd, foreground = 0, startup_pipe = -1, initialized = 0;
|
||||
unsigned int supported_counters[MAX_NUM_PAI] = { 0 };
|
||||
struct sigaction act;
|
||||
|
||||
if (argc > 1) {
|
||||
@@ -485,7 +561,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (perf_init() != 0) {
|
||||
supported_functions(supported_counters);
|
||||
|
||||
if (perf_init(supported_counters) != 0) {
|
||||
eprint("Couldn't initialize perf lib\n");
|
||||
goto error;
|
||||
}
|
||||
@@ -548,13 +626,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (cmd == ENABLE)
|
||||
rc = do_enable(s, ctr);
|
||||
rc = do_enable(s, ctr, supported_counters);
|
||||
else if (cmd == DISABLE)
|
||||
rc = do_disable(s, ctr);
|
||||
rc = do_disable(s, ctr, supported_counters);
|
||||
else if (cmd == RESET)
|
||||
rc = do_reset(s, ctr);
|
||||
rc = do_reset(s, ctr, supported_counters);
|
||||
else if (cmd == PRINT)
|
||||
rc = do_print(s, ctr);
|
||||
rc = do_print(s, ctr, supported_counters);
|
||||
else {
|
||||
eprint("Received unknown command %d, ignoring\n",
|
||||
(int) cmd);
|
||||
|
||||
@@ -52,8 +52,8 @@ static struct pmf_data {
|
||||
|
||||
struct percpucounter {
|
||||
int ctr_fds[ALL_COUNTER];
|
||||
int pai_user[NUM_PAI_USER];
|
||||
int pai_kernel[NUM_PAI_KERNEL];
|
||||
int pai_user[MAX_NUM_PAI];
|
||||
int pai_kernel[MAX_NUM_PAI];
|
||||
unsigned int cpunum;
|
||||
struct percpucounter *next;
|
||||
};
|
||||
@@ -84,10 +84,10 @@ static struct percpucounter *allocpercpucounter(unsigned int cpunum)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; ++i)
|
||||
ppc->ctr_fds[i] = -1;
|
||||
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
ppc->pai_user[i] = -1;
|
||||
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||
ppc->pai_kernel[i] = -1;
|
||||
}
|
||||
ppc->cpunum = cpunum;
|
||||
ppc->next = NULL;
|
||||
}
|
||||
@@ -100,10 +100,10 @@ static void freepercpucounter(struct percpucounter *pcpu)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; ++i)
|
||||
(void)close(pcpu->ctr_fds[i]);
|
||||
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
(void)close(pcpu->pai_user[i]);
|
||||
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||
(void)close(pcpu->pai_kernel[i]);
|
||||
}
|
||||
free(pcpu);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ static int perf_event_encode(int *pmutype, int *eventid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int activatecpu(unsigned int cpu)
|
||||
static int activatecpu(unsigned int cpu, unsigned int *supported_counters)
|
||||
{
|
||||
struct perf_event_attr pfm_event;
|
||||
struct percpucounter *ppc;
|
||||
@@ -273,7 +273,10 @@ static int activatecpu(unsigned int cpu)
|
||||
(ctr_state[PAI_USER] == UNSUPPORTED) ==
|
||||
(ctr_state[PAI_KERNEL] == UNSUPPORTED) */
|
||||
if (ctr_state[PAI_USER] != UNSUPPORTED) {
|
||||
for (i = 1; i <= NUM_PAI_USER; ++i) {
|
||||
for (i = 1; i <= MAX_NUM_PAI; ++i) {
|
||||
if (is_user_space(i - 1) != KERNEL_AND_USER_COUNTER ||
|
||||
supported_counters[i - 1] != 1)
|
||||
continue;
|
||||
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||
pfm_event.size = sizeof(pfm_event);
|
||||
pfm_event.type = paipmutype;
|
||||
@@ -307,7 +310,14 @@ static int activatecpu(unsigned int cpu)
|
||||
ppc->pai_kernel[i - 1] = fd;
|
||||
}
|
||||
}
|
||||
for (; i <= NUM_PAI_KERNEL; ++i) {
|
||||
/*
|
||||
* i can start at the index of the first PAI counter
|
||||
* for which kernel space is needed
|
||||
*/
|
||||
for (i = FIRST_KERNEL_ONLY_COUNTER; i <= MAX_NUM_PAI; ++i) {
|
||||
if (is_user_space(i - 1) == SUPPRESS_COUNTER ||
|
||||
supported_counters[i - 1] != 1)
|
||||
continue;
|
||||
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||
pfm_event.size = sizeof(pfm_event);
|
||||
pfm_event.type = paipmutype;
|
||||
@@ -348,10 +358,10 @@ static void deactivatecpu(unsigned int cpunum)
|
||||
if (pcpu != NULL) {
|
||||
for (i = 0; i < ALL_COUNTER; ++i)
|
||||
(void)close(pcpu->ctr_fds[i]);
|
||||
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
(void)close(pcpu->pai_user[i]);
|
||||
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||
(void)close(pcpu->pai_kernel[i]);
|
||||
}
|
||||
free(pcpu);
|
||||
if (enabledcounter)
|
||||
hotplugdetected = 1;
|
||||
@@ -359,7 +369,7 @@ static void deactivatecpu(unsigned int cpunum)
|
||||
pthread_mutex_unlock(&rootmux);
|
||||
}
|
||||
|
||||
static int addallcpus(void)
|
||||
static int addallcpus(unsigned int *supported_counters)
|
||||
{
|
||||
unsigned int start, end;
|
||||
int scanned, rc = 0;
|
||||
@@ -379,7 +389,7 @@ static int addallcpus(void)
|
||||
if (scanned == 1)
|
||||
end = start;
|
||||
for (; start <= end; ++start) {
|
||||
if (activatecpu(start)) {
|
||||
if (activatecpu(start, supported_counters)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
@@ -409,7 +419,7 @@ static int perf_load_counter_data(void)
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *hotplughandler(void *UNUSED(unused))
|
||||
static void *hotplughandler(void *supported_counters)
|
||||
{
|
||||
struct udev *hotplug;
|
||||
struct udev_monitor *monitor;
|
||||
@@ -452,7 +462,7 @@ static void *hotplughandler(void *UNUSED(unused))
|
||||
path = udev_device_get_devpath(dev);
|
||||
if (sscanf(path, "/devices/system/cpu/cpu%u", &cpunum) != 1)
|
||||
continue;
|
||||
if (on && activatecpu(cpunum))
|
||||
if (on && activatecpu(cpunum, (unsigned int *) supported_counters))
|
||||
eprint("Failed to attach to hotplugged CPU %u\n", cpunum);
|
||||
if (off)
|
||||
deactivatecpu(cpunum);
|
||||
@@ -462,11 +472,18 @@ static void *hotplughandler(void *UNUSED(unused))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int perf_init(void)
|
||||
int perf_init(unsigned int *supported_counters)
|
||||
{
|
||||
int ecc_supported, i, num;
|
||||
static const char *cpum_cf[] = {
|
||||
"DEA_FUNCTIONS",
|
||||
"AES_FUNCTIONS",
|
||||
"SHA_FUNCTIONS",
|
||||
"PRNG_FUNCTIONS",
|
||||
"ECC_FUNCTION_COUNT"
|
||||
};
|
||||
unsigned long maxfd;
|
||||
struct rlimit rlim;
|
||||
int i, num;
|
||||
FILE *f;
|
||||
|
||||
/* initialize performance monitoring library */
|
||||
@@ -478,16 +495,18 @@ int perf_init(void)
|
||||
/* We currently support all cpumf counters plus two virtual
|
||||
* counters for PAI. */
|
||||
num = ALL_COUNTER + 2;
|
||||
/* Check if ECC is supported on current hardware */
|
||||
ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT");
|
||||
|
||||
if (!cpumf_authorized()) {
|
||||
for (i = 0; i < ALL_COUNTER; ++i)
|
||||
ctr_state[i] = UNSUPPORTED;
|
||||
num -= ALL_COUNTER;
|
||||
} else if (!ecc_supported) {
|
||||
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
|
||||
--num;
|
||||
} else {
|
||||
for (i = 0; i < ALL_COUNTER; i++) {
|
||||
if (!perf_counter_supported("cpum_cf", cpum_cf[i])) {
|
||||
ctr_state[i] = UNSUPPORTED;
|
||||
num--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!perf_counter_supported("pai_crypto", "CRYPTO_ALL")) {
|
||||
@@ -523,11 +542,12 @@ int perf_init(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pthread_create(&hotplugthread, NULL, hotplughandler, NULL)) {
|
||||
if (pthread_create(&hotplugthread, NULL, hotplughandler,
|
||||
supported_counters)) {
|
||||
eprint("Failed to start hotplug handler thread\n");
|
||||
return -1;
|
||||
}
|
||||
return addallcpus();
|
||||
return addallcpus(supported_counters);
|
||||
}
|
||||
|
||||
|
||||
@@ -552,11 +572,14 @@ void perf_close(void)
|
||||
}
|
||||
|
||||
|
||||
static int enable_array(int *arr, int size)
|
||||
static int enable_array(int *arr, int user, unsigned int *supported_counters)
|
||||
{
|
||||
int i, ec, rc = 0;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
||||
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
||||
continue;
|
||||
ec = ioctl(arr[i], PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (ec < 0) {
|
||||
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
||||
@@ -568,14 +591,14 @@ static int enable_array(int *arr, int size)
|
||||
}
|
||||
|
||||
|
||||
int perf_enable_ctr(enum ctr_e ctr)
|
||||
int perf_enable_ctr(enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
struct percpucounter *pcpu;
|
||||
int ec, rc = 0;
|
||||
|
||||
if (ctr == ALL_COUNTER) {
|
||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||
rc = perf_enable_ctr(ctr);
|
||||
rc = perf_enable_ctr(ctr, supported_counters);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
@@ -593,7 +616,7 @@ int perf_enable_ctr(enum ctr_e ctr)
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_USER) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = enable_array(pcpu->pai_user, NUM_PAI_USER);
|
||||
ec = enable_array(pcpu->pai_user, 1, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
@@ -602,7 +625,7 @@ int perf_enable_ctr(enum ctr_e ctr)
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_KERNEL) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = enable_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||
ec = enable_array(pcpu->pai_kernel, 0, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
@@ -617,11 +640,14 @@ int perf_enable_ctr(enum ctr_e ctr)
|
||||
}
|
||||
|
||||
|
||||
static int disable_array(int *arr, int size)
|
||||
static int disable_array(int *arr, int user, unsigned int *supported_counters)
|
||||
{
|
||||
int i, ec, rc = 0;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
||||
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
||||
continue;
|
||||
ec = ioctl(arr[i], PERF_EVENT_IOC_DISABLE, 0);
|
||||
if (ec < 0) {
|
||||
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
||||
@@ -633,14 +659,14 @@ static int disable_array(int *arr, int size)
|
||||
}
|
||||
|
||||
|
||||
int perf_disable_ctr(enum ctr_e ctr)
|
||||
int perf_disable_ctr(enum ctr_e ctr, unsigned int *supported_counters)
|
||||
{
|
||||
struct percpucounter *pcpu;
|
||||
int ec, rc = 0;
|
||||
|
||||
if (ctr == ALL_COUNTER) {
|
||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||
rc = perf_disable_ctr(ctr);
|
||||
rc = perf_disable_ctr(ctr, supported_counters);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
@@ -660,7 +686,7 @@ int perf_disable_ctr(enum ctr_e ctr)
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_USER) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = disable_array(pcpu->pai_user, NUM_PAI_USER);
|
||||
ec = disable_array(pcpu->pai_user, 1, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
@@ -671,7 +697,7 @@ int perf_disable_ctr(enum ctr_e ctr)
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_KERNEL) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = disable_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||
ec = disable_array(pcpu->pai_kernel, 0, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
@@ -688,11 +714,14 @@ int perf_disable_ctr(enum ctr_e ctr)
|
||||
}
|
||||
|
||||
|
||||
static int reset_array(int *arr, int size)
|
||||
static int reset_array(int *arr, int user, unsigned int *supported_counters)
|
||||
{
|
||||
int ec, rc = 0, i;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
||||
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
||||
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
||||
continue;
|
||||
ec = ioctl(arr[i], PERF_EVENT_IOC_RESET, 0);
|
||||
if (ec < 0) {
|
||||
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
||||
@@ -704,14 +733,15 @@ static int reset_array(int *arr, int size)
|
||||
}
|
||||
|
||||
|
||||
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
||||
*supported_counters)
|
||||
{
|
||||
struct percpucounter *pcpu;
|
||||
int ec, rc = 0;
|
||||
|
||||
if (ctr == ALL_COUNTER) {
|
||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||
rc = perf_reset_ctr(ctr, value);
|
||||
rc = perf_reset_ctr(ctr, value, supported_counters);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
@@ -727,14 +757,14 @@ int perf_reset_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_USER) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = reset_array(pcpu->pai_user, NUM_PAI_USER);
|
||||
ec = reset_array(pcpu->pai_user, 1, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
endforeachcpu();
|
||||
} else if (ctr == PAI_KERNEL) {
|
||||
foreachcpu(pcpu) {
|
||||
ec = reset_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||
ec = reset_array(pcpu->pai_kernel, 0, supported_counters);
|
||||
if (ec < 0)
|
||||
rc = -1;
|
||||
}
|
||||
@@ -743,12 +773,13 @@ int perf_reset_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
rc = -1;
|
||||
}
|
||||
if (rc == 0)
|
||||
rc = perf_read_ctr(ctr, value);
|
||||
rc = perf_read_ctr(ctr, value, supported_counters);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
||||
*supported_counters)
|
||||
{
|
||||
struct percpucounter *pcpu;
|
||||
int ec, rc = 0;
|
||||
@@ -761,11 +792,26 @@ int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
return 0;
|
||||
}
|
||||
if (ctr == PAI_USER) {
|
||||
*value = NUM_PAI_USER;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; i < MAX_NUM_PAI; i++) {
|
||||
if (is_user_space(i) == KERNEL_AND_USER_COUNTER &&
|
||||
supported_counters[i] == 1)
|
||||
c++;
|
||||
}
|
||||
|
||||
*value = c;
|
||||
return 0;
|
||||
}
|
||||
if (ctr == PAI_KERNEL) {
|
||||
*value = NUM_PAI_KERNEL;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; i < MAX_NUM_PAI; i++) {
|
||||
if (supported_counters[i] == 1)
|
||||
c++;
|
||||
}
|
||||
|
||||
*value = c;
|
||||
return 0;
|
||||
}
|
||||
if (ctr >= ALL_COUNTER)
|
||||
@@ -802,14 +848,15 @@ int perf_ctr_state(enum ctr_e ctr) {
|
||||
int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value)
|
||||
{
|
||||
struct percpucounter *pcpu;
|
||||
unsigned int maxctr;
|
||||
int *arr, ec, rc = 0;
|
||||
uint64_t val;
|
||||
|
||||
*value = 0;
|
||||
maxctr = user ? NUM_PAI_USER : NUM_PAI_KERNEL;
|
||||
if (ctrnum >= maxctr)
|
||||
|
||||
if (is_user_space(ctrnum) == SUPPRESS_COUNTER ||
|
||||
(user && is_user_space(ctrnum) == KERNEL_ONLY_COUNTER))
|
||||
return -1;
|
||||
|
||||
foreachcpu(pcpu) {
|
||||
arr = user ? pcpu->pai_user : pcpu->pai_kernel;
|
||||
ec = read(arr[ctrnum], &val, sizeof(val));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include ../common.mak
|
||||
|
||||
BIN_FILES = lscpumf chcpumf lshwc pai
|
||||
MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8
|
||||
BIN_FILES = lscpumf chcpumf lshwc pai lspai
|
||||
MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 lspai.8
|
||||
|
||||
all: $(BIN_FILES)
|
||||
|
||||
@@ -11,6 +11,7 @@ lscpumf: lscpumf.o $(libs)
|
||||
chcpumf: chcpumf.o $(libs)
|
||||
lshwc: lshwc.o $(libs)
|
||||
pai: pai.o $(libs)
|
||||
lspai: lspai.o $(libs)
|
||||
|
||||
install: all install-man
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
@@ -3473,6 +3473,7 @@ static struct counters *get_counter(int ctrset, size_t *len)
|
||||
read_ccerror(cp, *len);
|
||||
break;
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||
cp = cpumcf_z16_counters;
|
||||
*len = ARRAY_SIZE(cpumcf_z16_counters);
|
||||
}
|
||||
|
||||
352
cpumf/lspai.c
Normal file
352
cpumf/lspai.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/* Copyright IBM Corp. 2023
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
/* List available Processor Assist Instrumentation (PAI) counters. */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "numeric", no_argument, NULL, 'n' },
|
||||
.desc = "Sort PAI counters by counter number"
|
||||
},
|
||||
{
|
||||
.option = { "type", required_argument, NULL, 't' },
|
||||
.argument = "TYPE",
|
||||
.desc = "Type of PAI counters to show: crypto, nnpa"
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "List Processor Assist Information counter sets",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2023,
|
||||
.pub_last = 2023,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
static bool numsort; /* If true sort counter numerically */
|
||||
|
||||
#define PAI_PATH "/bus/event_source/devices/%s"
|
||||
|
||||
enum pai_types { /* Bit mask for supported PAI counters */
|
||||
pai_type_crypto = 0, /* PAI Crypto Counters */
|
||||
pai_type_nnpa = 1, /* PAI NNPA Counters */
|
||||
pai_type_max = 2, /* PAI maximum value, must be last */
|
||||
};
|
||||
|
||||
static int pai_types_show;
|
||||
|
||||
struct pai_ctrname { /* List of defined counters */
|
||||
char *name; /* Counter name */
|
||||
unsigned long nr; /* Counter number */
|
||||
};
|
||||
|
||||
struct pai_node { /* Head for PAI counter sets */
|
||||
struct util_list_node node; /* Successor in PAI counter set list */
|
||||
enum pai_types type; /* PAI type */
|
||||
int pmu; /* Assigned PMU type number */
|
||||
const char *name; /* Counter set name */
|
||||
char *name_uc; /* Counter set name upper case */
|
||||
const char *sysfs_name; /* Counter set name in /sysfs tree */
|
||||
const char *filter_name; /* Counter set name for scandir filter */
|
||||
struct pai_ctrname *ctrlist; /* List of counter names & numbers */
|
||||
size_t ctrsize; /* Total size in bytes of ctrlist */
|
||||
int ctridx; /* Index of last entry used in ctrlist */
|
||||
unsigned long base; /* Base number for counter set */
|
||||
};
|
||||
|
||||
static struct util_list pai_list;
|
||||
|
||||
/* Return base of counter set, this is the first counter of this set. */
|
||||
static unsigned long pai_type_base(enum pai_types t)
|
||||
{
|
||||
switch (t) {
|
||||
case pai_type_crypto:
|
||||
return 0x1000;
|
||||
case pai_type_nnpa:
|
||||
return 0x1800;
|
||||
case pai_type_max:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test PAI counter name from command line option. */
|
||||
static const char *pai_type_name(enum pai_types t)
|
||||
{
|
||||
switch (t) {
|
||||
case pai_type_crypto:
|
||||
return "crypto";
|
||||
case pai_type_nnpa:
|
||||
return "nnpa";
|
||||
case pai_type_max:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/* Convert PAI counter type to sysfs directory name. Only validated
|
||||
* input at this time.
|
||||
*/
|
||||
static const char *pai_type_sysfs(enum pai_types t)
|
||||
{
|
||||
if (t == pai_type_crypto)
|
||||
return "pai_crypto";
|
||||
return "pai_ext";
|
||||
}
|
||||
|
||||
/* Convert PAI counter type to sysfs directory name filter for scandir(). */
|
||||
static const char *pai_type_filter(enum pai_types t)
|
||||
{
|
||||
if (t == pai_type_nnpa)
|
||||
return "^NNPA";
|
||||
return "[^.]"; /* Matches anything but . and .. in sysfs */
|
||||
}
|
||||
|
||||
/* Sort PAI counter names by assigned counter number. */
|
||||
static int pai_ctrcmp(const void *p1, const void *p2)
|
||||
{
|
||||
struct pai_ctrname *l = (struct pai_ctrname *)p1;
|
||||
struct pai_ctrname *r = (struct pai_ctrname *)p2;
|
||||
|
||||
return l->nr > r->nr ? 1 : -1;
|
||||
}
|
||||
|
||||
/* Convert string to upper case. */
|
||||
static char *str2uc(const char *s)
|
||||
{
|
||||
char *uc = util_strdup(s), *old_uc = uc;
|
||||
|
||||
for (; *uc; ++uc)
|
||||
*uc = toupper(*uc);
|
||||
return old_uc;
|
||||
}
|
||||
|
||||
/* Read counter names and assigned event number from sysfs file tree.
|
||||
* Exit when sysfs directory can not be scanned.
|
||||
*/
|
||||
static void read_counternames(struct pai_node *node)
|
||||
{
|
||||
int i, more = 0, ctr = 0, count = 0;
|
||||
struct dirent **namelist = NULL;
|
||||
char *path, *ctrpath;
|
||||
|
||||
/* Read counter names and assigned event number. */
|
||||
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
|
||||
count = util_scandir(&namelist, alphasort, path, node->filter_name);
|
||||
if (count <= 0)
|
||||
errx(EXIT_FAILURE, "Cannot open %s", path);
|
||||
|
||||
node->ctrsize = count * sizeof(*node->ctrlist);
|
||||
node->ctrlist = util_malloc(node->ctrsize);
|
||||
for (i = 0; i < count && ctr >= 0; i++) {
|
||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
|
||||
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
|
||||
node->ctrlist[node->ctridx++].nr = ctr;
|
||||
more++;
|
||||
} else {
|
||||
warnx("Cannot parse %s", ctrpath);
|
||||
}
|
||||
free(ctrpath);
|
||||
}
|
||||
util_scandir_free(namelist, count);
|
||||
free(path);
|
||||
|
||||
if (numsort && more > 1)
|
||||
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
|
||||
}
|
||||
|
||||
static void show_painode(void)
|
||||
{
|
||||
struct pai_node *node;
|
||||
int indent = 0;
|
||||
int offset = 0;
|
||||
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i)
|
||||
indent = MAX((size_t)indent, strlen(node->ctrlist[i].name));
|
||||
}
|
||||
|
||||
printf("RAW %*s NAME %*s DESCRIPTION\n", 3, "", indent - 5, "");
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
printf("%d:%ld %s", node->pmu,
|
||||
node->ctrlist[i].nr, node->ctrlist[i].name);
|
||||
|
||||
offset = indent - strlen(node->ctrlist[i].name) + 1;
|
||||
printf("%*s", offset, "");
|
||||
|
||||
printf("Counter %ld / PAI %s counter set\n",
|
||||
node->ctrlist[i].nr - node->base, node->name_uc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Release all memory allocated at make_painode(). */
|
||||
static void free_painode(void)
|
||||
{
|
||||
struct pai_node *next, *node;
|
||||
|
||||
util_list_iterate_safe(&pai_list, node, next) {
|
||||
free(node->name_uc);
|
||||
for (int i = 0; i < node->ctridx; ++i)
|
||||
free(node->ctrlist[i].name);
|
||||
free(node->ctrlist);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
static void make_painode(enum pai_types t)
|
||||
{
|
||||
struct pai_node *node = util_zalloc(sizeof(*node));
|
||||
char *path;
|
||||
|
||||
node->type = t;
|
||||
node->sysfs_name = pai_type_sysfs(t);
|
||||
node->name = pai_type_name(t);
|
||||
node->name_uc = str2uc(node->name);
|
||||
node->filter_name = pai_type_filter(t);
|
||||
node->base = pai_type_base(t);
|
||||
|
||||
/* Read PMU type number. */
|
||||
path = util_path_sysfs(PAI_PATH, node->sysfs_name);
|
||||
node->pmu = libcpumf_pmutype(path);
|
||||
if (node->pmu < 0)
|
||||
errx(EXIT_FAILURE, "Cannot open %s", path);
|
||||
free(path);
|
||||
|
||||
read_counternames(node);
|
||||
|
||||
util_list_add_tail(&pai_list, node);
|
||||
}
|
||||
|
||||
static int painode_cmp(void *a, void *b, void *UNUSED(data))
|
||||
{
|
||||
struct pai_node *n1 = (struct pai_node *)a;
|
||||
struct pai_node *n2 = (struct pai_node *)b;
|
||||
|
||||
return n1->pmu < n2->pmu ? -1 : 1;
|
||||
}
|
||||
|
||||
static void sort_painode(void)
|
||||
{
|
||||
util_list_sort(&pai_list, painode_cmp, NULL);
|
||||
}
|
||||
|
||||
/* Check for hardware support and return false if not available. */
|
||||
static bool have_support(enum pai_types t)
|
||||
{
|
||||
const char *sysfn = pai_type_sysfs(t);
|
||||
char *path = util_path_sysfs(PAI_PATH, sysfn);
|
||||
bool rc = true;
|
||||
|
||||
if (!util_path_is_dir(path)) {
|
||||
warnx("No support for PAI %s facility", pai_type_name(t));
|
||||
rc = false;
|
||||
}
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the argument for option -t. It must be a valid PAI counter set.
|
||||
* Exit when an invalid PAI counter set name has been specified.
|
||||
*/
|
||||
static void check_type_name(const char *type)
|
||||
{
|
||||
bool no_match = true;
|
||||
enum pai_types i;
|
||||
const char *fn;
|
||||
|
||||
for (i = pai_type_crypto; i < pai_type_max; ++i) {
|
||||
fn = pai_type_name(i);
|
||||
if (!strcasecmp(fn, type)) {
|
||||
pai_types_show |= (1 << i);
|
||||
no_match = false;
|
||||
}
|
||||
}
|
||||
if (no_match)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
|
||||
util_list_init(&pai_list, struct pai_node, node);
|
||||
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 'n':
|
||||
numsort = true;
|
||||
break;
|
||||
case 't':
|
||||
check_type_name(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Nothing specified, show all PAI counters */
|
||||
if (!pai_types_show)
|
||||
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
|
||||
|
||||
/* Check for hardware support */
|
||||
for (enum pai_types i = pai_type_crypto; i < pai_type_max; ++i) {
|
||||
if ((pai_types_show & (1 << i))) {
|
||||
if (!have_support(i))
|
||||
pai_types_show &= ~(1 << i);
|
||||
else
|
||||
make_painode(i);
|
||||
}
|
||||
}
|
||||
sort_painode();
|
||||
show_painode();
|
||||
free_painode();
|
||||
return ch;
|
||||
}
|
||||
80
cpumf/man/lspai.8
Normal file
80
cpumf/man/lspai.8
Normal file
@@ -0,0 +1,80 @@
|
||||
.\" lspai.8
|
||||
.\"
|
||||
.\"
|
||||
.\" 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 \fBlspai\fP
|
||||
.
|
||||
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.SH NAME
|
||||
\*c \- list Processor Activity Instrumentation (PAI) counters
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
.RB [ \-n ]
|
||||
.RB [ \-t
|
||||
.IR "\ TYPE" ]
|
||||
.br
|
||||
\*c
|
||||
.BR \-h | \-\-help
|
||||
.br
|
||||
\*c
|
||||
.BR \-v | \-\-version
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
\*c displays the Processor Activity Instrumentation (PAI) counters
|
||||
for Linux on IBM Z.
|
||||
The output is a human-readable list of available PAI counter
|
||||
names and numbers.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Displays help information, then exits.
|
||||
.
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
Displays version information, then exits.
|
||||
.
|
||||
.TP
|
||||
.BR \-t ", " \-\-type "\ TYPE"
|
||||
Specifies the PAI counter set to list.
|
||||
Valid counter set values are
|
||||
.I crypto
|
||||
and
|
||||
.IR nnpa .
|
||||
By default, the command lists all available PAI counter sets.
|
||||
NNPA refers to the Neural Network Processing Assist facility counter set.
|
||||
Crypto refers to the Cryptografic Processing Assist facility counter set.
|
||||
.
|
||||
.TP
|
||||
.BR \-n ", " \-\-numeric
|
||||
Shows the PAI counter sets sorted by counter number.
|
||||
Default sort order is PAI counter name.
|
||||
.
|
||||
.SH "EXAMPLE"
|
||||
The \*c invocation lists all PAI Neural Network Processing Assist Facility
|
||||
(NNPA) counters in numeric order:
|
||||
.nf
|
||||
# lspai -t nnpa -n
|
||||
RAW NAME DESCRIPTION
|
||||
13:6144 NNPA_ALL Counter 0 / PAI NNPA counter set
|
||||
13:6145 NNPA_ADD Counter 1 / PAI NNPA counter set
|
||||
13:6146 NNPA_SUB Counter 2 / PAI NNPA counter set
|
||||
13:6147 NNPA_MUL Counter 3 / PAI NNPA counter set
|
||||
\&...
|
||||
.fi
|
||||
The first column shows the raw event number suitable for
|
||||
.IR perf "(8)"
|
||||
raw event specification.
|
||||
The second column shows the PAI NNPA counter name,
|
||||
suitable for
|
||||
.IR perf "(8)"
|
||||
event specification by name.
|
||||
The third gives a short explanation, if available.
|
||||
.SH "SEE ALSO"
|
||||
.BR pai (8)
|
||||
.BR lscpumf (8)
|
||||
@@ -18,6 +18,8 @@
|
||||
.IR size ]
|
||||
.RB [ \-i | \-\-interval
|
||||
.IR ms ]
|
||||
.RB [ \-R | \-\-realtime
|
||||
.IR prio ]
|
||||
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||
.br
|
||||
\*c
|
||||
@@ -25,6 +27,8 @@
|
||||
.IR size ]
|
||||
.RB [ \-i | \-\-interval
|
||||
.IR ms ]
|
||||
.RB [ \-R | \-\-realtime
|
||||
.IR prio ]
|
||||
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||
.br
|
||||
\*c
|
||||
@@ -191,6 +195,14 @@ The ring buffer is created with the
|
||||
.IR mmap (2)
|
||||
system call.
|
||||
.
|
||||
.TP
|
||||
.BR \-R ", " \-\-realtime "\ prio"
|
||||
Collect data using the RT SCHED_FIFO priority specified by
|
||||
.BR prio .
|
||||
Valid values are integers in the range 1 (low) to 99 (high).
|
||||
Use this option when gathering data from multiple CPUs
|
||||
to prevent data loss.
|
||||
.
|
||||
.SH ARGUMENT
|
||||
The command line options determine how command line
|
||||
arguments are interpreted.
|
||||
|
||||
39
cpumf/pai.c
39
cpumf/pai.c
@@ -320,7 +320,7 @@ static void readmap(int fd)
|
||||
* ring buffer per event, sleep some short time and always read all
|
||||
* ring buffer for new contents.
|
||||
*/
|
||||
static void collect(unsigned long cnt)
|
||||
static int collect(unsigned long cnt)
|
||||
{
|
||||
fd_set r_fds, e_fds, a_fds;
|
||||
struct pai_event *p;
|
||||
@@ -328,6 +328,7 @@ static void collect(unsigned long cnt)
|
||||
int rc, max_fd;
|
||||
|
||||
do {
|
||||
rc = -1;
|
||||
max_fd = -1;
|
||||
tv.tv_sec = read_interval / 1000;
|
||||
tv.tv_usec = (1000 * read_interval) % 1000000;
|
||||
@@ -357,6 +358,7 @@ static void collect(unsigned long cnt)
|
||||
}
|
||||
}
|
||||
} while (rc != -1 && --cnt > 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void lookup_event(__u64 evtnum, __u16 ctr, __u64 value)
|
||||
@@ -449,6 +451,11 @@ static void evt_show(__u64 evtnum, const char *evtsel, struct pai_event_out *ev)
|
||||
ev->u.s_comm.tid);
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SWITCH:
|
||||
printf("cs-%s",
|
||||
(ev->misc & PERF_RECORD_MISC_SWITCH_OUT) ? "out" : "in");
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SWITCH_CPU_WIDE:
|
||||
if (ev->misc & PERF_RECORD_MISC_SWITCH_OUT) {
|
||||
short p = PERF_RECORD_MISC_SWITCH_OUT_PREEMPT;
|
||||
@@ -549,6 +556,9 @@ static int evt_scan(char *fn, unsigned char *buf, size_t len,
|
||||
offset -= sizeof(__u64);
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SWITCH:
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SWITCH_CPU_WIDE:
|
||||
memcpy(&ev.u, buf + offset, sizeof(ev.u.s_cs));
|
||||
offset += sizeof(ev.u.s_cs);
|
||||
@@ -583,7 +593,7 @@ static int evt_scan(char *fn, unsigned char *buf, size_t len,
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown header-type %d ", hdr->type);
|
||||
printf("unknown header-type %d\n", hdr->type);
|
||||
offset += hdr->size - sizeof(*hdr);
|
||||
goto bypass;
|
||||
}
|
||||
@@ -944,6 +954,11 @@ static struct util_opt opt_vec[] = {
|
||||
.option = { "report", no_argument, NULL, 'r' },
|
||||
.desc = "Report file contents"
|
||||
},
|
||||
{
|
||||
.option = { "realtime", required_argument, NULL, 'R' },
|
||||
.argument = "PRIO",
|
||||
.desc = "Collect data with this RT SCHED_FIFO priority"
|
||||
},
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "NUMBER",
|
||||
@@ -1007,6 +1022,19 @@ static unsigned long check_mapsize(unsigned long n)
|
||||
return cnt == 1 ? n : 0;
|
||||
}
|
||||
|
||||
static void setprio(const char *prio)
|
||||
{
|
||||
struct sched_param param;
|
||||
char *endstr;
|
||||
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = strtoul(prio, &endstr, 0);
|
||||
if (*endstr)
|
||||
errno = EINVAL;
|
||||
if (*endstr || sched_setscheduler(0, SCHED_FIFO, ¶m))
|
||||
err(EXIT_FAILURE, "Could not set realtime priority");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool crypto_record = false, report = false;
|
||||
@@ -1061,6 +1089,9 @@ int main(int argc, char **argv)
|
||||
record_cpus_nnpa(optarg);
|
||||
nnpa_record = true;
|
||||
break;
|
||||
case 'R':
|
||||
setprio(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
report = true;
|
||||
break;
|
||||
@@ -1094,12 +1125,12 @@ int main(int argc, char **argv)
|
||||
ev_install(group);
|
||||
ev_enable();
|
||||
|
||||
collect(loop_count);
|
||||
ch = collect(loop_count);
|
||||
|
||||
ev_disable();
|
||||
ev_deinstall();
|
||||
ev_dealloc();
|
||||
return EXIT_SUCCESS;
|
||||
return ch < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Must be reporting */
|
||||
|
||||
@@ -25,7 +25,7 @@ int get_numcpus()
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
/* check whether file exists and is readable */
|
||||
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
|
||||
sprintf(path, "/sys/devices/system/cpu/cpu%d", i);
|
||||
if (access(path, R_OK) == 0)
|
||||
number++;
|
||||
else
|
||||
@@ -45,11 +45,13 @@ int get_num_online_cpus()
|
||||
int status = 0;
|
||||
int value_of_onlinefile, rc;
|
||||
|
||||
for (i = 0; i <= get_numcpus(); i++) {
|
||||
for (i = 0; i < get_numcpus(); i++) {
|
||||
/* check wether file exists and is readable */
|
||||
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
|
||||
if (access(path, R_OK) != 0)
|
||||
if (access(path, R_OK) != 0) {
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
filp = fopen(path, "r");
|
||||
if (!filp)
|
||||
cpuplugd_exit("Cannot open cpu online file: "
|
||||
@@ -101,10 +103,8 @@ int hotplug(int cpuid)
|
||||
cpuid);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
cpuplugd_error("hotplugging cpu with id %d failed\n", cpuid);
|
||||
return -1;
|
||||
}
|
||||
cpuplugd_debug("cpu with id %d cannot be hotplugged\n", cpuid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -135,9 +135,8 @@ int hotunplug(int cpuid)
|
||||
fclose(filp);
|
||||
if (state == 0)
|
||||
return 1;
|
||||
} else {
|
||||
cpuplugd_error("unplugging cpu with id %d failed\n", cpuid);
|
||||
}
|
||||
cpuplugd_debug("cpu with id %d cannot be hotunplugged\n", cpuid);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -163,6 +162,8 @@ int is_online(int cpuid)
|
||||
retval = 0;
|
||||
}
|
||||
fclose(filp);
|
||||
} else {
|
||||
retval = 1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -7,45 +7,45 @@
|
||||
dasdfmt \- formatting of DASD (ECKD) disk drives.
|
||||
|
||||
.SH SYNOPSIS
|
||||
\fBdasdfmt\fR [-h] [-t] [-v] [-y] [-p] [-P] [-m \fIstep\fR]
|
||||
\fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-P] [\-m \fIstep\fR]
|
||||
.br
|
||||
[-r \fIcylinder\fR] [-b \fIblksize\fR] [-l \fIvolser\fR] [-d \fIlayout\fR]
|
||||
[\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR]
|
||||
.br
|
||||
[-L] [-V] [-F] [-k] [-C] [-M \fImode\fR] \fIdevice\fR
|
||||
[\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] \fIdevice\fR
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it
|
||||
for usage with Linux for S/390.
|
||||
for usage with Linux for S/390.
|
||||
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
|
||||
Any device node created by udev for kernel 2.6 can be used
|
||||
Any device node created by udev for kernel 2.6 can be used
|
||||
(e.g. '/dev/dasd/0.0.b100/disc').
|
||||
.br
|
||||
|
||||
\fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in
|
||||
\fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in
|
||||
\fBLOSS OF DATA\fR.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-h\fR or \fB--help\fR
|
||||
\fB\-h\fR or \fB\-\-help\fR
|
||||
Print usage and exit.
|
||||
|
||||
.TP
|
||||
\fB-t\fR or \fB--test\fR
|
||||
Disables any modification of the disk drive.
|
||||
\fB\-t\fR or \fB\-\-test\fR
|
||||
Disables any modification of the disk drive.
|
||||
.br
|
||||
\fBdasdfmt\fR just prints
|
||||
out, what it \fBwould\fR do.
|
||||
|
||||
.TP
|
||||
\fB-v\fR
|
||||
\fB\-v\fR
|
||||
Increases verbosity.
|
||||
|
||||
.TP
|
||||
\fB-y\fR
|
||||
\fB\-y\fR
|
||||
Start formatting without further user-confirmation.
|
||||
|
||||
.TP
|
||||
\fB--norecordzero\fR
|
||||
\fB\-\-norecordzero\fR
|
||||
Remove permission for subsystem to format write record zero.
|
||||
.br
|
||||
This is an expert option: Per default in recent dasd drivers, subsystems are
|
||||
@@ -54,80 +54,80 @@ to remove this permission.
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-L\fR or \fB--no_label\fR
|
||||
\fB\-L\fR or \fB\-\-no_label\fR
|
||||
Omit the writing of a disk label after formatting.
|
||||
.br
|
||||
This makes only sense for the 'ldl' disk layout.
|
||||
.br
|
||||
The '-L' option has to be specified after the '-d ldl' option.
|
||||
The '\-L' option has to be specified after the '\-d ldl' option.
|
||||
.br
|
||||
|
||||
e.g. dasdfmt -d ldl -L /dev/...
|
||||
e.g. dasdfmt \-d ldl \-L /dev/...
|
||||
|
||||
|
||||
.TP
|
||||
\fB-V\fR or \fB--version\fR
|
||||
\fB\-V\fR or \fB\-\-version\fR
|
||||
Print version number and exit.
|
||||
|
||||
.TP
|
||||
\fB-F\fR or \fB--force\fR
|
||||
\fB\-F\fR or \fB\-\-force\fR
|
||||
Formats the device without performing sanity checking.
|
||||
|
||||
.TP
|
||||
\fB-C\fR or \fB--check_host_count\fR
|
||||
\fB\-C\fR or \fB\-\-check_host_count\fR
|
||||
Force dasdfmt to check the host access open count to ensure the device
|
||||
is not online on another operating system instance
|
||||
|
||||
.TP
|
||||
\fB-d\fR \fIlayout\fR or \fB--disk_layout\fR=\fIlayout\fR
|
||||
\fB\-d\fR \fIlayout\fR or \fB\-\-disk_layout\fR=\fIlayout\fR
|
||||
Formats the device with compatible disk layout or linux disk layout.
|
||||
\fIlayout\fR is either \fIcdl\fR for the compatible disk layout
|
||||
(default) or \fIldl\fR for the linux disk layout.
|
||||
.br
|
||||
Compatible disk layout means a special handling of the
|
||||
first two tracks of the volume. This enables other S/390 or zSeries
|
||||
Compatible disk layout means a special handling of the
|
||||
first two tracks of the volume. This enables other S/390 or zSeries
|
||||
operating systems to access this device (e.g. for backup purposes).
|
||||
|
||||
.TP
|
||||
\fB-p\fR or \fB--progressbar\fR
|
||||
Print a progress bar while formatting.
|
||||
Print a progress bar while formatting.
|
||||
Do not use this option if you are using a 3270 console,
|
||||
running in background or redirecting the output to a file.
|
||||
|
||||
.TP
|
||||
\fB-P\fR or \fB--percentage\fR
|
||||
\fB\-P\fR or \fB\-\-percentage\fR
|
||||
Print one line for each formatted cylinder showing the number of the
|
||||
cylinder and percentage of formatting process.
|
||||
Intended to be used by higher level interfaces.
|
||||
|
||||
.TP
|
||||
\fB-m\fR \fIstep\fR or \fB--hashmarks\fR=\fIstep\fR
|
||||
\fB\-m\fR \fIstep\fR or \fB\-\-hashmarks\fR=\fIstep\fR
|
||||
Print a hashmark every \fIstep\fR cylinders. The value \fIstep\fR has to be within range [1,1000], otherwise it will be set to the default, which is 10.
|
||||
.br
|
||||
You can use this option to see the progress of formatting in case you
|
||||
are not able to use the progress bar option -p, e.g. with a 3270
|
||||
are not able to use the progress bar option \-p, e.g. with a 3270
|
||||
terminal.
|
||||
.br
|
||||
The value will be at least as big as the -r or --requestsize value.
|
||||
The value will be at least as big as the \-r or \-\-requestsize value.
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-M\fR \fImode\fR or \fB--mode\fR=\fImode\fR
|
||||
\fB\-M\fR \fImode\fR or \fB\-\-mode\fR=\fImode\fR
|
||||
Specify the \fImode\fR to be used to format the device. Valid modes are:
|
||||
.RS
|
||||
.IP full
|
||||
Format the entire disk with the specified blocksize. (default)
|
||||
.IP quick
|
||||
Format the first two tracks and write label and partition information. Use this
|
||||
option only if you are sure that the target DASD already contains a regular
|
||||
format with the specified blocksize. A blocksize can optionally be specified
|
||||
using \fB-b\fR (\fB--blocksize\fR).
|
||||
Format the first two tracks and write label and partition information.
|
||||
.br
|
||||
For thin-provisioned DASD ESE volumes, quick is the default mode. A full space
|
||||
release then precedes the formatting step. If this space release fails, dasdfmt
|
||||
falls back to a full-format mode. Formatting stops if the space release fails
|
||||
and quick mode was specified explicitly using \fB-M\fR. Specify the
|
||||
\fB--no-discard\fR option to omit the space release.
|
||||
Use this option for DASD ESE volumes to take the benefits of thin provisioning.
|
||||
In this case, a full space release precedes the formatting step. If this space
|
||||
release fails, then the formatting also fails. Specify the \fB\-\-no\-discard\fR
|
||||
option to omit the space release.
|
||||
.br
|
||||
For non-ESE volumes use this option only if you are sure that the target DASD
|
||||
already contains a regular format with the specified blocksize. A blocksize can
|
||||
optionally be specified using \fB\-b\fR (\fB\-\-blocksize\fR).
|
||||
|
||||
.IP expand
|
||||
Format all unformatted tracks at the end of the target DASD. This mode assumes
|
||||
@@ -135,20 +135,20 @@ that tracks at the beginning of the DASD volume have already been correctly
|
||||
formatted, while a consecutive set of tracks at the end are unformatted. You can
|
||||
use this mode to make added space available for Linux use after dynamically
|
||||
increasing the size of a DASD volume. A blocksize can optionally be specified
|
||||
using \fB-b\fR (\fB--blocksize\fR).
|
||||
using \fB\-b\fR (\fB\-\-blocksize\fR).
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fB--check\fR
|
||||
\fB\-\-check\fR
|
||||
Perform a complete format check on a DASD volume. A blocksize can be specified
|
||||
with \fB-b\fR (\fB--blocksize\fR).
|
||||
with \fB\-b\fR (\fB\-\-blocksize\fR).
|
||||
|
||||
.TP
|
||||
\fB--no-discard\fR
|
||||
\fB\-\-no\-discard\fR
|
||||
Omit a full space release when formatting a thin-provisioned DASD ESE volume.
|
||||
|
||||
.TP
|
||||
\fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR
|
||||
\fB\-r\fR \fIcylindercount\fR or \fB\-\-requestsize\fR=\fIcylindercount\fR
|
||||
Number of cylinders to be processed in one formatting step.
|
||||
The value must be an integer in the range 1 - 255.
|
||||
.br
|
||||
@@ -158,48 +158,47 @@ devices, counting the base device and all alias devices.
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-b\fR \fIblksize\fR or \fB--blocksize\fR=\fIblksize\fR
|
||||
\fB\-b\fR \fIblksize\fR or \fB\-\-blocksize\fR=\fIblksize\fR
|
||||
Specify blocksize to be used. \fIblksize\fR must be a positive integer
|
||||
and always be a power of two. The recommended blocksize is 4096 bytes.
|
||||
|
||||
.TP
|
||||
\fB-l\fR \fIvolser\fR or \fB--label\fR=\fIvolser\fR
|
||||
Specify the volume serial number or volume identifier to be written
|
||||
to disk after formatting. If no label is specified, a sensible default
|
||||
is used. \fIvolser\fR is interpreted as ASCII string and is automatically
|
||||
\fB\-l\fR \fIvolser\fR or \fB\-\-label\fR=\fIvolser\fR
|
||||
Specify the volume serial number or volume identifier to be written
|
||||
to disk after formatting. If no label is specified, a sensible default
|
||||
is used. \fIvolser\fR is interpreted as ASCII string and is automatically
|
||||
converted to uppercase and then to EBCDIC.
|
||||
.br
|
||||
|
||||
e.g. -l LNX001 or --label=DASD01
|
||||
e.g. \-l LNX001 or \-\-label=DASD01
|
||||
.br
|
||||
|
||||
The \fIvolser\fR identifies by serial number the volume. A volume serial
|
||||
The \fIvolser\fR identifies by serial number the volume. A volume serial
|
||||
number is 1 through 6 alphanumeric or one of the following special
|
||||
characters: $, #, @, %. Enclose a serial number that contains special
|
||||
characters in apostrophes. If the number is shorter than six
|
||||
characters: $, #, @, %. Enclose a serial number that contains special
|
||||
characters in apostrophes. If the number is shorter than six
|
||||
characters, it is padded with trailing blanks.
|
||||
.br
|
||||
.br
|
||||
|
||||
Do not code a volume serial number as SCRTCH, PRIVAT, or Lnnnnn (L with
|
||||
five numbers); these are used in OS/390 messages to ask the operator to
|
||||
mount a volume. Do not code a volume serial number as MIGRAT, which is
|
||||
used by the OS/390 Hierarchical Storage Manager DFSMShsm for migrated
|
||||
Do not code a volume serial number as SCRTCH, PRIVAT, or Lnnnnn (L with
|
||||
five numbers); these are used in OS/390 messages to ask the operator to
|
||||
mount a volume. Do not code a volume serial number as MIGRAT, which is
|
||||
used by the OS/390 Hierarchical Storage Manager DFSMShsm for migrated
|
||||
data sets.
|
||||
.br
|
||||
|
||||
NOTE: Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser.
|
||||
NOTE: Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser.
|
||||
.br
|
||||
In case you really have to use special characters, make sure you are using quotes. In addition there is a special handling for the '$' sign. Please specify it using '\\$' if necessary.
|
||||
.br
|
||||
|
||||
e.g. -l 'a@b\\$c#' to get A@B$C#
|
||||
e.g. \-l 'a@b\\$c#' to get A@B$C#
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-k\fR or \fB--keep_volser\fR
|
||||
Keeps the Volume Serial Number, when writing the Volume Label. This is
|
||||
useful, if the Serial Number has been written with a VM Tool and should not
|
||||
be overwritten.
|
||||
\fB\-k\fR or \fB\-\-keep_volser\fR
|
||||
Keeps the Volume Serial Number when writing the Volume Label. This is useful if
|
||||
the volume already has a Serial Number that should not be overwritten.
|
||||
.br
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
@@ -1230,7 +1230,7 @@ static void dasdfmt_find_start(unsigned int cylinders, unsigned int heads,
|
||||
format_params->start_unit = first;
|
||||
}
|
||||
|
||||
static int dasdfmt_release_space(void)
|
||||
static void dasdfmt_release_space(void)
|
||||
{
|
||||
format_data_t r = {
|
||||
.start_unit = 0,
|
||||
@@ -1240,21 +1240,12 @@ static int dasdfmt_release_space(void)
|
||||
int err = 0;
|
||||
|
||||
if (!g.ese || g.no_discard)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
printf("Releasing space for the entire device...\n");
|
||||
err = dasd_release_space(g.dev_node, &r);
|
||||
/*
|
||||
* Warn or Error on failing RAS depending on QUICK mode set explicitly or automatically
|
||||
*/
|
||||
if (err && !g.mode_specified) {
|
||||
warnx("Could not release space. Falling back to full format.");
|
||||
return 1;
|
||||
} else if (err && g.mode_specified) {
|
||||
if (err)
|
||||
error("Could not release space: %s", strerror(err));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dasdfmt_prepare_and_format(unsigned int cylinders, unsigned int heads,
|
||||
@@ -1454,12 +1445,8 @@ static void do_format_dasd(volume_label_t *vlabel, format_data_t *p,
|
||||
dasdfmt_prepare_and_format(cylinders, heads, p);
|
||||
break;
|
||||
case QUICK:
|
||||
if (dasdfmt_release_space()) {
|
||||
p->stop_unit = (cylinders * heads) - 1;
|
||||
dasdfmt_prepare_and_format(cylinders, heads, p);
|
||||
} else {
|
||||
dasdfmt_quick_format(cylinders, heads, p);
|
||||
}
|
||||
dasdfmt_release_space();
|
||||
dasdfmt_quick_format(cylinders, heads, p);
|
||||
break;
|
||||
case EXPAND:
|
||||
dasdfmt_expand_format(cylinders, heads, p);
|
||||
@@ -1491,7 +1478,7 @@ static void eval_format_mode(void)
|
||||
}
|
||||
|
||||
if (!g.mode_specified)
|
||||
mode = g.ese ? QUICK : FULL;
|
||||
mode = FULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,28 +7,28 @@
|
||||
.B "dasdinfo "
|
||||
\- tool to read unique id from s390 DASD device
|
||||
.SH SYNOPSIS
|
||||
.BI "dasdinfo [-a] [-l] [-u] [-x] [-e] {-i " <busid>
|
||||
.BI "| -b " <blockdev>
|
||||
.BI " | -d " <devnode>
|
||||
.BI "dasdinfo [\-a] [\-l] [\-u] [\-x] [\-e] {\-i " <busid>
|
||||
.BI "| \-b " <blockdev>
|
||||
.BI " | \-d " <devnode>
|
||||
.BI "}"
|
||||
.sp
|
||||
.BI "dasdinfo [-h] [-v]"
|
||||
.BI "dasdinfo [\-h] [\-v]"
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B dasdinfo
|
||||
.B dasdinfo
|
||||
displays specific information about a specified DASD device.
|
||||
It is normally called from a udev rule, to provide udev with a unique id string and
|
||||
additional information (type, serial) for an S390 DASD drive. Udev can use this
|
||||
information to create symlinks in /dev/disk/by-id and /dev/disk/by-label
|
||||
information to create symlinks in /dev/disk/by\-id and /dev/disk/by\-label
|
||||
to the real device node.
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
.BI "-a|--all"
|
||||
.BI "\-a|\-\-all"
|
||||
Same as -u -x -l
|
||||
.TP
|
||||
.BI "-x|--extended-uid"
|
||||
.BI "\-x|\-\-extended\-uid"
|
||||
Print DASD uid
|
||||
|
||||
This option prints the full uid of the DASD. When z/VM provides two
|
||||
@@ -42,13 +42,13 @@ For z/VM: VM support for the hypervisor injected Special Node Element
|
||||
Qualifier (SNEQ) (or hypervisor injected self-description data) is
|
||||
available by applying the PTFs for VM APAR VM64273 on z/VM 5.2.0 and higher.
|
||||
.TP
|
||||
.BI "-u|--uid"
|
||||
.BI "\-u|\-\-uid"
|
||||
Print DASD uid without z/VM minidisk token
|
||||
|
||||
z/VM may provide an additional token that can be used to distinguish
|
||||
between different minidisks (see --extended-uid option). To remain
|
||||
between different minidisks (see \-\-extended\-uid option). To remain
|
||||
compatible with systems that were installed on older Linux or z/VM
|
||||
levels, the -u option will print the uid excluding any z/VM-provided
|
||||
levels, the \-u option will print the uid excluding any z/VM-provided
|
||||
minidisk token.
|
||||
|
||||
For example, if the extended uid is
|
||||
@@ -57,35 +57,35 @@ uid is IBM.75000000092461.e900.10. If the extended uid contains no
|
||||
minidisk token, e.g. in an LPAR environment, then both uids are the
|
||||
same.
|
||||
.TP
|
||||
.BI "-l|--label"
|
||||
.BI "\-l|\-\-label"
|
||||
Print DASD volume label (volser).
|
||||
.TP
|
||||
.BI "-i|--busid " <busid>
|
||||
.BI "\-i|\-\-busid " <busid>
|
||||
Use the bus ID as input parameter, e.g. 0.0.e910.
|
||||
.TP
|
||||
.BI "-b|--block " <blockdev>
|
||||
.BI "\-b|\-\-block " <blockdev>
|
||||
Use the block device name as input parameter, e.g. dasdb.
|
||||
.TP
|
||||
.BI "-d|--devnode " <devnode>
|
||||
.BI "\-d|\-\-devnode " <devnode>
|
||||
Use a device node as input parameter, e.g. /dev/dasdb.
|
||||
.TP
|
||||
.BI "-e|--export"
|
||||
.BI "\-e|\-\-export"
|
||||
Print all values (ID_BUS, ID_TYPE, ID_SERIAL).
|
||||
.TP
|
||||
.BI "-h|--help"
|
||||
.BI "\-h|\-\-help"
|
||||
Print usage text.
|
||||
.TP
|
||||
.BI "-v|--version"
|
||||
.BI "\-v|\-\-version"
|
||||
Print version number.
|
||||
|
||||
.SH EXAMPLES
|
||||
dasdinfo -u -i 0.0.e910
|
||||
dasdinfo \-u \-i 0.0.e910
|
||||
|
||||
dasdinfo -u -b dasdb
|
||||
dasdinfo \-u \-b dasdb
|
||||
|
||||
dasdinfo -u -d /dev/dasdb
|
||||
dasdinfo \-u \-d /dev/dasdb
|
||||
|
||||
All three examples should return the same unique ID for
|
||||
All three examples should return the same unique ID for
|
||||
the same DASD device, e.g. IBM.75000000092461.e900.10.
|
||||
|
||||
In case this uid is not available, dasdinfo will return
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
dasdview \- Display DASD and VTOC information and dump the content of a DASD
|
||||
to the console.
|
||||
.SH SYNOPSIS
|
||||
\fBdasdview\fR [-h] [-v]
|
||||
\fBdasdview\fR [\-h] [\-v]
|
||||
.br
|
||||
[-b \fIbegin\fR] [-s \fIsize\fR] [-1|-2]
|
||||
[\-b \fIbegin\fR] [\-s \fIsize\fR] [\-1|\-2]
|
||||
.br
|
||||
[-i] [-x] [-j] [-c]
|
||||
[\-i] [\-x] [\-j] [\-c]
|
||||
.br
|
||||
[-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}]
|
||||
[\-l] [\-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}]
|
||||
.br
|
||||
\fIdevice\fR
|
||||
.SH DESCRIPTION
|
||||
\fBdasdview\fR prints you some useful information of your disks to the console.
|
||||
You can display a disk dump by specifying start point and offset and you can
|
||||
You can display a disk dump by specifying start point and offset and you can
|
||||
print the volume label and VTOC entries.
|
||||
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
|
||||
Any device node created by udev for kernel 2.6 can be used
|
||||
Any device node created by udev for kernel 2.6 can be used
|
||||
(e.g. '/dev/dasd/0.0.b100/disc').
|
||||
|
||||
DASD devices in raw_track_access mode are supported and detected
|
||||
@@ -29,30 +29,30 @@ automatically. When in raw_track_access mode, the same basic
|
||||
functions are available as in the regular mode, but the output may
|
||||
have a slightly different layout:
|
||||
.IP \(bu 2
|
||||
The disk dump functions (\fB-b\fR and \fB-s\fR) print the count,
|
||||
The disk dump functions (\fB\-b\fR and \fB\-s\fR) print the count,
|
||||
key and data information for the whole track, and not just the
|
||||
contents of the data areas.
|
||||
.IP \(bu 2
|
||||
The VTOC listing (\fB-t\fR) print all specified DSCBs in the same
|
||||
The VTOC listing (\fB\-t\fR) print all specified DSCBs in the same
|
||||
format as in the regular mode, but in the sequence as they appear in
|
||||
the VTOC. The \fB-t info\fR overview contains more details for each
|
||||
the VTOC. The \fB\-t info\fR overview contains more details for each
|
||||
data set than in the regular mode, to support the larger variety of
|
||||
data set layouts.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-h\fR or \fB--help\fR
|
||||
\fB\-h\fR or \fB\-\-help\fR
|
||||
Print usage and exit.
|
||||
|
||||
.TP
|
||||
\fB-v\fR or \fB--version\fR
|
||||
\fB\-v\fR or \fB\-\-version\fR
|
||||
Print version number and exit.
|
||||
|
||||
.TP
|
||||
\fB-b\fR \fIbegin\fR or \fB--begin=\fR\fIbegin\fR
|
||||
Print a disk dump to the console, starting with \fIbegin\fR. The content of
|
||||
the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text.
|
||||
If no size is specified dasdview will take the default size. The variable
|
||||
\fB\-b\fR \fIbegin\fR or \fB\-\-begin=\fR\fIbegin\fR
|
||||
Print a disk dump to the console, starting with \fIbegin\fR. The content of
|
||||
the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text.
|
||||
If no size is specified dasdview will take the default size. The variable
|
||||
\fIbegin\fR can be specified in one of the following ways:
|
||||
.br
|
||||
|
||||
@@ -63,10 +63,10 @@ The default for \fIbegin\fR is \fI0\fR.
|
||||
.br
|
||||
|
||||
\fBNote 1:\fR dasdview will show you the content of your disk using the DASD
|
||||
driver. If this driver decides to hide or add some parts of the disk, you have
|
||||
to live with it. This happens for example with the first two tracks of a
|
||||
cdl-formatted disk. In this case the DASD driver fills up shorter blocks with
|
||||
zeros to have a constant blocksize. And all applications, including dasdview,
|
||||
driver. If this driver decides to hide or add some parts of the disk, you have
|
||||
to live with it. This happens for example with the first two tracks of a
|
||||
cdl-formatted disk. In this case the DASD driver fills up shorter blocks with
|
||||
zeros to have a constant blocksize. And all applications, including dasdview,
|
||||
believe it.
|
||||
.br
|
||||
\fBNote 2:\fR In raw_track_access mode \fIbegin\fR must be aligned to
|
||||
@@ -76,24 +76,24 @@ cylinder as starting point.
|
||||
|
||||
examples:
|
||||
.br
|
||||
-b 32 --> start printing at Byte 32
|
||||
\-b 32 --> start printing at Byte 32
|
||||
.br
|
||||
-b 32k --> start printing at kByte 32
|
||||
\-b 32k --> start printing at kByte 32
|
||||
.br
|
||||
-b 32m --> start printing at MByte 32
|
||||
\-b 32m --> start printing at MByte 32
|
||||
.br
|
||||
-b 32b --> start printing at block 32
|
||||
\-b 32b --> start printing at block 32
|
||||
.br
|
||||
-b 32t --> start printing at track 32
|
||||
\-b 32t --> start printing at track 32
|
||||
.br
|
||||
-b 32c --> start printing at cylinder 32
|
||||
\-b 32c --> start printing at cylinder 32
|
||||
|
||||
.TP
|
||||
\fB-s\fR \fIsize\fR or \fB--size=\fR\fIsize\fR
|
||||
Print a disk dump to the console, starting with \fIbegin\fR, specified with
|
||||
the \fB-b\fR option and size \fIsize\fR. The content of the disk will be
|
||||
displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no start
|
||||
value is specified dasdview will take the default start value. The variable
|
||||
\fB\-s\fR \fIsize\fR or \fB\-\-size=\fR\fIsize\fR
|
||||
Print a disk dump to the console, starting with \fIbegin\fR, specified with
|
||||
the \fB\-b\fR option and size \fIsize\fR. The content of the disk will be
|
||||
displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no start
|
||||
value is specified dasdview will take the default start value. The variable
|
||||
\fIsize\fR can be specified in one of the following ways:
|
||||
.br
|
||||
|
||||
@@ -111,76 +111,76 @@ in raw_track_access mode.
|
||||
|
||||
examples:
|
||||
.br
|
||||
-s 16 --> use a 16 Byte size
|
||||
\-s 16 --> use a 16 Byte size
|
||||
.br
|
||||
-s 16k --> use a 16 kByte size
|
||||
\-s 16k --> use a 16 kByte size
|
||||
.br
|
||||
-s 16m --> use a 16 MByte size
|
||||
\-s 16m --> use a 16 MByte size
|
||||
.br
|
||||
-s 16b --> use a 16 block size
|
||||
\-s 16b --> use a 16 block size
|
||||
.br
|
||||
-s 16t --> use a 16 track size
|
||||
\-s 16t --> use a 16 track size
|
||||
.br
|
||||
-s 16c --> use a 16 cylinder size
|
||||
\-s 16c --> use a 16 cylinder size
|
||||
|
||||
.TP
|
||||
\fB-1\fR
|
||||
This option tells dasdview to print the disk dump using format 1. This means
|
||||
you will get 16 Bytes per line in hex, ascii and ebcdic. There is no line
|
||||
\fB\-1\fR
|
||||
This option tells dasdview to print the disk dump using format 1. This means
|
||||
you will get 16 Bytes per line in hex, ascii and ebcdic. There is no line
|
||||
number.
|
||||
.br
|
||||
The \fB-1\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
|
||||
options.
|
||||
The \fB\-1\fR option makes only sense with the \fB\-b\fR and/or the \fB\-s\fR
|
||||
options.
|
||||
.br
|
||||
This is the default.
|
||||
|
||||
.TP
|
||||
\fB-2\fR
|
||||
This option tells dasdview to print the disk dump using format 2. This means
|
||||
you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line
|
||||
\fB\-2\fR
|
||||
This option tells dasdview to print the disk dump using format 2. This means
|
||||
you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line
|
||||
number and a decimal and hexadecimal byte count will be printed.
|
||||
.br
|
||||
The \fB-2\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
|
||||
The \fB\-2\fR option makes only sense with the \fB\-b\fR and/or the \fB\-s\fR
|
||||
options. In raw_track_access mode this format is not supported and the
|
||||
option will be ignored.
|
||||
|
||||
.TP
|
||||
\fB-i\fR or \fB--info\fR
|
||||
\fB\-i\fR or \fB\-\-info\fR
|
||||
Print some useful information (e.g. device node/number/type or geometry data).
|
||||
When running dasdview on a kernel 2.6 based distribution the busid
|
||||
When running dasdview on a kernel 2.6 based distribution the busid
|
||||
is printed instead of the device number.
|
||||
|
||||
.TP
|
||||
\fB-x\fR or \fB--extended\fR
|
||||
\fB\-x\fR or \fB\-\-extended\fR
|
||||
Print some more DASD information (e.g. open count, subchannel identifier).
|
||||
|
||||
.TP
|
||||
\fB-j\fR or \fB--volser\fR
|
||||
\fB\-j\fR or \fB\-\-volser\fR
|
||||
Print volume serial number (volume identifier).
|
||||
|
||||
.TP
|
||||
\fB-l\fR or \fB--label\fR
|
||||
\fB\-l\fR or \fB\-\-label\fR
|
||||
Print the volume label.
|
||||
|
||||
.TP
|
||||
\fB-c\fR or \fB--characteristic\fR
|
||||
\fB\-c\fR or \fB\-\-characteristic\fR
|
||||
Print some information about the device e.g. if it is encrypted.
|
||||
|
||||
.TP
|
||||
\fB-t\fR \fIspec\fR or \fB--vtoc=\fR\fIspec\fR
|
||||
\fB\-t\fR \fIspec\fR or \fB\-\-vtoc=\fR\fIspec\fR
|
||||
Print the VTOC (table of content) or single VTOC entries to the console.
|
||||
\fIspec\fR can be one of the following strings:
|
||||
.br
|
||||
|
||||
\fIinfo\fR:
|
||||
\fIinfo\fR:
|
||||
.br
|
||||
Gives you a VTOC overview. You will see what other S/390 or zSeries operating
|
||||
Gives you a VTOC overview. You will see what other S/390 or zSeries operating
|
||||
systems would see (e.g. data set names and sizes).
|
||||
.br
|
||||
|
||||
\fIf1\fR:
|
||||
.br
|
||||
Print the content of all format 1 DSCBs.
|
||||
Print the content of all format 1 DSCBs.
|
||||
.br
|
||||
|
||||
\fIf3\fR:
|
||||
@@ -188,17 +188,17 @@ Print the content of all format 1 DSCBs.
|
||||
Print the content of all format 3 DSCBs.
|
||||
.br
|
||||
|
||||
\fIf4\fR:
|
||||
\fIf4\fR:
|
||||
.br
|
||||
Print the content of the format 4 DSCB.
|
||||
.br
|
||||
|
||||
\fIf5\fR:
|
||||
\fIf5\fR:
|
||||
.br
|
||||
Print the content of the format 5 DSCB.
|
||||
.br
|
||||
|
||||
\fIf7\fR:
|
||||
\fIf7\fR:
|
||||
.br
|
||||
Print the content of the format 7 DSCB.
|
||||
.br
|
||||
@@ -213,6 +213,6 @@ Print the content of all format 8 DSCBs.
|
||||
Print the content of all format 9 DSCBs.
|
||||
.br
|
||||
|
||||
\fIall\fR:
|
||||
\fIall\fR:
|
||||
.br
|
||||
Print the content of all DSCBs.
|
||||
Print the content of all DSCBs.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -I../include -std=gnu99 -Wno-unused-parameter
|
||||
ALL_CPPFLAGS += -I../include -Wno-unused-parameter
|
||||
LDLIBS += -lpthread -lrt
|
||||
ifneq ($(HAVE_ZLIB),0)
|
||||
ALL_CPPFLAGS += -DHAVE_ZLIB
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
# DEVICE=0.0.4e13
|
||||
# DELAY_MINUTES=5
|
||||
|
||||
#
|
||||
# Dump on ECKD device (DASD)
|
||||
#
|
||||
#ON_PANIC=dump
|
||||
#DUMP_TYPE=eckd
|
||||
#DEVICE=0.0.1004
|
||||
#BOOTPROG=0
|
||||
#BR_CHR=auto
|
||||
|
||||
#
|
||||
# Dump on fcp device (SCSI Disk)
|
||||
#
|
||||
|
||||
@@ -8,21 +8,21 @@ fdasd \- partitioning tool.
|
||||
.SH SYNOPSIS
|
||||
interactive mode:
|
||||
.br
|
||||
\fBfdasd\fR [-s] [-r] [-C] \fIdevice\fR
|
||||
\fBfdasd\fR [\-s] [\-r] [\-C] \fIdevice\fR
|
||||
.br
|
||||
command line mode:
|
||||
.br
|
||||
\fBfdasd\fR [-s] [-r] [-C] {-a[-k|-l \fIvolser\fR]|-i|-p|-c \fIconf_file\fR}
|
||||
\fBfdasd\fR [\-s] [\-r] [\-C] {\-a[\-k|\-l \fIvolser\fR]|\-i|\-p|\-c \fIconf_file\fR}
|
||||
[-f \fI[type,blocksize]\fR] \fIdevice\fR
|
||||
.br
|
||||
help:
|
||||
.br
|
||||
\fBfdasd\fR {-h|-v}
|
||||
\fBfdasd\fR {\-h|\-v}
|
||||
.SH DESCRIPTION
|
||||
\fBfdasd\fR writes a partition table to a cdl (compatible disk layout)
|
||||
\fBfdasd\fR writes a partition table to a cdl (compatible disk layout)
|
||||
formatted DASD, in the form of
|
||||
a VTOC (volume table of contents) for usage with Linux for S/390
|
||||
or zSeries. If fdasd detects a valid \fBVOL1\fR volume label, it
|
||||
or zSeries. If fdasd detects a valid \fBVOL1\fR volume label, it
|
||||
will use it, otherwise it asks to write a new one.
|
||||
.br
|
||||
|
||||
@@ -30,66 +30,66 @@ will use it, otherwise it asks to write a new one.
|
||||
\fBfdasd\fR can result in loss of data.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-h\fR or \fB--help\fR
|
||||
\fB\-h\fR or \fB\-\-help\fR
|
||||
Print usage information, then exit.
|
||||
|
||||
.TP
|
||||
\fB-v\fR or \fB--version\fR
|
||||
\fB\-v\fR or \fB\-\-version\fR
|
||||
Print version information, then exit.
|
||||
|
||||
.TP
|
||||
\fB-s\fR or \fB--silent\fR
|
||||
\fB\-s\fR or \fB\-\-silent\fR
|
||||
Suppress messages in non-interactive mode.
|
||||
|
||||
.TP
|
||||
\fB-r\fR or \fB--verbose\fR
|
||||
\fB\-r\fR or \fB\-\-verbose\fR
|
||||
Provide more verbose output.
|
||||
|
||||
.TP
|
||||
\fB-a\fR or \fB--auto\fR
|
||||
Automatically create a partition using the entire disk in non-interactive
|
||||
\fB\-a\fR or \fB\-\-auto\fR
|
||||
Automatically create a partition using the entire disk in non-interactive
|
||||
mode.
|
||||
|
||||
.TP
|
||||
\fB-k\fR or \fB--keep_volser\fR
|
||||
Keeps the volume serial when writing the volume label.
|
||||
\fB\-k\fR or \fB\-\-keep_volser\fR
|
||||
Keeps the Volume Serial Number when writing the Volume Label.
|
||||
.br
|
||||
This is useful, if the volume serial has been written before and should not
|
||||
be overwritten. This option is only applicable in non-interactive mode.
|
||||
This is useful if the volume already has a Serial Number that should not be
|
||||
overwritten. This option is only applicable in non-interactive mode.
|
||||
|
||||
.TP
|
||||
\fB-l\fR \fIvolser\fR or \fB--label\fR \fIvolser\fR
|
||||
\fB\-l\fR \fIvolser\fR or \fB\-\-label\fR \fIvolser\fR
|
||||
Specify the volume serial.
|
||||
.br
|
||||
\fIvolser\fR is interpreted as ASCII string and is automatically converted to
|
||||
\fIvolser\fR is interpreted as ASCII string and is automatically converted to
|
||||
uppercase, padded with blanks and finally converted to EBCDIC to be written
|
||||
to disk. This option is only applicable in non-interactive mode.
|
||||
.br
|
||||
|
||||
Do not use the following reserved volume serial: SCRTCH, PRIVAT, MIGRAT,
|
||||
or Lnnnnn (L with five digit number); These are used as keywords by
|
||||
Do not use the following reserved volume serial: SCRTCH, PRIVAT, MIGRAT,
|
||||
or Lnnnnn (L with five digit number); These are used as keywords by
|
||||
other operating systems (OS/390).
|
||||
.br
|
||||
|
||||
A volume serial is 1 through 6 alphanumeric characters or one of the
|
||||
following special characters: $, #, @, %. All other characters are simply
|
||||
ignored.
|
||||
A volume serial is 1 through 6 alphanumeric characters or one of the
|
||||
following special characters: $, #, @, %. All other characters are simply
|
||||
ignored.
|
||||
.br
|
||||
Try to avoid using special characters in the volume serial.
|
||||
This may cause problems accessing a disk by volser.
|
||||
In case you really have to use special characters, make sure you are using
|
||||
quotes. In addition there is a special handling for the '$' sign.
|
||||
Try to avoid using special characters in the volume serial.
|
||||
This may cause problems accessing a disk by volser.
|
||||
In case you really have to use special characters, make sure you are using
|
||||
quotes. In addition there is a special handling for the '$' sign.
|
||||
Please specify it using '\\$' if necessary.
|
||||
.br
|
||||
|
||||
e.g. -l 'a@b\\$c#' to get A@B$C#
|
||||
e.g. \-l 'a@b\\$c#' to get A@B$C#
|
||||
.br
|
||||
|
||||
Omitting this parameter causes fdasd to ask for it in case it is needed.
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-c\fR \fIconf_file\fR or \fB--config\fR \fIconf_file\fR
|
||||
\fB\-c\fR \fIconf_file\fR or \fB\-\-config\fR \fIconf_file\fR
|
||||
Use this option to create multiple partitions according to
|
||||
specifications in a configuration file, \fIconf_file\fR.
|
||||
.br
|
||||
@@ -124,37 +124,37 @@ partitions that use the entire disk:
|
||||
.br
|
||||
|
||||
.TP
|
||||
\fB-i\fR or \fB--volser\fR
|
||||
\fB\-i\fR or \fB\-\-volser\fR
|
||||
Print the volume serial, then exit.
|
||||
|
||||
.TP
|
||||
\fB-p\fR or \fB--table\fR
|
||||
Print partition table, then exit.
|
||||
\fB\-p\fR or \fB\-\-table\fR
|
||||
Print partition table, then exit.
|
||||
.br
|
||||
In combination with the -s option fdasd will display a short version of the
|
||||
In combination with the \-s option fdasd will display a short version of the
|
||||
partition table.
|
||||
|
||||
.TP
|
||||
\fB-C\fR or \fB--check_host_count\fR
|
||||
\fB\-C\fR or \fB\-\-check_host_count\fR
|
||||
Force fdasd to check the host access open count to ensure the device
|
||||
is not online on another operating system instance
|
||||
|
||||
.TP
|
||||
\fB-f\fR \fI[type,blocksize]\fR or \fB--force\fR \fI[type,blocksize]\fR
|
||||
\fB\-f\fR \fI[type,blocksize]\fR or \fB\-\-force\fR \fI[type,blocksize]\fR
|
||||
Force fdasd to work on non DASD devices.
|
||||
.br
|
||||
If fdasd is to be used on a block device that is neither a native DASD
|
||||
nor exposes the proper disk geometry of a DASD of type 3390,
|
||||
then the --force option can be used to assume the geometry of a
|
||||
then the \-\-force option can be used to assume the geometry of a
|
||||
given device type. The default device type is 3390 and the default
|
||||
block size is 4096. An optional argument of <device type>,<blocksize>
|
||||
can be used to specify type and blocksize explicitly. For example:
|
||||
|
||||
-f
|
||||
\-f
|
||||
|
||||
has the same effect as
|
||||
|
||||
-f3390,4096 or --force=3390,4096
|
||||
\-f3390,4096 or \-\-force=3390,4096
|
||||
|
||||
Valid device types are: 3390, 3380, 9345
|
||||
.br
|
||||
@@ -179,7 +179,7 @@ In case your are not using the device file system, please specify:
|
||||
.br
|
||||
|
||||
where \fIx\fR is one or more lowercase letter(s) or any other device
|
||||
node specification configured by udev for kernel 2.6 or higher.
|
||||
node specification configured by udev for kernel 2.6 or higher.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR dasdfmt (8)
|
||||
|
||||
@@ -3,7 +3,7 @@ include ../common.mak
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
PKGDATADIR := "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
PKGDATADIR := "$(TOOLS_DATADIR)/genprotimg"
|
||||
TESTS :=
|
||||
SUBDIRS := boot src man
|
||||
RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
@@ -11,8 +11,8 @@ RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
all: all-recursive
|
||||
|
||||
install: install-recursive
|
||||
$(INSTALL) -d -m 755 "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(PKGDATADIR)"
|
||||
$(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(DESTDIR)$(PKGDATADIR)"
|
||||
|
||||
clean: clean-recursive
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ DEBUG_FILES := $(addsuffix .debug,$(FILES))
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
ZIPL_DIR := $(rootdir)/zipl
|
||||
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot
|
||||
PKGDATADIR := $(DESTDIR)$(TOOLS_DATADIR)/genprotimg
|
||||
PKGDATADIR := $(TOOLS_DATADIR)/genprotimg
|
||||
|
||||
INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
|
||||
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
|
||||
@@ -79,15 +79,16 @@ stage3b.elf: head.o $(ZIPL_OBJS)
|
||||
|
||||
%.bin.debug: %.elf
|
||||
$(OBJCOPY) --only-keep-debug $< $@
|
||||
@chmod a-x $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
@chmod a-x $@
|
||||
|
||||
install: stage3a.bin stage3b_reloc.bin
|
||||
$(INSTALL) -d -m 755 "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin "$(PKGDATADIR)"
|
||||
$(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin "$(DESTDIR)$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin "$(DESTDIR)$(PKGDATADIR)"
|
||||
|
||||
else
|
||||
# Don't generate the dependency files (see `common.mak` for the
|
||||
|
||||
@@ -61,13 +61,17 @@ void __noreturn start(void)
|
||||
if (cmdline->size > get_kernel_cmdline_size())
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel cmdline */
|
||||
memmove((void *)COMMAND_LINE,
|
||||
(void *)cmdline->src,
|
||||
cmdline->size);
|
||||
if (cmdline->size > 0) {
|
||||
/* make sure the cmdline is a null-terminated string */
|
||||
if (((char *)cmdline->src)[cmdline->size - 1] != '\0')
|
||||
panic(EINTERNAL, "Command line needs to be null-terminated\n");
|
||||
|
||||
/* move the kernel cmdline */
|
||||
memmove((void *)COMMAND_LINE, (void *)cmdline->src, cmdline->size);
|
||||
}
|
||||
/* the initrd does not need to be moved */
|
||||
|
||||
if (initrd->size != 0) {
|
||||
if (initrd->size > 0) {
|
||||
/* copy initrd start address and size into new kernel space */
|
||||
*(unsigned long long *)INITRD_START = initrd->src;
|
||||
*(unsigned long long *)INITRD_SIZE = initrd->size;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Sample script to verify that a host key document is genuine by
|
||||
# verifying the issuer, the validity date and the signature.
|
||||
# Optionally verify the full trust chain using a CA certficate.
|
||||
# Optionally verify the full trust chain using a CA certificate.
|
||||
#
|
||||
# Sample invocation:
|
||||
#
|
||||
@@ -15,31 +15,33 @@
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
|
||||
# Allocate temporary files
|
||||
ISSUER_PUBKEY_FILE=$(mktemp)
|
||||
SIGNATURE_FILE=$(mktemp)
|
||||
BODY_FILE=$(mktemp)
|
||||
ISSUER_DN_FILE=$(mktemp)
|
||||
SUBJECT_DN_FILE=$(mktemp)
|
||||
DEF_ISSUER_DN_FILE=$(mktemp)
|
||||
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
|
||||
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
|
||||
CANONICAL_ISSUER_DN_FILE=$(mktemp)
|
||||
CRL_SERIAL_FILE=$(mktemp)
|
||||
|
||||
# Cleanup on exit
|
||||
cleanup()
|
||||
{
|
||||
rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \
|
||||
$ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \
|
||||
$CANONICAL_ISSUER_DN_FILE $CRL_SERIAL_FILE
|
||||
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
|
||||
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
|
||||
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Enhanced error checking for bash
|
||||
if [ -n "${BASH}" ]
|
||||
then
|
||||
if [ -n "${BASH}" ]; then
|
||||
# shellcheck disable=SC3040
|
||||
set -o posix
|
||||
# shellcheck disable=SC3040
|
||||
set -o pipefail
|
||||
# shellcheck disable=SC3040
|
||||
set -o nounset
|
||||
fi
|
||||
set -e
|
||||
@@ -47,8 +49,8 @@ set -e
|
||||
# Usage
|
||||
usage()
|
||||
{
|
||||
cat <<-EOF
|
||||
Usage: `basename $1` [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
|
||||
cat <<-EOF
|
||||
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
|
||||
|
||||
Verify an IBM Secure Execution host key document against
|
||||
a signing key.
|
||||
@@ -71,8 +73,7 @@ check_verify_chain()
|
||||
{
|
||||
# Verify certificate chain in case a CA certificate file/bundle
|
||||
# was specified on the command line.
|
||||
if [ $# = 1 ]
|
||||
then
|
||||
if [ -z "$2" ]; then
|
||||
cat >&2 <<-EOF
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
No CA certificate specified! Skipping trust chain verification.
|
||||
@@ -80,37 +81,37 @@ Make sure that '$1' is a valid certificate.
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
EOF
|
||||
else
|
||||
openssl verify -crl_download -crl_check $2 &&
|
||||
openssl verify -crl_download -crl_check -untrusted $2 $1 ||
|
||||
exit 1
|
||||
openssl verify -crl_download -crl_check "$2" &&
|
||||
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_pubkey()
|
||||
{
|
||||
openssl x509 -in $1 -pubkey -noout > $2
|
||||
openssl x509 -in "$1" -pubkey -noout >"$2"
|
||||
}
|
||||
|
||||
extract_signature()
|
||||
{
|
||||
# Assuming that the last field is the signature
|
||||
SIGOFFSET=$(openssl asn1parse -in $1 | tail -1 | cut -d : -f 1)
|
||||
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
|
||||
|
||||
openssl asn1parse -in $1 -out $2 -strparse $SIGOFFSET -noout
|
||||
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
|
||||
}
|
||||
|
||||
extract_body()
|
||||
{
|
||||
# Assuming that the first field is the full cert body
|
||||
SIGOFFSET=$(openssl asn1parse -in $1 | head -2 | tail -1 | cut -d : -f 1)
|
||||
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
|
||||
|
||||
openssl asn1parse -in $1 -out $2 -strparse $SIGOFFSET -noout
|
||||
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
|
||||
}
|
||||
|
||||
verify_signature()
|
||||
{
|
||||
# Assuming that the signature algorithm is SHA512 with RSA
|
||||
openssl sha512 -verify $1 -signature $2 $3
|
||||
openssl sha512 -verify "$1" -signature "$2" "$3"
|
||||
}
|
||||
|
||||
canonical_dn()
|
||||
@@ -120,18 +121,30 @@ canonical_dn()
|
||||
DNTYPE=$3
|
||||
OUTPUT=$4
|
||||
|
||||
openssl $OBJTYPE -in $OBJ -$DNTYPE -noout -nameopt multiline \
|
||||
| sort | grep -v $DNTYPE= > $OUTPUT
|
||||
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
|
||||
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
|
||||
}
|
||||
|
||||
default_issuer()
|
||||
default_issuer_armonk()
|
||||
{
|
||||
cat <<-EOF
|
||||
commonName = International Business Machines Corporation
|
||||
countryName = US
|
||||
localityName = Armonk
|
||||
organizationName = International Business Machines Corporation
|
||||
organizationalUnitName = Key Signing Service
|
||||
stateOrProvinceName = New York
|
||||
EOF
|
||||
}
|
||||
|
||||
default_issuer_pougkeepsie()
|
||||
{
|
||||
cat <<-EOF
|
||||
commonName = International Business Machines Corporation
|
||||
countryName = US
|
||||
localityName = Poughkeepsie
|
||||
organizationalUnitName = Key Signing Service
|
||||
organizationName = International Business Machines Corporation
|
||||
organizationalUnitName = Key Signing Service
|
||||
stateOrProvinceName = New York
|
||||
EOF
|
||||
}
|
||||
@@ -141,42 +154,37 @@ EOF
|
||||
# stripping off the prefix
|
||||
verify_default_issuer()
|
||||
{
|
||||
default_issuer > $DEF_ISSUER_DN_FILE
|
||||
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
|
||||
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
|
||||
|
||||
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
|
||||
$ISSUER_DN_FILE > $CANONICAL_ISSUER_DN_FILE
|
||||
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
|
||||
|
||||
if ! diff $CANONICAL_ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
then
|
||||
if ! {
|
||||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
|
||||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
|
||||
} >/dev/null 2>&1; then
|
||||
echo Incorrect default issuer >&2 && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
{
|
||||
if [ $1 -eq 1 ]
|
||||
then
|
||||
verify_default_issuer
|
||||
fi
|
||||
|
||||
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
||||
then
|
||||
echo Issuer verification OK
|
||||
else
|
||||
echo Issuer verification failed >&2 && exit 1
|
||||
if [ "$1" -eq 1 ]; then
|
||||
verify_default_issuer
|
||||
fi
|
||||
}
|
||||
|
||||
cert_time()
|
||||
{
|
||||
DATE=$(openssl x509 -in $1 -$2 -noout | sed "s/^.*=//")
|
||||
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
|
||||
|
||||
date -d "$DATE" +%s
|
||||
}
|
||||
|
||||
crl_time()
|
||||
{
|
||||
DATE=$(openssl crl -in $1 -$2 -noout | sed "s/^.*=//")
|
||||
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
|
||||
|
||||
date -d "$DATE" +%s
|
||||
}
|
||||
@@ -188,8 +196,7 @@ verify_dates()
|
||||
MSG="${3:-Certificate}"
|
||||
NOW=$(date +%s)
|
||||
|
||||
if [ $START -le $NOW -a $NOW -le $END ]
|
||||
then
|
||||
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
|
||||
echo "${MSG} dates are OK"
|
||||
else
|
||||
echo "${MSG} date verification failed" >&2 && exit 1
|
||||
@@ -198,22 +205,21 @@ verify_dates()
|
||||
|
||||
crl_serials()
|
||||
{
|
||||
openssl crl -in $1 -text -noout | \
|
||||
grep "Serial Number" > $CRL_SERIAL_FILE
|
||||
openssl crl -in "$1" -text -noout |
|
||||
grep "Serial Number" >"$CRL_SERIAL_FILE"
|
||||
}
|
||||
|
||||
check_serial()
|
||||
{
|
||||
CERT_SERIAL=$(openssl x509 -in $1 -noout -serial | cut -d = -f 2)
|
||||
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
|
||||
|
||||
grep -q $CERT_SERIAL $CRL_SERIAL_FILE
|
||||
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
|
||||
}
|
||||
|
||||
check_file()
|
||||
{
|
||||
[ $# = 0 ] ||
|
||||
[ -e "$1" ] ||
|
||||
(echo "File '$1' not found" >&2 && exit 1)
|
||||
(echo "File '$1' not found" >&2 && exit 1)
|
||||
}
|
||||
|
||||
# check args
|
||||
@@ -221,28 +227,25 @@ CRL_FILE=
|
||||
CA_FILE=
|
||||
CHECK_DEFAULT_ISSUER=1
|
||||
|
||||
args=$(getopt -qu "dr:c:h" $*)
|
||||
if [ $? = 0 ]
|
||||
then
|
||||
set -- $args
|
||||
while [ $1 != "" ]
|
||||
do
|
||||
case $1 in
|
||||
-d) CHECK_DEFAULT_ISSUER=0; shift;;
|
||||
-r) CRL_FILE=$2; shift 2;;
|
||||
-c) CA_FILE=$2; shift 2;;
|
||||
-h) usage $0; exit 0;;
|
||||
--) shift; break;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
usage $0 >&2
|
||||
exit 1
|
||||
fi
|
||||
while getopts 'dr:c:h' opt; do
|
||||
case $opt in
|
||||
d) CHECK_DEFAULT_ISSUER=0 ;;
|
||||
r) CRL_FILE=$OPTARG ;;
|
||||
c) CA_FILE=$OPTARG ;;
|
||||
h)
|
||||
usage "$0"
|
||||
exit 0
|
||||
;;
|
||||
?)
|
||||
usage "$0"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$((OPTIND - 1))"
|
||||
|
||||
if [ $# -ne 2 ]
|
||||
then
|
||||
usage $0 >&2
|
||||
if [ $# -ne 2 ]; then
|
||||
usage "$0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -250,51 +253,51 @@ HKD_FILE=$1
|
||||
HKSK_FILE=$2
|
||||
|
||||
# Check whether all specified files exist
|
||||
check_file $HKD_FILE
|
||||
check_file $HKSK_FILE
|
||||
check_file $CA_FILE
|
||||
check_file $CRL_FILE
|
||||
check_file "$HKD_FILE"
|
||||
check_file "$HKSK_FILE"
|
||||
# CA and CRL are optional arguments
|
||||
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
|
||||
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
|
||||
|
||||
# Check trust chain
|
||||
check_verify_chain $HKSK_FILE $CA_FILE
|
||||
check_verify_chain "$HKSK_FILE" "$CA_FILE"
|
||||
|
||||
# Verify host key document signature
|
||||
echo -n "Checking host key document signature: "
|
||||
extract_pubkey $HKSK_FILE $ISSUER_PUBKEY_FILE &&
|
||||
extract_signature $HKD_FILE $SIGNATURE_FILE &&
|
||||
extract_body $HKD_FILE $BODY_FILE &&
|
||||
verify_signature $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE ||
|
||||
exit 1
|
||||
printf "Checking host key document signature: "
|
||||
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
|
||||
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
|
||||
extract_body "$HKD_FILE" "$BODY_FILE" &&
|
||||
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
|
||||
exit 1
|
||||
|
||||
# Verify the issuer
|
||||
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
|
||||
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
# Verify dates
|
||||
verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate)
|
||||
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
|
||||
|
||||
# Check CRL if specified
|
||||
if [ -n "$CRL_FILE" ]
|
||||
then
|
||||
echo -n "Checking CRL signature: "
|
||||
extract_signature $CRL_FILE $SIGNATURE_FILE &&
|
||||
extract_body $CRL_FILE $BODY_FILE &&
|
||||
verify_signature $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE ||
|
||||
exit 1
|
||||
if [ -n "$CRL_FILE" ]; then
|
||||
printf "Checking CRL signature: "
|
||||
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
|
||||
extract_body "$CRL_FILE" "$BODY_FILE" &&
|
||||
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
|
||||
exit 1
|
||||
|
||||
echo -n "CRL "
|
||||
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
printf "CRL "
|
||||
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
|
||||
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL'
|
||||
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
|
||||
|
||||
crl_serials $CRL_FILE
|
||||
check_serial $HKD_FILE &&
|
||||
echo "Certificate is revoked, do not use it anymore!" >&2 &&
|
||||
exit 1
|
||||
crl_serials "$CRL_FILE"
|
||||
check_serial "$HKD_FILE" &&
|
||||
echo "Certificate is revoked, do not use it anymore!" >&2 &&
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We made it
|
||||
echo All checks reqested for \'$HKD_FILE\' were successful
|
||||
echo All checks requested for \'"$HKD_FILE"\' were successful
|
||||
|
||||
@@ -3,7 +3,7 @@ include ../../common.mak
|
||||
|
||||
bin_PROGRAM = genprotimg
|
||||
|
||||
PKGDATADIR ?= "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
PKGDATADIR ?= "$(TOOLS_DATADIR)/genprotimg"
|
||||
SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
TOP_SRCDIR := $(SRC_DIR)/../
|
||||
ROOT_DIR = $(TOP_SRC_DIR)/../../
|
||||
@@ -27,7 +27,7 @@ $(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \
|
||||
$(NULL)
|
||||
$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
||||
|
||||
ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
|
||||
ALL_CFLAGS += -DPKGDATADIR=$(PKGDATADIR) \
|
||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
||||
-DOPENSSL_API_COMPAT=0x10100000L \
|
||||
$(WARNINGS) \
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
/* IBM signing key subject */
|
||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
||||
|
||||
@@ -684,7 +684,26 @@ gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err)
|
||||
{
|
||||
g_autoptr(PvComponent) comp = NULL;
|
||||
|
||||
comp = pv_component_new_file(arg->type, arg->path, err);
|
||||
switch (arg->type) {
|
||||
case PV_COMP_TYPE_INITRD:
|
||||
case PV_COMP_TYPE_KERNEL:
|
||||
case PV_COMP_TYPE_STAGE3B:
|
||||
comp = pv_component_new_file(arg->type, arg->path, err);
|
||||
break;
|
||||
case PV_COMP_TYPE_CMDLINE: {
|
||||
g_autoptr(PvBuffer) buf = NULL;
|
||||
g_autofree char *data = NULL;
|
||||
gsize length;
|
||||
|
||||
if (!g_file_get_contents(arg->path, &data, &length, err))
|
||||
return -1;
|
||||
|
||||
/* Add one for the null terminator */
|
||||
buf = pv_buffer_take(g_steal_pointer(&data), length + 1);
|
||||
comp = pv_component_new_buf(arg->type, buf, err);
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!comp)
|
||||
return -1;
|
||||
|
||||
|
||||
@@ -26,6 +26,15 @@ PvBuffer *pv_buffer_alloc(gsize size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
PvBuffer *pv_buffer_take(char *data, gsize size)
|
||||
{
|
||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
||||
|
||||
ret->data = data;
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
||||
{
|
||||
PvBuffer *ret;
|
||||
|
||||
@@ -21,6 +21,10 @@ typedef struct PvBuffer {
|
||||
} PvBuffer;
|
||||
|
||||
PvBuffer *pv_buffer_alloc(gsize size);
|
||||
/* After this call @data belongs to the PvBuffer and must no longer be modified
|
||||
* by the caller.
|
||||
*/
|
||||
PvBuffer *pv_buffer_take(char *data, gsize size);
|
||||
void pv_buffer_free(PvBuffer *buf);
|
||||
void pv_buffer_clear(PvBuffer **buf);
|
||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
||||
|
||||
@@ -664,62 +664,9 @@ static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid,
|
||||
return memcmp(data, y, data_len) == 0;
|
||||
}
|
||||
|
||||
static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x,
|
||||
const X509_NAME_ENTRY *y)
|
||||
{
|
||||
const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x);
|
||||
const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x);
|
||||
const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y);
|
||||
const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y);
|
||||
gint x_len = ASN1_STRING_length(x_data);
|
||||
gint y_len = ASN1_STRING_length(y_data);
|
||||
|
||||
if (x_len < 0 || x_len != y_len)
|
||||
return FALSE;
|
||||
|
||||
/* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also
|
||||
* compares the type, which is sometimes different.
|
||||
*/
|
||||
return OBJ_cmp(x_obj, y_obj) == 0 &&
|
||||
memcmp(ASN1_STRING_get0_data(x_data),
|
||||
ASN1_STRING_get0_data(y_data),
|
||||
(unsigned long)x_len) == 0;
|
||||
}
|
||||
|
||||
static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y)
|
||||
{
|
||||
gint x_count = X509_NAME_entry_count(x);
|
||||
gint y_count = X509_NAME_entry_count(y);
|
||||
|
||||
if (x != y && (!x || !y))
|
||||
return FALSE;
|
||||
|
||||
if (x_count != y_count)
|
||||
return FALSE;
|
||||
|
||||
for (gint i = 0; i < x_count; i++) {
|
||||
const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i);
|
||||
gboolean entry_found = FALSE;
|
||||
|
||||
for (gint j = 0; j < y_count; j++) {
|
||||
const X509_NAME_ENTRY *entry_j =
|
||||
X509_NAME_get_entry(y, j);
|
||||
|
||||
if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) {
|
||||
entry_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry_found)
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Checks whether the subject of @cert is a IBM signing key subject. For this we
|
||||
* must check that the subject is equal to: 'C = US, ST = New York, L =
|
||||
* Poughkeepsie, O = International Business Machines Corporation, CN =
|
||||
* Poughkeepsie or Armonk, O = International Business Machines Corporation, CN =
|
||||
* International Business Machines Corporation' and the organization unit (OUT)
|
||||
* must end with the suffix ' Key Signing Service'.
|
||||
*/
|
||||
@@ -743,8 +690,10 @@ static gboolean has_ibm_signing_subject(X509 *cert)
|
||||
PV_IBM_Z_SUBJECT_STATE))
|
||||
return FALSE;
|
||||
|
||||
if (!x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
PV_IBM_Z_SUBJECT_LOCALITY_NAME))
|
||||
if (!(x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) ||
|
||||
x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK)))
|
||||
return FALSE;
|
||||
|
||||
if (!x509_name_data_by_nid_equal(subject, NID_organizationName,
|
||||
@@ -806,6 +755,39 @@ static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/** Replace locality 'Armonk' with 'Pougkeepsie'. If Armonk was not set return
|
||||
* `NULL`.
|
||||
*/
|
||||
static X509_NAME *x509_armonk_locality_fixup(const X509_NAME *name)
|
||||
{
|
||||
g_autoptr(X509_NAME) ret = NULL;
|
||||
int pos;
|
||||
|
||||
/* Check if ``L=Armonk`` */
|
||||
if (!x509_name_data_by_nid_equal((X509_NAME *)name, NID_localityName,
|
||||
PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK))
|
||||
return NULL;
|
||||
|
||||
ret = X509_NAME_dup((X509_NAME *)name);
|
||||
if (!ret)
|
||||
g_abort();
|
||||
|
||||
pos = X509_NAME_get_index_by_NID(ret, NID_localityName, -1);
|
||||
if (pos == -1)
|
||||
return NULL;
|
||||
|
||||
X509_NAME_ENTRY_free(X509_NAME_delete_entry(ret, pos));
|
||||
|
||||
/* Create a new name entry at the same position as before */
|
||||
if (X509_NAME_add_entry_by_NID(
|
||||
ret, NID_localityName, MBSTRING_UTF8,
|
||||
(const unsigned char *)&PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE,
|
||||
sizeof(PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) - 1, pos, 0) != 1)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory
|
||||
* ordered. The problem is that our certificates are not consistent in the order
|
||||
* (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details).
|
||||
@@ -828,24 +810,10 @@ X509_NAME *c2b_name(const X509_NAME *name)
|
||||
return X509_NAME_dup((X509_NAME *)name);
|
||||
}
|
||||
|
||||
/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */
|
||||
/* Verify that SKID(issuer) == AKID(crl) if available */
|
||||
static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err)
|
||||
{
|
||||
const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl);
|
||||
const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
|
||||
AUTHORITY_KEYID *akid = NULL;
|
||||
|
||||
if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) {
|
||||
g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject,
|
||||
NULL, 0);
|
||||
g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0);
|
||||
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
||||
_("issuer mismatch:\n%s\n%s"),
|
||||
issuer_subject_str, crl_issuer_str);
|
||||
return -1;
|
||||
}
|
||||
g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
/* If AKID(@crl) is specified it must match with SKID(@issuer) */
|
||||
akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL);
|
||||
@@ -881,7 +849,6 @@ gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check that the @crl issuer matches with the subject name of @cert*/
|
||||
if (check_crl_issuer(crl, cert, err) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -910,6 +877,60 @@ gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function contains work-arounds for some known subject(CRT)<->issuer(CRL)
|
||||
* issues.
|
||||
*/
|
||||
static STACK_OF_X509_CRL *quirk_X509_STORE_ctx_get1_crls(X509_STORE_CTX *ctx,
|
||||
const X509_NAME *subject, GError **err)
|
||||
{
|
||||
g_autoptr(X509_NAME) fixed_subject = NULL;
|
||||
g_autoptr(STACK_OF_X509_CRL) ret = NULL;
|
||||
|
||||
ret = Pv_X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
return g_steal_pointer(&ret);
|
||||
|
||||
/* Workaround to fix the mismatch between issuer name of the * IBM
|
||||
* signing CRLs and the IBM signing key subject name. Locality name has
|
||||
* changed from Poughkeepsie to Armonk.
|
||||
*/
|
||||
fixed_subject = x509_armonk_locality_fixup(subject);
|
||||
/* Was the locality replaced? */
|
||||
if (fixed_subject) {
|
||||
X509_NAME *tmp;
|
||||
|
||||
sk_X509_CRL_free(ret);
|
||||
ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
return g_steal_pointer(&ret);
|
||||
|
||||
/* Workaround to fix the ordering mismatch between issuer name
|
||||
* of the IBM signing CRLs and the IBM signing key subject name.
|
||||
*/
|
||||
tmp = fixed_subject;
|
||||
fixed_subject = c2b_name(fixed_subject);
|
||||
X509_NAME_free(tmp);
|
||||
sk_X509_CRL_free(ret);
|
||||
ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
return g_steal_pointer(&ret);
|
||||
X509_NAME_free(fixed_subject);
|
||||
fixed_subject = NULL;
|
||||
}
|
||||
|
||||
/* Workaround to fix the ordering mismatch between issuer name of the
|
||||
* IBM signing CRLs and the IBM signing key subject name.
|
||||
*/
|
||||
fixed_subject = c2b_name(subject);
|
||||
sk_X509_CRL_free(ret);
|
||||
ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
return g_steal_pointer(&ret);
|
||||
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, _("no CRL found"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Given a certificate @cert try to find valid revocation lists in @ctx. If no
|
||||
* valid CRL was found NULL is returned.
|
||||
*/
|
||||
@@ -927,20 +948,9 @@ STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
if (!ret) {
|
||||
/* Workaround to fix the mismatch between issuer name of the
|
||||
* IBM Z signing CRLs and the IBM Z signing key subject name.
|
||||
*/
|
||||
g_autoptr(X509_NAME) broken_subject = c2b_name(subject);
|
||||
|
||||
ret = X509_STORE_CTX_get1_crls(ctx, broken_subject);
|
||||
if (!ret) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL,
|
||||
_("no CRL found"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
ret = quirk_X509_STORE_ctx_get1_crls(ctx, subject, err);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
/* Filter out non-valid CRLs for @cert */
|
||||
for (gint i = 0; i < sk_X509_CRL_num(ret); i++) {
|
||||
@@ -1328,32 +1338,14 @@ gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
|
||||
/* It's almost the same as X509_check_issed from OpenSSL does except that we
|
||||
* don't check the key usage of the potential issuer. This means we check:
|
||||
* 1. issuer_name(cert) == subject_name(issuer)
|
||||
* 2. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
* 3. Check that the cert algrithm matches the subject algorithm
|
||||
* 4. Verify the signature of certificate @cert is using the public key of
|
||||
* 1. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
* 2. Check that the cert algrithm matches the subject algorithm
|
||||
* 3. Verify the signature of certificate @cert is using the public key of
|
||||
* @issuer.
|
||||
*/
|
||||
static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err)
|
||||
{
|
||||
const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
|
||||
const X509_NAME *cert_issuer = X509_get_issuer_name(cert);
|
||||
AUTHORITY_KEYID *akid = NULL;
|
||||
|
||||
/* We cannot use X509_NAME_cmp() because it considers the order of the
|
||||
* X509_NAME_Entries.
|
||||
*/
|
||||
if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) {
|
||||
g_autofree char *issuer_subject_str =
|
||||
X509_NAME_oneline(issuer_subject, NULL, 0);
|
||||
g_autofree char *cert_issuer_str =
|
||||
X509_NAME_oneline(cert_issuer, NULL, 0);
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
||||
_("Subject issuer mismatch:\n'%s'\n'%s'"),
|
||||
issuer_subject_str, cert_issuer_str);
|
||||
return -1;
|
||||
}
|
||||
g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL);
|
||||
if (akid && X509_check_akid(issuer, akid) != X509_V_OK) {
|
||||
@@ -1834,14 +1826,12 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
g_assert(out_len >= 0);
|
||||
|
||||
num_bytes_written = BIO_write(b_out, out_buf, out_len);
|
||||
if (num_bytes_written < 0) {
|
||||
if (num_bytes_written != out_len) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to write"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(num_bytes_written == out_len);
|
||||
|
||||
tmp_size_out += (guint)num_bytes_written;
|
||||
|
||||
/* Set new tweak value. Please keep in mind that the
|
||||
|
||||
@@ -75,6 +75,7 @@ void x509_pair_free(x509_pair *pair);
|
||||
/* Register auto cleanup functions */
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
|
||||
|
||||
@@ -404,11 +404,11 @@ s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_co
|
||||
s64 component1, component2, smt_us;
|
||||
double smt_factor = g.o.smt_factor;
|
||||
|
||||
component1 = G0(thread_per_core * core_us - thr_us);
|
||||
component1 = thread_per_core * core_us - thr_us;
|
||||
if (thread_per_core > 1)
|
||||
component1 /= smt_factor;
|
||||
component2 = G0(thr_us - core_us);
|
||||
smt_us = component1 + component2 + mgm_us;
|
||||
component2 = thr_us - core_us;
|
||||
smt_us = G0(component1 + component2 + mgm_us);
|
||||
|
||||
return smt_us;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,19 @@
|
||||
#define STAGE2_DESC _AC(0x78, UL)
|
||||
#define STAGE2_ENTRY _AC(0x2018, UL)
|
||||
#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL)
|
||||
#define ECKD2DUMP_SV_HEAP_ADDRESS _AC(0xb000, UL)
|
||||
#define STAGE2_HEAP_SIZE _AC(0x3000, UL)
|
||||
#define STAGE2_STACK_ADDRESS _AC(0xe400, UL)
|
||||
#define STAGE2_STACK_SIZE _AC(0x1c00, UL)
|
||||
#define ECKD2DUMP_SV_STACK_ADDRESS _AC(0xe000, UL)
|
||||
#define ECKD2DUMP_SV_STACK_SIZE _AC(0x2000, UL)
|
||||
#define STAGE2_MAX_SIZE _AC(0x3000, UL)
|
||||
|
||||
#define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL)
|
||||
#define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL)
|
||||
|
||||
#define STAGE3_ENTRY _AC(0xa000, UL)
|
||||
|
||||
#define STAGE2_LOAD_ADDRESS _AC(0x2000, UL)
|
||||
|
||||
@@ -24,11 +24,25 @@
|
||||
#define OS_INFO_VMCOREINFO 0
|
||||
#define OS_INFO_REIPL_BLOCK 1
|
||||
#define OS_INFO_FLAGS_ENTRY 2
|
||||
#define OS_INFO_RESERVED 3
|
||||
#define OS_INFO_IDENTITY_BASE 4
|
||||
#define OS_INFO_KASLR_OFFSET 5
|
||||
#define OS_INFO_KASLR_OFF_PHYS 6
|
||||
#define OS_INFO_VMEMMAP 7
|
||||
#define OS_INFO_AMODE31_START 8
|
||||
#define OS_INFO_AMODE31_END 9
|
||||
#define OS_INFO_IMAGE_START 10
|
||||
#define OS_INFO_IMAGE_END 11
|
||||
#define OS_INFO_IMAGE_PHYS 12
|
||||
#define OS_INFO_MAX 13
|
||||
|
||||
#define OS_INFO_FLAG_REIPL_CLEAR (1UL << 0)
|
||||
|
||||
struct os_info_entry {
|
||||
uint64_t addr;
|
||||
union {
|
||||
uint64_t addr;
|
||||
uint64_t val;
|
||||
};
|
||||
uint64_t size;
|
||||
uint32_t csum;
|
||||
} __packed;
|
||||
@@ -40,8 +54,8 @@ struct os_info {
|
||||
uint16_t version_minor;
|
||||
uint64_t crashkernel_addr;
|
||||
uint64_t crashkernel_size;
|
||||
struct os_info_entry entry[3];
|
||||
uint8_t reserved[4004];
|
||||
struct os_info_entry entry[OS_INFO_MAX];
|
||||
uint8_t reserved[3804];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define STACK_FRAME_OVERHEAD _AC(160, U)
|
||||
|
||||
/* Facilities */
|
||||
#define DFLTCC_FACILITY _AC(151, U)
|
||||
#define UNPACK_FACILITY _AC(161, U)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
145
include/dump/s390_dump.h
Normal file
145
include/dump/s390_dump.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* s390 related definitions and functions.
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2023
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef S390_DUMP_H
|
||||
#define S390_DUMP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/page.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/*
|
||||
* S390 dump format defines
|
||||
*/
|
||||
#define DF_S390_MAGIC 0xa8190173618f23fdULL
|
||||
#define DF_S390_MAGIC_EXT 0xa8190173618f23feULL
|
||||
#define DF_S390_HDR_SIZE 0x1000
|
||||
#define DF_S390_EM_SIZE 16
|
||||
#define DF_S390_EM_MAGIC 0x44554d505f454e44ULL
|
||||
#define DF_S390_EM_STR "DUMP_END"
|
||||
#define DF_S390_CPU_MAX 512
|
||||
#define DF_S390_MAGIC_BLK_ECKD 3
|
||||
#define DF_S390_DUMPER_MAGIC_SIZE 7
|
||||
#define DF_S390_DUMPER_MAGIC32 "ZECKD31"
|
||||
#define DF_S390_DUMPER_MAGIC64 "ZECKD64"
|
||||
#define DF_S390_DUMPER_MAGIC_EXT "XECKD64"
|
||||
#define DF_S390_DUMPER_MAGIC32_FBA "ZDFBA31"
|
||||
#define DF_S390_DUMPER_MAGIC64_FBA "ZDFBA64"
|
||||
#define DF_S390_DUMPER_MAGIC_FBA_EXT "XDFBA64"
|
||||
#define DF_S390_DUMPER_MAGIC_MV "ZMULT64"
|
||||
#define DF_S390_DUMPER_MAGIC_MV_EXT "XMULT64"
|
||||
#define OLD_DUMPER_HEX_INSTR1 "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */
|
||||
#define OLD_DUMPER_HEX_INSTR2 "\x0d\xd0" /* BASR 13,0 */
|
||||
|
||||
/*
|
||||
* Architecture of dumped system
|
||||
*/
|
||||
enum df_s390_arch {
|
||||
DF_S390_ARCH_32 = 1,
|
||||
DF_S390_ARCH_64 = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* zipl parameters passed at tail of dump tools
|
||||
*/
|
||||
struct stage2dump_parm_tail {
|
||||
char reserved[6];
|
||||
uint8_t no_compress;
|
||||
uint8_t mvdump_force;
|
||||
uint64_t mem_upper_limit;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* s390 dump header format
|
||||
*/
|
||||
struct df_s390_hdr {
|
||||
uint64_t magic; /* 0x000 */
|
||||
uint32_t version; /* 0x008 */
|
||||
uint32_t hdr_size; /* 0x00c */
|
||||
uint32_t dump_level; /* 0x010 */
|
||||
uint32_t page_size; /* 0x014 */
|
||||
uint64_t mem_size; /* 0x018 */
|
||||
uint64_t mem_start; /* 0x020 */
|
||||
uint64_t mem_end; /* 0x028 */
|
||||
uint32_t num_pages; /* 0x030 */
|
||||
uint32_t pad; /* 0x034 */
|
||||
uint64_t tod; /* 0x038 */
|
||||
uint64_t cpu_id; /* 0x040 */
|
||||
uint32_t arch; /* 0x048 */
|
||||
uint32_t volnr; /* 0x04c */
|
||||
uint32_t build_arch; /* 0x050 */
|
||||
uint64_t mem_size_real; /* 0x054 */
|
||||
uint8_t mvdump; /* 0x05c */
|
||||
uint16_t cpu_cnt; /* 0x05d */
|
||||
uint16_t real_cpu_cnt; /* 0x05f */
|
||||
uint8_t zlib_version_s390; /* 0x061 */
|
||||
uint32_t zlib_entry_size; /* 0x062 */
|
||||
uint8_t end_pad1[0x200 - 0x066]; /* 0x066 */
|
||||
uint64_t mvdump_sign; /* 0x200 */
|
||||
uint64_t mvdump_zipl_time; /* 0x208 */
|
||||
uint8_t end_pad2[0x800 - 0x210]; /* 0x210 */
|
||||
uint32_t lc_vec[DF_S390_CPU_MAX]; /* 0x800 */
|
||||
} __packed __aligned(16);
|
||||
|
||||
/*
|
||||
* End marker: Should be at the end of every valid s390 crash dump
|
||||
*/
|
||||
struct df_s390_em {
|
||||
union {
|
||||
uint64_t magic;
|
||||
char str[8];
|
||||
};
|
||||
uint64_t tod;
|
||||
} __packed __aligned(16);
|
||||
|
||||
/*
|
||||
* Dump segment header
|
||||
*/
|
||||
struct df_s390_dump_segm_hdr {
|
||||
union {
|
||||
struct {
|
||||
uint64_t start; /* 0x000 */
|
||||
uint64_t len; /* 0x008 */
|
||||
uint64_t stop_marker; /* 0x010 */
|
||||
/* Size in blocks of compressed dump segment written to disk */
|
||||
uint32_t size_on_disk; /* 0x018 */
|
||||
uint8_t reserved_pad[0x30 - 0x1c]; /* 0x01c */
|
||||
/*
|
||||
* Number of compressed entries in this dump segment (up to
|
||||
* 1011 entries)
|
||||
*/
|
||||
uint32_t entry_count; /* 0x030 */
|
||||
/*
|
||||
* Offsets in blocks to compressed entries written to disk
|
||||
* from the start of the dump segment.
|
||||
* High-order bit is set if the entry has been written
|
||||
* uncompressed.
|
||||
*/
|
||||
uint32_t entry_offset[]; /* 0x034 */
|
||||
} __packed;
|
||||
uint8_t padding[PAGE_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
/* Data compression granularity (size of input data chunk for zlib deflate) */
|
||||
#define DUMP_SEGM_ZLIB_ENTSIZE (1 * MIB)
|
||||
/* Maximum number of compressed entries in one dump segment */
|
||||
#define DUMP_SEGM_ZLIB_MAXENTS ((sizeof(struct df_s390_dump_segm_hdr) \
|
||||
- offsetof(struct df_s390_dump_segm_hdr, entry_offset)) \
|
||||
/ sizeof(uint32_t))
|
||||
/*
|
||||
* Maximum length of compressed dump segment considering the size of
|
||||
* a single input chunk
|
||||
*/
|
||||
#define DUMP_SEGM_ZLIB_MAXLEN (DUMP_SEGM_ZLIB_MAXENTS * DUMP_SEGM_ZLIB_ENTSIZE)
|
||||
/* Bitmask to mark uncompressed chunks */
|
||||
#define DUMP_SEGM_ENTRY_UNCOMPRESSED 0x80000000
|
||||
|
||||
#endif /* S390_DUMP_H */
|
||||
@@ -21,7 +21,9 @@
|
||||
#define AP_UDEV_FILE "/etc/udev/rules.d/41-ap.rules"
|
||||
#define AP_LOCKFILE "/run/lock/s390apconfig.lock"
|
||||
|
||||
#define AP_LOCK_RETRIES 15
|
||||
#define AP_LOCK_RETRIES 3000
|
||||
#define AP_LOCK_DELAY_US 30000 /* wait at least 30ms between lock retries */
|
||||
#define AP_LOCK_VARIANCE_US 3000 /* or as much as 33ms */
|
||||
|
||||
/* apmask and aqmask are each represented as 67 character strings with:
|
||||
* '0x' leading characters
|
||||
@@ -89,6 +91,7 @@ void ap_list_remove_all(struct util_list *list);
|
||||
/* Lock Functions */
|
||||
int ap_get_lock(void);
|
||||
int ap_get_lock_callout(void);
|
||||
int ap_try_lock_callout(void);
|
||||
int ap_release_lock(void);
|
||||
int ap_release_lock_callout(void);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ enum util_arch_machine_type {
|
||||
UTIL_ARCH_MACHINE_TYPE_Z15 = 8561,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z15_T02 = 8562,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z16 = 3931,
|
||||
UTIL_ARCH_MACHINE_TYPE_Z16_A02 = 3932,
|
||||
};
|
||||
|
||||
int util_arch_machine_type(void);
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "zt_common.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
void util_hexdump(FILE *fh, const char *tag, const void *data, int cnt);
|
||||
void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group,
|
||||
@@ -37,4 +40,30 @@ static inline void util_ptr_vec_free(void **ptr_vec, int count)
|
||||
free(ptr_vec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand size of dynamic array (element_t *) by one element
|
||||
*
|
||||
* @param[in,out] array Pointer to array (element_t **)
|
||||
* @param[in,out] num Pointer to integer containing number of elements
|
||||
*/
|
||||
#define util_expand_array(array, num) \
|
||||
do { \
|
||||
unsigned int __size = sizeof(*(*(array))); \
|
||||
*(array) = util_realloc(*(array), ++(*(num)) * __size); \
|
||||
memset(&((*(array))[*(num) - 1]), 0, __size); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Append one element to dynamic array (element_t *)
|
||||
*
|
||||
* @param[in,out] array Pointer to array (element_t **)
|
||||
* @param[in,out] num Pointer to integer containing number of elements
|
||||
* @param[in] element Element to add (element_t)
|
||||
*/
|
||||
#define util_add_array(array, num, element) \
|
||||
do { \
|
||||
util_expand_array(array, num); \
|
||||
(*(array))[*(num) - 1] = (element) ; \
|
||||
} while (0)
|
||||
|
||||
#endif /* LIB_UTIL_BASE_H */
|
||||
|
||||
235
include/lib/util_fmt.h
Normal file
235
include/lib/util_fmt.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* util_fmt - Format structured key-value data as JSON, text pairs, or CSV
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
* This module provides helper functions for converting structured key-value
|
||||
* data into different output formats.
|
||||
*
|
||||
* Benefits:
|
||||
* - Output format can be dynamically configured at run-time
|
||||
* - Callers do not need to add extra code for each output format
|
||||
* - Some format-specific requirements such as quoting, indentation, and
|
||||
* comma-placement are automated
|
||||
*
|
||||
* Basic API calling sequence:
|
||||
*
|
||||
* util_fmt_init() => Select output format
|
||||
* util_fmt_obj_start() => Start a new object or list
|
||||
* util_fmt_pair() => Emit a key-value pair
|
||||
* util_fmt_obj_end() => End the latest object or list
|
||||
* util_fmt_exit() => Cleanup
|
||||
*
|
||||
* Note:
|
||||
* - Supported data elements are objects, lists and key-value pairs (mappings)
|
||||
* - Scalars are only supported as part of a mapping
|
||||
* - For CSV output and key filtering, mapping keys must be unique - this can
|
||||
* be achieved either by choosing unique key names or by including object
|
||||
* names via the FMT_PREFIX flag
|
||||
* - For CSV output, at least one object or list with the FMT_ROW flag must be
|
||||
* emitted
|
||||
* - Common tool-specific meta-information such as API-level, tool version,
|
||||
* etc. is automatically added to the output
|
||||
*/
|
||||
|
||||
#ifndef LIB_UTIL_FMT_H
|
||||
#define LIB_UTIL_FMT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Flag value for default behavior (all flag types). */
|
||||
#define FMT_DEFAULT 0
|
||||
|
||||
/* Names of supported output format types. */
|
||||
#define FMT_TYPE_NAMES "json json-seq pairs csv"
|
||||
|
||||
/**
|
||||
* enum util_fmt_t - Output format types.
|
||||
* @FMT_JSON: JavaScript Object Notation output data structure
|
||||
* @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464
|
||||
* @FMT_PAIRS: Textual key=value pairs
|
||||
* @FMT_CSV: Comma-separated-values output
|
||||
*
|
||||
* Use these types with util_fmt_init() to control the output format.
|
||||
*/
|
||||
enum util_fmt_t {
|
||||
FMT_JSON,
|
||||
FMT_JSONSEQ,
|
||||
FMT_PAIRS,
|
||||
FMT_CSV,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum util_fmt_flags_t - Format control flags.
|
||||
* @FMT_NOPREFIX: (pairs) Remove object hierarchy prefix from keys
|
||||
* @FMT_KEEPINVAL: (all) Print mappings even if value is marked as invalid
|
||||
* Values will be replaced with null (JSON) or an empty
|
||||
* string
|
||||
* @FMT_QUOTEALL: (all) Add quotes to all mapping values
|
||||
* @FMT_FILTER: (all) Ignore keys not announced via util_fmt_add_key()
|
||||
* @FMT_HANDLEINT: (json) Ensure correct JSON closure when interrupted
|
||||
* @FMT_NOMETA: (all) Do not emit tool meta-data
|
||||
* @FMT_WARN: (all) Warn about incorrect API usage
|
||||
*
|
||||
* Use these flags with util_fmt_init() to control generic aspects.
|
||||
*/
|
||||
enum util_fmt_flags_t {
|
||||
FMT_NOPREFIX = (1 << 0),
|
||||
FMT_KEEPINVAL = (1 << 1),
|
||||
FMT_QUOTEALL = (1 << 2),
|
||||
FMT_FILTER = (1 << 3),
|
||||
FMT_HANDLEINT = (1 << 4),
|
||||
FMT_NOMETA = (1 << 5),
|
||||
FMT_WARN = (1 << 6),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum util_fmt_oflags_t - Object flags.
|
||||
* @FMT_LIST: (all) Object is a list
|
||||
* @FMT_ROW: (csv) Start a new CSV row with this object
|
||||
* @FMT_PREFIX: (all) Include object name in key prefix for CSV headings
|
||||
* and filter keys
|
||||
*
|
||||
* Use these flags with util_fmt_obj_start() to control object related
|
||||
* aspects.
|
||||
*/
|
||||
enum util_fmt_oflags_t {
|
||||
FMT_LIST = (1 << 0),
|
||||
FMT_ROW = (1 << 1),
|
||||
FMT_PREFIX = (1 << 2),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum util_fmt_mflags_t - Mapping flags.
|
||||
* @FMT_QUOTE: (all) Quote value
|
||||
* @FMT_INVAL: (all) Mark value as invalid
|
||||
* @FMT_PERSIST: (csv) Keep value across CSV rows until overwritten
|
||||
*
|
||||
* Use these flags with util_fmt_pair() to control mapping related aspects.
|
||||
*/
|
||||
enum util_fmt_mflags_t {
|
||||
FMT_QUOTE = (1 << 0),
|
||||
FMT_INVAL = (1 << 1),
|
||||
FMT_PERSIST = (1 << 2),
|
||||
};
|
||||
|
||||
/**
|
||||
* util_fmt_init() - Initialize output formatter.
|
||||
* @fd : Output file descriptor
|
||||
* @type : Output format type
|
||||
* @flags: Formatting parameters
|
||||
* @api_level: Output format level indicator
|
||||
*
|
||||
* Prepare for writing formatted output with the given @type to @fd. Additional
|
||||
* @flags can be specified to control certain output aspects (see &enum
|
||||
* util_fmt_flags_t).
|
||||
*
|
||||
* @api_level represents an application-specific output format version number:
|
||||
* this number starts at 1 and must be increased whenever an incompatible format
|
||||
* change is introduced, e.g. when a non-optional object or mapping is removed
|
||||
* or used for different data.
|
||||
*/
|
||||
void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
||||
int api_level);
|
||||
|
||||
/**
|
||||
* util_fmt_exit() - Release resources used by output formatter.
|
||||
*
|
||||
* Release all resources currently in use by the output formatter.
|
||||
*/
|
||||
void util_fmt_exit(void);
|
||||
|
||||
/**
|
||||
* util_fmt_name_to_type() - Convert format name to type identifier.
|
||||
* @name: Format name
|
||||
* @type: Pointer to resulting format type identifier
|
||||
*
|
||||
* Search supported output format types for a type with associated @name. If
|
||||
* found, store resulting type identifier in @type.
|
||||
*
|
||||
* Return: %true if type is found, %false otherwise.
|
||||
*/
|
||||
bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type);
|
||||
|
||||
/**
|
||||
* util_fmt_set_indent() - Set indentation parameters.
|
||||
* @base : Base indentation level to apply to all output lines (default 0)
|
||||
* @width : Number of indentation characters per intendation level (default 2)
|
||||
* @ind_char: Indentation characters to use (default space).
|
||||
*/
|
||||
void util_fmt_set_indent(unsigned int base, unsigned int width, char ind_char);
|
||||
|
||||
/**
|
||||
* util_fmt_add_key() - Register expected mapping keys.
|
||||
* @fmt: Format string to generate key
|
||||
*
|
||||
* Register a mapping key before the associated key-value pair is emitted.
|
||||
*
|
||||
* Use this function together with format control flag @FMT_FILTER to ignore all
|
||||
* key-value pairs for which the key has not been registered. This can be
|
||||
* useful to allow for dynamically configured filtering of output based on
|
||||
* a static list of emitted mappings.
|
||||
*
|
||||
* When creating CSV output, use this function to register all column keys
|
||||
* in advance to enable a stable column list in case of rows that do not
|
||||
* provide data for all columns.
|
||||
*/
|
||||
void util_fmt_add_key(const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* util_fmt_obj_start() - Start a new data object.
|
||||
* @oflags: Flags controlling aspects of this object.
|
||||
* @fmt : Format string for generating an object name or %NULL.
|
||||
*
|
||||
* Use this function to start a new object in output data. Depending on
|
||||
* @oflags, the new object represents either a normal object or a list. @oflags
|
||||
* can also be used to indicated that an object corresponds to a new row of
|
||||
* CSV data. If @fmt is non-%NULL, the resulting name is used in a format
|
||||
* type specified way:
|
||||
*
|
||||
* Pairs:
|
||||
* - Object names are reflected as dot-separated component in the mapping
|
||||
* prefix, e.g. 'a.b.key=value'
|
||||
* - An index is generated for mappings and objects that are part of list,
|
||||
* e.g. 'a.b[1].key=value'
|
||||
* JSON:
|
||||
* - Object names are reflected as key-object mappings, e.g.
|
||||
* <name>: { }
|
||||
* - Required commas between objects and mappings are automatically generated
|
||||
* CSV:
|
||||
* - Object names and the list type flag have no effect
|
||||
* - When flag @FMT_ROW is specified, a CSV row will be emitted when
|
||||
* util_fmt_obj_end() is called for the associated object
|
||||
*/
|
||||
void util_fmt_obj_start(unsigned int oflags, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* util_fmt_obj_end() - Announce the end of the latest data object started.
|
||||
*
|
||||
* Each object started with util_fmt_obj_start() must be ended with an
|
||||
* associated util_fmt_obj_end() call.
|
||||
*/
|
||||
void util_fmt_obj_end(void);
|
||||
|
||||
/**
|
||||
* util_fmt_pair() - Emit a key-value pair.
|
||||
* @mflags: Flags controlling this pair.
|
||||
* @key : Key for this pair, excluding prefix.
|
||||
* @fmt : Format string used to generated the pair value.
|
||||
*
|
||||
* Emit a key-value pair with the specified @key and the value that results
|
||||
* from format string @fmt.
|
||||
*
|
||||
* Notes:
|
||||
* - For JSON, a mapping can only occur after util_fmt_obj_start()
|
||||
* - For CSV, each @key must be unique, either by choosing unique key names
|
||||
* or by including object names as prefix via the use of FMT_PREFIX in
|
||||
* parent objects
|
||||
*/
|
||||
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...);
|
||||
|
||||
#endif /* LIB_UTIL_FMT_H */
|
||||
@@ -127,6 +127,7 @@ do { \
|
||||
int __util_vsprintf(const char *func, const char *file, int line,
|
||||
char *str, const char *fmt, va_list ap);
|
||||
char *util_strcat_realloc(char *str1, const char *str2);
|
||||
void util_concatf(char **str1, const char *fmt, ...);
|
||||
void util_str_toupper(char *str);
|
||||
|
||||
char *util_strstrip(char *s);
|
||||
|
||||
@@ -18,9 +18,15 @@
|
||||
#define UTIL_LOCKFILE_ERR 4 /* Other, unexpected error conditions */
|
||||
|
||||
int util_lockfile_lock(char *lockfile, int retries);
|
||||
int util_lockfile_lock_cw(char *lockfile, int retries, unsigned int waitinc,
|
||||
unsigned int maxwait);
|
||||
int util_lockfile_parent_lock(char *lockfile, int retries);
|
||||
int util_lockfile_parent_lock_cw(char *lockfile, int retries,
|
||||
unsigned int waitinc, unsigned int maxwait);
|
||||
|
||||
int util_lockfile_release(char *lockfile);
|
||||
int util_lockfile_parent_release(char *lockfile);
|
||||
|
||||
int util_lockfile_peek_owner(char *lockfile, int *pid);
|
||||
|
||||
#endif /** LIB_UTIL_LOCKFILE_H @} */
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#define LV_COMPAT_CYL 0xFFFE
|
||||
|
||||
#define VTOC_ERROR "VTOC error:"
|
||||
#define MAX_VTOC_ENTRIES 9 /* max number of VTOC labels for cdl formatted DASD */
|
||||
|
||||
typedef struct ttr
|
||||
{
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
/*
|
||||
* Certificate functions and definitions.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIBPV_CERT_H
|
||||
#define LIBPV_CERT_H
|
||||
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "libpv/common.h"
|
||||
|
||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
||||
#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6
|
||||
|
||||
/* Minimum security level for the keys/certificates used to establish a chain of
|
||||
* trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html
|
||||
* for details).
|
||||
*/
|
||||
#define PV_CERTS_SECURITY_LEVEL 2
|
||||
|
||||
/** pv_cert_init:
|
||||
*
|
||||
* Should not be called by user.
|
||||
* Use pv_init() instead which
|
||||
* calls this function during creation.
|
||||
*
|
||||
* Sets up data structures for caching CRLs.
|
||||
*/
|
||||
void pv_cert_init(void);
|
||||
|
||||
/** pv_cert_cleanup:
|
||||
*
|
||||
* Should not be called by user.
|
||||
* Use pv_cleanup() instead which
|
||||
* calls this function during creation.
|
||||
*
|
||||
* Cleans up data structures for caching CRLs.
|
||||
*/
|
||||
void pv_cert_cleanup(void);
|
||||
|
||||
#define PV_CERT_ERROR g_quark_from_static_string("pv-cert-error-quark")
|
||||
typedef enum {
|
||||
PV_CERT_ERROR_CERT_REVOKED,
|
||||
PV_CERT_ERROR_CERT_SIGNATURE_INVALID,
|
||||
PV_CERT_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
||||
PV_CERT_ERROR_CRL_DOWNLOAD_FAILED,
|
||||
PV_CERT_ERROR_CRL_SIGNATURE_INVALID,
|
||||
PV_CERT_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
||||
PV_CERT_ERROR_FAILED_DOWNLOAD_CRL,
|
||||
PV_CERT_ERROR_INTERNAL,
|
||||
PV_CERT_ERROR_INVALID_PARM,
|
||||
PV_CERT_ERROR_INVALID_SIGNATURE_ALGORITHM,
|
||||
PV_CERT_ERROR_INVALID_VALIDITY_PERIOD,
|
||||
PV_CERT_ERROR_LOAD_CRL,
|
||||
PV_CERT_ERROR_LOAD_DEFAULT_CA,
|
||||
PV_CERT_ERROR_LOAD_ROOT_CA,
|
||||
PV_CERT_ERROR_MALFORMED_CERTIFICATE,
|
||||
PV_CERT_ERROR_MALFORMED_ROOT_CA,
|
||||
PV_CERT_ERROR_NO_CRL,
|
||||
PV_CERT_ERROR_NO_CRLDP,
|
||||
PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND,
|
||||
PV_CERT_ERROR_NO_PUBLIC_KEY,
|
||||
PV_CERT_ERROR_READ_CERTIFICATE,
|
||||
PV_CERT_ERROR_READ_CRL,
|
||||
PV_CERT_ERROR_SIGNATURE_ALGORITHM_MISMATCH,
|
||||
PV_CERT_ERROR_SKID_AKID_MISMATCH,
|
||||
PV_CERT_ERROR_VERIFICATION_FAILED,
|
||||
PV_CERT_ERROR_WRONG_CA_USED,
|
||||
} PvCertErrors;
|
||||
|
||||
/** PvX509WithPath - X509 certificate associated with a path
|
||||
*/
|
||||
typedef struct {
|
||||
X509 *cert;
|
||||
char *path;
|
||||
} PvX509WithPath;
|
||||
|
||||
/** pv_x509_with_path_new:
|
||||
*
|
||||
* @cert: X509 certificate
|
||||
* @path: Path of that X509 certificate
|
||||
*
|
||||
* Returns: (nullable) (transfer full): new X509 with path
|
||||
*/
|
||||
PvX509WithPath *pv_x509_with_path_new(X509 *cert, const char *path);
|
||||
|
||||
/** pv_x509_with_path_free:
|
||||
*
|
||||
* Frees the path and the PvX509WithPath; Decreases the refcount of the X509
|
||||
*/
|
||||
void pv_x509_with_path_free(PvX509WithPath *cert);
|
||||
|
||||
typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT;
|
||||
typedef STACK_OF(X509) STACK_OF_X509;
|
||||
typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL;
|
||||
typedef GSList PvCertWithPathList;
|
||||
|
||||
typedef struct {
|
||||
X509 *cert;
|
||||
STACK_OF_X509_CRL *crls;
|
||||
} PvX509Pair;
|
||||
|
||||
/** pv_x509_pair_new_take:
|
||||
* @cert: ptr to X509
|
||||
* @crls: ptr to CRLs
|
||||
*
|
||||
* Takes a X509 and the associated CRLs and builds a pair.
|
||||
* Both, *cert and *crls will be NULL afterwards, and owned by the pair.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): New PvX509Pair
|
||||
*/
|
||||
PvX509Pair *pv_x509_pair_new_take(X509 **cert, STACK_OF_X509_CRL **crls);
|
||||
|
||||
/** pv_x509_pair_free:
|
||||
*
|
||||
* Decreases the refcount of the X509 and crls.
|
||||
* Frees the PvX509Pair.
|
||||
*/
|
||||
void pv_x509_pair_free(PvX509Pair *pair);
|
||||
|
||||
void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack);
|
||||
void STACK_OF_X509_free(STACK_OF_X509 *stack);
|
||||
void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack);
|
||||
|
||||
/** pv_x509_from_pem_der_data:
|
||||
*
|
||||
* @data: GBytes containing the cert in PEM format
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): X509 cert
|
||||
*/
|
||||
X509 *pv_x509_from_pem_der_data(GBytes *data, GError **error);
|
||||
|
||||
/** pv_x509_get_ec_pubkey:
|
||||
*
|
||||
* @cert: X509 to extract elliptic curve pubkey from
|
||||
* @nid: numerical identifier of the expected curve
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): corresponding pupkey for the given certificate
|
||||
*/
|
||||
EVP_PKEY *pv_x509_get_ec_pubkey(X509 *cert, int nid, GError **error);
|
||||
|
||||
/** pv_get_ec_pubkeys:
|
||||
*
|
||||
* @certs_with_path: List of PvX509WithPath
|
||||
* @nid: numerical identifier of the expected curve
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): List of corresponding public keys for the given certificate
|
||||
*/
|
||||
GSList *pv_get_ec_pubkeys(PvCertWithPathList *certs_with_path, int nid, GError **error);
|
||||
|
||||
/* pv_load_certificates:
|
||||
*
|
||||
* @cert_paths: list of cert paths.
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* @cert_paths must contain at least one element, otherwise an error is
|
||||
* reported.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): List of PvX509WithPath corresponding to the given paths
|
||||
*/
|
||||
PvCertWithPathList *pv_load_certificates(char **cert_paths, GError **error);
|
||||
|
||||
/* pv_load_first_cert_from_file:
|
||||
*
|
||||
* @path: location of the x509
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* This function reads in only the first certificate and ignores all other. This
|
||||
* is only relevant for the PEM file format. For the host-key document and the
|
||||
* root CA this behavior is expected.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): PvX509WithPath corresponding to the given path
|
||||
*/
|
||||
X509 *pv_load_first_cert_from_file(const char *path, GError **error);
|
||||
|
||||
/* pv_load_first_crl_from_file:
|
||||
*
|
||||
* @path: location of the x509 CRL
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* This function reads in only the first CRL and ignores all other. This
|
||||
* is only relevant for the PEM file format.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): X509_CRL corresponding to the given path
|
||||
*/
|
||||
X509_CRL *pv_load_first_crl_from_file(const char *path, GError **error);
|
||||
|
||||
/** pv_store_setup_crl_download:
|
||||
*
|
||||
* @st: X509_STORE
|
||||
*/
|
||||
void pv_store_setup_crl_download(X509_STORE *st);
|
||||
|
||||
/** pv_load_first_crl_by_cert:
|
||||
* @cert: X509 to specify the download location.
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* This function returns the first X509_CRL found from the CRL distribution
|
||||
* points specified in @cert.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): x509 CRL corresponding to the given X509
|
||||
*/
|
||||
X509_CRL *pv_load_first_crl_by_cert(X509 *cert, GError **error);
|
||||
|
||||
/** pv_try_load_crls_by_certs:
|
||||
*
|
||||
* @certs_with_path: List of PvX509WithPath
|
||||
*
|
||||
* Returns: (nullable) (transfer full): Stack of CRLs corresponding to the given X509
|
||||
*/
|
||||
STACK_OF_X509_CRL *pv_try_load_crls_by_certs(PvCertWithPathList *certs_with_path);
|
||||
|
||||
/** pv_store_setup:
|
||||
*
|
||||
* @root_ca_path: Location of the rootCA or NULL if SystemRoot CA shall be used
|
||||
* @crl_paths: List of CRL paths or NULL
|
||||
* @cert_with_crl_paths: List of (untrusted) X509 paths
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* The untrusted certs need to be verified before actually verifying a Host Key Document.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): X509_store with given input data.
|
||||
*
|
||||
*/
|
||||
X509_STORE *pv_store_setup(char *root_ca_path, char **crl_paths, char **cert_with_crl_paths,
|
||||
GError **error);
|
||||
|
||||
/** pv_get_x509_stack:
|
||||
*
|
||||
* x509_with_path_list: list of PvX509WithPath
|
||||
*
|
||||
* Returns: (nullable) (transfer full): Stack of X509 corresponding to the given x509 with path
|
||||
*/
|
||||
STACK_OF_X509 *pv_get_x509_stack(const GSList *x509_with_path_list);
|
||||
|
||||
/** pv_init_store_ctx:
|
||||
*
|
||||
* @ctx: a uninitialized Store CTX
|
||||
* @trusted: X509_STORE with a trusted rootCA
|
||||
* @chain: untrusted X509s
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Can be called multiple times on the same context if X509_STORE_CTX_cleanup(ctx)
|
||||
* was called before.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success
|
||||
* -1 in failure
|
||||
*/
|
||||
int pv_init_store_ctx(X509_STORE_CTX *ctx, X509_STORE *trusted, STACK_OF_X509 *chain,
|
||||
GError **error) PV_NONNULL(1, 2, 3);
|
||||
|
||||
/** pv_init_store_ctx:
|
||||
*
|
||||
* @trusted: X509_STORE with a trusted rootCA
|
||||
* @chain: untrusted X509s
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): X509_STORE_CTX setup with the input data
|
||||
*/
|
||||
X509_STORE_CTX *pv_create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, GError **error)
|
||||
PV_NONNULL(1, 2);
|
||||
/** pv_remove_ibm_signing_certs:
|
||||
*
|
||||
* @certs: Stack of X509s
|
||||
*
|
||||
* Returns: (transfer full):
|
||||
* List of all IBM Z signing key certificates in @certs and remove them
|
||||
* from the chain.
|
||||
* Empty stack if no IBM Z signing key is found.
|
||||
*/
|
||||
STACK_OF_X509 *pv_remove_ibm_signing_certs(STACK_OF_X509 *certs);
|
||||
|
||||
/** pv_c2b_name:
|
||||
*
|
||||
* Workaround to fix the mismatch between issuer name of the
|
||||
* IBM Z signing CRLs and the IBM Z signing key subject name.
|
||||
*
|
||||
* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory
|
||||
* ordered. The problem is that our certificates are not consistent in the order
|
||||
* (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details).
|
||||
*
|
||||
* This function tries to reorder the name attributes such that
|
||||
* further OpenSSL calls can work with it. The caller is
|
||||
* responsible to free the returned value.
|
||||
*/
|
||||
X509_NAME *pv_c2b_name(const X509_NAME *name);
|
||||
|
||||
/** pv_verify_host_key:
|
||||
*
|
||||
* @host_key: X509 to be verified
|
||||
* @issuer_pairs: IBM signing key X509+CRLs Pairs used for verification
|
||||
* @level: Security level. see PV_CERTS_SECURITY_LEVEL
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns:
|
||||
* 0 if Host key could be verified with one of the IBM signing keys
|
||||
* -1 if no IBM signing key could verify the authenticity of the given host key
|
||||
*
|
||||
*/
|
||||
int pv_verify_host_key(X509 *host_key, GSList *issuer_pairs, int verify_flags, int level,
|
||||
GError **error);
|
||||
|
||||
/** pv_verify_cert:
|
||||
*
|
||||
* @ctx: trusted store ctx used for verification
|
||||
* @cert: X509 to be verified
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Cannot be used to verify host keys with IBM signing keys, as IBM signing
|
||||
* keys are no intermediate CAs. Use pv_verify_host_key() instead.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if @cert could be verified
|
||||
* -1 if @cert could not be verified
|
||||
*/
|
||||
int pv_verify_cert(X509_STORE_CTX *ctx, X509 *cert, GError **error) PV_NONNULL(1, 2);
|
||||
|
||||
/** pv_check_crl_valid_for_cert:
|
||||
*
|
||||
* @crl: CRL to be verified
|
||||
* @cert: Cert that probably issued the given CRL
|
||||
* @verify_flags: X509 Verification flags (X509_V_FLAG_<TYPE>)
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Verify whether a revocation list @crl is valid and is issued by @cert. For
|
||||
* this multiple steps must be done:
|
||||
*
|
||||
* 1. verify issuer of the CRL matches with the suject name of @cert
|
||||
* 2. verify the validity period of the CRL
|
||||
* 3. verify the signature of the CRL
|
||||
*
|
||||
* Important: This function does not verify whether @cert is allowed to issue a
|
||||
* CRL.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if @crl is valid and issued by @cert
|
||||
* -1 otherwise
|
||||
*/
|
||||
int pv_verify_crl(X509_CRL *crl, X509 *cert, int verify_flags, GError **error);
|
||||
|
||||
/** pv_check_chain_parameters:
|
||||
*
|
||||
* @chain: chain of trust to be validated
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Verifies that chain has at least a RootCA ans intermediate CA
|
||||
* and logs the used ROD CA subject
|
||||
*
|
||||
* Returns:
|
||||
* 0 @chain is valid
|
||||
* -1 otherwise
|
||||
*/
|
||||
int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error);
|
||||
|
||||
/** pv_store_set_verify_param:
|
||||
*
|
||||
* @store: X509_STORE to set parameters
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success
|
||||
* -1 on failure
|
||||
*/
|
||||
int pv_store_set_verify_param(X509_STORE *store, GError **error);
|
||||
|
||||
/** pv_store_ctx_find_valid_crls:
|
||||
*
|
||||
* @ctx: STORE_CTX for searching CRLs
|
||||
* @cert: X509 to match CRLs aggainst
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): STACK of CRLs related to given @crl fin @ctx
|
||||
*/
|
||||
STACK_OF_X509_CRL *pv_store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, GError **error)
|
||||
PV_NONNULL(1, 2);
|
||||
|
||||
/** pv_verify_host_key_doc:
|
||||
*
|
||||
* @host_key_certs_with_path: X509s to be verified
|
||||
* @trusted. X509_STORE with a rusted RootCA
|
||||
* @untrusted_certs: STACK OF untrusted X509s
|
||||
* @online: true if CRLs shall be downloaded
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns:
|
||||
* 0 if all given HKDs could be verified using the chain of trust.
|
||||
* -1 otherwise
|
||||
*/
|
||||
int pv_verify_host_key_doc(PvCertWithPathList *host_key_certs_with_path, X509_STORE *trusted,
|
||||
STACK_OF_X509 *untrusted_certs, gboolean online, GError **error)
|
||||
PV_NONNULL(1, 2, 3);
|
||||
|
||||
/** pv_verify_host_key_docs_by_path:
|
||||
*
|
||||
* @host_key_paths: locations of X509 to be verified
|
||||
* @optional_root_ca_path: rootCA location or NULL if Default shall be used
|
||||
* @optional_crl_paths: locations of CRLs or NULL
|
||||
* @untrusted_cert_paths: locations of IntermediateCAs including the IBM signing key
|
||||
* @online: true if CRLs shall be downloaded
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns:
|
||||
* 0 if all given HKDs could be verfied using the chain of trust.
|
||||
* -1 otherwise
|
||||
*/
|
||||
int pv_verify_host_key_docs_by_path(char **host_key_paths, char *optional_root_ca_path,
|
||||
char **optional_crl_paths, char **untrusted_cert_paths,
|
||||
gboolean online, GError **error) PV_NONNULL(1, 4);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvX509WithPath, pv_x509_with_path_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvX509Pair, pv_x509_pair_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free)
|
||||
|
||||
#endif /* LIBPV_CERT_H */
|
||||
@@ -14,22 +14,6 @@
|
||||
* the glib version is supported
|
||||
*/
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "libpv/openssl-compat.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
/** pv_init:
|
||||
*
|
||||
* Must be called before any libpv call.
|
||||
*/
|
||||
int pv_init(void);
|
||||
|
||||
/** pv_cleanup:
|
||||
*
|
||||
* Must be called when done with using libpv.
|
||||
*/
|
||||
void pv_cleanup(void);
|
||||
|
||||
#endif /* LIBPV_COMMON_H */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "libpv/common.h"
|
||||
#define PV_NONNULL(...)
|
||||
|
||||
typedef struct pv_cipher_parms {
|
||||
const EVP_CIPHER *cipher;
|
||||
@@ -26,17 +27,6 @@ typedef struct pv_cipher_parms {
|
||||
};
|
||||
} PvCipherParms;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t x[80];
|
||||
uint8_t y[80];
|
||||
};
|
||||
uint8_t data[160];
|
||||
} PvEcdhPubKey;
|
||||
G_STATIC_ASSERT(sizeof(PvEcdhPubKey) == 160);
|
||||
|
||||
typedef GSList PvEvpKeyList;
|
||||
|
||||
enum PvCryptoMode {
|
||||
PV_ENCRYPT,
|
||||
PV_DECRYPT,
|
||||
@@ -61,42 +51,6 @@ char *pv_get_openssl_errors(void);
|
||||
*/
|
||||
int pv_BIO_reset(BIO *b);
|
||||
|
||||
/**
|
||||
* pv_generate_rand_data:
|
||||
* @size: number of generated random bytes using a crypographically secure pseudo random generator
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Creates a new #GBytes with @size random bytes using a cryptographically
|
||||
* secure pseudo random generator.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error
|
||||
*/
|
||||
GBytes *pv_generate_rand_data(size_t size, GError **error);
|
||||
|
||||
/**
|
||||
* pv_generate_key:
|
||||
* @cipher: specifies the OpenSSL cipher for which a cryptographically secure key should be generated
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Creates a random key for @cipher using a cryptographically secure pseudo
|
||||
* random generator.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error
|
||||
*/
|
||||
GBytes *pv_generate_key(const EVP_CIPHER *cipher, GError **error) PV_NONNULL(1);
|
||||
|
||||
/**
|
||||
* pv_generate_iv:
|
||||
* @cipher: specifies the OpenSSL cipher for which a cryptographically secure IV should be generated
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Creates a random IV for @cipher using a cryptographically secure pseudo
|
||||
* random generator.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error
|
||||
*/
|
||||
GBytes *pv_generate_iv(const EVP_CIPHER *cipher, GError **error) PV_NONNULL(1);
|
||||
|
||||
/* Symmetric en/decryption functions */
|
||||
|
||||
/**
|
||||
@@ -135,7 +89,7 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP
|
||||
* @derived_key_len: size of the output key
|
||||
* @key: input key
|
||||
* @salt: salt for the extraction
|
||||
* @info: infor for the expansion
|
||||
* @info: info for the expansion
|
||||
* @md: EVP mode of operation
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
@@ -147,54 +101,15 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP
|
||||
GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info,
|
||||
const EVP_MD *md, GError **error) PV_NONNULL(2, 3, 4, 5);
|
||||
|
||||
/** pv_generate_ec_key:
|
||||
*
|
||||
* @nid: Numerical identifier of the curve
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): new random key based on the given curve
|
||||
*/
|
||||
EVP_PKEY *pv_generate_ec_key(int nid, GError **error);
|
||||
|
||||
/** pv_evp_pkey_to_ecdh_pub_key:
|
||||
*
|
||||
* @key: input key in EVP_PKEY format
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: the public part of the input @key in ECDH format.
|
||||
*/
|
||||
PvEcdhPubKey *pv_evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **error) PV_NONNULL(1);
|
||||
|
||||
/** pv_derive_exchange_key:
|
||||
* @cust: Customer Key
|
||||
* @host: Host key
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): Shared Secret of @cust and @host
|
||||
*/
|
||||
GBytes *pv_derive_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **error) PV_NONNULL(1, 2);
|
||||
|
||||
GQuark pv_crypto_error_quark(void);
|
||||
#define PV_CRYPTO_ERROR pv_crypto_error_quark()
|
||||
typedef enum {
|
||||
PV_CRYPTO_ERROR_DERIVE,
|
||||
PV_CRYPTO_ERROR_HKDF_FAIL,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
PV_CRYPTO_ERROR_INVALID_KEY_SIZE,
|
||||
PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
PV_CRYPTO_ERROR_READ_FILE,
|
||||
PV_CRYPTO_ERROR_NO_MATCH_TAG,
|
||||
} PvCryptoErrors;
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_GROUP, EC_GROUP_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_KEY, EC_KEY_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_POINT, EC_POINT_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Libcurl utils
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIBPV_CURL_H
|
||||
#define LIBPV_CURL_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "libpv/common.h"
|
||||
|
||||
#define CRL_DOWNLOAD_TIMEOUT_MS 3000
|
||||
#define CRL_DOWNLOAD_MAX_SIZE 0x100000
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup)
|
||||
|
||||
/** curl_download:
|
||||
* @url: URL to specify location of data
|
||||
* @timeout_ms: time to wait until fail
|
||||
* @max_size: Maximum size of the downloaded data
|
||||
* @error: return location for a GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): Downloaded data as #GByteArray
|
||||
*/
|
||||
GByteArray *curl_download(const char *url, long timeout_ms, uint max_size, GError **err);
|
||||
|
||||
/** pv_curl_init:
|
||||
*
|
||||
* Should not be called by user.
|
||||
* Use pv_init() instead which
|
||||
* calls this function during creation.
|
||||
*/
|
||||
int pv_curl_init(void);
|
||||
|
||||
/** pv_curl_cleanup:
|
||||
*
|
||||
* Should not be called by user.
|
||||
* Use pv_cleanup() instead which
|
||||
* calls this function during creation.
|
||||
*/
|
||||
void pv_curl_cleanup(void);
|
||||
|
||||
#define PV_CURL_ERROR g_quark_from_static_string("pv-curl-error-quark")
|
||||
typedef enum {
|
||||
PV_CURL_ERROR_CURL_INIT_FAILED,
|
||||
PV_CURL_ERROR_DOWNLOAD_FAILED,
|
||||
} PvCurlErrors;
|
||||
|
||||
#endif /* LIBPV_CURL_H */
|
||||
@@ -28,8 +28,6 @@
|
||||
#include <gmodule.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libpv/macros.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \
|
||||
DO_PRAGMA(clang diagnostic push) \
|
||||
@@ -40,6 +38,8 @@
|
||||
#define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
|
||||
#define pv_wrapped_g_assert(__expr) g_assert(__expr)
|
||||
|
||||
/** pv_sec_gbytes_new_take:
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Hashing definitions.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIBPV_HASH_H
|
||||
#define LIBPV_HASH_H
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include "libpv/common.h"
|
||||
|
||||
/** pv_digest_ctx_new:
|
||||
* @md: mode of digest, e.g. #EVP_sha256()
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): a new #EVP_MD_CTX, or %NULL in case of an error
|
||||
*/
|
||||
EVP_MD_CTX *pv_digest_ctx_new(const EVP_MD *md, GError **error);
|
||||
|
||||
/** pv_digest_ctx_update:
|
||||
* @ctx: EVP_MD_CTX to add data
|
||||
* @data: #GBytes to add to the context
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Adds @data to the digest context. Can be called multiple times.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
int pv_digest_ctx_update(EVP_MD_CTX *ctx, GBytes *data, GError **error);
|
||||
|
||||
/** pv_digest_ctx_update_raw:
|
||||
* @ctx: #EVP_MD_CTX to add data
|
||||
* @buf: data to add to the context
|
||||
* @size: size of @buf
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Adds @buf to the digest context. Can be called multiple times.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
int pv_digest_ctx_update_raw(EVP_MD_CTX *ctx, const uint8_t *buf, size_t size, GError **error);
|
||||
|
||||
/** pv_digest_ctx_finalize:
|
||||
* @ctx: #EVP_MD_CTX with data to digest
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Calculates the digest of all previously added data. Do not use @ctx afterwards.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): Digest of all data added before as #GBytes, or NULL in case of error.
|
||||
*/
|
||||
GBytes *pv_digest_ctx_finalize(EVP_MD_CTX *ctx, GError **error);
|
||||
|
||||
/** pv_sha256_hash:
|
||||
* @buf: data for which a sha256 hash sould be calculated
|
||||
* @size: size of @buf
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Shorthand for initializing a sha256-digest ctx, updating, and finalizing.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): SHA256 of @buf as #GBytes, or NULL in case of error.
|
||||
*/
|
||||
GBytes *pv_sha256_hash(uint8_t *buf, size_t size, GError **error);
|
||||
|
||||
/** pv_hmac_ctx_new:
|
||||
* @key: key used for the HMAC
|
||||
* @md: mode of digest, e.g. #EVP_sha512()
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Returns: (nullable) (transfer full): New #HMAC_CTX or NULL in case of error
|
||||
*/
|
||||
HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error);
|
||||
|
||||
/** pv_hmac_ctx_update_raw:
|
||||
* @ctx: #HMAC_CTX to add data
|
||||
* @buf: data to add to the context
|
||||
* @size: size of @buf
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Adds @buf to the HMAC context. Can be called multiple times.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *data, size_t size, GError **error);
|
||||
|
||||
/** pv_hmac_ctx_update:
|
||||
* @ctx: #HMAC_CTX to add data
|
||||
* @data: #GBytes to add to the context
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Adds @data to the HMAC context. Can be called multiple times.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
|
||||
int pv_hmac_ctx_update(HMAC_CTX *ctx, GBytes *data, GError **error);
|
||||
|
||||
/** pv_hmac_ctx_finalize:
|
||||
* @ctx: #HMAC_CTX with data to digest
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Calculates the HMAC of all previously added data. Do not use @ctx afterwards.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): HMAC of all data added before as #GBytes, or NULL in case of error.
|
||||
*/
|
||||
GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error);
|
||||
|
||||
#define PV_HASH_ERROR g_quark_from_static_string("pv-crypro-error-quark")
|
||||
typedef enum {
|
||||
PV_HASH_ERROR_INTERNAL,
|
||||
} PvHashErrors;
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(HMAC_CTX, HMAC_CTX_free)
|
||||
|
||||
#endif /* LIBPV_HASH_H */
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Libpv common macro definitions.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
*/
|
||||
#ifndef LIBPV_MACROS_H
|
||||
#define LIBPV_MACROS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PV_NONNULL(...)
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
|
||||
/* Most significant bit */
|
||||
#define PV_MSB(idx) ((uint64_t)1 << (63 - (idx)))
|
||||
|
||||
#endif /* LIBPV_MACROS_H */
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* OpenSSL compatibility utils
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIBPV_OPENSSL_COMPAT_H
|
||||
#define LIBPV_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(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)
|
||||
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
#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))
|
||||
#endif
|
||||
|
||||
#endif /* LIBPV_OPENSSL_COMPAT_H */
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* PV/SE header definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIBPV_SE_HDR_H
|
||||
#define LIBPV_SE_HDR_H
|
||||
|
||||
#include "libpv/common.h"
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "boot/psw.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
/* Magic number which is used to identify the file containing the PV
|
||||
* header
|
||||
*/
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* Plaintext control flags */
|
||||
/* dumping of the configuration is allowed */
|
||||
#define PV_PCF_ALLOW_DUMPING PV_MSB(34)
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_NO_DECRYPTION PV_MSB(35)
|
||||
/* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA PV_MSB(56)
|
||||
/* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES PV_MSB(57)
|
||||
/* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC PV_MSB(58)
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
#define PV_V1_PV_HDR_MIN_SIZE \
|
||||
(sizeof(struct pv_hdr_head) + sizeof(struct pv_hdr_encrypted) + \
|
||||
sizeof(((struct pv_hdr *)0)->tag) + 1 * sizeof(struct pv_hdr_key_slot))
|
||||
#define PV_V1_PV_HDR_MAX_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
#define PV_IMAGE_ENCR_KEY_SIZE 64
|
||||
|
||||
typedef struct pv_hdr_key_slot {
|
||||
uint8_t digest_key[SHA256_DIGEST_LENGTH];
|
||||
uint8_t wrapped_key[32];
|
||||
uint8_t tag[16];
|
||||
} __packed PvHdrKeySlot;
|
||||
|
||||
typedef struct pv_hdr_opt_item {
|
||||
uint32_t otype;
|
||||
uint8_t ibk[32];
|
||||
uint8_t data[];
|
||||
} __packed PvHdrOptItem;
|
||||
|
||||
/* integrity protected data (by GCM tag), but non-encrypted */
|
||||
struct pv_hdr_head {
|
||||
uint64_t magic;
|
||||
uint32_t version;
|
||||
uint32_t phs;
|
||||
uint8_t iv[12];
|
||||
uint32_t res1;
|
||||
uint64_t nks;
|
||||
uint64_t sea;
|
||||
uint64_t nep;
|
||||
uint64_t pcf;
|
||||
PvEcdhPubKey cust_pub_key;
|
||||
uint8_t pld[SHA512_DIGEST_LENGTH];
|
||||
uint8_t ald[SHA512_DIGEST_LENGTH];
|
||||
uint8_t tld[SHA512_DIGEST_LENGTH];
|
||||
} __packed;
|
||||
|
||||
/* Must not have any padding */
|
||||
struct pv_hdr_encrypted {
|
||||
uint8_t cust_comm_key[32];
|
||||
uint8_t img_enc_key_1[PV_IMAGE_ENCR_KEY_SIZE / 2];
|
||||
uint8_t img_enc_key_2[PV_IMAGE_ENCR_KEY_SIZE / 2];
|
||||
struct psw_t psw;
|
||||
uint64_t scf;
|
||||
uint32_t noi;
|
||||
uint32_t res2;
|
||||
};
|
||||
G_STATIC_ASSERT(sizeof(struct pv_hdr_encrypted) == 32 + 32 + 32 + sizeof(struct psw_t) + 8 + 4 + 4);
|
||||
|
||||
typedef struct pv_hdr {
|
||||
struct pv_hdr_head head;
|
||||
struct pv_hdr_key_slot *slots;
|
||||
struct pv_hdr_encrypted *encrypted;
|
||||
struct pv_hdr_opt_item **optional_items;
|
||||
uint8_t tag[16];
|
||||
} PvHdr;
|
||||
|
||||
#endif /* LIBPV_SE_HDR_H */
|
||||
@@ -15,8 +15,31 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "ipl_tools.h"
|
||||
|
||||
/*
|
||||
* Look up for the device in /sys/devices/ hierarchy.
|
||||
*
|
||||
* path must be PATH_MAX large and the value will be replaced in place
|
||||
*/
|
||||
static int device_sysfs_path(const char *device, char *path, const size_t path_size)
|
||||
{
|
||||
util_assert(device != NULL, "Internal error: device is NULL");
|
||||
util_assert(path != NULL, "Internal error: path is NULL");
|
||||
util_assert(path_size == PATH_MAX, "Internal error: path_size is '%zu', but must be '%zu'",
|
||||
path_size, PATH_MAX);
|
||||
char *buf = util_path_sysfs("block/%s/device", device);
|
||||
|
||||
if (!realpath(buf, path)) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the specified device number is a valid device number
|
||||
* which can be found in the /sys/bus/ccw/drivers/dasd-eckd/
|
||||
@@ -42,6 +65,26 @@ int ccw_is_device(const char *busid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the specified device is a valid virtio subchannel device
|
||||
*/
|
||||
int ccw_is_virtio_device(const char *device)
|
||||
{
|
||||
char path[PATH_MAX] = { '\0' };
|
||||
unsigned virtio = 0;
|
||||
|
||||
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* The output has the following format:
|
||||
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
||||
*/
|
||||
if (sscanf(path, "/sys/devices/css0/%*[0-9a-f.]/%*[0-9a-f.]/virtio%u", &virtio) != 1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return CCW Bus ID (old sysfs)
|
||||
*/
|
||||
@@ -77,11 +120,9 @@ out_fclose:
|
||||
*/
|
||||
static int ccw_busid_get_sysfs_new(const char *device, char *busid)
|
||||
{
|
||||
char path[PATH_MAX], buf[4096];
|
||||
char path[PATH_MAX] = { '\0' };
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
snprintf(path, sizeof(path), "/sys/block/%s/device", device);
|
||||
if (realpath(path, buf) == NULL)
|
||||
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -89,7 +130,7 @@ static int ccw_busid_get_sysfs_new(const char *device, char *busid)
|
||||
* /sys/devices/css0/0.0.0119/0.0.3f19/block/dasda
|
||||
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
||||
*/
|
||||
if (sscanf(buf, "/sys/devices/css0/%*[0-9a-f.]/%[0-9a-f.]", busid) != 1)
|
||||
if (sscanf(path, "/sys/devices/css0/%*[0-9a-f.]/%[0-9a-f.]", busid) != 1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -391,7 +391,8 @@ static void dev_from_part_nvme(char *dev_name)
|
||||
static int set_reipl_type(const char *dev_name)
|
||||
{
|
||||
if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 ||
|
||||
strncmp(dev_name, "vd", strlen("vd")) == 0)
|
||||
strncmp(dev_name, "vd", strlen("vd")) == 0 ||
|
||||
ccw_is_virtio_device(dev_name) == 0)
|
||||
l.reipl_type = REIPL_CCW;
|
||||
else if (strncmp(dev_name, "sd", strlen("sd")) == 0)
|
||||
l.reipl_type = REIPL_FCP;
|
||||
|
||||
@@ -87,6 +87,7 @@ int nvme_is_device(char *fid_str, char *nsid_str);
|
||||
* CCW
|
||||
*/
|
||||
extern int ccw_is_device(const char *devno);
|
||||
extern int ccw_is_virtio_device(const char *device);
|
||||
extern void ccw_busid_get(const char *device, char *devno);
|
||||
|
||||
/*
|
||||
|
||||
54
libap/ap.c
54
libap/ap.c
@@ -11,12 +11,15 @@
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef HAVE_JSONC
|
||||
#include <json-c/json.h>
|
||||
@@ -698,6 +701,20 @@ void ap_list_remove_all(struct util_list *list)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int random_delay(void)
|
||||
{
|
||||
static bool libap_seed = true;
|
||||
struct timeval t;
|
||||
|
||||
if (libap_seed) {
|
||||
gettimeofday(&t, NULL);
|
||||
srand((unsigned int)((t.tv_sec + t.tv_usec) % UINT_MAX));
|
||||
libap_seed = false;
|
||||
}
|
||||
|
||||
return AP_LOCK_DELAY_US + (rand() % AP_LOCK_VARIANCE_US);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the ap config lock using this Process ID
|
||||
*
|
||||
@@ -707,7 +724,9 @@ void ap_list_remove_all(struct util_list *list)
|
||||
*/
|
||||
int ap_get_lock(void)
|
||||
{
|
||||
return util_lockfile_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
unsigned int delay = random_delay();
|
||||
|
||||
return util_lockfile_lock_cw(AP_LOCKFILE, AP_LOCK_RETRIES, delay, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -719,7 +738,38 @@ int ap_get_lock(void)
|
||||
*/
|
||||
int ap_get_lock_callout(void)
|
||||
{
|
||||
return util_lockfile_parent_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
unsigned int delay = random_delay();
|
||||
|
||||
return util_lockfile_parent_lock_cw(AP_LOCKFILE, AP_LOCK_RETRIES, delay,
|
||||
delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to acquire the ap config lock using the Parent Process ID without
|
||||
* waiting/retries. Detect if the attempt was rejected because the lock is
|
||||
* already held by the Parent Process ID.
|
||||
*
|
||||
* @retval 0 Lock acquired on behalf of parent process
|
||||
* @retval 1 Lock not obtained, already held by parent
|
||||
* @retval != 0 Lock was not obtained, other error
|
||||
*/
|
||||
int ap_try_lock_callout(void)
|
||||
{
|
||||
int pid, ppid, rc;
|
||||
|
||||
if (util_lockfile_parent_lock(AP_LOCKFILE, 0)) {
|
||||
/* Lock is already held, let's peek at the owner */
|
||||
ppid = getppid();
|
||||
rc = util_lockfile_peek_owner(AP_LOCKFILE, &pid);
|
||||
if (rc || pid != ppid) {
|
||||
/* We didn't get the lock, unknown or other owner */
|
||||
return 2;
|
||||
}
|
||||
/* Signify that the lock is already held by the caller */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,7 +55,7 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
||||
"detect-openssl-version.dep", \
|
||||
"openssl-devel version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
-I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
-I. `$(PKG_CONFIG) --cflags --libs libcrypto` -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
$(call check_dep, \
|
||||
"libekmfweb", \
|
||||
"json-c/json.h", \
|
||||
@@ -66,7 +66,7 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
|
||||
`$(PKG_CONFIG) --cflags --libs libcurl`)
|
||||
$(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
|
||||
|
||||
@@ -85,8 +85,8 @@ 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 `$(CURL_CONFIG) --cflags`
|
||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(CURL_CONFIG) --libs` -ldl
|
||||
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `$(PKG_CONFIG) --cflags json-c libcurl libcrypto libssl`
|
||||
libekmfweb.so.$(VERSION): LDLIBS = `$(PKG_CONFIG) --libs json-c libcurl libcrypto libssl` -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 $(libs)
|
||||
|
||||
@@ -51,7 +51,6 @@ detect-openssl-version.dep:
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
CURL_CONFIG ?= curl-config
|
||||
XML2_CONFIG ?= xml2-config
|
||||
|
||||
check-dep-libkmipclient: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
@@ -59,7 +58,7 @@ check-dep-libkmipclient: detect-openssl-version.dep
|
||||
"detect-openssl-version.dep", \
|
||||
"openssl-devel version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
-I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
-I. `$(PKG_CONFIG) --cflags --libs libcrypto` -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"json-c/json.h", \
|
||||
@@ -70,13 +69,13 @@ check-dep-libkmipclient: detect-openssl-version.dep
|
||||
"libxml/tree.h", \
|
||||
"libxml2-devel", \
|
||||
"HAVE_LIBXML2=0", \
|
||||
`$(XML2_CONFIG) --cflags` `$(XML2_CONFIG) --libs`)
|
||||
`$(PKG_CONFIG) --cflags --libs libxml-2.0`)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
|
||||
`$(PKG_CONFIG) --cflags --libs libcurl`)
|
||||
$(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
|
||||
|
||||
@@ -107,8 +106,8 @@ tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient
|
||||
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_CFLAGS += -fPIC `$(PKG_CONFIG) --cflags json-c libcrypto libssl libxml-2.0 libcurl`
|
||||
libkmipclient.so.$(VERSION): LDLIBS = `$(PKG_CONFIG) --libs json-c libcrypto libssl libxml-2.0 libcurl`
|
||||
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 \
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <json-c/json.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
||||
@@ -9,10 +9,7 @@ GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0)
|
||||
GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0)
|
||||
LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto)
|
||||
LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto)
|
||||
LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl)
|
||||
LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl)
|
||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS)
|
||||
|
||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
|
||||
WARNINGS := -Wall -Wextra -Wshadow \
|
||||
-Wcast-align -Wwrite-strings -Wmissing-prototypes \
|
||||
-Wmissing-declarations -Wredundant-decls -Wnested-externs \
|
||||
@@ -21,22 +18,18 @@ WARNINGS := -Wall -Wextra -Wshadow \
|
||||
-Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \
|
||||
$(NULL)
|
||||
|
||||
ALL_CFLAGS += -std=gnu11 \
|
||||
-DOPENSSL_API_COMPAT=0x10101000L \
|
||||
ALL_CFLAGS += -DOPENSSL_API_COMPAT=0x10101000L \
|
||||
$(GLIB2_CFLAGS) \
|
||||
$(LIBCRYPTO_CFLAGS) \
|
||||
$(LIBCURL_CFLAGS) \
|
||||
$(WARNINGS) \
|
||||
$(NULL)
|
||||
|
||||
BUILD_TARGETS := skip-$(LIB)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
ifneq (${HAVE_LIBCURL},0)
|
||||
BUILD_TARGETS := $(LIB)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
sources := $(wildcard *.c)
|
||||
objects := $(patsubst %.c,%.o,$(sources))
|
||||
@@ -83,9 +76,4 @@ skip-$(LIB):
|
||||
"openssl-devel / libssl-dev version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
"-I.")
|
||||
$(call check_dep, \
|
||||
"$(LIB)", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0")
|
||||
touch $@
|
||||
|
||||
1654
libpv/cert.c
1654
libpv/cert.c
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Libpv common functions.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include "libpv/common.h"
|
||||
#include "libpv/cert.h"
|
||||
#include "libpv/curl.h"
|
||||
|
||||
/* setup and tear down */
|
||||
int pv_init(void)
|
||||
{
|
||||
static size_t openssl_initalized;
|
||||
|
||||
if (g_once_init_enter(&openssl_initalized)) {
|
||||
if (OPENSSL_VERSION_NUMBER < 0x1000100fL)
|
||||
g_assert_not_reached();
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
#else
|
||||
OPENSSL_init_crypto(0, NULL);
|
||||
#endif
|
||||
|
||||
if (pv_curl_init() != 0)
|
||||
return -1;
|
||||
|
||||
pv_cert_init();
|
||||
g_once_init_leave(&openssl_initalized, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pv_cleanup(void)
|
||||
{
|
||||
pv_cert_cleanup();
|
||||
pv_curl_cleanup();
|
||||
}
|
||||
216
libpv/crypto.c
216
libpv/crypto.c
@@ -17,7 +17,6 @@
|
||||
#include "lib/zt_common.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/glib-helper.h"
|
||||
#include "libpv/hash.h"
|
||||
|
||||
char *pv_get_openssl_errors(void)
|
||||
{
|
||||
@@ -46,59 +45,6 @@ int pv_BIO_reset(BIO *b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
GBytes *pv_generate_rand_data(size_t size, GError **error)
|
||||
{
|
||||
g_autofree uint8_t *data = NULL;
|
||||
|
||||
if (size > INT_MAX) {
|
||||
g_set_error_literal(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
"Too many random data requested. Split it up");
|
||||
OPENSSL_clear_free(data, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = g_malloc(size);
|
||||
if (RAND_bytes(data, (int)size) != 1) {
|
||||
g_set_error_literal(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
"The required amount of random data is not available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pv_sec_gbytes_new_take(g_steal_pointer(&data), size);
|
||||
}
|
||||
|
||||
GBytes *pv_generate_key(const EVP_CIPHER *cipher, GError **error)
|
||||
{
|
||||
int size;
|
||||
|
||||
pv_wrapped_g_assert(cipher);
|
||||
|
||||
size = EVP_CIPHER_key_length(cipher);
|
||||
if (size <= 0) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
"Unknown cipher");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pv_generate_rand_data((guint)size, error);
|
||||
}
|
||||
|
||||
GBytes *pv_generate_iv(const EVP_CIPHER *cipher, GError **error)
|
||||
{
|
||||
int size;
|
||||
|
||||
pv_wrapped_g_assert(cipher);
|
||||
|
||||
size = EVP_CIPHER_iv_length(cipher);
|
||||
if (size <= 0) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
"Unknown cipher");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pv_generate_rand_data((guint)size, error);
|
||||
}
|
||||
|
||||
static int64_t pv_gcm_encrypt_decrypt(GBytes *input, GBytes *aad, const PvCipherParms *parms,
|
||||
GBytes **output, GBytes **tagp, enum PvCryptoMode mode,
|
||||
GError **error)
|
||||
@@ -360,168 +306,6 @@ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *
|
||||
return pv_sec_gbytes_new_take(g_steal_pointer(&derived_key), derived_key_len);
|
||||
}
|
||||
|
||||
EVP_PKEY *pv_generate_ec_key(int nid, GError **error)
|
||||
{
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
|
||||
g_autoptr(EVP_PKEY) ret = NULL;
|
||||
|
||||
g_assert(ctx);
|
||||
|
||||
if (EVP_PKEY_keygen_init(ctx) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
_("EC key could not be auto-generated"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
_("EC key could not be auto-generated"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_keygen(ctx, &ret) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
_("EC key could not be auto-generated"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* Convert a EVP_PKEY to the key format used in the PV header */
|
||||
PvEcdhPubKey *pv_evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **error)
|
||||
{
|
||||
g_autofree PvEcdhPubKey *ret = g_new0(PvEcdhPubKey, 1);
|
||||
g_autoptr(BIGNUM) pub_x_big = NULL, pub_y_big = NULL;
|
||||
g_autoptr(EC_KEY) ec_key = NULL;
|
||||
const EC_POINT *pub_key;
|
||||
const EC_GROUP *grp;
|
||||
|
||||
pv_wrapped_g_assert(key);
|
||||
|
||||
ec_key = EVP_PKEY_get1_EC_KEY(key);
|
||||
if (!ec_key) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Key has the wrong type"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pub_key = EC_KEY_get0_public_key(ec_key);
|
||||
if (!pub_key) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to get public key"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
grp = EC_KEY_get0_group(ec_key);
|
||||
if (!grp) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to get EC group"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pub_x_big = BN_new();
|
||||
if (!pub_x_big)
|
||||
g_abort();
|
||||
|
||||
pub_y_big = BN_new();
|
||||
if (!pub_y_big)
|
||||
g_abort();
|
||||
|
||||
if (EC_POINT_get_affine_coordinates_GFp(grp, pub_key, pub_x_big, pub_y_big, NULL) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Cannot convert key to internal format"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BN_bn2binpad(pub_x_big, ret->x, sizeof(ret->x)) < 0) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Cannot convert key to internal format"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BN_bn2binpad(pub_y_big, ret->y, sizeof(ret->y)) < 0) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Cannot convert key to internal format"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static GBytes *derive_key(EVP_PKEY *key1, EVP_PKEY *key2, GError **error)
|
||||
{
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||
uint8_t *data = NULL;
|
||||
size_t data_size, key_size;
|
||||
|
||||
ctx = EVP_PKEY_CTX_new(key1, NULL);
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
if (EVP_PKEY_derive_init(ctx) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_set_peer(ctx, key2) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Determine buffer length */
|
||||
if (EVP_PKEY_derive(ctx, NULL, &key_size) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_size = key_size;
|
||||
data = OPENSSL_malloc(data_size);
|
||||
if (!data)
|
||||
g_abort();
|
||||
if (EVP_PKEY_derive(ctx, data, &data_size) != 1) {
|
||||
OPENSSL_clear_free(data, data_size);
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert(data_size == key_size);
|
||||
return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size);
|
||||
}
|
||||
|
||||
GBytes *pv_derive_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **error)
|
||||
{
|
||||
const guint8 append[] = { 0x00, 0x00, 0x00, 0x01 };
|
||||
g_autoptr(GBytes) derived_key = NULL, ret = NULL;
|
||||
g_autoptr(GByteArray) der_key_ga = NULL;
|
||||
g_autofree uint8_t *raw = NULL;
|
||||
size_t raw_len;
|
||||
|
||||
pv_wrapped_g_assert(cust);
|
||||
pv_wrapped_g_assert(host);
|
||||
|
||||
derived_key = derive_key(cust, host, error);
|
||||
if (!derived_key)
|
||||
return NULL;
|
||||
|
||||
der_key_ga = g_bytes_unref_to_array(g_steal_pointer(&derived_key));
|
||||
/* ANSI X.9.63-2011: 66 bytes x with leading 7 bits and
|
||||
* concatenate 32 bit int '1'
|
||||
*/
|
||||
der_key_ga = g_byte_array_append(der_key_ga, append, sizeof(append));
|
||||
/* free GBytesArray and get underlying data */
|
||||
raw_len = der_key_ga->len;
|
||||
raw = g_byte_array_free(g_steal_pointer(&der_key_ga), FALSE);
|
||||
|
||||
ret = pv_sha256_hash(raw, raw_len, error);
|
||||
OPENSSL_cleanse(raw, raw_len);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
GQuark pv_crypto_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-crypto-error-quark");
|
||||
|
||||
116
libpv/curl.c
116
libpv/curl.c
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Libcurl utils
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "libpv/curl.h"
|
||||
|
||||
struct UserData {
|
||||
GByteArray *buffer;
|
||||
uint max_size;
|
||||
};
|
||||
|
||||
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
g_assert(userdata);
|
||||
struct UserData *data = (struct UserData *)userdata;
|
||||
GByteArray *buffer = data->buffer;
|
||||
uint64_t actual_size;
|
||||
size_t err;
|
||||
|
||||
g_assert(buffer);
|
||||
|
||||
if (!g_uint64_checked_mul(&actual_size, size, nmemb))
|
||||
g_abort();
|
||||
|
||||
/* Signal an error condition by returning a amount that differs
|
||||
* from the amount passed to the callback. This results in a
|
||||
* CURLE_WRITE_ERROR.
|
||||
*/
|
||||
err = actual_size + 1;
|
||||
|
||||
if (actual_size > G_MAXUINT)
|
||||
return err;
|
||||
|
||||
data->buffer = g_byte_array_append(buffer, (uint8_t *)ptr, (uint)actual_size);
|
||||
if (data->buffer->len > data->max_size)
|
||||
return err;
|
||||
|
||||
return actual_size;
|
||||
}
|
||||
|
||||
int pv_curl_init(void)
|
||||
{
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pv_curl_cleanup(void)
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
GByteArray *curl_download(const char *url, long timeout_ms, uint max_size, GError **err)
|
||||
{
|
||||
g_autoptr(GByteArray) ret = NULL;
|
||||
g_autoptr(CURL) handle = NULL;
|
||||
g_autofree char *agent = NULL;
|
||||
struct UserData userdata;
|
||||
CURLcode rc;
|
||||
|
||||
/* set up curl session */
|
||||
handle = curl_easy_init();
|
||||
if (!handle)
|
||||
g_abort();
|
||||
|
||||
/* follow redirection */
|
||||
rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
agent = g_strdup_printf("%s/%s", GETTEXT_PACKAGE, RELEASE_STRING);
|
||||
rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
ret = g_byte_array_new();
|
||||
userdata.buffer = ret;
|
||||
userdata.max_size = max_size;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
rc = curl_easy_setopt(handle, CURLOPT_URL, url);
|
||||
if (rc != CURLE_OK)
|
||||
goto curl_err;
|
||||
|
||||
rc = curl_easy_perform(handle);
|
||||
if (rc != CURLE_OK) {
|
||||
g_set_error(err, PV_CURL_ERROR, PV_CURL_ERROR_DOWNLOAD_FAILED,
|
||||
_("download failed: %s"), curl_easy_strerror(rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
curl_err:
|
||||
g_set_error(err, PV_CURL_ERROR, PV_CURL_ERROR_CURL_INIT_FAILED,
|
||||
_("cURL initialization failed: %s"), curl_easy_strerror(rc));
|
||||
return NULL;
|
||||
}
|
||||
153
libpv/hash.c
153
libpv/hash.c
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Hashing functions.
|
||||
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/hash.h"
|
||||
|
||||
GBytes *pv_sha256_hash(uint8_t *buf, size_t size, GError **error)
|
||||
{
|
||||
g_autoptr(EVP_MD_CTX) ctx = NULL;
|
||||
|
||||
ctx = pv_digest_ctx_new(EVP_sha256(), error);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
if (pv_digest_ctx_update_raw(ctx, buf, size, error) != 0)
|
||||
return NULL;
|
||||
|
||||
return pv_digest_ctx_finalize(ctx, error);
|
||||
}
|
||||
|
||||
EVP_MD_CTX *pv_digest_ctx_new(const EVP_MD *md, GError **error)
|
||||
{
|
||||
g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new();
|
||||
|
||||
if (!ctx) {
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
_("Hash context generation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
_("EVP_DigestInit_ex failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
int pv_digest_ctx_update_raw(EVP_MD_CTX *ctx, const uint8_t *buf, size_t size, GError **error)
|
||||
{
|
||||
if (!buf || size == 0)
|
||||
return 0;
|
||||
|
||||
if (EVP_DigestUpdate(ctx, buf, size) != 1) {
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pv_digest_ctx_update(EVP_MD_CTX *ctx, GBytes *data, GError **error)
|
||||
{
|
||||
const uint8_t *buf;
|
||||
size_t buf_size;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
buf = g_bytes_get_data((GBytes *)data, &buf_size);
|
||||
return pv_digest_ctx_update_raw(ctx, buf, buf_size, error);
|
||||
}
|
||||
|
||||
GBytes *pv_digest_ctx_finalize(EVP_MD_CTX *ctx, GError **error)
|
||||
{
|
||||
int md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
||||
g_autofree uint8_t *digest = NULL;
|
||||
unsigned int digest_size;
|
||||
|
||||
g_assert(md_size > 0);
|
||||
|
||||
digest = g_malloc0((uint)md_size);
|
||||
if (EVP_DigestFinal_ex(ctx, digest, &digest_size) != 1) {
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
_("EVP_DigestFinal_ex failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert(digest_size == (uint)md_size);
|
||||
return g_bytes_new_take(g_steal_pointer(&digest), digest_size);
|
||||
}
|
||||
|
||||
HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error)
|
||||
{
|
||||
g_autoptr(HMAC_CTX) ctx = HMAC_CTX_new();
|
||||
const uint8_t *key_data;
|
||||
size_t key_size;
|
||||
|
||||
key_data = g_bytes_get_data(key, &key_size);
|
||||
|
||||
if (HMAC_Init_ex(ctx, key_data, (int)key_size, md, NULL) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to create HMAC context: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *buf, size_t size, GError **error)
|
||||
{
|
||||
if (!buf || size == 0)
|
||||
return 0;
|
||||
|
||||
if (HMAC_Update(ctx, buf, size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to add data to HMAC context: %s", openssl_err_msg);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pv_hmac_ctx_update(HMAC_CTX *ctx, GBytes *data, GError **error)
|
||||
{
|
||||
const uint8_t *buf;
|
||||
size_t buf_size;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
buf = g_bytes_get_data((GBytes *)data, &buf_size);
|
||||
return pv_hmac_ctx_update_raw(ctx, buf, buf_size, error);
|
||||
}
|
||||
|
||||
GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error)
|
||||
{
|
||||
int md_size = EVP_MD_size(HMAC_CTX_get_md(ctx));
|
||||
g_autofree uint8_t *hmac = NULL;
|
||||
unsigned int hmac_size = 0;
|
||||
|
||||
g_assert(md_size > 0);
|
||||
|
||||
hmac = g_malloc0((unsigned int)md_size);
|
||||
|
||||
if (HMAC_Final(ctx, hmac, &hmac_size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to calculate HMAC: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_bytes_new_take(g_steal_pointer(&hmac), hmac_size);
|
||||
}
|
||||
@@ -90,10 +90,10 @@ const char *util_arch_machine_type_to_str(int type)
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1:
|
||||
return "IBM z14 ZR1";
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
return "IBM z15";
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
return "IBM z15 Model T02";
|
||||
return "IBM z15";
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||
return "IBM z16";
|
||||
default:
|
||||
return "Unknown machine type";
|
||||
@@ -111,6 +111,7 @@ unsigned long util_arch_hsa_maxsize(void)
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||
return HSA_SIZE_512M;
|
||||
default:
|
||||
return HSA_SIZE_32M;
|
||||
|
||||
@@ -27,7 +27,7 @@ void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int grp,
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (first) {
|
||||
fprintf(fh, "%*s", indent, " ");
|
||||
fprintf(fh, "%*s", indent, "");
|
||||
if (tag)
|
||||
fprintf(fh, "%s: ", tag);
|
||||
fprintf(fh, "%08x: ", i);
|
||||
|
||||
@@ -110,7 +110,8 @@ static int file_puts(const char *str, const char *path)
|
||||
}
|
||||
rc = 0;
|
||||
out_fclose:
|
||||
fclose(fp);
|
||||
if (fclose(fp))
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -311,13 +312,13 @@ int util_file_read_i(int *val, int base, const char *fmt, ...)
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%do", val);
|
||||
count = sscanf(buf, "%o", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%dd", val);
|
||||
count = sscanf(buf, "%d", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%dx", val);
|
||||
count = sscanf(buf, "%x", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
@@ -425,13 +426,13 @@ int util_file_read_ui(unsigned int *val, int base, const char *fmt, ...)
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%uo", val);
|
||||
count = sscanf(buf, "%o", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%uu", val);
|
||||
count = sscanf(buf, "%u", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%ux", val);
|
||||
count = sscanf(buf, "%x", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
|
||||
763
libutil/util_fmt.c
Normal file
763
libutil/util_fmt.c
Normal file
@@ -0,0 +1,763 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Format structured data as key-value pairs, JSON, or CSV
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
struct obj_t {
|
||||
char *name;
|
||||
bool is_list;
|
||||
bool is_row;
|
||||
bool is_prefix;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
struct key_t {
|
||||
char *name;
|
||||
bool persist;
|
||||
};
|
||||
|
||||
static struct {
|
||||
enum util_fmt_t type;
|
||||
FILE *fd;
|
||||
int fileno;
|
||||
/* Format control. */
|
||||
bool hide_prefix;
|
||||
bool hide_inval;
|
||||
bool quote_all;
|
||||
bool do_filter;
|
||||
bool do_warn;
|
||||
bool hide_meta;
|
||||
bool handle_int;
|
||||
int api_level;
|
||||
const char *nl;
|
||||
/* JSON specifics. */
|
||||
unsigned int ind_base;
|
||||
unsigned int ind_width;
|
||||
char ind_char;
|
||||
bool meta_done;
|
||||
/* CSV specifics. */
|
||||
struct util_rec *csv_rec;
|
||||
bool csv_hdr;
|
||||
bool csv_data;
|
||||
/* State. */
|
||||
unsigned int lvl;
|
||||
struct obj_t *objs;
|
||||
unsigned int num_objs;
|
||||
struct key_t *keys;
|
||||
unsigned int num_keys;
|
||||
struct sigaction old_int;
|
||||
struct sigaction old_term;
|
||||
/* Methods. */
|
||||
void (*obj_start)(struct obj_t *parent, struct obj_t *obj);
|
||||
void (*obj_end)(struct obj_t *parent, struct obj_t *obj);
|
||||
void (*map)(struct obj_t *parent, unsigned int mflags, const char *key,
|
||||
const char *val);
|
||||
void (*term)(void);
|
||||
} f;
|
||||
|
||||
#define fwarn(fmt, ...) \
|
||||
do { if (f.do_warn) warnx(fmt, ##__VA_ARGS__); } while (0)
|
||||
|
||||
/* Map format name to format ID. */
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum util_fmt_t fmt;
|
||||
} formats[] = {
|
||||
{ "json", FMT_JSON },
|
||||
{ "json-seq", FMT_JSONSEQ },
|
||||
{ "pairs", FMT_PAIRS },
|
||||
{ "csv", FMT_CSV },
|
||||
};
|
||||
|
||||
/* Signal mask for blocking INT and TERM signals. */
|
||||
static sigset_t no_int_mask;
|
||||
|
||||
bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(formats); i++) {
|
||||
if (strcasecmp(name, formats[i].name) == 0) {
|
||||
*type = formats[i].fmt;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void safe_write(const char *str)
|
||||
{
|
||||
size_t done, todo;
|
||||
ssize_t rc;
|
||||
|
||||
if (f.fileno < 0)
|
||||
return;
|
||||
for (done = 0; (todo = strlen(&str[done])) > 0; done += (size_t)rc) {
|
||||
rc = write(f.fileno, &str[done], todo);
|
||||
if (rc <= 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void _indent(unsigned int off, bool safe)
|
||||
{
|
||||
unsigned int num, i;
|
||||
|
||||
if (f.type == FMT_JSONSEQ)
|
||||
return;
|
||||
num = f.ind_base + off;
|
||||
if (f.type == FMT_JSON && f.lvl > 0)
|
||||
num += f.lvl - 1;
|
||||
for (i = 0; i < num * f.ind_width; i++) {
|
||||
if (!safe) {
|
||||
fputc(f.ind_char, f.fd);
|
||||
} else if (f.fileno >= 0) {
|
||||
if (write(f.fileno, &f.ind_char, 1) <= 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define indent(x) _indent(x, false)
|
||||
|
||||
static void obj_free(struct obj_t *obj)
|
||||
{
|
||||
free(obj->name);
|
||||
memset(obj, 0, sizeof(*obj));
|
||||
}
|
||||
|
||||
static void disable_int(sigset_t *saved)
|
||||
{
|
||||
if (f.handle_int)
|
||||
sigprocmask(SIG_BLOCK, &no_int_mask, saved);
|
||||
}
|
||||
|
||||
static void enable_int(sigset_t *saved)
|
||||
{
|
||||
if (f.handle_int) {
|
||||
/* Ensure latest updates are flushed to file descriptor. */
|
||||
fflush(f.fd);
|
||||
sigprocmask(SIG_SETMASK, saved, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void int_handler(int signum)
|
||||
{
|
||||
struct sigaction *old;
|
||||
|
||||
if (f.term)
|
||||
f.term();
|
||||
/* Re-install and call original handler. */
|
||||
old = (signum == SIGINT) ? &f.old_int : &f.old_term;
|
||||
sigaction(signum, old, NULL);
|
||||
raise(signum);
|
||||
}
|
||||
|
||||
static void setup_int_handler(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = &int_handler;
|
||||
sigaction(SIGINT, &act, &f.old_int);
|
||||
sigaction(SIGTERM, &act, &f.old_term);
|
||||
}
|
||||
|
||||
static void remove_int_handler(void)
|
||||
{
|
||||
sigaction(SIGINT, &f.old_int, NULL);
|
||||
sigaction(SIGTERM, &f.old_term, NULL);
|
||||
}
|
||||
|
||||
void util_fmt_exit(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (f.handle_int)
|
||||
remove_int_handler();
|
||||
if (f.lvl > 0)
|
||||
fwarn("%s before remaining %d util_obj_end()", __func__, f.lvl);
|
||||
for (i = 0; i < f.num_keys; i++)
|
||||
free(f.keys[i].name);
|
||||
free(f.keys);
|
||||
for (i = 0; i < f.num_objs; i++)
|
||||
obj_free(&f.objs[i]);
|
||||
free(f.objs);
|
||||
if (f.type == FMT_CSV)
|
||||
util_rec_free(f.csv_rec);
|
||||
}
|
||||
|
||||
void util_fmt_set_indent(unsigned int base, unsigned int width, char ind_char)
|
||||
{
|
||||
f.ind_base = base;
|
||||
f.ind_width = width;
|
||||
f.ind_char = ind_char;
|
||||
}
|
||||
|
||||
static unsigned int to_hex(char *str, int val, unsigned int num_digits)
|
||||
{
|
||||
int digit;
|
||||
char *c;
|
||||
|
||||
for (c = str + num_digits - 1; c >= str; c--) {
|
||||
digit = (val & 0xf);
|
||||
val >>= 4;
|
||||
*c = (char)((digit >= 10) ? digit - 10 + 'a' : digit + '0');
|
||||
}
|
||||
|
||||
return num_digits;
|
||||
}
|
||||
|
||||
static char get_escape(const char *map, char c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; map[i] && map[i + 1]; i += 2) {
|
||||
if (map[i] == c)
|
||||
return map[i + 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct quote_params {
|
||||
const char *double_chars;
|
||||
const char *esc_map;
|
||||
char hex_char;
|
||||
unsigned int hex_digits;
|
||||
unsigned int max_width_per_char;
|
||||
};
|
||||
|
||||
static char *do_quote(const char *str, const struct quote_params *p)
|
||||
{
|
||||
unsigned int from, to;
|
||||
char *q, esc, c;
|
||||
|
||||
/* Start with worst-case length assuming every char is replaced. */
|
||||
q = util_zalloc(strlen(str) * p->max_width_per_char + /* "" nul */ 3);
|
||||
to = 0;
|
||||
q[to++] = '"';
|
||||
for (from = 0; (c = str[from]); from++) {
|
||||
if (p->double_chars && strchr(p->double_chars, c)) {
|
||||
/* Escape characters by doubling them ("" in CSV). */
|
||||
q[to++] = c;
|
||||
q[to++] = c;
|
||||
} else if (p->esc_map && (esc = get_escape(p->esc_map, c))) {
|
||||
/* Escape characters with backslash + letter. */
|
||||
q[to++] = '\\';
|
||||
q[to++] = esc;
|
||||
} else if (p->hex_char && !isprint(c)) {
|
||||
/* Escape characters with backslash + hex code. */
|
||||
q[to++] = '\\';
|
||||
q[to++] = p->hex_char;
|
||||
to += to_hex(&q[to], c, p->hex_digits);
|
||||
} else {
|
||||
q[to++] = c;
|
||||
}
|
||||
}
|
||||
q[to++] = '"';
|
||||
|
||||
return util_realloc(q, (size_t)to + 1);
|
||||
}
|
||||
|
||||
static char *csv_quote(const char *str)
|
||||
{
|
||||
static const struct quote_params csv_quote_params = {
|
||||
.double_chars = "\"",
|
||||
.esc_map = NULL,
|
||||
.hex_char = 0,
|
||||
.hex_digits = 0,
|
||||
.max_width_per_char = 2 /* " => "" */,
|
||||
};
|
||||
|
||||
return do_quote(str, &csv_quote_params);
|
||||
}
|
||||
|
||||
static void add_key(const char *name, bool persist)
|
||||
{
|
||||
struct key_t key;
|
||||
char *hdr;
|
||||
|
||||
key.name = util_strdup(name);
|
||||
key.persist = persist;
|
||||
util_add_array(&f.keys, &f.num_keys, key);
|
||||
if (f.type == FMT_CSV) {
|
||||
hdr = csv_quote(name);
|
||||
util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, hdr);
|
||||
free(hdr);
|
||||
util_rec_set(f.csv_rec, name, "\"\"");
|
||||
f.csv_hdr = true;
|
||||
}
|
||||
}
|
||||
|
||||
static struct key_t *get_key(const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < f.num_keys; i++) {
|
||||
if (strcmp(name, f.keys[i].name) == 0)
|
||||
return &f.keys[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void util_fmt_add_key(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *key;
|
||||
|
||||
va_start(args, fmt);
|
||||
util_vasprintf(&key, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/* Only add unique keys. */
|
||||
if (!get_key(key))
|
||||
add_key(key, true);
|
||||
free(key);
|
||||
}
|
||||
|
||||
static bool update_key(const char *name, bool persist)
|
||||
{
|
||||
struct key_t *key;
|
||||
bool rc = true;
|
||||
|
||||
key = get_key(name);
|
||||
if (key) {
|
||||
key->persist = persist;
|
||||
} else if (!f.do_filter) {
|
||||
add_key(name, persist);
|
||||
} else {
|
||||
fwarn("util_fmt_pair for key '%s' without util_fmt_add_key()",
|
||||
name);
|
||||
rc = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct obj_t *curr_obj(int off)
|
||||
{
|
||||
int lvl = (int)f.lvl - 1 + off;
|
||||
|
||||
return lvl < 0 ? NULL : &f.objs[lvl];
|
||||
}
|
||||
|
||||
static void _util_fmt_obj_end(void);
|
||||
|
||||
/*
|
||||
* By s390-tools convention, all tool output must be contained in an extra
|
||||
* top-level object that includes tool-invocation meta-data.
|
||||
*/
|
||||
static void emit_meta_object(void)
|
||||
{
|
||||
unsigned int quoted = FMT_PERSIST | FMT_QUOTE, unquoted = FMT_PERSIST;
|
||||
char hostname[HOST_NAME_MAX + 1] = { 0 }, date[30];
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
|
||||
f.meta_done = true;
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_obj_start(FMT_PREFIX, "meta");
|
||||
|
||||
/*
|
||||
* "meta": {
|
||||
* "api_level": 1,
|
||||
* "version": "2.32.0",
|
||||
* "host": "localhost",
|
||||
* "time_epoch": 1714392976,
|
||||
* "time": "2024-04-29 14:16:16+0200",
|
||||
* }
|
||||
*/
|
||||
util_fmt_pair(unquoted, "api_level", "%d", f.api_level);
|
||||
util_fmt_pair(quoted, "version", "%s", RELEASE_STRING);
|
||||
gethostname(hostname, sizeof(hostname) - 1);
|
||||
util_fmt_pair(quoted, "host", "%s", hostname);
|
||||
gettimeofday(&tv, NULL);
|
||||
util_fmt_pair(unquoted, "time_epoch", "%llu", tv.tv_sec);
|
||||
tm = localtime(&tv.tv_sec);
|
||||
if (!strftime(date, sizeof(date), "%F %T%z", tm))
|
||||
date[0] = 0;
|
||||
util_fmt_pair(quoted, "time", "%s", date);
|
||||
_util_fmt_obj_end();
|
||||
|
||||
if (f.type == FMT_JSONSEQ) {
|
||||
/* Tool meta-data is a separate object for JSONSEQ. */
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
}
|
||||
|
||||
void util_fmt_obj_start(unsigned int oflags, const char *fmt, ...)
|
||||
{
|
||||
struct obj_t *parent, *obj;
|
||||
char *name = NULL;
|
||||
sigset_t set;
|
||||
va_list args;
|
||||
|
||||
if (!f.hide_meta && !f.meta_done && f.lvl == 0) {
|
||||
emit_meta_object();
|
||||
/*
|
||||
* Allow override of top-level key name for supplementary
|
||||
* output formats.
|
||||
*/
|
||||
if (!fmt)
|
||||
name = util_strdup(program_invocation_short_name);
|
||||
}
|
||||
if (fmt) {
|
||||
va_start(args, fmt);
|
||||
util_vasprintf(&name, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
f.lvl++;
|
||||
if (f.lvl > f.num_objs)
|
||||
util_expand_array(&f.objs, &f.num_objs);
|
||||
parent = curr_obj(-1);
|
||||
obj = curr_obj(0);
|
||||
obj->name = name;
|
||||
obj->is_list = (oflags & FMT_LIST);
|
||||
obj->is_row = (oflags & FMT_ROW);
|
||||
obj->is_prefix = (oflags & FMT_PREFIX);
|
||||
obj->index = 0;
|
||||
if (f.obj_start) {
|
||||
disable_int(&set);
|
||||
f.obj_start(parent, obj);
|
||||
enable_int(&set);
|
||||
}
|
||||
if (parent)
|
||||
parent->index++;
|
||||
}
|
||||
|
||||
static void _util_fmt_obj_end(void)
|
||||
{
|
||||
struct obj_t *obj, *parent;
|
||||
sigset_t set;
|
||||
|
||||
if (f.lvl == 0) {
|
||||
fwarn("%s without util_fmt_obj_start", __func__);
|
||||
return;
|
||||
}
|
||||
parent = curr_obj(-1);
|
||||
obj = curr_obj(0);
|
||||
if (f.obj_end) {
|
||||
disable_int(&set);
|
||||
f.obj_end(parent, obj);
|
||||
enable_int(&set);
|
||||
}
|
||||
f.lvl--;
|
||||
obj_free(obj);
|
||||
}
|
||||
|
||||
void util_fmt_obj_end(void)
|
||||
{
|
||||
_util_fmt_obj_end();
|
||||
|
||||
if (f.lvl == 1 && f.meta_done && f.type != FMT_JSONSEQ) {
|
||||
/* Emit closure for top-level meta-container object. */
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
}
|
||||
|
||||
static char *add_prefix(const char *str, bool full)
|
||||
{
|
||||
struct obj_t *obj;
|
||||
unsigned int i;
|
||||
char *prefix;
|
||||
|
||||
prefix = util_strdup("");
|
||||
for (i = 0; i < f.lvl; i++) {
|
||||
obj = &f.objs[i];
|
||||
if (!full && !obj->is_prefix)
|
||||
continue;
|
||||
if (obj->name) {
|
||||
if (*prefix)
|
||||
util_concatf(&prefix, ".");
|
||||
util_concatf(&prefix, "%s", obj->name);
|
||||
}
|
||||
if (obj->is_list && full)
|
||||
util_concatf(&prefix, "[%d]", obj->index - 1);
|
||||
}
|
||||
if (*prefix)
|
||||
util_concatf(&prefix, ".");
|
||||
util_concatf(&prefix, "%s", str);
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...)
|
||||
{
|
||||
char *val, *prefixed_key;
|
||||
struct obj_t *obj;
|
||||
bool is_filtered;
|
||||
sigset_t set;
|
||||
va_list args;
|
||||
|
||||
obj = curr_obj(0);
|
||||
if (!obj) {
|
||||
fwarn("%s before util_fmt_obj_start", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Filter by key. */
|
||||
if (f.do_filter) {
|
||||
prefixed_key = add_prefix(key, false);
|
||||
is_filtered = !get_key(prefixed_key);
|
||||
free(prefixed_key);
|
||||
if (is_filtered)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Filter by validity. */
|
||||
if (f.hide_inval && (mflags & FMT_INVAL))
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
util_vasprintf(&val, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (f.map) {
|
||||
disable_int(&set);
|
||||
f.map(obj, mflags, key, val);
|
||||
enable_int(&set);
|
||||
}
|
||||
obj->index++;
|
||||
|
||||
free(val);
|
||||
}
|
||||
|
||||
static char *pairs_quote(const char *str)
|
||||
{
|
||||
static const struct quote_params pairs_quote_params = {
|
||||
.double_chars = NULL,
|
||||
.esc_map = "\"\"$$``\\\\\aa\bb\ee\ff\nn\rr\tt\vv",
|
||||
.hex_char = 'x',
|
||||
.hex_digits = 2,
|
||||
.max_width_per_char = 4 /* '\x' + 2 hex_digits */,
|
||||
};
|
||||
|
||||
return do_quote(str, &pairs_quote_params);
|
||||
}
|
||||
|
||||
static void pairs_map(struct obj_t *UNUSED(obj), unsigned int mflags,
|
||||
const char *key, const char *val)
|
||||
{
|
||||
char *full_key, *qval = NULL;
|
||||
|
||||
if (mflags & FMT_INVAL)
|
||||
val = "";
|
||||
indent(0);
|
||||
if (f.quote_all || (mflags & FMT_QUOTE))
|
||||
qval = pairs_quote(val);
|
||||
if (f.hide_prefix) {
|
||||
fprintf(f.fd, "%s=%s\n", key, qval ?: val);
|
||||
} else {
|
||||
full_key = add_prefix(key, true);
|
||||
fprintf(f.fd, "%s=%s\n", full_key, qval ?: val);
|
||||
free(full_key);
|
||||
}
|
||||
free(qval);
|
||||
}
|
||||
|
||||
static char *json_quote(const char *str)
|
||||
{
|
||||
static const struct quote_params json_quote_params = {
|
||||
.double_chars = NULL,
|
||||
.esc_map = "\"\"\\\\\bb\ff\nn\rr\tt",
|
||||
.hex_char = 'u',
|
||||
.hex_digits = 4,
|
||||
.max_width_per_char = 6 /* '\u' + 4 hex_digits */,
|
||||
};
|
||||
|
||||
return do_quote(str, &json_quote_params);
|
||||
}
|
||||
|
||||
static void json_obj_start(struct obj_t *parent, struct obj_t *obj)
|
||||
{
|
||||
char *key;
|
||||
|
||||
if (!parent && f.type == FMT_JSONSEQ) {
|
||||
/* Emit leading record separator according to RFC 7464. */
|
||||
fprintf(f.fd, "\x1e");
|
||||
}
|
||||
if (parent && parent->index > 0)
|
||||
fprintf(f.fd, ",%s", f.nl);
|
||||
indent(0);
|
||||
if (parent && !parent->is_list && obj->name) {
|
||||
key = json_quote(obj->name);
|
||||
fprintf(f.fd, "%s: ", key);
|
||||
free(key);
|
||||
}
|
||||
fprintf(f.fd, obj->is_list ? "[%s" : "{%s", f.nl);
|
||||
}
|
||||
|
||||
static void json_obj_end(struct obj_t *parent, struct obj_t *obj)
|
||||
{
|
||||
if (obj->index > 0)
|
||||
fprintf(f.fd, "%s", f.nl);
|
||||
indent(0);
|
||||
fprintf(f.fd, obj->is_list ? "]" : "}");
|
||||
if (!parent)
|
||||
fprintf(f.fd, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure syntactically correct JSON by emitting all pending closure elements.
|
||||
* Called in signal context - only use signal-safe functions.
|
||||
*/
|
||||
static void json_term(void)
|
||||
{
|
||||
struct obj_t *obj, *parent;
|
||||
|
||||
for (; f.lvl > 0; f.lvl--) {
|
||||
obj = curr_obj(0);
|
||||
parent = curr_obj(-1);
|
||||
if (obj->index > 0)
|
||||
safe_write(f.nl);
|
||||
_indent(0, true);
|
||||
safe_write(obj->is_list ? "]" : "}");
|
||||
if (!parent)
|
||||
safe_write(f.nl);
|
||||
}
|
||||
}
|
||||
|
||||
static void json_map(struct obj_t *parent, unsigned int mflags,
|
||||
const char *key, const char *val)
|
||||
{
|
||||
char *qkey, *qval = NULL;
|
||||
|
||||
qkey = json_quote(key);
|
||||
if (mflags & FMT_INVAL)
|
||||
qval = util_strdup("null");
|
||||
else if (f.quote_all || (mflags & FMT_QUOTE))
|
||||
qval = json_quote(val);
|
||||
if (parent->index > 0)
|
||||
fprintf(f.fd, ",%s", f.nl);
|
||||
indent(1);
|
||||
fprintf(f.fd, "%s: %s", qkey, qval ?: val);
|
||||
free(qval);
|
||||
free(qkey);
|
||||
}
|
||||
|
||||
static void csv_obj_start(struct obj_t *UNUSED(parent), struct obj_t *obj)
|
||||
{
|
||||
if (!obj->is_row)
|
||||
return;
|
||||
}
|
||||
|
||||
static void csv_obj_end(struct obj_t *UNUSED(parent), struct obj_t *obj)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!(obj->is_row || (f.lvl == 1 && f.csv_data)))
|
||||
return;
|
||||
if (f.csv_hdr) {
|
||||
/* Print row with CSV header. */
|
||||
indent(0);
|
||||
util_rec_print_hdr(f.csv_rec);
|
||||
f.csv_hdr = false;
|
||||
}
|
||||
/* Print row with CSV data. */
|
||||
indent(0);
|
||||
util_rec_print(f.csv_rec);
|
||||
f.csv_data = false;
|
||||
/* Reset non-persistent fields. */
|
||||
for (i = 0; i < f.num_keys; i++) {
|
||||
if (!f.keys[i].persist)
|
||||
util_rec_set(f.csv_rec, f.keys[i].name, "\"\"");
|
||||
}
|
||||
}
|
||||
|
||||
static void csv_map(struct obj_t *UNUSED(obj), unsigned int mflags,
|
||||
const char *key, const char *val)
|
||||
{
|
||||
char *qval = NULL, *prefixed_key;
|
||||
|
||||
/* Use empty string for invalid values. */
|
||||
if (mflags & FMT_INVAL)
|
||||
val = "";
|
||||
/* Quote value if requested. */
|
||||
if (f.quote_all || (mflags & FMT_QUOTE))
|
||||
qval = csv_quote(val);
|
||||
/* Process key and value. */
|
||||
prefixed_key = add_prefix(key, false);
|
||||
if (update_key(prefixed_key, mflags & FMT_PERSIST)) {
|
||||
util_rec_set(f.csv_rec, prefixed_key, "%s", qval ?: val);
|
||||
f.csv_data = true;
|
||||
}
|
||||
free(prefixed_key);
|
||||
free(qval);
|
||||
}
|
||||
|
||||
void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
||||
int api_level)
|
||||
{
|
||||
memset(&f, 0, sizeof(f));
|
||||
f.type = type;
|
||||
f.fd = fd;
|
||||
f.fileno = fileno(fd);
|
||||
f.hide_prefix = (flags & FMT_NOPREFIX);
|
||||
f.hide_inval = !(flags & FMT_KEEPINVAL);
|
||||
f.hide_meta = (flags & FMT_NOMETA);
|
||||
f.quote_all = (flags & FMT_QUOTEALL);
|
||||
f.do_filter = (flags & FMT_FILTER);
|
||||
f.do_warn = (flags & FMT_WARN);
|
||||
f.handle_int = (flags & FMT_HANDLEINT);
|
||||
f.api_level = api_level;
|
||||
if (type == FMT_JSONSEQ)
|
||||
f.nl = "";
|
||||
else
|
||||
f.nl = "\n";
|
||||
f.ind_width = 2;
|
||||
f.ind_char = ' ';
|
||||
f.meta_done = false;
|
||||
switch (type) {
|
||||
case FMT_PAIRS:
|
||||
f.map = &pairs_map;
|
||||
break;
|
||||
case FMT_JSON:
|
||||
case FMT_JSONSEQ:
|
||||
f.obj_start = &json_obj_start;
|
||||
f.obj_end = &json_obj_end;
|
||||
f.map = &json_map;
|
||||
f.term = &json_term;
|
||||
break;
|
||||
case FMT_CSV:
|
||||
f.obj_start = &csv_obj_start;
|
||||
f.obj_end = &csv_obj_end;
|
||||
f.map = &csv_map;
|
||||
f.csv_rec = util_rec_new_csv(",");
|
||||
f.csv_hdr = true;
|
||||
f.csv_data = false;
|
||||
break;
|
||||
}
|
||||
/* Ensure consistent number format for callers that use setlocale(). */
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
if (f.handle_int) {
|
||||
setup_int_handler();
|
||||
sigemptyset(&no_int_mask);
|
||||
sigaddset(&no_int_mask, SIGINT);
|
||||
sigaddset(&no_int_mask, SIGTERM);
|
||||
}
|
||||
}
|
||||
251
libutil/util_fmt_example.c
Normal file
251
libutil/util_fmt_example.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* util_fmt_example - Example program for util_fmt
|
||||
*
|
||||
* Copyright IBM Corp. 2024
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_fmt.h"
|
||||
|
||||
#define API_LEVEL 1
|
||||
|
||||
static void meta_example(enum util_fmt_t format)
|
||||
{
|
||||
util_fmt_init(stdout, format, FMT_DEFAULT, API_LEVEL);
|
||||
|
||||
/*
|
||||
* First call to util_fmt_obj_start() automatically adds meta-data
|
||||
* object as required by s390-tools convention.
|
||||
*/
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_pair(FMT_QUOTE, "key", "value");
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void simple_example(enum util_fmt_t format, int fmt_flags)
|
||||
{
|
||||
/*
|
||||
* Note: Meta-data is excluded in this example for readability but
|
||||
* must be included in actual tool output.
|
||||
*/
|
||||
util_fmt_init(stdout, format, fmt_flags | FMT_NOMETA, API_LEVEL);
|
||||
|
||||
/*
|
||||
* {
|
||||
* "child": {
|
||||
* "key": "value",
|
||||
* "invalid":"invalidvalue" <== Marked as invalid
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_obj_start(FMT_DEFAULT, "child");
|
||||
util_fmt_pair(FMT_QUOTE, "key", "value");
|
||||
util_fmt_pair(FMT_QUOTE | FMT_INVAL, "invalid", "invalidvalue");
|
||||
util_fmt_obj_end();
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void list_example(enum util_fmt_t format, int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Note: Meta-data is excluded in this example for readability but
|
||||
* must be included in actual tool output.
|
||||
*/
|
||||
util_fmt_init(stdout, format, flags | FMT_NOMETA, API_LEVEL);
|
||||
|
||||
/*
|
||||
* "cond","key"
|
||||
* condvalue0,value0
|
||||
* "",value1
|
||||
* "",value2
|
||||
* "",value3
|
||||
*/
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_obj_start(FMT_LIST, "list");
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
util_fmt_obj_start(FMT_ROW, NULL);
|
||||
if (i == 0)
|
||||
util_fmt_pair(flags, "cond", "condvalue%d", i);
|
||||
util_fmt_pair(FMT_DEFAULT, "key", "value%d", i);
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
|
||||
util_fmt_obj_end();
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
#define NUM_KEYS 4
|
||||
|
||||
static void vary_example(enum util_fmt_t format, bool add)
|
||||
{
|
||||
const char *keys[NUM_KEYS] = { "key_a", "key_b", "key_c", "key_d" };
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Note: Meta-data is excluded in this example for readability but
|
||||
* must be included in actual tool output.
|
||||
*/
|
||||
util_fmt_init(stdout, format, FMT_NOMETA, API_LEVEL);
|
||||
|
||||
if (add) {
|
||||
/* Make keys known before starting output. */
|
||||
for (i = 0; i < NUM_KEYS; i++)
|
||||
util_fmt_add_key(keys[i]);
|
||||
}
|
||||
|
||||
util_fmt_obj_start(FMT_LIST, "list");
|
||||
for (i = 0; i < 4; i++) {
|
||||
util_fmt_obj_start(FMT_ROW, NULL);
|
||||
util_fmt_pair(FMT_DEFAULT, keys[i], "value%d", i);
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void filter_example(enum util_fmt_t format)
|
||||
{
|
||||
/*
|
||||
* Note: Meta-data is excluded in this example for readability but
|
||||
* must be included in actual tool output.
|
||||
*/
|
||||
util_fmt_init(stdout, format, FMT_FILTER | FMT_NOMETA, API_LEVEL);
|
||||
util_fmt_add_key("key_a");
|
||||
/*
|
||||
* {
|
||||
* "key_a": "value_a",
|
||||
* "key_b": "value_b" <== Not announced via util_fmt_add_key()
|
||||
* }
|
||||
*/
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_pair(FMT_QUOTE, "key_a", "value_a");
|
||||
util_fmt_pair(FMT_QUOTE, "key_b", "value_b");
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void prefix_example(enum util_fmt_t format, bool do_prefix)
|
||||
{
|
||||
/*
|
||||
* Note: Meta-data is excluded in this example for readability but
|
||||
* must be included in actual tool output.
|
||||
*/
|
||||
util_fmt_init(stdout, format, FMT_NOMETA, API_LEVEL);
|
||||
|
||||
/*
|
||||
* {
|
||||
* "key": "value0",
|
||||
* "obj1": { // Marked as prefix object
|
||||
* "key": "value1"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
util_fmt_obj_start(FMT_DEFAULT, "obj0");
|
||||
util_fmt_pair(FMT_QUOTE, "key", "value0");
|
||||
util_fmt_obj_start(do_prefix ? FMT_PREFIX : FMT_DEFAULT, "obj1");
|
||||
util_fmt_pair(FMT_QUOTE, "key", "value1");
|
||||
util_fmt_obj_end();
|
||||
util_fmt_obj_end();
|
||||
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void announce(const char *example_name)
|
||||
{
|
||||
static int example_number;
|
||||
int i;
|
||||
|
||||
if (example_number++ > 0)
|
||||
printf("\n");
|
||||
|
||||
printf("%d. %s\n====", example_number, example_name);
|
||||
for (i = strlen(example_name); i > 0; i--)
|
||||
printf("=");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int UNUSED(argc), char *UNUSED(argv[]))
|
||||
{
|
||||
announce("JSON output");
|
||||
simple_example(FMT_JSON, FMT_KEEPINVAL);
|
||||
|
||||
announce("JSON without invalid pairs");
|
||||
simple_example(FMT_JSON, FMT_DEFAULT);
|
||||
|
||||
announce("JSON formatted as sequence");
|
||||
simple_example(FMT_JSONSEQ, FMT_DEFAULT);
|
||||
|
||||
announce("Pairs output");
|
||||
simple_example(FMT_PAIRS, FMT_KEEPINVAL);
|
||||
|
||||
announce("Pairs output without invalid pairs");
|
||||
simple_example(FMT_PAIRS, FMT_DEFAULT);
|
||||
|
||||
announce("Pairs without prefix");
|
||||
simple_example(FMT_PAIRS, FMT_NOPREFIX);
|
||||
|
||||
announce("CSV output");
|
||||
simple_example(FMT_CSV, FMT_KEEPINVAL);
|
||||
|
||||
announce("CSV list output");
|
||||
list_example(FMT_CSV, FMT_DEFAULT);
|
||||
|
||||
announce("CSV list with persistent cond value");
|
||||
list_example(FMT_CSV, FMT_PERSIST);
|
||||
|
||||
announce("JSON with filtered key");
|
||||
filter_example(FMT_JSON);
|
||||
|
||||
announce("Pairs with filtered key");
|
||||
filter_example(FMT_PAIRS);
|
||||
|
||||
announce("CSV with filtered key");
|
||||
filter_example(FMT_CSV);
|
||||
|
||||
announce("CSV list with varying keys");
|
||||
vary_example(FMT_CSV, false);
|
||||
|
||||
announce("CSV list with pre-announced varying keys");
|
||||
vary_example(FMT_CSV, true);
|
||||
|
||||
announce("JSON output with meta-data");
|
||||
meta_example(FMT_JSON);
|
||||
|
||||
announce("JSON sequence output with meta-data");
|
||||
meta_example(FMT_JSONSEQ);
|
||||
|
||||
announce("Pairs output with meta-data");
|
||||
meta_example(FMT_PAIRS);
|
||||
|
||||
announce("CSV output with meta-data");
|
||||
meta_example(FMT_CSV);
|
||||
|
||||
announce("JSON output with duplicate keys");
|
||||
prefix_example(FMT_JSON, false);
|
||||
|
||||
announce("CSV output with duplicate keys");
|
||||
prefix_example(FMT_CSV, false);
|
||||
|
||||
announce("CSV output with duplicate keys distinguished by prefix");
|
||||
prefix_example(FMT_CSV, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -139,6 +139,26 @@ char *util_strcat_realloc(char *str1, const char *str2)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a string with the result of a format string expansion
|
||||
*
|
||||
* @param[in, out] str1 Pointer to pointer to first string
|
||||
* @param[in] fmt Format string for generation of the second string
|
||||
* @param[in] ... Parameters for format string
|
||||
*/
|
||||
void util_concatf(char **str1, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *str2;
|
||||
|
||||
va_start(args, fmt);
|
||||
util_vasprintf(&str2, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
*str1 = util_strcat_realloc(*str1, str2);
|
||||
free(str2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to uppercase
|
||||
*
|
||||
|
||||
@@ -40,6 +40,14 @@ int main(void)
|
||||
fprintf(stderr, "result = \"%s\"\n", str);
|
||||
free(str);
|
||||
|
||||
/* Use util_concatf() for string concatenation */
|
||||
fprintf(stderr, "Try to concatenate \"list\" plus comma-separated list of numbers 1 to 3: ");
|
||||
str = NULL;
|
||||
util_concatf(&str, "list:");
|
||||
for (int i = 1; i <= 3; i++)
|
||||
util_concatf(&str, "%s%d", (i > 1 ? "," : ""), i);
|
||||
fprintf(stderr, "result = %s\n", str); /* list:part1,part2,part3 */
|
||||
|
||||
/* One byte allocation should work */
|
||||
fprintf(stderr, "Try to allocate 1 byte: ");
|
||||
ptr = util_malloc(1);
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
#define WAITPID 120 /* Time to wait for pid to be written */
|
||||
#define WAITINC 5 /* Additional time to wait each retry */
|
||||
#define MAXWAIT 60 /* Maximum wait between retries */
|
||||
#define DEF_WAITINC_US 5000000 /* Additional time to wait each retry */
|
||||
#define DEF_MAXWAIT_US 60000000 /* Maximum wait between retries */
|
||||
#define BUFSIZE 40 /* Buffer must be large enough to fit pid string */
|
||||
|
||||
/**
|
||||
@@ -133,15 +133,20 @@ static int handle_stale_lock(char *lockfile)
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
* @param[in] waitinc How many micro-seconds to extend wait time before
|
||||
* additional retry
|
||||
* @param[in] maxwait Maximum wait time before retry
|
||||
* @param[in] pid PID to use for lock ownership
|
||||
*
|
||||
* @retval 0 Lock created with PID as owner
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid)
|
||||
static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid,
|
||||
unsigned int waitinc, unsigned int maxwait)
|
||||
{
|
||||
int fd, plen, len, rc = 0, snooze = 0;
|
||||
unsigned int tries = retries + 1;
|
||||
int fd, plen, len, rc = 0;
|
||||
unsigned int snooze = 0;
|
||||
char buf[BUFSIZE];
|
||||
char *tpath;
|
||||
|
||||
@@ -190,9 +195,9 @@ static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid)
|
||||
if (rc != 0) {
|
||||
tries--;
|
||||
if (tries > 0) {
|
||||
snooze += WAITINC;
|
||||
snooze = (snooze > MAXWAIT) ? MAXWAIT : snooze;
|
||||
sleep(snooze);
|
||||
snooze += waitinc;
|
||||
snooze = (snooze > maxwait) ? maxwait : snooze;
|
||||
usleep(snooze);
|
||||
}
|
||||
}
|
||||
} while (tries > 0);
|
||||
@@ -255,7 +260,27 @@ static int do_lockfile_release(char *lockfile, int pid)
|
||||
*/
|
||||
int util_lockfile_lock(char *lockfile, int retries)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getpid());
|
||||
return do_lockfile_lock(lockfile, retries, getpid(), DEF_WAITINC_US,
|
||||
DEF_MAXWAIT_US);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a lockfile owned by this process at the specified path
|
||||
* using a custom wait/retry time.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
* @param[in] waitinc How many micro-seconds to extend wait time before
|
||||
* additional retry
|
||||
* @param[in] maxwait Maximum wait time before retry
|
||||
*
|
||||
* @retval 0 Lock created
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
int util_lockfile_lock_cw(char *lockfile, int retries, unsigned int waitinc,
|
||||
unsigned int maxwait)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getpid(), waitinc, maxwait);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +295,27 @@ int util_lockfile_lock(char *lockfile, int retries)
|
||||
*/
|
||||
int util_lockfile_parent_lock(char *lockfile, int retries)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getppid());
|
||||
return do_lockfile_lock(lockfile, retries, getppid(), DEF_WAITINC_US,
|
||||
DEF_MAXWAIT_US);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a lockfile owned by the parent of this process at the
|
||||
* specified path using a custom wait/retry time.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
* @param[in] waitinc How many micro-seconds to extend wait time before
|
||||
* additional retry
|
||||
* @param[in] maxwait Maximum wait time before retry
|
||||
*
|
||||
* @retval 0 Lock created
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
int util_lockfile_parent_lock_cw(char *lockfile, int retries,
|
||||
unsigned int waitinc, unsigned int maxwait)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getppid(), waitinc, maxwait);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,3 +344,35 @@ int util_lockfile_parent_release(char *lockfile)
|
||||
{
|
||||
return do_lockfile_release(lockfile, getppid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pid that owns the specified lockfile.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in,out] pid Buffer to place owning pid
|
||||
*
|
||||
* @retval 0 pid provided in buffer
|
||||
* @retval !=0 Error, no pid provided
|
||||
*/
|
||||
int util_lockfile_peek_owner(char *lockfile, int *pid)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
int fd, len;
|
||||
|
||||
if (!lockfile || !pid)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
/* Open lockfile, read the owning pid if it exists */
|
||||
fd = open(lockfile, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if (len <= 0)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
buf[len] = 0;
|
||||
*pid = atoi(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ const char *util_rec_fld_get_key(struct util_rec_fld *fld)
|
||||
/**
|
||||
* Create a new record with "wide" output format
|
||||
*
|
||||
* @param[in] hdr_sep Header separator
|
||||
* @param[in] hdr_sep Header separator or %NULL for no separator
|
||||
*
|
||||
* @returns Pointer to the created record
|
||||
*/
|
||||
@@ -121,7 +121,7 @@ struct util_rec *util_rec_new_wide(const char *hdr_sep)
|
||||
|
||||
rec->list = util_list_new(struct util_rec_fld, node);
|
||||
rec->fmt.type = REC_FMT_WIDE;
|
||||
rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep);
|
||||
rec->fmt.d.wide_p.hdr_sep = hdr_sep ? util_strdup(hdr_sep) : NULL;
|
||||
rec->fmt.d.wide_p.argz_sep = ',';
|
||||
rec->fmt.indent = 0;
|
||||
return rec;
|
||||
@@ -249,7 +249,7 @@ static void rec_free_wide(struct util_rec *rec)
|
||||
/**
|
||||
* Create a new record with "long" output format
|
||||
*
|
||||
* @param[in] hdr_sep Header separator
|
||||
* @param[in] hdr_sep Header separator or %NULL for no separator
|
||||
* @param[in] col_sep Column separator
|
||||
* @param[in] key Primary key of record
|
||||
* @param[in] key_size Width of left column i.e. keys
|
||||
@@ -264,7 +264,7 @@ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep,
|
||||
|
||||
rec->list = util_list_new(struct util_rec_fld, node);
|
||||
rec->fmt.type = REC_FMT_LONG;
|
||||
rec->fmt.d.long_p.hdr_sep = util_strdup(hdr_sep);
|
||||
rec->fmt.d.long_p.hdr_sep = hdr_sep ? util_strdup(hdr_sep) : NULL;
|
||||
rec->fmt.d.long_p.col_sep = util_strdup(col_sep);
|
||||
rec->fmt.d.long_p.key = util_strdup(key);
|
||||
rec->fmt.d.long_p.key_size = key_size;
|
||||
|
||||
@@ -45,7 +45,7 @@ vmcmd: Trigger CP command according to the 'VMCMD_X' configuration in
|
||||
|
||||
.TP
|
||||
\fB - DUMP_TYPE:\fR
|
||||
Type of dump device. Possible values are 'ccw', 'fcp' and 'nvme'.
|
||||
Type of dump device. Possible values are 'ccw', 'eckd', 'fcp' and 'nvme'.
|
||||
|
||||
.TP
|
||||
\fB - DEVICE:\fR
|
||||
@@ -71,6 +71,11 @@ Namespace ID for NVMe dump device.
|
||||
\fB - BOOTPROG:\fR
|
||||
Boot program selector.
|
||||
|
||||
.TP
|
||||
\fB - BR_CHR:\fR
|
||||
Boot record location in "C,H,R" format (comma separated values for
|
||||
Cylinder, Head and Record) or "auto".
|
||||
|
||||
.TP
|
||||
\fB - BR_LBA:\fR
|
||||
Boot record logical block address.
|
||||
@@ -146,6 +151,23 @@ DEVICE=0.0.1234
|
||||
DELAY_MINUTES=5
|
||||
.br
|
||||
|
||||
#
|
||||
.br
|
||||
# Example configuration for an ECKD dump device (DASD)
|
||||
.br
|
||||
#
|
||||
.br
|
||||
ON_PANIC=dump
|
||||
.br
|
||||
DUMP_TYPE=eckd
|
||||
.br
|
||||
DEVICE=0.0.1004
|
||||
.br
|
||||
BOOTPROG=0
|
||||
.br
|
||||
BR_CHR=auto
|
||||
.br
|
||||
|
||||
#
|
||||
.br
|
||||
# Example configuration for an FCP dump device (SCSI Disk)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/* we may use header_generic and header_simple_table from the util_funcs module */
|
||||
|
||||
config_require(util_funcs)
|
||||
config_require(util_funcs);
|
||||
|
||||
|
||||
/* function prototypes */
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Common definitions
|
||||
include ../common.mak
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
PKGDATADIR := "$(DESTDIR)$(TOOLS_DATADIR)/pvattest"
|
||||
SUBDIRS := src man tools
|
||||
RECURSIVE_TARGETS := all-recursive clean-recursive install-recursive
|
||||
|
||||
all: all-recursive
|
||||
|
||||
install: install-recursive
|
||||
|
||||
clean: clean-recursive
|
||||
|
||||
$(RECURSIVE_TARGETS):
|
||||
@target=`echo $@ |sed s/-recursive//`; \
|
||||
for d in $(SUBDIRS); do \
|
||||
$(MAKE) -C $$d $$target || exit 1; \
|
||||
done
|
||||
|
||||
.PHONY: all install clean $(RECURSIVE_TARGETS)
|
||||
@@ -1,100 +0,0 @@
|
||||
# pvattest
|
||||
|
||||
Use `pvattest` to attest an IBM Secure Execution guest running on z16 and later.
|
||||
|
||||
With `pvattest` you can create attestation requests in a trusted environment and attest
|
||||
an IBM Secure Execution for Linux guest to verify that a provider is running the correct image.
|
||||
To achieve this, use the following commands:
|
||||
* `create` On a trusted system, creates an attestation request.
|
||||
* `perform` Performs an attestation measurement on the SE-guest to be attested. For this a
|
||||
attestation request is sent to the Ultravisor (UV) and the answer received. The `perform`
|
||||
command requires IBM z16 or later z/Architecture hardware.
|
||||
* `verify` On a trusted system, compares the answer from the Ultravisor to the
|
||||
expected answer. If they differ, the Secure Execution guest might be a different guest
|
||||
than expected, or not secure at all.
|
||||
|
||||
For meaningful results, run `create` and `verify` only in a trusted environment,
|
||||
like your workstation or a previously attested IBM Secure Execution guest.
|
||||
Otherwise, the attestation can be compromised.
|
||||
For all certificates, revocation lists, and host-key documents, both the PEM and DER input
|
||||
formats are supported. If you run this program on a non S390 System, 'perform' is not be available.
|
||||
|
||||
## Getting started
|
||||
|
||||
If all dependencies are met (see the s390-tools README) issue `make` in the source tree to build `pvattest`.
|
||||
|
||||
## Details
|
||||
### create
|
||||
`pvattest create` needs the host-key-document, a location to store the
|
||||
attestation request protection key, and a location to store the request data.
|
||||
Unless the `--no-verify` flag is set it additionally requires the IBM signing key
|
||||
and the intermediate CA. The output contains the request in binary form which serves as input
|
||||
to `pvattest perform`. Must be run in a trusted environment. Especially, do not create the request
|
||||
on a system you want to attest. The attestation request protection key is valid for this request only,
|
||||
must be kept until the verification is completed and must be destroyed afterwards
|
||||
Keep the key secret.
|
||||
|
||||
### perform
|
||||
`pvattest perform` needs a request in binary form generated by `pvattest create`and
|
||||
a location to store the output. It will send the request to the device at `/dev/uv`
|
||||
which passes the request to the Ultravisor.
|
||||
Kernel will then send the request to the Ultravisor which will calculate the answer.
|
||||
The Answer is then passed back to userspace and handled by `pvattest`
|
||||
The output includes the original request and the answer from the Ultravisor.
|
||||
|
||||
### verify
|
||||
`pvattest verify` needs the SE-guest header, the attestation request protection key,
|
||||
and the attestation request and the response to the `pvattest perform` command from the Ultravisor.
|
||||
It calculates the measurement in the trusted environment and compares it to the response from
|
||||
the Ultravisor in the previous step.
|
||||
The following return codes are possible:
|
||||
|
||||
0. successful verification: The calculated measurement matches the response from the Ultravisor
|
||||
|
||||
1. failed verification: The command ended with an error, for example, because of incorrect input or an invalid SE header
|
||||
|
||||
2. failed verification: The calculated measurement does not match the response from the Ultravisor
|
||||
|
||||
Run `pvattest verify` in a trusted environment. Especially, do not verify on the system you want to attest.
|
||||
|
||||
## Measurement
|
||||
The measurement is a cryptographic measurement of the following block.
|
||||
Only HMAC-SHA512 is supported.
|
||||
|
||||
| Start | Size | Content |
|
||||
|---------|------------|---------------------------------------------------------------|
|
||||
| 0x0 | 0x40 | Page List Digest (from SE header) |
|
||||
| 0x40 | 0x40 | Address List Digest (from SE header) |
|
||||
| 0x80 | 0x40 | Tweak List Digest (from SE header) |
|
||||
| 0xc0 | 0x10 | SE Header Tag (from SE header) |
|
||||
| 0xd0 | 0x10 | Configuration UID (generated by UV, included in the answer) |
|
||||
| 0xe0 | 0x02 | User Data Length (defined during measurement on the SE-guest) |
|
||||
| 0xe2 | 0x02 | Zeros |
|
||||
| 0xe4 | 0x04 | Additional Data Length (set by UV, included in the answer) |
|
||||
| 0xe8 | 0 - 0x100 | User Data (generated during measurement on the SE-guest) |
|
||||
| ... | 0 or 0x10 | Optional Nonce (generated during request creation) |
|
||||
| ... | 0 - 0x8000 | Additional Data (generated by UV, included in the answer) |
|
||||
|
||||
### User Data
|
||||
By default `pvattest` does not include any User Data, therefore the length is zero.
|
||||
`User Data` is data generated by the SE guest and passed to UV during the measurement.
|
||||
The `User Data` must be known to or be replicable by the verifier to verify the correctness of the User Data.
|
||||
The addition of user data is currently an experimental setting.
|
||||
|
||||
### Additional Data
|
||||
`Additional data` is data known to the Ultravisor. By default UV will not include any `Additional Data`.
|
||||
Adding `Additional Data` is currently an experimental setting.
|
||||
|
||||
## Example
|
||||
|
||||
Create an attestation request in a trusted environment:
|
||||
|
||||
`pvattest create -k hkd.crt --arpk arp.key -o arcb.bin --cert IntermediateCA.crt --cert IbmSigningKey.crt`
|
||||
|
||||
Perform an attestation measurement on an IBM Secure Execution guest:
|
||||
|
||||
`pvattest perform --input arcb.bin --output measurement.bin`
|
||||
|
||||
Verify the response from the Ultravisor against the attestation request in a trusted environment:
|
||||
|
||||
`pvattest verify --input measurement.bin --arpk arp.key --hdr se_guest.hdr`
|
||||
@@ -1,9 +0,0 @@
|
||||
include ../../common.mak
|
||||
|
||||
all:
|
||||
|
||||
install:
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
|
||||
$(INSTALL) -m 644 -c *.1 -t $(DESTDIR)$(MANDIR)/man1
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -1,83 +0,0 @@
|
||||
.\" Copyright 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH pvattest-create 1 "07 June 2022" "s390-tools" "Attestation Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
\fBpvattest [OPTION?] create [OPTIONS] \fP- create an attestation measurement request
|
||||
\fB
|
||||
.SH DESCRIPTION
|
||||
Prepare attestation measurement requests for an IBM Secure Execution guest.
|
||||
Only prepare attestation requests in a trusted environment, such as your workstation.
|
||||
The 'pvattest create' command creates a randomly generated key to protect the attestation request.
|
||||
This key is only valid for this specific request. In order to avoid compromising the attestation,
|
||||
do not publish the protection key and delete it after verification.
|
||||
Every 'create' command generates a new, random protection key.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Prints usage information, then exits.
|
||||
.TP
|
||||
.B
|
||||
\fB-k\fP, \fB--host-key-document\fP=\fBFILE\fP
|
||||
Specify one or more host key documents. At least one is required.
|
||||
Specify this option multiple times to create an attestation request control block that is usable on multiple hosts.
|
||||
.TP
|
||||
.B
|
||||
\fB-C\fP, \fB--cert\fP=\fBFILE\fP
|
||||
Specifies the certificate that is used to establish a chain of trust for the verification of the host-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA). Required. Ignored when \fB--no-verify\fP is specified.
|
||||
.TP
|
||||
.B
|
||||
\fB--crl\fP=\fBFILE\fP
|
||||
Specifies the revocation list that is used to check whether a certificate of the chain of trust is
|
||||
revoked. Specify this option multiple times to use multiple CRLs (optional).
|
||||
.TP
|
||||
.B
|
||||
\fB--root-ca\fP=\fBFILE\fP
|
||||
Specifies the root CA certificate for the verification. If omitted,
|
||||
the system wide root CAs installed on the system are used. Use
|
||||
this only if you trust the specified certificate. Optional.
|
||||
.TP
|
||||
.B
|
||||
\fB-o\fP, \fB--output\fP=\fBFILE\fP
|
||||
\fBFILE\fP specifies the output for the attestation request control block.
|
||||
.TP
|
||||
.B
|
||||
\fB-a\fP, \fB--arpk\fP=\fBFILE\fP
|
||||
Save the protection key as GCM-AES256 key in \fBFILE\fP Do not publish this key, otherwise your attestation is compromised.
|
||||
.TP
|
||||
.B
|
||||
\fB--no-verify\fP
|
||||
Disable the host-key document verification. Does not require the host-key documents to be valid. Do
|
||||
not use for a production request unless you verified the host-key document before (optional).
|
||||
.TP
|
||||
.B
|
||||
\fB--offline\fP
|
||||
Specifies offline mode, in which no attempt is made to download CRLs. (optional).
|
||||
.TP
|
||||
.B
|
||||
\fB-V\fP, \fB--verbose\fP
|
||||
Provide more detailed output (optional).
|
||||
.SH EXAMPLE
|
||||
Create an attestation request with the protection key 'arp.key', write the request to 'arcb.bin', and verify the host-key document using the CA-signed key 'DigiCertCA.crt' and the intermediate key 'IbmSigningKey.crt'.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
pvattest create -k hkd.crt --arpk arp.key -o attreq.bin --cert DigiCertCA.crt --cert IbmSigningKey.crt
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
Create an attestation request with the protection key 'arp.key', write the request to 'arcb.bin', verify the host-key document using the CA-signed key 'DigiCertCA.crt' and the intermediate key 'IbmSigningKey.crt', and instead of downloading the certificate revocation list use certificate revocation lists 'DigiCertCA.crl', 'IbmSigningKey.crl', and 'rootCA.crl'.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
pvattest create -k hkd.crt --arpk arp.key -o attreq.bin --cert DigiCertCA.crt --cert IbmSigningKey.crt --offline --crl DigiCertCA.crl --crl IbmSigningKey.crl --crl rootCA.crl
|
||||
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
\fBpvattest\fP(1), \fBpvattest-verify\fP(1), \fBpvattest-perform\fP(1)
|
||||
@@ -1,50 +0,0 @@
|
||||
.\" Copyright 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH pvattest-perform 1 "07 June 2022" "s390-tools" "Attestation Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
\fBpvattest [OPTION?] perform [OPTIONS] \fP- execute an attestation measurement request
|
||||
\fB
|
||||
.SH DESCRIPTION
|
||||
Run a measurement of this system using '/dev/uv'. Works only if this device is
|
||||
available and the attestation Ultravisor facility is present.
|
||||
The input must be an attestation request created with 'pvattest create'.
|
||||
Output will contain the original request and the response from the Ultravisor.
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Show help options
|
||||
.TP
|
||||
.B
|
||||
\fB-i\fP, \fB--input\fP=\fBFILE\fP
|
||||
\fBFILE\fP specifies the attestation request as input.
|
||||
.TP
|
||||
.B
|
||||
\fB-o\fP, \fB--output\fP=\fBFILE\fP
|
||||
\fBFILE\fP specifies the output for the attestation result.
|
||||
.TP
|
||||
.B
|
||||
\fB-V\fP, \fB--verbose\fP
|
||||
Provide more detailed output (optional)
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH EXAMPLE
|
||||
Perform an attestation measurement with the attestation request 'arcb.bin' and write the output to 'measurement.bin'.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
pvattest perform --input attreq.bin --output attresp.bin
|
||||
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
\fBpvattest\fP(1), \fBpvattest-create\fP(1), \fBpvattest-verify\fP(1)
|
||||
@@ -1,75 +0,0 @@
|
||||
.\" Copyright 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH pvattest-verify 1 "07 June 2022" "s390-tools" "Attestation Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
\fBpvattest [OPTION?] verify [OPTIONS] \fP- verify an attestation measurement
|
||||
\fB
|
||||
.SH DESCRIPTION
|
||||
Verify that a previously generated attestation measurement of an IBM Secure Execution guest is as expected. Only verify attestation requests in a trusted environment, such as your workstation. Input must contain the response as produced by 'pvattest perform'. The protection key must be the one that was used to create the request by 'pvattest create'. Please delete it after verification. The header must be the IBM Secure Execution header of the image that was attested during 'pvattest perform'
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Show help options
|
||||
.TP
|
||||
.B
|
||||
\fB-i\fP, \fB--input\fP=\fBFILE\fP
|
||||
\fBFILE\fP specifies the attestation result as input.
|
||||
.TP
|
||||
.B
|
||||
\fB-o\fP, \fB--ouput\fP=\fBFILE\fP
|
||||
\fBFILE\fP specifies the output for the verification result.
|
||||
.TP
|
||||
.B
|
||||
\fB--hdr\fP=\fBFILE\fP
|
||||
Specify the header of the guest image. Exactly one is required.
|
||||
.TP
|
||||
.B
|
||||
\fB-a\fP, \fB--arpk\fP=\fBFILE\fP
|
||||
Use \fBFILE\fP to specify the GCM-AES256 key to decrypt the attestation request. Delete this key after verification.
|
||||
.TP
|
||||
.B
|
||||
\fB--format\fP=\fByaml\fP
|
||||
Define the output format.
|
||||
Default value: 'yaml'
|
||||
|
||||
Possible values:
|
||||
.RS 4
|
||||
- \fByaml\fP: Use YAML format
|
||||
.RE
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB-V\fP, \fB--verbose\fP
|
||||
Provide more detailed output (optional)
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH EXAMPLE
|
||||
To verify a measurement in 'measurement.bin' with the protection key 'arp.kep' and SE-guest header 'se_guest.hdr'.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
pvattest verify --input attresp.bin --arpk arp.key --hdr se_guest.hdr
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
If the verification was successful the program exists with zero.
|
||||
If the verification failed it exists with 2 and prints the following to stderr:
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
ERROR: Attestation measurement verification failed:
|
||||
Calculated and received attestation measurement are not the same.
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
\fBpvattest\fP(1), \fBpvattest-create\fP(1), \fBpvattest-perform\fP(1)
|
||||
@@ -1,104 +0,0 @@
|
||||
.\" Copyright 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH pvattest 1 "07 June 2022" "s390-tools" "Attestation Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
\fBpvattest [OPTION?] COMMAND [OPTIONS] \fP- create, perform, and verify attestation measurements
|
||||
\fB
|
||||
.RE
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.fam C
|
||||
\fBpvattest\fP \fIcreate\fP [\fIOPTIONS\fP]
|
||||
\fBpvattest\fP \fIperform\fP [\fIOPTIONS\fP]
|
||||
\fBpvattest\fP \fIverify\fP [\fIOPTIONS\fP]
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
.fam T
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Use \fBpvattest\fP to attest that an IBM Secure Execution guest is the correct guest, and that it was started in a secure manner.
|
||||
Run '\fBpvattest\fP \fIcreate\fP' and '\fBpvattest\fP \fIverify\fP' in a trusted environment only.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
create On a trusted system, creates an attestation request.
|
||||
|
||||
perform On the SE-guest to be attested, sends the attestation request to the Ultravisor and receives the answer.
|
||||
|
||||
verify On a trusted system, compares the answer from the Ultravisor to the one from your trusted environment. If they differ, the Secure Execution guest might be compromised.
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
For meaningful results, run '\fIcreate\fP' and '\fIverify\fP' in a trusted environment, like your workstation or a previously attested IBM Secure Execution guest. Otherwise, the attestation might be tampered with. For all certificates, revocation lists, and host-key documents, both the PEM and DER input formats are supported. If you run \fBpvattest\fP on a machine architecture other than z/Architecture, 'measure' is not available.
|
||||
.PP
|
||||
Use '\fBpvattest\fP [COMMAND] \fB-h\fP' to get detailed help
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Show help options
|
||||
.TP
|
||||
.B
|
||||
\fB-v\fP, \fB--version\fP
|
||||
Print the version and exit.
|
||||
.TP
|
||||
.B
|
||||
\fB-V\fP, \fB--verbose\fP
|
||||
Provide more detailed output (optional)
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH EXAMPLE
|
||||
For details refer to the man page of the command.
|
||||
.PP
|
||||
Create the request on a trusted system.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
trusted:~$ pvattest create -k hkd.crt --cert CA.crt --cert ibmsk.crt --arpk arp.key -o attreq.bin
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
On the SE-guest, \fIperform\fP the attestation.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
seguest:~$ pvattest perform -i attreq.bin -o attresp.bin
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
On a trusted system, \fIverify\fP that the response is correct. Here, the protection key from the creation and the SE-guest’s header is used to \fIverify\fP the measurement.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
trusted:~$ pvattest verify -i attresp.bin --arpk arp.key --hdr se_guest.hdr
|
||||
trusted:~$ echo $?
|
||||
0
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
|
||||
If the measurements do not match \fBpvattest\fP exits with code 2 and emits an error message. The SE-guest attestation failed.
|
||||
.PP
|
||||
.nf
|
||||
.fam C
|
||||
trusted:~$ pvattest verify -i wrongresp.bin --arpk arp.key --hdr se_guest.hdr
|
||||
ERROR: Attestation measurement verification failed:
|
||||
Calculated and received attestation measurement are not the same.
|
||||
trusted:~$ echo $?
|
||||
2
|
||||
|
||||
.fam T
|
||||
.fi
|
||||
|
||||
.SH SEE ALSO
|
||||
\fBpvattest\fP-\fIcreate\fP(1), \fBpvattest-\fIverify\fP\fP(1), \fBpvattest\fP-\fIperform\fP(1)
|
||||
2
pvattest/src/.gitignore
vendored
2
pvattest/src/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
.check-dep-pvattest
|
||||
.detect-openssl.dep.c
|
||||
@@ -1,113 +0,0 @@
|
||||
include ../../common.mak
|
||||
|
||||
BIN_PROGRAM = pvattest
|
||||
PKGDATADIR ?= "$(DESTDIR)$(TOOLS_DATADIR)/$(BIN_PROGRAM)"
|
||||
|
||||
SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
PVATTESTDIR := $(rootdir)/pvattest
|
||||
INCLUDE_PATHS = "$(SRC_DIR)" "$(rootdir)/include"
|
||||
INCLUDE_PARMS = $(addprefix -I,$(INCLUDE_PATHS))
|
||||
|
||||
LIBPV_DIR = $(rootdir)/libpv
|
||||
LIBPV = $(LIBPV_DIR)/libpv.a
|
||||
|
||||
WARNINGS := -Wall -Wextra -Wshadow \
|
||||
-Wcast-align -Wwrite-strings -Wmissing-prototypes \
|
||||
-Wmissing-declarations -Wredundant-decls -Wnested-externs \
|
||||
-Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \
|
||||
-Wpointer-arith -Wno-error=inline \
|
||||
-Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \
|
||||
$(NULL)
|
||||
|
||||
PVATTEST_SRCS := $(wildcard *.c) \
|
||||
$(NULL)
|
||||
|
||||
$(BIN_PROGRAM)_SRCS := \
|
||||
$(PVATTEST_SRCS) \
|
||||
$(NULL)
|
||||
|
||||
$(BIN_PROGRAM)_OBJS := $($(BIN_PROGRAM)_SRCS:.c=.o)
|
||||
|
||||
GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0)
|
||||
GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0)
|
||||
LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto)
|
||||
LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto)
|
||||
LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl)
|
||||
LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl)
|
||||
|
||||
ALL_CFLAGS += -std=gnu11 \
|
||||
-DPKGDATADIR=$(PKGDATADIR) \
|
||||
-DOPENSSL_API_COMPAT=0x10101000L \
|
||||
$(GLIB2_CFLAGS) \
|
||||
$(LIBCRYPTO_CFLAGS) \
|
||||
$(LIBCURL_CFLAGS) \
|
||||
$(WARNINGS) \
|
||||
$(NULL)
|
||||
|
||||
ifneq ($(call check_header_prereq,"asm/uvdevice.h"),yes)
|
||||
ALL_CFLAGS += -DPVATTEST_NO_PERFORM
|
||||
endif
|
||||
|
||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS)
|
||||
|
||||
BUILD_TARGETS := skip-$(BIN_PROGRAM)
|
||||
INSTALL_TARGETS := skip-$(BIN_PROGRAM)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
ifneq (${HAVE_LIBCURL}, 0)
|
||||
BUILD_TARGETS := $(BIN_PROGRAM)
|
||||
INSTALL_TARGETS := install-$(BIN_PROGRAM)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
install: $(INSTALL_TARGETS)
|
||||
|
||||
$(BIN_PROGRAM): $($(BIN_PROGRAM)_OBJS) $(LIBPV)
|
||||
|
||||
skip-$(BIN_PROGRAM):
|
||||
echo " SKIP $(BIN_PROGRAM) due to unresolved dependencies"
|
||||
|
||||
clean:
|
||||
$(RM) -f -- $($(BIN_PROGRAM)_OBJS) $(BIN_PROGRAM) .check-dep-$(BIN_PROGRAM) .detect-openssl.dep.c
|
||||
|
||||
install-$(BIN_PROGRAM): $(BIN_PROGRAM)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
$(INSTALL) -c $^ $(DESTDIR)$(USRBINDIR)
|
||||
|
||||
|
||||
.PHONY: all install clean skip-$(BIN_PROGRAM) install-$(BIN_PROGRAM)
|
||||
|
||||
$($(BIN_PROGRAM)_OBJS): .check-dep-$(BIN_PROGRAM)
|
||||
|
||||
.detect-openssl.dep.c:
|
||||
echo "#include <openssl/evp.h>" > $@
|
||||
echo "#if OPENSSL_VERSION_NUMBER < 0x10101000L" >> $@
|
||||
echo " #error openssl version 1.1.1 is required" >> $@
|
||||
echo "#endif" >> $@
|
||||
echo "static void __attribute__((unused)) test(void) {" >> $@
|
||||
echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@
|
||||
echo " EVP_MD_CTX_free(ctx);" >> $@
|
||||
echo "}" >> $@
|
||||
|
||||
.check-dep-$(BIN_PROGRAM): .detect-openssl.dep.c
|
||||
$(call check_dep, \
|
||||
"$(BIN_PROGRAM)", \
|
||||
"glib.h", \
|
||||
"glib2-devel / libglib2.0-dev", \
|
||||
"HAVE_GLIB2=0")
|
||||
$(call check_dep, \
|
||||
"$(BIN_PROGRAM)", \
|
||||
$^, \
|
||||
"openssl-devel / libssl-dev version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
"-I.")
|
||||
$(call check_dep, \
|
||||
"$(BIN_PROGRAM)", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0")
|
||||
touch $@
|
||||
@@ -1,423 +0,0 @@
|
||||
/*
|
||||
* Attestation Request Control Block related functions
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/hash.h"
|
||||
|
||||
#include "arcb.h"
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
|
||||
#define ARVN_VERSION_1 0x0100
|
||||
#define MAX_ARL 0x2000
|
||||
|
||||
typedef struct arcb_v1_hdr {
|
||||
uint64_t reserved0; /* 0x0000 */
|
||||
be32_t arvn; /* 0x0008 */
|
||||
be32_t arl; /* 0x000c */
|
||||
uint8_t iv[ARCB_V1_IV_SIZE]; /* 0x0010 */
|
||||
uint32_t reserved1c; /* 0x001c */
|
||||
uint8_t reserved20[7]; /* 0x0020 */
|
||||
uint8_t nks; /* 0x0027 */
|
||||
uint32_t reserved28; /* 0x0028 */
|
||||
be32_t sea; /* 0x002c */
|
||||
be64_t paf; /* 0x0030 */
|
||||
be32_t mai; /* 0x0038 */
|
||||
uint32_t reserved3c; /* 0x003c */
|
||||
PvEcdhPubKey cpk; /* 0x0040 */
|
||||
} __packed arcb_v1_hdr_t;
|
||||
G_STATIC_ASSERT(sizeof(arcb_v1_hdr_t) == 0xe0);
|
||||
|
||||
typedef struct arcb_v1_key_slot {
|
||||
uint8_t phkh[ARCB_V1_PHKH_SIZE];
|
||||
uint8_t warpk[ARCB_V1_ATTEST_PROT_KEY_SIZE];
|
||||
uint8_t kst[ARCB_V1_TAG_SIZE];
|
||||
} __packed arcb_v1_key_slot_t;
|
||||
G_STATIC_ASSERT(sizeof(arcb_v1_key_slot_t) == 0x50);
|
||||
|
||||
struct arcb_v1 {
|
||||
/* authenticated data */
|
||||
uint32_t arvn;
|
||||
uint32_t mai;
|
||||
uint64_t paf;
|
||||
GBytes *iv;
|
||||
EVP_PKEY *evp_cust_pub_key;
|
||||
GSList *host_key_slots;
|
||||
|
||||
/* confidential data */
|
||||
GBytes *confidential_measurement_key;
|
||||
GBytes *confidential_optional_nonce;
|
||||
GBytes *confidential_att_req_prot_key;
|
||||
};
|
||||
|
||||
void arcb_v1_clear_free(arcb_v1_t *arcb)
|
||||
{
|
||||
if (!arcb)
|
||||
return;
|
||||
|
||||
g_slist_free_full(arcb->host_key_slots, g_free);
|
||||
g_bytes_unref(arcb->confidential_measurement_key);
|
||||
g_bytes_unref(arcb->confidential_optional_nonce);
|
||||
g_bytes_unref(arcb->confidential_att_req_prot_key);
|
||||
g_bytes_unref(arcb->iv);
|
||||
EVP_PKEY_free(arcb->evp_cust_pub_key);
|
||||
g_free(arcb);
|
||||
}
|
||||
|
||||
static void arcb_v1_set_paf(arcb_v1_t *arcb, const uint64_t paf, GError **error)
|
||||
{
|
||||
const uint64_t known_flags = ARCB_V1_PAF_ALL & ~ARCB_V1_PAF_NONCE;
|
||||
|
||||
if ((paf & ARCB_V1_PAF_NONCE) != 0) {
|
||||
g_set_error(error, ARCB_ERROR, ARCB_ERR_INVALID_PAF,
|
||||
_("The given paf (%#.16lx) specifies the NONCE flag (%#.16lx)."), paf,
|
||||
ARCB_V1_PAF_NONCE);
|
||||
return;
|
||||
}
|
||||
if ((paf & ~known_flags) != 0)
|
||||
pvattest_log_warning(
|
||||
_("The given paf (%#.16lx) specifies unknown flags. Use at your own risk!"),
|
||||
paf, known_flags);
|
||||
arcb->paf = paf;
|
||||
}
|
||||
|
||||
arcb_v1_t *arcb_v1_new(GBytes *arpk, GBytes *iv, uint32_t mai, EVP_PKEY *evp_cpk, GBytes *mkey,
|
||||
uint64_t paf, GError **error)
|
||||
{
|
||||
g_autoptr(arcb_v1_t) arcb = g_new0(arcb_v1_t, 1);
|
||||
|
||||
g_assert(g_bytes_get_size(iv) == ARCB_V1_IV_SIZE);
|
||||
g_assert(g_bytes_get_size(arpk) == ARCB_V1_ATTEST_PROT_KEY_SIZE);
|
||||
g_assert(g_bytes_get_size(mkey) == HMAC_SHA512_KEY_SIZE);
|
||||
|
||||
pv_wrapped_g_assert(arpk);
|
||||
pv_wrapped_g_assert(iv);
|
||||
pv_wrapped_g_assert(evp_cpk);
|
||||
pv_wrapped_g_assert(mkey);
|
||||
|
||||
arcb->arvn = ARVN_VERSION_1;
|
||||
arcb->mai = mai;
|
||||
arcb_v1_set_paf(arcb, paf, error);
|
||||
if (*error)
|
||||
return NULL;
|
||||
arcb->iv = g_bytes_ref(iv);
|
||||
|
||||
if (EVP_PKEY_up_ref(evp_cpk) != 1)
|
||||
g_abort();
|
||||
arcb->evp_cust_pub_key = evp_cpk;
|
||||
|
||||
arcb->confidential_att_req_prot_key = g_bytes_ref(arpk);
|
||||
arcb->confidential_measurement_key = g_bytes_ref(mkey);
|
||||
|
||||
return g_steal_pointer(&arcb);
|
||||
}
|
||||
|
||||
int arcb_v1_add_key_slot(arcb_v1_t *arcb, EVP_PKEY *evp_host, GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) warpk = NULL, tag = NULL, phkh = NULL;
|
||||
g_autoptr(GBytes) exchange_key = NULL, iv = NULL;
|
||||
g_autofree arcb_v1_key_slot_t *key_slot = NULL;
|
||||
g_autofree PvEcdhPubKey *ecdh_host = NULL;
|
||||
g_autofree uint8_t *iv_raw = NULL;
|
||||
PvCipherParms parms;
|
||||
int64_t gcm_rc;
|
||||
|
||||
g_assert(arcb->confidential_att_req_prot_key);
|
||||
|
||||
pv_wrapped_g_assert(arcb);
|
||||
pv_wrapped_g_assert(evp_host);
|
||||
|
||||
/* encrypt (=wrap) attestation request protection key, store warpk + tag */
|
||||
exchange_key = pv_derive_exchange_key(arcb->evp_cust_pub_key, evp_host, error);
|
||||
if (!exchange_key)
|
||||
return -1;
|
||||
|
||||
iv_raw = g_malloc0(ARCB_V1_IV_SIZE);
|
||||
iv = g_bytes_new_take(g_steal_pointer(&iv_raw), ARCB_V1_IV_SIZE);
|
||||
if (!iv)
|
||||
g_abort();
|
||||
|
||||
parms.key = exchange_key;
|
||||
parms.iv = iv;
|
||||
parms.cipher = EVP_aes_256_gcm();
|
||||
parms.tag_size = ARCB_V1_TAG_SIZE;
|
||||
gcm_rc = pv_gcm_encrypt(arcb->confidential_att_req_prot_key, NULL, &parms, &warpk, &tag,
|
||||
error);
|
||||
if (gcm_rc != ARCB_V1_ATTEST_PROT_KEY_SIZE)
|
||||
return -1;
|
||||
|
||||
/* calculate public host key hash */
|
||||
ecdh_host = pv_evp_pkey_to_ecdh_pub_key(evp_host, error);
|
||||
if (!ecdh_host)
|
||||
return -1;
|
||||
phkh = pv_sha256_hash(ecdh_host->data, sizeof(ecdh_host->data), error);
|
||||
if (!phkh)
|
||||
return -1;
|
||||
|
||||
/* copy to list */
|
||||
g_assert(g_bytes_get_size(warpk) == sizeof(key_slot->warpk));
|
||||
g_assert(g_bytes_get_size(tag) == sizeof(key_slot->kst));
|
||||
g_assert(g_bytes_get_size(phkh) == sizeof(key_slot->phkh));
|
||||
|
||||
key_slot = g_malloc0(sizeof(*key_slot));
|
||||
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk, NULL);
|
||||
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag, NULL);
|
||||
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh, NULL);
|
||||
|
||||
arcb->host_key_slots = g_slist_prepend(arcb->host_key_slots, g_steal_pointer(&key_slot));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void arcb_v1_set_nonce(arcb_v1_t *arcb, GBytes *nonce)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
pv_wrapped_g_assert(nonce);
|
||||
arcb_v1_rm_nonce(arcb);
|
||||
g_assert(!arcb->confidential_optional_nonce);
|
||||
|
||||
g_assert(g_bytes_get_size(nonce) == ARCB_V1_NONCE_SIZE);
|
||||
arcb->confidential_optional_nonce = g_bytes_ref(nonce);
|
||||
|
||||
arcb->paf |= ARCB_V1_PAF_NONCE;
|
||||
}
|
||||
|
||||
void arcb_v1_rm_nonce(arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
if (!arcb->confidential_optional_nonce)
|
||||
return;
|
||||
g_bytes_unref(arcb->confidential_optional_nonce);
|
||||
arcb->confidential_optional_nonce = NULL;
|
||||
arcb->paf &= ~ARCB_V1_PAF_NONCE;
|
||||
}
|
||||
|
||||
GBytes *arcb_v1_serialize(const arcb_v1_t *arcb, GError **error)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
g_autoptr(GByteArray) arcb_gba = NULL;
|
||||
g_autoptr(GBytes) confidential_area = NULL;
|
||||
g_autoptr(GBytes) aad = NULL;
|
||||
g_autoptr(GBytes) art = NULL;
|
||||
g_autoptr(GBytes) encrypted_area = NULL;
|
||||
g_autoptr(GBytes) result = NULL;
|
||||
g_autofree PvEcdhPubKey *ecdh_cpk = NULL;
|
||||
PvCipherParms parms = {
|
||||
.cipher = EVP_aes_256_gcm(),
|
||||
.tag_size = AES_256_GCM_TAG_SIZE,
|
||||
};
|
||||
size_t att_req_len = 0, nks = 0, sea = 0;
|
||||
|
||||
arcb_v1_hdr_t hdr = {
|
||||
.arvn = GUINT32_TO_BE(arcb->arvn),
|
||||
.paf = GUINT64_TO_BE(arcb->paf),
|
||||
.mai = GUINT32_TO_BE(arcb->mai),
|
||||
};
|
||||
|
||||
g_assert(arcb->host_key_slots);
|
||||
|
||||
/* calculate sizes */
|
||||
nks = g_slist_length(arcb->host_key_slots);
|
||||
g_assert(nks < 0xFF);
|
||||
|
||||
sea = g_bytes_get_size(arcb->confidential_measurement_key);
|
||||
if (arcb->confidential_optional_nonce)
|
||||
sea += g_bytes_get_size(arcb->confidential_optional_nonce);
|
||||
|
||||
g_assert(sea == HMAC_SHA512_KEY_SIZE || sea == HMAC_SHA512_KEY_SIZE + ARCB_V1_NONCE_SIZE);
|
||||
|
||||
att_req_len = sizeof(hdr) + nks * sizeof(arcb_v1_key_slot_t) + HMAC_SHA512_KEY_SIZE +
|
||||
ARCB_V1_TAG_SIZE;
|
||||
if (arcb->confidential_optional_nonce)
|
||||
att_req_len += ARCB_V1_NONCE_SIZE;
|
||||
|
||||
g_assert(att_req_len <= MAX_ARL);
|
||||
|
||||
/* copy plain data to contiguous memory */
|
||||
hdr.arl = GUINT32_TO_BE((uint32_t)att_req_len);
|
||||
|
||||
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv, NULL);
|
||||
hdr.nks = (uint8_t)nks;
|
||||
hdr.sea = GUINT32_TO_BE((uint32_t)sea);
|
||||
ecdh_cpk = pv_evp_pkey_to_ecdh_pub_key(arcb->evp_cust_pub_key, error);
|
||||
memcpy(&hdr.cpk, ecdh_cpk, sizeof(*ecdh_cpk));
|
||||
arcb_gba = g_byte_array_sized_new((guint)att_req_len);
|
||||
g_byte_array_append(arcb_gba, (const uint8_t *)&hdr, sizeof(hdr));
|
||||
|
||||
for (GSList *elem = arcb->host_key_slots; elem; elem = elem->next)
|
||||
g_byte_array_append(arcb_gba, elem->data, sizeof(arcb_v1_key_slot_t));
|
||||
|
||||
/* encrypt the confidential data */
|
||||
confidential_area = secure_gbytes_concat(arcb->confidential_measurement_key,
|
||||
arcb->confidential_optional_nonce);
|
||||
parms.key = arcb->confidential_att_req_prot_key;
|
||||
parms.iv = arcb->iv;
|
||||
aad = g_bytes_new(arcb_gba->data, arcb_gba->len);
|
||||
pv_gcm_encrypt(confidential_area, aad, &parms, &encrypted_area, &art, error);
|
||||
if (*error)
|
||||
return NULL;
|
||||
|
||||
g_byte_array_append(arcb_gba, g_bytes_get_data(encrypted_area, NULL), (guint)sea);
|
||||
g_byte_array_append(arcb_gba, g_bytes_get_data(art, NULL), ARCB_V1_TAG_SIZE);
|
||||
|
||||
result = g_byte_array_free_to_bytes(arcb_gba);
|
||||
arcb_gba = NULL;
|
||||
return g_steal_pointer(&result);
|
||||
}
|
||||
|
||||
uint32_t arcb_v1_get_required_measurement_size(const arcb_v1_t *arcb, GError **error)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
switch (arcb->mai) {
|
||||
case MAI_HMAC_SHA512:
|
||||
return HMAC_SHA512_KEY_SIZE;
|
||||
default:
|
||||
g_set_error(error, ARCB_ERROR, ARCB_ERR_INVALID_MAI,
|
||||
_("Unknown measurement algorithm ID specified (%#x)."), arcb->mai);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t arcb_v1_get_required_additional_size(const arcb_v1_t *arcb)
|
||||
{
|
||||
uint32_t size = 0;
|
||||
|
||||
pv_wrapped_g_assert(arcb);
|
||||
|
||||
if (arcb_v1_additional_has_phkh_image(arcb))
|
||||
size += ARCB_V1_PHKH_SIZE;
|
||||
if (arcb_v1_additional_has_phkh_attest(arcb))
|
||||
size += ARCB_V1_PHKH_SIZE;
|
||||
return size;
|
||||
}
|
||||
|
||||
gboolean arcb_v1_use_nonce(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
return arcb->confidential_optional_nonce != NULL;
|
||||
}
|
||||
|
||||
gboolean arcb_v1_additional_has_phkh_image(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
return (arcb->paf & ARCB_V1_PAF_AAD_PHKH_HEADER) != 0;
|
||||
}
|
||||
|
||||
gboolean arcb_v1_additional_has_phkh_attest(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
return (arcb->paf & ARCB_V1_PAF_AAD_PHKH_ATTEST) != 0;
|
||||
}
|
||||
|
||||
GBytes *arcb_v1_get_measurement_key(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
return g_bytes_ref(arcb->confidential_measurement_key);
|
||||
}
|
||||
|
||||
GBytes *arcb_v1_get_nonce(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
if (arcb->confidential_optional_nonce)
|
||||
return g_bytes_ref(arcb->confidential_optional_nonce);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBytes *arcb_v1_get_arp_key(const arcb_v1_t *arcb)
|
||||
{
|
||||
pv_wrapped_g_assert(arcb);
|
||||
return g_bytes_ref(arcb->confidential_att_req_prot_key);
|
||||
}
|
||||
|
||||
static gboolean is_v1_arcb(size_t aad_size, size_t sea, size_t arl, size_t serialized_arcb_size,
|
||||
uint32_t arcb_version, gboolean has_nonce)
|
||||
{
|
||||
gboolean result = aad_size + sea + ARCB_V1_TAG_SIZE == arl;
|
||||
|
||||
result &= arl <= serialized_arcb_size;
|
||||
result &= arcb_version == ARVN_VERSION_1;
|
||||
result &= has_nonce ? sea == HMAC_SHA512_KEY_SIZE + ARCB_V1_NONCE_SIZE :
|
||||
sea == HMAC_SHA512_KEY_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean arcb_v1_verify_serialized_arcb(GBytes *serialized_arcb, GBytes *arpk,
|
||||
GBytes **measurement_key, GBytes **optional_nonce,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) encr = NULL, decr = NULL, aad = NULL, tag = NULL, iv = NULL;
|
||||
const struct arcb_v1_hdr *serialized_arcb_hdr;
|
||||
const uint8_t *encr_u8, *aad_u8, *tag_u8;
|
||||
const uint8_t *serialized_arcb_u8;
|
||||
size_t serialized_arcb_size;
|
||||
uint32_t arcb_version, mai;
|
||||
size_t aad_size, arl, sea;
|
||||
PvCipherParms parms;
|
||||
gboolean has_nonce;
|
||||
uint64_t paf;
|
||||
|
||||
pv_wrapped_g_assert(serialized_arcb);
|
||||
pv_wrapped_g_assert(arpk);
|
||||
serialized_arcb_u8 = g_bytes_get_data(serialized_arcb, &serialized_arcb_size);
|
||||
serialized_arcb_hdr = (const arcb_v1_hdr_t *)serialized_arcb_u8;
|
||||
arl = GUINT32_FROM_BE(serialized_arcb_hdr->arl);
|
||||
arcb_version = GUINT32_FROM_BE(serialized_arcb_hdr->arvn);
|
||||
mai = GUINT32_FROM_BE(serialized_arcb_hdr->mai);
|
||||
|
||||
aad_u8 = serialized_arcb_u8;
|
||||
aad_size = sizeof(*serialized_arcb_hdr) +
|
||||
serialized_arcb_hdr->nks * sizeof(arcb_v1_key_slot_t);
|
||||
encr_u8 = aad_u8 + aad_size;
|
||||
sea = GUINT32_FROM_BE(serialized_arcb_hdr->sea);
|
||||
tag_u8 = encr_u8 + sea;
|
||||
paf = GUINT64_FROM_BE(serialized_arcb_hdr->paf);
|
||||
has_nonce = (paf & ARCB_V1_PAF_NONCE) != 0;
|
||||
|
||||
if (!is_v1_arcb(aad_size, sea, arl, serialized_arcb_size, arcb_version, has_nonce)) {
|
||||
g_set_error(error, ARCB_ERROR, ARCB_ERR_INVALID_ARCB,
|
||||
_("The provided attestation request is not valid"));
|
||||
return FALSE;
|
||||
}
|
||||
if (mai != MAI_HMAC_SHA512) {
|
||||
g_set_error(error, ARCB_ERROR, ARCB_ERR_INVALID_MAI,
|
||||
_("Unsupported measurement argument ID (%#x)"), mai);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
aad = g_bytes_new(aad_u8, aad_size);
|
||||
encr = g_bytes_new(encr_u8, sea);
|
||||
tag = g_bytes_new(tag_u8, ARCB_V1_TAG_SIZE);
|
||||
iv = g_bytes_new(serialized_arcb_hdr->iv, sizeof(serialized_arcb_hdr->iv));
|
||||
|
||||
parms.cipher = EVP_aes_256_gcm();
|
||||
parms.tag_size = AES_256_GCM_TAG_SIZE;
|
||||
parms.key = arpk;
|
||||
parms.iv = iv;
|
||||
pv_gcm_decrypt(encr, aad, tag, &parms, &decr, error);
|
||||
if (*error) {
|
||||
GError *tmp_error = NULL;
|
||||
|
||||
g_set_error(&tmp_error, ARCB_ERROR, ARCB_ERR_INVALID_ARCB,
|
||||
_("Cannot verify the attestation request: %s"), (*error)->message);
|
||||
g_clear_error(error);
|
||||
g_propagate_error(error, tmp_error);
|
||||
return FALSE;
|
||||
}
|
||||
if (measurement_key)
|
||||
*measurement_key = g_bytes_new(g_bytes_get_data(decr, NULL), HMAC_SHA512_KEY_SIZE);
|
||||
if (optional_nonce && has_nonce)
|
||||
*optional_nonce =
|
||||
g_bytes_new((uint8_t *)g_bytes_get_data(decr, NULL) + HMAC_SHA512_KEY_SIZE,
|
||||
ARCB_V1_NONCE_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Attestation Request Control Block related functions
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef PVATTEST_ARCB_H
|
||||
#define PVATTEST_ARCB_H
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define MAI_HMAC_RESERVED_INVALID 0
|
||||
#define MAI_HMAC_SHA512 0x1
|
||||
|
||||
#define HMAC_SHA512_KEY_SIZE 64
|
||||
#define ARCB_V1_ATTEST_PROT_KEY_SIZE 32
|
||||
#define ARCB_V1_NONCE_SIZE 16
|
||||
#define ARCB_V1_TAG_SIZE 16
|
||||
#define ARCB_V1_IV_SIZE 12
|
||||
#define ARCB_V1_PHKH_SIZE 32
|
||||
|
||||
/* Optional nonce in ARCB */
|
||||
#define ARCB_V1_PAF_NONCE PV_MSB(1)
|
||||
/* Public host key hash used to unseal SE header added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_HEADER PV_MSB(2)
|
||||
/* Public host key hash used to unseal this attestation added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_ATTEST PV_MSB(3)
|
||||
/* Temporary backup-host-key use allowed */
|
||||
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED PV_MSB(62)
|
||||
|
||||
/* Global not-host-specific key allowed */
|
||||
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED PV_MSB(63)
|
||||
|
||||
#define ARCB_V1_PAF_ALL \
|
||||
(ARCB_V1_PAF_NONCE | ARCB_V1_PAF_AAD_PHKH_HEADER | ARCB_V1_PAF_AAD_PHKH_ATTEST | \
|
||||
ARCB_V1_PAF_TMP_BACKUP_ALLOWED | ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED)
|
||||
|
||||
typedef struct arcb_v1 arcb_v1_t;
|
||||
|
||||
/** arcb_v1_new:
|
||||
*
|
||||
* @arpk: Attestation Request Protection key. AES-GCM-256 key to
|
||||
* protect Measurement Key and Nonce.
|
||||
* Must be ´ARCB_V1_ATTEST_PROT_KEY_SIZE´ bytes long.
|
||||
* @iv: IV for protecting Measuremt Key and Nonce.
|
||||
* Should be random for each new ARPK.
|
||||
* Must be ´ARCB_V1_IV_SIZE´ bytes long.
|
||||
* @mai: Measurement Algorithm Identifier for the attestation measurement.
|
||||
* See ´enum mai´
|
||||
* @evp_cpk: Customer key in EVP_PKEY format. Must contain private and public key pair.
|
||||
* @mkey: Measurement key to calculate the Measurement.
|
||||
* Must be ´HMAC_SHA512_KEY_SIZE´ bytes long.
|
||||
* @paf: Plain text Attestation Flags. See ´enum plaintext_attestattion_flags´.
|
||||
* ´ARCB_V1_PAF_NONCE´ must not be set.
|
||||
* @error: GError. *error will != NULL if error occours.
|
||||
*
|
||||
* arpk, mkey, and iv must me correct size
|
||||
* If not this is considered as a programming error (No warning;
|
||||
* Results in Assertion or undefined behavior).
|
||||
*
|
||||
* GBytes will be ref'ed.
|
||||
*
|
||||
* All numbers must be in system byte order and will be converted to big endian
|
||||
* if needed.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): new ARCB context.
|
||||
*/
|
||||
arcb_v1_t *arcb_v1_new(GBytes *arpk, GBytes *iv, uint32_t mai, EVP_PKEY *evp_cpk, GBytes *mkey,
|
||||
uint64_t paf, GError **error) PV_NONNULL(1, 2, 4, 5);
|
||||
void arcb_v1_clear_free(arcb_v1_t *arcb);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(arcb_v1_t, arcb_v1_clear_free)
|
||||
|
||||
/** arcb_v1_add_key_slot:
|
||||
*
|
||||
* @arcb: ARCB context.
|
||||
* @evp_host: Host public key.
|
||||
* @error: GError. *error will != NULL if error occours.
|
||||
*
|
||||
* Builds a key slot. Calculates exchange key, wraps ARPK with the exchange key.
|
||||
* Calculates the public host key hash. Calculates the key slot tag.
|
||||
* Adds it to the ARCB.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise
|
||||
*/
|
||||
int arcb_v1_add_key_slot(arcb_v1_t *arcb, EVP_PKEY *evp_host, GError **error) PV_NONNULL(1, 2);
|
||||
void arcb_v1_set_nonce(arcb_v1_t *arcb, GBytes *nonce) PV_NONNULL(1, 2);
|
||||
void arcb_v1_rm_nonce(arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
|
||||
/** arcb_v1_serialize:
|
||||
*
|
||||
* @arcb: ARCB context.
|
||||
* @error: GError. *error will != NULL if error occurs.
|
||||
*
|
||||
* Will create a valid ARCB for the UV. Including encrypting confidential data.
|
||||
* At least one key_slot must be added beforehand.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): The serialized ARCB which can be added to the
|
||||
* Retrieve Attestation Measurement UVC as GBytes.
|
||||
*/
|
||||
GBytes *arcb_v1_serialize(const arcb_v1_t *arcb, GError **error) PV_NONNULL(1, 2);
|
||||
|
||||
uint32_t arcb_v1_get_required_measurement_size(const arcb_v1_t *arcb, GError **error)
|
||||
PV_NONNULL(1, 2);
|
||||
uint32_t arcb_v1_get_required_additional_size(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
gboolean arcb_v1_use_nonce(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
gboolean arcb_v1_additional_has_phkh_image(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
gboolean arcb_v1_additional_has_phkh_attest(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
|
||||
GBytes *arcb_v1_get_measurement_key(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
GBytes *arcb_v1_get_nonce(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
GBytes *arcb_v1_get_arp_key(const arcb_v1_t *arcb) PV_NONNULL(1);
|
||||
|
||||
/** arcb_v1_verify_serialized_arcb:
|
||||
*
|
||||
* @serialized_arcb: binary ARCB in UV readable format.
|
||||
* @arpk: Attestation Request Protection key that was used to create serialized_arpk
|
||||
* @measurement_key: Output parameter: decrypted measurement key if no error.
|
||||
* May be NULL if not interested for this output.
|
||||
* @optional_nonce: Output parameter: decrypted nonce if no error.
|
||||
* May be NULL if not interested for this output.
|
||||
* @error: GError. *error will != NULL if error occurs.
|
||||
*
|
||||
*
|
||||
* Checks if sizes are sound and flags are known by this implementation.
|
||||
* Decrypts Measurement key and nonce (if given) and verifies ARCB tag.
|
||||
*
|
||||
* Returns: TRUE if ARCB is valid, including matching ARCB tag. Otherwise FALSE.
|
||||
*
|
||||
*/
|
||||
gboolean arcb_v1_verify_serialized_arcb(GBytes *serialized_arcb, GBytes *arpk,
|
||||
GBytes **measurement_key, GBytes **optional_nonce,
|
||||
GError **error) PV_NONNULL(1, 2);
|
||||
|
||||
#define ARCB_ERROR g_quark_from_static_string("pv-arcb_error-quark")
|
||||
typedef enum arcb_error {
|
||||
ARCB_ERR_INVALID_ARCB,
|
||||
ARCB_ERR_INVALID_PAF,
|
||||
ARCB_ERR_INVALID_MAI,
|
||||
ARCB_ERR_UNABLE_ENCR_ARPK,
|
||||
} arcb_error_e;
|
||||
|
||||
#endif /* PVATTEST_ARCB_H */
|
||||
@@ -1,674 +0,0 @@
|
||||
/*
|
||||
* Definitions used for parsing arguments.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "argparse.h"
|
||||
#include "log.h"
|
||||
#include "common.h"
|
||||
|
||||
#define DEFAULT_OUTPUT_FILE_NAME "attest.bin"
|
||||
#define DEFAULT_OPTION_PHKH_IMG FALSE
|
||||
#define DEFAULT_OPTION_PHKH_ATT FALSE
|
||||
#define DEFAULT_OPTION_NO_VERIFY FALSE
|
||||
#define DEFAULT_OPTION_ONLINE TRUE
|
||||
#define DEFAULT_OPTION_NONCE TRUE
|
||||
|
||||
static pvattest_config_t pvattest_config = {
|
||||
.general = {
|
||||
.log_level = PVATTEST_LOG_LVL_DEFAULT,
|
||||
},
|
||||
.create = {
|
||||
.output_path = NULL,
|
||||
.host_key_document_paths = NULL,
|
||||
.crl_paths = NULL,
|
||||
.root_ca_path = NULL,
|
||||
.certificate_paths = NULL,
|
||||
.arp_key_out_path = NULL,
|
||||
.phkh_img = DEFAULT_OPTION_PHKH_IMG,
|
||||
.phkh_att = DEFAULT_OPTION_PHKH_ATT,
|
||||
.online = DEFAULT_OPTION_ONLINE,
|
||||
.use_nonce = DEFAULT_OPTION_NONCE,
|
||||
.paf = 0,
|
||||
.x_aad_size = -1,
|
||||
},
|
||||
.perform = {
|
||||
.output_path = NULL,
|
||||
.input_path = NULL,
|
||||
},
|
||||
.verify = {
|
||||
.input_path = NULL,
|
||||
.output_path = NULL,
|
||||
.hdr_path = NULL,
|
||||
.arp_key_in_path = NULL,
|
||||
.output_fmt = VERIFY_FMT_YAML,
|
||||
},
|
||||
};
|
||||
typedef gboolean (*verify_options_fn_t)(GError **);
|
||||
|
||||
static gboolean check_for_non_null(const void *ptr, const char *msg, GError **error)
|
||||
{
|
||||
if (!ptr) {
|
||||
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG, "%s", msg);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean _check_for_invalid_path(const char *path, gboolean must_exist, GError **error)
|
||||
{
|
||||
int cached_errno = 0;
|
||||
|
||||
g_assert(path);
|
||||
|
||||
if (must_exist) {
|
||||
if (access(path, F_OK | R_OK) != 0)
|
||||
cached_errno = errno;
|
||||
}
|
||||
if (cached_errno) {
|
||||
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG, "Cannot access '%s': %s",
|
||||
path, g_strerror(cached_errno));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean check_for_optional_invalid_path(const char *path, gboolean must_exist,
|
||||
GError **error)
|
||||
{
|
||||
if (!path)
|
||||
return TRUE;
|
||||
return _check_for_invalid_path(path, must_exist, error);
|
||||
}
|
||||
|
||||
static gboolean check_for_invalid_path(const char *path, gboolean must_exist, const char *null_msg,
|
||||
GError **error)
|
||||
{
|
||||
if (!check_for_non_null(path, null_msg, error))
|
||||
return FALSE;
|
||||
return _check_for_invalid_path(path, must_exist, error);
|
||||
}
|
||||
|
||||
static gboolean _check_file_list(char **path_list, gboolean must_exist, GError **error)
|
||||
{
|
||||
char *path = NULL;
|
||||
for (char **path_it = path_list; path_it != NULL && *path_it != NULL; path_it++) {
|
||||
path = *path_it;
|
||||
if (!_check_for_invalid_path(path, must_exist, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean check_optional_file_list(char **path_list, gboolean must_exist, GError **error)
|
||||
{
|
||||
if (!path_list)
|
||||
return TRUE;
|
||||
return _check_file_list(path_list, must_exist, error);
|
||||
}
|
||||
|
||||
static gboolean check_file_list(char **path_list, gboolean must_exist, const char *null_msg,
|
||||
GError **error)
|
||||
{
|
||||
if (!check_for_non_null(path_list, null_msg, error))
|
||||
return FALSE;
|
||||
return _check_file_list(path_list, must_exist, error);
|
||||
}
|
||||
|
||||
static gboolean hex_str_toull(const char *nptr, uint64_t *dst, GError **error)
|
||||
{
|
||||
uint64_t value;
|
||||
gchar *end;
|
||||
|
||||
g_assert(dst);
|
||||
|
||||
if (!g_str_is_ascii(nptr)) {
|
||||
g_set_error(
|
||||
error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
||||
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
||||
nptr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
value = g_ascii_strtoull(nptr, &end, 16);
|
||||
if ((value == G_MAXUINT64 && errno == ERANGE) || (end && *end != '\0')) {
|
||||
g_set_error(
|
||||
error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
||||
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
||||
nptr);
|
||||
return FALSE;
|
||||
}
|
||||
*dst = value;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
/************************* SHARED OPTIONS *************************************/
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_host_key_document(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "host-key-document", .short_name = 'k', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
||||
.description = \
|
||||
"FILE specifies a host-key document. At least one is required.\n" __indent \
|
||||
"Specify this option multiple times to enable the request for\n" __indent \
|
||||
"more than one host.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_certs(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "cert", .short_name = 'C', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
||||
.description = "FILE contains a certificate that is used to\n" __indent \
|
||||
"establish a chain of trust for the verification\n" __indent \
|
||||
"of the host-key documents. The IBM Z signing\n" __indent \
|
||||
"key and intermediate CA certificate (signed\n" __indent \
|
||||
"by the root CA) are required.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_crls(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "crl", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
||||
.description = "FILE contains a certificate revocation list (optional).\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_root_ca(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "root-ca", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
||||
.description = "Use FILE as the trusted root CA instead the\n" __indent \
|
||||
"root CAs that are installed on the system (optional).\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_guest_hdr(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "hdr", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = "FILE specifies the header of the guest image.\n" __indent \
|
||||
"Exactly one is required.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_input(__arg_data, __additional_text, __indent) \
|
||||
{ \
|
||||
.long_name = "input", .short_name = 'i', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = "FILE specifies the " __additional_text " as input.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_output(__arg_data, __additional_text, __indent) \
|
||||
{ \
|
||||
.long_name = "output", .short_name = 'o', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = "FILE specifies the output for the " __additional_text "\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_att_prot_key_save(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "arpk", .short_name = 'a', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = \
|
||||
"Save the protection key as GCM-AES256 key in FILE\n" __indent \
|
||||
"Do not publish this key, otherwise your attestation is compromised.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
#define _entry_att_prot_key_load(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "arpk", .short_name = 'a', .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = "Use FILE to specify the GCM-AES256 key to decrypt\n" __indent \
|
||||
"the attestation request.\n" __indent \
|
||||
"Delete this key after verification.\n", \
|
||||
.arg_description = "FILE", \
|
||||
}
|
||||
|
||||
#define _entry_phkh_img(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-phkh-img", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
||||
.description = "Add the public host key hash of the\n" __indent \
|
||||
"image header used to decrypt\n" __indent \
|
||||
"the secure guest to the measurement. (optional)\n" \
|
||||
}
|
||||
|
||||
#define _entry_phkh_att(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-phkh-att", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
||||
.description = "Add the public host key hash of the\n" __indent \
|
||||
"attestation header used to decrypt\n" __indent \
|
||||
"the attestation request to the measurement. (optional)\n" \
|
||||
}
|
||||
|
||||
#define _entry_no_verify(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "no-verify", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
||||
.description = "Disable the host-key document verification.\n" __indent \
|
||||
"(optional)\n", \
|
||||
}
|
||||
|
||||
#define _entry_offline_maps_to_online(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "offline", .short_name = 0, .flags = G_OPTION_FLAG_REVERSE, \
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
||||
.description = "Don't download CRLs. (optional)\n", \
|
||||
}
|
||||
|
||||
#define _entry_verbose(__indent) \
|
||||
{ \
|
||||
.long_name = "verbose", .short_name = 'V', .flags = G_OPTION_FLAG_NO_ARG, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, .arg_data = &increase_log_lvl, \
|
||||
.description = "Provide more detailed output. (optional)\n", \
|
||||
.arg_description = NULL, \
|
||||
}
|
||||
|
||||
#define _entry_x_paf(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-paf", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, .arg_data = __arg_data, \
|
||||
.description = "Specify the Plain text Attestation Flags\n" __indent \
|
||||
"as a hexadecimal value. Flags that change\n" __indent \
|
||||
"the paf (--phkh-*) take precedence over\n" __indent \
|
||||
"this flag.\n" __indent \
|
||||
"Setting the nonce paf is not allowed here.\n" __indent \
|
||||
"(optional, default 0x0)\n", \
|
||||
.arg_description = "HEX", \
|
||||
}
|
||||
|
||||
#define _entry_x_no_nonce(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-no-nonce", .short_name = 0, .flags = G_OPTION_FLAG_REVERSE, \
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
||||
.description = "Do not use a nonce in the request.\n" __indent \
|
||||
"(optional, not recommended)\n" \
|
||||
}
|
||||
|
||||
#define _entry_x_aad_size(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-add-size", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_INT, .arg_data = __arg_data, \
|
||||
.description = "Specify the size of the additional area\n" __indent \
|
||||
"Overwrite every flag that changes\n" __indent \
|
||||
"this size implicitly. No verification is performed!\n" __indent \
|
||||
"Ignored if negative.\n" __indent "(optional, default ignored)\n", \
|
||||
.arg_description = "INT" \
|
||||
}
|
||||
|
||||
#define _entry_x_user_data(__arg_data, __indent) \
|
||||
{ \
|
||||
.long_name = "x-user-data", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
.description = "Use FILE to specify the user data.\n", .arg_description = "FILE", \
|
||||
}
|
||||
|
||||
#define _entry__verify_format(__indent) \
|
||||
{ \
|
||||
.long_name = "format", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, .arg_data = &set_verify_output_format, \
|
||||
.description = "Define the output format.\n" __indent \
|
||||
"Defaults to 'yaml'. (possible values: 'yaml')\n", \
|
||||
.arg_description = "FORMAT", \
|
||||
}
|
||||
|
||||
static gboolean increase_log_lvl(G_GNUC_UNUSED const char *option_name,
|
||||
G_GNUC_UNUSED const char *value, G_GNUC_UNUSED void *data,
|
||||
G_GNUC_UNUSED GError **error)
|
||||
{
|
||||
pvattest_log_increase_log_lvl(&pvattest_config.general.log_level);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean set_verify_output_format(const char *option_name, const char *value,
|
||||
G_GNUC_UNUSED void *data, GError **error)
|
||||
{
|
||||
if (!g_strcmp0(value, "yaml")) {
|
||||
pvattest_config.verify.output_fmt = VERIFY_FMT_YAML;
|
||||
} else {
|
||||
g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
|
||||
_("Found value '%s' for option '%s', but only 'yaml' is allowed."),
|
||||
value, option_name);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean create_set_paf(G_GNUC_UNUSED const char *option_name, const char *value,
|
||||
G_GNUC_UNUSED void *data, GError **error)
|
||||
{
|
||||
return hex_str_toull(value, &pvattest_config.create.paf, error);
|
||||
}
|
||||
|
||||
/***************************** GENERAL OPTIONS ********************************/
|
||||
static gboolean print_version = FALSE;
|
||||
|
||||
static GOptionEntry general_options[] = {
|
||||
{
|
||||
.long_name = "version",
|
||||
.short_name = 'v',
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_NONE,
|
||||
.arg_data = &print_version,
|
||||
.description = "Print the version and exit.\n",
|
||||
.arg_description = NULL,
|
||||
},
|
||||
_entry_verbose(""),
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
/************************* CREATE ATTESTATION OPTIONS *************************/
|
||||
#define create_indent " "
|
||||
|
||||
static GOptionEntry create_options[] = {
|
||||
_entry_host_key_document(&pvattest_config.create.host_key_document_paths, create_indent),
|
||||
_entry_certs(&pvattest_config.create.certificate_paths, create_indent),
|
||||
_entry_crls(&pvattest_config.create.crl_paths, create_indent),
|
||||
_entry_root_ca(&pvattest_config.create.root_ca_path, create_indent),
|
||||
_entry_output(&pvattest_config.create.output_path, "attestation request", create_indent),
|
||||
_entry_att_prot_key_save(&pvattest_config.create.arp_key_out_path, create_indent),
|
||||
|
||||
_entry_no_verify(&pvattest_config.create.no_verify, create_indent),
|
||||
_entry_offline_maps_to_online(&pvattest_config.create.online, create_indent),
|
||||
_entry_verbose(create_indent),
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static GOptionEntry experimental_create_options[] = {
|
||||
_entry_x_no_nonce(&pvattest_config.create.use_nonce, create_indent),
|
||||
_entry_x_paf(&create_set_paf, create_indent),
|
||||
_entry_x_aad_size(&pvattest_config.create.x_aad_size, create_indent),
|
||||
_entry_phkh_img(&pvattest_config.create.phkh_img, create_indent),
|
||||
_entry_phkh_att(&pvattest_config.create.phkh_att, create_indent),
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean verify_create(GError **error)
|
||||
{
|
||||
if (!check_file_list(pvattest_config.create.host_key_document_paths, TRUE,
|
||||
_("Specify --host-key-document at least once."), error))
|
||||
return FALSE;
|
||||
if (!pvattest_config.create.no_verify) {
|
||||
if (!check_file_list(
|
||||
pvattest_config.create.certificate_paths, TRUE,
|
||||
_("Either specify the IBM Z signing key and"
|
||||
" intermediate CA certificate\nby using the '--cert' option, or"
|
||||
" use the '--no-verify' flag to disable the\nhost-key document"
|
||||
" verification completely (at your own risk).\n"
|
||||
"Only use this option in test environments or if"
|
||||
" you trust the unverified document."),
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
if (!check_for_invalid_path(pvattest_config.create.arp_key_out_path, FALSE,
|
||||
_("Missing argument for --arpk."), error))
|
||||
return FALSE;
|
||||
if (!check_for_invalid_path(pvattest_config.create.output_path, FALSE,
|
||||
_("Missing argument for --output."), error))
|
||||
return FALSE;
|
||||
if (!check_optional_file_list(pvattest_config.create.crl_paths, TRUE, error))
|
||||
return FALSE;
|
||||
if (!check_for_optional_invalid_path(pvattest_config.create.root_ca_path, TRUE, error))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
/************************* MEASUREMENT OPTIONS ********************************/
|
||||
#define perform_indent " "
|
||||
|
||||
static GOptionEntry perform_options[] = {
|
||||
_entry_input(&pvattest_config.perform.input_path, "attestation request", perform_indent),
|
||||
_entry_output(&pvattest_config.perform.output_path, "attestation result", perform_indent),
|
||||
_entry_verbose(perform_indent),
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static GOptionEntry experimental_perform_options[] = {
|
||||
_entry_x_user_data(&pvattest_config.perform.user_data_path, perform_indent),
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static gboolean verify_perform(GError **error)
|
||||
{
|
||||
if (!check_for_invalid_path(pvattest_config.perform.input_path, TRUE,
|
||||
_("Missing argument for --input."), error))
|
||||
return FALSE;
|
||||
if (!check_for_invalid_path(pvattest_config.perform.output_path, FALSE,
|
||||
_("Missing argument for --output."), error))
|
||||
return FALSE;
|
||||
if (!check_for_optional_invalid_path(pvattest_config.perform.user_data_path, TRUE, error))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/************************* VERIFY OPTIONS ************************************/
|
||||
#define verify_indent " "
|
||||
|
||||
static GOptionEntry verify_options[] = {
|
||||
_entry_input(&pvattest_config.verify.input_path, "attestation result", verify_indent),
|
||||
_entry_output(&pvattest_config.verify.output_path,
|
||||
"verification result.\n" verify_indent "(optional)", verify_indent),
|
||||
_entry_guest_hdr(&pvattest_config.verify.hdr_path, verify_indent),
|
||||
_entry_att_prot_key_load(&pvattest_config.verify.arp_key_in_path, verify_indent),
|
||||
_entry_verbose(verify_indent),
|
||||
_entry__verify_format(verify_indent),
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static gboolean verify_verify(GError **error)
|
||||
{
|
||||
if (!check_for_invalid_path(pvattest_config.verify.input_path, TRUE,
|
||||
_("Missing argument for --input."), error))
|
||||
return FALSE;
|
||||
if (!check_for_invalid_path(pvattest_config.verify.hdr_path, TRUE,
|
||||
_("Missing argument for --hdr."), error))
|
||||
return FALSE;
|
||||
if (!check_for_invalid_path(pvattest_config.verify.arp_key_in_path, TRUE,
|
||||
_("Missing argument for --arpk."), error))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/************************** OPTIONS END ***************************************/
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static char summary[] =
|
||||
"\n"
|
||||
"Create, perform, and verify attestation measurements for IBM Secure Execution guest"
|
||||
" systems.\n"
|
||||
"\n"
|
||||
"COMMANDS\n"
|
||||
" create On a trusted system, creates an attestation request.\n"
|
||||
" perform On the SE-guest to be attested, sends the attestation request\n"
|
||||
" to the Ultravisor and receives the answer.\n"
|
||||
#ifndef PVATTEST_COMPILE_PERFORM
|
||||
" (not supported on this platform)\n"
|
||||
#endif /* PVATTEST_COMPILE_PERFORM */
|
||||
|
||||
" verify On a trusted system, compares the one from your trusted system.\n"
|
||||
" If they differ, the Secure Execution guest might not be compromised\n"
|
||||
"\n"
|
||||
"Use '" GETTEXT_PACKAGE " [COMMAND] -h' to get detailed help\n";
|
||||
static char create_summary[] =
|
||||
"Create attestation measurement requests to attest an\n"
|
||||
"IBM Secure Execution guest. Only build attestation requests in a trusted\n"
|
||||
"environment such as your Workstation.\n"
|
||||
"To avoid compromising the attestation do not publish the\n"
|
||||
"protection key and delete it after verification.\n"
|
||||
"Every 'create' will generate a new, random protection key.\n";
|
||||
static char perform_summary[] =
|
||||
#ifndef PVATTEST_COMPILE_PERFORM
|
||||
"This system does NOT support 'perform'.\n"
|
||||
#endif /* PVATTEST_COMPILE_PERFORM */
|
||||
"Perform a measurement of this IBM Secure Execution guest using '/dev/uv'.\n";
|
||||
static char verify_summary[] =
|
||||
"Verify that a previously generated attestation measurement of an\n"
|
||||
"IBM Secure Execution guest yielded the expected results.\n"
|
||||
"Verify attestation requests only in a trusted environment, such as your workstation.";
|
||||
|
||||
static void print_version_and_exit(void)
|
||||
{
|
||||
printf("%s version %s\n", GETTEXT_PACKAGE, RELEASE_STRING);
|
||||
printf("%s\n", COPYRIGHT_NOTICE);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static GOptionContext *create_ctx(GOptionEntry *options, GOptionEntry *experimental_options,
|
||||
const char *param_name, const char *opt_summary)
|
||||
{
|
||||
GOptionContext *ret = g_option_context_new(param_name);
|
||||
GOptionGroup *x_group = NULL;
|
||||
g_option_context_add_main_entries(ret, options, NULL);
|
||||
g_option_context_set_summary(ret, opt_summary);
|
||||
if (experimental_options) {
|
||||
x_group = g_option_group_new(
|
||||
"experimental",
|
||||
"Experimental Options; Do not use in a production environment",
|
||||
"Show experimental options", NULL, NULL);
|
||||
g_option_group_add_entries(x_group, experimental_options);
|
||||
g_option_context_add_group(ret, x_group);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum pvattest_command pvattest_parse(int *argc, char **argvp[], pvattest_config_t **config,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GOptionContext) main_context = NULL, subc_context = NULL;
|
||||
char **argv = *argvp;
|
||||
enum pvattest_command subc = PVATTEST_SUBC_INVALID;
|
||||
verify_options_fn_t verify_options_fn = NULL;
|
||||
|
||||
pv_wrapped_g_assert(argc);
|
||||
pv_wrapped_g_assert(argvp);
|
||||
pv_wrapped_g_assert(config);
|
||||
|
||||
/*
|
||||
* First parse until the first non dash argument. This must be one of the commands.
|
||||
* (strict POSIX parsing)
|
||||
*/
|
||||
main_context = g_option_context_new(
|
||||
"COMMAND [OPTIONS] - create, perform, and verify attestation measurements");
|
||||
g_option_context_set_strict_posix(main_context, TRUE);
|
||||
g_option_context_add_main_entries(main_context, general_options, NULL);
|
||||
g_option_context_set_summary(main_context, summary);
|
||||
|
||||
if (!g_option_context_parse(main_context, argc, argvp, error))
|
||||
return PVATTEST_SUBC_INVALID;
|
||||
if (print_version)
|
||||
print_version_and_exit();
|
||||
|
||||
/*
|
||||
* Parse depending on the specified command
|
||||
*/
|
||||
else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_CREATE) == 0) {
|
||||
subc_context =
|
||||
create_ctx(create_options, experimental_create_options,
|
||||
"create [OPTIONS] - create an attestation measurement request",
|
||||
create_summary);
|
||||
subc = PVATTEST_SUBC_CREATE;
|
||||
verify_options_fn = &verify_create;
|
||||
} else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_PERFORM) == 0) {
|
||||
subc_context =
|
||||
create_ctx(perform_options, experimental_perform_options,
|
||||
"perform [OPTIONS] - perform an attestation measurement request",
|
||||
perform_summary);
|
||||
subc = PVATTEST_SUBC_PERFORM;
|
||||
verify_options_fn = &verify_perform;
|
||||
#ifndef PVATTEST_COMPILE_PERFORM
|
||||
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
||||
_("This system does not support the 'perform' command."));
|
||||
return PVATTEST_SUBC_INVALID;
|
||||
#endif /* PVATTEST_COMPILE_PERFORM */
|
||||
} else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_VERIFY) == 0) {
|
||||
subc_context = create_ctx(verify_options, NULL,
|
||||
"verify [OPTIONS] - verify an attestation measurement",
|
||||
verify_summary);
|
||||
subc = PVATTEST_SUBC_VERIFY;
|
||||
verify_options_fn = &verify_verify;
|
||||
} else {
|
||||
if (argv[1])
|
||||
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARGV,
|
||||
_("Invalid command specified: %s."), argv[1]);
|
||||
else
|
||||
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARGV,
|
||||
_("No command specified."));
|
||||
return PVATTEST_SUBC_INVALID;
|
||||
}
|
||||
g_assert(verify_options_fn);
|
||||
|
||||
if (!g_option_context_parse(subc_context, argc, argvp, error))
|
||||
return PVATTEST_SUBC_INVALID;
|
||||
|
||||
if (!verify_options_fn(error))
|
||||
return PVATTEST_SUBC_INVALID;
|
||||
|
||||
*config = &pvattest_config;
|
||||
return subc;
|
||||
}
|
||||
|
||||
static void pvattest_parse_clear_create_config(pvattest_create_config_t *config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
g_strfreev(config->host_key_document_paths);
|
||||
g_strfreev(config->certificate_paths);
|
||||
g_free(config->arp_key_out_path);
|
||||
g_free(config->output_path);
|
||||
}
|
||||
|
||||
static void pvattest_parse_clear_perform_config(pvattest_perform_config_t *config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
g_free(config->input_path);
|
||||
g_free(config->output_path);
|
||||
}
|
||||
|
||||
static void pvattest_parse_clear_verify_config(pvattest_verify_config_t *config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
g_free(config->input_path);
|
||||
g_free(config->output_path);
|
||||
g_free(config->hdr_path);
|
||||
g_free(config->arp_key_in_path);
|
||||
}
|
||||
|
||||
void pvattest_parse_clear_config(pvattest_config_t *config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
pvattest_parse_clear_create_config(&config->create);
|
||||
pvattest_parse_clear_perform_config(&config->perform);
|
||||
pvattest_parse_clear_verify_config(&config->verify);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user