mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
304 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25968033bb | ||
|
|
1e746990c0 | ||
|
|
3002e7f754 | ||
|
|
ea10995f4d | ||
|
|
8b31319ddf | ||
|
|
1df4d66387 | ||
|
|
37348ef662 | ||
|
|
644432ba23 | ||
|
|
6eddae9a8a | ||
|
|
d19f0915c3 | ||
|
|
0ecf18b66d | ||
|
|
26c544998e | ||
|
|
edaa72d68a | ||
|
|
041e3ad996 | ||
|
|
301eece09b | ||
|
|
36bd05c4ba | ||
|
|
8aa3f064af | ||
|
|
48a7da096d | ||
|
|
094f52d604 | ||
|
|
0626dc7a72 | ||
|
|
0180054d07 | ||
|
|
800fe15d21 | ||
|
|
c570f51f5f | ||
|
|
57b70a0fe0 | ||
|
|
cc0d030ce9 | ||
|
|
9dce6793ab | ||
|
|
d8054d1a1a | ||
|
|
5a9c381225 | ||
|
|
3c6890317a | ||
|
|
1c42b9e3e4 | ||
|
|
beb6a1d6fd | ||
|
|
40e4b71159 | ||
|
|
8c0f2491a3 | ||
|
|
e44ae22989 | ||
|
|
3be8be4ac7 | ||
|
|
544c88ca39 | ||
|
|
c7afb5baec | ||
|
|
cc9b202a9b | ||
|
|
9f99706c26 | ||
|
|
8137128a96 | ||
|
|
1cdfb4946e | ||
|
|
5fb30f1e6f | ||
|
|
d8089e69fa | ||
|
|
cbf7f02d69 | ||
|
|
91b1692b16 | ||
|
|
cd8a733c82 | ||
|
|
bcce1e8d18 | ||
|
|
f832428109 | ||
|
|
1d7bb283fd | ||
|
|
a2f14fcfd7 | ||
|
|
bb6a47db55 | ||
|
|
35dd59d04c | ||
|
|
3fa511cfb9 | ||
|
|
c6c4df9697 | ||
|
|
1b0ab844b4 | ||
|
|
92fc94f152 | ||
|
|
f25aaf32b8 | ||
|
|
13f8709ec2 | ||
|
|
6a860a01c3 | ||
|
|
131a910ac5 | ||
|
|
7007937456 | ||
|
|
a528bb41e0 | ||
|
|
ed52031821 | ||
|
|
60f33fb2de | ||
|
|
5737a57920 | ||
|
|
b044aec4ff | ||
|
|
e584c88d8f | ||
|
|
fd817280d3 | ||
|
|
046d27a05b | ||
|
|
fd174cb952 | ||
|
|
70f1dcd770 | ||
|
|
fcd5d64a45 | ||
|
|
320e11dcf9 | ||
|
|
34cfca722c | ||
|
|
2215ba672e | ||
|
|
1003d81412 | ||
|
|
943c5dc51d | ||
|
|
c367a6bb65 | ||
|
|
0db89894b5 | ||
|
|
2f87cba1c7 | ||
|
|
c31eba5e00 | ||
|
|
04b0b43739 | ||
|
|
798967aba3 | ||
|
|
f2f4dc209c | ||
|
|
d7b816ff58 | ||
|
|
ae66f79512 | ||
|
|
3217e0438f | ||
|
|
41fae58ecd | ||
|
|
6dab7637fb | ||
|
|
53b949926f | ||
|
|
e08e07baa9 | ||
|
|
e679a4002e | ||
|
|
20b05e1f82 | ||
|
|
ce02dd7167 | ||
|
|
d2fc700b73 | ||
|
|
199b030580 | ||
|
|
71b36d17f0 | ||
|
|
ca8750e820 | ||
|
|
0472b5ea5c | ||
|
|
94a3272dac | ||
|
|
d72cf322c0 | ||
|
|
ab35d92028 | ||
|
|
39ceb02404 | ||
|
|
454f1427d3 | ||
|
|
1b65b23b43 | ||
|
|
0c583ec1a6 | ||
|
|
0ac7ce964e | ||
|
|
4eea67cd6f | ||
|
|
9a68a25ab6 | ||
|
|
1a150b2fe0 | ||
|
|
0843b7db36 | ||
|
|
cc069af26d | ||
|
|
d978fc6141 | ||
|
|
2fe5f27975 | ||
|
|
93a0cb254e | ||
|
|
eb4e806cdc | ||
|
|
19f747847f | ||
|
|
ce65c39e18 | ||
|
|
6c04f97773 | ||
|
|
cb11d6baec | ||
|
|
c673bacd7e | ||
|
|
6cf7f31746 | ||
|
|
c4a0933165 | ||
|
|
e67f630086 | ||
|
|
465348ced9 | ||
|
|
f148df4cc3 | ||
|
|
4d61a21811 | ||
|
|
e0ffb3c584 | ||
|
|
f4f2220693 | ||
|
|
fb62cc9e14 | ||
|
|
c91d8bd5f9 | ||
|
|
4753340e79 | ||
|
|
f742ed73e1 | ||
|
|
65b9fc442c | ||
|
|
2c106425e8 | ||
|
|
11bdab2629 | ||
|
|
b06af6026f | ||
|
|
1086548607 | ||
|
|
fbf8513d43 | ||
|
|
67496afe57 | ||
|
|
16b2799150 | ||
|
|
c063273b14 | ||
|
|
d2f8f972cf | ||
|
|
2d600570df | ||
|
|
3356d6f4fa | ||
|
|
67aef9bbf3 | ||
|
|
5d2871d626 | ||
|
|
6fcf64ebe0 | ||
|
|
e51663bbca | ||
|
|
f99560f734 | ||
|
|
303a3707e2 | ||
|
|
a37170b8be | ||
|
|
305235a7bc | ||
|
|
2568863f58 | ||
|
|
b0f82d22f9 | ||
|
|
b83c8944f1 | ||
|
|
f454c6825f | ||
|
|
c55ceabc67 | ||
|
|
2e28291c75 | ||
|
|
24fe8c1d1b | ||
|
|
67e76b8ebd | ||
|
|
97ab8fb4e9 | ||
|
|
c07104dbc7 | ||
|
|
c871050097 | ||
|
|
7e37a1d4e0 | ||
|
|
d884fb8db4 | ||
|
|
0e385a81ca | ||
|
|
675c854fa3 | ||
|
|
9d39a4bd47 | ||
|
|
400167f512 | ||
|
|
4fa9656613 | ||
|
|
87b54fc884 | ||
|
|
299fd2b772 | ||
|
|
36fed0e6c6 | ||
|
|
f7430027b4 | ||
|
|
8874b90825 | ||
|
|
6fe9e6c55c | ||
|
|
cc16e41595 | ||
|
|
bac3f93772 | ||
|
|
971970989b | ||
|
|
d415b8efed | ||
|
|
33031241c3 | ||
|
|
49cbaba302 | ||
|
|
304c3d8086 | ||
|
|
d77234ddb6 | ||
|
|
bc0d40c580 | ||
|
|
a1697a581d | ||
|
|
f75f4aff8f | ||
|
|
729a98fcb3 | ||
|
|
f52aeabca4 | ||
|
|
0be7efc956 | ||
|
|
88e6a18f96 | ||
|
|
a7e47685e0 | ||
|
|
b48aa5f435 | ||
|
|
d91e728e3c | ||
|
|
ce4704f365 | ||
|
|
2ca442feed | ||
|
|
82c86fa8bd | ||
|
|
e7f446432b | ||
|
|
1a87d3a3b4 | ||
|
|
0de533aef9 | ||
|
|
27a6409a4a | ||
|
|
6f9337d101 | ||
|
|
27f6c0a167 | ||
|
|
0652c687e3 | ||
|
|
16a62dff03 | ||
|
|
094905af87 | ||
|
|
dcce14923c | ||
|
|
1fd78d9f5e | ||
|
|
9d1baaa594 | ||
|
|
efcfbce386 | ||
|
|
3d74c534bf | ||
|
|
ca2ffe9e18 | ||
|
|
52192908eb | ||
|
|
d4cee7e65a | ||
|
|
bfbf456b83 | ||
|
|
235e997a77 | ||
|
|
c079e48d7d | ||
|
|
4762e65acb | ||
|
|
cb614ed1ee | ||
|
|
8face3e63e | ||
|
|
ea14f5471c | ||
|
|
b2ae5a91ac | ||
|
|
edfb6a03d8 | ||
|
|
5eace91ee7 | ||
|
|
86856f98db | ||
|
|
154734efc7 | ||
|
|
c7fcb457b0 | ||
|
|
244bf4e550 | ||
|
|
0f7ed7d4fc | ||
|
|
121d5d8013 | ||
|
|
abe0ba7412 | ||
|
|
2227bb8330 | ||
|
|
aa09b29248 | ||
|
|
2790d4faaa | ||
|
|
bbc46edaf5 | ||
|
|
e5c0fb249b | ||
|
|
b6d8e5833c | ||
|
|
d71628326d | ||
|
|
8bfe18e674 | ||
|
|
275105fe3d | ||
|
|
19259aeb65 | ||
|
|
125df52887 | ||
|
|
852fb9dfb8 | ||
|
|
b0fe75b92f | ||
|
|
bc987c8d18 | ||
|
|
a86e41a518 | ||
|
|
0d9e42264d | ||
|
|
7fede7021e | ||
|
|
e7d79d5c5c | ||
|
|
7d4b1e18b6 | ||
|
|
b0cc0e4737 | ||
|
|
b7bb90c552 | ||
|
|
560b672bfa | ||
|
|
0fab6bdf2a | ||
|
|
b56c74fe7b | ||
|
|
663d362ff3 | ||
|
|
d4027e6506 | ||
|
|
ddde3f354f | ||
|
|
298fab68fe | ||
|
|
b47007b8ac | ||
|
|
91c35543ca | ||
|
|
9de85f4295 | ||
|
|
e15e8a1bfa | ||
|
|
4fc0c3cfef | ||
|
|
a0ed6709cf | ||
|
|
552a915465 | ||
|
|
1091b0bf65 | ||
|
|
016a0a56fc | ||
|
|
0b4cbf0041 | ||
|
|
d854aed4b8 | ||
|
|
7f8e31e861 | ||
|
|
a5b58038a0 | ||
|
|
c2244a5795 | ||
|
|
ea7cc9ea60 | ||
|
|
b32c031474 | ||
|
|
bfc3dd018c | ||
|
|
625b81130a | ||
|
|
bf8872e94a | ||
|
|
a84d1c5d58 | ||
|
|
696e8458f0 | ||
|
|
95c7258ea7 | ||
|
|
21de913a5e | ||
|
|
7a2a96e8a3 | ||
|
|
7a569c7e33 | ||
|
|
dc2ac0c916 | ||
|
|
367598b187 | ||
|
|
c01bcfa73a | ||
|
|
fe457e2082 | ||
|
|
1d25112589 | ||
|
|
27a8c4434c | ||
|
|
e6a3b18104 | ||
|
|
36292cb166 | ||
|
|
49da03e2e7 | ||
|
|
139dff3525 | ||
|
|
94227b44d5 | ||
|
|
a9f4ae2db3 | ||
|
|
15b7f9d83f | ||
|
|
214580a704 | ||
|
|
96f13ead2b | ||
|
|
8023a72b11 | ||
|
|
abc1875300 | ||
|
|
7d4c8c2781 | ||
|
|
f0afc6c89a |
22
.gitignore
vendored
22
.gitignore
vendored
@@ -11,9 +11,8 @@
|
||||
cmsfs-fuse/cmsfs-fuse
|
||||
cpacfstats/cpacfstats
|
||||
cpacfstats/cpacfstatsd
|
||||
cpumf/bin/chcpumf
|
||||
cpumf/bin/cpumf_helper
|
||||
cpumf/bin/lscpumf
|
||||
cpumf/chcpumf
|
||||
cpumf/lscpumf
|
||||
cpuplugd/cpuplugd
|
||||
dasdfmt/dasdfmt
|
||||
dasdinfo/dasdinfo
|
||||
@@ -35,6 +34,9 @@ iucvterm/src/iucvconn
|
||||
iucvterm/src/iucvtty
|
||||
iucvterm/src/ttyrun
|
||||
iucvterm/test/test_afiucv
|
||||
libekmfweb/check-dep-libekmfweb
|
||||
libekmfweb/detect-openssl-version.dep
|
||||
libekmfweb/libekmfweb.so
|
||||
libutil/util_base_example
|
||||
libutil/util_file_example
|
||||
libutil/util_libc_example
|
||||
@@ -45,8 +47,9 @@ libutil/util_path_example
|
||||
libutil/util_prg_example
|
||||
libutil/util_rec_example
|
||||
libutil/util_scandir_example
|
||||
libzds/libzds.a
|
||||
libvmcp/vmcp_example
|
||||
libzds/libzds.a
|
||||
lsstp/lsstp
|
||||
mon_tools/mon_fsstatd
|
||||
mon_tools/mon_procd
|
||||
osasnmpd/osasnmpd
|
||||
@@ -76,10 +79,10 @@ zdev/src/lszdev
|
||||
zdev/src/lszdev_usage.c
|
||||
zdsfs/zdsfs
|
||||
zdump/zgetdump
|
||||
zfcpdump/cpioinit
|
||||
zfcpdump/zfcpdump_part
|
||||
zfcpdump/zfcpdump-initrd
|
||||
zfcpdump/10-zfcpdump.install
|
||||
zfcpdump/cpioinit
|
||||
zfcpdump/zfcpdump-initrd
|
||||
zfcpdump/zfcpdump_part
|
||||
ziomon/ziomon_mgr
|
||||
ziomon/ziomon_util
|
||||
ziomon/ziomon_zfcpdd
|
||||
@@ -91,9 +94,10 @@ zipl/boot/data.h
|
||||
zipl/src/chreipl_helper.device-mapper
|
||||
zipl/src/zipl
|
||||
zipl/src/zipl_helper.device-mapper
|
||||
zkey/zkey
|
||||
zkey/zkey-cryptsetup
|
||||
zkey/check-dep-zkey
|
||||
zkey/check-dep-zkey-cryptsetup
|
||||
zkey/detect-libcryptsetup.dep
|
||||
zkey/ekmfweb/zkey-ekmfweb.so
|
||||
zkey/zkey
|
||||
zkey/zkey-cryptsetup
|
||||
zpcictl/zpcictl
|
||||
|
||||
10
AUTHORS.md
10
AUTHORS.md
@@ -1,6 +1,7 @@
|
||||
List of all individuals having contributed content to s390-tools
|
||||
----------------------------------------------------------------
|
||||
|
||||
- Alexander Egorenkov
|
||||
- Alexey Ishchuk
|
||||
- Andreas Herrmann
|
||||
- Andre Wild
|
||||
@@ -18,6 +19,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Despina Papadopoulou
|
||||
- Dimitri John Ledkov
|
||||
- Eberhard Pasch
|
||||
- Eduard Shishkin
|
||||
- Einar Lueck
|
||||
- Eric Sandeen
|
||||
- Erwin Vicari
|
||||
@@ -32,6 +34,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Fritz Elfert
|
||||
- Gerald Schaefer
|
||||
- Gerhard Tonn
|
||||
- Guevenc Guelce
|
||||
- Hannes Reinecke
|
||||
- Hans-Joachim Picht
|
||||
- Hans Wippel
|
||||
@@ -47,16 +50,20 @@ List of all individuals having contributed content to s390-tools
|
||||
- Jan Glauber
|
||||
- Jan Hoeppner
|
||||
- Jan Willeke
|
||||
- Jason J. Herne
|
||||
- Javier Martinez Canillas
|
||||
- Jean-Baptiste Joret
|
||||
- Jens Remus
|
||||
- Jochen Roehrig
|
||||
- Juergen Christ
|
||||
- Julian Wiedmann
|
||||
- Karsten Graul
|
||||
- Kittipon Meesompop
|
||||
- Klaus-Dieter Wacker
|
||||
- Lakhvich Dmitriy
|
||||
- Marc Hartmayer
|
||||
- Mark Dettinger
|
||||
- Mark Post
|
||||
- Martin Kammerer
|
||||
- Martin Peschke
|
||||
- Martin Petermann
|
||||
@@ -68,6 +75,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Michael Mueller
|
||||
- Mijo Safradin
|
||||
- Mikhail Zaslonko
|
||||
- Niklas Schnelle
|
||||
- Peter Oberparleiter
|
||||
- Peter Tiedemann
|
||||
- Philipp Kern
|
||||
@@ -90,6 +98,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Steffen Maier
|
||||
- Steffen Thoss
|
||||
- Susanne Wintenberger
|
||||
- Sven Schnelle
|
||||
- Sven Schuetz
|
||||
- Swen Schillig
|
||||
- Taraka R. Bodireddy
|
||||
@@ -97,6 +106,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Thomas Richter
|
||||
- Thomas Spatzier
|
||||
- Thomas Weber
|
||||
- Tuan Hoang
|
||||
- Ursula Braun
|
||||
- Utz Bacher
|
||||
- Vasily Gorbik
|
||||
|
||||
132
CHANGELOG.md
132
CHANGELOG.md
@@ -1,6 +1,122 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __v2.10.0 (2019-07-31)__
|
||||
* __v2.15.0 (2020-10-15)__
|
||||
|
||||
For Linux kernel version: 5.9
|
||||
|
||||
Add new tool:
|
||||
- lsstp: A small utility to display the Server Time Protocol (STP) information present in sysfs
|
||||
|
||||
Changes of existing tools:
|
||||
- dumpconf: support NVMe dump/reipl device
|
||||
- ipl_tools: support clear attribute for nvme re-IPL
|
||||
- zcrypt: Support new config state with lszcrypt and chzcrypt
|
||||
- zkey: Add support for key management system plugins
|
||||
including the KMS commands:
|
||||
bind, unbind, info, configure, rencipher, list, import, refresh
|
||||
- zkey: Add EKMFWeb support to remotely generate secure keys
|
||||
- libekmfweb: Add new EKMFWeb client library
|
||||
- libutil: Add util_file_read_va()
|
||||
- libutil: Add util_file_read_i()/util_file_read_ui()
|
||||
|
||||
Bug Fixes:
|
||||
- cpumf: Fix version and help printout when CPUMF is not installed
|
||||
- ziomon/ziorep_printers: fix virtual adapter CSV output
|
||||
- zipl: Fix Error when title is not the first field in BLS file
|
||||
|
||||
|
||||
* __v2.14.0 (2020-08-21)__
|
||||
|
||||
For Linux kernel version: 5.7 / 5.8
|
||||
|
||||
Changes of existing tools:
|
||||
- cpacfstats: Add ECC counters
|
||||
- dbginfo: Added collection of /proc/softirqs
|
||||
- ipl-tools: Add nvme device support to lsreipl/chreipl
|
||||
- zdsfs: Add coordinated read access
|
||||
- libzds: Add curl interface to access zosmf rest api
|
||||
- util_opt: Change util_opt_init() to honor current command, if set
|
||||
Bug Fixes:
|
||||
- lsluns: Try harder to find udevadm
|
||||
- mon_tools: Update udevadm location
|
||||
- zipl: Fix NVMe partition and base device detection
|
||||
- zipl/stage3: Correctly handle diag308 response code
|
||||
- znetconf: Introduce better ways to locate udevadm
|
||||
|
||||
* __v2.13.0 (2020-05-06)__
|
||||
|
||||
For Linux kernel version: 5.5 / 5.6
|
||||
|
||||
Add new tool:
|
||||
- genprotimg: Add genprotimg to create protected virtualization images
|
||||
- genprotimg: Add sample script to verify host keys
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo: Gather bridge related data (using 'bridge')
|
||||
- dbginfo: Removed collection of /var/log/opencryptoki/
|
||||
- dbginfo: collect softnet_stat
|
||||
- dbginfo: gather ethtool output for per-queue coalescing
|
||||
- ipl_tools: Support clear attribute for FCP and CCW re-IPL
|
||||
- zdev: Report FC Endpoint Security of zfcp devices
|
||||
- zdev/dracut/95zdev/module-setup.sh: Add ctcm kernel module
|
||||
- cpumf/data: Add new deflate counters for IBM z15
|
||||
- zkey: Add support for EP11 secure keys
|
||||
- zpcictl: Initiate recover after reset
|
||||
- zipl: Add support for NVMe devices
|
||||
- zipl: A multitude of code and stability improvements
|
||||
|
||||
Bug Fixes:
|
||||
- zipl: Prevent endless loop during IPL
|
||||
- zipl/libc: Fix potential buffer overflow in printf
|
||||
- zkey: Fix listing of keys on file systems reporting DT_UNKNOWN
|
||||
- zkey: Fix display of clear key size for XTS, CCA-AESCIPHER, and EP11-AES XTS keys
|
||||
|
||||
|
||||
* __v2.12.0 (2019-12-17)__
|
||||
|
||||
For Linux kernel version: 5.4
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo: Gather qdisc related data (using 'tc')
|
||||
- dbginfo: Gather extended network statistics (using 'ip link')
|
||||
- dbginfo: Collect all files under /usr/lib/systemd/system/
|
||||
- cpumf/cpumf_helper: Add IBM z15 machine name
|
||||
- zkey: Display MKVP when validating a secure key
|
||||
- zkey: Cross check APQNs when generating, validating, or importing secure keys,
|
||||
and when changing APQN associations
|
||||
- zkey: Check crypto card level during APQN cross checking
|
||||
- zkey: Add support for generating, validating, and re-enciphering AES CIPHER keys
|
||||
- zkey-cryptsetup: Add --to-new and --from-old options
|
||||
- zkey-cryptsetup: Allow setkey to set different key types
|
||||
- lszcrypt/chzcrypt: CEX7S exploitation support
|
||||
- zcryptstats: Add support for CEX7 crypto card
|
||||
- zipl: Ship a minimal zipl.conf
|
||||
- zipl: Add value of target= as search path for BLS case
|
||||
|
||||
Bug Fixes:
|
||||
- dasdview: Fix exit status in error cases
|
||||
- zipl: Fix various compile warnings
|
||||
- zipl: Fix dependency generation in zipl/boot
|
||||
- zipl: Fix entry point for stand-alone kdump
|
||||
- zipl: Add missing options to help output
|
||||
|
||||
* __v2.11.0 (2019-09-06)__
|
||||
|
||||
For Linux kernel version: 5.3
|
||||
|
||||
Changes of existing tools:
|
||||
- dasdfmt: Add support for thin-provisioned volumes
|
||||
- lsdasd: Add support for thin-provisioned volumes
|
||||
- libdasd: Provide function to utilise release space ioctl
|
||||
- libdasd: Provide function to read ese sysfs attribute
|
||||
- dbginfo: Add lspci (PCI devices) and smc_dbg (SMC sockets)
|
||||
- dbginfo: Gather ethtool related data
|
||||
|
||||
Bug Fixes:
|
||||
- zipl: Fix freeing of uninitialized pointer
|
||||
- zipl: Set correct secure IPL default value
|
||||
|
||||
* __v2.10.0 (2019-07-31)__
|
||||
|
||||
For Linux kernel version: 5.2
|
||||
|
||||
@@ -18,7 +134,7 @@ Release history for s390-tools (MIT version)
|
||||
- zipl: Do not overwrite BOOT_IMAGE entry
|
||||
- zkey: Fix auto-detection of clear key bitsize for XTS keys
|
||||
|
||||
* __v2.9.0 (2019-05-21)__
|
||||
* __v2.9.0 (2019-05-21)__
|
||||
|
||||
For Linux kernel version: 5.0 / 5.1
|
||||
|
||||
@@ -51,7 +167,7 @@ Release history for s390-tools (MIT version)
|
||||
- zfcpdbf: Warn about ambiguous payload records with dup reqid & payarea
|
||||
- zpcictl: Check for regular directory to prevent possible buffer overflow
|
||||
|
||||
* __v2.8.0 (2019-02-15)__
|
||||
* __v2.8.0 (2019-02-15)__
|
||||
|
||||
For Linux kernel version: 4.20
|
||||
|
||||
@@ -65,7 +181,7 @@ Release history for s390-tools (MIT version)
|
||||
- zkey: Avoid EPERM on key change if user is not owner of key file
|
||||
- cpumf/cpumf_helper: Always return list reference for --sfb-size
|
||||
|
||||
* __v2.7.1 (2018-12-13)__
|
||||
* __v2.7.1 (2018-12-13)__
|
||||
|
||||
For Linux kernel version: 4.19
|
||||
|
||||
@@ -89,7 +205,7 @@ Release history for s390-tools (MIT version)
|
||||
ip_watcher, libvmdump, libvtoc, lsqeth, lszcrypt, qethqoat, zdev, zdsfs,
|
||||
zgetdump, zipl, zpcictl
|
||||
|
||||
* __v2.7.0 (2018-10-31)__
|
||||
* __v2.7.0 (2018-10-31)__
|
||||
|
||||
For Linux kernel version: 4.19
|
||||
|
||||
@@ -114,7 +230,7 @@ Release history for s390-tools (MIT version)
|
||||
- Direct --help and --version output to stdout for several tools
|
||||
- osasnmpd: Start without real OSA devices
|
||||
|
||||
* __v2.6.0 (2018-08-10)__
|
||||
* __v2.6.0 (2018-08-10)__
|
||||
|
||||
For Linux kernel version: 4.18
|
||||
|
||||
@@ -128,7 +244,7 @@ Release history for s390-tools (MIT version)
|
||||
- netboot: Include compressed kernel modules in initramfs
|
||||
- netboot: Send client architecture and handle path prefix
|
||||
|
||||
* __v2.5.0 (2018-06-08)__
|
||||
* __v2.5.0 (2018-06-08)__
|
||||
|
||||
For Linux kernel version: 4.17
|
||||
|
||||
@@ -140,7 +256,7 @@ Release history for s390-tools (MIT version)
|
||||
Bug Fixes:
|
||||
- lsluns: Print a message if no adapter or port exists
|
||||
|
||||
* __v2.4.0 (2018-05-07)__
|
||||
* __v2.4.0 (2018-05-07)__
|
||||
|
||||
For Linux kernel version: 4.16
|
||||
|
||||
|
||||
6
Makefile
6
Makefile
@@ -3,12 +3,14 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
||||
# Include common definitions
|
||||
include common.mak
|
||||
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||
genprotimg lsstp
|
||||
|
||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
all: $(TOOL_DIRS)
|
||||
|
||||
32
README.md
32
README.md
@@ -1,4 +1,3 @@
|
||||
|
||||
s390-tools
|
||||
==========
|
||||
|
||||
@@ -31,6 +30,9 @@ Package contents
|
||||
* dasdinfo:
|
||||
Display unique DASD ID, either UID or volser.
|
||||
|
||||
* genprotimg:
|
||||
Create a protected virtualization image.
|
||||
|
||||
* udev rules:
|
||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||
@@ -241,6 +243,12 @@ Package contents
|
||||
Provides simple tools to create a binary that can be used to implement
|
||||
simple network boot setups following the PXELINUX conventions.
|
||||
|
||||
* libekmfweb:
|
||||
A shared library that provides functions to communicate with an EKMF Web
|
||||
server via REST calls over HTTPS. EKMF Web stands for IBM Enterprise Key
|
||||
Management Foundation - Web Edition, and is used to manage keys in an
|
||||
enterprise.
|
||||
|
||||
For more information refer to the following publications:
|
||||
|
||||
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||
@@ -265,9 +273,11 @@ build options:
|
||||
| pfm | `HAVE_PFM` | cpacfstats |
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | zkey |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg |
|
||||
| libcurl | `HAVE_LIBCURL` | libekmfweb |
|
||||
|
||||
This table lists additional build or install options:
|
||||
|
||||
@@ -288,6 +298,14 @@ the different tools are provided:
|
||||
* dbginfo.sh:
|
||||
The tar package is required to archive collected data.
|
||||
|
||||
* genprotimg:
|
||||
For building genprotimg you need OpenSSL version 1.1.0 or newer
|
||||
installed (openssl-devel.rpm). Also required is glib2
|
||||
(glib2-devel.rpm). Tip: you may skip the genprotimg build by adding
|
||||
`HAVE_OPENSSL=0` or `HAVE_GLIB2=0`.
|
||||
|
||||
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
||||
|
||||
* osasnmpd:
|
||||
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||
installed, before building the osasnmpd subagent.
|
||||
@@ -386,3 +404,11 @@ the different tools are provided:
|
||||
tool must be added to this group. The owner of the default key repository
|
||||
'/etc/zkey/repository' must be set to group 'zkeyadm' with write permission
|
||||
for this group.
|
||||
|
||||
* libekmfweb:
|
||||
For building the libekmfweb shared library you need openssl version 1.1.1 or
|
||||
newer installed (openssl-devel.rpm). Also required are json-c version 0.13 or
|
||||
newer (json-c-devel.rpm), and libcurl version 7.59 or newer
|
||||
(libcurl-devel.rpm).
|
||||
Tip: you may skip the libekmfweb build by adding `HAVE_OPENSSL=0`,
|
||||
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "cmsfs-fuse.h"
|
||||
#include "edf.h"
|
||||
#include "helper.h"
|
||||
|
||||
@@ -49,6 +49,4 @@ extern FILE *logfile;
|
||||
fprintf(stderr, COMP "Warning, " __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
#endif
|
||||
|
||||
13
common.mak
13
common.mak
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 10
|
||||
RELEASE = 15
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -163,6 +163,7 @@ USRSBINDIR = $(INSTALLDIR)/usr/sbin
|
||||
USRBINDIR = $(INSTALLDIR)/usr/bin
|
||||
BINDIR = $(INSTALLDIR)/sbin
|
||||
LIBDIR = $(INSTALLDIR)/lib
|
||||
LIB64DIR = $(INSTALLDIR)/lib64
|
||||
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||
MANDIR = $(INSTALLDIR)/usr/share/man
|
||||
VARDIR = $(INSTALLDIR)/var
|
||||
@@ -172,14 +173,16 @@ ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump
|
||||
# Systemd support files are installed only if a directory is specified
|
||||
# for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system)
|
||||
SYSTEMDSYSTEMUNITDIR =
|
||||
USRINCLUDEDIR = $(INSTALLDIR)/usr/include
|
||||
|
||||
INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||
$(SYSCONFDIR) $(SYSCONFDIR)/sysconfig \
|
||||
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR)
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||
$(LIB64DIR) $(USRINCLUDEDIR)
|
||||
OWNER = $(shell id -un)
|
||||
GROUP = $(shell id -gn)
|
||||
export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP
|
||||
export INSTALLDIR BINDIR LIBDIR LIB64DIR MANDIR OWNER GROUP
|
||||
|
||||
# Special defines for zfcpdump
|
||||
ZFCPDUMP_IMAGE = zfcpdump-image
|
||||
@@ -339,6 +342,10 @@ $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp
|
||||
$(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a
|
||||
.PHONY: $(rootdir)/libvmcp
|
||||
|
||||
$(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb
|
||||
$(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so
|
||||
.PHONY: $(rootdir)/libekmfweb
|
||||
|
||||
$(rootdir)/zipl/boot/data.o:
|
||||
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
||||
|
||||
|
||||
@@ -2,28 +2,10 @@ include ../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
||||
|
||||
|
||||
ifeq (${HAVE_PFM},0)
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_PFM=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_PFM=0
|
||||
|
||||
else
|
||||
|
||||
check_dep:
|
||||
$(call check_dep, \
|
||||
"cpacfstats", \
|
||||
"perfmon/pfmlib.h", \
|
||||
"libpfm-devel or libpfm4-dev", \
|
||||
"HAVE_PFM=0")
|
||||
|
||||
all: check_dep cpacfstats cpacfstatsd
|
||||
all: cpacfstats cpacfstatsd
|
||||
|
||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -lpfm -o $@
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
cpacfstats: cpacfstats.o stats_sock.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
@@ -34,7 +16,6 @@ install: all
|
||||
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
|
||||
|
||||
endif
|
||||
clean:
|
||||
rm -f *.o *~ cpacfstatsd cpacfstats
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" cpacfstats.1
|
||||
.\"
|
||||
.\" Copyright IBM Corp. 2015, 2017
|
||||
.\" Copyright IBM Corp. 2015, 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.
|
||||
.\"
|
||||
@@ -80,31 +80,33 @@ Display help information for the command.
|
||||
Display version and copyright information for the command.
|
||||
.TP
|
||||
\fB\-e\fR or \fB\-\-enable\fR [counter]
|
||||
Enable one or all CPACF performance counters. The optional counter argument
|
||||
can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or \fBall\fR. If
|
||||
the counter argument is omitted, all performance counters are
|
||||
enabled. Enabling a counter does not reset it. New events are added to the
|
||||
current counter value.
|
||||
Enable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are enabled. Enabling a counter does not reset
|
||||
it. New events are added to the current counter value.
|
||||
.TP
|
||||
\fB\-d\fR or \fB\-\-disable\fR [counter]
|
||||
Disable one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
|
||||
\fBall\fR. If the counter argument is omitted, all performance counters
|
||||
are disabled. Disabling a counter does not reset it. The counter value is
|
||||
preserved when a counter is disabled, and counting will resume using the
|
||||
preserved value when the counter is re-enabled.
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are disabled. Disabling a counter does not reset
|
||||
it. The counter value is preserved when a counter is disabled, and
|
||||
counting will resume using the preserved value when the counter is
|
||||
re-enabled.
|
||||
.TP
|
||||
\fB\-r\fR or \fB\-\-reset\fR [counter]
|
||||
Reset one or all CPACF performance counters. The optional counter
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
|
||||
\fBall\fR. If the counter argument is omitted, all performance counters are
|
||||
reset to 0.
|
||||
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||
performance counters are reset to 0.
|
||||
.TP
|
||||
\fB\-p\fR or \fB\-\-print\fR [counter]
|
||||
Display the value of one or all CPACF performance counters. The optional
|
||||
counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR
|
||||
or \fBall\fR. If the counter argument is omitted or if there is no
|
||||
argument, all performance counters are displayed.
|
||||
Display the value of one or all CPACF performance counters. The
|
||||
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
|
||||
\fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter
|
||||
argument is omitted or if there is no argument, all performance
|
||||
counters are displayed.
|
||||
.TP
|
||||
The default command is --print all.
|
||||
.
|
||||
@@ -123,5 +125,10 @@ version mismatch between client and daemon, or the application is out of
|
||||
memory. The application prints a message with the details of the error and
|
||||
the errno value.
|
||||
.
|
||||
.SH NOTES
|
||||
ECC counters are only available since z15. cpacfstats will show the
|
||||
counters as \fIunsupported\fR if the hardware does not support ECC
|
||||
counters.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
cpacfstatsd (8)
|
||||
.BR cpacfstatsd (8)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* cpacfstats client implementation
|
||||
*
|
||||
* Copyright IBM Corp. 2015, 2017
|
||||
* Copyright IBM Corp. 2015, 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.
|
||||
@@ -39,13 +39,14 @@ static const char *const usage =
|
||||
"\t-d, --disable [counter] Disable one or all counters\n"
|
||||
"\t-r, --reset [counter] Reset one or all counter values\n"
|
||||
"\t-p, --print [counter] Print one or all counter values\n"
|
||||
"\tcounter can be: 'aes' 'des' 'rng' 'sha' or 'all'\n";
|
||||
"\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc' or 'all'\n";
|
||||
|
||||
static const char *const counter_str[] = {
|
||||
[DES_FUNCTIONS] = "des",
|
||||
[AES_FUNCTIONS] = "aes",
|
||||
[SHA_FUNCTIONS] = "sha",
|
||||
[PRNG_FUNCTIONS] = "rng",
|
||||
[ECC_FUNCTIONS] = "ecc",
|
||||
[ALL_COUNTER] = "all"
|
||||
};
|
||||
|
||||
@@ -98,6 +99,8 @@ static void print_answer(int ctr, int state, uint64_t value)
|
||||
counter_str[ctr], state);
|
||||
else if (state == DISABLED)
|
||||
printf(" %s counter: disabled\n", counter_str[ctr]);
|
||||
else if (state == UNSUPPORTED)
|
||||
printf(" %s counter: unsupported\n", counter_str[ctr]);
|
||||
else
|
||||
printf(" %s counter: %"PRIu64"\n", counter_str[ctr], value);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* common function prototypes and definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2015, 2017
|
||||
* Copyright IBM Corp. 2015, 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define COPYRIGHT "Copyright IBM Corp. 2015, 2017"
|
||||
#define COPYRIGHT "Copyright IBM Corp. 2015, 2020"
|
||||
|
||||
int eprint(const char *format, ...);
|
||||
|
||||
@@ -27,6 +27,7 @@ enum ctr_e {
|
||||
AES_FUNCTIONS,
|
||||
SHA_FUNCTIONS,
|
||||
PRNG_FUNCTIONS,
|
||||
ECC_FUNCTIONS,
|
||||
ALL_COUNTER
|
||||
};
|
||||
|
||||
@@ -44,7 +45,8 @@ enum cmd_e {
|
||||
|
||||
enum state_e {
|
||||
DISABLED = 0,
|
||||
ENABLED
|
||||
ENABLED,
|
||||
UNSUPPORTED
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -108,5 +110,6 @@ int perf_enable_ctr(enum ctr_e ctr);
|
||||
int perf_disable_ctr(enum ctr_e ctr);
|
||||
int perf_reset_ctr(enum ctr_e ctr);
|
||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
|
||||
int perf_ecc_supported(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" cpacfstatsd.8
|
||||
.\"
|
||||
.\" Copyright IBM Corp. 2015, 2017
|
||||
.\" Copyright IBM Corp. 2015, 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.
|
||||
.\"
|
||||
@@ -37,7 +37,10 @@ config option enabled.
|
||||
.P
|
||||
- Libpfm version 4 or higher is needed to successfully run the daemon.
|
||||
.P
|
||||
- Your LPAR must be configured to enable the "Counter Facility Security Options".
|
||||
- On the HMC or SE, authorize the LPAR for each counter set you want
|
||||
to use. Customize the LPAR activation profile and modify the Counter
|
||||
Facility Security Options. You need to activate the "Crypto activity
|
||||
counter set authorization control" checkbox.
|
||||
.P
|
||||
- The daemon requires root privileges to interact with the performance
|
||||
ioctls of the kernel.
|
||||
@@ -47,7 +50,7 @@ restart the daemon to ensure correct summing of the per-CPU performance
|
||||
counters.
|
||||
|
||||
The starting daemon first checks for any stale pid file
|
||||
/run/cpacfstatsd.pid. If this file exists, and the process ID in the
|
||||
\%/run/cpacfstatsd.pid. If this file exists, and the process ID in the
|
||||
file belongs to an active process, an error message is printed to the
|
||||
console and the program terminates.
|
||||
|
||||
@@ -94,4 +97,4 @@ done in the re-spawned process. Check the syslog for success or failure.
|
||||
The daemon could not be set to run in the background.
|
||||
|
||||
.SH SEE ALSO
|
||||
cpacfstats (1)
|
||||
.BR cpacfstats (1)
|
||||
|
||||
@@ -94,20 +94,24 @@ static int do_enable(int s, enum ctr_e ctr)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; i++) {
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
if (!ctr_state[i]) {
|
||||
if (ctr_state[i] == DISABLED) {
|
||||
rc = perf_enable_ctr(i);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
}
|
||||
ctr_state[i] = 1;
|
||||
ctr_state[i] = ENABLED;
|
||||
}
|
||||
rc = perf_read_ctr(i, &value);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
if (ctr_state[i] == UNSUPPORTED) {
|
||||
send_answer(s, i, UNSUPPORTED, 0);
|
||||
} else {
|
||||
rc = perf_read_ctr(i, &value);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
break;
|
||||
}
|
||||
send_answer(s, i, ENABLED, value);
|
||||
}
|
||||
send_answer(s, i, ENABLED, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +125,7 @@ static int do_disable(int s, enum ctr_e ctr)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; i++) {
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
if (ctr_state[i]) {
|
||||
if (ctr_state[i] == ENABLED) {
|
||||
rc = perf_disable_ctr(i);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
@@ -129,7 +133,7 @@ static int do_disable(int s, enum ctr_e ctr)
|
||||
}
|
||||
ctr_state[i] = 0;
|
||||
}
|
||||
send_answer(s, i, DISABLED, 0);
|
||||
send_answer(s, i, ctr_state[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +147,7 @@ static int do_reset(int s, enum ctr_e ctr)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; i++) {
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
if (ctr_state[i]) {
|
||||
if (ctr_state[i] == ENABLED) {
|
||||
rc = perf_reset_ctr(i);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
@@ -151,7 +155,7 @@ static int do_reset(int s, enum ctr_e ctr)
|
||||
}
|
||||
send_answer(s, i, ENABLED, 0);
|
||||
} else {
|
||||
send_answer(s, i, DISABLED, 0);
|
||||
send_answer(s, i, ctr_state[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +170,7 @@ static int do_print(int s, enum ctr_e ctr)
|
||||
|
||||
for (i = 0; i < ALL_COUNTER; i++) {
|
||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||
if (ctr_state[i]) {
|
||||
if (ctr_state[i] == ENABLED) {
|
||||
rc = perf_read_ctr(i, &value);
|
||||
if (rc != 0) {
|
||||
send_answer(s, i, rc, 0);
|
||||
@@ -174,7 +178,7 @@ static int do_print(int s, enum ctr_e ctr)
|
||||
}
|
||||
send_answer(s, i, ENABLED, value);
|
||||
} else {
|
||||
send_answer(s, i, DISABLED, 0);
|
||||
send_answer(s, i, ctr_state[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,6 +416,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
atexit(perf_close);
|
||||
|
||||
if (!perf_ecc_supported())
|
||||
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
|
||||
|
||||
sfd = open_socket(SERVER);
|
||||
if (sfd < 0) {
|
||||
eprint("Couldn't initialize server socket\n");
|
||||
|
||||
@@ -3,36 +3,39 @@
|
||||
*
|
||||
* low level perf functions
|
||||
*
|
||||
* Copyright IBM Corp. 2015, 2017
|
||||
* Copyright IBM Corp. 2015, 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.
|
||||
*/
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <perfmon/perf_event.h>
|
||||
#include <perfmon/pfmlib.h>
|
||||
#include <perfmon/pfmlib_perf_event.h>
|
||||
#include <limits.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cpacfstats.h"
|
||||
|
||||
/* correlation between counter and perf counter string */
|
||||
static const struct {
|
||||
char pfm_name[80];
|
||||
char pmu[20];
|
||||
char pfm_name[60];
|
||||
enum ctr_e ctr;
|
||||
} pmf_counter_name[ALL_COUNTER] = {
|
||||
{"cpum_cf::DEA_FUNCTIONS", DES_FUNCTIONS},
|
||||
{"cpum_cf::AES_FUNCTIONS", AES_FUNCTIONS},
|
||||
{"cpum_cf::SHA_FUNCTIONS", SHA_FUNCTIONS},
|
||||
{"cpum_cf::PRNG_FUNCTIONS", PRNG_FUNCTIONS}
|
||||
{"cpum_cf", "DEA_FUNCTIONS", DES_FUNCTIONS},
|
||||
{"cpum_cf", "AES_FUNCTIONS", AES_FUNCTIONS},
|
||||
{"cpum_cf", "SHA_FUNCTIONS", SHA_FUNCTIONS},
|
||||
{"cpum_cf", "PRNG_FUNCTIONS", PRNG_FUNCTIONS},
|
||||
{"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -53,27 +56,106 @@ static const struct {
|
||||
*/
|
||||
static int *ctr_fds[ALL_COUNTER];
|
||||
|
||||
static int ecc_supported;
|
||||
|
||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||
int cpu, int group_fd, unsigned long flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
|
||||
group_fd, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perf_supported(void)
|
||||
{
|
||||
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
|
||||
}
|
||||
|
||||
static int perf_counter_supported(const char *pmu, const char *counter)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
||||
pmu, counter) >= PATH_MAX) {
|
||||
eprint("overflow in path name");
|
||||
return 0;
|
||||
}
|
||||
return !access(buf, R_OK);
|
||||
}
|
||||
|
||||
static int perf_event_encode(struct perf_event_attr *attr,
|
||||
const char *pmu, const char *event)
|
||||
{
|
||||
FILE *f;
|
||||
int eventid;
|
||||
int pmutype;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
||||
pmu, event) >= PATH_MAX) {
|
||||
eprint("overflow in path name");
|
||||
return -1;
|
||||
}
|
||||
f = fopen(buf, "r");
|
||||
if (!f) {
|
||||
eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu,
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fscanf(f, "event=0x%x\n", &eventid) != 1) {
|
||||
fclose(f);
|
||||
eprint("Event file %s has invalid format\n", buf);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/type",
|
||||
pmu) >= PATH_MAX) {
|
||||
eprint("overflow in path name");
|
||||
return -1;
|
||||
}
|
||||
f = fopen(buf, "r");
|
||||
if (!f) {
|
||||
eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu,
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fscanf(f, "%d\n", &pmutype) != 1) {
|
||||
fclose(f);
|
||||
eprint("Type file %s has invalid format\n", buf);
|
||||
return -1;
|
||||
}
|
||||
attr->type = pmutype;
|
||||
attr->config = eventid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_init(void)
|
||||
{
|
||||
int i, cpus, ctr, cpu, ec, *fds;
|
||||
int i, cpus, ctr, cpu, *fds;
|
||||
|
||||
memset(ctr_fds, 0, sizeof(ctr_fds));
|
||||
|
||||
/* initialize performance monitoring library */
|
||||
ec = pfm_initialize();
|
||||
if (ec != PFM_SUCCESS) {
|
||||
eprint("Pfm_initialize() returned with failure (%d:%s)\n",
|
||||
ec, pfm_strerror(ec));
|
||||
if (!perf_supported()) {
|
||||
eprint("Performance counter not supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if ECC is supported on current hardware */
|
||||
ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT");
|
||||
|
||||
/* get number of logical processors */
|
||||
cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
/* for each counter */
|
||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||
|
||||
/* Skip ECC counters completely if unsupported */
|
||||
if (ctr == ECC_FUNCTIONS && !ecc_supported)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* allocate an array of ints to store for each CPU
|
||||
* one filedescriptor + a terminating 0
|
||||
@@ -88,37 +170,28 @@ int perf_init(void)
|
||||
|
||||
ctr_fds[ctr] = fds;
|
||||
|
||||
/* search for the counter's corresponding pfm name */
|
||||
for (i = ALL_COUNTER-1; i >= 0; i--)
|
||||
if ((int) pmf_counter_name[i].ctr == ctr)
|
||||
break;
|
||||
if (i < 0) {
|
||||
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
|
||||
ctr, __FILE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (cpu = 0; cpu < cpus; cpu++) {
|
||||
pfm_perf_encode_arg_t pfm_arg;
|
||||
struct perf_event_attr pfm_event;
|
||||
int fd;
|
||||
|
||||
memset(&pfm_arg, 0, sizeof(pfm_arg));
|
||||
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||
pfm_arg.attr = &pfm_event;
|
||||
pfm_arg.size = sizeof(pfm_arg);
|
||||
pfm_event.size = sizeof(pfm_event);
|
||||
|
||||
/* search for the counter's corresponding pfm name */
|
||||
for (i = ALL_COUNTER-1; i >= 0; i--)
|
||||
if ((int) pmf_counter_name[i].ctr == ctr)
|
||||
break;
|
||||
if (i < 0) {
|
||||
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
|
||||
ctr, __FILE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* encode the counters perf event into pfm_arg.attr */
|
||||
ec = pfm_get_os_event_encoding(
|
||||
pmf_counter_name[i].pfm_name,
|
||||
PFM_PLM0,
|
||||
PFM_OS_PERF_EVENT,
|
||||
&pfm_arg);
|
||||
if (ec != PFM_SUCCESS) {
|
||||
eprint("Pfm_initialize() for %s failed (%d:%s)\n",
|
||||
if (perf_event_encode(&pfm_event,
|
||||
pmf_counter_name[i].pmu,
|
||||
pmf_counter_name[i].pfm_name)) {
|
||||
eprint("Failed to initialize counter %s for pmu %s\n",
|
||||
pmf_counter_name[i].pfm_name,
|
||||
ec, pfm_strerror(ec));
|
||||
pmf_counter_name[i].pmu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -257,3 +330,8 @@ int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int perf_ecc_supported(void)
|
||||
{
|
||||
return ecc_supported;
|
||||
}
|
||||
|
||||
@@ -1,66 +1,28 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
include ../common.mak
|
||||
|
||||
|
||||
CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf
|
||||
DATA_FILES = cpum-cf-hw-counter.map \
|
||||
cpum-cf-cfvn-1.ctr cpum-cf-cfvn-3.ctr \
|
||||
cpum-cf-csvn-12345.ctr cpum-cf-csvn-6.ctr \
|
||||
cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \
|
||||
cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \
|
||||
cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr
|
||||
LIB_FILES = bin/cpumf_helper
|
||||
USRBIN_SCRIPTS = bin/lscpumf
|
||||
USRSBIN_SCRIPTS = bin/chcpumf
|
||||
BIN_FILES = lscpumf chcpumf
|
||||
MAN_FILES = lscpumf.1 chcpumf.8
|
||||
|
||||
all:
|
||||
all: $(BIN_FILES)
|
||||
|
||||
scripts: $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
|
||||
chmod +x $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
check:
|
||||
lscpumf: lscpumf.o $(libs)
|
||||
chcpumf: chcpumf.o $(libs)
|
||||
|
||||
install: scripts install-man
|
||||
for prg in $(USRBIN_SCRIPTS); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \
|
||||
done
|
||||
for prg in $(USRSBIN_SCRIPTS); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRSBINDIR) ; \
|
||||
done
|
||||
test -d $(DESTDIR)$(CPUMF_DATADIR) || mkdir -p $(DESTDIR)$(CPUMF_DATADIR)
|
||||
for lib in $(LIB_FILES); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$lib $(DESTDIR)$(TOOLS_LIBDIR) ; \
|
||||
done
|
||||
for data in $(DATA_FILES); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 data/$$data $(DESTDIR)$(CPUMF_DATADIR) ; \
|
||||
install: all install-man
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||
for binf in $(BIN_FILES); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$binf $(DESTDIR)$(BINDIR); \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ $(BIN_FILES) core
|
||||
|
||||
install-man:
|
||||
for man in $(MAN_FILES); do \
|
||||
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D man/$$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
|
||||
done
|
||||
|
||||
man2pdf:
|
||||
for man in $(MAN_FILES); do \
|
||||
man -t man/$$man |ps2pdf -sPAPERSIZE=a4 - man/$${man}.pdf ; \
|
||||
done
|
||||
man2text:
|
||||
for man in $(MAN_FILES); do \
|
||||
MANWIDTH=80 LANG=C man man/$$man |col -b |expand > man/$${man}.txt ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -f $(LIB_FILES) $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS)
|
||||
|
||||
%: %.in
|
||||
real_libdir=$(TOOLS_LIBDIR); \
|
||||
real_cpumfdatadir=$(CPUMF_DATADIR); \
|
||||
$(SED) -e "s#@lib_path@#$$real_libdir#g" \
|
||||
-e "s#@cpumfdata_path@#$$real_cpumfdatadir#g" \
|
||||
-e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \
|
||||
< $< > $@
|
||||
|
||||
.PHONY: all scripts install install-man man2pdf man2text clean
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
#!/usr/bin/perl -W
|
||||
#
|
||||
# chcpumf - Control CPU-measurement facilities
|
||||
#
|
||||
# Copyright IBM Corp. 2014, 2017
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Basename qw/fileparse/;
|
||||
use Data::Dumper;
|
||||
use Getopt::Long qw/:config no_ignore_case/;
|
||||
|
||||
# Global constants
|
||||
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
|
||||
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
|
||||
|
||||
# Prototypes
|
||||
sub main();
|
||||
sub show_help();
|
||||
sub show_version();
|
||||
sub do_set_sfb_size($);
|
||||
sub cpumf_set_sfb_size($);
|
||||
sub invoke_cpumf_helper($);
|
||||
|
||||
sub main()
|
||||
{
|
||||
my $config = {
|
||||
# Internal data
|
||||
cpumf => {}, # CPU-MF information hash
|
||||
};
|
||||
|
||||
unless (GetOptions(
|
||||
# General options for help, version, verbose,...
|
||||
"h|help" => \&show_help,
|
||||
"v|version" => \&show_version,
|
||||
"V|verbose+" => \$config->{verbose},
|
||||
# Change specific options
|
||||
"m|min=i" => \$config->{min},
|
||||
"x|max=i" => \$config->{max},
|
||||
)) {
|
||||
print STDERR "One or more options are not valid\n";
|
||||
print STDERR "Try '" . fileparse($0) .
|
||||
" --help' for more information\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Collect CPU-MF information
|
||||
$config->{cpumf} = invoke_cpumf_helper("-i");
|
||||
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
|
||||
|
||||
# Process parameters
|
||||
my $exitval = 5;
|
||||
if (defined($config->{min}) || defined($config->{max})) {
|
||||
$exitval = do_set_sfb_size($config);
|
||||
} else {
|
||||
print STDERR "You must specify a valid option\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
exit($exitval);
|
||||
}
|
||||
|
||||
sub show_help()
|
||||
{
|
||||
my $prog = fileparse($0);
|
||||
|
||||
print <<"EoHelp";
|
||||
Usage: chcpumf -h|-v
|
||||
chcpumf -m <num_sdb>
|
||||
chcpumf -x <num_sdb>
|
||||
|
||||
Options:
|
||||
-m <num_sdb> Specifies the initial size of the sampling buffer.
|
||||
A sample-data-block (SDB) consumes about 4 kilobytes.
|
||||
-x <num_sdb> Specifies the maximum size of the sampling buffer.
|
||||
A sample-data-block (SDB) consumes about 4 kilobytes.
|
||||
-h Displays help information, then exits.
|
||||
-v Displays version information, then exits.
|
||||
|
||||
For more help information, issue 'man $prog'.
|
||||
EoHelp
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub show_version()
|
||||
{
|
||||
print <<'EoVersion';
|
||||
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
|
||||
Copyright IBM Corp. 2014, 2017
|
||||
|
||||
EoVersion
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub cpumf_set_sfb_size($)
|
||||
{
|
||||
my @size = @{shift()};
|
||||
my $val = join ',', @size[0,1];
|
||||
my ($SFBSIZE, $rc);
|
||||
|
||||
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
|
||||
# Check the return code of print and close to detect error conditions
|
||||
# reported by the device driver. Because perl might buffer data, print
|
||||
# might be successful, but close might then report the error condition.
|
||||
# So check the return code of both functions and always close the file
|
||||
# handle.
|
||||
$rc = print { $SFBSIZE } "$val\n";
|
||||
unless ($rc) {
|
||||
close($SFBSIZE);
|
||||
return $rc;
|
||||
}
|
||||
return close($SFBSIZE);
|
||||
}
|
||||
|
||||
sub do_set_sfb_size($)
|
||||
{
|
||||
my $c = shift();
|
||||
my $size;
|
||||
|
||||
# Check if sampling facility is available
|
||||
unless (exists $c->{cpumf}->{sf}) {
|
||||
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||
return 2;
|
||||
}
|
||||
# Check if perf support is available
|
||||
unless (exists $c->{cpumf}->{sf}->{perf}) {
|
||||
print STDERR "No perf support for the CPU-measurement" .
|
||||
" sampling facility availalble\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
# Optionally, change the sampling buffer sizes
|
||||
if (defined($c->{min}) || defined($c->{max})) {
|
||||
$size = invoke_cpumf_helper("--sfb-size");
|
||||
# Use current size value for zero min/max specifications
|
||||
$size->[0] = $c->{min} if defined($c->{min});
|
||||
$size->[1] = $c->{max} if defined($c->{max});
|
||||
# Validate new settings
|
||||
if ($size->[0] < 1 || $size->[1] < 1) {
|
||||
die "The specified number(s) are not valid\n";
|
||||
}
|
||||
if ($size->[0] >= $size->[1]) {
|
||||
die "The specified maximum must be greater " .
|
||||
"than the minimum\n";
|
||||
}
|
||||
# Set new sampling buffer sizes
|
||||
unless (cpumf_set_sfb_size($size)) {
|
||||
die "Failed to change sampling buffer size: $!\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Finally, show sampling buffer sizes
|
||||
if ($c->{verbose}) {
|
||||
$size = invoke_cpumf_helper("--sfb-size");
|
||||
print "Sampling buffer sizes:\n";
|
||||
printf " Minimum: %6u sample-data-blocks\n", $size->[0];
|
||||
printf " Maximum: %6u sample-data-blocks\n", $size->[1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub invoke_cpumf_helper($)
|
||||
{
|
||||
my $parms = shift();
|
||||
my $result;
|
||||
|
||||
# Call helper module
|
||||
my $output = qx"$CPUMF_HELPER $parms";
|
||||
die "Failed to run helper module for '$parms'\n" if $? >> 8;
|
||||
$result = eval "$output";
|
||||
die "Failed to parse helper module data\n" if $@;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
&main();
|
||||
__DATA__
|
||||
__END__
|
||||
@@ -1,525 +0,0 @@
|
||||
#!/usr/bin/perl -W
|
||||
#
|
||||
# cpumf_helper - Helper module for managing CPU-measurement facilities (CPU-MF)
|
||||
#
|
||||
# Copyright IBM Corp. 2014, 2017
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
use strict;
|
||||
use warnings;
|
||||
use Carp qw/croak/;
|
||||
use Data::Dumper;
|
||||
use Getopt::Long qw/:config no_ignore_case/;
|
||||
use Pod::Usage;
|
||||
|
||||
|
||||
# Global constants
|
||||
my $SERVICE_LEVELS = '/proc/service_levels';
|
||||
my $CPUMF_DATA_DIR = '@cpumfdata_path@';
|
||||
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
|
||||
my $CPUM_SF_DBF='/sys/kernel/debug/s390dbf/cpum_sf';
|
||||
|
||||
# Counter set bits (according to QUERY COUNTER INFORMATION)
|
||||
my $BASIC_SET = 0x0002;
|
||||
my $PROBLEM_STATE_SET = 0x0004;
|
||||
my $CRYPTO_SET = 0x0008;
|
||||
my $EXTENTED_SET = 0x0001;
|
||||
my $MT_DIAG_SET = 0x0020;
|
||||
my $COPROC_GRP_SET = 0x8000;
|
||||
|
||||
|
||||
# Public prototypes
|
||||
sub cpumf_collect_data();
|
||||
sub cpumf_get_sfb_size();
|
||||
sub cpumf_set_sfb_size($);
|
||||
sub cpumf_parse_ctrdef($;$);
|
||||
sub cpumf_load_ctrdef($;$);
|
||||
sub cpumf_get_counter_set($);
|
||||
sub cpumf_counter_set_names();
|
||||
sub cpumf_counter_set_ids();
|
||||
sub cpumf_hardware_counter_map();
|
||||
|
||||
# Internal prototypes
|
||||
sub cpumf_parse_cf($$);
|
||||
sub cpumf_parse_sf($$);
|
||||
|
||||
|
||||
sub cpumf_get_sfb_size()
|
||||
{
|
||||
my $val = "0,0";
|
||||
my $SFBSIZE;
|
||||
|
||||
if (open($SFBSIZE, '<', $CPUM_SFB_SIZE)) {
|
||||
$val = <$SFBSIZE>;
|
||||
chomp($val);
|
||||
close($SFBSIZE);
|
||||
}
|
||||
|
||||
return [split /,/, $val];
|
||||
}
|
||||
|
||||
sub cpumf_set_sfb_size($)
|
||||
{
|
||||
my @size = @{shift()};
|
||||
my $val = join ',', @size[0,1];
|
||||
my ($SFBSIZE, $rc);
|
||||
|
||||
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
|
||||
# Check the return code of print and close to detect error conditions
|
||||
# reported by the device driver. Because perl might buffer data, print
|
||||
# might be successful, but close might then report the error condition.
|
||||
# So check the return code of both functions and always close the file
|
||||
# handle.
|
||||
$rc = print { $SFBSIZE } "$val\n";
|
||||
unless ($rc) {
|
||||
close($SFBSIZE);
|
||||
return $rc;
|
||||
}
|
||||
return close($SFBSIZE);
|
||||
}
|
||||
|
||||
|
||||
sub cpumf_parse_cf($$)
|
||||
{
|
||||
my ($ent, $data) = @_;
|
||||
|
||||
if ($ent =~ /version=([0-9.]+) authorization=([[:xdigit:]]+)/) {
|
||||
$data->{cf} = { version => $1, auth => hex($2) };
|
||||
}
|
||||
}
|
||||
|
||||
sub cpumf_parse_sf($$)
|
||||
{
|
||||
my ($ent, $data) = @_;
|
||||
|
||||
# Parse common sampling facility entry
|
||||
if ($ent =~ /min_rate=(\d+) max_rate=(\d+) cpu_speed=(\d+)/) {
|
||||
$data->{sf} = {
|
||||
min_sampl_interval => $1,
|
||||
max_sampl_interval => $2,
|
||||
cpu_speed => $3,
|
||||
# This contains a list of authorized sampling modes, for
|
||||
# example, basic
|
||||
modes => {},
|
||||
};
|
||||
}
|
||||
|
||||
# Parse sampling facility mode entries
|
||||
if ($ent =~ /mode=(\w+) sample_size=(\d+)/) {
|
||||
$data->{sf}->{modes}->{$1}->{sample_size} = $2;
|
||||
}
|
||||
}
|
||||
|
||||
sub cpumf_collect_data()
|
||||
{
|
||||
my $SL;
|
||||
|
||||
# Collect CPU-MF information from /proc/service_levels
|
||||
return undef unless open($SL, '<', $SERVICE_LEVELS);
|
||||
my @sl = <$SL>;
|
||||
chomp(@sl);
|
||||
close($SL);
|
||||
|
||||
# Process CPU-MF information and build data hash
|
||||
my $data = {};
|
||||
foreach my $ent (@sl) {
|
||||
$ent =~ s/^CPU-MF: // or next;
|
||||
cpumf_parse_cf($ent, $data) if $ent =~ s/Counter facility: //;
|
||||
cpumf_parse_sf($ent, $data) if $ent =~ s/Sampling facility: //;
|
||||
}
|
||||
|
||||
# Collect perf support for available facilities
|
||||
if (-e '/sys/bus/event_source/devices/cpum_cf') {
|
||||
$data->{cf}->{perf} = "cpum_cf" if exists $data->{cf};
|
||||
}
|
||||
if (-e '/sys/bus/event_source/devices/cpum_sf') {
|
||||
$data->{sf}->{perf} = "cpum_sf" if exists $data->{sf};
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
# Parse the specified counter definition file and returns a hash containing
|
||||
# the parsed counter definition. The optional argument specifies a hash
|
||||
# reference to which the new definitions are added. This reference is returned.
|
||||
sub cpumf_parse_ctrdef($;$)
|
||||
{
|
||||
my $ctrdef = shift();
|
||||
my $h = @_ ? shift() : {};
|
||||
my $CTRDEF;
|
||||
|
||||
return undef unless open($CTRDEF, '<', "$CPUMF_DATA_DIR/$ctrdef");
|
||||
my ($ctr, $name);
|
||||
while (my $line = <$CTRDEF>) {
|
||||
next if $line =~ /^#/;
|
||||
chomp($line);
|
||||
|
||||
# Parse start of counter definition entry
|
||||
if ($line =~ m/^Counter:\s*(0x[[:xdigit:]]+|\d+)
|
||||
\s+Name:\s*([[:alnum:]_]+)$/x) {
|
||||
($ctr, $name) = ($1, $2);
|
||||
$ctr = hex($ctr) if $ctr =~ /^0x/;
|
||||
unless (length($ctr)) {
|
||||
print STDERR "Found invalid entry in counter " .
|
||||
"definition: line $.\n";
|
||||
}
|
||||
$h->{$ctr} = { name => $name || "" };
|
||||
}
|
||||
|
||||
# At this point, a counter must be defined
|
||||
next unless defined($ctr);
|
||||
|
||||
# Parse short description (optional)
|
||||
if ($line =~ m/^Short-Description:\s*(\S.*)?$/) {
|
||||
$h->{$ctr}->{shortdesc} = $1 || "";
|
||||
|
||||
# Parse start of counter description
|
||||
} elsif ($line =~ m/^Description:\s*(\S.*)?$/) {
|
||||
$h->{$ctr}->{desc} = $1 || "";
|
||||
|
||||
# Parse end of counter description
|
||||
} elsif ($line =~ m/^\.$/) {
|
||||
# Complete the counter definition
|
||||
$h->{$ctr}->{set} = cpumf_get_counter_set($ctr);
|
||||
# Trim whitespaces
|
||||
$h->{$ctr}->{shortdesc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{shortdesc};
|
||||
$h->{$ctr}->{desc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{desc};
|
||||
# Finally, reset counter for next entry
|
||||
$ctr = undef;
|
||||
|
||||
# Line is part of counter description (if $ctr_num is set)
|
||||
} else {
|
||||
$h->{$ctr}->{desc} .= " $line";
|
||||
}
|
||||
}
|
||||
close($CTRDEF);
|
||||
|
||||
return $h;
|
||||
}
|
||||
|
||||
# IBM System z hardware with CPU-M counter facility support
|
||||
my $system_z_hwtype_map = {
|
||||
# Machine type Description
|
||||
'' => 'Unknown hardware model',
|
||||
2097 => 'IBM System z10 EC',
|
||||
2098 => 'IBM System z10 BC',
|
||||
2817 => 'IBM zEnterprise 196',
|
||||
2818 => 'IBM zEnterprise 114',
|
||||
2827 => 'IBM zEnterprise EC12',
|
||||
2828 => 'IBM zEnterprise BC12',
|
||||
2964 => 'IBM z13',
|
||||
2965 => 'IBM z13s',
|
||||
3906 => 'IBM z14',
|
||||
3907 => 'IBM z14 ZR1',
|
||||
};
|
||||
|
||||
sub get_hardware_type()
|
||||
{
|
||||
my $type = "";
|
||||
my $SYSINFO;
|
||||
|
||||
return undef unless open($SYSINFO, '<', '/proc/sysinfo');
|
||||
while (my $line = <$SYSINFO>) {
|
||||
if ($line =~ m/^Type:\s*(\d+)\s*$/) {
|
||||
$type = $1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
close($SYSINFO);
|
||||
return $type;
|
||||
}
|
||||
|
||||
sub get_cpum_cf_version()
|
||||
{
|
||||
my $SL;
|
||||
|
||||
my $v = {
|
||||
cfvn => 0,
|
||||
csvn => 0,
|
||||
};
|
||||
|
||||
return $v unless open($SL, '<', $SERVICE_LEVELS);
|
||||
while (my $line = <$SL>) {
|
||||
# CPU-MF: Counter facility: version=3.5
|
||||
if ($line =~ m/^CPU-MF: Counter facility: version=(\d+)\.(\d+)/) {
|
||||
$v->{cfvn} = $1; # Counter First Version Number
|
||||
$v->{csvn} = $2; # Counter Second Version Number
|
||||
last;
|
||||
}
|
||||
}
|
||||
close($SL);
|
||||
return $v
|
||||
}
|
||||
|
||||
sub cpumf_load_ctrdef($;$)
|
||||
{
|
||||
my $hw_type = shift();
|
||||
my $authorized = @_ ? shift() : 0xffff; # Counter Set authorization
|
||||
|
||||
my $ctrmap = cpumf_hardware_counter_map();
|
||||
return unless $ctrmap;
|
||||
|
||||
# Obtain CPU-MF counter facility versions
|
||||
my $version = get_cpum_cf_version();
|
||||
|
||||
# List of "generic" counter sets
|
||||
my @def = ();
|
||||
push @def, "cfvn-" . $version->{cfvn};
|
||||
if ($version->{csvn} >= 1 && $version->{csvn} <= 6) {
|
||||
push @def, "csvn-12345";
|
||||
}
|
||||
if ($version->{csvn} == 6) {
|
||||
push @def, "csvn-6";
|
||||
}
|
||||
|
||||
my $h = {};
|
||||
# Load counter set definition
|
||||
foreach my $ent (@def) {
|
||||
cpumf_parse_ctrdef($ctrmap->{$ent}, $h) or
|
||||
croak "Failed to read counter definition for $ent: $!\n";
|
||||
}
|
||||
# Load hardware model specific counter set(s)
|
||||
if ($hw_type && $ctrmap->{$hw_type}) {
|
||||
# Hardware-model specific counter sets are:
|
||||
# - Extended Counter Set
|
||||
# - MT-diagnostic Counter Set
|
||||
cpumf_parse_ctrdef($ctrmap->{$hw_type}, $h) or
|
||||
croak "Failed to read hardware-model counter definition: $!\n";
|
||||
}
|
||||
|
||||
# Remove counter sets that miss authorizations
|
||||
my @no_auth_list = ();
|
||||
foreach my $ctr (sort keys %$h) {
|
||||
push @no_auth_list, $ctr unless $h->{$ctr}->{set} & $authorized;
|
||||
}
|
||||
delete $h->{$_} foreach (@no_auth_list);
|
||||
|
||||
return $h;
|
||||
}
|
||||
|
||||
|
||||
sub cpumf_get_counter_set($)
|
||||
{
|
||||
my $ctr = shift();
|
||||
|
||||
return $BASIC_SET if $ctr < 32;
|
||||
return $PROBLEM_STATE_SET if $ctr < 64;
|
||||
return $CRYPTO_SET if $ctr < 128;
|
||||
#
|
||||
# The extended counter set ranges from
|
||||
# 128 to
|
||||
# 159 for csvn == 1
|
||||
# 175 for csvn == 2
|
||||
# 255 for csvn > 2
|
||||
# Tolerate any future counters up to
|
||||
# the MT-diagnostic counter set.
|
||||
return $EXTENTED_SET if $ctr < 448;
|
||||
#
|
||||
# The MT-diagnostic counter set ranges from
|
||||
# 448 to
|
||||
# 495 for cvsn > 3
|
||||
return $MT_DIAG_SET if $ctr <= 495;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub cpumf_counter_set_names()
|
||||
{
|
||||
return {
|
||||
# Identifier Name
|
||||
$BASIC_SET => 'Basic Counter Set',
|
||||
$PROBLEM_STATE_SET => 'Problem-State Counter Set',
|
||||
$CRYPTO_SET => 'Crypto-Activity Counter Set',
|
||||
$EXTENTED_SET => 'Extended Counter Set',
|
||||
$MT_DIAG_SET => 'MT-diagnostic Counter Set',
|
||||
$COPROC_GRP_SET => 'Coprocessor Group Counter Set',
|
||||
};
|
||||
}
|
||||
|
||||
sub cpumf_counter_set_ids()
|
||||
{
|
||||
return [$BASIC_SET, $PROBLEM_STATE_SET, $CRYPTO_SET, $EXTENTED_SET,
|
||||
$MT_DIAG_SET, $COPROC_GRP_SET];
|
||||
}
|
||||
|
||||
sub cpumf_hardware_counter_map()
|
||||
{
|
||||
my $map = do "$CPUMF_DATA_DIR/cpum-cf-hw-counter.map";
|
||||
croak "Failed to parse mapfile: $@" if $@;
|
||||
croak "Failed to read mapfile: $!" unless defined $map;
|
||||
return $map;
|
||||
}
|
||||
|
||||
|
||||
sub cpumf_helper_main()
|
||||
{
|
||||
# Configuration settings and options
|
||||
my $conf = {
|
||||
};
|
||||
|
||||
# Parse command line option
|
||||
GetOptions(
|
||||
"i|info" => \$conf->{opt_info},
|
||||
"c|counter=i" => \$conf->{opt_ctr},
|
||||
"ctr-def=s" => \$conf->{opt_ctrdef},
|
||||
"hardware-type" => \$conf->{opt_hwtype},
|
||||
"ctr-set-names" => \$conf->{opt_ctrset_names},
|
||||
"ctr-set-ids" => \$conf->{opt_ctrset_ids},
|
||||
"sfb-size" => \$conf->{opt_sfb_size},
|
||||
"ctr-sf" => \$conf->{opt_sf_ctr},
|
||||
) or pod2usage(-message =>"One or more options are not valid",
|
||||
-exitval => 1);
|
||||
|
||||
# Setting up Data::Dumper to create parseable Perl output
|
||||
local $Data::Dumper::Purity = 1;
|
||||
local $Data::Dumper::Sortkeys = 1;
|
||||
local $Data::Dumper::Terse = 1;
|
||||
|
||||
###print STDERR "CONF: " . Dumper($conf) . "\n";
|
||||
|
||||
# Process command line options
|
||||
my $exitval = 0;
|
||||
my $result;
|
||||
if (defined($conf->{opt_info})) {
|
||||
$result = cpumf_collect_data();
|
||||
|
||||
# Display System z hardware type
|
||||
} elsif (defined($conf->{opt_hwtype})) {
|
||||
my $type = get_hardware_type();
|
||||
$result = [$type, $system_z_hwtype_map->{$type} || ""];
|
||||
|
||||
# Display counters for current System z hardware
|
||||
} elsif (defined($conf->{opt_ctr})) {
|
||||
my $type = get_hardware_type();
|
||||
$type = 0 unless $type;
|
||||
$result = cpumf_load_ctrdef($type, $conf->{opt_ctr});
|
||||
|
||||
# Display counters for a particular System z hardware type
|
||||
} elsif (defined($conf->{opt_ctrdef})) {
|
||||
my $m = cpumf_hardware_counter_map();
|
||||
if (exists $m->{$conf->{opt_ctrdef}}) {
|
||||
$result = cpumf_parse_ctrdef($m->{$conf->{opt_ctrdef}});
|
||||
} else {
|
||||
printf STDERR "Invalid counter definition\n";
|
||||
$exitval = 2;
|
||||
}
|
||||
# Display the size of the sampling facility buffer (sfb)
|
||||
} elsif (defined($conf->{opt_sfb_size})) {
|
||||
$result = cpumf_get_sfb_size();
|
||||
|
||||
# Display counter definitions for the sampling facility support (perf)
|
||||
} elsif (defined($conf->{opt_sf_ctr})) {
|
||||
$result = cpumf_parse_ctrdef('cpum-sf-modes.ctr');
|
||||
|
||||
# Display mapping of counter set IDs to counter set names
|
||||
} elsif (defined($conf->{opt_ctrset_names})) {
|
||||
$result = cpumf_counter_set_names();
|
||||
|
||||
# Display counter set IDs
|
||||
} elsif (defined($conf->{opt_ctrset_ids})) {
|
||||
$result = cpumf_counter_set_ids();
|
||||
|
||||
} else {
|
||||
pod2usage(-message => "No option specified",
|
||||
-exitval => 1);
|
||||
}
|
||||
|
||||
# Display result
|
||||
print Dumper($result) if $result;
|
||||
exit $exitval;
|
||||
}
|
||||
|
||||
&cpumf_helper_main();
|
||||
__DATA__
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
||||
B<cpumf_helper> - Helper module for managing CPU-Measurement Facilities (CPU-MF)
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The B<cpumf_helper> program is not intended for ordinary use.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 8
|
||||
|
||||
=item B<-i>, B<--info>
|
||||
|
||||
Displays detailed information about installed and available CPU-measurement
|
||||
facilities and the related Linux support.
|
||||
|
||||
=item B<-c>, B<--counter> I<authorization_value>
|
||||
|
||||
Displays counter information for the current System z hardware. The
|
||||
authorization value provides information about the authorized counter sets. The
|
||||
value must be specified in decimal format.
|
||||
|
||||
To display all supported counters, specify C<65535> (FFFF hex).
|
||||
|
||||
To display supported counters for a particular System z hardware, use the
|
||||
B<--ctr-def> option and specify the System z hardware type.
|
||||
|
||||
=item B<--hardware-type>
|
||||
|
||||
Displays the System z hardware type.
|
||||
|
||||
=item B<--ctr-def> I<ctr-definition>
|
||||
|
||||
Displays detailed information about the specified counter definition.
|
||||
Valid counter definitions start with C<cfvn-> or <csvn-> followed by
|
||||
the counter first/second version number of the CPU-Measurement Counter
|
||||
Facility. To display counter information of model-specific counter
|
||||
sets, specify the System z hardware type for I<ctr-definition>.
|
||||
|
||||
=item B<--ctr-set-names>
|
||||
|
||||
Displays the mapping of counter set IDs to counter set names. The counter set
|
||||
IDs are numbers that are used in counter definitions.
|
||||
|
||||
=item B<--ctr-set-ids>
|
||||
|
||||
Displays the counter set IDs. The counter set IDs are numbers that match the
|
||||
authorization bits (see QUERY COUNTER INFORMATION). The output format is a
|
||||
list. To get the ID for a particular counter set, use an index number as
|
||||
follows:
|
||||
|
||||
=over 16
|
||||
|
||||
=item 0: Basic Counter Set
|
||||
|
||||
=item 1: Problem-State Counter Set
|
||||
|
||||
=item 2: Crypto-Activity Counter Set
|
||||
|
||||
=item 3: Extended Counter Set
|
||||
|
||||
=item 4: MT-diagnostic Counter Set
|
||||
|
||||
=item 5: Coprocessor Group Counter Set
|
||||
|
||||
=back
|
||||
|
||||
=item B<--ctr-sf>
|
||||
|
||||
Displays the counter definitions for the sampling facility support. These
|
||||
counter definitions are specific to Linux perf infrastructure.
|
||||
|
||||
=item B<--sfb-size>
|
||||
|
||||
Displays the size of the sampling facility buffer (SFB). The minimum and
|
||||
maximum numbers are measured in units of sample-data-blocks. A
|
||||
sample-data-block uses about 4 kilobytes.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FILES
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<lscpumf(1)>, L<chcpumf(1)>
|
||||
|
||||
=cut
|
||||
@@ -1,387 +0,0 @@
|
||||
#!/usr/bin/perl -W
|
||||
#
|
||||
# lscpumf - Display information about CPU-measurement facilities
|
||||
#
|
||||
# Copyright IBM Corp. 2014, 2017
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
use strict;
|
||||
use warnings;
|
||||
use Data::Dumper;
|
||||
use File::Basename qw/fileparse/;
|
||||
use Getopt::Long qw/:config no_ignore_case/;
|
||||
|
||||
# Global constants
|
||||
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
|
||||
|
||||
# Prototypes
|
||||
sub main();
|
||||
sub show_help();
|
||||
sub show_version();
|
||||
sub do_show_info($);
|
||||
sub do_show_cf($);
|
||||
sub do_show_sf($);
|
||||
sub do_show_ctr($;$);
|
||||
sub do_show_sf_events($);
|
||||
sub invoke_cpumf_helper($);
|
||||
|
||||
|
||||
sub main()
|
||||
{
|
||||
my $config = {
|
||||
# Internal data
|
||||
cpumf => {}, # CPU-MF information hash
|
||||
};
|
||||
|
||||
unless (GetOptions(
|
||||
# General options for help, version,...
|
||||
"h|help" => \&show_help,
|
||||
"v|version" => \&show_version,
|
||||
# Display options
|
||||
"i|info" => \$config->{opt_info},
|
||||
"c|list-counters" => \$config->{opt_ctr},
|
||||
"C|list-all-counters" => \$config->{opt_ctr_all},
|
||||
"s|list-sampling-events" => \$config->{opt_ctr_sf},
|
||||
)) {
|
||||
print STDERR "One or more options are not valid\n";
|
||||
print STDERR "Try '" . fileparse($0) .
|
||||
" --help' for more information\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Collect CPU-MF information
|
||||
$config->{cpumf} = invoke_cpumf_helper("-i");
|
||||
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
|
||||
|
||||
# Process parameters
|
||||
my $exitval = 5;
|
||||
if (defined($config->{opt_info})) {
|
||||
do_show_cf($config);
|
||||
do_show_sf($config);
|
||||
$exitval = 0;
|
||||
|
||||
} elsif (defined($config->{opt_ctr})) {
|
||||
$exitval = do_show_ctr($config);
|
||||
} elsif (defined($config->{opt_ctr_all})) {
|
||||
$exitval = do_show_ctr($config, "all")
|
||||
} elsif (defined($config->{opt_ctr_sf})) {
|
||||
$exitval = do_show_sf_events($config);
|
||||
} else {
|
||||
$exitval = do_show_info($config);
|
||||
}
|
||||
|
||||
exit($exitval);
|
||||
}
|
||||
|
||||
sub show_help()
|
||||
{
|
||||
my $prog = fileparse($0);
|
||||
|
||||
print <<"EoHelp";
|
||||
Usage: lscpumf -h|-v
|
||||
lscpumf [-i]
|
||||
lscpumf -c|-C
|
||||
|
||||
Options:
|
||||
-i Displays detailed information.
|
||||
-c Lists counters for which the LPAR is authorized.
|
||||
-C Lists counters regardless of LPAR authorization.
|
||||
-s Lists perf raw events that activate the sampling facility.
|
||||
-h Displays help information, then exits.
|
||||
-v Displays version information, then exits.
|
||||
|
||||
For more help information, issue 'man $prog'.
|
||||
EoHelp
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub show_version()
|
||||
{
|
||||
print <<'EoVersion';
|
||||
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
|
||||
Copyright IBM Corp. 2014, 2017
|
||||
|
||||
EoVersion
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub do_show_info($)
|
||||
{
|
||||
my $c = shift();
|
||||
my $cpumf = $c->{cpumf};
|
||||
my @f = ();
|
||||
|
||||
push @f, "CPU-measurement Counter Facility" if exists $cpumf->{cf};
|
||||
push @f, "CPU-measurement Sampling Facility" if exists $cpumf->{sf};
|
||||
|
||||
if (@f) {
|
||||
print((join "\n", @f) . "\n");
|
||||
} else {
|
||||
print STDERR "No CPU-measurement facilities detected\n";
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub do_show_cf($)
|
||||
{
|
||||
my $c = shift();
|
||||
|
||||
# Check if counter facility is available
|
||||
unless (exists $c->{cpumf}->{cf}) {
|
||||
print STDERR "No CPU-measurement counter facility detected\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
# Retrieve counter facility information
|
||||
my $cf = $c->{cpumf}->{cf};
|
||||
|
||||
# Create list of authorized counter sets
|
||||
my @sets = ();
|
||||
push @sets, "None" unless $cf->{auth};
|
||||
push @sets, "Crypto-Activity counter set" if $cf->{auth} & 0x8;
|
||||
push @sets, "Problem-State counter set" if $cf->{auth} & 0x4;
|
||||
push @sets, "Basic counter set" if $cf->{auth} & 0x2;
|
||||
push @sets, "Extented counter set" if $cf->{auth} & 0x1;
|
||||
push @sets, "MT-diagnostic counter set" if $cf->{auth} & 0x20;
|
||||
|
||||
print "CPU-measurement counter facility\n";
|
||||
print "-" x 74 . "\n";
|
||||
# TODO Display additional information about available conters depending
|
||||
# on the version information
|
||||
print "Version: " . $cf->{version} . "\n";
|
||||
print "\n";
|
||||
print "Authorized counter sets:\n";
|
||||
print " $_\n" foreach (sort @sets);
|
||||
printf "\nLinux perf event support: %s\n\n",
|
||||
exists $cf->{perf} ? "Yes (PMU: $cf->{perf})" : "No";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub div_ceil($$)
|
||||
{
|
||||
my ($a, $b) = @_;
|
||||
return int(($a + $b - 1) / $b);
|
||||
}
|
||||
|
||||
sub humanize_bytes($;$)
|
||||
{
|
||||
my $bytes = shift();
|
||||
my @units = split //, " KMGTPEZY";
|
||||
|
||||
my $u = @_ ? shift() : 0;
|
||||
while ($bytes >= 1024 && $u <= $#units) {
|
||||
$bytes /= 1024;
|
||||
$u++;
|
||||
}
|
||||
return sprintf "%.f%sB", $bytes, $units[$u];
|
||||
}
|
||||
|
||||
sub get_sfb_details($)
|
||||
{
|
||||
my $n_sdb = shift();
|
||||
|
||||
# Calculate sampling buffer structure
|
||||
my $n_sdbt = div_ceil($n_sdb, 511);
|
||||
my $n_pages = $n_sdb + $n_sdbt;
|
||||
return [
|
||||
$n_sdb, # number of sample-data-blocks
|
||||
$n_sdbt, # number of sample-data-block-tables
|
||||
$n_pages, # number of 4K pages
|
||||
$n_pages * 4096, # size in bytes
|
||||
];
|
||||
}
|
||||
|
||||
sub do_show_sf($)
|
||||
{
|
||||
my $c = shift();
|
||||
|
||||
# Check if sampling facility is available
|
||||
unless (exists $c->{cpumf}->{sf}) {
|
||||
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||
return 2;
|
||||
}
|
||||
my $sf = $c->{cpumf}->{sf};
|
||||
my $size = invoke_cpumf_helper("--sfb-size");
|
||||
|
||||
# Sampling facility information
|
||||
print "CPU-measurement sampling facility\n";
|
||||
print "-" x 74 . "\n";
|
||||
print "Sampling Interval:\n";
|
||||
printf " Minimum: %10u cycles (approx. %8u Hz)\n",
|
||||
$sf->{min_sampl_interval},
|
||||
1000000 * $sf->{cpu_speed} / $sf->{min_sampl_interval};
|
||||
printf " Maximum: %10u cycles (approx. %8u Hz)\n",
|
||||
$sf->{max_sampl_interval},
|
||||
1000000 * $sf->{cpu_speed} / $sf->{max_sampl_interval};
|
||||
print "\n";
|
||||
print "Authorized sampling modes:\n";
|
||||
foreach my $m (sort keys %{$sf->{modes}}) {
|
||||
printf " %-10s (sample size: %3u bytes)\n", $m,
|
||||
$sf->{modes}->{$m}->{sample_size};
|
||||
}
|
||||
print "\n";
|
||||
printf "\nLinux perf event support: %s\n\n",
|
||||
exists $sf->{perf} ? "Yes (PMU: $sf->{perf})" : "No";
|
||||
|
||||
# Sampling buffer settings for cpum_sf
|
||||
goto out unless exists $sf->{perf};
|
||||
|
||||
print "Current sampling buffer settings for $sf->{perf}:\n";
|
||||
printf " Basic-sampling mode\n";
|
||||
my $s = get_sfb_details($size->[0]);
|
||||
printf " Minimum: %6u sample-data-blocks (%6s)\n",
|
||||
$s->[0], humanize_bytes($s->[3]);
|
||||
$s = get_sfb_details($size->[1]);
|
||||
printf " Maximum: %6u sample-data-blocks (%6s)\n",
|
||||
$s->[0], humanize_bytes($s->[3]);
|
||||
unless (exists $sf->{modes}->{diagnostic}) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
# Sampling buffer setting specific to diagnostic-sampling mode
|
||||
my $f_diag = div_ceil($sf->{modes}->{diagnostic}->{sample_size},
|
||||
$sf->{modes}->{basic}->{sample_size});
|
||||
print "\n";
|
||||
printf " Diagnostic-sampling mode (including basic-sampling)\n";
|
||||
$s = get_sfb_details($size->[0] * $f_diag);
|
||||
printf " Minimum: %6u sample-data-blocks (%6s)\n",
|
||||
$s->[0], humanize_bytes($s->[3]);
|
||||
$s = get_sfb_details($size->[1] * $f_diag);
|
||||
printf " Maximum: %6u sample-data-blocks (%6s)\n",
|
||||
$s->[0], humanize_bytes($s->[3]);
|
||||
printf " Size factor: %2u\n", $f_diag;
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub print_counters($$)
|
||||
{
|
||||
my ($ctrdef, $header) = @_;
|
||||
my $set_name_map = invoke_cpumf_helper('--ctr-set-names');
|
||||
my $out = [];
|
||||
|
||||
my ($ctr_perf, $ctr_num, $set, $name, $desc);
|
||||
format PERF_CTR_FORM =
|
||||
r@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
$ctr_perf, $name
|
||||
|
||||
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
$desc
|
||||
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
|
||||
$desc
|
||||
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
$set
|
||||
|
||||
.
|
||||
print $header;
|
||||
$~ = "PERF_CTR_FORM";
|
||||
foreach my $ctr (sort { $a <=> $b } keys %$ctrdef) {
|
||||
$ctr_perf = sprintf "%x", $ctr;
|
||||
$ctr_num = $ctr < (1 << 16) ? $ctr : "";
|
||||
$name = $ctrdef->{$ctr}->{name};
|
||||
$desc = $ctrdef->{$ctr}->{shortdesc} ?
|
||||
$ctrdef->{$ctr}->{shortdesc}
|
||||
: $ctrdef->{$ctr}->{desc};
|
||||
$desc .= ".";
|
||||
$set = $set_name_map->{$ctrdef->{$ctr}->{set}};
|
||||
if ($set) {
|
||||
$set = "Counter $ctr_num / $set.";
|
||||
} else {
|
||||
$set = "This event is not associated with a counter set.";
|
||||
}
|
||||
write;
|
||||
}
|
||||
}
|
||||
|
||||
sub do_show_ctr($;$)
|
||||
{
|
||||
my $c = shift();
|
||||
|
||||
# Check if counter facility is available
|
||||
unless (exists $c->{cpumf}->{cf}) {
|
||||
print STDERR "No CPU-measurement counter facility detected\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
# Retrieve counter authorization ("all" or authorized counters only)
|
||||
my $auth = @_ ? hex("0xFFFF") : $c->{cpumf}->{cf}->{auth};
|
||||
|
||||
# Retrieve counter information
|
||||
my $ctrs = invoke_cpumf_helper("-c $auth");
|
||||
unless ($ctrs) {
|
||||
print STDERR "No counters are available or authorized\n";
|
||||
return 3;
|
||||
}
|
||||
|
||||
# Retrieve hardware type
|
||||
my $hwtype = invoke_cpumf_helper("--hardware-type");
|
||||
my $model = length $hwtype->[1] ? "for $hwtype->[1]" : "";
|
||||
my $header = <<"EoHeader";
|
||||
Perf event counter list $model
|
||||
==============================================================================
|
||||
|
||||
Raw
|
||||
event Name Description
|
||||
------------------------------------------------------------------------------
|
||||
EoHeader
|
||||
print_counters($ctrs, $header);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub do_show_sf_events($)
|
||||
{
|
||||
my $c = shift();
|
||||
|
||||
# Check if sampling facility is available
|
||||
unless (exists $c->{cpumf}->{sf}) {
|
||||
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||
return 2;
|
||||
}
|
||||
my $sf = $c->{cpumf}->{sf};
|
||||
my $events = invoke_cpumf_helper("--ctr-sf");
|
||||
|
||||
# Remove events with missing authorization
|
||||
delete $events->{0xB0000} unless exists $sf->{modes}->{basic};
|
||||
delete $events->{0xBD000} unless exists $sf->{modes}->{diagnostic};
|
||||
|
||||
unless ($events) {
|
||||
print STDERR "Sampling facility is not authorized\n";
|
||||
return 3;
|
||||
}
|
||||
|
||||
# Display sampling facility events (aka. counters)
|
||||
my $header = <<"EoHeader";
|
||||
Perf events for activating the sampling facility
|
||||
==============================================================================
|
||||
|
||||
Raw
|
||||
event Name Description
|
||||
------------------------------------------------------------------------------
|
||||
EoHeader
|
||||
print_counters($events, $header);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub invoke_cpumf_helper($)
|
||||
{
|
||||
my $parms = shift();
|
||||
my $result;
|
||||
|
||||
# Call helper module
|
||||
my $output = qx"$CPUMF_HELPER $parms";
|
||||
die "Failed to run helper module for '$parms'\n" if $? >> 8;
|
||||
$result = eval "$output";
|
||||
die "Failed to parse helper module data\n" if $@;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
&main();
|
||||
__DATA__
|
||||
__END__
|
||||
215
cpumf/chcpumf.c
Normal file
215
cpumf/chcpumf.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* chcpumf - Change CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
static int verbose;
|
||||
static unsigned long min_sdb, max_sdb;
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "min", required_argument, NULL, 'm' },
|
||||
.argument = "num_sdb",
|
||||
.desc = "Specifies the initial size of the sampling buffer.\n"
|
||||
"A sample-data-block (SDB) consumes about 4 kilobytes.",
|
||||
},
|
||||
{
|
||||
.option = { "max", required_argument, NULL, 'x' },
|
||||
.argument = "num_sdb",
|
||||
.desc = "Specifies the maximum size of the sampling buffer.\n"
|
||||
"A sample-data-block (SDB) consumes about 4 kilobytes.",
|
||||
},
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Verbose, display new sample-data-block values.",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "Change CPU Measurement facility charactertics",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2020,
|
||||
.pub_last = 2020,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
static long parse_buffersize(char *string)
|
||||
{
|
||||
char *suffix;
|
||||
long bytes;
|
||||
|
||||
bytes = strtol(string, &suffix, 10);
|
||||
if (strlen(suffix) > 1)
|
||||
return -1;
|
||||
switch (*suffix) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
bytes *= 1024;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
bytes *= 1048576;
|
||||
break;
|
||||
case '\0':
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int read_sfb(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
unsigned long cur_min_sdb, cur_max_sdb;
|
||||
int rc = EXIT_SUCCESS;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) {
|
||||
fprintf(stderr, "Error: Can not parse file " PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
} else {
|
||||
if (*min == 0)
|
||||
*min = cur_min_sdb;
|
||||
if (*max == 0)
|
||||
*max = cur_max_sdb;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int write_sfb(unsigned long min, unsigned long max)
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
char text[64];
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "w");
|
||||
if (fp == NULL) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
||||
len = strlen(text) + 1;
|
||||
if (fwrite(text, 1, len, fp) != len) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (fclose(fp)) {
|
||||
linux_error(PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (verbose && rc != EXIT_FAILURE)
|
||||
fprintf(stderr, "Sampling buffer sizes:\n"
|
||||
" Minimum:%7ld sample-data-blocks\n"
|
||||
" Maximum:%7ld sample-data-blocks\n",
|
||||
min, max);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char **argv)
|
||||
{
|
||||
int opt, action = 0;
|
||||
long new;
|
||||
|
||||
while ((opt = util_opt_getopt_long(argc, argv)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'x':
|
||||
new = parse_buffersize(optarg);
|
||||
if (new < 1) {
|
||||
fprintf(stderr, "The specified number(s)"
|
||||
" are not valid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
max_sdb = new;
|
||||
action = 1;
|
||||
break;
|
||||
case 'm':
|
||||
new = parse_buffersize(optarg);
|
||||
if (new < 1) {
|
||||
fprintf(stderr, "The specified number(s)"
|
||||
" are not valid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
min_sdb = new;
|
||||
action = 1;
|
||||
break;
|
||||
case 'V':
|
||||
verbose = 1;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "One or more options are not valid\n");
|
||||
fprintf(stderr, "Try 'chcpumf --help' for more"
|
||||
" information\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (!action) {
|
||||
fprintf(stderr, "You must specify a valid option\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
struct stat sbuf;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
parse_args(argc, argv);
|
||||
if (stat(PERF_PATH PERF_SF, &sbuf) != 0) {
|
||||
fprintf(stderr,
|
||||
"No CPU-measurement sampling facility detected\n");
|
||||
return ret;
|
||||
}
|
||||
if (read_sfb(&min_sdb, &max_sdb))
|
||||
return ret;
|
||||
if (min_sdb >= max_sdb) {
|
||||
fprintf(stderr, "The specified maximum must be greater "
|
||||
"than the minimum\n");
|
||||
return ret;
|
||||
}
|
||||
return write_sfb(min_sdb, max_sdb);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
Counter: 0 Name:CPU_CYCLES
|
||||
Short-Description:CPU Cycles
|
||||
Description:
|
||||
Cycle Count
|
||||
.
|
||||
Counter: 1 Name:INSTRUCTIONS
|
||||
Short-Description:Instructions
|
||||
Description:
|
||||
Instruction Count
|
||||
.
|
||||
Counter: 2 Name:L1I_DIR_WRITES
|
||||
Short-Description:L1I Directory Writes
|
||||
Description:
|
||||
Level-1 I-Cache Directory Write Count
|
||||
.
|
||||
Counter: 3 Name:L1I_PENALTY_CYCLES
|
||||
Short-Description:L1I Penalty Cycles
|
||||
Description:
|
||||
Level-1 I-Cache Penalty Cycle Count
|
||||
.
|
||||
Counter: 4 Name:L1D_DIR_WRITES
|
||||
Short-Description:L1D Directory Writes
|
||||
Description:
|
||||
Level-1 D-Cache Directory Write Count
|
||||
.
|
||||
Counter: 5 Name:L1D_PENALTY_CYCLES
|
||||
Short-Description:L1D Penalty Cycles
|
||||
Description:
|
||||
Level-1 D-Cache Penalty Cycle Count
|
||||
.
|
||||
Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES
|
||||
Short-Description:Problem-State CPU Cycles
|
||||
Description:
|
||||
Problem-State Cycle Count
|
||||
.
|
||||
Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS
|
||||
Short-Description:Problem-State Instructions
|
||||
Description:
|
||||
Problem-State Instruction Count
|
||||
.
|
||||
Counter: 34 Name:PROBLEM_STATE_L1I_DIR_WRITES
|
||||
Short-Description:Problem-State L1I Directory Writes
|
||||
Description:
|
||||
Problem-State Level-1 I-Cache Directory Write Count
|
||||
.
|
||||
Counter: 35 Name:PROBLEM_STATE_L1I_PENALTY_CYCLES
|
||||
Short-Description:Problem-State L1I Penalty Cycles
|
||||
Description:
|
||||
Problem-State Level-1 I-Cache Penalty Cycle Count
|
||||
.
|
||||
Counter: 36 Name:PROBLEM_STATE_L1D_DIR_WRITES
|
||||
Short-Description:Problem-State L1D Directory Writes
|
||||
Description:
|
||||
Problem-State Level-1 D-Cache Directory Write Count
|
||||
.
|
||||
Counter: 37 Name:PROBLEM_STATE_L1D_PENALTY_CYCLES
|
||||
Short-Description:Problem-State L1D Penalty Cycles
|
||||
Description:
|
||||
Problem-State Level-1 D-Cache Penalty Cycle Count
|
||||
.
|
||||
@@ -1,40 +0,0 @@
|
||||
Counter: 0 Name:CPU_CYCLES
|
||||
Short-Description:CPU Cycles
|
||||
Description:
|
||||
Cycle Count
|
||||
.
|
||||
Counter: 1 Name:INSTRUCTIONS
|
||||
Short-Description:Instructions
|
||||
Description:
|
||||
Instruction Count
|
||||
.
|
||||
Counter: 2 Name:L1I_DIR_WRITES
|
||||
Short-Description:L1I Directory Writes
|
||||
Description:
|
||||
Level-1 I-Cache Directory Write Count
|
||||
.
|
||||
Counter: 3 Name:L1I_PENALTY_CYCLES
|
||||
Short-Description:L1I Penalty Cycles
|
||||
Description:
|
||||
Level-1 I-Cache Penalty Cycle Count
|
||||
.
|
||||
Counter: 4 Name:L1D_DIR_WRITES
|
||||
Short-Description:L1D Directory Writes
|
||||
Description:
|
||||
Level-1 D-Cache Directory Write Count
|
||||
.
|
||||
Counter: 5 Name:L1D_PENALTY_CYCLES
|
||||
Short-Description:L1D Penalty Cycles
|
||||
Description:
|
||||
Level-1 D-Cache Penalty Cycle Count
|
||||
.
|
||||
Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES
|
||||
Short-Description:Problem-State CPU Cycles
|
||||
Description:
|
||||
Problem-State Cycle Count
|
||||
.
|
||||
Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS
|
||||
Short-Description:Problem-State Instructions
|
||||
Description:
|
||||
Problem-State Instruction Count
|
||||
.
|
||||
@@ -1,100 +0,0 @@
|
||||
Counter: 64 Name:PRNG_FUNCTIONS
|
||||
Short-Description:PRNG Functions
|
||||
Description:
|
||||
Total number of the PRNG functions issued by the CPU
|
||||
.
|
||||
Counter: 65 Name:PRNG_CYCLES
|
||||
Short-Description:PRNG Cycles
|
||||
Description:
|
||||
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||
performing PRNG functions issued by the CPU
|
||||
.
|
||||
Counter: 66 Name:PRNG_BLOCKED_FUNCTIONS
|
||||
Short-Description:PRNG Blocked Functions
|
||||
Description:
|
||||
Total number of the PRNG functions that are issued by the CPU and are
|
||||
blocked because the DEA/AES coprocessor is busy performing a function
|
||||
issued by another CPU
|
||||
.
|
||||
Counter: 67 Name:PRNG_BLOCKED_CYCLES
|
||||
Short-Description:PRNG Blocked Cycles
|
||||
Description:
|
||||
Total number of CPU cycles blocked for the PRNG functions issued by
|
||||
the CPU because the DEA/AES coprocessor is busy performing a function
|
||||
issued by another CPU
|
||||
.
|
||||
Counter: 68 Name:SHA_FUNCTIONS
|
||||
Short-Description:SHA Functions
|
||||
Description:
|
||||
Total number of SHA functions issued by the CPU
|
||||
.
|
||||
Counter: 69 Name:SHA_CYCLES
|
||||
Short-Description:SHA Cycles
|
||||
Description:
|
||||
Total number of CPU cycles when the SHA coprocessor is busy performing
|
||||
the SHA functions issued by the CPU
|
||||
.
|
||||
Counter: 70 Name:SHA_BLOCKED_FUNCTIONS
|
||||
Short-Description:SHA Blocked Functions
|
||||
Description:
|
||||
Total number of the SHA functions that are issued by the CPU and are
|
||||
blocked because the SHA coprocessor is busy performing a function issued
|
||||
by another CPU
|
||||
.
|
||||
Counter: 71 Name:SHA_BLOCKED_CYCLES
|
||||
Short-Description:SHA Bloced Cycles
|
||||
Description:
|
||||
Total number of CPU cycles blocked for the SHA functions issued by the
|
||||
CPU because the SHA coprocessor is busy performing a function issued
|
||||
by another CPU
|
||||
.
|
||||
Counter: 72 Name:DEA_FUNCTIONS
|
||||
Short-Description:DEA Functions
|
||||
Description:
|
||||
Total number of the DEA functions issued by the CPU
|
||||
.
|
||||
Counter: 73 Name:DEA_CYCLES
|
||||
Short-Description:DEA Cycles
|
||||
Description:
|
||||
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||
performing the DEA functions issued by the CPU
|
||||
.
|
||||
Counter: 74 Name:DEA_BLOCKED_FUNCTIONS
|
||||
Short-Description:DEA Blocked Functions
|
||||
Description:
|
||||
Total number of the DEA functions that are issued by the CPU and are
|
||||
blocked because the DEA/AES coprocessor is busy performing a function
|
||||
issued by another CPU
|
||||
.
|
||||
Counter: 75 Name:DEA_BLOCKED_CYCLES
|
||||
Short-Description:DEA Blocked Cycles
|
||||
Description:
|
||||
Total number of CPU cycles blocked for the DEA functions issued by the
|
||||
CPU because the DEA/AES coprocessor is busy performing a function issued
|
||||
by another CPU
|
||||
.
|
||||
Counter: 76 Name:AES_FUNCTIONS
|
||||
Short-Description:AES Functions
|
||||
Description:
|
||||
Total number of AES functions issued by the CPU
|
||||
.
|
||||
Counter: 77 Name:AES_CYCLES
|
||||
Short-Description:AES Cycles
|
||||
Description:
|
||||
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||
performing the AES functions issued by the CPU
|
||||
.
|
||||
Counter: 78 Name:AES_BLOCKED_FUNCTIONS
|
||||
Short-Description:AES Blocked Functions
|
||||
Description:
|
||||
Total number of AES functions that are issued by the CPU and are blocked
|
||||
because the DEA/AES coprocessor is busy performing a function issued
|
||||
by another CPU
|
||||
.
|
||||
Counter: 79 Name:AES_BLOCKED_CYCLES
|
||||
Short-Description:AES Blocked Cycles
|
||||
Description:
|
||||
Total number of CPU cycles blocked for the AES functions issued by the
|
||||
CPU because the DEA/AES coprocessor is busy performing a function issued
|
||||
by another CPU
|
||||
.
|
||||
@@ -1,33 +0,0 @@
|
||||
Counter: 80 Name:ECC_FUNCTION_COUNT
|
||||
Short-Description:ECC Function Count
|
||||
Description:
|
||||
This counter counts the
|
||||
total number of the elliptic-curve cryptography (ECC)
|
||||
functions issued by the CPU.
|
||||
.
|
||||
Counter: 81 Name:ECC_CYCLES_COUNT
|
||||
Short-Description:ECC Cycles Count
|
||||
Description:
|
||||
This counter counts the total
|
||||
number of CPU cycles when the ECC coprocessor is
|
||||
busy performing the elliptic-curve cryptography
|
||||
(ECC) functions issued by the CPU.
|
||||
.
|
||||
Counter: 82 Name:ECC_BLOCKED_FUNCTION_COUNT
|
||||
Short-Description:Ecc Blocked Function Count
|
||||
Description:
|
||||
This counter
|
||||
counts the total number of the elliptic-curve
|
||||
cryptography (ECC) functions that are issued by the CPU
|
||||
and are blocked because the ECC coprocessor is
|
||||
busy performing a function issued by another CPU.
|
||||
.
|
||||
Counter: 83 Name:ECC_BLOCKED_CYCLES_COUNT
|
||||
Short-Description:ECC Blocked Cycles Count
|
||||
Description:
|
||||
This counter counts
|
||||
the total number of CPU cycles blocked for the elliptic-curve
|
||||
cryptography (ECC) functions issued by the
|
||||
CPU because the ECC coprocessor is busy perform-
|
||||
ing a function issued by another CPU.
|
||||
.
|
||||
@@ -1,114 +0,0 @@
|
||||
Counter: 128 Name:L1I_L2_SOURCED_WRITES
|
||||
Short-Description:L1I L2 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from the Level-2 (L1.5) cache
|
||||
.
|
||||
Counter: 129 Name:L1D_L2_SOURCED_WRITES
|
||||
Short-Description:L1D L2 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the installed
|
||||
cache line was sourced from the Level-2 (L1.5) cache
|
||||
.
|
||||
Counter: 130 Name:L1I_L3_LOCAL_WRITES
|
||||
Short-Description:L1I L3 Local Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the installed
|
||||
cache line was sourced from the Level-3 cache that is on the same book
|
||||
as the Instruction cache (Local L2 cache)
|
||||
.
|
||||
Counter: 131 Name:L1D_L3_LOCAL_WRITES
|
||||
Short-Description:L1D L3 Local Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the installtion
|
||||
cache line was source from the Level-3 cache that is on the same book
|
||||
as the Data cache (Local L2 cache)
|
||||
.
|
||||
Counter: 132 Name:L1I_L3_REMOTE_WRITES
|
||||
Short-Description:L1I L3 Remote Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the installed
|
||||
cache line was sourced from a Level-3 cache that is not on the same
|
||||
book as the Instruction cache (Remote L2 cache)
|
||||
.
|
||||
Counter: 133 Name:L1D_L3_REMOTE_WRITES
|
||||
Short-Description:L1D L3 Remote Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the installed
|
||||
cache line was sourced from a Level-3 cache that is not on the same
|
||||
book as the Data cache (Remote L2 cache)
|
||||
.
|
||||
Counter: 134 Name:L1D_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1D Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the installed
|
||||
cache line was sourced from memory that is attached to the same book
|
||||
as the Data cache (Local Memory)
|
||||
.
|
||||
Counter: 135 Name:L1I_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1I Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache where the installed cache
|
||||
line was sourced from memory that is attached to the s ame book as the
|
||||
Instruction cache (Local Memory)
|
||||
.
|
||||
Counter: 136 Name:L1D_RO_EXCL_WRITES
|
||||
Short-Description:L1D Read-only Exclusive Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache where the line was originally
|
||||
in a Read-Only state in the cache but has been updated to be in the
|
||||
Exclusive state that allows stores to the cache line
|
||||
.
|
||||
Counter: 137 Name:L1I_CACHELINE_INVALIDATES
|
||||
Short-Description:L1I Cacheline Invalidates
|
||||
Description:
|
||||
A cache line in the Level-1 I-Cache has been invalidated by a store on
|
||||
the same CPU as the Level-1 I-Cache
|
||||
.
|
||||
Counter: 138 Name:ITLB1_WRITES
|
||||
Short-Description:ITLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written into the Level-1 Instruction
|
||||
Translation Lookaside Buffer
|
||||
.
|
||||
Counter: 139 Name:DTLB1_WRITES
|
||||
Short-Description:DTLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer
|
||||
.
|
||||
Counter: 140 Name:TLB2_PTE_WRITES
|
||||
Short-Description:TLB2 PTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Page Table
|
||||
Entry arrays
|
||||
.
|
||||
Counter: 141 Name:TLB2_CRSTE_WRITES
|
||||
Short-Description:TLB2 CRSTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays
|
||||
.
|
||||
Counter: 142 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||
Short-Description:TLB2 CRSTE One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays for a one-megabyte large page translation
|
||||
.
|
||||
Counter: 145 Name:ITLB1_MISSES
|
||||
Short-Description:ITLB1 Misses
|
||||
Description:
|
||||
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||
cycle an ITLB1 miss is in progress
|
||||
.
|
||||
Counter: 146 Name:DTLB1_MISSES
|
||||
Short-Description:DTLB1 Misses
|
||||
Description:
|
||||
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||
an DTLB1 miss is in progress
|
||||
.
|
||||
Counter: 147 Name:L2C_STORES_SENT
|
||||
Short-Description:L2C Stores Sent
|
||||
Description:
|
||||
Incremented by one for every store sent to Level-2 (L1.5) cache
|
||||
.
|
||||
@@ -1,373 +0,0 @@
|
||||
# Counter decriptions for the
|
||||
# IBM z13 extended counter and MT-diagnostic counter set
|
||||
#
|
||||
# Notes for transactional-execution mode symbolic names:
|
||||
# TX .. transactional-execution mode
|
||||
# NC .. nonconstrained
|
||||
# C .. constrained
|
||||
#
|
||||
# Undefined counters in the extended counter set:
|
||||
# 142
|
||||
# 180-217
|
||||
# 221-225
|
||||
# Undefined counters in the MT-diagnostic counter set:
|
||||
# 450-495
|
||||
#
|
||||
#
|
||||
# Extended Counter Set
|
||||
# ---------------------------------------------------------------------
|
||||
Counter:128 Name:L1D_RO_EXCL_WRITES
|
||||
Short-Description:L1D Read-only Exclusive Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache where the line was
|
||||
originally in a Read-Only state in the cache but has been updated
|
||||
to be in the Exclusive state that allows stores to the cache line.
|
||||
.
|
||||
Counter:129 Name:DTLB1_WRITES
|
||||
Short-Description:DTLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer
|
||||
.
|
||||
Counter:130 Name:DTLB1_MISSES
|
||||
Short-Description:DTLB1 Misses
|
||||
Description:
|
||||
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||
a DTLB1 miss is in progress.
|
||||
.
|
||||
Counter:131 Name:DTLB1_HPAGE_WRITES
|
||||
Short-Description:DTLB1 One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer for a one-megabyte page
|
||||
.
|
||||
Counter:132 Name:DTLB1_GPAGE_WRITES
|
||||
Short-Description:DTLB1 Two-Gigabyte Page Writes
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer for a two-gigabyte page.
|
||||
.
|
||||
Counter:133 Name:L1D_L2D_SOURCED_WRITES
|
||||
Short-Description:L1D L2D Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from the Level-2 Data cache
|
||||
.
|
||||
Counter:134 Name:ITLB1_WRITES
|
||||
Short-Description:ITLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Instruction
|
||||
Translation Lookaside Buffer
|
||||
.
|
||||
Counter:135 Name:ITLB1_MISSES
|
||||
Short-Description:ITLB1 Misses
|
||||
Description:
|
||||
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||
cycle an ITLB1 miss is in progress
|
||||
.
|
||||
Counter:136 Name:L1I_L2I_SOURCED_WRITES
|
||||
Short-Description:L1I L2I Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from the Level-2 Instruction cache
|
||||
.
|
||||
Counter:137 Name:TLB2_PTE_WRITES
|
||||
Short-Description:TLB2 PTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Page Table
|
||||
Entry arrays
|
||||
.
|
||||
Counter:138 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||
Short-Description:TLB2 CRSTE One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Combined Region
|
||||
Segment Table Entry arrays for a one-megabyte large page translation
|
||||
.
|
||||
Counter:139 Name:TLB2_CRSTE_WRITES
|
||||
Short-Description:TLB2 CRSTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Combined Region
|
||||
Segment Table Entry arrays
|
||||
.
|
||||
Counter:140 Name:TX_C_TEND
|
||||
Short-Description:Completed TEND instructions in constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a constrained transactional-execution
|
||||
mode
|
||||
.
|
||||
Counter:141 Name:TX_NC_TEND
|
||||
Short-Description:Completed TEND instructions in non-constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a non-constrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:143 Name:L1C_TLB1_MISSES
|
||||
Short-Description:L1C TLB1 Misses
|
||||
Description:
|
||||
Increments by one for any cycle where a Level-1 cache or Level-1 TLB miss
|
||||
is in progress.
|
||||
.
|
||||
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Chip Level-3 cache without intervention
|
||||
.
|
||||
Counter:145 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Chip Level-3 cache with intervention
|
||||
.
|
||||
Counter:146 Name:L1D_ONNODE_L4_SOURCED_WRITES
|
||||
Short-Description:L1D On-Node L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Node Level-4 cache
|
||||
.
|
||||
Counter:147 Name:L1D_ONNODE_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Node L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Node Level-3 cache with intervention
|
||||
.
|
||||
Counter:148 Name:L1D_ONNODE_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Node L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Node Level-3 cache without intervention
|
||||
.
|
||||
Counter:149 Name:L1D_ONDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1D On-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Drawer Level-4 cache
|
||||
.
|
||||
Counter:150 Name:L1D_ONDRAWER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Drawer L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Drawer Level-3 cache with intervention
|
||||
.
|
||||
Counter:151 Name:L1D_ONDRAWER_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Drawer L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Drawer Level-3 cache without intervention
|
||||
.
|
||||
Counter:152 Name:L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Same-Column L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Same-Column Level-4 cache
|
||||
.
|
||||
Counter:153 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Drawer Same-Column L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Same-Column Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:154 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Same-Column L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Same-Column Level-3 cache
|
||||
without intervention
|
||||
.
|
||||
Counter:155 Name:L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Far-Column L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Far-Column Level-4 cache
|
||||
.
|
||||
Counter:156 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Drawer Far-Column L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Far-Column Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:157 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Far-Column L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Far-Column Level-3 cache
|
||||
without intervention
|
||||
.
|
||||
Counter:158 Name:L1D_ONNODE_MEM_SOURCED_WRITES
|
||||
Short-Description:L1D On-Node Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Node memory
|
||||
.
|
||||
Counter:159 Name:L1D_ONDRAWER_MEM_SOURCED_WRITES
|
||||
Short-Description:L1D On-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Drawer memory
|
||||
.
|
||||
Counter:160 Name:L1D_OFFDRAWER_MEM_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Drawer memory
|
||||
.
|
||||
Counter:161 Name:L1D_ONCHIP_MEM_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Chip memory
|
||||
.
|
||||
Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On-Chip Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:163 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On Chip Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:164 Name:L1I_ONNODE_L4_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On-Node Level-4 cache
|
||||
.
|
||||
Counter:165 Name:L1I_ONNODE_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Node L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an On-Node Level-3 cache
|
||||
with intervention
|
||||
.
|
||||
Counter:166 Name:L1I_ONNODE_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Node L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an On-Node Level-3 cache
|
||||
without intervention
|
||||
.
|
||||
Counter:167 Name:L1I_ONDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1I On-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an On-Drawer Level-4 cache
|
||||
.
|
||||
Counter:168 Name:L1I_ONDRAWER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Drawer L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an On-Drawer Level-3 cache
|
||||
with intervention
|
||||
.
|
||||
Counter:169 Name:L1I_ONDRAWER_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Drawer L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an On-Drawer Level-3 cache
|
||||
without intervention
|
||||
.
|
||||
Counter:170 Name:L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Same-Column L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||
Level-4 cache
|
||||
.
|
||||
Counter:171 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Drawer Same-Column L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||
Level-3 cache with intervention
|
||||
.
|
||||
Counter:172 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Same-Column L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||
Level-3 cache without intervention
|
||||
.
|
||||
Counter:173 Name:L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Far-Column L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||
Level-4 cache
|
||||
.
|
||||
Counter:174 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Drawer Far-Column L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||
Level-3 cache with intervention
|
||||
.
|
||||
Counter:175 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Far-Column L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||
Level-3 cache without intervention
|
||||
.
|
||||
Counter:176 Name:L1I_ONNODE_MEM_SOURCED_WRITES
|
||||
Short-Description:L1I On-Node Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from On-Node memory
|
||||
.
|
||||
Counter:177 Name:L1I_ONDRAWER_MEM_SOURCED_WRITES
|
||||
Short-Description:L1I On-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from On-Drawer memory
|
||||
.
|
||||
Counter:178 Name:L1I_OFFDRAWER_MEM_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from On-Drawer memory
|
||||
.
|
||||
Counter:179 Name:L1I_ONCHIP_MEM_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where
|
||||
the returned cache line was sourced from On-Chip memory
|
||||
.
|
||||
Counter:218 Name:TX_NC_TABORT
|
||||
Short-Description:Aborted transactions in non-constrained TX mode
|
||||
Description:
|
||||
A transaction abort has occurred in a non-constrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:219 Name:TX_C_TABORT_NO_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode not using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is not using any special logic to allow the transaction
|
||||
to complete
|
||||
.
|
||||
Counter:220 Name:TX_C_TABORT_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is using special logic to allow the transaction to
|
||||
complete
|
||||
.
|
||||
#
|
||||
# MT-diagnostic counter set
|
||||
# ---------------------------------------------------------------------
|
||||
Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE
|
||||
Short-Description:Cycle count with one thread active
|
||||
Description:
|
||||
Cycle count with one thread active
|
||||
.
|
||||
Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE
|
||||
Short-Description:Cycle count with two threads active
|
||||
Description:
|
||||
Cycle count with two threads active
|
||||
.
|
||||
@@ -1,357 +0,0 @@
|
||||
# Counter decriptions for the
|
||||
# IBM z14 extended counter and MT-diagnostic counter set
|
||||
#
|
||||
# Notes for transactional-execution mode symbolic names:
|
||||
# TX .. transactional-execution mode
|
||||
# NC .. nonconstrained
|
||||
# C .. constrained
|
||||
#
|
||||
# Undefined counters in the extended counter set:
|
||||
# 142
|
||||
# 158-161
|
||||
# 176-223
|
||||
# 227-231
|
||||
# 233-242
|
||||
# 246-255
|
||||
# Undefined counters in the MT-diagnostic counter set:
|
||||
# 450-495
|
||||
#
|
||||
#
|
||||
# Extended Counter Set
|
||||
# ---------------------------------------------------------------------
|
||||
Counter:128 Name:L1D_RO_EXCL_WRITES
|
||||
Short-Description:L1D Read-only Exclusive Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache where the line was
|
||||
originally in a Read-Only state in the cache but has been updated
|
||||
to be in the Exclusive state that allows stores to the cache line
|
||||
.
|
||||
Counter:129 Name:DTLB2_WRITES
|
||||
Short-Description:DTLB2 Writes
|
||||
Description:
|
||||
A translation has been written into The Translation Lookaside
|
||||
Buffer 2 (TLB2) and the request was made by the data cache
|
||||
.
|
||||
Counter:130 Name:DTLB2_MISSES
|
||||
Short-Description:DTLB2 Misses
|
||||
Description:
|
||||
A TLB2 miss is in progress for a request made by the data cache.
|
||||
Incremented by one for every TLB2 miss in progress for the Level-1
|
||||
Data cache on this cycle
|
||||
.
|
||||
Counter:131 Name:DTLB2_HPAGE_WRITES
|
||||
Short-Description:DTLB2 One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry was written into the Combined Region and Segment
|
||||
Table Entry array in the Level-2 TLB for a one-megabyte page or a
|
||||
Last Host Translation was done
|
||||
.
|
||||
Counter:132 Name:DTLB2_GPAGE_WRITES
|
||||
Short-Description:DTLB2 Two-Gigabyte Page Writes
|
||||
Description:
|
||||
A translation entry for a two-gigabyte page was written into the
|
||||
Level-2 TLB
|
||||
.
|
||||
Counter:133 Name:L1D_L2D_SOURCED_WRITES
|
||||
Short-Description:L1D L2D Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the
|
||||
returned cache line was sourced from the Level-2 Data cache
|
||||
.
|
||||
Counter:134 Name:ITLB2_WRITES
|
||||
Short-Description:ITLB2 Writes
|
||||
Description:
|
||||
A translation entry has been written into the Translation Lookaside
|
||||
Buffer 2 (TLB2) and the request was made by the instruction cache
|
||||
.
|
||||
Counter:135 Name:ITLB2_MISSES
|
||||
Short-Description:ITLB2 Misses
|
||||
Description:
|
||||
A TLB2 miss is in progress for a request made by the instruction cache.
|
||||
Incremented by one for every TLB2 miss in progress for the Level-1
|
||||
Instruction cache in a cycle
|
||||
.
|
||||
Counter:136 Name:L1I_L2I_SOURCED_WRITES
|
||||
Short-Description:L1I L2I Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from the Level-2 Instruction cache
|
||||
.
|
||||
Counter:137 Name:TLB2_PTE_WRITES
|
||||
Short-Description:TLB2 PTE Writes
|
||||
Description:
|
||||
A translation entry was written into the Page Table Entry array in the
|
||||
Level-2 TLB
|
||||
.
|
||||
Counter:138 Name:TLB2_CRSTE_WRITES
|
||||
Short-Description:TLB2 CRSTE Writes
|
||||
Description:
|
||||
Translation entries were written into the Combined Region and Segment
|
||||
Table Entry array and the Page Table Entry array in the Level-2 TLB
|
||||
.
|
||||
Counter:139 Name:TLB2_ENGINES_BUSY
|
||||
Short-Description:TLB2 Engines Busy
|
||||
Description:
|
||||
The number of Level-2 TLB translation engines busy in a cycle
|
||||
.
|
||||
Counter:140 Name:TX_C_TEND
|
||||
Short-Description:Completed TEND instructions in constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a constrained transactional-execution
|
||||
mode
|
||||
.
|
||||
Counter:141 Name:TX_NC_TEND
|
||||
Short-Description:Completed TEND instructions in non-constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a non-constrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:143 Name:L1C_TLB2_MISSES
|
||||
Short-Description:L1C TLB2 Misses
|
||||
Description:
|
||||
Increments by one for any cycle where a level-1 cache or level-2 TLB miss
|
||||
is in progress
|
||||
.
|
||||
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Chip Level-3 cache without intervention
|
||||
.
|
||||
Counter:145 Name:L1D_ONCHIP_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Chip memory
|
||||
.
|
||||
Counter:146 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Chip Level-3 cache with intervention
|
||||
.
|
||||
Counter:147 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Cluster L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Cluster Level-3 cache withountervention
|
||||
.
|
||||
Counter:148 Name:L1D_ONCLUSTER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1D On-Cluster Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Cluster memory
|
||||
.
|
||||
Counter:149 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Cluster L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On-Cluster Level-3 cache with intervention
|
||||
.
|
||||
Counter:150 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Cluster L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Cluster Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:151 Name:L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Cluster Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from Off-Cluster memory
|
||||
.
|
||||
Counter:152 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Cluster L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Cluster Level-3 cache with intervention
|
||||
.
|
||||
Counter:153 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:154 Name:L1D_OFFDRAWER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from Off-Drawer memory
|
||||
.
|
||||
Counter:155 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Drawer L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off-Drawer Level-3 cache with intervention
|
||||
.
|
||||
Counter:156 Name:L1D_ONDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1D On-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Drawer Level-4 cache
|
||||
.
|
||||
Counter:157 Name:L1D_OFFDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from Off-Drawer Level-4 cache
|
||||
.
|
||||
Counter:158 Name:L1D_ONCHIP_L3_SOURCED_WRITES_RO
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes read-only
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from On-Chip L3 but a read-only invalidate was
|
||||
done to remove other copies of the cache line
|
||||
.
|
||||
Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache ine was sourced from an On-Chip Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:163 Name:L1I_ONCHIP_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache ine was sourced from On-Chip memory
|
||||
.
|
||||
Counter:164 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache ine was sourced from an On-Chip Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:165 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Cluster L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On-Cluster Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:166 Name:L1I_ONCLUSTER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1I On-Cluster Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On-Cluster memory
|
||||
.
|
||||
Counter:167 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Cluster L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from On-Cluster Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:168 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Cluster L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off-Cluster Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:169 Name:L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Cluster Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from Off-Cluster memory
|
||||
.
|
||||
Counter:170 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Cluster L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off-Cluster Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:171 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off-Drawer Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:172 Name:L1I_OFFDRAWER_MEMORY_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from Off-Drawer memory
|
||||
.
|
||||
Counter:173 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Drawer L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off-Drawer Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:174 Name:L1I_ONDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1I On-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from On-Drawer Level-4 cache
|
||||
.
|
||||
Counter:175 Name:L1I_OFFDRAWER_L4_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Drawer L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from Off-Drawer Level-4 cache
|
||||
.
|
||||
Counter:224 Name:BCD_DFP_EXECUTION_SLOTS
|
||||
Short-Description:BCD DFP Execution Slots
|
||||
Description:
|
||||
Count of floating point execution slots used for finished Binary Coded
|
||||
Decimal to Decimal Floating Point conversions. Instructions: CDZT,
|
||||
CXZT, CZDT, CZXT
|
||||
.
|
||||
Counter:225 Name:VX_BCD_EXECUTION_SLOTS
|
||||
Short-Description:VX BCD Execution Slots
|
||||
Description:
|
||||
Count of floating point execution slots used for finished vector arithmetic
|
||||
Binary Coded Decimal instructions. Instructions: VAP, VSP, VMPVMSP, VDP,
|
||||
VSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ, VCVB, VCVBG, VCVDVCVDG
|
||||
.
|
||||
Counter:226 Name:DECIMAL_INSTRUCTIONS
|
||||
Short-Description:Decimal Instructions
|
||||
Description:
|
||||
Decimal instructions dispatched. Instructions: CVB, CVD, AP, CP, DP, ED,
|
||||
EDMK, MP, SRP, SP, ZAP
|
||||
.
|
||||
Counter:232 Name:LAST_HOST_TRANSLATIONS
|
||||
Short-Description:Last host translation done
|
||||
Description:
|
||||
Last Host Translation done
|
||||
.
|
||||
Counter:243 Name:TX_NC_TABORT
|
||||
Short-Description:Aborted transactions in non-constrained TX mode
|
||||
Description:
|
||||
A transaction abort has occurred in a non-constrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:244 Name:TX_C_TABORT_NO_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode not using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is not using any special logic to allow the transaction
|
||||
to complete
|
||||
.
|
||||
Counter:245 Name:TX_C_TABORT_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is using special logic to allow the transaction to
|
||||
complete
|
||||
.
|
||||
#
|
||||
# MT-diagnostic counter set
|
||||
# ---------------------------------------------------------------------
|
||||
Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE
|
||||
Short-Description:Cycle count with one thread active
|
||||
Description:
|
||||
Cycle count with one thread active
|
||||
.
|
||||
Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE
|
||||
Short-Description:Cycle count with two threads active
|
||||
Description:
|
||||
Cycle count with two threads active
|
||||
.
|
||||
@@ -1,146 +0,0 @@
|
||||
Counter: 128 Name:L1D_L2_SOURCED_WRITES
|
||||
Short-Description:L1D L2 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from the Level-2 cache
|
||||
.
|
||||
Counter: 129 Name:L1I_L2_SOURCED_WRITES
|
||||
Short-Description:L1I L2 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from the Level-2 cache
|
||||
.
|
||||
Counter: 130 Name:DTLB1_MISSES
|
||||
Short-Description:DTLB1 Misses
|
||||
Description:
|
||||
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||
a DTLB1 miss is in progress.
|
||||
.
|
||||
Counter: 131 Name:ITLB1_MISSES
|
||||
Short-Description:ITLB1 Misses
|
||||
Description:
|
||||
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||
cycle a ITLB1 miss is in progress.
|
||||
.
|
||||
Counter: 133 Name:L2C_STORES_SENT
|
||||
Short-Description:L2C Stores Sent
|
||||
Description:
|
||||
Incremented by one for every store sent to Level-2 cache
|
||||
.
|
||||
Counter: 134 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Book L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-3 cache
|
||||
.
|
||||
Counter: 135 Name:L1D_ONBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1D On-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from an On Book Level-4 cache
|
||||
.
|
||||
Counter: 136 Name:L1I_ONBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1I On-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from an On Book Level-4 cache
|
||||
.
|
||||
Counter: 137 Name:L1D_RO_EXCL_WRITES
|
||||
Short-Description:L1D Read-only Exclusive Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache where the line was originally
|
||||
in a Read-Only state in the cache but has been updated to be in the
|
||||
Exclusive state that allows stores to the cache line
|
||||
.
|
||||
Counter: 138 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-4 cache
|
||||
.
|
||||
Counter: 139 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-4 cache
|
||||
.
|
||||
Counter: 140 Name:DTLB1_HPAGE_WRITES
|
||||
Short-Description:DTLB1 One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer for a one-megabyte page
|
||||
.
|
||||
Counter: 141 Name:L1D_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1D Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache where the installed cache line
|
||||
was sourced from memory that is attached to the same book as the Data
|
||||
cache (Local Memory)
|
||||
.
|
||||
Counter: 142 Name:L1I_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1I Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache where the installed cache
|
||||
line was sourced from memory that is attached to the same book as the
|
||||
Instruction cache (Local Memory)
|
||||
.
|
||||
Counter: 143 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Book L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-3 cache
|
||||
.
|
||||
Counter: 144 Name:DTLB1_WRITES
|
||||
Short-Description:DTLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer
|
||||
.
|
||||
Counter: 145 Name:ITLB1_WRITES
|
||||
Short-Description:ITLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Instruction
|
||||
Translation Lookaside Buffer
|
||||
.
|
||||
Counter: 146 Name:TLB2_PTE_WRITES
|
||||
Short-Description:TLB2 PTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Page Table
|
||||
Entry arrays
|
||||
.
|
||||
Counter: 147 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||
Short-Description:TLB2 CRSTE One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays for a one-megabyte large page translation
|
||||
.
|
||||
Counter: 148 Name:TLB2_CRSTE_WRITES
|
||||
Short-Description:TLB2 CRSTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays
|
||||
.
|
||||
Counter: 150 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from an On Chip Level-3 cache
|
||||
.
|
||||
Counter: 152 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache directory where the returned
|
||||
cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||
.
|
||||
Counter: 153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from an On Chip Level-3 cache
|
||||
.
|
||||
Counter: 155 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 I-Cache directory where the returned
|
||||
cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||
.
|
||||
@@ -1,230 +0,0 @@
|
||||
Counter: 128 Name:DTLB1_MISSES
|
||||
Short-Description:DTLB1 Misses
|
||||
Description:
|
||||
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||
a DTLB1 miss is in progress.
|
||||
.
|
||||
Counter:129 Name:ITLB1_MISSES
|
||||
Short-Description:ITLB1 Misses
|
||||
Description:
|
||||
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||
cycle a ITLB1 miss is in progress.
|
||||
.
|
||||
Counter:130 Name:L1D_L2I_SOURCED_WRITES
|
||||
Short-Description:L1D L2I Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from the Level-2 Instruction cache
|
||||
.
|
||||
Counter:131 Name:L1I_L2I_SOURCED_WRITES
|
||||
Short-Description:L1I L2I Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from the Level-2 Instruction cache
|
||||
.
|
||||
Counter:132 Name:L1D_L2D_SOURCED_WRITES
|
||||
Short-Description:L1D L2D Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from the Level-2 Data cache
|
||||
.
|
||||
Counter:133 Name:DTLB1_WRITES
|
||||
Short-Description:DTLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer
|
||||
.
|
||||
Counter:135 Name:L1D_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1D Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache where the installed cache line
|
||||
was sourced from memory that is attached to the same book as the Data
|
||||
cache (Local Memory)
|
||||
.
|
||||
Counter:137 Name:L1I_LMEM_SOURCED_WRITES
|
||||
Short-Description:L1I Local Memory Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache where the installed cache
|
||||
line was sourced from memory that is attached to the same book as the
|
||||
Instruction cache (Local Memory)
|
||||
.
|
||||
Counter:138 Name:L1D_RO_EXCL_WRITES
|
||||
Short-Description:L1D Read-only Exclusive Writes
|
||||
Description:
|
||||
A directory write to the Level-1 D-Cache where the line was originally
|
||||
in a Read-Only state in the cache but has been updated to be in the
|
||||
Exclusive state that allows stores to the cache line
|
||||
.
|
||||
Counter:139 Name:DTLB1_HPAGE_WRITES
|
||||
Short-Description:DTLB1 One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Data Translation
|
||||
Lookaside Buffer for a one-megabyte page
|
||||
.
|
||||
Counter:140 Name:ITLB1_WRITES
|
||||
Short-Description:ITLB1 Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-1 Instruction
|
||||
Translation Lookaside Buffer
|
||||
.
|
||||
Counter:141 Name:TLB2_PTE_WRITES
|
||||
Short-Description:TLB2 PTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Page Table
|
||||
Entry arrays
|
||||
.
|
||||
Counter:142 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||
Short-Description:TLB2 CRSTE One-Megabyte Page Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays for a one-megabyte large page translation
|
||||
.
|
||||
Counter:143 Name:TLB2_CRSTE_WRITES
|
||||
Short-Description:TLB2 CRSTE Writes
|
||||
Description:
|
||||
A translation entry has been written to the Level-2 TLB Common Region
|
||||
Segment Table Entry arrays
|
||||
.
|
||||
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On Chip Level-3 cache without intervention
|
||||
.
|
||||
Counter:145 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off Chip/On Book Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:146 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Book L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-3 cache without intervention
|
||||
.
|
||||
Counter:147 Name:L1D_ONBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1D On-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an On Book Level-4 cache
|
||||
.
|
||||
Counter:148 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1D Off-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-4 cache
|
||||
.
|
||||
#
|
||||
# Notes for the transactional-execution mode notations:
|
||||
# TX .. transactional-execution mode
|
||||
# NC .. nonconstrained
|
||||
# C .. constrained
|
||||
#
|
||||
Counter:149 Name:TX_NC_TEND
|
||||
Short-Description:Completed TEND instructions in non-constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a nonconstrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:150 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from a On Chip Level-3 cache with intervention
|
||||
.
|
||||
Counter:151 Name:L1D_OFFCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off Chip/On Book Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
# XXX Remove SOURCED from L1D_OFFBOOK_L3_SOURCED_WRITES...
|
||||
Counter:152 Name:L1D_OFFBOOK_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1D Off-Book L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Data cache directory where the returned
|
||||
cache line was sourced from an Off Book Level-3 cache with intervention
|
||||
.
|
||||
Counter:153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On Chip Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:154 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Chip L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||
without intervention
|
||||
.
|
||||
Counter:155 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Book L3 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off Book Level-3 cache without
|
||||
intervention
|
||||
.
|
||||
Counter:156 Name:L1I_ONBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1I On-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On Book Level-4 cache
|
||||
.
|
||||
Counter:157 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
|
||||
Short-Description:L1I Off-Book L4 Sourced Writes
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off Book Level-4 cache
|
||||
.
|
||||
Counter:158 Name:TX_C_TEND
|
||||
Short-Description:Completed TEND instructions in constrained TX mode
|
||||
Description:
|
||||
A TEND instruction has completed in a constrained transactional-execution
|
||||
mode
|
||||
.
|
||||
Counter:159 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I On-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an On Chip Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:160 Name:L1I_OFFCHIP_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Chip L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||
with intervention
|
||||
.
|
||||
Counter:161 Name:L1I_OFFBOOK_L3_SOURCED_WRITES_IV
|
||||
Short-Description:L1I Off-Book L3 Sourced Writes with Intervention
|
||||
Description:
|
||||
A directory write to the Level-1 Instruction cache directory where the
|
||||
returned cache line was sourced from an Off Book Level-3 cache with
|
||||
intervention
|
||||
.
|
||||
Counter:177 Name:TX_NC_TABORT
|
||||
Short-Description:Aborted transactions in non-constrained TX mode
|
||||
Description:
|
||||
A transaction abort has occurred in a nonconstrained
|
||||
transactional-execution mode
|
||||
.
|
||||
Counter:178 Name:TX_C_TABORT_NO_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode not using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is not using any special logic to allow the transaction
|
||||
to complete
|
||||
.
|
||||
Counter:179 Name:TX_C_TABORT_SPECIAL
|
||||
Short-Description:Aborted transactions in constrained TX mode using special completion logic
|
||||
Description:
|
||||
A transaction abort has occurred in a constrained transactional-execution
|
||||
mode and the CPU is using special logic to allow the transaction to
|
||||
complete
|
||||
.
|
||||
@@ -1,34 +0,0 @@
|
||||
# CPU-measurement facilities
|
||||
#
|
||||
# Mapping of:
|
||||
# 1. CPU-MF counter first/second version numbers to "generic" counter
|
||||
# definitions
|
||||
# 2. IBM z Systems hardware to respective extended counter set definitions
|
||||
#
|
||||
#
|
||||
{
|
||||
# Definition # File name
|
||||
|
||||
# CFVN
|
||||
'cfvn-1' => 'cpum-cf-cfvn-1.ctr',
|
||||
'cfvn-3' => 'cpum-cf-cfvn-3.ctr',
|
||||
|
||||
# CSVN
|
||||
'csvn-12345' => 'cpum-cf-csvn-12345.ctr',
|
||||
'csvn-6' => 'cpum-cf-csvn-6.ctr',
|
||||
|
||||
# Extended counters
|
||||
2097 => 'cpum-cf-extended-z10.ctr',
|
||||
2098 => 'cpum-cf-extended-z10.ctr',
|
||||
2817 => 'cpum-cf-extended-z196.ctr',
|
||||
2818 => 'cpum-cf-extended-z196.ctr',
|
||||
2827 => 'cpum-cf-extended-zEC12.ctr',
|
||||
2828 => 'cpum-cf-extended-zEC12.ctr',
|
||||
2964 => 'cpum-cf-extended-z13.ctr',
|
||||
2965 => 'cpum-cf-extended-z13.ctr',
|
||||
3906 => 'cpum-cf-extended-z14.ctr',
|
||||
3907 => 'cpum-cf-extended-z14.ctr',
|
||||
# Identical with z14
|
||||
8561 => 'cpum-cf-extended-z14.ctr',
|
||||
8562 => 'cpum-cf-extended-z14.ctr',
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
# Perf raw event counter definitions for the
|
||||
# CPU-measurment sampling facility
|
||||
#
|
||||
# Basic-sampling mode
|
||||
Counter: 0xB0000 Name:SF_CYCLES_BASIC
|
||||
Short-Description:Sample CPU Cycles Using Basic-sampling Mode
|
||||
Description:
|
||||
Sample CPU cycles using basic-sampling mode
|
||||
.
|
||||
# Diagnostic-sampling mode (includes basic-sampling)
|
||||
Counter: 0xBD000 Name:SF_CYCLES_BASIC_DIAG
|
||||
Short-Description:Sample CPU Cycle Using Diagnostic-sampling Mode (not for ordinary use)
|
||||
Description:
|
||||
Sample CPU cycle using diagnostic-sampling mode (not for ordinary use)
|
||||
.
|
||||
23
cpumf/defines.h
Normal file
23
cpumf/defines.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Defines for CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DEFINES_H
|
||||
#define DEFINES_H
|
||||
|
||||
#define PERF_SFB_SIZE "/sys/module/kernel/parameters/cpum_sfb_size"
|
||||
#define PERF_PATH "/sys/bus/event_source/devices/"
|
||||
#define PERF_SF "cpum_sf"
|
||||
#define PERF_CF "cpum_cf"
|
||||
|
||||
static inline void linux_error(const char *message)
|
||||
{
|
||||
fprintf(stderr, "Error: %s: %s\n", message, strerror(errno));
|
||||
}
|
||||
|
||||
#endif
|
||||
3120
cpumf/lscpumf.c
Normal file
3120
cpumf/lscpumf.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ chcpumf \- manage the CPU-measurement facilities support
|
||||
.IR num_sdb ]
|
||||
.RB [ \-x | \-\-max
|
||||
.IR num_sdb ]
|
||||
.RB [ \-V | \-\-verbose ]
|
||||
.br
|
||||
.B chcpumf
|
||||
.BR \-h | \-\-help
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
\" lscpumf.1
|
||||
.\"
|
||||
.\"
|
||||
.\" Copyright IBM Corp. 2014, 2017
|
||||
.\" Copyright IBM Corp. 2014, 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.
|
||||
.\" ----------------------------------------------------------------------
|
||||
.TH lscpumf "1" "February 2014" "s390-tools" "CPU-MF management programs"
|
||||
.TH lscpumf "1" "May 2020" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.ds c \fBlscpumf\fP
|
||||
.
|
||||
@@ -19,9 +19,11 @@ lscpumf \- display information about CPU-measurement facilities
|
||||
.RB [ \-i | \-\-info ]
|
||||
.br
|
||||
.B lscpumf
|
||||
.RB [ \-c | \-\-list\-counters ]
|
||||
.RB [ \-C | \-\-list\-all\-counters ]
|
||||
.RB [ \-s | \-\-list\-sampling\-events ]
|
||||
.RB \-c | \-\-list\-counters | \-C | \-\-list\-all\-counters
|
||||
.RB [ \-n ]
|
||||
.br
|
||||
.B lscpumf
|
||||
.RB \-s | \-\-list\-sampling\-events
|
||||
.br
|
||||
.B lscpumf
|
||||
.BR \-h | \-\-help
|
||||
@@ -46,12 +48,25 @@ facilities.
|
||||
Lists counters that are provided by the CPU-measurement facility, omitting
|
||||
counters for which the LPAR is not authorized. For counter measurements with
|
||||
the perf program, the raw event identifier is displayed.
|
||||
For Linux version 5.5 and later, the raw event identifier is
|
||||
displayed as <type>:<number>, where type is an integer that the kernel
|
||||
assignes to the CPU Measurement counter facility device driver.
|
||||
For earlier Linux versions the raw event identifier is displayed as r<number>.
|
||||
.
|
||||
.TP
|
||||
.BR \-C ", " \-\-list\-all\-counters
|
||||
Lists all counters that are provided by the CPU-measurement counter facility,
|
||||
regardless of LPAR authorization. To list only those counters for which the
|
||||
LPAR is authorized, use the -c option.
|
||||
For linux version 5.5 and later the raw event identifier is
|
||||
displayed as <type>:<number>, where type is the number the CPU Measurement
|
||||
counter facility device driver was assigned to by the kernel.
|
||||
For earlier linux versions the raw event identifier is displayed as r<number>.
|
||||
.
|
||||
.TP
|
||||
.BR \-n ", " \-\-name
|
||||
.
|
||||
Display the counter name together with the raw event identifier.
|
||||
.
|
||||
.TP
|
||||
.BR \-s ", " \-\-list\-sampling\-events
|
||||
|
||||
@@ -152,7 +152,6 @@ struct symbol_names {
|
||||
};
|
||||
|
||||
extern int foreground;
|
||||
extern int debug;
|
||||
extern char *configfile;
|
||||
extern int debug; /* is verbose specified? */
|
||||
extern int memory;
|
||||
|
||||
@@ -122,6 +122,8 @@ Format the first two tracks and write label and partition information. Only use
|
||||
this option 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).
|
||||
.br
|
||||
For thin-provisioned DASD ESE volumes this is the default mode.
|
||||
.IP expand
|
||||
Format all unformatted tracks at the end of the target DASD. This mode assumes
|
||||
that tracks at the beginning of the DASD volume have already been correctly
|
||||
@@ -136,6 +138,10 @@ using \fB-b\fR (\fB--blocksize\fR).
|
||||
Perform a complete format check on a DASD volume. A blocksize can be specified
|
||||
with \fB-b\fR (\fB--blocksize\fR).
|
||||
|
||||
.TP
|
||||
\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
|
||||
Number of cylinders to be processed in one formatting step.
|
||||
|
||||
@@ -53,6 +53,7 @@ static const struct util_prg prg = {
|
||||
/* Defines for options with no short command */
|
||||
#define OPT_CHECK 128
|
||||
#define OPT_NOZERO 129
|
||||
#define OPT_NODISCARD 130
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("FORMAT ACTIONS"),
|
||||
@@ -105,6 +106,11 @@ static struct util_opt opt_vec[] = {
|
||||
.desc = "Prevent storage server from modifying record 0",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "no-discard", no_argument, NULL, OPT_NODISCARD },
|
||||
.desc = "Do not discard space before formatting",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, 'y' },
|
||||
.desc = "Start formatting without further user-confirmation",
|
||||
@@ -921,6 +927,8 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname,
|
||||
printf("Drive Geometry: %d Cylinders * %d Heads = %d Tracks\n",
|
||||
cylinders, heads, (cylinders * heads));
|
||||
|
||||
printf("Device Type: %s Provisioned\n",
|
||||
info->ese ? "Thinly" : "Fully");
|
||||
printf("\nI am going to format the device ");
|
||||
printf("%s in the following way:\n", devname);
|
||||
printf(" Device number of device : 0x%x\n", info->dasd_info.devno);
|
||||
@@ -939,7 +947,10 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname,
|
||||
(p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no");
|
||||
printf(" Blocksize : %d\n", p->blksize);
|
||||
printf(" Mode : %s\n", mode_str[mode]);
|
||||
|
||||
if (info->ese) {
|
||||
printf(" Full Space Release : %s\n",
|
||||
(info->no_discard || mode == FULL) ? "no" : "yes");
|
||||
}
|
||||
if (info->testmode)
|
||||
printf("Test mode active, omitting ioctl.\n");
|
||||
}
|
||||
@@ -1234,6 +1245,26 @@ static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders,
|
||||
process_tracks(info, cylinders, heads, format_params);
|
||||
}
|
||||
|
||||
static void dasdfmt_release_space(dasdfmt_info_t *info)
|
||||
{
|
||||
format_data_t r = {
|
||||
.start_unit = 0,
|
||||
.stop_unit = 0,
|
||||
.intensity = DASD_FMT_INT_ESE_FULL,
|
||||
};
|
||||
int err = 0;
|
||||
|
||||
if (!info->ese || info->no_discard)
|
||||
return;
|
||||
|
||||
printf("Releasing space for the entire device...\n");
|
||||
err = dasd_release_space(dev_filename, &r);
|
||||
if (err) {
|
||||
ERRMSG_EXIT(EXIT_FAILURE, "%s: Could not release space (%s)\n",
|
||||
prog_name, strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
static void dasdfmt_prepare_and_format(dasdfmt_info_t *info,
|
||||
unsigned int cylinders,
|
||||
unsigned int heads, format_data_t *p)
|
||||
@@ -1329,6 +1360,8 @@ static void dasdfmt_quick_format(dasdfmt_info_t *info, unsigned int cylinders,
|
||||
|
||||
if (info->force) {
|
||||
printf("Skipping format check due to --force.\n");
|
||||
} else if (info->ese) {
|
||||
printf("Skipping format check due to thin-provisioned device.\n");
|
||||
} else {
|
||||
check_blocksize(info, p->blksize);
|
||||
|
||||
@@ -1438,6 +1471,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
|
||||
dasdfmt_prepare_and_format(info, cylinders, heads, p);
|
||||
break;
|
||||
case QUICK:
|
||||
dasdfmt_release_space(info);
|
||||
dasdfmt_quick_format(info, cylinders, heads, p);
|
||||
break;
|
||||
case EXPAND:
|
||||
@@ -1461,6 +1495,19 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
|
||||
}
|
||||
}
|
||||
|
||||
static void eval_format_mode(dasdfmt_info_t *info)
|
||||
{
|
||||
if (!info->force && info->mode_specified && info->ese && mode == EXPAND) {
|
||||
ERRMSG_EXIT(EXIT_FAILURE,
|
||||
"WARNING: The specified device is thin-provisioned\n"
|
||||
"Format mode 'expand' is not feasible.\n"
|
||||
"Use --mode=full or --mode=quick to perform a clean format\n");
|
||||
}
|
||||
|
||||
if (!info->mode_specified)
|
||||
mode = info->ese ? QUICK : FULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dasdfmt_info_t info = {
|
||||
@@ -1496,8 +1543,6 @@ int main(int argc, char *argv[])
|
||||
format_params.blksize = DEFAULT_BLOCKSIZE;
|
||||
format_params.intensity = DASD_FMT_INT_COMPAT;
|
||||
|
||||
mode = FULL;
|
||||
|
||||
/*************** parse parameters **********************/
|
||||
|
||||
while (1) {
|
||||
@@ -1601,6 +1646,10 @@ int main(int argc, char *argv[])
|
||||
"invalid. Consult the man page for "
|
||||
"more information.\n",
|
||||
prog_name, optarg);
|
||||
info.mode_specified = 1;
|
||||
break;
|
||||
case OPT_NODISCARD:
|
||||
info.no_discard = 1;
|
||||
break;
|
||||
case OPT_CHECK:
|
||||
info.check = 1;
|
||||
@@ -1645,6 +1694,9 @@ int main(int argc, char *argv[])
|
||||
"device information failed (%s).\n",
|
||||
prog_name, strerror(rc));
|
||||
|
||||
info.ese = dasd_sys_ese(dev_filename);
|
||||
eval_format_mode(&info);
|
||||
|
||||
/* Either let the user specify the blksize or get it from the kernel */
|
||||
if (!info.blksize_specified) {
|
||||
if (!(mode == FULL ||
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/****************************************************************************
|
||||
* SECTION: Definition needed for DASD-API (see dasd.h) *
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Represents possible format modes that can be specified when formatting
|
||||
* a DASD.
|
||||
@@ -45,57 +41,16 @@ static const char mode_str[3][10] = {
|
||||
"Full", "Quick", "Expand"
|
||||
};
|
||||
|
||||
/*
|
||||
* values to be used for format_data_t.intensity
|
||||
* 0/8: normal format
|
||||
* 1/9: also write record zero
|
||||
* 3/11: also write home address
|
||||
* 4/12: invalidate track
|
||||
*/
|
||||
#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
|
||||
#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
|
||||
#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
|
||||
#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
|
||||
#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
|
||||
|
||||
/*
|
||||
* values to be used in format_check_t for indicating
|
||||
* possible format errors
|
||||
*/
|
||||
#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
|
||||
#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
|
||||
#define DASD_FMT_ERR_BLKSIZE 3
|
||||
#define DASD_FMT_ERR_RECORD_ID 4
|
||||
#define DASD_FMT_ERR_KEY_LENGTH 5
|
||||
|
||||
/*
|
||||
* values to be used for dasd_information2_t.format
|
||||
* 0x00: NOT formatted
|
||||
* 0x01: Linux disc layout
|
||||
* 0x02: Common disc layout
|
||||
*/
|
||||
#define DASD_FORMAT_NONE 0
|
||||
#define DASD_FORMAT_LDL 1
|
||||
#define DASD_FORMAT_CDL 2
|
||||
|
||||
/****************************************************************************
|
||||
* SECTION: DASDFMT internal types *
|
||||
****************************************************************************/
|
||||
|
||||
#define DASD_PARTN_BITS 2
|
||||
#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
|
||||
|
||||
#define EXIT_MISUSE 1
|
||||
#define EXIT_BUSY 2
|
||||
#define LABEL_LENGTH 14
|
||||
#define VLABEL_CHARS 84
|
||||
#define LINE_LENGTH 80
|
||||
#define ERR_LENGTH 90
|
||||
|
||||
#define DEFAULT_BLOCKSIZE 4096
|
||||
/* requestsize - number of cylinders in one format step */
|
||||
#define DEFAULT_REQUESTSIZE 10
|
||||
#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1)
|
||||
|
||||
#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);}
|
||||
#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);}
|
||||
@@ -137,6 +92,9 @@ typedef struct dasdfmt_info {
|
||||
int force_host;
|
||||
int layout_specified;
|
||||
int check;
|
||||
int mode_specified;
|
||||
int ese;
|
||||
int no_discard;
|
||||
} dasdfmt_info_t;
|
||||
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
||||
mode = S_IFBLK | S_IRWXU;
|
||||
|
||||
/* Try several locations for the temporary device node. */
|
||||
for (path = 0; path < UTIL_ARRAY_SIZE(pathname); path++) {
|
||||
for (path = 0; path < ARRAY_SIZE(pathname); path++) {
|
||||
if (pathname[path] == NULL)
|
||||
continue;
|
||||
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||
|
||||
@@ -1,14 +1,39 @@
|
||||
include ../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -DSYSFS
|
||||
|
||||
all: dasdview
|
||||
|
||||
libs = $(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
ifneq (${HAVE_CURL},0)
|
||||
|
||||
check_dep:
|
||||
$(call check_dep, \
|
||||
"dasdview", \
|
||||
"curl/curl.h", \
|
||||
"curl-devel or libcurl-dev", \
|
||||
"HAVE_CURL=0")
|
||||
|
||||
BUILDTARGET = check_dep
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
CURL_CFLAGS = $(shell pkg-config --silence-errors --cflags libcurl)
|
||||
CURL_LDLIBS = $(shell pkg-config --silence-errors --libs libcurl)
|
||||
else
|
||||
CURL_CFLAGS = -I/usr/include/s390x-linux-gnu/curl
|
||||
CURL_LDLIBS = -lcurl
|
||||
endif # shell
|
||||
|
||||
endif # HAVE_CURL
|
||||
|
||||
BUILDTARGET += dasdview
|
||||
|
||||
ALL_CPPFLAGS += -DSYSFS
|
||||
ALL_CFLAGS += $(CURL_CFLAGS)
|
||||
LDLIBS += $(CURL_LDLIBS)
|
||||
|
||||
all: $(BUILDTARGET)
|
||||
|
||||
dasdview: dasdview.o $(libs)
|
||||
|
||||
install: all
|
||||
|
||||
@@ -160,19 +160,19 @@ dasdview_get_info(dasdview_info_t *info)
|
||||
if (err != EBADF)
|
||||
zt_error_print("dasdview: "
|
||||
"Could not retrieve geo information!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (dasd_get_blocksize(info->device, &info->blksize) != 0) {
|
||||
zt_error_print("dasdview: "
|
||||
"Could not retrieve blocksize information!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (dasd_get_info(info->device, &info->dasd_info) != 0) {
|
||||
zt_error_print("dasdview: "
|
||||
"Could not retrieve disk information!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
characteristics = (struct dasd_eckd_characteristics *)
|
||||
@@ -261,7 +261,7 @@ dasdview_parse_input(unsigned long long *p, dasdview_info_t *info, char *s)
|
||||
error:
|
||||
zt_error_print("dasdview: usage error\n"
|
||||
"%s is not a valid begin/size value!", s);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -404,7 +404,7 @@ dasdview_print_extended_info(dasdview_info_t *info)
|
||||
if (dasd_info->features == DASD_FEATURE_DEFAULT) {
|
||||
printf("default\n");
|
||||
} else {
|
||||
for (i = 0; i < UTIL_ARRAY_SIZE(flist); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(flist); i++)
|
||||
if (dasd_info->features & flist[i].mask)
|
||||
printf("%s ", flist[i].name);
|
||||
printf("\n");
|
||||
@@ -454,7 +454,7 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
||||
if (rc) {
|
||||
zt_error_print("error when reading label from device:"
|
||||
" rc=%d\n", rc);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
lzds_dasd_get_vlabel(info->dasd, &tmpvlabel);
|
||||
memcpy(&vlabel, tmpvlabel, sizeof(vlabel));
|
||||
@@ -600,7 +600,7 @@ dasdview_print_volser(dasdview_info_t *info)
|
||||
if (rc) {
|
||||
zt_error_print("error when reading label from device:"
|
||||
" rc=%d\n", rc);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
lzds_dasd_get_vlabel(info->dasd, &tmpvlabel);
|
||||
memcpy(&vlabel, tmpvlabel, sizeof(vlabel));
|
||||
@@ -644,7 +644,7 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: disk layout error\n"
|
||||
"%s is not formatted with the z/OS "
|
||||
"compatible disk layout!\n", info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
vtocblk = (u_int64_t)vtoc_get_cyl_from_cchhb(&vlabel.vtoc) *
|
||||
@@ -664,7 +664,7 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
||||
if ((vtocblk <= 0) || (vtocblk > maxblk)) {
|
||||
zt_error_print("dasdview: VTOC error\n"
|
||||
"Volume label VTOC pointer is not valid!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
vtoc_read_label(info->device, (vtocblk - 1) * info->blksize,
|
||||
@@ -676,7 +676,7 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
||||
/* format4 DSCB is invalid */
|
||||
zt_error_print("dasdview: VTOC error\n"
|
||||
"Format 4 DSCB is invalid!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
info->f4c++;
|
||||
@@ -724,19 +724,19 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
||||
if (info->f4c > 1) {
|
||||
zt_error_print("dasdview: VTOC error\n"
|
||||
"More than one FMT4 DSCB!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info->f5c > 1) {
|
||||
zt_error_print("dasdview: VTOC error\n"
|
||||
"More than one FMT5 DSCB!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info->f7c > 1) {
|
||||
zt_error_print("dasdview: VTOC error\n"
|
||||
"More than one FMT7 DSCB!\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,7 +975,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1,
|
||||
}
|
||||
if (rc) {
|
||||
zt_error_print("dasdview: Broken format 3 DSCB chain \n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL;
|
||||
|
||||
@@ -990,7 +990,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1,
|
||||
if (f3->DS3FMTID != 0xf3) {
|
||||
zt_error_print("dasdview: Broken format 3 DSCB"
|
||||
" chain \n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
for (j = 0; j < 4; ++j)
|
||||
dasdview_print_short_info_extent_raw(&f3->DS3EXTNT[j]);
|
||||
@@ -1002,7 +1002,7 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1,
|
||||
if (rc) {
|
||||
zt_error_print("dasdview: Broken format 3 DSCB"
|
||||
" chain \n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -1025,7 +1025,7 @@ static void dasdview_print_vtoc_info_raw(dasdview_info_t *info)
|
||||
rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
|
||||
if (rc) {
|
||||
zt_error_print("dasdview: could not allocate DSCB iterator \n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) {
|
||||
if (dscb->fmtid == 0xf1)
|
||||
@@ -1058,7 +1058,7 @@ static void dasdview_print_vtoc_info_raw(dasdview_info_t *info)
|
||||
rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
|
||||
if (rc) {
|
||||
zt_error_print("dasdview: could not allocate DSCB iterator \n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) {
|
||||
if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8)
|
||||
@@ -1739,23 +1739,23 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info)
|
||||
if (rc) {
|
||||
zt_error_print("error when reading label from device:"
|
||||
" rc=%d\n", rc);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rc = lzds_dasd_alloc_rawvtoc(info->dasd);
|
||||
if (rc == EINVAL) {
|
||||
zt_error_print("dasdview: Cannot read VTOC because disk does"
|
||||
" not contain valid VOL1 label.\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (rc) {
|
||||
zt_error_print("error when reading vtoc from device:"
|
||||
" rc=%d\n", rc);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rc = lzds_dasd_get_rawvtoc(info->dasd, &info->rawvtoc);
|
||||
if (rc || !info->rawvtoc) {
|
||||
zt_error_print("dasdview: libvtoc could not read vtoc\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info->vtoc_info || info->vtoc_all)
|
||||
@@ -1764,7 +1764,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info)
|
||||
rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
|
||||
if (rc) {
|
||||
zt_error_print("dasdview: could not allocate DSCB iterator\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
while (!lzds_dscbiterator_get_next_dscb(it, &record))
|
||||
dasdview_print_vtoc_dscb(info, record);
|
||||
@@ -1858,7 +1858,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: open error\n"
|
||||
"Unable to open device %s in read-only mode!\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
j = (info->begin / SEEK_STEP);
|
||||
@@ -1877,7 +1877,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: seek error\n"
|
||||
"Unable to seek in device %s!\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
b++;
|
||||
a += SEEK_STEP;
|
||||
@@ -1890,7 +1890,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: seek error\n"
|
||||
"Unable to seek in device %s!\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1926,7 +1926,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: read error\n"
|
||||
"Unable to read from device %s!\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info->format1)
|
||||
@@ -1945,7 +1945,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
||||
zt_error_print("dasdview: read error\n"
|
||||
"Unable to read from device %s!\n",
|
||||
info->device);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info->format1)
|
||||
@@ -2112,18 +2112,18 @@ static void dasdview_view_raw(dasdview_info_t *info)
|
||||
trackdata = memalign(4096, trckbuffsize * RAWTRACKSIZE);
|
||||
if (!trackdata) {
|
||||
zt_error_print("failed to allocate memory\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rc = lzds_dasd_alloc_dasdhandle(info->dasd, &dasdh);
|
||||
if (rc) {
|
||||
zt_error_print("failed to allocate memory\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rc = lzds_dasdhandle_open(dasdh);
|
||||
if (rc) {
|
||||
lzds_dasdhandle_free(dasdh);
|
||||
zt_error_print("failed to open device\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* residual is the number of tracks we still have to read */
|
||||
residual = tracks_to_read;
|
||||
@@ -2135,7 +2135,7 @@ static void dasdview_view_raw(dasdview_info_t *info)
|
||||
trckend, trackdata);
|
||||
if (rc) {
|
||||
perror("Error on read");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
data = trackdata;
|
||||
for (i = 0; i < trckcount; ++i) {
|
||||
@@ -2153,7 +2153,7 @@ static void dasdview_view_raw(dasdview_info_t *info)
|
||||
lzds_dasdhandle_free(dasdh);
|
||||
if (rc < 0) {
|
||||
perror("Error on closing file");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2253,7 +2253,7 @@ int main(int argc, char *argv[])
|
||||
zt_error_print("dasdview: usage error\n"
|
||||
"%s is no valid argument for"
|
||||
" option -t/--vtoc\n", optarg);
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
info.vtoc = 1;
|
||||
info.action_specified = 1;
|
||||
@@ -2301,13 +2301,13 @@ int main(int argc, char *argv[])
|
||||
rc = lzds_zdsroot_alloc(&info.zdsroot);
|
||||
if (rc) {
|
||||
zt_error_print("Could not allocate index\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rc = lzds_zdsroot_add_device(info.zdsroot, info.device,
|
||||
&info.dasd);
|
||||
if (rc) {
|
||||
zt_error_print("Could not add device to index\n");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2328,7 +2328,7 @@ int main(int argc, char *argv[])
|
||||
if (info.begin > max) {
|
||||
zt_error_print("dasdview: usage error\n"
|
||||
"'begin' value is not within disk range!");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info.size_specified)
|
||||
@@ -2343,7 +2343,7 @@ int main(int argc, char *argv[])
|
||||
zt_error_print("dasdview: usage error\n"
|
||||
"'begin' + 'size' is not within "
|
||||
"disk range!");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((info.begin_specified || info.size_specified) &&
|
||||
@@ -2355,7 +2355,7 @@ int main(int argc, char *argv[])
|
||||
zt_error_print("dasdview: usage error\n"
|
||||
"Options -1 or -2 make only sense with "
|
||||
"options -b or -s!");
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* do the output */
|
||||
|
||||
@@ -225,7 +225,7 @@ function CheckDeviceString() {
|
||||
fi
|
||||
}
|
||||
|
||||
setup_device()
|
||||
setup_ccw_device()
|
||||
{
|
||||
DEV="$(CheckDeviceString $DEVICE)"
|
||||
if [ "$DEV" != "" ]; then
|
||||
@@ -235,27 +235,37 @@ setup_device()
|
||||
pr_error "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
if [ $2 == "fcp" ]; then
|
||||
echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid WWPN '$WWPN'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid LUN '$LUN'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
setup_fcp_device()
|
||||
{
|
||||
DEV="$(CheckDeviceString $DEVICE)"
|
||||
if [ "$DEV" != "" ]; then
|
||||
echo $DEV > $1/$2/device
|
||||
else
|
||||
RETVAL=1
|
||||
pr_error "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid WWPN '$WWPN'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid LUN '$LUN'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -264,6 +274,30 @@ setup_nss_device()
|
||||
echo $NSS_NAME > $1/nss/name || RETVAL=1
|
||||
}
|
||||
|
||||
setup_nvme_device()
|
||||
{
|
||||
echo $FID > $1/nvme/fid 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid FID '$FID'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $NSID > $1/nvme/nsid 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid NSID '$NSID'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BOOTPROG > $1/nvme/bootprog 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
echo $BR_LBA > $1/nvme/br_lba 2>/dev/null || RETVAL=1
|
||||
if [ $RETVAL -eq 1 ]; then
|
||||
pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
setup_reipl()
|
||||
{
|
||||
if [ "$REIPL_TYPE" == "" ]; then
|
||||
@@ -271,15 +305,25 @@ setup_reipl()
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$REIPL_TYPE" == "ccw" ] || [ "$REIPL_TYPE" == "fcp" ]; then
|
||||
setup_device $REIPL_CONFIG_DIR $REIPL_TYPE
|
||||
elif [ "$REIPL_TYPE" == "nss" ]; then
|
||||
setup_nss_device $REIPL_CONFIG_DIR
|
||||
else
|
||||
pr_error "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG
|
||||
RETVAL=1
|
||||
return
|
||||
fi
|
||||
case "$REIPL_TYPE" in
|
||||
ccw)
|
||||
setup_ccw_device $REIPL_CONFIG_DIR $REIPL_TYPE
|
||||
;;
|
||||
fcp)
|
||||
setup_fcp_device $REIPL_CONFIG_DIR $REIPL_TYPE
|
||||
;;
|
||||
nvme)
|
||||
setup_nvme_device $REIPL_CONFIG_DIR
|
||||
;;
|
||||
nss)
|
||||
setup_nss_device $REIPL_CONFIG_DIR
|
||||
;;
|
||||
*)
|
||||
pr_error "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG
|
||||
RETVAL=1
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
echo $REIPL_TYPE > $REIPL_CONFIG_DIR/reipl_type || RETVAL=1
|
||||
|
||||
@@ -292,13 +336,24 @@ setup_reipl()
|
||||
|
||||
setup_dump()
|
||||
{
|
||||
if [ "$DUMP_TYPE" == "ccw" ] || [ "$DUMP_TYPE" == "fcp" ]; then
|
||||
setup_device $DUMP_CONFIG_DIR $DUMP_TYPE
|
||||
elif [ "$DUMP_TYPE" != "none" ]; then
|
||||
pr_error "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG
|
||||
RETVAL=1
|
||||
return
|
||||
fi
|
||||
case "$DUMP_TYPE" in
|
||||
ccw)
|
||||
setup_ccw_device $DUMP_CONFIG_DIR $DUMP_TYPE
|
||||
;;
|
||||
fcp)
|
||||
setup_fcp_device $DUMP_CONFIG_DIR $DUMP_TYPE
|
||||
;;
|
||||
nvme)
|
||||
setup_nvme_device $DUMP_CONFIG_DIR
|
||||
;;
|
||||
none)
|
||||
;;
|
||||
*)
|
||||
pr_error "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG
|
||||
RETVAL=1
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
echo $DUMP_TYPE > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
|
||||
|
||||
@@ -358,6 +413,18 @@ print_ccw_device()
|
||||
pr_info "device..: $DEVICE"
|
||||
}
|
||||
|
||||
print_nvme_device()
|
||||
{
|
||||
FID=$(cat $1/nvme/fid) || RETVAL=1
|
||||
pr_info "fid.....: $FID"
|
||||
NSID=$(cat $1/nvme/nsid) || RETVAL=1
|
||||
pr_info "nsid....: $NSID"
|
||||
BOOTPROG=$(cat $1/nvme/bootprog) || RETVAL=1
|
||||
pr_info "bootprog: $BOOTPROG"
|
||||
BR_LBA=$(cat $1/nvme/br_lba) || RETVAL=1
|
||||
pr_info "br_lba..: $BR_LBA"
|
||||
}
|
||||
|
||||
print_nss_name()
|
||||
{
|
||||
NAME=$(cat $1/nss/device) || RETVAL=1
|
||||
@@ -367,35 +434,52 @@ print_nss_name()
|
||||
status_dump()
|
||||
{
|
||||
CONF_DUMP_TYPE=$(cat $DUMP_CONFIG_DIR/dump_type) || RETVAL=1
|
||||
if [ "$CONF_DUMP_TYPE" == "none" ]; then
|
||||
pr_info "type....: no dump device configured"
|
||||
elif [ "$CONF_DUMP_TYPE" == "ccw" ]; then
|
||||
pr_info "type....: ccw"
|
||||
print_ccw_device $DUMP_CONFIG_DIR
|
||||
verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device)
|
||||
elif [ "$CONF_DUMP_TYPE" == "fcp" ]; then
|
||||
pr_info "type....: fcp"
|
||||
print_fcp_device $DUMP_CONFIG_DIR
|
||||
else
|
||||
pr_error "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!"
|
||||
pr_error " Please check if you have the latest dumpconf package!"
|
||||
fi
|
||||
case "$CONF_DUMP_TYPE" in
|
||||
none)
|
||||
pr_info "type....: no dump device configured"
|
||||
;;
|
||||
ccw)
|
||||
pr_info "type....: ccw"
|
||||
print_ccw_device $DUMP_CONFIG_DIR
|
||||
verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device)
|
||||
;;
|
||||
fcp)
|
||||
pr_info "type....: fcp"
|
||||
print_fcp_device $DUMP_CONFIG_DIR
|
||||
;;
|
||||
nvme)
|
||||
pr_info "type....: nvme"
|
||||
print_nvme_device $DUMP_CONFIG_DIR
|
||||
;;
|
||||
*)
|
||||
pr_error "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!"
|
||||
pr_error " Please check if you have the latest dumpconf package!"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
status_reipl()
|
||||
{
|
||||
REIPL_TYPE=$(cat $REIPL_CONFIG_DIR/reipl_type) || RETVAL=1
|
||||
pr_info "type....: $REIPL_TYPE"
|
||||
if [ "$REIPL_TYPE" == "ccw" ]; then
|
||||
print_ccw_device $REIPL_CONFIG_DIR
|
||||
elif [ "$REIPL_TYPE" == "fcp" ]; then
|
||||
print_fcp_device $REIPL_CONFIG_DIR
|
||||
elif [ "$REIPL_TYPE" == "nss" ]; then
|
||||
print_nss_name $REIPL_CONFIG_DIR
|
||||
else
|
||||
pr_error "ERROR: Unknown reipl device type '$REIPL_TYPE'!"
|
||||
pr_error " Please check if you have the latest dumpconf package!"
|
||||
fi
|
||||
case "$REIPL_TYPE" in
|
||||
ccw)
|
||||
print_ccw_device $REIPL_CONFIG_DIR
|
||||
;;
|
||||
fcp)
|
||||
print_fcp_device $REIPL_CONFIG_DIR
|
||||
;;
|
||||
nvme)
|
||||
print_nvme_device $REIPL_CONFIG_DIR
|
||||
;;
|
||||
nss)
|
||||
print_nss_name $REIPL_CONFIG_DIR
|
||||
;;
|
||||
*)
|
||||
pr_error "ERROR: Unknown reipl device type '$REIPL_TYPE'!"
|
||||
pr_error " Please check if you have the latest dumpconf package!"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
status_dump_reipl()
|
||||
|
||||
@@ -31,7 +31,7 @@ if [ -f $CONFIG_FILE ]; then
|
||||
. $CONFIG_FILE
|
||||
fi
|
||||
|
||||
UDEVSETTLE=/sbin/udevadm
|
||||
UDEVSETTLE=/usr/bin/udevadm
|
||||
if [ ! -e $UDEVSETTLE ]
|
||||
then
|
||||
UDEVSETTLE=/sbin/udevsettle
|
||||
|
||||
@@ -39,6 +39,16 @@
|
||||
# BOOTPROG=0
|
||||
# BR_LBA=0
|
||||
|
||||
#
|
||||
# Dump on nvme device (NVMe Disk)
|
||||
#
|
||||
# ON_PANIC=dump
|
||||
# DUMP_TYPE=nvme
|
||||
# FID=0x00000300
|
||||
# NSID=0x00000001
|
||||
# BOOTPROG=3
|
||||
# BR_LBA=0
|
||||
|
||||
#
|
||||
# Use VMDUMP
|
||||
#
|
||||
|
||||
@@ -75,7 +75,7 @@ static int get_part_name_by_dsname(char *dsname, char **name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < UTIL_ARRAY_SIZE(partition_types); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(partition_types); i++) {
|
||||
if (strstr(dsname, partition_types[i].dsname) != NULL) {
|
||||
*name = partition_types[i].name;
|
||||
return 0;
|
||||
@@ -97,7 +97,7 @@ static int get_part_type_by_dsname(char *dsname, int *type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < UTIL_ARRAY_SIZE(partition_types); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(partition_types); i++) {
|
||||
if (strstr(dsname, partition_types[i].dsname) != NULL) {
|
||||
*type = partition_types[i].type;
|
||||
return 0;
|
||||
@@ -112,7 +112,7 @@ static int get_part_type_by_dsname(char *dsname, int *type)
|
||||
*/
|
||||
static int get_part_dsname_by_type(unsigned int type, char **dsname)
|
||||
{
|
||||
if (type > UTIL_ARRAY_SIZE(partition_types))
|
||||
if (type > ARRAY_SIZE(partition_types))
|
||||
return 1;
|
||||
|
||||
if (type == 0)
|
||||
@@ -1205,7 +1205,7 @@ static void fdasd_list_known_partitions(void)
|
||||
unsigned int i;
|
||||
|
||||
for (i = VALID_PARTITION_OFFSET;
|
||||
i < UTIL_ARRAY_SIZE(partition_types); i++) {
|
||||
i < ARRAY_SIZE(partition_types); i++) {
|
||||
printf("%3d %s\n",
|
||||
partition_types[i].type, partition_types[i].name);
|
||||
}
|
||||
@@ -1622,7 +1622,7 @@ static void fdasd_change_part_type(fdasd_anchor_t *anc)
|
||||
|
||||
part_type = 0;
|
||||
while (part_type < 1 ||
|
||||
part_type > UTIL_ARRAY_SIZE(partition_types) - 1) {
|
||||
part_type > ARRAY_SIZE(partition_types) - 1) {
|
||||
do {
|
||||
part_type = read_char("new partition type: ");
|
||||
} while (!isdigit(part_type));
|
||||
|
||||
5
genprotimg/.gitignore
vendored
Normal file
5
genprotimg/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
tags
|
||||
compile_commands.json
|
||||
src/.check-dep-genprotimg
|
||||
src/.detect-openssl.dep.c
|
||||
src/genprotimg
|
||||
27
genprotimg/Makefile
Normal file
27
genprotimg/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
# Common definitions
|
||||
include ../common.mak
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
PKGDATADIR := "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
TESTS :=
|
||||
SUBDIRS := boot src man
|
||||
RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
|
||||
all: all-recursive
|
||||
|
||||
install: all install-recursive
|
||||
$(INSTALL) -d -m 755 "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 boot/stage3a.bin "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 boot/stage3b_reloc.bin "$(PKGDATADIR)"
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(PKGDATADIR)"
|
||||
|
||||
clean: clean-recursive
|
||||
|
||||
$(RECURSIVE_TARGETS):
|
||||
@target=`echo $@ |sed s/-recursive//`; \
|
||||
for d in $(SUBDIRS); do \
|
||||
$(MAKE) -C $$d $$target; \
|
||||
done
|
||||
|
||||
.PHONY: all install clean $(RECURSIVE_TARGETS)
|
||||
85
genprotimg/README.md
Normal file
85
genprotimg/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# genprotimg
|
||||
|
||||
`genprotimg` takes a kernel, key files, optionally an initrd image,
|
||||
optionally a file containing the kernel command line parameters, and
|
||||
generates a single, bootable image file. The generated image file
|
||||
consists of a concatenation of a plain text boot loader, the encrypted
|
||||
components for kernel, initrd, kernel command line, and the
|
||||
integrity-protected PV header, containing the metadata necessary for
|
||||
running the guest in protected mode. See [Memory Layout](#memory-layout)
|
||||
for details about the internal structure of the created image.
|
||||
|
||||
It is possible to use the generated image as a kernel for zipl or for
|
||||
a direct kernel boot using QEMU.
|
||||
|
||||
## Getting started
|
||||
|
||||
If all dependencies are met a simple `make` call in the source tree
|
||||
should be enough for building `genprotimg`.
|
||||
|
||||
## Details
|
||||
|
||||
The main idea of `genprotimg` is:
|
||||
|
||||
1. read in all keys, IVs, and other information needed for the
|
||||
encryption of the components and the generation of the PV header
|
||||
2. add stub stage3a (so we can calculate the memory addresses)
|
||||
3. add components: prepare the components (alignment and encryption)
|
||||
and add them to the memory layout
|
||||
4. build and add stage3b: generate the stage3b and add it to the memory layout
|
||||
5. generate the PV header: generate the hashes (pld, ald, and tld) of
|
||||
the components and create the PV header and IPIB
|
||||
6. parameterize the stub stage3a: uses the IPIB and PV header
|
||||
7. write the final image to the specified output path
|
||||
|
||||
### Boot Loader
|
||||
|
||||
The boot loader consists of two parts:
|
||||
|
||||
1. stage3a boot loader (cleartext), this loader is responsible for the
|
||||
transition into the protected mode by doing diag308 subcode 8 and
|
||||
10 calls.
|
||||
2. stage3b boot loader (encrypted), this loader is very similar to the
|
||||
normal zipl stage3 boot loader. It will be loaded by the Ultravisor
|
||||
after the successful transition into protected mode. Like the zipl
|
||||
stage3 boot loader it moves the kernel and patches in the values
|
||||
for initrd and parmline.
|
||||
|
||||
The loaders have the following constraints:
|
||||
|
||||
1. It must be possible to place stage3a and stage3b at a location
|
||||
greater than 0x10000 because the zipl stage3 loader zeroes out
|
||||
everything at addresses lower than 0x10000 of the image.
|
||||
2. As the stage3 loader of zipl assumes that the passed kernel image
|
||||
looks like a normal kernel image, the zipl stage3 loader modifies the
|
||||
content at the memory area 0x10400 - 0x10800, therefore we leave this
|
||||
area unused in our stage3a loader.
|
||||
3. The default entry address used by the zipl stage3 loader is 0x10000
|
||||
so we add a simple branch to 0x11000 at 0x10000 so the zipl stage3
|
||||
loader can modify the area 0x10400 - 0x10800 without affecting the
|
||||
stage3a loader.
|
||||
|
||||
#### Detail about stage3b
|
||||
|
||||
The stage3b.bin is linked at address 0x9000, therefore it will not
|
||||
work at another address. The relocation support for the stage3b
|
||||
loader, so that it can be placed at addresses != 0x9000, is added in
|
||||
the loader with the name stage3b_reloc.bin. By default, if we're
|
||||
talking about stage3b we refer to stage3b_reloc.bin.
|
||||
|
||||
### Memory Layout
|
||||
|
||||
The memory layout of the bootable file looks like:
|
||||
|
||||
| Start | End | Use |
|
||||
|------------------------|------------|-----------------------------------------------------------------------|
|
||||
| 0 | 0x7 | Short PSW, starting instruction at 0x11000 |
|
||||
| 0x10000 | 0x10012 | Branch to 0x11000 |
|
||||
| 0x10013 | 0x10fff | Left intentionally unused |
|
||||
| 0x11000 | 0x12fff | Stage3a |
|
||||
| 0x13000 | 0x13fff | IPIB used as argument for the diag308 call |
|
||||
| 0x14000 | 0x1[45]fff | UV header used for the diag308 call (size can be either 1 or 2 pages) |
|
||||
| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel |
|
||||
| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel parameters |
|
||||
| NEXT_PAGE_ALIGNED_ADDR | | Encrypted initrd |
|
||||
| NEXT_PAGE_ALIGNED_ADDR | | Encrypted stage3b_reloc |
|
||||
4
genprotimg/boot/.gitignore
vendored
Normal file
4
genprotimg/boot/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.elf
|
||||
*.lds
|
||||
*.bin
|
||||
*.d
|
||||
97
genprotimg/boot/Makefile
Normal file
97
genprotimg/boot/Makefile
Normal file
@@ -0,0 +1,97 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
ZIPL_DIR := $(rootdir)/zipl
|
||||
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot
|
||||
|
||||
INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
|
||||
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
|
||||
|
||||
ALL_CFLAGS := $(NO_PIE_CFLAGS) -Os -g \
|
||||
$(INCLUDE_PARMS) \
|
||||
-DENABLE_SCLP_ASCII=1 \
|
||||
-DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
|
||||
-fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \
|
||||
-fno-delete-null-pointer-checks \
|
||||
-fexec-charset=IBM1047 -m64 -mpacked-stack \
|
||||
-mstack-size=4096 -mstack-guard=128 -msoft-float \
|
||||
-Wall -Wformat-security -Wextra -Werror
|
||||
|
||||
FILES := stage3a.bin stage3b.bin stage3b_reloc.bin
|
||||
|
||||
ZIPL_SRCS_C := libc.c ebcdic.c ebcdic_conv.c sclp.c
|
||||
ZIPL_SRCS_ASM := entry.S
|
||||
|
||||
ZIPL_OBJS_C := $(ZIPL_SRCS_C:%.c=%.o)
|
||||
ZIPL_OBJS_ASM := $(ZIPL_SRCS_ASM:%.S=%.o)
|
||||
ZIPL_OBJS := $(ZIPL_OBJS_C) $(ZIPL_OBJS_ASM)
|
||||
|
||||
|
||||
all: $(FILES)
|
||||
|
||||
# Prevent make from using some default rules...
|
||||
%: %.S
|
||||
|
||||
%.o: %.S Makefile
|
||||
$(CC) $(ALL_CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.c Makefile
|
||||
$(CC) $(ALL_CFLAGS) -c -o $@ $<
|
||||
|
||||
|
||||
# Dependencies for the .lds generation
|
||||
sources_lds_S = $(wildcard *.lds.S)
|
||||
dependencies_lds_S = $(sources_lds_S:%.lds.S=.%.lds.d)
|
||||
# Include all ".lds.d" dependency files for all make targets except for "clean"
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(dependencies_lds_S)
|
||||
endif
|
||||
|
||||
%.lds: %.lds.S Makefile
|
||||
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $<
|
||||
|
||||
# Special rules for zipl object files
|
||||
$(ZIPL_OBJS_C): %.o : $(ZIPL_BOOT_DIR)/%.c
|
||||
$(CC) $(ALL_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(ZIPL_OBJS_ASM): %.o : $(ZIPL_BOOT_DIR)/%.S
|
||||
$(CC) $(ALL_CFLAGS) -c -o $@ $<
|
||||
|
||||
dependencies_zipl_c := $(ZIPL_SRCS_C:%.c=.%.o.d)
|
||||
|
||||
$(dependencies_zipl_c): .%.o.d : $(ZIPL_BOOT_DIR)/%.c
|
||||
$(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(dependencies_zipl_c)
|
||||
endif
|
||||
|
||||
stage3b_reloc.o: stage3b.bin
|
||||
|
||||
stage3a.elf: head.o stage3a_init.o stage3a.o stage3a.lds $(ZIPL_OBJS)
|
||||
stage3b.elf: head.o stage3b.o stage3b.lds $(ZIPL_OBJS)
|
||||
stage3b_reloc.elf:
|
||||
|
||||
%.elf: %.o
|
||||
case $* in \
|
||||
stage3a) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
||||
stage3b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
||||
stage3b_reloc) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||
esac; \
|
||||
$(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@
|
||||
@chmod a-x $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary \
|
||||
--only-section=.text* \
|
||||
--only-section=.ex_table* \
|
||||
--only-section=.fixup* \
|
||||
--only-section=.data* \
|
||||
--only-section=.rodata* \
|
||||
$< $@
|
||||
@chmod a-x $@
|
||||
|
||||
clean:
|
||||
rm -f *.o *.elf *.bin *.map .*.d *.lds
|
||||
|
||||
.PHONY: all clean
|
||||
25
genprotimg/boot/common_memory_layout.h
Normal file
25
genprotimg/boot/common_memory_layout.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Common memory layout for stage3a and stage3b bootloader.
|
||||
*
|
||||
* 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 COMMON_MEMORY_LAYOUT_H
|
||||
#define COMMON_MEMORY_LAYOUT_H
|
||||
|
||||
#include "boot/loaders_layout.h"
|
||||
|
||||
#define STACK_ADDRESS STAGE3_STACK_ADDRESS
|
||||
#define STACK_SIZE STAGE3_STACK_SIZE
|
||||
|
||||
#define HEAP_ADDRESS STAGE3_HEAP_ADDRESS
|
||||
#define HEAP_SIZE STAGE3_HEAP_SIZE
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* COMMON_MEMORY_LAYOUT_H */
|
||||
29
genprotimg/boot/head.S
Normal file
29
genprotimg/boot/head.S
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Entry code for stage 3a boot loader
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include "common_memory_layout.h"
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "boot/sigp.h"
|
||||
|
||||
.section .text.start
|
||||
.globl _start
|
||||
_start:
|
||||
/* Might be called after a diag308 so better set
|
||||
* architecture and addressing mode
|
||||
*/
|
||||
lhi %r1, 1
|
||||
sigp %r1, %r0, SIGP_SET_ARCHITECTURE
|
||||
sam64
|
||||
|
||||
/* Initialize stack */
|
||||
lgfi %r15, STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD
|
||||
brasl %r14, initialize
|
||||
.previous
|
||||
62
genprotimg/boot/stage3a.c
Normal file
62
genprotimg/boot/stage3a.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Main program for stage3a bootloader
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include "stage3a.h"
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "boot/s390.h"
|
||||
#include "boot/ipl.h"
|
||||
#include "sclp.h"
|
||||
#include "error.h"
|
||||
|
||||
|
||||
static volatile struct stage3a_args __section(".loader_parms") loader_parms;
|
||||
|
||||
void __noreturn start(void)
|
||||
{
|
||||
int rc;
|
||||
volatile struct stage3a_args *args = &loader_parms;
|
||||
/* calculate the IPIB memory address */
|
||||
struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs);
|
||||
|
||||
/* Calculate the PV header memory address and set it and its
|
||||
* size in the IPIB. This allows the PV header to be position
|
||||
* independent.
|
||||
*/
|
||||
ipib->pv.pv_hdr_addr = (uint64_t)args + args->hdr_offs;
|
||||
ipib->pv.pv_hdr_size = args->hdr_size;
|
||||
|
||||
/* set up ASCII and line-mode */
|
||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||
|
||||
/* test if Secure Execution Unpack facility is available */
|
||||
stfle(S390_lowcore.stfle_fac_list,
|
||||
ARRAY_SIZE(S390_lowcore.stfle_fac_list));
|
||||
rc = test_facility(UNPACK_FACILITY);
|
||||
if (rc == 0)
|
||||
panic(ENOPV, "Secure unpack facility is not available\n");
|
||||
|
||||
rc = diag308(DIAG308_SET_PV, ipib);
|
||||
if (rc != DIAG308_RC_OK)
|
||||
panic(EPV, "Protected boot setup has failed: 0x%x\n", rc);
|
||||
|
||||
rc = diag308(DIAG308_UNPACK_PV, 0x0);
|
||||
if (rc != DIAG308_RC_OK) {
|
||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||
panic(EPV, "Protected boot has failed: 0x%x\n", rc);
|
||||
}
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void panic_notify(unsigned long UNUSED(rc))
|
||||
{
|
||||
}
|
||||
34
genprotimg/boot/stage3a.h
Normal file
34
genprotimg/boot/stage3a.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Main program for stage3a bootloader.
|
||||
*
|
||||
* 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 STAGE3A_H
|
||||
#define STAGE3A_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "boot/loaders_layout.h"
|
||||
|
||||
#define STAGE3A_INIT_ENTRY IMAGE_ENTRY
|
||||
#define STAGE3A_ENTRY (STAGE3A_INIT_ENTRY + _AC(0x1000, UL))
|
||||
#define STAGE3A_LOAD_ADDRESS IMAGE_LOAD_ADDRESS
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Must not have any padding */
|
||||
struct stage3a_args {
|
||||
uint64_t hdr_offs;
|
||||
uint64_t hdr_size;
|
||||
uint64_t ipib_offs;
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct stage3a_args) == 3 * 8)
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* STAGE3A_H */
|
||||
103
genprotimg/boot/stage3a.lds.S
Normal file
103
genprotimg/boot/stage3a.lds.S
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Memory layout for stage 3a
|
||||
* ==========================
|
||||
*
|
||||
* General memory layout
|
||||
* ---------------------
|
||||
*
|
||||
* 0x00000 - 0x01fff Lowcore
|
||||
* 0x02000 - 0x05fff Memory allocation (heap)
|
||||
* 0x0f000 - 0x0ffff Stack
|
||||
* 0x10000 - 0x10012 Jump to the "actual" stage3a code
|
||||
* 0x11000 - 0x12fff Stage3a code + arguments (offsets and lengths to the
|
||||
* actual data: IPIB and UV header)
|
||||
*/
|
||||
|
||||
#include "stage3a.h"
|
||||
#include "common_memory_layout.h"
|
||||
|
||||
OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
|
||||
OUTPUT_ARCH(s390:64-bit)
|
||||
|
||||
ENTRY(_init)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0;
|
||||
|
||||
. = HEAP_ADDRESS;
|
||||
__heap_start = .;
|
||||
.heap : {
|
||||
. = . + HEAP_SIZE;
|
||||
ASSERT(__heap_stop - __heap_start == HEAP_SIZE,
|
||||
"Heap section doesn't conform to the described memory layout");
|
||||
}
|
||||
__heap_stop = .;
|
||||
|
||||
. = STACK_ADDRESS;
|
||||
__stack_start = .;
|
||||
.stack : {
|
||||
. = . + STACK_SIZE;
|
||||
ASSERT(__stack_end - __stack_start == STACK_SIZE,
|
||||
"Stack section doesn't conform to the described memory layout");
|
||||
}
|
||||
__stack_end = .;
|
||||
|
||||
. = STAGE3A_INIT_ENTRY;
|
||||
__text_init_start = .;
|
||||
.text : {
|
||||
stage3a_init.o(.text.init)
|
||||
__text_init_stop = ABSOLUTE(.);
|
||||
/* Text size of text_init must be smaller than 'PARMAREA - IMAGE_ENTRY',
|
||||
* otherwise the text data could be overwritten by the original zipl stage3
|
||||
* boot loader */
|
||||
ASSERT(__text_init_stop - __text_init_start < PARMAREA - IMAGE_ENTRY,
|
||||
"Text size must be smaller than 'PARMAREA - IMAGE_ENTRY'");
|
||||
. = 0x1000;
|
||||
ASSERT(ABSOLUTE(.) == STAGE3A_ENTRY,
|
||||
"Text section doesn't conform to the described memory layout");
|
||||
head.o(.text.start)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.ex_table ALIGN(16) : {
|
||||
__ex_table_start = .;
|
||||
*(.ex_table)
|
||||
__ex_table_stop = .;
|
||||
}
|
||||
|
||||
.bss ALIGN(16) : {
|
||||
__bss_start = .;
|
||||
*(.bss)
|
||||
__bss_stop = .;
|
||||
}
|
||||
|
||||
.rodata ALIGN(16) : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
|
||||
.data ALIGN(16) : {
|
||||
*(.data)
|
||||
. = ALIGN(16);
|
||||
/* The IPIB offset and the UV header offset and size will be
|
||||
* saved in 'loader_parms' */
|
||||
__loader_parms_start = .;
|
||||
KEEP(*(.loader_parms));
|
||||
__loader_parms_stop = .;
|
||||
ASSERT(__loader_parms_stop - __loader_parms_start == 3 * 8,
|
||||
"Data size must be equal to 'sizeof(struct stage3a_args)'");
|
||||
ASSERT(ABSOLUTE(.) < 0x13000, "Data section doesn't conform to the described memory layout");
|
||||
}
|
||||
|
||||
/* List this explicitly as otherwise .note.gnu.build-id will be
|
||||
* put at 0x0 */
|
||||
.notes : {
|
||||
*(.note.*)
|
||||
}
|
||||
|
||||
/* Sections to be discarded */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
}
|
||||
}
|
||||
26
genprotimg/boot/stage3a_init.S
Normal file
26
genprotimg/boot/stage3a_init.S
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Entry code for stage 3a boot loader
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "stage3a.h"
|
||||
#include "boot/sigp.h"
|
||||
|
||||
.section .text.init
|
||||
.globl _init
|
||||
_init:
|
||||
/* set architecture and switch to 64bit */
|
||||
lhi %r1, 1
|
||||
sigp %r1, %r0, SIGP_SET_ARCHITECTURE
|
||||
sam64
|
||||
/* The original stage3 boot loader will try to store the
|
||||
* kernel command line and the address and size of the
|
||||
* ramdisk. Simply ignore this by starting at 0x11000.
|
||||
*/
|
||||
lgfi %r1, STAGE3A_ENTRY
|
||||
br %r1
|
||||
.previous
|
||||
77
genprotimg/boot/stage3b.c
Normal file
77
genprotimg/boot/stage3b.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Main program for stage3b bootloader
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include "stage3b.h"
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "boot/s390.h"
|
||||
#include "boot/linux_layout.h"
|
||||
#include "boot/loaders_layout.h"
|
||||
#include "sclp.h"
|
||||
#include "error.h"
|
||||
|
||||
|
||||
static volatile struct stage3b_args __section(".loader_parms") loader_parms;
|
||||
|
||||
static inline void __noreturn load_psw(struct psw_t psw)
|
||||
{
|
||||
asm volatile("lpswe %0" : : "Q"(psw) : "cc");
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void __noreturn start(void)
|
||||
{
|
||||
volatile struct stage3b_args *args = &loader_parms;
|
||||
volatile struct memblob *kernel = &args->kernel;
|
||||
volatile struct memblob *cmdline = &args->cmdline;
|
||||
volatile struct memblob *initrd = &args->initrd;
|
||||
volatile struct psw_t psw = args->psw;
|
||||
|
||||
/* set up ASCII and line-mode */
|
||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||
|
||||
if (kernel->size < IMAGE_LOAD_ADDRESS)
|
||||
panic(EINTERNAL, "Invalid kernel\n");
|
||||
|
||||
if (cmdline->size > COMMAND_LINE_SIZE)
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel and cut the kernel header */
|
||||
memmove((void *)IMAGE_LOAD_ADDRESS,
|
||||
(void *)(kernel->src + IMAGE_LOAD_ADDRESS),
|
||||
kernel->size - IMAGE_LOAD_ADDRESS);
|
||||
|
||||
/* 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) {
|
||||
/* copy initrd start address and size into new kernel space */
|
||||
*(unsigned long long *)INITRD_START = initrd->src;
|
||||
*(unsigned long long *)INITRD_SIZE = initrd->size;
|
||||
}
|
||||
|
||||
/* disable ASCII and line-mode */
|
||||
sclp_setup(SCLP_DISABLE);
|
||||
|
||||
/* use lpswe instead of diag308 as a I/O subsystem reset is not
|
||||
* needed as this was already done by the diag308 subcode 10 call
|
||||
* in stage3a
|
||||
*/
|
||||
load_psw(psw);
|
||||
}
|
||||
|
||||
void panic_notify(unsigned long UNUSED(rc))
|
||||
{
|
||||
}
|
||||
42
genprotimg/boot/stage3b.h
Normal file
42
genprotimg/boot/stage3b.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Main program for stage3b bootloader
|
||||
*
|
||||
* 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 STAGE3B_H
|
||||
#define STAGE3B_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "boot/loaders_layout.h"
|
||||
|
||||
#define STAGE3B_ENTRY STAGE3_ENTRY
|
||||
#define STAGE3B_LOAD_ADDRESS STAGE3B_ENTRY
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
|
||||
/* Must not have any padding included */
|
||||
struct memblob {
|
||||
uint64_t src;
|
||||
uint64_t size;
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct memblob) == 2 * 8)
|
||||
|
||||
/* Must not have any padding included */
|
||||
struct stage3b_args {
|
||||
struct memblob kernel;
|
||||
struct memblob cmdline;
|
||||
struct memblob initrd;
|
||||
struct psw_t psw;
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct stage3b_args) == 3 * sizeof(struct memblob) + 16)
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* STAGE3B_H */
|
||||
87
genprotimg/boot/stage3b.lds.S
Normal file
87
genprotimg/boot/stage3b.lds.S
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Memory layout for stage 3b
|
||||
* ==========================
|
||||
*
|
||||
* General memory layout
|
||||
* ---------------------
|
||||
*
|
||||
* 0x00000 - 0x01fff Lowcore
|
||||
* 0x02000 - 0x05fff Memory allocation (heap)
|
||||
* 0x0a000 - 0x0efff Stage3b code
|
||||
* 0x0f000 - 0x0ffff Stack
|
||||
*/
|
||||
|
||||
#include "stage3b.h"
|
||||
#include "common_memory_layout.h"
|
||||
|
||||
OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
|
||||
OUTPUT_ARCH(s390:64-bit)
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0;
|
||||
|
||||
. = HEAP_ADDRESS;
|
||||
__heap_start = .;
|
||||
.heap : {
|
||||
. = . + HEAP_SIZE;
|
||||
ASSERT(__heap_stop - __heap_start == HEAP_SIZE,
|
||||
"Heap section doesn't conform to the described memory layout");
|
||||
}
|
||||
__heap_stop = .;
|
||||
|
||||
. = STAGE3B_ENTRY;
|
||||
.text : {
|
||||
head.o(.text.start)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.ex_table ALIGN(16) : {
|
||||
__ex_table_start = .;
|
||||
*(.ex_table)
|
||||
__ex_table_stop = .;
|
||||
}
|
||||
|
||||
.bss ALIGN(16) : {
|
||||
__bss_start = .;
|
||||
*(.bss)
|
||||
__bss_stop = .;
|
||||
}
|
||||
|
||||
.rodata ALIGN(16) : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
|
||||
.data ALIGN(16) : {
|
||||
*(.data)
|
||||
. = ALIGN(16);
|
||||
__loader_parms_start = .;
|
||||
KEEP(*(.loader_parms));
|
||||
__loader_parms_end = .;
|
||||
ASSERT(__loader_parms_end - __loader_parms_start == 3 * 16 + 16,
|
||||
"Data size must be equal to 'sizeof(struct stage3b_args)'");
|
||||
}
|
||||
|
||||
. = STACK_ADDRESS;
|
||||
__stack_start = .;
|
||||
.stack : {
|
||||
. = . + STACK_SIZE;
|
||||
ASSERT(__stack_end - __stack_start == STACK_SIZE,
|
||||
"Stack section doesn't conform to the described memory layout");
|
||||
}
|
||||
__stack_end = .;
|
||||
|
||||
/* List this explicitly as otherwise .note.gnu.build-id will be
|
||||
* put at 0x0 */
|
||||
.notes : {
|
||||
*(.note.*)
|
||||
}
|
||||
|
||||
/* Sections to be discarded */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
}
|
||||
}
|
||||
53
genprotimg/boot/stage3b_reloc.S
Normal file
53
genprotimg/boot/stage3b_reloc.S
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Relocator code for stage 3b boot loader
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "stage3b.h"
|
||||
#include "boot/sigp.h"
|
||||
|
||||
.macro MEMCPY dst,src,len
|
||||
lgr %r0, \dst
|
||||
lgr %r1, \len
|
||||
lgr %r2, \src
|
||||
lgr %r3, \len
|
||||
|
||||
20: mvcle %r0, %r2, 0
|
||||
jo 20b
|
||||
.endm
|
||||
|
||||
.org 0x0
|
||||
.section .text.start
|
||||
.globl stage3b_reloc_start
|
||||
stage3b_reloc_start:
|
||||
/* Might be called after a diag308 so better set
|
||||
* architecture and addressing mode
|
||||
*/
|
||||
lhi %r1, 1
|
||||
sigp %r1, %r0, SIGP_SET_ARCHITECTURE
|
||||
sam64
|
||||
|
||||
.copy_stage3b:
|
||||
/* Location of stage3b in memory */
|
||||
larl %r8, stage3b_start
|
||||
|
||||
/* Destination for stage3b */
|
||||
lgfi %r9, STAGE3B_LOAD_ADDRESS
|
||||
|
||||
/* Size of stage3b */
|
||||
lghi %r11, stage3b_end - stage3b_start
|
||||
|
||||
/* Copy the stage3b loader to address STAGE3B_LOAD_ADDRESS */
|
||||
MEMCPY %r9, %r8, %r11
|
||||
|
||||
/* Branch to STAGE3B_ENTRY */
|
||||
lgfi %r9, STAGE3B_ENTRY
|
||||
br %r9
|
||||
stage3b_start:
|
||||
.incbin "stage3b.bin"
|
||||
stage3b_end:
|
||||
.previous
|
||||
12
genprotimg/man/Makefile
Normal file
12
genprotimg/man/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
all:
|
||||
|
||||
install:
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c genprotimg.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
|
||||
.PHONY: all install clean
|
||||
97
genprotimg/man/genprotimg.8
Normal file
97
genprotimg/man/genprotimg.8
Normal file
@@ -0,0 +1,97 @@
|
||||
.\" Copyright 2020 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 GENPROTIMG 8 "March 2020" "s390-tools"
|
||||
.SH NAME
|
||||
genprotimg \- Create a protected virtualization image
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY
|
||||
.B genprotimg
|
||||
\fB\-k\fR \fIHOST_KEY_DOCUMENT\fR...
|
||||
\fB\-i\fR \fIVMLINUZ\fR
|
||||
[\fB\-r\fR \fIRAMDISK\fR]
|
||||
[\fB\-p\fR \fIPARMFILE\fR]
|
||||
\fB\-o\fR \fIOUTFILE\fR
|
||||
[\fIOPTION\fR]...
|
||||
.YS
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Use \fBgenprotimg\fR to generate a single bootable image file with
|
||||
encrypted and integrity-protected parts. The command requires a kernel
|
||||
image, a host-key document, and an output file name. Optionally,
|
||||
specify an initial RAM filesystem, and a file containing the kernel
|
||||
parameters. Should special circumstances require it, you can
|
||||
optionally specify your own keys for the encryption by using the
|
||||
experimental options. In the resulting image file, a plain text boot
|
||||
loader, the encrypted components for kernel, initial RAM disk, kernel
|
||||
parameters, and the encrypted and integrity-protected header are
|
||||
concatenated. The header contains metadata necessary for running the
|
||||
guest in protected mode.
|
||||
.PP
|
||||
Use this image file as a kernel image for zipl or for a direct kernel
|
||||
boot using QEMU.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Prints usage information, then exits.
|
||||
.TP
|
||||
\fB\-\-help-experimental\fR
|
||||
Prints experimental usage information, then exits.
|
||||
.TP
|
||||
\fB\-\-help-all\fR
|
||||
Prints all usage information, then exits.
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-verbose\fR
|
||||
Provides more detailed output.
|
||||
.TP
|
||||
\fB\-k\fR, \fB\-\-host-key-document\fR=\fI\,HOST_KEY_DOCUMENT\/\fR
|
||||
Specifies a host-key document. At least one is required. Specify this
|
||||
option multiple times to enable the image to run on more than one
|
||||
host.
|
||||
.TP
|
||||
\fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR
|
||||
Specifies the output file. Required.
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-image\fR=\fI\,VMLINUZ\/\fR
|
||||
Specifies the Linux kernel image file. Required.
|
||||
.TP
|
||||
\fB\-r\fR, \fB\-\-ramdisk\fR=\fI\,RAMDISK\/\fR
|
||||
Specifies the RAM disk image. Optional.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR
|
||||
Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional.
|
||||
.TP
|
||||
\fB\-\-no-verify\fR
|
||||
Do not require the host-key documents to be valid. For testing
|
||||
purposes, do not use for a production image. Optional.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Prints version information, then exits.
|
||||
|
||||
.SH EXAMPLE
|
||||
.PP
|
||||
Generate a protected virtualization image in
|
||||
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR,
|
||||
the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
||||
\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR:
|
||||
.PP
|
||||
.Vb 1
|
||||
.EX
|
||||
\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||
.EE
|
||||
.Ve
|
||||
.PP
|
||||
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
An ELF file cannot be used as a Linux kernel image.
|
||||
.IP "2." 4
|
||||
Remember to re-run \fBzipl\fR after updating a protected
|
||||
virtualization image.
|
||||
|
||||
.SH SEE ALSO
|
||||
\&\fBzipl\fR\|(5), \fBqemu\fR\|(1)
|
||||
275
genprotimg/samples/check_hostkeydoc
Executable file
275
genprotimg/samples/check_hostkeydoc
Executable file
@@ -0,0 +1,275 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# check_hostkeydoc - Verify an IBM Secure Execution host key document
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Sample invocation:
|
||||
#
|
||||
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
# 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)
|
||||
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 \
|
||||
$CRL_SERIAL_FILE
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Enhanced error checking for bash
|
||||
if [ -n "${BASH}" ]
|
||||
then
|
||||
set -o posix
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
fi
|
||||
set -e
|
||||
|
||||
# Usage
|
||||
usage()
|
||||
{
|
||||
cat <<-EOF
|
||||
Usage: `basename $1` host-key-doc signing-key-cert [-c CA-cert] [-r CRL]
|
||||
|
||||
Verify an IBM Secure Execution host key document against
|
||||
a signing key.
|
||||
|
||||
Note that in order to have the full trust chain verified
|
||||
it is necessary to provide the issueing CA's certificate.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_verify_chain()
|
||||
{
|
||||
# Verify certificate chain in case a CA certificate file/bundle
|
||||
# was specified on the command line.
|
||||
if [ $# = 1 ]
|
||||
then
|
||||
cat >&2 <<-EOF
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
No CA certificate specified! Skipping trust chain verification.
|
||||
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
|
||||
fi
|
||||
}
|
||||
|
||||
extract_pubkey()
|
||||
{
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
canonical_dn()
|
||||
{
|
||||
OBJTYPE=$1
|
||||
OBJ=$2
|
||||
DNTYPE=$3
|
||||
OUTPUT=$4
|
||||
|
||||
openssl $OBJTYPE -in $OBJ -$DNTYPE -noout -nameopt multiline \
|
||||
| sort | grep -v $DNTYPE= > $OUTPUT
|
||||
}
|
||||
|
||||
default_issuer()
|
||||
{
|
||||
cat <<-EOF
|
||||
commonName = International Business Machines Corporation
|
||||
countryName = US
|
||||
localityName = Poughkeepsie
|
||||
organizationalUnitName = IBM Z Host Key Signing Service
|
||||
organizationName = International Business Machines Corporation
|
||||
stateOrProvinceName = New York
|
||||
EOF
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
{
|
||||
default_issuer > $DEF_ISSUER_DN_FILE
|
||||
|
||||
if ! diff $ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
then
|
||||
echo Incorrect default issuer >&2 && exit 1
|
||||
fi
|
||||
|
||||
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
||||
then
|
||||
echo Issuer verification OK
|
||||
else
|
||||
echo Issuer verification failed >&2 && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cert_time()
|
||||
{
|
||||
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 -d "$DATE" +%s
|
||||
}
|
||||
|
||||
verify_dates()
|
||||
{
|
||||
START="$1"
|
||||
END="$2"
|
||||
MSG="${3:-Certificate}"
|
||||
NOW=$(date +%s)
|
||||
|
||||
if [ $START -le $NOW -a $NOW -le $END ]
|
||||
then
|
||||
echo "${MSG} dates are OK"
|
||||
else
|
||||
echo "${MSG} date verification failed" >&2 && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
crl_serials()
|
||||
{
|
||||
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)
|
||||
|
||||
grep -q $CERT_SERIAL $CRL_SERIAL_FILE
|
||||
}
|
||||
|
||||
check_file()
|
||||
{
|
||||
[ $# = 0 ] ||
|
||||
[ -e "$1" ] ||
|
||||
(echo "File '$1' not found" >&2 && exit 1)
|
||||
}
|
||||
|
||||
# check args
|
||||
CRL_FILE=
|
||||
CA_FILE=
|
||||
|
||||
args=$(getopt -qu "r:c:h" $*)
|
||||
if [ $? = 0 ]
|
||||
then
|
||||
set -- $args
|
||||
while [ $1 != "" ]
|
||||
do
|
||||
case $1 in
|
||||
-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
|
||||
|
||||
if [ $# -ne 2 ]
|
||||
then
|
||||
usage $0 >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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 trust chain
|
||||
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
|
||||
|
||||
# Verify the issuer
|
||||
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
|
||||
# Verify dates
|
||||
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
|
||||
|
||||
echo -n "CRL "
|
||||
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
|
||||
verify_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
|
||||
fi
|
||||
|
||||
# We made it
|
||||
echo All checks reqested for \'$HKD_FILE\' were successful
|
||||
101
genprotimg/src/Makefile
Normal file
101
genprotimg/src/Makefile
Normal file
@@ -0,0 +1,101 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
bin_PROGRAM = genprotimg
|
||||
|
||||
PKGDATADIR ?= "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
TOP_SRCDIR := $(SRC_DIR)/../
|
||||
ROOT_DIR = $(TOP_SRC_DIR)/../../
|
||||
ZIPL_DIR = $(ROOT_DIR)/zipl
|
||||
LOADER_DIR = $(TOP_SRCDIR)/boot
|
||||
|
||||
INCLUDE_PATHS = "$(SRC_DIR)" "$(TOP_SRCDIR)" "$(ROOTDIR)/include"
|
||||
INCLUDE_PARMS = $(addprefix -I,$(INCLUDE_PATHS))
|
||||
|
||||
WARNINGS := -Wall -Wextra -Wshadow \
|
||||
-Wcast-align -Wwrite-strings -Wmissing-prototypes \
|
||||
-Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline \
|
||||
-Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \
|
||||
-Wpointer-arith -Werror \
|
||||
$(NULL)
|
||||
|
||||
$(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \
|
||||
pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \
|
||||
pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \
|
||||
pv/pv_opt_item.c \
|
||||
$(NULL)
|
||||
$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
||||
|
||||
ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
|
||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \
|
||||
$(WARNINGS) \
|
||||
$(NULL)
|
||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
|
||||
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
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)
|
||||
else
|
||||
GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
|
||||
GLIB2_LIBS := -lglib-2.0
|
||||
LIBCRYPTO_CFLAGS :=
|
||||
LIBCRYPTO_LIBS := -lcrypto
|
||||
endif
|
||||
|
||||
BUILD_TARGETS := skip-$(bin_PROGRAM)
|
||||
INSTALL_TARGETS := skip-$(bin_PROGRAM)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
BUILD_TARGETS := $(bin_PROGRAM)
|
||||
INSTALL_TARGETS := install-$(bin_PROGRAM)
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
install: $(INSTALL_TARGETS)
|
||||
|
||||
$(bin_PROGRAM): $($(bin_PROGRAM)_OBJS)
|
||||
|
||||
skip-$(bin_PROGRAM):
|
||||
echo " SKIP $(bin_PROGRAM) due to unresolved dependencies"
|
||||
|
||||
install-$(bin_PROGRAM): $(bin_PROGRAM)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
$(INSTALL) -c $^ $(DESTDIR)$(USRBINDIR)
|
||||
|
||||
clean:
|
||||
$(RM) -f $($(bin_PROGRAM)_OBJS) $(bin_PROGRAM) .check-dep-$(bin_PROGRAM) .detect-openssl.dep.c
|
||||
|
||||
.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 < 0x10100000L" >> $@
|
||||
echo " #error openssl version 1.1.0 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.0", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
"-I.")
|
||||
touch $@
|
||||
35
genprotimg/src/common.h
Normal file
35
genprotimg/src/common.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#define GETTEXT_PACKAGE "genprotimg"
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
#include "boot/linux_layout.h"
|
||||
#include "boot/s390.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
static const gchar tool_name[] = "genprotimg";
|
||||
static const gchar copyright_notice[] = "Copyright IBM Corp. 2020";
|
||||
|
||||
/* default values */
|
||||
#define GENPROTIMG_STAGE3A_PATH (STRINGIFY(PKGDATADIR) "/stage3a.bin")
|
||||
#define GENPROTIMG_STAGE3B_PATH (STRINGIFY(PKGDATADIR) "/stage3b_reloc.bin")
|
||||
|
||||
#define DEFAULT_INITIAL_PSW_ADDR IMAGE_ENTRY
|
||||
#define DEFAULT_INITIAL_PSW_MASK (PSW_MASK_EA | PSW_MASK_BA)
|
||||
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
|
||||
# ifdef __clang__
|
||||
# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \
|
||||
DO_PRAGMA(clang diagnostic push) \
|
||||
DO_PRAGMA(clang diagnostic ignored "-Wunused-function") \
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) \
|
||||
DO_PRAGMA(clang diagnostic pop)
|
||||
# else
|
||||
# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
181
genprotimg/src/genprotimg.c
Normal file
181
genprotimg/src/genprotimg.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* genprotimg - build relocatable secure images
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "pv/pv_args.h"
|
||||
#include "pv/pv_image.h"
|
||||
|
||||
enum {
|
||||
LOG_LEVEL_CRITICAL = 0,
|
||||
LOG_LEVEL_INFO = 1,
|
||||
LOG_LEVEL_DEBUG = 2,
|
||||
};
|
||||
|
||||
static gint log_level = LOG_LEVEL_CRITICAL;
|
||||
static gchar *tmp_dir;
|
||||
|
||||
static void rmdir_recursive(gchar *dir_path, GError **err)
|
||||
{
|
||||
const gchar *file = NULL;
|
||||
g_autoptr(GDir) d = NULL;
|
||||
|
||||
if (!dir_path)
|
||||
return;
|
||||
|
||||
d = g_dir_open(dir_path, 0, err);
|
||||
if (!d) {
|
||||
g_set_error(err, G_FILE_ERROR,
|
||||
(gint)g_file_error_from_errno(errno),
|
||||
_("Failed to open directory '%s': %s"), dir_path,
|
||||
g_strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
while ((file = g_dir_read_name(d)) != NULL) {
|
||||
g_autofree gchar *file_path =
|
||||
g_build_filename(dir_path, file, NULL);
|
||||
/* ignore error */
|
||||
(void)g_unlink(file_path);
|
||||
}
|
||||
|
||||
if (g_rmdir(dir_path) != 0) {
|
||||
g_set_error(err, G_FILE_ERROR,
|
||||
(gint)g_file_error_from_errno(errno),
|
||||
_("Failed to remove directory '%s': %s"), dir_path,
|
||||
g_strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void sig_term_handler(int signal G_GNUC_UNUSED)
|
||||
{
|
||||
rmdir_recursive(tmp_dir, NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED,
|
||||
GLogLevelFlags level, const gchar *message,
|
||||
gpointer user_data G_GNUC_UNUSED)
|
||||
{
|
||||
const gchar *prefix = "";
|
||||
|
||||
/* filter out messages depending on debugging level */
|
||||
if ((level & G_LOG_LEVEL_DEBUG) && log_level < LOG_LEVEL_DEBUG)
|
||||
return;
|
||||
|
||||
if ((level & G_LOG_LEVEL_INFO) && log_level < LOG_LEVEL_INFO)
|
||||
return;
|
||||
|
||||
if (level & G_LOG_LEVEL_WARNING)
|
||||
prefix = "WARNING: ";
|
||||
|
||||
if (level & G_LOG_LEVEL_ERROR)
|
||||
prefix = "ERROR: ";
|
||||
|
||||
if (level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR))
|
||||
g_printerr("%s%s\n", prefix, message);
|
||||
else
|
||||
g_print("%s%s\n", prefix, message);
|
||||
}
|
||||
|
||||
static void setup_prgname(const gchar *name)
|
||||
{
|
||||
g_set_prgname(name);
|
||||
g_set_application_name(_(name));
|
||||
}
|
||||
|
||||
static void setup_handler(const gint *signals, const gsize signals_n)
|
||||
{
|
||||
/* set up logging handler */
|
||||
g_log_set_handler(NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
|
||||
G_LOG_FLAG_RECURSION,
|
||||
log_handler_cb, NULL);
|
||||
|
||||
/* set signal handler */
|
||||
for (gsize i = 0; i < signals_n; i++)
|
||||
signal(signals[i], sig_term_handler);
|
||||
}
|
||||
|
||||
static void remove_signal_handler(const gint *signals, const gsize signals_n)
|
||||
{
|
||||
for (gsize i = 0; i < signals_n; i++)
|
||||
signal(signals[i], SIG_DFL);
|
||||
}
|
||||
|
||||
gint main(gint argc, gchar *argv[])
|
||||
{
|
||||
g_autoptr(PvArgs) args = pv_args_new();
|
||||
gint signals[] = { SIGINT, SIGTERM };
|
||||
g_autoptr(PvImage) img = NULL;
|
||||
gint ret = EXIT_FAILURE;
|
||||
GError *err = NULL;
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
setup_prgname(tool_name);
|
||||
setup_handler(signals, G_N_ELEMENTS(signals));
|
||||
|
||||
if (pv_args_parse_options(args, &argc, &argv, &err) < 0)
|
||||
goto error;
|
||||
|
||||
/* set new log level */
|
||||
log_level = args->log_level;
|
||||
|
||||
/* if the user has not specified a temporary directory let's
|
||||
* create one
|
||||
*/
|
||||
if (!args->tmp_dir) {
|
||||
tmp_dir = g_dir_make_tmp("genprotimg-XXXXXX", &err);
|
||||
if (!tmp_dir)
|
||||
goto error;
|
||||
args->tmp_dir = g_strdup(tmp_dir);
|
||||
}
|
||||
|
||||
/* allocate and initialize ``pv_img`` data structure */
|
||||
img = pv_img_new(args, GENPROTIMG_STAGE3A_PATH, &err);
|
||||
if (!img)
|
||||
goto error;
|
||||
|
||||
/* add user components: `args->comps` must be sorted by the
|
||||
* component type => by memory address
|
||||
*/
|
||||
for (GSList *iterator = args->comps; iterator; iterator = iterator->next) {
|
||||
const PvArg *arg = iterator->data;
|
||||
|
||||
if (pv_img_add_component(img, arg, &err) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pv_img_finalize(img, GENPROTIMG_STAGE3B_PATH, &err) < 0)
|
||||
goto error;
|
||||
|
||||
if (pv_img_write(img, args->output_path, &err) < 0)
|
||||
goto error;
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
if (err) {
|
||||
fputs(err->message, stderr);
|
||||
fputc('\n', stderr);
|
||||
g_clear_error(&err);
|
||||
}
|
||||
rmdir_recursive(tmp_dir, NULL);
|
||||
remove_signal_handler(signals, G_N_ELEMENTS(signals));
|
||||
g_free(tmp_dir);
|
||||
exit(ret);
|
||||
}
|
||||
25
genprotimg/src/include/pv_crypto_def.h
Normal file
25
genprotimg/src/include/pv_crypto_def.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* PV cryptography related 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 PV_CRYPTO_DEF_H
|
||||
#define PV_CRYPTO_DEF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
union ecdh_pub_key {
|
||||
struct {
|
||||
uint8_t x[80];
|
||||
uint8_t y[80];
|
||||
};
|
||||
uint8_t data[160];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
84
genprotimg/src/include/pv_hdr_def.h
Normal file
84
genprotimg/src/include/pv_hdr_def.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* PV 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 PV_HDR_DEF_H
|
||||
#define PV_HDR_DEF_H
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "lib/zt_common.h"
|
||||
#include "utils/crypto.h"
|
||||
|
||||
#include "pv_crypto_def.h"
|
||||
|
||||
/* Magic number which is used to identify the file containing the PV
|
||||
* header
|
||||
*/
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_CFLAG_NO_DECRYPTION 0x10000000ULL
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
#define PV_V1_PV_HDR_MAX_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
typedef struct pv_hdr_key_slot {
|
||||
uint8_t digest_key[SHA256_DIGEST_LENGTH];
|
||||
uint8_t wrapped_key[32];
|
||||
uint8_t tag[AES_256_GCM_TAG_SIZE];
|
||||
} __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[AES_256_GCM_IV_SIZE];
|
||||
uint32_t res1;
|
||||
uint64_t nks;
|
||||
uint64_t sea;
|
||||
uint64_t nep;
|
||||
uint64_t pcf;
|
||||
union ecdh_pub_key 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[AES_256_XTS_KEY_SIZE / 2];
|
||||
uint8_t img_enc_key_2[AES_256_XTS_KEY_SIZE / 2];
|
||||
struct psw_t psw;
|
||||
uint64_t scf;
|
||||
uint32_t noi;
|
||||
uint32_t res2;
|
||||
};
|
||||
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[AES_256_GCM_TAG_SIZE];
|
||||
} PvHdr;
|
||||
|
||||
#endif
|
||||
405
genprotimg/src/pv/pv_args.c
Normal file
405
genprotimg/src/pv/pv_args.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* PV arguments related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
#include "pv_error.h"
|
||||
#include "pv_args.h"
|
||||
|
||||
static gchar summary[] =
|
||||
"Use genprotimg to create a protected virtualization kernel image file,\n"
|
||||
"which can be loaded using zipl or QEMU.";
|
||||
|
||||
static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2)
|
||||
{
|
||||
g_assert(arg_1);
|
||||
g_assert(arg_2);
|
||||
|
||||
PvComponentType a = ((PvArg *)arg_1)->type;
|
||||
PvComponentType b = ((PvArg *)arg_2)->type;
|
||||
|
||||
if (a < b)
|
||||
return -1;
|
||||
if (a == b)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static gint pv_arg_has_type(gconstpointer arg, gconstpointer type)
|
||||
{
|
||||
const PvArg *c = arg;
|
||||
const PvComponentType *t = type;
|
||||
|
||||
g_assert(arg);
|
||||
|
||||
if (c->type == *t)
|
||||
return 0;
|
||||
if (c->type < *t)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static gint pv_args_set_defaults(PvArgs *args, GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
if (!args->psw_addr)
|
||||
args->psw_addr =
|
||||
g_strdup_printf("0x%lx", DEFAULT_INITIAL_PSW_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
{
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
if (args->unused_values->len > 0) {
|
||||
g_autofree gchar *unused = NULL;
|
||||
|
||||
for (gsize i = args->unused_values->len; i > 0; i--) {
|
||||
g_autofree gchar *tmp = unused;
|
||||
|
||||
unused = g_strjoin(" ", g_ptr_array_index(args->unused_values, i - 1),
|
||||
tmp,
|
||||
NULL);
|
||||
}
|
||||
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_INVALID_ARGUMENT,
|
||||
_("Unrecognized arguments: '%s'.\nUse 'genprotimg --help' for more information"),
|
||||
unused);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!args->output_path) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--output' is required.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_slist_find_custom(args->comps, &KERNEL, pv_arg_has_type)) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--image' is required.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!args->host_keys || g_strv_length(args->host_keys) == 0) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--host-key-document' is required.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!args->no_verify) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Use the option '--no-verify' as the verification support is not available yet."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean cb_add_component(const gchar *option, const gchar *value,
|
||||
PvArgs *args, GError **err)
|
||||
{
|
||||
PvArg *comp = NULL;
|
||||
gint type = -1;
|
||||
|
||||
if (g_str_equal(option, "-i") || g_str_equal(option, "--image"))
|
||||
type = PV_COMP_TYPE_KERNEL;
|
||||
if (g_str_equal(option, "-r") || g_str_equal(option, "--ramdisk"))
|
||||
type = PV_COMP_TYPE_INITRD;
|
||||
if (g_str_equal(option, "-p") || g_str_equal(option, "--parmfile"))
|
||||
type = PV_COMP_TYPE_CMDLINE;
|
||||
|
||||
if (type < 0) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("Invalid option '%s': "), option);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_slist_find_custom(args->comps, &type, pv_arg_has_type)) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("Multiple values for option '%s'"), option);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
comp = pv_arg_new((PvComponentType)type, value);
|
||||
args->comps = g_slist_insert_sorted(args->comps, comp, pv_arg_compare);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
PvArgs *args, GError **err)
|
||||
{
|
||||
gchar **args_option = NULL;
|
||||
|
||||
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
||||
args_option = &args->output_path;
|
||||
if (g_str_equal(option, "--x-comp-key"))
|
||||
args_option = &args->xts_key_path;
|
||||
if (g_str_equal(option, "--x-comm-key"))
|
||||
args_option = &args->cust_comm_key_path;
|
||||
if (g_str_equal(option, "--x-header-key"))
|
||||
args_option = &args->cust_root_key_path;
|
||||
if (g_str_equal(option, "--x-pcf"))
|
||||
args_option = &args->pcf;
|
||||
if (g_str_equal(option, "--x-psw"))
|
||||
args_option = &args->psw_addr;
|
||||
if (g_str_equal(option, "--x-scf"))
|
||||
args_option = &args->scf;
|
||||
|
||||
if (!args_option) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("Invalid option '%s': "), option);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (*args_option) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("Multiple values for option '%s'"), option);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*args_option = g_strdup(value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_set_log_level(const gchar *option G_GNUC_UNUSED,
|
||||
const gchar *value G_GNUC_UNUSED, PvArgs *args,
|
||||
GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
args->log_level++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED,
|
||||
const gchar *value, PvArgs *args,
|
||||
GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
g_ptr_array_add(args->unused_values, g_strdup(value));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define INDENT " "
|
||||
|
||||
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
gboolean print_version = FALSE;
|
||||
GOptionGroup *group, *x_group;
|
||||
|
||||
g_autofree gchar *psw_desc = g_strdup_printf(
|
||||
_("Load from the specified hexadecimal ADDRESS.\n" INDENT
|
||||
"Optional; default: '0x%lx'."),
|
||||
DEFAULT_INITIAL_PSW_ADDR);
|
||||
GOptionEntry entries[] = {
|
||||
{ .long_name = "host-key-document",
|
||||
.short_name = 'k',
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY,
|
||||
.arg_data = &args->host_keys,
|
||||
.description =
|
||||
_("FILE specifies a host-key document. At least\n" INDENT
|
||||
"one is required."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "output",
|
||||
.short_name = 'o',
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _("Set FILE as the output file."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "image",
|
||||
.short_name = 'i',
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_add_component,
|
||||
.description = _("Use IMAGE as the Linux kernel image."),
|
||||
.arg_description = _("IMAGE") },
|
||||
{ .long_name = "ramdisk",
|
||||
.short_name = 'r',
|
||||
.flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_add_component,
|
||||
.description = _("Use RAMDISK as the initial RAM disk\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("RAMDISK") },
|
||||
{ .long_name = "parmfile",
|
||||
.short_name = 'p',
|
||||
.flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_add_component,
|
||||
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("PARMFILE") },
|
||||
{ .long_name = "no-verify",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_NONE,
|
||||
.arg_data = &args->no_verify,
|
||||
.description = _("Disable the host-key document verification\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = NULL },
|
||||
{ .long_name = "verbose",
|
||||
.short_name = 'V',
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_log_level,
|
||||
.description = _("Provide more detailed output (optional)."),
|
||||
.arg_description = NULL },
|
||||
{ .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."),
|
||||
.arg_description = NULL },
|
||||
{ .long_name = G_OPTION_REMAINING,
|
||||
.short_name = 0,
|
||||
.flags = 0,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_remaining_values,
|
||||
.description = NULL,
|
||||
.arg_description = NULL },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
GOptionEntry x_entries[] = {
|
||||
{ .long_name = "x-comm-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Use FILE as the customer communication key.\n" INDENT
|
||||
"Optional; default: auto-generated."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "x-comp-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Use FILE as the AES 256-bit XTS key\n" INDENT
|
||||
"that is used for the component encryption.\n" INDENT
|
||||
"Optional; default: auto-generated."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "x-header-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Use FILE as the AES 256-bit GCM header key\n" INDENT
|
||||
"that protects the PV header.\n" INDENT
|
||||
"Optional; default: auto-generated."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "x-pcf",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description =
|
||||
_("Specify the plaintext control flags\n" INDENT
|
||||
"as a hexadecimal value.\n" INDENT
|
||||
"Optional; default: '0x0'."),
|
||||
.arg_description = _("VALUE") },
|
||||
{ .long_name = "x-psw",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = psw_desc,
|
||||
.arg_description = _("ADDRESS") },
|
||||
{ .long_name = "x-scf",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _("Specify the secret control flags\n" INDENT
|
||||
"as a hexadecimal value.\n" INDENT
|
||||
"Optional; default: '0x0'."),
|
||||
.arg_description = _("VALUE") },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
context = g_option_context_new(
|
||||
_("- Create a protected virtualization image"));
|
||||
g_option_context_set_summary(context, _(summary));
|
||||
group = g_option_group_new(GETTEXT_PACKAGE, _("Application Options:"),
|
||||
_("Show help options"), args, NULL);
|
||||
g_option_group_add_entries(group, entries);
|
||||
g_option_context_set_main_group(context, group);
|
||||
|
||||
x_group = g_option_group_new("experimental", _("Experimental Options:"),
|
||||
_("Show experimental options"), args, NULL);
|
||||
g_option_group_add_entries(x_group, x_entries);
|
||||
g_option_context_add_group(context, x_group);
|
||||
if (!g_option_context_parse(context, argc, argv, err))
|
||||
return -1;
|
||||
|
||||
if (print_version) {
|
||||
g_printf(_("%s version %s\n"), tool_name, RELEASE_STRING);
|
||||
g_printf("%s\n", copyright_notice);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (pv_args_set_defaults(args, err) < 0)
|
||||
return -1;
|
||||
|
||||
return pv_args_validate_options(args, err);
|
||||
}
|
||||
|
||||
PvArgs *pv_args_new(void)
|
||||
{
|
||||
g_autoptr(PvArgs) args = g_new0(PvArgs, 1);
|
||||
|
||||
args->unused_values = g_ptr_array_new_with_free_func(g_free);
|
||||
return g_steal_pointer(&args);
|
||||
}
|
||||
|
||||
void pv_args_free(PvArgs *args)
|
||||
{
|
||||
if (!args)
|
||||
return;
|
||||
|
||||
g_free(args->pcf);
|
||||
g_free(args->scf);
|
||||
g_free(args->psw_addr);
|
||||
g_free(args->cust_root_key_path);
|
||||
g_free(args->cust_comm_key_path);
|
||||
g_free(args->gcm_iv_path);
|
||||
g_strfreev(args->host_keys);
|
||||
g_free(args->xts_key_path);
|
||||
g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free);
|
||||
g_ptr_array_free(args->unused_values, TRUE);
|
||||
g_free(args->output_path);
|
||||
g_free(args->tmp_dir);
|
||||
g_free(args);
|
||||
}
|
||||
|
||||
void pv_arg_free(PvArg *arg)
|
||||
{
|
||||
if (!arg)
|
||||
return;
|
||||
|
||||
g_free(arg->path);
|
||||
g_free(arg);
|
||||
}
|
||||
PvArg *pv_arg_new(PvComponentType type, const gchar *path)
|
||||
{
|
||||
g_autoptr(PvArg) ret = g_new0(struct pv_arg, 1);
|
||||
|
||||
ret->type = type;
|
||||
ret->path = g_strdup(path);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
53
genprotimg/src/pv/pv_args.h
Normal file
53
genprotimg/src/pv/pv_args.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* PV arguments related definitions and functions
|
||||
*
|
||||
* 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 PV_ARGS_H
|
||||
#define PV_ARGS_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "pv_comp.h"
|
||||
|
||||
typedef struct pv_arg {
|
||||
PvComponentType type;
|
||||
gchar *path;
|
||||
} PvArg;
|
||||
|
||||
PvArg *pv_arg_new(PvComponentType type, const gchar *path);
|
||||
void pv_arg_free(PvArg *arg);
|
||||
|
||||
typedef struct {
|
||||
gint log_level;
|
||||
gint no_verify;
|
||||
gchar *pcf;
|
||||
gchar *scf;
|
||||
gchar *psw_addr; /* PSW address which will be used for the start of
|
||||
* the actual component (e.g. Linux kernel)
|
||||
*/
|
||||
gchar *cust_root_key_path;
|
||||
gchar *cust_comm_key_path;
|
||||
gchar *gcm_iv_path;
|
||||
gchar **host_keys;
|
||||
gchar *xts_key_path;
|
||||
GSList *comps;
|
||||
gchar *output_path;
|
||||
gchar *tmp_dir;
|
||||
GPtrArray *unused_values;
|
||||
} PvArgs;
|
||||
|
||||
PvArgs *pv_args_new(void);
|
||||
void pv_args_free(PvArgs *args);
|
||||
|
||||
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
GError **err);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArg, pv_arg_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArgs, pv_args_free)
|
||||
|
||||
#endif
|
||||
446
genprotimg/src/pv/pv_comp.c
Normal file
446
genprotimg/src/pv/pv_comp.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* PV component related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "common.h"
|
||||
#include "utils/align.h"
|
||||
#include "utils/buffer.h"
|
||||
#include "utils/crypto.h"
|
||||
#include "utils/file_utils.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
#include "pv_error.h"
|
||||
|
||||
static void comp_file_free(CompFile *comp)
|
||||
{
|
||||
if (!comp)
|
||||
return;
|
||||
|
||||
g_free(comp->path);
|
||||
g_free(comp);
|
||||
}
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompFile, comp_file_free)
|
||||
|
||||
static PvComponent *pv_component_new(PvComponentType type, gsize size,
|
||||
PvComponentDataType d_type, void **data,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(PvComponent) ret = g_new0(PvComponent, 1);
|
||||
|
||||
g_assert(type >= 0 && type <= UINT16_MAX);
|
||||
|
||||
ret->type = (int)type;
|
||||
ret->d_type = (int)d_type;
|
||||
ret->data = g_steal_pointer(data);
|
||||
ret->orig_size = size;
|
||||
|
||||
if (generate_tweak(&ret->tweak, (uint16_t)type, err) < 0)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(CompFile) file = g_new0(CompFile, 1);
|
||||
gsize size;
|
||||
gint rc;
|
||||
|
||||
g_assert(path != NULL);
|
||||
|
||||
rc = file_size(path, &size, err);
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
|
||||
file->path = g_strdup(path);
|
||||
file->size = size;
|
||||
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
||||
}
|
||||
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(buf);
|
||||
|
||||
g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE);
|
||||
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
||||
err);
|
||||
}
|
||||
|
||||
void pv_component_free(PvComponent *component)
|
||||
{
|
||||
if (!component)
|
||||
return;
|
||||
|
||||
switch ((PvComponentDataType)component->d_type) {
|
||||
case DATA_BUFFER:
|
||||
buffer_clear(&component->buf);
|
||||
break;
|
||||
case DATA_FILE:
|
||||
comp_file_free(component->file);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(component);
|
||||
}
|
||||
|
||||
gint pv_component_type(const PvComponent *component)
|
||||
{
|
||||
return component->type;
|
||||
}
|
||||
|
||||
const gchar *pv_component_name(const PvComponent *component)
|
||||
{
|
||||
gint type = pv_component_type(component);
|
||||
|
||||
switch ((PvComponentType)type) {
|
||||
case PV_COMP_TYPE_KERNEL:
|
||||
return "kernel";
|
||||
case PV_COMP_TYPE_INITRD:
|
||||
return "ramdisk";
|
||||
case PV_COMP_TYPE_CMDLINE:
|
||||
return "parmline";
|
||||
case PV_COMP_TYPE_STAGE3B:
|
||||
return "stage3b";
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
uint64_t pv_component_size(const PvComponent *component)
|
||||
{
|
||||
switch ((PvComponentDataType)component->d_type) {
|
||||
case DATA_BUFFER:
|
||||
return component->buf->size;
|
||||
case DATA_FILE:
|
||||
return component->file->size;
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
uint64_t pv_component_get_src_addr(const PvComponent *component)
|
||||
{
|
||||
return component->src_addr;
|
||||
}
|
||||
|
||||
uint64_t pv_component_get_orig_size(const PvComponent *component)
|
||||
{
|
||||
return component->orig_size;
|
||||
}
|
||||
|
||||
uint64_t pv_component_get_tweak_prefix(const PvComponent *component)
|
||||
{
|
||||
return GUINT64_FROM_BE(component->tweak.cmp_idx.data);
|
||||
}
|
||||
|
||||
gboolean pv_component_is_stage3b(const PvComponent *component)
|
||||
{
|
||||
return pv_component_type(component) == PV_COMP_TYPE_STAGE3B;
|
||||
}
|
||||
|
||||
gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path,
|
||||
void *opaque, GError **err)
|
||||
{
|
||||
struct cipher_parms *parms = opaque;
|
||||
|
||||
switch ((PvComponentDataType)component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
g_autoptr(Buffer) enc_buf = NULL;
|
||||
|
||||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
||||
g_autoptr(Buffer) new = NULL;
|
||||
|
||||
/* create a page aligned copy */
|
||||
new = buffer_dup(component->buf, TRUE);
|
||||
buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&new);
|
||||
}
|
||||
enc_buf = encrypt_buf(parms, component->buf, err);
|
||||
if (!enc_buf)
|
||||
return -1;
|
||||
|
||||
buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&enc_buf);
|
||||
return 0;
|
||||
}
|
||||
case DATA_FILE: {
|
||||
const gchar *comp_name = pv_component_name(component);
|
||||
gchar *path_in = component->file->path;
|
||||
g_autofree gchar *path_out = NULL;
|
||||
gsize orig_size;
|
||||
gsize prep_size;
|
||||
|
||||
g_assert(path_in);
|
||||
|
||||
path_out = g_build_filename(tmp_path, comp_name, NULL);
|
||||
if (encrypt_file(parms, path_in, path_out, &orig_size,
|
||||
&prep_size, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (component->orig_size != orig_size) {
|
||||
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
||||
_("File has changed during the preparation '%s'"),
|
||||
path_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free(component->file->path);
|
||||
component->file->size = prep_size;
|
||||
component->file->path = g_steal_pointer(&path_out);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Page align the size of the component */
|
||||
gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||||
void *opaque G_GNUC_UNUSED, GError **err)
|
||||
{
|
||||
if (IS_PAGE_ALIGNED(pv_component_size(component)))
|
||||
return 0;
|
||||
|
||||
switch (component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
g_autoptr(Buffer) buf = NULL;
|
||||
|
||||
buf = buffer_dup(component->buf, TRUE);
|
||||
buffer_clear(&component->buf);
|
||||
component->buf = g_steal_pointer(&buf);
|
||||
return 0;
|
||||
} break;
|
||||
case DATA_FILE: {
|
||||
const gchar *comp_name = pv_component_name(component);
|
||||
g_autofree gchar *path_out =
|
||||
g_build_filename(tmp_path, comp_name, NULL);
|
||||
gchar *path_in = component->file->path;
|
||||
gsize size_out;
|
||||
|
||||
if (pad_file_right(path_out, path_in, &size_out, PAGE_SIZE,
|
||||
err) < 0)
|
||||
return -1;
|
||||
|
||||
g_free(component->file->path);
|
||||
component->file->path = g_steal_pointer(&path_out);
|
||||
component->file->size = size_out;
|
||||
return 0;
|
||||
} break;
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Convert uint64_t address to byte array */
|
||||
static void uint64_to_uint8_buf(uint8_t dst[8], uint64_t addr)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)&addr;
|
||||
|
||||
g_assert(dst);
|
||||
|
||||
for (gint i = 0; i < 8; i++) {
|
||||
/* cppcheck-suppress objectIndex */
|
||||
dst[i] = p[i];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err)
|
||||
{
|
||||
uint64_t addr = pv_component_get_src_addr(comp);
|
||||
uint64_t size = pv_component_size(comp);
|
||||
uint64_t cur = addr;
|
||||
int64_t nep = 0;
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
||||
|
||||
do {
|
||||
uint64_t cur_be = GUINT64_TO_BE(cur);
|
||||
uint8_t addr_buf[8];
|
||||
|
||||
uint64_to_uint8_buf(addr_buf, cur_be);
|
||||
|
||||
if (EVP_DigestUpdate(ctx, addr_buf, sizeof(addr_buf)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur += PAGE_SIZE;
|
||||
nep++;
|
||||
} while (cur < addr + size);
|
||||
|
||||
return nep;
|
||||
}
|
||||
|
||||
int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err)
|
||||
{
|
||||
uint64_t size = pv_component_size(comp);
|
||||
int64_t nep = 0;
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
||||
|
||||
switch (comp->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
const Buffer *buf = comp->buf;
|
||||
|
||||
g_assert(buf->size <= INT64_MAX);
|
||||
g_assert(buf->size == size);
|
||||
|
||||
if (EVP_DigestUpdate(ctx, buf->data, buf->size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
nep = (int64_t)(buf->size / PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
case DATA_FILE: {
|
||||
const gchar *in_path = comp->file->path;
|
||||
guchar in_buf[PAGE_SIZE];
|
||||
gsize num_bytes_read_total = 0;
|
||||
gsize num_bytes_read = 0;
|
||||
FILE *f_in;
|
||||
|
||||
f_in = file_open(in_path, "rb", err);
|
||||
if (!f_in)
|
||||
return -1;
|
||||
|
||||
do {
|
||||
/* Read data in blocks. Update the digest
|
||||
* context each read.
|
||||
*/
|
||||
if (file_read(f_in, in_buf, sizeof(*in_buf),
|
||||
sizeof(in_buf), &num_bytes_read,
|
||||
err) < 0) {
|
||||
fclose(f_in);
|
||||
return -1;
|
||||
}
|
||||
num_bytes_read_total += num_bytes_read;
|
||||
|
||||
if (EVP_DigestUpdate(ctx, in_buf, sizeof(in_buf)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
fclose(f_in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nep++;
|
||||
} while (num_bytes_read_total < pv_component_size(comp) &&
|
||||
num_bytes_read != 0);
|
||||
|
||||
if (num_bytes_read_total != pv_component_size(comp)) {
|
||||
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
||||
_("'%s' has changed during the preparation"),
|
||||
in_path);
|
||||
fclose(f_in);
|
||||
return -1;
|
||||
}
|
||||
fclose(f_in);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return nep;
|
||||
}
|
||||
|
||||
int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err)
|
||||
{
|
||||
uint64_t size = pv_component_size(comp);
|
||||
const union tweak *tweak = &comp->tweak;
|
||||
g_autoptr(BIGNUM) tweak_num = NULL;
|
||||
int64_t nep = 0;
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
||||
|
||||
tweak_num = BN_bin2bn(tweak->data, sizeof(tweak->data), NULL);
|
||||
if (!tweak_num) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bin2bn failed"));
|
||||
}
|
||||
|
||||
for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) {
|
||||
guchar tmp[sizeof(tweak->data)] = { 0 };
|
||||
|
||||
g_assert(BN_num_bytes(tweak_num) >= 0);
|
||||
g_assert(sizeof(tmp) - (guint)BN_num_bytes(tweak_num) > 0);
|
||||
|
||||
if (BN_bn2binpad(tweak_num, tmp, sizeof(tmp)) < 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bn2binpad failed"));
|
||||
}
|
||||
|
||||
if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* calculate new tweak value */
|
||||
if (BN_add_word(tweak_num, PAGE_SIZE) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_add_word failed"));
|
||||
}
|
||||
|
||||
nep++;
|
||||
}
|
||||
|
||||
return nep;
|
||||
}
|
||||
|
||||
gint pv_component_write(const PvComponent *component, FILE *f, GError **err)
|
||||
{
|
||||
uint64_t offset = pv_component_get_src_addr(component);
|
||||
|
||||
g_assert(f);
|
||||
|
||||
switch (component->d_type) {
|
||||
case DATA_BUFFER: {
|
||||
const Buffer *buf = component->buf;
|
||||
|
||||
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case DATA_FILE: {
|
||||
const CompFile *file = component->file;
|
||||
|
||||
if (seek_and_write_file(f, file, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
78
genprotimg/src/pv/pv_comp.h
Normal file
78
genprotimg/src/pv/pv_comp.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* PV component related definitions and functions
|
||||
*
|
||||
* 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 PV_COMP_H
|
||||
#define PV_COMP_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "utils/crypto.h"
|
||||
|
||||
/* The order of this enum also implicitly defines the order of the
|
||||
* components within the PV image!
|
||||
*/
|
||||
typedef enum {
|
||||
PV_COMP_TYPE_KERNEL = 0,
|
||||
PV_COMP_TYPE_CMDLINE = 1,
|
||||
PV_COMP_TYPE_INITRD = 2,
|
||||
PV_COMP_TYPE_STAGE3B = 3,
|
||||
} PvComponentType;
|
||||
|
||||
typedef enum {
|
||||
DATA_FILE = 0,
|
||||
DATA_BUFFER,
|
||||
} PvComponentDataType;
|
||||
|
||||
typedef struct comp_file {
|
||||
gchar *path;
|
||||
gsize size;
|
||||
} CompFile;
|
||||
|
||||
typedef struct {
|
||||
gint type; /* PvComponentType */
|
||||
gint d_type; /* PvComponentDataType */
|
||||
union {
|
||||
struct comp_file *file;
|
||||
Buffer *buf;
|
||||
void *data;
|
||||
};
|
||||
uint64_t src_addr;
|
||||
uint64_t orig_size;
|
||||
union tweak tweak; /* used for the AES XTS encryption */
|
||||
} PvComponent;
|
||||
|
||||
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||||
GError **err);
|
||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||||
GError **err);
|
||||
void pv_component_free(PvComponent *component);
|
||||
gint pv_component_type(const PvComponent *component);
|
||||
const gchar *pv_component_name(const PvComponent *component);
|
||||
uint64_t pv_component_size(const PvComponent *component);
|
||||
uint64_t pv_component_get_src_addr(const PvComponent *component);
|
||||
uint64_t pv_component_get_orig_size(const PvComponent *component);
|
||||
uint64_t pv_component_get_tweak_prefix(const PvComponent *component);
|
||||
gboolean pv_component_is_stage3b(const PvComponent *component);
|
||||
gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path,
|
||||
void *opaque, GError **err);
|
||||
gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||||
void *opaque G_GNUC_UNUSED, GError **err);
|
||||
int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err);
|
||||
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err);
|
||||
int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err);
|
||||
gint pv_component_write(const PvComponent *component, FILE *f, GError **err);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvComponent, pv_component_free)
|
||||
|
||||
#endif
|
||||
252
genprotimg/src/pv/pv_comps.c
Normal file
252
genprotimg/src/pv/pv_comps.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* PV components related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "boot/stage3b.h"
|
||||
#include "common.h"
|
||||
#include "utils/align.h"
|
||||
#include "utils/crypto.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
#include "pv_comps.h"
|
||||
#include "pv_error.h"
|
||||
#include "pv_stage3.h"
|
||||
|
||||
struct _pv_img_comps {
|
||||
gboolean finalized;
|
||||
uint64_t next_src;
|
||||
uint64_t nep;
|
||||
EVP_MD_CTX *ald; /* context used for the hash of the addresses */
|
||||
EVP_MD_CTX *pld; /* context used for the hash of the pages content */
|
||||
EVP_MD_CTX *tld; /* context used for the hash of the tweaks */
|
||||
GSList *comps; /* elements sorted by component type */
|
||||
};
|
||||
|
||||
void pv_img_comps_free(PvImgComps *comps)
|
||||
{
|
||||
if (!comps)
|
||||
return;
|
||||
|
||||
EVP_MD_CTX_free(comps->ald);
|
||||
EVP_MD_CTX_free(comps->pld);
|
||||
EVP_MD_CTX_free(comps->tld);
|
||||
g_slist_free_full(comps->comps, (GDestroyNotify)pv_component_free);
|
||||
g_free(comps);
|
||||
}
|
||||
|
||||
PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md,
|
||||
const EVP_MD *tld_md, GError **err)
|
||||
{
|
||||
g_autoptr(PvImgComps) ret = g_new0(PvImgComps, 1);
|
||||
|
||||
ret->ald = digest_ctx_new(ald_md, err);
|
||||
if (!ret->ald)
|
||||
return NULL;
|
||||
|
||||
ret->pld = digest_ctx_new(pld_md, err);
|
||||
if (!ret->pld)
|
||||
return NULL;
|
||||
|
||||
ret->tld = digest_ctx_new(tld_md, err);
|
||||
if (!ret->tld)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
guint pv_img_comps_length(const PvImgComps *comps)
|
||||
{
|
||||
return g_slist_length(comps->comps);
|
||||
}
|
||||
|
||||
/* Update hashes and nep */
|
||||
/* Returns 0 in case of success and -1 in case of a failure */
|
||||
static gint pv_img_comps_hash_comp(PvImgComps *comps, const PvComponent *comp,
|
||||
GError **err)
|
||||
{
|
||||
int64_t nep_1 = 0;
|
||||
int64_t nep_2 = 0;
|
||||
int64_t nep_3 = 0;
|
||||
|
||||
/* update pld */
|
||||
nep_1 = pv_component_update_pld(comp, comps->pld, err);
|
||||
if (nep_1 < 0)
|
||||
return -1;
|
||||
|
||||
/* update ald */
|
||||
nep_2 = pv_component_update_ald(comp, comps->ald, err);
|
||||
if (nep_2 < 0)
|
||||
return -1;
|
||||
|
||||
/* update tld */
|
||||
nep_3 = pv_component_update_tld(comp, comps->tld, err);
|
||||
if (nep_3 < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(nep_1 == nep_2);
|
||||
g_assert(nep_2 == nep_3);
|
||||
|
||||
/* update comps->nep */
|
||||
g_assert_true(g_uint64_checked_add(&comps->nep, comps->nep,
|
||||
(uint64_t)nep_1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(comp);
|
||||
g_assert(*comp);
|
||||
g_assert(comps);
|
||||
g_assert(IS_PAGE_ALIGNED(comps->next_src));
|
||||
|
||||
uint64_t src_addr = comps->next_src;
|
||||
uint64_t src_size = pv_component_size(*comp)
|
||||
? PAGE_ALIGN(pv_component_size(*comp))
|
||||
: PAGE_SIZE;
|
||||
|
||||
if (comps->finalized) {
|
||||
g_set_error(err, PV_COMPONENT_ERROR, PV_COMPONENT_ERROR_FINALIZED,
|
||||
_("Failed to add component, image is already finalized"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set the address of the component in the memory layout */
|
||||
(*comp)->src_addr = src_addr;
|
||||
|
||||
g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)",
|
||||
pv_component_name(*comp), pv_component_get_src_addr(*comp),
|
||||
pv_component_size(*comp), pv_component_get_orig_size(*comp));
|
||||
|
||||
/* append the component and pass the responsibility of @comp
|
||||
* to @comps
|
||||
*/
|
||||
comps->comps = g_slist_append(comps->comps, g_steal_pointer(comp));
|
||||
comps->next_src += src_size;
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(comps->next_src));
|
||||
g_assert(!*comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps,
|
||||
struct psw_t *psw)
|
||||
{
|
||||
g_autofree struct stage3b_args *ret = g_new0(struct stage3b_args, 1);
|
||||
|
||||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
||||
const PvComponent *img_comp = iterator->data;
|
||||
uint64_t src_addr, dst_size;
|
||||
|
||||
g_assert(img_comp);
|
||||
|
||||
src_addr = pv_component_get_src_addr(img_comp);
|
||||
dst_size = pv_component_get_orig_size(img_comp);
|
||||
|
||||
g_assert(dst_size <= pv_component_size(img_comp));
|
||||
|
||||
switch ((PvComponentType)pv_component_type(img_comp)) {
|
||||
case PV_COMP_TYPE_KERNEL:
|
||||
memblob_init(&ret->kernel, src_addr, dst_size);
|
||||
break;
|
||||
case PV_COMP_TYPE_CMDLINE:
|
||||
memblob_init(&ret->cmdline, src_addr, dst_size);
|
||||
break;
|
||||
case PV_COMP_TYPE_INITRD:
|
||||
memblob_init(&ret->initrd, src_addr, dst_size);
|
||||
break;
|
||||
case PV_COMP_TYPE_STAGE3B:
|
||||
/* nothing needs to be done since it is the
|
||||
* stage3b itself
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* for `stage3b_args` big-endian format must be used */
|
||||
ret->psw.mask = GUINT64_TO_BE(psw->mask);
|
||||
ret->psw.addr = GUINT64_TO_BE(psw->addr);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err)
|
||||
{
|
||||
g_assert(IS_PAGE_ALIGNED(comps->next_src));
|
||||
|
||||
if (!IS_PAGE_ALIGNED(offset)) {
|
||||
g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET,
|
||||
_("Offset must be page aligned"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pv_img_comps_length(comps) > 0) {
|
||||
g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET,
|
||||
_("Offset cannot be changed after a component was added"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
comps->next_src += offset;
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(comps->next_src));
|
||||
return 0;
|
||||
}
|
||||
|
||||
GSList *pv_img_comps_get_comps(const PvImgComps *comps)
|
||||
{
|
||||
return comps->comps;
|
||||
}
|
||||
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||||
Buffer **ald_digest, Buffer **tld_digest,
|
||||
uint64_t *nep, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) tmp_pld_digest = NULL;
|
||||
g_autoptr(Buffer) tmp_ald_digest = NULL;
|
||||
g_autoptr(Buffer) tmp_tld_digest = NULL;
|
||||
|
||||
comps->finalized = TRUE;
|
||||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
||||
const PvComponent *comp = iterator->data;
|
||||
|
||||
/* update hashes and nep */
|
||||
if (pv_img_comps_hash_comp(comps, comp, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_pld_digest = digest_ctx_finalize(comps->pld, err);
|
||||
if (!tmp_pld_digest)
|
||||
return -1;
|
||||
|
||||
tmp_ald_digest = digest_ctx_finalize(comps->ald, err);
|
||||
if (!tmp_ald_digest)
|
||||
return -1;
|
||||
|
||||
tmp_tld_digest = digest_ctx_finalize(comps->tld, err);
|
||||
if (!tmp_tld_digest)
|
||||
return -1;
|
||||
|
||||
*pld_digest = g_steal_pointer(&tmp_pld_digest);
|
||||
*ald_digest = g_steal_pointer(&tmp_ald_digest);
|
||||
*tld_digest = g_steal_pointer(&tmp_tld_digest);
|
||||
*nep = comps->nep;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n)
|
||||
{
|
||||
return g_slist_nth_data(comps->comps, n);
|
||||
}
|
||||
42
genprotimg/src/pv/pv_comps.h
Normal file
42
genprotimg/src/pv/pv_comps.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* PV components related definitions and functions
|
||||
*
|
||||
* 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 PV_COMPS_H
|
||||
#define PV_COMPS_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "boot/stage3b.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
|
||||
typedef struct _pv_img_comps PvImgComps;
|
||||
|
||||
PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md,
|
||||
const EVP_MD *tld_md, GError **err);
|
||||
guint pv_img_comps_length(const PvImgComps *comps);
|
||||
GSList *pv_img_comps_get_comps(const PvImgComps *comps);
|
||||
struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps,
|
||||
struct psw_t *psw);
|
||||
gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp,
|
||||
GError **err);
|
||||
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n);
|
||||
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err);
|
||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||||
Buffer **ald_digest, Buffer **tld_digest,
|
||||
uint64_t *nep, GError **err);
|
||||
void pv_img_comps_free(PvImgComps *comps);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImgComps, pv_img_comps_free)
|
||||
|
||||
#endif
|
||||
37
genprotimg/src/pv/pv_error.c
Normal file
37
genprotimg/src/pv/pv_error.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* PV error related functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "pv_error.h"
|
||||
|
||||
GQuark pv_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-error-quark");
|
||||
}
|
||||
|
||||
GQuark pv_crypto_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-crypto-error-quark");
|
||||
}
|
||||
|
||||
GQuark pv_component_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-component-error-quark");
|
||||
}
|
||||
|
||||
GQuark pv_image_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-image-error-quark");
|
||||
}
|
||||
|
||||
GQuark pv_parse_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("pv-parse-error-quark");
|
||||
}
|
||||
62
genprotimg/src/pv/pv_error.h
Normal file
62
genprotimg/src/pv/pv_error.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* PV error related definitions and functions
|
||||
*
|
||||
* 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 PV_ERROR_H
|
||||
#define PV_ERROR_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
GQuark pv_error_quark(void);
|
||||
GQuark pv_parse_error_quark(void);
|
||||
GQuark pv_component_error_quark(void);
|
||||
GQuark pv_crypto_error_quark(void);
|
||||
GQuark pv_image_error_quark(void);
|
||||
|
||||
#define PV_ERROR pv_error_quark()
|
||||
#define PV_PARSE_ERROR pv_parse_error_quark()
|
||||
#define PV_CRYPTO_ERROR pv_crypto_error_quark()
|
||||
#define PV_COMPONENT_ERROR pv_component_error_quark()
|
||||
#define PV_IMAGE_ERROR pv_image_error_quark()
|
||||
|
||||
typedef enum {
|
||||
PV_ERROR_IPIB_SIZE,
|
||||
PV_ERROR_PV_HDR_SIZE,
|
||||
PV_ERROR_INTERNAL,
|
||||
} PvErrors;
|
||||
|
||||
typedef enum {
|
||||
PV_PARSE_ERROR_OK = 0,
|
||||
PV_PARSE_ERROR_SYNTAX,
|
||||
PR_PARSE_ERROR_INVALID_ARGUMENT,
|
||||
PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
} PvParseErrors;
|
||||
|
||||
typedef enum {
|
||||
PV_COMPONENT_ERROR_UNALIGNED,
|
||||
PV_COMPONENT_ERROR_FINALIZED,
|
||||
} PvComponentErrors;
|
||||
|
||||
typedef enum {
|
||||
PV_IMAGE_ERROR_OFFSET,
|
||||
PV_IMAGE_ERROR_FINALIZED,
|
||||
} PvImageErrors;
|
||||
|
||||
typedef enum {
|
||||
PV_CRYPTO_ERROR_VERIFICATION,
|
||||
PV_CRYPTO_ERROR_INIT,
|
||||
PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
PV_CRYPTO_ERROR_DERIVE,
|
||||
PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
PV_CRYPTO_ERROR_INVALID_KEY_SIZE,
|
||||
} PvCryptoErrors;
|
||||
|
||||
#endif
|
||||
293
genprotimg/src/pv/pv_hdr.c
Normal file
293
genprotimg/src/pv/pv_hdr.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* PV header related functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "include/pv_crypto_def.h"
|
||||
#include "utils/buffer.h"
|
||||
#include "utils/crypto.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
#include "pv_hdr.h"
|
||||
#include "pv_image.h"
|
||||
|
||||
void pv_hdr_free(PvHdr *hdr)
|
||||
{
|
||||
if (!hdr)
|
||||
return;
|
||||
|
||||
g_free(hdr->optional_items);
|
||||
g_free(hdr->encrypted);
|
||||
g_free(hdr->slots);
|
||||
g_free(hdr);
|
||||
}
|
||||
|
||||
uint32_t pv_hdr_size(const PvHdr *hdr)
|
||||
{
|
||||
return GUINT32_FROM_BE(hdr->head.phs);
|
||||
}
|
||||
|
||||
gboolean pv_hdr_uses_encryption(const PvHdr *hdr)
|
||||
{
|
||||
return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_CFLAG_NO_DECRYPTION);
|
||||
}
|
||||
|
||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr)
|
||||
{
|
||||
return GUINT64_FROM_BE(hdr->head.sea);
|
||||
}
|
||||
|
||||
uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr)
|
||||
{
|
||||
uint64_t size = pv_hdr_enc_size(hdr);
|
||||
|
||||
if (size > UINT32_MAX)
|
||||
g_abort();
|
||||
|
||||
return (uint32_t)size;
|
||||
}
|
||||
|
||||
static guint pv_hdr_tag_size(const PvHdr *hdr)
|
||||
{
|
||||
return sizeof(hdr->tag);
|
||||
}
|
||||
|
||||
uint32_t pv_hdr_aad_size(const PvHdr *hdr)
|
||||
{
|
||||
return pv_hdr_size(hdr) - pv_hdr_enc_size_casted(hdr) -
|
||||
pv_hdr_tag_size(hdr);
|
||||
}
|
||||
|
||||
uint64_t pv_hdr_get_nks(const PvHdr *hdr)
|
||||
{
|
||||
return GUINT64_FROM_BE(hdr->head.nks);
|
||||
}
|
||||
|
||||
/* In-place modification of ``buf`` */
|
||||
static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf,
|
||||
GError **err)
|
||||
{
|
||||
uint32_t hdr_len = pv_hdr_size(hdr);
|
||||
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
||||
guint tag_len = pv_hdr_tag_size(hdr);
|
||||
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
||||
const Buffer aad_part = { .data = buf->data, .size = aad_len };
|
||||
Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||||
.size = enc_len };
|
||||
Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||||
.size = tag_len };
|
||||
struct cipher_parms parms;
|
||||
int64_t c_len;
|
||||
|
||||
g_assert(aad_part.size + enc_part.size + tag_part.size == buf->size);
|
||||
g_assert(img->cust_root_key->size <= INT_MAX);
|
||||
g_assert(img->gcm_iv->size <= INT_MAX);
|
||||
g_assert(EVP_CIPHER_key_length(img->gcm_cipher) ==
|
||||
(int)img->cust_root_key->size);
|
||||
g_assert(EVP_CIPHER_iv_length(img->gcm_cipher) == (int)img->gcm_iv->size);
|
||||
|
||||
parms.key = img->cust_root_key;
|
||||
parms.iv_or_tweak = img->gcm_iv;
|
||||
parms.cipher = img->gcm_cipher;
|
||||
|
||||
/* in-place encryption */
|
||||
c_len = gcm_encrypt(&enc_part, &aad_part, &parms, &enc_part, &tag_part, err);
|
||||
if (c_len < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(c_len == enc_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initializes the unencrypted, but integrity protected part of the PV
|
||||
* header
|
||||
*/
|
||||
static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err)
|
||||
{
|
||||
g_autofree union ecdh_pub_key *cust_pub_key = NULL;
|
||||
struct pv_hdr_key_slot *hdr_slot = hdr->slots;
|
||||
struct pv_hdr_head *head = &hdr->head;
|
||||
g_autoptr(Buffer) pld = NULL;
|
||||
g_autoptr(Buffer) ald = NULL;
|
||||
g_autoptr(Buffer) tld = NULL;
|
||||
uint64_t nep = 0;
|
||||
|
||||
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
||||
g_assert(sizeof(head->cust_pub_key) == sizeof(*cust_pub_key));
|
||||
|
||||
cust_pub_key = evp_pkey_to_ecdh_pub_key(img->cust_pub_priv_key, err);
|
||||
if (!cust_pub_key)
|
||||
return -1;
|
||||
|
||||
head->magic = GUINT64_TO_BE(PV_MAGIC_NUMBER);
|
||||
head->version = GUINT32_TO_BE(PV_VERSION_1);
|
||||
/* ``phs`` is already set so we can skip it here */
|
||||
memcpy(head->iv, img->gcm_iv->data, sizeof(head->iv));
|
||||
/* ``nks`` is already set so we can skip it here */
|
||||
/* ``sea`` is already set so we can skip it here */
|
||||
head->pcf = GUINT64_TO_BE(img->pcf);
|
||||
memcpy(head->cust_pub_key.data, cust_pub_key,
|
||||
sizeof(head->cust_pub_key));
|
||||
|
||||
if (pv_img_calc_pld_ald_tld_nep(img, &pld, &ald, &tld, &nep, err) < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(sizeof(head->pld) == pld->size);
|
||||
g_assert(sizeof(head->ald) == ald->size);
|
||||
g_assert(sizeof(head->tld) == tld->size);
|
||||
|
||||
head->nep = GUINT64_TO_BE(nep);
|
||||
memcpy(head->pld, pld->data, sizeof(head->pld));
|
||||
memcpy(head->ald, ald->data, sizeof(head->ald));
|
||||
memcpy(head->tld, tld->data, sizeof(head->tld));
|
||||
|
||||
/* set the key slots */
|
||||
for (GSList *iterator = img->key_slots; iterator; iterator = iterator->next) {
|
||||
const PvHdrKeySlot *slot = iterator->data;
|
||||
|
||||
g_assert(slot);
|
||||
|
||||
/* the memory for the slots is pre-allocated so we
|
||||
* have not to allocate and since PvHdrKeySlot is
|
||||
* stored in the big-edian format we can simply use
|
||||
* memcpy.
|
||||
*/
|
||||
memcpy(hdr_slot++, slot, sizeof(*slot));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initializes the encrypted and also integrity protected part of the
|
||||
* PV header
|
||||
*/
|
||||
static gint pv_hdr_enc_init(PvHdr *hdr, const PvImage *img, GError **err)
|
||||
{
|
||||
struct pv_hdr_encrypted *enc = hdr->encrypted;
|
||||
const PvComponent *stage3b;
|
||||
struct psw_t psw;
|
||||
|
||||
g_assert(sizeof(enc->img_enc_key_1) + sizeof(enc->img_enc_key_2) ==
|
||||
EVP_CIPHER_key_length(img->xts_cipher));
|
||||
g_assert(sizeof(enc->cust_comm_key) == img->cust_comm_key->size);
|
||||
g_assert(img->xts_key->size ==
|
||||
(guint)EVP_CIPHER_key_length(img->xts_cipher));
|
||||
|
||||
stage3b = pv_img_get_stage3b_comp(img, err);
|
||||
if (!stage3b)
|
||||
return -1;
|
||||
|
||||
memcpy(enc->cust_comm_key, img->cust_comm_key->data,
|
||||
sizeof(enc->cust_comm_key));
|
||||
memcpy(enc->img_enc_key_1, img->xts_key->data,
|
||||
sizeof(enc->img_enc_key_1));
|
||||
memcpy(enc->img_enc_key_2,
|
||||
(uint8_t *)img->xts_key->data + sizeof(enc->img_enc_key_1),
|
||||
sizeof(enc->img_enc_key_2));
|
||||
|
||||
/* Setup program check handler */
|
||||
psw.mask = GUINT64_TO_BE(DEFAULT_INITIAL_PSW_MASK);
|
||||
psw.addr = GUINT64_TO_BE(pv_component_get_src_addr(stage3b));
|
||||
enc->psw = psw;
|
||||
enc->scf = GUINT64_TO_BE(img->scf);
|
||||
enc->noi = GUINT32_TO_BE(g_slist_length(img->optional_items));
|
||||
|
||||
/* set the optional items */
|
||||
for (GSList *iterator = img->optional_items; iterator;
|
||||
iterator = iterator->next) {
|
||||
const struct pv_hdr_opt_item *item = iterator->data;
|
||||
|
||||
g_assert(item);
|
||||
|
||||
/* not supported in the first version */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PvHdr *pv_hdr_new(const PvImage *img, GError **err)
|
||||
{
|
||||
uint32_t noi = g_slist_length(img->optional_items);
|
||||
uint32_t hdr_size = pv_img_get_pv_hdr_size(img);
|
||||
gsize nks = g_slist_length(img->key_slots);
|
||||
uint32_t sea = pv_img_get_enc_size(img);
|
||||
g_autoptr(PvHdr) ret = NULL;
|
||||
|
||||
g_assert(nks > 0);
|
||||
/* must be a multiple of AES block size */
|
||||
g_assert(sea % AES_BLOCK_SIZE == 0);
|
||||
g_assert(sea >= sizeof(struct pv_hdr_encrypted));
|
||||
|
||||
ret = g_new0(PvHdr, 1);
|
||||
ret->slots = g_new0(struct pv_hdr_key_slot, nks);
|
||||
ret->head.phs = GUINT32_TO_BE(hdr_size);
|
||||
ret->head.nks = GUINT64_TO_BE(nks);
|
||||
ret->head.sea = GUINT64_TO_BE(sea);
|
||||
|
||||
ret->encrypted = g_new0(struct pv_hdr_encrypted, 1);
|
||||
ret->optional_items = g_malloc0(sea - sizeof(struct pv_hdr_encrypted));
|
||||
ret->encrypted->noi = GUINT32_TO_BE(noi);
|
||||
|
||||
if (pv_hdr_aad_init(ret, img, err) < 0)
|
||||
return NULL;
|
||||
|
||||
if (pv_hdr_enc_init(ret, img, err) < 0)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
||||
{
|
||||
uint64_t nks = pv_hdr_get_nks(hdr);
|
||||
uint8_t *data;
|
||||
|
||||
g_assert(dst->size == pv_hdr_size(hdr));
|
||||
g_assert(pv_hdr_enc_size_casted(hdr) >= sizeof(*hdr->encrypted));
|
||||
|
||||
data = memcpy(dst->data, &hdr->head, sizeof(hdr->head));
|
||||
data = memcpy(data + sizeof(hdr->head), hdr->slots,
|
||||
sizeof(struct pv_hdr_key_slot) * nks);
|
||||
data = memcpy(data + sizeof(struct pv_hdr_key_slot) * nks,
|
||||
hdr->encrypted, sizeof(*hdr->encrypted));
|
||||
if (pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted) > 0) {
|
||||
(void)memcpy(data + sizeof(*hdr->encrypted),
|
||||
hdr->optional_items,
|
||||
pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted));
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
{
|
||||
uint32_t hdr_size = pv_hdr_size(hdr);
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
|
||||
ret = buffer_alloc(hdr_size);
|
||||
pv_hdr_memcpy(hdr, ret);
|
||||
|
||||
if (mode == PV_ENCRYPT) {
|
||||
/* The buffer @ret is modified in-place */
|
||||
if (pv_hdr_encrypt(hdr, img, ret, err) < 0)
|
||||
return NULL;
|
||||
} else {
|
||||
/* Simply copy the tag */
|
||||
memcpy((uint8_t *)ret->data + hdr_size - pv_hdr_tag_size(hdr),
|
||||
hdr->tag, pv_hdr_tag_size(hdr));
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
36
genprotimg/src/pv/pv_hdr.h
Normal file
36
genprotimg/src/pv/pv_hdr.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* PV header related functions
|
||||
*
|
||||
* 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 PV_HDR_H
|
||||
#define PV_HDR_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "include/pv_hdr_def.h"
|
||||
#include "utils/crypto.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
#include "pv_image.h"
|
||||
|
||||
PvHdr *pv_hdr_new(const PvImage *img, GError **err);
|
||||
void pv_hdr_free(PvHdr *hdr);
|
||||
G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr);
|
||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||
enum PvCryptoMode mode, GError **err);
|
||||
uint32_t pv_hdr_size(const PvHdr *hdr);
|
||||
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
||||
uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr);
|
||||
uint64_t pv_hdr_get_nks(const PvHdr *hdr);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdr, pv_hdr_free)
|
||||
|
||||
#endif
|
||||
821
genprotimg/src/pv/pv_image.c
Normal file
821
genprotimg/src/pv/pv_image.c
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* PV image related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "boot/stage3a.h"
|
||||
#include "common.h"
|
||||
#include "include/pv_crypto_def.h"
|
||||
#include "include/pv_hdr_def.h"
|
||||
#include "utils/align.h"
|
||||
#include "utils/crypto.h"
|
||||
#include "utils/file_utils.h"
|
||||
|
||||
#include "pv_args.h"
|
||||
#include "pv_comps.h"
|
||||
#include "pv_error.h"
|
||||
#include "pv_hdr.h"
|
||||
#include "pv_image.h"
|
||||
#include "pv_ipib.h"
|
||||
#include "pv_opt_item.h"
|
||||
#include "pv_stage3.h"
|
||||
|
||||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err)
|
||||
{
|
||||
const PvComponent *comp;
|
||||
|
||||
g_return_val_if_fail(pv_img_comps_length(img->comps) >= 1, NULL);
|
||||
|
||||
comp = pv_img_comps_get_nth_comp(img->comps,
|
||||
pv_img_comps_length(img->comps) - 1);
|
||||
if (!pv_component_is_stage3b(comp)) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Failed to get 'stage3b' component"));
|
||||
return NULL;
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
|
||||
typedef gint (*prepare_func)(PvComponent *obj, const gchar *tmp_path,
|
||||
void *opaque, GError **err);
|
||||
|
||||
static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||||
GError **err)
|
||||
{
|
||||
struct cipher_parms parms = { 0 };
|
||||
g_autoptr(Buffer) tweak = NULL;
|
||||
prepare_func func = NULL;
|
||||
void *opaque = NULL;
|
||||
gint rc;
|
||||
|
||||
if (img->pcf & PV_CFLAG_NO_DECRYPTION) {
|
||||
/* we only need to align the components */
|
||||
func = pv_component_align;
|
||||
opaque = NULL;
|
||||
} else {
|
||||
const EVP_CIPHER *cipher = img->xts_cipher;
|
||||
|
||||
g_assert_cmpint((int)img->xts_key->size, ==,
|
||||
EVP_CIPHER_key_length(cipher));
|
||||
g_assert_cmpint((int)PAGE_SIZE % EVP_CIPHER_block_size(cipher),
|
||||
==, 0);
|
||||
g_assert_cmpint(sizeof(comp->tweak), ==,
|
||||
EVP_CIPHER_iv_length(cipher));
|
||||
g_assert(img->xts_key->size <= UINT_MAX);
|
||||
|
||||
tweak = buffer_alloc(sizeof(comp->tweak.data));
|
||||
memcpy(tweak->data, comp->tweak.data, tweak->size);
|
||||
func = pv_component_align_and_encrypt;
|
||||
parms.cipher = cipher;
|
||||
parms.key = img->xts_key;
|
||||
parms.iv_or_tweak = tweak;
|
||||
|
||||
opaque = &parms;
|
||||
}
|
||||
|
||||
rc = (*func)(comp, img->tmp_dir, opaque, err);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) tmp_ret = NULL;
|
||||
Buffer *ret = NULL;
|
||||
gsize bytes_read;
|
||||
FILE *f = NULL;
|
||||
gsize size;
|
||||
|
||||
if (file_size(path, &size, err) != 0)
|
||||
return NULL;
|
||||
|
||||
if (size - key_size != 0) {
|
||||
g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE,
|
||||
_("Wrong file size '%s': read %zd, expected %u"), path, size,
|
||||
key_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = file_open(path, "rb", err);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
tmp_ret = buffer_alloc(size);
|
||||
if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0)
|
||||
goto err;
|
||||
|
||||
if (bytes_read - key_size != 0) {
|
||||
g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE,
|
||||
_("Wrong file size '%s': read %zd, expected %u"),
|
||||
path, bytes_read, key_size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = g_steal_pointer(&tmp_ret);
|
||||
err:
|
||||
if (f)
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static EVP_PKEY *pv_img_get_cust_pub_priv_key(gint nid, GError **err)
|
||||
{
|
||||
return generate_ec_key(nid, err);
|
||||
}
|
||||
|
||||
static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths,
|
||||
X509_STORE *store, gint nid,
|
||||
GError **err)
|
||||
{
|
||||
g_autoslist(EVP_PKEY) ret = NULL;
|
||||
|
||||
g_assert(host_cert_paths);
|
||||
|
||||
for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL;
|
||||
iterator++) {
|
||||
g_autoptr(EVP_PKEY) host_key = NULL;
|
||||
const gchar *path = *iterator;
|
||||
|
||||
g_assert(path);
|
||||
|
||||
host_key = read_ec_pubkey_cert(store, nid, path, err);
|
||||
if (!host_key)
|
||||
return NULL;
|
||||
|
||||
ret = g_slist_append(ret, g_steal_pointer(&host_key));
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
gint key_len = EVP_CIPHER_key_length(cipher);
|
||||
|
||||
g_assert(key_len > 0);
|
||||
|
||||
if (path)
|
||||
return pv_img_read_key(path, (guint)key_len, err);
|
||||
|
||||
return generate_aes_key((guint)key_len, err);
|
||||
}
|
||||
|
||||
static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
gint iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
|
||||
g_assert(iv_len > 0);
|
||||
|
||||
if (path)
|
||||
return pv_img_read_key(path, (guint)iv_len, err);
|
||||
|
||||
return generate_aes_iv((guint)iv_len, err);
|
||||
}
|
||||
|
||||
static int hex_str_toull(const gchar *nptr, uint64_t *dst,
|
||||
GError **err)
|
||||
{
|
||||
uint64_t value;
|
||||
gchar *end;
|
||||
|
||||
g_assert(dst);
|
||||
|
||||
if (!g_str_is_ascii(nptr)) {
|
||||
g_set_error(err, PV_ERROR, EINVAL,
|
||||
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
||||
nptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = g_ascii_strtoull(nptr, &end, 16);
|
||||
if ((value == G_MAXUINT64 && errno == ERANGE) ||
|
||||
(end && *end != '\0')) {
|
||||
g_set_error(err, PV_ERROR, EINVAL,
|
||||
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
||||
nptr);
|
||||
return -1;
|
||||
}
|
||||
*dst = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s,
|
||||
GError **err)
|
||||
{
|
||||
if (psw_addr_s) {
|
||||
uint64_t psw_addr;
|
||||
|
||||
if (hex_str_toull(psw_addr_s, &psw_addr, err) < 0)
|
||||
return -1;
|
||||
|
||||
img->initial_psw.addr = psw_addr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
const gchar *scf_s, GError **err)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
if (pcf_s) {
|
||||
if (hex_str_toull(pcf_s, &flags, err) < 0)
|
||||
return -1;
|
||||
|
||||
img->pcf = flags;
|
||||
}
|
||||
|
||||
if (scf_s) {
|
||||
if (hex_str_toull(scf_s, &flags, err) < 0)
|
||||
return -1;
|
||||
|
||||
img->scf = flags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read in the keys or auto-generate them */
|
||||
static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err)
|
||||
{
|
||||
g_autoptr(X509_STORE) store = NULL;
|
||||
|
||||
g_assert(img->xts_cipher);
|
||||
g_assert(img->cust_comm_cipher);
|
||||
g_assert(img->gcm_cipher);
|
||||
g_assert(img->nid);
|
||||
|
||||
img->xts_key = pv_img_get_key(img->xts_cipher, args->xts_key_path, err);
|
||||
if (!img->xts_key)
|
||||
return -1;
|
||||
|
||||
img->cust_comm_key = pv_img_get_key(img->cust_comm_cipher,
|
||||
args->cust_comm_key_path, err);
|
||||
if (!img->cust_comm_key)
|
||||
return -1;
|
||||
|
||||
img->cust_root_key =
|
||||
pv_img_get_key(img->gcm_cipher, args->cust_root_key_path, err);
|
||||
if (!img->cust_root_key)
|
||||
return -1;
|
||||
|
||||
img->gcm_iv = pv_img_get_iv(img->gcm_cipher, args->gcm_iv_path, err);
|
||||
if (!img->gcm_iv)
|
||||
return -1;
|
||||
|
||||
img->cust_pub_priv_key = pv_img_get_cust_pub_priv_key(img->nid, err);
|
||||
if (!img->cust_pub_priv_key)
|
||||
return -1;
|
||||
|
||||
img->host_pub_keys =
|
||||
pv_img_get_host_keys(args->host_keys, store, img->nid, err);
|
||||
if (!img->host_pub_keys)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pv_img_add_host_slot(PvImage *img, PvHdrKeySlot *slot)
|
||||
{
|
||||
img->key_slots = g_slist_append(img->key_slots, slot);
|
||||
}
|
||||
|
||||
static void pv_hdr_key_slot_free(PvHdrKeySlot *slot)
|
||||
{
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
g_free(slot);
|
||||
}
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free)
|
||||
|
||||
static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||||
const Buffer *cust_root_key,
|
||||
EVP_PKEY *cust_key, EVP_PKEY *host_key,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1);
|
||||
g_autofree union ecdh_pub_key *pub = NULL;
|
||||
g_autoptr(Buffer) exchange_key = NULL;
|
||||
g_autoptr(Buffer) digest_key = NULL;
|
||||
g_autoptr(Buffer) iv = NULL;
|
||||
Buffer pub_buf;
|
||||
/* No AAD data is used */
|
||||
Buffer aad = { .data = NULL, .size = 0 };
|
||||
/* Set the output buffers for the encrypted data and the
|
||||
* generated GCM tag
|
||||
*/
|
||||
Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||||
Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||||
struct cipher_parms parms;
|
||||
int64_t c_len = 0;
|
||||
|
||||
g_assert(EVP_CIPHER_iv_length(gcm_cipher) >= 0);
|
||||
|
||||
pub = evp_pkey_to_ecdh_pub_key(host_key, err);
|
||||
if (!pub)
|
||||
return NULL;
|
||||
|
||||
pub_buf.data = pub->data;
|
||||
pub_buf.size = sizeof(*pub);
|
||||
digest_key = sha256_buffer(&pub_buf, err);
|
||||
if (!digest_key)
|
||||
return NULL;
|
||||
|
||||
g_assert(digest_key->size == sizeof(ret->digest_key));
|
||||
/* set `digest_key` field */
|
||||
memcpy(ret->digest_key, digest_key->data, sizeof(ret->digest_key));
|
||||
|
||||
exchange_key = compute_exchange_key(cust_key, host_key, err);
|
||||
if (!exchange_key)
|
||||
return NULL;
|
||||
|
||||
/* initialize cipher parameters */
|
||||
g_assert(exchange_key->size <= INT_MAX);
|
||||
g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher));
|
||||
|
||||
/* create zero IV */
|
||||
iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||||
parms.iv_or_tweak = iv;
|
||||
parms.key = exchange_key;
|
||||
parms.cipher = gcm_cipher;
|
||||
|
||||
/* Encrypt the customer root key that is used for the encryption
|
||||
* of the PV header
|
||||
*/
|
||||
c_len = gcm_encrypt(cust_root_key, &aad, &parms, &enc, &tag, err);
|
||||
if (c_len < 0)
|
||||
return NULL;
|
||||
|
||||
g_assert(c_len == (int64_t)cust_root_key->size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static gint pv_img_set_host_slots(PvImage *img, GError **err)
|
||||
{
|
||||
for (GSList *iterator = img->host_pub_keys; iterator; iterator = iterator->next) {
|
||||
EVP_PKEY *host_key = iterator->data;
|
||||
|
||||
g_assert(host_key);
|
||||
|
||||
PvHdrKeySlot *slot = pv_hdr_key_slot_new(img->gcm_cipher,
|
||||
img->cust_root_key,
|
||||
img->cust_pub_priv_key,
|
||||
host_key, err);
|
||||
if (!slot)
|
||||
return -1;
|
||||
|
||||
pv_img_add_host_slot(img, slot);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint pv_img_set_comps_offset(PvImage *img, uint64_t offset, GError **err)
|
||||
{
|
||||
return pv_img_comps_set_offset(img->comps, offset, err);
|
||||
}
|
||||
|
||||
PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
||||
{
|
||||
g_autoptr(PvImage) ret = g_new0(PvImage, 1);
|
||||
uint64_t offset;
|
||||
|
||||
g_assert(args->tmp_dir);
|
||||
g_assert(stage3a_path);
|
||||
|
||||
if (args->no_verify)
|
||||
g_warning(_("host-key document verification is disabled. Your workload is not secured."));
|
||||
|
||||
ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
|
||||
if (!ret->comps)
|
||||
return NULL;
|
||||
|
||||
ret->cust_comm_cipher = EVP_aes_256_gcm();
|
||||
ret->gcm_cipher = EVP_aes_256_gcm();
|
||||
ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR;
|
||||
ret->initial_psw.mask = DEFAULT_INITIAL_PSW_MASK;
|
||||
ret->nid = NID_secp521r1;
|
||||
ret->tmp_dir = g_strdup(args->tmp_dir);
|
||||
ret->xts_cipher = EVP_aes_256_xts();
|
||||
|
||||
/* set initial PSW that will be loaded by the stage3b */
|
||||
if (pv_img_set_psw_addr(ret, args->psw_addr, err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* set the control flags: PCF and SCF */
|
||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf, err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* read in the keys */
|
||||
if (pv_img_set_keys(ret, args, err) < 0)
|
||||
return NULL;
|
||||
|
||||
if (pv_img_set_host_slots(ret, err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* allocate enough memory for the stage3a args and load the
|
||||
* stage3a template into memory and set the loader_psw
|
||||
*/
|
||||
if (pv_img_load_and_set_stage3a(ret, stage3a_path, err) < 0)
|
||||
return NULL;
|
||||
|
||||
offset = PAGE_ALIGN(STAGE3A_LOAD_ADDRESS + ret->stage3a->size);
|
||||
|
||||
/* shift right all components by the size of stage3a loader */
|
||||
if (pv_img_set_comps_offset(ret, offset, err) < 0)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void pv_img_free(PvImage *img)
|
||||
{
|
||||
if (!img)
|
||||
return;
|
||||
|
||||
g_slist_free_full(img->optional_items,
|
||||
(GDestroyNotify)pv_opt_item_free);
|
||||
g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free);
|
||||
g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free);
|
||||
EVP_PKEY_free(img->cust_pub_priv_key);
|
||||
buffer_clear(&img->stage3a);
|
||||
pv_img_comps_free(img->comps);
|
||||
g_free(img->tmp_dir);
|
||||
buffer_free(img->xts_key);
|
||||
buffer_free(img->cust_root_key);
|
||||
buffer_free(img->gcm_iv);
|
||||
buffer_free(img->cust_comm_key);
|
||||
g_free(img);
|
||||
}
|
||||
|
||||
static gint pv_img_prepare_and_add_component(PvImage *img, PvComponent **comp,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(comp);
|
||||
g_assert(*comp);
|
||||
|
||||
/* prepares the component: does the alignment and encryption
|
||||
* if required
|
||||
*/
|
||||
if (pv_img_prepare_component(img, *comp, err) < 0)
|
||||
return -1;
|
||||
|
||||
/* calculates the memory layout and adds the component to its
|
||||
* internal list
|
||||
*/
|
||||
if (pv_img_comps_add_component(img->comps, comp, err) < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(!*comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!comp)
|
||||
return -1;
|
||||
|
||||
if (pv_img_prepare_and_add_component(img, &comp, err) < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(!comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||||
Buffer **tld, uint64_t *nep, GError **err)
|
||||
{
|
||||
return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err);
|
||||
}
|
||||
|
||||
static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
||||
{
|
||||
g_autofree struct stage3b_args *args = NULL;
|
||||
|
||||
args = pv_img_comps_get_stage3b_args(img->comps, &img->initial_psw);
|
||||
if (!args) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Cannot generate stage3b arguments"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
build_stage3b(stage3b, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err)
|
||||
{
|
||||
g_autoptr(PvComponent) comp = NULL;
|
||||
g_autoptr(Buffer) stage3b = NULL;
|
||||
|
||||
stage3b = stage3b_getblob(path, err);
|
||||
if (!stage3b)
|
||||
return -1;
|
||||
|
||||
/* set the stage3b data */
|
||||
if (pv_img_build_stage3b(img, stage3b, err) < 0)
|
||||
return -1;
|
||||
|
||||
comp = pv_component_new_buf(PV_COMP_TYPE_STAGE3B, stage3b, err);
|
||||
if (!comp)
|
||||
return -1;
|
||||
|
||||
if (pv_img_prepare_and_add_component(img, &comp, err) < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(!comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t pv_img_get_aad_size(const PvImage *img)
|
||||
{
|
||||
uint32_t key_size, size = 0;
|
||||
|
||||
g_assert(sizeof(struct pv_hdr_head) <= UINT32_MAX);
|
||||
g_assert(sizeof(struct pv_hdr_key_slot) <= UINT32_MAX);
|
||||
|
||||
g_assert_true(g_uint_checked_add(&size, size,
|
||||
(uint32_t)sizeof(struct pv_hdr_head)));
|
||||
g_assert_true(g_uint_checked_mul(&key_size,
|
||||
(uint32_t)sizeof(struct pv_hdr_key_slot),
|
||||
g_slist_length(img->key_slots)));
|
||||
g_assert_true(g_uint_checked_add(&size, size, key_size));
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint32_t pv_img_get_opt_items_size(const PvImage *img)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
g_assert(img);
|
||||
|
||||
for (GSList *iterator = img->optional_items; iterator;
|
||||
iterator = iterator->next) {
|
||||
const struct pv_hdr_opt_item *item = iterator->data;
|
||||
|
||||
g_assert(item);
|
||||
g_assert_true(g_uint_checked_add(&ret, ret, pv_opt_item_size(item)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t pv_img_get_enc_size(const PvImage *img)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
g_assert(sizeof(struct pv_hdr_encrypted) <= UINT32_MAX);
|
||||
|
||||
g_assert_true(g_uint_checked_add(
|
||||
&ret, ret, (uint32_t)sizeof(struct pv_hdr_encrypted)));
|
||||
g_assert_true(
|
||||
g_uint_checked_add(&ret, ret, pv_img_get_opt_items_size(img)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t pv_img_get_tag_size(const PvImage *img G_GNUC_UNUSED)
|
||||
{
|
||||
g_assert(sizeof(((struct pv_hdr *)0)->tag) <= UINT32_MAX);
|
||||
|
||||
return (uint32_t)sizeof(((struct pv_hdr *)0)->tag);
|
||||
}
|
||||
|
||||
uint32_t pv_img_get_pv_hdr_size(const PvImage *img)
|
||||
{
|
||||
uint32_t size = 0;
|
||||
|
||||
g_assert_true(
|
||||
g_uint_checked_add(&size, size, pv_img_get_aad_size(img)));
|
||||
g_assert_true(
|
||||
g_uint_checked_add(&size, size, pv_img_get_enc_size(img)));
|
||||
g_assert_true(
|
||||
g_uint_checked_add(&size, size, pv_img_get_tag_size(img)));
|
||||
return size;
|
||||
}
|
||||
|
||||
static gint get_stage3a_data_size(const PvImage *img, gsize *data_size,
|
||||
GError **err)
|
||||
{
|
||||
gsize ipib_size, hdr_size;
|
||||
|
||||
g_assert(data_size);
|
||||
g_assert(*data_size == 0);
|
||||
|
||||
ipib_size = pv_ipib_get_size(pv_img_comps_length(img->comps));
|
||||
if (ipib_size > PV_V1_IPIB_MAX_SIZE) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE,
|
||||
_("IPIB size is too large: '%zu' > '%zu'"),
|
||||
ipib_size, PV_V1_IPIB_MAX_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr_size = pv_img_get_pv_hdr_size(img);
|
||||
if (hdr_size > PV_V1_PV_HDR_MAX_SIZE) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_PV_HDR_SIZE,
|
||||
_("PV header size is too large: '%zu' > '%zu'"),
|
||||
hdr_size, PV_V1_PV_HDR_MAX_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*data_size += PAGE_ALIGN(ipib_size);
|
||||
*data_size += PAGE_ALIGN(hdr_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) stage3a = NULL;
|
||||
gsize bin_size, data_size = 0;
|
||||
|
||||
if (get_stage3a_data_size(img, &data_size, err) < 0)
|
||||
return -1;
|
||||
|
||||
stage3a = stage3a_getblob(path, &bin_size, data_size, err);
|
||||
if (!stage3a)
|
||||
return -1;
|
||||
|
||||
img->stage3a_psw.addr = STAGE3A_ENTRY;
|
||||
img->stage3a_psw.mask = DEFAULT_INITIAL_PSW_MASK;
|
||||
|
||||
/* set addresses and size */
|
||||
img->stage3a = g_steal_pointer(&stage3a);
|
||||
img->stage3a_bin_size = bin_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Creates the PV IPIB and sets the stage3a arguments */
|
||||
static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
||||
GSList *comps, const Buffer *hdr, GError **err)
|
||||
{
|
||||
g_autofree struct ipl_parameter_block *ipib = NULL;
|
||||
|
||||
g_assert(stage3a);
|
||||
g_assert(hdr);
|
||||
|
||||
ipib = pv_ipib_new(comps, hdr, err);
|
||||
if (!ipib)
|
||||
return -1;
|
||||
|
||||
if (build_stage3a(stage3a, stage3a_bin_size, hdr, ipib, err) < 0)
|
||||
return -1;
|
||||
|
||||
g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)", "stage3a",
|
||||
STAGE3A_LOAD_ADDRESS, stage3a->size, stage3a->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Creates the actual PV header (serialized and AES-GCM encrypted) */
|
||||
static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) hdr_buf = NULL;
|
||||
g_autoptr(PvHdr) hdr = NULL;
|
||||
|
||||
hdr = pv_hdr_new(img, err);
|
||||
if (!hdr)
|
||||
return NULL;
|
||||
|
||||
hdr_buf = pv_hdr_serialize(hdr, img, PV_ENCRYPT, err);
|
||||
if (!hdr_buf)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&hdr_buf);
|
||||
}
|
||||
|
||||
/* No changes to the components are allowed after calling this
|
||||
* function
|
||||
*/
|
||||
gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) hdr = NULL;
|
||||
|
||||
/* load stage3b template into memory and add it to the list of
|
||||
* components. This must be done before calling
|
||||
* `pv_img_load_and_set_stage3a`.
|
||||
*/
|
||||
if (pv_img_add_stage3b_comp(pv, stage3b_path, err) < 0)
|
||||
return -1;
|
||||
|
||||
/* create the PV header */
|
||||
hdr = pv_img_create_pv_hdr(pv, err);
|
||||
if (!hdr)
|
||||
return -1;
|
||||
|
||||
/* generate stage3a. At this point in time the PV header and
|
||||
* the stage3b must be generated and encrypted
|
||||
*/
|
||||
if (pv_img_build_stage3a(pv->stage3a, pv->stage3a_bin_size,
|
||||
pv_img_comps_get_comps(pv->comps), hdr, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint convert_psw_to_short_psw(const struct psw_t *psw, uint64_t *dst,
|
||||
GError **err)
|
||||
{
|
||||
g_assert(psw);
|
||||
g_assert(dst);
|
||||
|
||||
uint64_t psw_addr = psw->addr;
|
||||
uint64_t psw_mask = psw->mask;
|
||||
|
||||
/* test if PSW mask can be converted */
|
||||
if (psw_mask & PSW32_ADDR_MASK) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Failed to convert PSW to short PSW"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* test for bit 12 */
|
||||
if (psw_mask & PSW_MASK_BIT_12) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Failed to convert PSW to short PSW"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* test if PSW addr can be converted */
|
||||
if (psw_addr & ~PSW32_ADDR_MASK) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Failed to convert PSW to short PSW"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*dst = psw_mask;
|
||||
/* set bit 12 to 1 */
|
||||
*dst |= PSW_MASK_BIT_12;
|
||||
*dst |= psw_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint write_short_psw(FILE *f, struct psw_t *psw, GError **err)
|
||||
{
|
||||
uint64_t short_psw, short_psw_be;
|
||||
|
||||
if (convert_psw_to_short_psw(psw, &short_psw, err) < 0)
|
||||
return -1;
|
||||
|
||||
short_psw_be = GUINT64_TO_BE(short_psw);
|
||||
return file_write(f, &short_psw_be, 1, sizeof(short_psw_be), NULL, err);
|
||||
}
|
||||
|
||||
gint pv_img_write(PvImage *img, const gchar *path, GError **err)
|
||||
{
|
||||
gint ret = -1;
|
||||
FILE *f = file_open(path, "wb", err);
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
if (write_short_psw(f, &img->stage3a_psw, err) < 0) {
|
||||
g_prefix_error(err, _("Failed to write image '%s': "), path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (seek_and_write_buffer(f, img->stage3a, STAGE3A_LOAD_ADDRESS, err) <
|
||||
0) {
|
||||
g_prefix_error(err, _("Failed to write image '%s': "), path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* list is sorted by component type => by address */
|
||||
for (GSList *iterator = pv_img_comps_get_comps(img->comps); iterator;
|
||||
iterator = iterator->next) {
|
||||
gint rc;
|
||||
const PvComponent *comp = iterator->data;
|
||||
|
||||
rc = pv_component_write(comp, f, err);
|
||||
if (rc < 0) {
|
||||
g_prefix_error(err, _("Failed to write image '%s': "),
|
||||
path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
if (f)
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
68
genprotimg/src/pv/pv_image.h
Normal file
68
genprotimg/src/pv/pv_image.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* PV image related definitions and functions
|
||||
*
|
||||
* 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 PV_IMAGE_H
|
||||
#define PV_IMAGE_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
#include "pv_args.h"
|
||||
#include "pv_comp.h"
|
||||
#include "pv_comps.h"
|
||||
#include "pv_stage3.h"
|
||||
|
||||
typedef struct {
|
||||
gchar *tmp_dir; /* directory used for temporary files */
|
||||
Buffer *stage3a; /* stage3a containing IPIB and PV header */
|
||||
gsize stage3a_bin_size; /* size of stage3a.bin */
|
||||
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
||||
* location 0 of the created image
|
||||
*/
|
||||
struct psw_t initial_psw; /* PSW loaded by stage3b */
|
||||
EVP_PKEY *cust_pub_priv_key; /* customer private/public key */
|
||||
GSList *host_pub_keys; /* public host keys */
|
||||
gint nid; /* Elliptic Curve used for the key derivation */
|
||||
/* keys and cipher used for the AES-GCM encryption */
|
||||
Buffer *cust_root_key;
|
||||
Buffer *gcm_iv;
|
||||
const EVP_CIPHER *gcm_cipher;
|
||||
/* Information for the IPIB and PV header */
|
||||
uint64_t pcf;
|
||||
uint64_t scf;
|
||||
Buffer *cust_comm_key;
|
||||
const EVP_CIPHER *cust_comm_cipher;
|
||||
Buffer *xts_key;
|
||||
const EVP_CIPHER *xts_cipher;
|
||||
GSList *key_slots;
|
||||
GSList *optional_items;
|
||||
PvImgComps *comps;
|
||||
} PvImage;
|
||||
|
||||
PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err);
|
||||
void pv_img_free(PvImage *img);
|
||||
gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err);
|
||||
gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err);
|
||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||||
Buffer **tld, uint64_t *nep, GError **err);
|
||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
||||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
||||
uint32_t pv_img_get_enc_size(const PvImage *img);
|
||||
uint32_t pv_img_get_pv_hdr_size(const PvImage *img);
|
||||
gint pv_img_write(PvImage *img, const gchar *path, GError **err);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImage, pv_img_free)
|
||||
|
||||
#endif
|
||||
128
genprotimg/src/pv/pv_ipib.c
Normal file
128
genprotimg/src/pv/pv_ipib.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* PV IPIB related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "boot/ipl.h"
|
||||
#include "boot/s390.h"
|
||||
#include "common.h"
|
||||
#include "include/pv_hdr_def.h"
|
||||
#include "lib/zt_common.h"
|
||||
#include "utils/align.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
#include "pv_comp.h"
|
||||
#include "pv_error.h"
|
||||
#include "pv_ipib.h"
|
||||
|
||||
uint64_t pv_ipib_get_size(uint32_t num_comp)
|
||||
{
|
||||
gsize ipib_size = sizeof(struct ipl_pl_hdr) +
|
||||
sizeof(struct ipl_pb0_pv) +
|
||||
num_comp * sizeof(struct ipl_pb0_pv_comp);
|
||||
|
||||
/* the minimal size is one page */
|
||||
return MAX(ipib_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||||
const Buffer *hdr)
|
||||
{
|
||||
g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX);
|
||||
g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX);
|
||||
g_assert(sizeof(struct ipl_pb0_pv) <= UINT32_MAX);
|
||||
g_assert(ipib);
|
||||
|
||||
guint comps_length = g_slist_length(comps);
|
||||
uint32_t ipl_pl_hdr_size = (uint32_t)sizeof(struct ipl_pl_hdr);
|
||||
struct ipl_pb0_pv *pv = &ipib->pv;
|
||||
uint32_t ipib_comps_size;
|
||||
uint32_t blk0_len;
|
||||
uint32_t ipib_size;
|
||||
gsize i;
|
||||
|
||||
g_assert_true(
|
||||
g_uint_checked_mul(&ipib_comps_size, comps_length,
|
||||
(uint32_t)sizeof(struct ipl_pb0_pv_comp)));
|
||||
g_assert_true(g_uint_checked_add(&blk0_len, (uint32_t)sizeof(*pv),
|
||||
ipib_comps_size));
|
||||
g_assert(ipl_pl_hdr_size + blk0_len <= PAGE_SIZE);
|
||||
|
||||
ipib_size = MAX(ipl_pl_hdr_size + blk0_len, (uint32_t)PAGE_SIZE);
|
||||
g_assert(pv_ipib_get_size(comps_length) == ipib_size);
|
||||
|
||||
pv->pbt = IPL_TYPE_PV;
|
||||
pv->len = GUINT32_TO_BE(blk0_len);
|
||||
pv->num_comp = GUINT32_TO_BE(comps_length);
|
||||
/* both values will be overwritten during the IPL process by
|
||||
* the stage3a loader
|
||||
*/
|
||||
pv->pv_hdr_addr = GUINT64_TO_BE(0x0);
|
||||
pv->pv_hdr_size = GUINT64_TO_BE(hdr->size);
|
||||
|
||||
ipib->hdr.len = GUINT32_TO_BE(ipib_size);
|
||||
ipib->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
|
||||
i = 0;
|
||||
for (GSList *iterator = comps; iterator; iterator = iterator->next, i++) {
|
||||
const PvComponent *comp = iterator->data;
|
||||
uint64_t comp_addr, comp_size;
|
||||
|
||||
g_assert(comp);
|
||||
|
||||
comp_addr = pv_component_get_src_addr(comp);
|
||||
comp_size = pv_component_size(comp);
|
||||
|
||||
g_assert(IS_PAGE_ALIGNED(comp_size));
|
||||
|
||||
pv->components[i].addr = GUINT64_TO_BE(comp_addr);
|
||||
pv->components[i].len = GUINT64_TO_BE(comp_size);
|
||||
pv->components[i].tweak_pref =
|
||||
GUINT64_TO_BE(pv_component_get_tweak_prefix(comp));
|
||||
if (i > 0) {
|
||||
/* tweak prefixes of the components must grow
|
||||
* strictly monotonous
|
||||
*/
|
||||
g_assert(GUINT64_FROM_BE(pv->components[i].tweak_pref) >
|
||||
GUINT64_FROM_BE(pv->components[i - 1].tweak_pref));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err)
|
||||
{
|
||||
uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps));
|
||||
g_autoptr(IplParameterBlock) ret = NULL;
|
||||
|
||||
if (ipib_size > PV_V1_IPIB_MAX_SIZE) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE,
|
||||
_("IPIB size is too large: %lu < %lu"), ipib_size,
|
||||
PAGE_SIZE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_malloc0(ipib_size);
|
||||
if (pv_ipib_init(ret, comps, hdr) < 0)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void pv_ipib_free(IplParameterBlock *ipib)
|
||||
{
|
||||
if (!ipib)
|
||||
return;
|
||||
|
||||
g_free(ipib);
|
||||
}
|
||||
27
genprotimg/src/pv/pv_ipib.h
Normal file
27
genprotimg/src/pv/pv_ipib.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* PV IPIB related definitions and functions
|
||||
*
|
||||
* 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 PV_IPIB_H
|
||||
#define PV_IPIB_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/ipl.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
typedef struct ipl_parameter_block IplParameterBlock;
|
||||
|
||||
uint64_t pv_ipib_get_size(uint32_t num_comp);
|
||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err);
|
||||
void pv_ipib_free(IplParameterBlock *ipib);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
||||
|
||||
#endif
|
||||
26
genprotimg/src/pv/pv_opt_item.c
Normal file
26
genprotimg/src/pv/pv_opt_item.c
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* PV optional item related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "pv_opt_item.h"
|
||||
|
||||
uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item G_GNUC_UNUSED)
|
||||
{
|
||||
/* not implemented yet */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
void pv_opt_item_free(struct pv_hdr_opt_item *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
g_free(item);
|
||||
}
|
||||
20
genprotimg/src/pv/pv_opt_item.h
Normal file
20
genprotimg/src/pv/pv_opt_item.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* PV optional item related definitions and functions
|
||||
*
|
||||
* 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 PV_OPT_ITEM_H
|
||||
#define PV_OPT_ITEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "include/pv_hdr_def.h"
|
||||
|
||||
uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item);
|
||||
void pv_opt_item_free(struct pv_hdr_opt_item *item);
|
||||
|
||||
#endif
|
||||
164
genprotimg/src/pv/pv_stage3.c
Normal file
164
genprotimg/src/pv/pv_stage3.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* PV stage3 loader related definitions and functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/ipl.h"
|
||||
#include "boot/stage3a.h"
|
||||
#include "boot/stage3b.h"
|
||||
#include "common.h"
|
||||
#include "utils/align.h"
|
||||
|
||||
#include "pv_error.h"
|
||||
#include "pv_stage3.h"
|
||||
|
||||
#define STAGE3A_ARGS(data_ptr, loader_size) \
|
||||
((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \
|
||||
sizeof(struct stage3a_args)))
|
||||
|
||||
static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize args_size, gsize data_size,
|
||||
gboolean data_aligned, GError **err)
|
||||
{
|
||||
g_autoptr(GMappedFile) mapped_file = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
gsize size, tmp_loader_size;
|
||||
gchar *loader_data;
|
||||
|
||||
g_assert(loader_size);
|
||||
|
||||
mapped_file = g_mapped_file_new(filename, FALSE, err);
|
||||
if (!mapped_file)
|
||||
return NULL;
|
||||
|
||||
loader_data = g_mapped_file_get_contents(mapped_file);
|
||||
if (!loader_data) {
|
||||
g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF,
|
||||
_("File '%s' is empty"), filename);
|
||||
return NULL;
|
||||
}
|
||||
tmp_loader_size = g_mapped_file_get_length(mapped_file);
|
||||
|
||||
if (tmp_loader_size < args_size) {
|
||||
g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF,
|
||||
_("File size less than expected: %lu < %ln"),
|
||||
tmp_loader_size, loader_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For example, the PV header and IPIB data must be page
|
||||
* aligned.
|
||||
*/
|
||||
size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) +
|
||||
data_size;
|
||||
|
||||
ret = buffer_alloc(size);
|
||||
|
||||
/* copy the loader "template" */
|
||||
memcpy(ret->data, loader_data, tmp_loader_size);
|
||||
/* reset our dummy data (offsets and length) to zeros */
|
||||
memset((uint8_t *)ret->data + tmp_loader_size - args_size, 0,
|
||||
args_size);
|
||||
*loader_size = tmp_loader_size;
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err)
|
||||
{
|
||||
return loader_getblob(filename, loader_size,
|
||||
sizeof(struct stage3a_args), data_size, TRUE,
|
||||
err);
|
||||
}
|
||||
|
||||
/* For the memory layout see stage3a.lds */
|
||||
/* Set the right offsets and sizes in the stage3a template + add
|
||||
* the IPIB block with the PV header
|
||||
*/
|
||||
static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
||||
const Buffer *hdr, struct ipl_parameter_block *ipib,
|
||||
GError **err)
|
||||
{
|
||||
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
||||
gsize args_size = sizeof(struct stage3a_args);
|
||||
uint32_t hdr_size = (uint32_t)hdr->size;
|
||||
uint64_t args_addr, next_data_addr;
|
||||
|
||||
if (hdr->size > UINT32_MAX) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Invalid header size: %zu"), hdr->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we assume here that the loader ``stage3a`` is loaded page
|
||||
* aligned in the guest
|
||||
*/
|
||||
args_addr = (uint64_t)loader->data + loader_size - args_size;
|
||||
|
||||
/* therefore `next_data_addr` is also page aligned */
|
||||
next_data_addr = (uint64_t)loader->data + PAGE_ALIGN(loader_size);
|
||||
|
||||
/* copy IPIB data */
|
||||
memcpy((void *)next_data_addr, ipib, ipib_size);
|
||||
|
||||
/* set IPIB offset in relation to the stage3a arguments */
|
||||
STAGE3A_ARGS(loader->data, loader_size)->ipib_offs =
|
||||
GUINT64_TO_BE(next_data_addr - args_addr);
|
||||
|
||||
next_data_addr = next_data_addr + PAGE_ALIGN(ipib_size);
|
||||
/* copy PV header */
|
||||
memcpy((void *)next_data_addr, hdr->data, hdr_size);
|
||||
/* set PV header size and offset in relation to the stage3a
|
||||
* arguments
|
||||
*/
|
||||
STAGE3A_ARGS(loader->data, loader_size)->hdr_offs =
|
||||
GUINT64_TO_BE(next_data_addr - args_addr);
|
||||
STAGE3A_ARGS(loader->data, loader_size)->hdr_size = GUINT64_TO_BE(hdr_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err)
|
||||
{
|
||||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
||||
}
|
||||
|
||||
Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
gsize rb_size;
|
||||
|
||||
ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0,
|
||||
FALSE, err);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
g_assert(ret->size == rb_size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args)
|
||||
{
|
||||
g_assert(stage3b->size > sizeof(*args));
|
||||
|
||||
/* at the end of the stage3b there are the stage3b args
|
||||
* positioned
|
||||
*/
|
||||
memcpy((uint8_t *)stage3b->data + stage3b->size - sizeof(*args), args,
|
||||
sizeof(*args));
|
||||
}
|
||||
|
||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size)
|
||||
{
|
||||
arg->src = GUINT64_TO_BE(src);
|
||||
arg->size = GUINT64_TO_BE(size);
|
||||
}
|
||||
30
genprotimg/src/pv/pv_stage3.h
Normal file
30
genprotimg/src/pv/pv_stage3.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* PV stage3 loader related definitions and functions
|
||||
*
|
||||
* 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 PV_STAGE3_H
|
||||
#define PV_STAGE3_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot/ipl.h"
|
||||
#include "boot/s390.h"
|
||||
#include "boot/stage3b.h"
|
||||
#include "utils/buffer.h"
|
||||
|
||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||
gsize data_size, GError **err);
|
||||
gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr,
|
||||
struct ipl_parameter_block *ipib, GError **err);
|
||||
Buffer *stage3b_getblob(const gchar *filename, GError **err);
|
||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args);
|
||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
||||
|
||||
#endif
|
||||
24
genprotimg/src/utils/align.h
Normal file
24
genprotimg/src/utils/align.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Alignment 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.
|
||||
*/
|
||||
|
||||
#ifndef PV_UTILS_ALIGN_H
|
||||
#define PV_UTILS_ALIGN_H
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define IS_ALIGNED(addr, size) (!(addr & (size - 1)))
|
||||
|
||||
/* align addr to the next page boundary */
|
||||
#define PAGE_ALIGN(addr) ALIGN((unsigned long)addr, PAGE_SIZE)
|
||||
|
||||
/* test whether an address is aligned to PAGE_SIZE or not */
|
||||
#define IS_PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)(addr), PAGE_SIZE)
|
||||
|
||||
#endif
|
||||
69
genprotimg/src/utils/buffer.c
Normal file
69
genprotimg/src/utils/buffer.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Buffer functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "align.h"
|
||||
#include "buffer.h"
|
||||
#include "common.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
Buffer *buffer_alloc(gsize size)
|
||||
{
|
||||
Buffer *ret = g_new0(Buffer, 1);
|
||||
|
||||
ret->data = g_malloc0(size);
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
||||
{
|
||||
Buffer *ret;
|
||||
gsize size;
|
||||
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
size = buf->size;
|
||||
if (page_aligned)
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
ret = buffer_alloc(size);
|
||||
|
||||
/* content will be 0-right-padded */
|
||||
memcpy(ret->data, buf->data, buf->size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err)
|
||||
{
|
||||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
||||
}
|
||||
|
||||
void buffer_free(Buffer *buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
g_free(buf->data);
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
void buffer_clear(Buffer **buf)
|
||||
{
|
||||
if (!buf || !*buf)
|
||||
return;
|
||||
|
||||
buffer_free(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
31
genprotimg/src/utils/buffer.h
Normal file
31
genprotimg/src/utils/buffer.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Buffer definition and functions
|
||||
*
|
||||
* 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 PV_UTILS_BUFFER_H
|
||||
#define PV_UTILS_BUFFER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct Buffer {
|
||||
void *data;
|
||||
gsize size; /* in bytes */
|
||||
} Buffer;
|
||||
|
||||
Buffer *buffer_alloc(gsize size);
|
||||
void buffer_free(Buffer *buf);
|
||||
void buffer_clear(Buffer **buf);
|
||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err);
|
||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned);
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free)
|
||||
|
||||
#endif
|
||||
798
genprotimg/src/utils/crypto.c
Normal file
798
genprotimg/src/utils/crypto.c
Normal file
@@ -0,0 +1,798 @@
|
||||
/*
|
||||
* General cryptography helper functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "common.h"
|
||||
#include "include/pv_crypto_def.h"
|
||||
#include "pv/pv_error.h"
|
||||
|
||||
#include "buffer.h"
|
||||
#include "crypto.h"
|
||||
|
||||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new();
|
||||
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestInit_ex failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||||
{
|
||||
gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
guint digest_size;
|
||||
|
||||
g_assert(md_size > 0);
|
||||
|
||||
ret = buffer_alloc((guint)md_size);
|
||||
if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestFinal_ex failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert(digest_size == (guint)md_size);
|
||||
g_assert(digest_size == ret->size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* Returns the digest of @buf using the hash algorithm @md */
|
||||
static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_MD_CTX) md_ctx = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_assert(buf);
|
||||
|
||||
md_ctx = digest_ctx_new(md, err);
|
||||
if (!md_ctx)
|
||||
return NULL;
|
||||
|
||||
if (EVP_DigestUpdate(md_ctx, buf->data, buf->size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = digest_ctx_finalize(md_ctx, err);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* Returns the SHA256 digest of @buf */
|
||||
Buffer *sha256_buffer(const Buffer *buf, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
|
||||
ret = digest_buffer(EVP_sha256(), buf, err);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
g_assert(ret->size == SHA256_DIGEST_LENGTH);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* Convert a EVP_PKEY to the key format used in the PV header */
|
||||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err)
|
||||
{
|
||||
g_autofree union ecdh_pub_key *ret = g_new0(union ecdh_pub_key, 1);
|
||||
g_autoptr(BIGNUM) pub_x_big = NULL;
|
||||
g_autoptr(BIGNUM) pub_y_big = NULL;
|
||||
g_autoptr(EC_KEY) ec_key = NULL;
|
||||
const EC_POINT *pub_key;
|
||||
const EC_GROUP *grp;
|
||||
|
||||
ec_key = EVP_PKEY_get1_EC_KEY(key);
|
||||
if (!ec_key) {
|
||||
g_set_error(err, 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(err, 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(err, 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(err, 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(err, 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(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Cannot convert key to internal format"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
gsize key_size;
|
||||
|
||||
ctx = EVP_PKEY_CTX_new(cust, NULL);
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
if (EVP_PKEY_derive_init(ctx) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_set_peer(ctx, host) != 1) {
|
||||
g_set_error(err, 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(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = buffer_alloc(key_size);
|
||||
if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||
_("Key derivation failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert(ret->size == key_size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) raw = buffer_alloc(70);
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(Buffer) key = NULL;
|
||||
guchar *data;
|
||||
|
||||
key = derive_key(cust, host, err);
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
g_assert(key->size == 66);
|
||||
g_assert(key->size < raw->size);
|
||||
|
||||
/* ANSI X.9.63-2011: 66 bytes x with leading 7 bits and
|
||||
* concatenate 32 bit int '1'
|
||||
*/
|
||||
memcpy(raw->data, key->data, key->size);
|
||||
data = raw->data;
|
||||
data[66] = 0x00;
|
||||
data[67] = 0x00;
|
||||
data[68] = 0x00;
|
||||
data[69] = 0x01;
|
||||
|
||||
ret = sha256_buffer(raw, err);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err)
|
||||
{
|
||||
tweak->cmp_idx.idx = GUINT16_TO_BE(i);
|
||||
if (RAND_bytes(tweak->cmp_idx.rand, sizeof(tweak->cmp_idx.rand)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
_("Generating a tweak failed because the required amount of random data is not available"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) buf = buffer_alloc(size);
|
||||
|
||||
g_assert(size <= INT_MAX);
|
||||
|
||||
if (RAND_bytes(buf->data, (int)size) != 1) {
|
||||
g_set_error_literal(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
err_msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&buf);
|
||||
}
|
||||
|
||||
Buffer *generate_aes_iv(guint size, GError **err)
|
||||
{
|
||||
return generate_rand_data(size,
|
||||
_("Generating a IV failed because the required amount of random data is not available"),
|
||||
err);
|
||||
}
|
||||
|
||||
Buffer *generate_aes_key(guint size, GError **err)
|
||||
{
|
||||
return generate_rand_data(size,
|
||||
_("Generating a key failed because the required amount of random data is not available"),
|
||||
err);
|
||||
}
|
||||
|
||||
EVP_PKEY *generate_ec_key(gint nid, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
|
||||
g_autoptr(EVP_PKEY) ret = NULL;
|
||||
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
if (EVP_PKEY_keygen_init(ctx) != 1) {
|
||||
g_set_error(err, 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(err, 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(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
_("EC key could not be auto-generated"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static gboolean certificate_uses_correct_curve(EVP_PKEY *key, gint nid,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(EC_KEY) ec = NULL;
|
||||
gint rc;
|
||||
|
||||
g_assert(key);
|
||||
|
||||
if (EVP_PKEY_id(key) != EVP_PKEY_EC) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("No EC key found"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ec = EVP_PKEY_get1_EC_KEY(key);
|
||||
if (!ec) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("No EC key found"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (EC_KEY_check_key(ec) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("Invalid EC key"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
|
||||
if (rc != nid) {
|
||||
/* maybe the NID is unset */
|
||||
if (rc == 0) {
|
||||
g_autoptr(EC_GROUP) grp = EC_GROUP_new_by_curve_name(nid);
|
||||
const EC_POINT *pub = EC_KEY_get0_public_key(ec);
|
||||
g_autoptr(BN_CTX) ctx = BN_CTX_new();
|
||||
|
||||
if (EC_POINT_is_on_curve(grp, pub, ctx) != 1) {
|
||||
g_set_error_literal(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("Invalid EC curve"));
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
/* NID was set but doesn't match with the expected NID
|
||||
*/
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("Wrong NID used: '%d'"),
|
||||
EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err)
|
||||
{
|
||||
g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new();
|
||||
if (!csc)
|
||||
g_abort();
|
||||
|
||||
if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT,
|
||||
_("Failed to initialize X.509 store"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (X509_verify_cert(csc) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION,
|
||||
_("Failed to verify host-key document"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static X509 *load_certificate(const gchar *path, GError **err)
|
||||
{
|
||||
g_autoptr(X509) ret = NULL;
|
||||
g_autoptr(BIO) bio = BIO_new_file(path, "rb");
|
||||
|
||||
if (!bio) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
_("Failed to read host-key document: '%s'"), path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = PEM_read_bio_X509(bio, NULL, 0, NULL);
|
||||
if (!ret) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
_("Failed to load host-key document: '%s'"), path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(EVP_PKEY) ret = NULL;
|
||||
g_autoptr(X509) cert = NULL;
|
||||
|
||||
cert = load_certificate(path, err);
|
||||
if (!cert)
|
||||
return NULL;
|
||||
|
||||
if (store && !verify_certificate(store, cert, err)) {
|
||||
g_prefix_error(err,
|
||||
_("Failed to load host-key document: '%s': "),
|
||||
path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = X509_get_pubkey(cert);
|
||||
if (!ret) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM,
|
||||
_("Failed to get public key from host-key document: '%s'"),
|
||||
path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!certificate_uses_correct_curve(ret, nid, err)) {
|
||||
g_prefix_error(err,
|
||||
_("Failed to load host-key document: '%s': "),
|
||||
path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||||
BIO *b_out, gsize *size_in, gsize *size_out,
|
||||
gboolean encrypt, GError **err)
|
||||
{
|
||||
gint num_bytes_read, num_bytes_written;
|
||||
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
||||
g_autoptr(BIGNUM) tweak_num = NULL;
|
||||
const EVP_CIPHER *cipher = parms->cipher;
|
||||
gint cipher_block_size = EVP_CIPHER_block_size(cipher);
|
||||
guchar in_buf[PAGE_SIZE],
|
||||
out_buf[PAGE_SIZE + (guint)cipher_block_size];
|
||||
const Buffer *key = parms->key;
|
||||
const Buffer *tweak = parms->iv_or_tweak;
|
||||
g_autofree guchar *tmp_tweak = NULL;
|
||||
gint out_len, tweak_size;
|
||||
gsize tmp_size_in = 0, tmp_size_out = 0;
|
||||
|
||||
g_assert(cipher_block_size > 0);
|
||||
g_assert(key);
|
||||
g_assert(tweak);
|
||||
g_assert(tweak->size <= INT_MAX);
|
||||
|
||||
/* copy the value for leaving the original value untouched */
|
||||
tmp_tweak = g_malloc0(tweak->size);
|
||||
memcpy(tmp_tweak, tweak->data, tweak->size);
|
||||
tweak_size = (int)tweak->size;
|
||||
tweak_num = BN_bin2bn(tmp_tweak, tweak_size, NULL);
|
||||
if (!tweak_num) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bin2bn failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
/* don't set the key or tweak right away as we want to check
|
||||
* lengths before
|
||||
*/
|
||||
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherInit_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now we can set the key and tweak */
|
||||
if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, tmp_tweak, encrypt) !=
|
||||
1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherInit_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
memset(in_buf, 0, sizeof(in_buf));
|
||||
/* Read in data in 4096 bytes blocks. Update the ciphering
|
||||
* with each read.
|
||||
*/
|
||||
num_bytes_read = BIO_read(b_in, in_buf, (int)PAGE_SIZE);
|
||||
if (num_bytes_read < 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to read"));
|
||||
return -1;
|
||||
}
|
||||
tmp_size_in += (guint)num_bytes_read;
|
||||
|
||||
/* in case we reached the end and it's not the special
|
||||
* case of an empty component we can break here
|
||||
*/
|
||||
if (num_bytes_read == 0 && tmp_size_in != 0)
|
||||
break;
|
||||
|
||||
if (EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf,
|
||||
sizeof(in_buf)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(out_len >= 0);
|
||||
|
||||
num_bytes_written = BIO_write(b_out, out_buf, out_len);
|
||||
if (num_bytes_written < 0) {
|
||||
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
|
||||
* tweaks are stored in big-endian form. Therefore we
|
||||
* must use the correct OpenSSL functions
|
||||
*/
|
||||
if (BN_add_word(tweak_num, PAGE_SIZE) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_add_word failed"));
|
||||
}
|
||||
g_assert(BN_num_bytes(tweak_num) > 0);
|
||||
g_assert(BN_num_bytes(tweak_num) <= tweak_size);
|
||||
|
||||
if (BN_bn2binpad(tweak_num, tmp_tweak, tweak_size) < 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("BN_bn2binpad failed"));
|
||||
};
|
||||
|
||||
/* set new tweak */
|
||||
if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, tmp_tweak,
|
||||
encrypt) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherInit_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
} while (num_bytes_read == PAGE_SIZE);
|
||||
|
||||
/* Now cipher the final block and write it out to file */
|
||||
if (EVP_CipherFinal_ex(ctx, out_buf, &out_len) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherFinal_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(out_len >= 0);
|
||||
|
||||
num_bytes_written = BIO_write(b_out, out_buf, out_len);
|
||||
if (num_bytes_written < 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to write"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(out_len == num_bytes_written);
|
||||
tmp_size_out += (guint)out_len;
|
||||
|
||||
if (BIO_flush(b_out) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Failed to flush"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*size_in = tmp_size_in;
|
||||
*size_out = tmp_size_out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||||
const Buffer *in, gboolean encrypt,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(Buffer) ret = NULL;
|
||||
g_autoptr(BIO) b_out = NULL;
|
||||
g_autoptr(BIO) b_in = NULL;
|
||||
gsize in_size, out_size;
|
||||
gchar *data = NULL;
|
||||
long data_size;
|
||||
|
||||
g_assert(in->size <= INT_MAX);
|
||||
|
||||
b_in = BIO_new_mem_buf(in->data, (int)in->size);
|
||||
if (!b_in)
|
||||
g_abort();
|
||||
|
||||
b_out = BIO_new(BIO_s_mem());
|
||||
if (!b_out)
|
||||
g_abort();
|
||||
|
||||
if (__encrypt_decrypt_bio(parms, b_in, b_out, &in_size, &out_size,
|
||||
encrypt, err) < 0)
|
||||
return NULL;
|
||||
|
||||
data_size = BIO_get_mem_data(b_out, &data);
|
||||
if (data_size < 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Could not read buffer"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = buffer_alloc((unsigned long)data_size);
|
||||
memcpy(ret->data, data, ret->size);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_buffer(parms, in, TRUE, err);
|
||||
}
|
||||
|
||||
Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_buffer(parms, in, FALSE, err);
|
||||
}
|
||||
|
||||
static gint __encrypt_decrypt_file(const struct cipher_parms *parms,
|
||||
const gchar *path_in, const gchar *path_out,
|
||||
gsize *size_in, gsize *size_out, gboolean encrypt,
|
||||
GError **err)
|
||||
{
|
||||
g_autoptr(BIO) b_out = NULL;
|
||||
g_autoptr(BIO) b_in = NULL;
|
||||
|
||||
b_in = BIO_new_file(path_in, "rb");
|
||||
if (!b_in) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
_("Failed to read file '%s'"), path_in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
b_out = BIO_new_file(path_out, "wb");
|
||||
if (!b_out) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||
_("Failed to write file '%s'"), path_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (__encrypt_decrypt_bio(parms, b_in, b_out, size_in, size_out,
|
||||
encrypt, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint encrypt_file(const struct cipher_parms *parms, const gchar *path_in,
|
||||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_file(parms, path_in, path_out, in_size,
|
||||
out_size, TRUE, err);
|
||||
}
|
||||
|
||||
G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms,
|
||||
const gchar *path_in, const gchar *path_out,
|
||||
gsize *in_size, gsize *out_size,
|
||||
GError **err)
|
||||
{
|
||||
return __encrypt_decrypt_file(parms, path_in, path_out, in_size,
|
||||
out_size, FALSE, err);
|
||||
}
|
||||
|
||||
/* GCM mode uses (zero-)padding */
|
||||
static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
||||
const struct cipher_parms *parms,
|
||||
Buffer *out, Buffer *tag,
|
||||
enum PvCryptoMode mode, GError **err)
|
||||
{
|
||||
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
||||
const EVP_CIPHER *cipher = parms->cipher;
|
||||
const Buffer *iv = parms->iv_or_tweak;
|
||||
gboolean encrypt = mode == PV_ENCRYPT;
|
||||
const Buffer *key = parms->key;
|
||||
int64_t ret = -1;
|
||||
gint len = -1;
|
||||
|
||||
g_assert(cipher);
|
||||
g_assert(key);
|
||||
g_assert(iv);
|
||||
/* Checks for later casts */
|
||||
g_assert(aad->size <= INT_MAX);
|
||||
g_assert(in->size <= INT_MAX);
|
||||
g_assert(tag->size <= INT_MAX);
|
||||
g_assert(iv->size <= INT_MAX);
|
||||
g_assert(out->size == in->size);
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (!ctx)
|
||||
g_abort();
|
||||
|
||||
/* First, set the cipher algorithm so we can verify our key/IV lengths
|
||||
*/
|
||||
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CIPHER_CTX_new failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set IV length */
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv->size, NULL) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CIPHER_CTX_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialise key and IV */
|
||||
if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, iv->data, encrypt) !=
|
||||
1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherInit_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (aad->size > 0) {
|
||||
/* Provide any AAD data */
|
||||
if (EVP_CipherUpdate(ctx, NULL, &len, aad->data,
|
||||
(int)aad->size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
g_assert(len == (int)aad->size);
|
||||
}
|
||||
|
||||
/* Provide data to be en/decrypted */
|
||||
if (EVP_CipherUpdate(ctx, out->data, &len, in->data, (int)in->size) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherUpdate failed"));
|
||||
return -1;
|
||||
}
|
||||
ret = len;
|
||||
|
||||
if (!encrypt) {
|
||||
/* Set expected tag value */
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
|
||||
(int)tag->size, tag->data) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Setting the GCM tag failed"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the en/decryption */
|
||||
if (EVP_CipherFinal_ex(ctx, (guchar *)out->data + len, &len) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_CipherFinal_ex failed"));
|
||||
return -1;
|
||||
}
|
||||
ret += len;
|
||||
|
||||
if (encrypt) {
|
||||
/* Get the tag */
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
|
||||
(int)tag->size, tag->data) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("Getting the GCM tag failed"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert(ret == (int)in->size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||||
const struct cipher_parms *parms, Buffer *out, Buffer *tag,
|
||||
GError **err)
|
||||
{
|
||||
return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err);
|
||||
}
|
||||
104
genprotimg/src/utils/crypto.h
Normal file
104
genprotimg/src/utils/crypto.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* General cryptography helper functions and 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 PV_UTILS_CRYPTO_H
|
||||
#define PV_UTILS_CRYPTO_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdh.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "include/pv_crypto_def.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#define AES_256_GCM_IV_SIZE 12
|
||||
#define AES_256_GCM_TAG_SIZE 16
|
||||
|
||||
#define AES_256_XTS_TWEAK_SIZE 16
|
||||
#define AES_256_XTS_KEY_SIZE 64
|
||||
|
||||
enum PvCryptoMode {
|
||||
PV_ENCRYPT,
|
||||
PV_DECRYPT,
|
||||
};
|
||||
|
||||
typedef GSList HostKeyList;
|
||||
|
||||
/* Register auto cleanup functions */
|
||||
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)
|
||||
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_MD_CTX, EVP_MD_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)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_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)
|
||||
|
||||
union cmp_index {
|
||||
struct {
|
||||
uint16_t idx;
|
||||
guchar rand[6];
|
||||
} __packed;
|
||||
uint64_t data;
|
||||
};
|
||||
|
||||
/* The tweak is always stored in big endian format */
|
||||
union tweak {
|
||||
struct {
|
||||
union cmp_index cmp_idx;
|
||||
uint64_t page_idx; /* page index */
|
||||
} __packed;
|
||||
uint8_t data[AES_256_XTS_TWEAK_SIZE];
|
||||
};
|
||||
|
||||
struct cipher_parms {
|
||||
const EVP_CIPHER *cipher;
|
||||
const Buffer *key;
|
||||
const Buffer *iv_or_tweak;
|
||||
};
|
||||
|
||||
EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path,
|
||||
GError **err);
|
||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||||
Buffer *generate_aes_key(guint size, GError **err);
|
||||
Buffer *generate_aes_iv(guint size, GError **err);
|
||||
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
||||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
||||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
||||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||||
Buffer *sha256_buffer(const Buffer *buf, GError **err);
|
||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||||
const struct cipher_parms *parms, Buffer *out,
|
||||
Buffer *tag, GError **err);
|
||||
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
||||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
||||
GError **err);
|
||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||||
GError **err);
|
||||
G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms,
|
||||
const Buffer *in, GError **err);
|
||||
|
||||
#endif
|
||||
234
genprotimg/src/utils/file_utils.c
Normal file
234
genprotimg/src/utils/file_utils.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* General file 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pv/pv_error.h"
|
||||
|
||||
#include "align.h"
|
||||
#include "buffer.h"
|
||||
#include "common.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
FILE *file_open(const gchar *filename, const gchar *mode, GError **err)
|
||||
{
|
||||
FILE *f = fopen(filename, mode);
|
||||
|
||||
if (!f) {
|
||||
g_set_error(err, G_FILE_ERROR,
|
||||
(gint)g_file_error_from_errno(errno),
|
||||
_("Failed to open file '%s': %s"), filename,
|
||||
g_strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
gint file_size(const gchar *filename, gsize *size, GError **err)
|
||||
{
|
||||
GStatBuf st_buf;
|
||||
|
||||
g_assert(size);
|
||||
|
||||
if (g_stat(filename, &st_buf) != 0) {
|
||||
g_set_error(err, G_FILE_ERROR,
|
||||
(gint)g_file_error_from_errno(errno),
|
||||
_("Failed to get file status '%s': %s"), filename,
|
||||
g_strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st_buf.st_mode)) {
|
||||
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
||||
_("File '%s' is not a regular file"), filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (st_buf.st_size < 0) {
|
||||
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
||||
_("Invalid file size for '%s': %zu"), filename,
|
||||
st_buf.st_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*size = (gsize)st_buf.st_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0 on success, otherwise -1. Stores the total number of
|
||||
* elements successfully read in @count_read
|
||||
*/
|
||||
gint file_read(FILE *in, void *ptr, gsize size, gsize count,
|
||||
gsize *count_read, GError **err)
|
||||
{
|
||||
gsize tmp_count_read;
|
||||
|
||||
tmp_count_read = fread(ptr, size, count, in);
|
||||
if (count_read)
|
||||
*count_read = tmp_count_read;
|
||||
|
||||
if (ferror(in)) {
|
||||
g_set_error(err, G_FILE_ERROR, 0, _("Failed to read file"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint file_write(FILE *out, const void *ptr, gsize size, gsize count,
|
||||
gsize *count_written, GError **err)
|
||||
{
|
||||
gsize tmp_count_written;
|
||||
|
||||
tmp_count_written = fwrite(ptr, size, count, out);
|
||||
if (count_written)
|
||||
*count_written = tmp_count_written;
|
||||
|
||||
if (tmp_count_written != count || ferror(out)) {
|
||||
g_set_error(err, G_FILE_ERROR, 0, _("Failed to write file"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint file_seek(FILE *f, uint64_t offset, GError **err)
|
||||
{
|
||||
gint rc;
|
||||
|
||||
if (offset > LONG_MAX) {
|
||||
g_set_error(err, PV_ERROR, 0, _("Offset is too large"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = fseek(f, (long)offset, SEEK_SET);
|
||||
if (rc != 0) {
|
||||
g_set_error(err, G_FILE_ERROR,
|
||||
(gint)g_file_error_from_errno(errno),
|
||||
_("Failed to seek: '%s'"), g_strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||||
GError **err)
|
||||
{
|
||||
gsize bytes_read, bytes_written;
|
||||
gsize total_bytes_read = 0;
|
||||
FILE *i = NULL;
|
||||
gchar buf[4096];
|
||||
gint ret = -1;
|
||||
|
||||
if (file_seek(o, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
i = file_open(ifile->path, "rb", err);
|
||||
if (!i)
|
||||
return -1;
|
||||
|
||||
do {
|
||||
if (file_read(i, buf, 1, sizeof(buf), &bytes_read, err) < 0) {
|
||||
g_prefix_error(err, _("Failed to read file '%s': "),
|
||||
ifile->path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
|
||||
total_bytes_read += bytes_read;
|
||||
|
||||
if (file_write(o, buf, bytes_read, 1, &bytes_written, err) < 0)
|
||||
goto err;
|
||||
} while (bytes_written != 0);
|
||||
|
||||
if (ifile->size != total_bytes_read) {
|
||||
g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL,
|
||||
_("'%s' has changed during the preparation"),
|
||||
ifile->path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
fclose(i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset,
|
||||
GError **err)
|
||||
{
|
||||
if (file_seek(o, offset, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (buffer_write(buf, o, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint pad_file_right(const gchar *path_out, const gchar *path_in, gsize *size_out,
|
||||
guint padding, GError **err)
|
||||
{
|
||||
FILE *f_in, *f_out = NULL;
|
||||
guchar buf[padding];
|
||||
gsize num_bytes_written;
|
||||
gsize num_bytes_read;
|
||||
uint64_t size_in = 0;
|
||||
gint ret = -1;
|
||||
|
||||
*size_out = 0;
|
||||
f_in = file_open(path_in, "rb", err);
|
||||
if (!f_in)
|
||||
goto err;
|
||||
|
||||
f_out = file_open(path_out, "wb", err);
|
||||
if (!f_out)
|
||||
goto err;
|
||||
|
||||
do {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (file_read(f_in, buf, 1, sizeof(buf), &num_bytes_read, err) < 0) {
|
||||
g_prefix_error(err, _("Failed to read file '%s': "),
|
||||
path_in);
|
||||
goto err;
|
||||
}
|
||||
|
||||
size_in += num_bytes_read;
|
||||
|
||||
if (file_write(f_out, buf, 1, sizeof(buf), &num_bytes_written, err)) {
|
||||
g_prefix_error(err, _("Failed to write file '%s': "),
|
||||
path_out);
|
||||
goto err;
|
||||
}
|
||||
|
||||
*size_out += num_bytes_written;
|
||||
} while (num_bytes_read == padding);
|
||||
|
||||
g_assert(num_bytes_written == ALIGN(num_bytes_read, padding));
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
if (f_out)
|
||||
fclose(f_out);
|
||||
if (f_in)
|
||||
fclose(f_in);
|
||||
return ret;
|
||||
}
|
||||
34
genprotimg/src/utils/file_utils.h
Normal file
34
genprotimg/src/utils/file_utils.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* General file 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.
|
||||
*/
|
||||
|
||||
#ifndef PV_FILE_UTILS_H
|
||||
#define PV_FILE_UTILS_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pv/pv_comp.h"
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
FILE *file_open(const gchar *filename, const gchar *mode, GError **err);
|
||||
gint file_size(const gchar *filename, gsize *size, GError **err);
|
||||
gint file_read(FILE *in, void *ptr, gsize size, gsize count,
|
||||
gsize *count_read, GError **err);
|
||||
gint file_write(FILE *out, const void *ptr, gsize size, gsize count,
|
||||
gsize *count_written, GError **err);
|
||||
gint pad_file_right(const gchar *path_out, const gchar *path_in,
|
||||
gsize *size_out, guint padding, GError **err);
|
||||
gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset,
|
||||
GError **err);
|
||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||||
GError **err);
|
||||
|
||||
#endif
|
||||
191
include/boot/ipl.h
Normal file
191
include/boot/ipl.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* IPL related 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 IPL_H
|
||||
#define IPL_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "s390.h"
|
||||
|
||||
#define IPL_FLAG_SECURE 0x40
|
||||
|
||||
#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80
|
||||
#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40
|
||||
|
||||
#define IPL_MAX_SUPPORTED_VERSION 0
|
||||
#define IPL_PARM_BLOCK_VERSION 0x1
|
||||
|
||||
/* IPL Types */
|
||||
#define IPL_TYPE_PV 0x5
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* IPL Parameter List header */
|
||||
struct ipl_pl_hdr {
|
||||
uint32_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t version;
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block header */
|
||||
struct ipl_pb_hdr {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 with common fields */
|
||||
struct ipl_pb0_common {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[84];
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 for FCP */
|
||||
struct ipl_pb0_fcp {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t reserved1[3];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[304];
|
||||
uint8_t opt;
|
||||
uint8_t reserved3[3];
|
||||
uint8_t cssid;
|
||||
uint8_t reserved4[1];
|
||||
uint8_t devno;
|
||||
uint8_t reserved5[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved6[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved7[260];
|
||||
uint8_t scp_data[];
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 for CCW */
|
||||
struct ipl_pb0_ccw {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[84];
|
||||
uint16_t reserved3 : 13;
|
||||
uint8_t ssid : 3;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved4[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved5[8];
|
||||
} __packed;
|
||||
|
||||
/* Structure must not have any padding */
|
||||
struct ipl_pb0_pv_comp {
|
||||
uint64_t tweak_pref;
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct ipl_pb0_pv_comp) == 3 * 8)
|
||||
|
||||
/* IPL Parameter Block 0 for PV */
|
||||
struct ipl_pb0_pv {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t reserved1[3];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[84];
|
||||
uint8_t reserved3[3];
|
||||
uint8_t version;
|
||||
uint8_t reserved4[4];
|
||||
uint32_t num_comp;
|
||||
uint64_t pv_hdr_addr;
|
||||
uint64_t pv_hdr_size;
|
||||
struct ipl_pb0_pv_comp components[];
|
||||
} __packed;
|
||||
|
||||
struct ipl_parameter_block {
|
||||
struct ipl_pl_hdr hdr;
|
||||
union {
|
||||
struct ipl_pb_hdr pb0_hdr;
|
||||
struct ipl_pb0_common common;
|
||||
struct ipl_pb0_fcp fcp;
|
||||
struct ipl_pb0_ccw ccw;
|
||||
struct ipl_pb0_pv pv;
|
||||
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* IPL Report List header */
|
||||
struct ipl_rl_hdr {
|
||||
uint32_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t version;
|
||||
uint8_t reserved2[8];
|
||||
} __packed;
|
||||
|
||||
/* IPL Report Block header */
|
||||
/* Structure must not have any padding */
|
||||
struct ipl_rb_hdr {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct ipl_rb_hdr) == 4 + 1 + 11)
|
||||
|
||||
/* IPL Report Block types */
|
||||
enum ipl_rbt {
|
||||
IPL_RBT_CERTIFICATES = 1,
|
||||
IPL_RBT_COMPONENTS = 2,
|
||||
};
|
||||
|
||||
/* IPL Report Block for the certificate list */
|
||||
struct ipl_rb_certificate_entry {
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
} __packed;
|
||||
|
||||
struct ipl_rb_certificates {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
struct ipl_rb_certificate_entry entries[];
|
||||
} __packed;
|
||||
|
||||
/* IPL Report Block for the component list */
|
||||
struct ipl_rb_component_entry {
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[5];
|
||||
uint16_t certificate_index;
|
||||
uint8_t reserved2[8];
|
||||
};
|
||||
|
||||
/* Structure must not have any padding */
|
||||
struct ipl_rb_components {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
struct ipl_rb_component_entry entries[];
|
||||
};
|
||||
STATIC_ASSERT(sizeof(struct ipl_rb_components) == 4 + 1 + 11)
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* IPL_H */
|
||||
34
include/boot/linux_layout.h
Normal file
34
include/boot/linux_layout.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* s390 Linux layout 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 LINUX_LAYOUT_H
|
||||
#define LINUX_LAYOUT_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/* Entry address offsets */
|
||||
#define IMAGE_ENTRY _AC(0x10000, UL)
|
||||
#define IMAGE_ENTRY_KDUMP _AC(0x10010, UL)
|
||||
|
||||
/* Parameter address offsets */
|
||||
#define PARMAREA _AC(0x10400, UL)
|
||||
#define IPL_DEVICE _AC(0x10400, UL)
|
||||
#define INITRD_START _AC(0x10408, UL)
|
||||
#define INITRD_SIZE _AC(0x10410, UL)
|
||||
#define OLDMEM_BASE _AC(0x10418, UL)
|
||||
#define OLDMEM_SIZE _AC(0x10420, UL)
|
||||
#define COMMAND_LINE _AC(0x10480, UL)
|
||||
|
||||
/* Parameter sizes */
|
||||
#define COMMAND_LINE_SIZE 896
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* LINUX_LAYOUT_H */
|
||||
43
include/boot/loaders_layout.h
Normal file
43
include/boot/loaders_layout.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* zipl stage2/stage3 layout 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 LOADERS_LAYOUT_H
|
||||
#define LOADERS_LAYOUT_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "linux_layout.h"
|
||||
|
||||
#define STAGE2_DESC _AC(0x78, UL)
|
||||
#define STAGE2_ENTRY _AC(0x2018, UL)
|
||||
#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL)
|
||||
#define STAGE2_HEAP_SIZE _AC(0x3000, UL)
|
||||
#define STAGE2_STACK_ADDRESS _AC(0xe400, UL)
|
||||
#define STAGE2_STACK_SIZE _AC(0x1c00, UL)
|
||||
|
||||
#define STAGE3_ENTRY _AC(0xa000, UL)
|
||||
|
||||
#define STAGE2_LOAD_ADDRESS _AC(0x2000, UL)
|
||||
#define STAGE3_LOAD_ADDRESS STAGE3_ENTRY
|
||||
#define IMAGE_LOAD_ADDRESS IMAGE_ENTRY
|
||||
|
||||
#define STAGE3_MAXIMUM_SIZE _AC(0x3000, UL)
|
||||
#define STAGE3_HEAP_SIZE _AC(0x4000, UL)
|
||||
#define STAGE3_HEAP_ADDRESS _AC(0x2000, UL)
|
||||
#define STAGE3_STACK_SIZE _AC(0x1000, UL)
|
||||
#define STAGE3_STACK_ADDRESS _AC(0xF000, UL)
|
||||
#define STAGE3_PARAMS_ADDRESS _AC(0x9000, UL)
|
||||
#define STAGE3_PARAMS_MAXIMUM_SIZE _AC(0x1000, UL)
|
||||
|
||||
#define COMMAND_LINE_EXTRA _AC(0xE000, UL)
|
||||
#define COMMAND_LINE_EXTRA_SIZE _AC(0x0400, UL)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* LOADERS_LAYOUT_H */
|
||||
@@ -1,24 +1,38 @@
|
||||
/*
|
||||
* zipl - zSeries Initial Program Loader tool
|
||||
* s390 related definitions and functions.
|
||||
*
|
||||
* System z specific functions
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2017
|
||||
* Copyright IBM Corp. 2013, 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 S390_H
|
||||
#define S390_H
|
||||
|
||||
#include "../../include/lib/zt_common.h"
|
||||
#include "libc.h"
|
||||
#include "lib/zt_common.h"
|
||||
#include "boot/sigp.h"
|
||||
|
||||
#define __pa32(x) ((uint32_t)(unsigned long)(x))
|
||||
#define __pa(x) ((unsigned long)(x))
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define barrier() __asm__ __volatile__("": : :"memory")
|
||||
#define inline inline __attribute__((always_inline))
|
||||
#define __LC_IPLDEV 0x0c6c
|
||||
#define __LC_OS_INFO 0x0e18
|
||||
|
||||
#define PAGE_SIZE _AC(4096, UL)
|
||||
|
||||
/* Minimum size of a stack frame in bytes */
|
||||
#define STACK_FRAME_OVERHEAD _AC(160, U)
|
||||
|
||||
/* Facilities */
|
||||
#define UNPACK_FACILITY _AC(161, U)
|
||||
|
||||
#define PSW32_ADDR_MASK _AC(0x000000007fffffff, UL)
|
||||
#define PSW_MASK_BA _AC(0x0000000080000000, UL)
|
||||
#define PSW_MASK_EA _AC(0x0000000100000000, UL)
|
||||
#define PSW_MASK_BIT_12 _AC(0x0008000000000000, UL)
|
||||
#define PSW_LOAD _AC(0x0008000080000000, UL)
|
||||
#define PSW_DISABLED_WAIT _AC(0x000a000000000000, UL)
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
/*
|
||||
* Helper macro for exception table entries
|
||||
@@ -199,21 +213,8 @@ struct _lowcore {
|
||||
|
||||
#define S390_lowcore (*((struct _lowcore *) 0))
|
||||
|
||||
#define __LC_IPLDEV 0x0c6c
|
||||
#define __LC_OS_INFO 0x0e18
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
void panic_notify(unsigned long reason);
|
||||
|
||||
#define panic(reason, x...) \
|
||||
do { \
|
||||
printf(x); \
|
||||
panic_notify(reason); \
|
||||
libc_stop(reason); \
|
||||
} while (0)
|
||||
|
||||
static inline int page_is_valid(unsigned long addr)
|
||||
static __always_inline int page_is_valid(unsigned long addr)
|
||||
{
|
||||
unsigned long tmp;
|
||||
int rc;
|
||||
@@ -233,7 +234,7 @@ static inline int page_is_valid(unsigned long addr)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline uint32_t csum_partial(const void *buf, int len, uint32_t sum)
|
||||
static __always_inline uint32_t csum_partial(const void *buf, int len, uint32_t sum)
|
||||
{
|
||||
register unsigned long reg2 asm("2") = (unsigned long) buf;
|
||||
register unsigned long reg3 asm("3") = (unsigned long) len;
|
||||
@@ -261,7 +262,7 @@ static inline uint32_t csum_partial(const void *buf, int len, uint32_t sum)
|
||||
"i" (low), "i" (high)); \
|
||||
})
|
||||
|
||||
static inline void __ctl_set_bit(unsigned int cr, unsigned int bit)
|
||||
static __always_inline void __ctl_set_bit(unsigned int cr, unsigned int bit)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
@@ -274,14 +275,21 @@ static inline void __ctl_set_bit(unsigned int cr, unsigned int bit)
|
||||
* DIAG 308 support
|
||||
*/
|
||||
enum diag308_subcode {
|
||||
DIAG308_REL_HSA = 2,
|
||||
DIAG308_IPL = 3,
|
||||
DIAG308_DUMP = 4,
|
||||
DIAG308_SET = 5,
|
||||
DIAG308_STORE = 6,
|
||||
DIAG308_REL_HSA = 2,
|
||||
DIAG308_IPL = 3,
|
||||
DIAG308_DUMP = 4,
|
||||
DIAG308_SET = 5,
|
||||
DIAG308_STORE = 6,
|
||||
DIAG308_SET_PV = 8,
|
||||
DIAG308_UNPACK_PV = 10,
|
||||
};
|
||||
|
||||
static inline int diag308(unsigned long subcode, void *addr)
|
||||
enum diag308_rc {
|
||||
DIAG308_RC_OK = 0x0001,
|
||||
DIAG308_RC_NO_CONF = 0x0102,
|
||||
};
|
||||
|
||||
static __always_inline unsigned long diag308(unsigned long subcode, void *addr)
|
||||
{
|
||||
register unsigned long _addr asm("0") = (unsigned long) addr;
|
||||
register unsigned long _rc asm("1") = 0;
|
||||
@@ -294,47 +302,10 @@ static inline int diag308(unsigned long subcode, void *addr)
|
||||
return _rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal Processor
|
||||
*/
|
||||
#define SIGP_STOP_AND_STORE_STATUS 9
|
||||
#define SIGP_SET_MULTI_THREADING 22
|
||||
#define SIGP_STORE_ASTATUS_AT_ADDRESS 23
|
||||
|
||||
#define SIGP_CC_ORDER_CODE_ACCEPTED 0
|
||||
#define SIGP_CC_BUSY 2
|
||||
|
||||
static inline int sigp(uint16_t addr, uint8_t order, uint32_t parm,
|
||||
uint32_t *status)
|
||||
{
|
||||
register unsigned int reg1 asm ("1") = parm;
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" sigp %1,%2,0(%3)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc");
|
||||
if (status && cc == 1)
|
||||
*status = reg1;
|
||||
return cc;
|
||||
}
|
||||
|
||||
static inline int sigp_busy(uint16_t addr, uint8_t order, uint32_t parm,
|
||||
uint32_t *status)
|
||||
{
|
||||
int cc;
|
||||
|
||||
do {
|
||||
cc = sigp(addr, order, parm, status);
|
||||
} while (cc == SIGP_CC_BUSY);
|
||||
return cc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store CPU address
|
||||
*/
|
||||
static inline unsigned short stap(void)
|
||||
static __always_inline unsigned short stap(void)
|
||||
{
|
||||
unsigned short cpu_address;
|
||||
|
||||
@@ -345,7 +316,7 @@ static inline unsigned short stap(void)
|
||||
/*
|
||||
* Program the clock comparator
|
||||
*/
|
||||
static inline void set_clock_comparator(uint64_t time)
|
||||
static __always_inline void set_clock_comparator(uint64_t time)
|
||||
{
|
||||
asm volatile("sckc %0" : : "Q" (time));
|
||||
}
|
||||
@@ -353,7 +324,7 @@ static inline void set_clock_comparator(uint64_t time)
|
||||
/*
|
||||
* Program the CPU timer
|
||||
*/
|
||||
static inline void set_cpu_timer(uint64_t timer)
|
||||
static __always_inline void set_cpu_timer(uint64_t timer)
|
||||
{
|
||||
asm volatile("spt %0" : : "Q" (timer));
|
||||
}
|
||||
@@ -361,7 +332,7 @@ static inline void set_cpu_timer(uint64_t timer)
|
||||
/*
|
||||
* Get current time (store clock)
|
||||
*/
|
||||
static inline unsigned long long get_tod_clock(void)
|
||||
static __always_inline unsigned long long get_tod_clock(void)
|
||||
{
|
||||
unsigned long long clk;
|
||||
|
||||
@@ -379,7 +350,7 @@ struct cpuid {
|
||||
unsigned int unused:16;
|
||||
} __packed __aligned(8);
|
||||
|
||||
static inline void get_cpu_id(struct cpuid *ptr)
|
||||
static __always_inline void get_cpu_id(struct cpuid *ptr)
|
||||
{
|
||||
asm volatile("stidp %0" : "=Q" (*ptr));
|
||||
}
|
||||
@@ -387,7 +358,7 @@ static inline void get_cpu_id(struct cpuid *ptr)
|
||||
/*
|
||||
* Check if we run under z/VM
|
||||
*/
|
||||
static inline int is_zvm(void)
|
||||
static __always_inline int is_zvm(void)
|
||||
{
|
||||
struct cpuid cpuid;
|
||||
|
||||
@@ -395,17 +366,22 @@ static inline int is_zvm(void)
|
||||
return cpuid.version == 0xff;
|
||||
}
|
||||
|
||||
/* To avoid conflicts add a macro guard since __vector128 is also
|
||||
* defined in 'linux/asm/types.h'.
|
||||
*/
|
||||
#ifndef _S390_TYPES_H
|
||||
/*
|
||||
* Vector register definition
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t u[4];
|
||||
} __vector128;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Save vector registers
|
||||
*/
|
||||
static inline void save_vx_regs(__vector128 *vxrs)
|
||||
static __always_inline void save_vx_regs(__vector128 *vxrs)
|
||||
{
|
||||
typedef struct { __vector128 _[32]; } addrtype;
|
||||
|
||||
@@ -419,7 +395,7 @@ static inline void save_vx_regs(__vector128 *vxrs)
|
||||
/*
|
||||
* Save vector registers safe
|
||||
*/
|
||||
static inline void save_vx_regs_safe(__vector128 *vxrs)
|
||||
static __always_inline void save_vx_regs_safe(__vector128 *vxrs)
|
||||
{
|
||||
unsigned long cr0;
|
||||
|
||||
@@ -432,7 +408,7 @@ static inline void save_vx_regs_safe(__vector128 *vxrs)
|
||||
|
||||
#define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */
|
||||
|
||||
static inline int __test_facility(unsigned long nr, void *facilities)
|
||||
static __always_inline int __test_facility(unsigned long nr, void *facilities)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
|
||||
@@ -447,12 +423,12 @@ static inline int __test_facility(unsigned long nr, void *facilities)
|
||||
* That makes it easier to query facility bits with the bit number as
|
||||
* documented in the Principles of Operation.
|
||||
*/
|
||||
static inline int test_facility(unsigned long nr)
|
||||
static __always_inline int test_facility(unsigned long nr)
|
||||
{
|
||||
return __test_facility(nr, &S390_lowcore.stfle_fac_list);
|
||||
}
|
||||
|
||||
static inline unsigned long __stfle_asm(u64 *stfle_fac_list, int size)
|
||||
static __always_inline unsigned long __stfle_asm(uint64_t *stfle_fac_list, unsigned int size)
|
||||
{
|
||||
register unsigned long reg0 asm("0") = size - 1;
|
||||
|
||||
@@ -469,7 +445,7 @@ static inline unsigned long __stfle_asm(u64 *stfle_fac_list, int size)
|
||||
* @stfle_fac_list: array where facility list can be stored
|
||||
* @size: size of passed in array in double words
|
||||
*/
|
||||
static inline void stfle(u64 *stfle_fac_list, int size)
|
||||
static __always_inline void stfle(uint64_t *stfle_fac_list, unsigned int size)
|
||||
{
|
||||
unsigned long nr;
|
||||
|
||||
@@ -488,4 +464,5 @@ static inline void stfle(u64 *stfle_fac_list, int size)
|
||||
memset((char *) stfle_fac_list + nr, 0, size * 8 - nr);
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* S390_H */
|
||||
56
include/boot/sigp.h
Normal file
56
include/boot/sigp.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SIGP related definitions and functions.
|
||||
*
|
||||
* 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 S390_SIGP_H
|
||||
#define S390_SIGP_H
|
||||
|
||||
/* Signal Processor Order Codes */
|
||||
#define SIGP_STOP_AND_STORE_STATUS 9
|
||||
#define SIGP_SET_ARCHITECTURE 18
|
||||
#define SIGP_SET_MULTI_THREADING 22
|
||||
#define SIGP_STORE_ASTATUS_AT_ADDRESS 23
|
||||
|
||||
/* Signal Processor Condition Codes */
|
||||
#define SIGP_CC_ORDER_CODE_ACCEPTED 0
|
||||
#define SIGP_CC_BUSY 2
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline int sigp(uint16_t addr, uint8_t order, uint32_t parm,
|
||||
uint32_t *status)
|
||||
{
|
||||
register unsigned int reg1 asm ("1") = parm;
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" sigp %1,%2,0(%3)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc");
|
||||
if (status && cc == 1)
|
||||
*status = reg1;
|
||||
return cc;
|
||||
}
|
||||
|
||||
static inline int sigp_busy(uint16_t addr, uint8_t order, uint32_t parm,
|
||||
uint32_t *status)
|
||||
{
|
||||
int cc;
|
||||
|
||||
do {
|
||||
cc = sigp(addr, order, parm, status);
|
||||
} while (cc == SIGP_CC_BUSY);
|
||||
return cc;
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* S390_SIGP_H */
|
||||
1071
include/ekmfweb/ekmfweb.h
Normal file
1071
include/ekmfweb/ekmfweb.h
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user