mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
522 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aca6704cfa | ||
|
|
3a85d1ebe6 | ||
|
|
e8f9244224 | ||
|
|
a49887de1f | ||
|
|
4e61b4c9f4 | ||
|
|
d884cc27c1 | ||
|
|
bf3810ea6a | ||
|
|
99d7ec51a7 | ||
|
|
327d132aac | ||
|
|
fcb89662e0 | ||
|
|
a52243ea04 | ||
|
|
805a4d4408 | ||
|
|
b1e4f6f331 | ||
|
|
10d72caac0 | ||
|
|
0ea693d95c | ||
|
|
0ff130ed84 | ||
|
|
8a57017af3 | ||
|
|
41ec030db1 | ||
|
|
34aff761d0 | ||
|
|
01cb8c4085 | ||
|
|
8d58a9abb0 | ||
|
|
ce82010827 | ||
|
|
c0d8034962 | ||
|
|
95290562e0 | ||
|
|
638cbbe332 | ||
|
|
70157462d8 | ||
|
|
40bc181f31 | ||
|
|
a2eb03660d | ||
|
|
f4c4c40b78 | ||
|
|
fe78faa8ae | ||
|
|
617a8248e8 | ||
|
|
c5695e43c4 | ||
|
|
7b3add9389 | ||
|
|
44fdf586e1 | ||
|
|
0d6e63abdc | ||
|
|
1771e7f2f3 | ||
|
|
c403208332 | ||
|
|
2a99007a6e | ||
|
|
994269c278 | ||
|
|
ae5687f1d1 | ||
|
|
b856ae62a7 | ||
|
|
7b4be02441 | ||
|
|
2403d1e166 | ||
|
|
3f03ceb007 | ||
|
|
150ae08678 | ||
|
|
43a10f4806 | ||
|
|
4e08c5fcd8 | ||
|
|
6532bf02c2 | ||
|
|
3f7b459606 | ||
|
|
14830b1ead | ||
|
|
1dae9b4f49 | ||
|
|
b58414d7dc | ||
|
|
64524f17fa | ||
|
|
1b87b016d4 | ||
|
|
94bdb17d01 | ||
|
|
2625cd704f | ||
|
|
59df2c6d02 | ||
|
|
8ba03642b7 | ||
|
|
1956e7abc5 | ||
|
|
d76cb1d53a | ||
|
|
7d5f2ba038 | ||
|
|
8bbf1d5bea | ||
|
|
b2fcb608d5 | ||
|
|
76b7928592 | ||
|
|
339b69cf3b | ||
|
|
419f042f55 | ||
|
|
128e5f86fe | ||
|
|
b29e824923 | ||
|
|
3175d52ce7 | ||
|
|
17024c02af | ||
|
|
5cd680c0be | ||
|
|
c333f91d4c | ||
|
|
095e5ee226 | ||
|
|
6ee5ffef91 | ||
|
|
82f8c137e1 | ||
|
|
b21a824fda | ||
|
|
f74b8d7d6f | ||
|
|
b766d4a53c | ||
|
|
a1126352ec | ||
|
|
35e539a428 | ||
|
|
99100b3f99 | ||
|
|
49faa1d7fd | ||
|
|
86ce85b3a6 | ||
|
|
b1c1bea4e2 | ||
|
|
e68547f06f | ||
|
|
95d784d866 | ||
|
|
783ae9735e | ||
|
|
5a78c7f45d | ||
|
|
586040a0ad | ||
|
|
deb61b5cd9 | ||
|
|
8be51ad454 | ||
|
|
cef50c2140 | ||
|
|
33bd112e2f | ||
|
|
2c31ed6f14 | ||
|
|
771623832b | ||
|
|
b8060baf44 | ||
|
|
76a0401deb | ||
|
|
e6ac87d7a2 | ||
|
|
0a711d4934 | ||
|
|
f90e913806 | ||
|
|
94a7ec22f2 | ||
|
|
940ce67900 | ||
|
|
c12faf0128 | ||
|
|
a3d50487ed | ||
|
|
b1d8e40bfe | ||
|
|
8d3949f77b | ||
|
|
2f49025d49 | ||
|
|
4c9ceeeb64 | ||
|
|
3a0c394fa2 | ||
|
|
20a4ebd83c | ||
|
|
4a9f66fc31 | ||
|
|
35d68b2101 | ||
|
|
522c67a2c3 | ||
|
|
f4ee6c2c8d | ||
|
|
377f2a59f5 | ||
|
|
176f0405b8 | ||
|
|
839d34e758 | ||
|
|
a68d1e323b | ||
|
|
549c7ef9e7 | ||
|
|
103c9444c9 | ||
|
|
fe7bef3f09 | ||
|
|
8a88ada9c5 | ||
|
|
538bc7b4e8 | ||
|
|
b56451e5a4 | ||
|
|
63f9a672d0 | ||
|
|
2c5c5ef4b4 | ||
|
|
5a3ffc2036 | ||
|
|
e576443859 | ||
|
|
43067df78e | ||
|
|
973059438f | ||
|
|
9d3567d9ce | ||
|
|
7687b0fcda | ||
|
|
6f70f34b79 | ||
|
|
37aed81745 | ||
|
|
5aa1824eef | ||
|
|
82603e589a | ||
|
|
3ba897a1be | ||
|
|
fa92a7f7c9 | ||
|
|
f569849e57 | ||
|
|
458c27a0e1 | ||
|
|
e4dcf084c5 | ||
|
|
bed1bc8676 | ||
|
|
8e315c4401 | ||
|
|
ae8fa4371f | ||
|
|
29ff9408e0 | ||
|
|
a24be25779 | ||
|
|
58337d7f90 | ||
|
|
fcaad5a8e2 | ||
|
|
4d598ade86 | ||
|
|
b455ee9a31 | ||
|
|
82b9328e48 | ||
|
|
f45a45c38c | ||
|
|
d1bf9e68c6 | ||
|
|
0356d78bc2 | ||
|
|
a4f48415a0 | ||
|
|
c9f0e3accf | ||
|
|
9dd432ca94 | ||
|
|
179bc57cdf | ||
|
|
e1d1c6df9c | ||
|
|
e894fb61d8 | ||
|
|
c3a84109b7 | ||
|
|
1ca5f63dee | ||
|
|
60bc1e38d0 | ||
|
|
431e4542ca | ||
|
|
731f00202c | ||
|
|
2945593991 | ||
|
|
f375327085 | ||
|
|
338bea49c2 | ||
|
|
d15e36cf24 | ||
|
|
4b76530788 | ||
|
|
cb1c002bed | ||
|
|
d6f7eff5b5 | ||
|
|
e1692b7079 | ||
|
|
54b3c9ef24 | ||
|
|
48743aa28b | ||
|
|
7ce988ac59 | ||
|
|
f4fec5f120 | ||
|
|
2739b8a488 | ||
|
|
ce9c518b97 | ||
|
|
bd2a2cc6a1 | ||
|
|
6547d1ae4c | ||
|
|
650265e296 | ||
|
|
b122acb422 | ||
|
|
9710f29770 | ||
|
|
ed01bac8e2 | ||
|
|
66d3aa849a | ||
|
|
95e4b3413a | ||
|
|
06e0d569f1 | ||
|
|
660bab6e68 | ||
|
|
3dd91bb84e | ||
|
|
8fadcd9fe5 | ||
|
|
e8237e6ae0 | ||
|
|
c746c5c267 | ||
|
|
b69b750ef5 | ||
|
|
120ad30f1e | ||
|
|
bfc9854eb5 | ||
|
|
619c466275 | ||
|
|
017377c093 | ||
|
|
7e38c7106b | ||
|
|
8dc06d14d7 | ||
|
|
f07504d92e | ||
|
|
02554fa6d8 | ||
|
|
3eb09e8842 | ||
|
|
3eb9711021 | ||
|
|
2349236bb4 | ||
|
|
2d86117167 | ||
|
|
a0265d1069 | ||
|
|
084792bdc6 | ||
|
|
5ece6a3373 | ||
|
|
14c977768e | ||
|
|
5bedcff65c | ||
|
|
479820528c | ||
|
|
486882a4df | ||
|
|
ce997c6d01 | ||
|
|
b9d1c86613 | ||
|
|
f9337f0576 | ||
|
|
a06302e7f0 | ||
|
|
7bc0fb809d | ||
|
|
bc511f84fe | ||
|
|
3a3cea0061 | ||
|
|
616d2f52f9 | ||
|
|
6dd671f24e | ||
|
|
443652dcd2 | ||
|
|
b11bb64732 | ||
|
|
2cd7c7a875 | ||
|
|
8929d21948 | ||
|
|
f7bba3a687 | ||
|
|
5d2c93bc7a | ||
|
|
f1055a62e3 | ||
|
|
4c4ab4fd70 | ||
|
|
34a5e47508 | ||
|
|
6d7cdb9e10 | ||
|
|
1fefdecbe7 | ||
|
|
e62cdf9a6e | ||
|
|
fa00d1eac1 | ||
|
|
0eac97542d | ||
|
|
b3db52fd16 | ||
|
|
a9eb80b1a2 | ||
|
|
e93668ae5b | ||
|
|
c68c3cb8b2 | ||
|
|
f63c548c2a | ||
|
|
1ab2fbb482 | ||
|
|
fed79474e4 | ||
|
|
8c1ffd8989 | ||
|
|
33fc534a81 | ||
|
|
7e832ead9e | ||
|
|
7f0f765bb4 | ||
|
|
69c7f45bb7 | ||
|
|
d843e08828 | ||
|
|
055c455571 | ||
|
|
ba387af804 | ||
|
|
8b9960abe9 | ||
|
|
efdadfcdb9 | ||
|
|
ec10654c35 | ||
|
|
76bddf9715 | ||
|
|
b4cc30ac10 | ||
|
|
9f9dddddd4 | ||
|
|
f821a3c174 | ||
|
|
ee4cb78dc2 | ||
|
|
6f82c5792a | ||
|
|
2c5d4073a7 | ||
|
|
9efd1df31d | ||
|
|
6f53d7d3cd | ||
|
|
92486dfcaf | ||
|
|
29007541e8 | ||
|
|
2278f112ba | ||
|
|
c39722aff2 | ||
|
|
1e8edb6524 | ||
|
|
bd5f03d1c4 | ||
|
|
f8ccd56bf1 | ||
|
|
72218c5bc3 | ||
|
|
b94566048b | ||
|
|
85a232a711 | ||
|
|
931c61f141 | ||
|
|
253ca8a30e | ||
|
|
829fc3b294 | ||
|
|
3c5fb0f885 | ||
|
|
9df45d4256 | ||
|
|
283ae4dd2f | ||
|
|
718907d9ba | ||
|
|
26cf3ec769 | ||
|
|
7150da3e4f | ||
|
|
f946ce27f7 | ||
|
|
4942504a9a | ||
|
|
e1245ed4e5 | ||
|
|
272eb5ad8c | ||
|
|
543d41cb59 | ||
|
|
8619f14c0a | ||
|
|
56aa3e2d26 | ||
|
|
a9fbfcc7ae | ||
|
|
a32adbf5d8 | ||
|
|
f9050799bb | ||
|
|
66a10d5e3e | ||
|
|
152f446d76 | ||
|
|
646e080217 | ||
|
|
3fd8c48245 | ||
|
|
c93b74b527 | ||
|
|
4fe6025b18 | ||
|
|
73b6a684ec | ||
|
|
40d9df5cc8 | ||
|
|
9ebb8da624 | ||
|
|
589ce29d41 | ||
|
|
b343672eb8 | ||
|
|
df47036f01 | ||
|
|
5febec5f44 | ||
|
|
84f44dd20f | ||
|
|
d86dd0658f | ||
|
|
00c36baee0 | ||
|
|
a40519fd16 | ||
|
|
b836b4fb83 | ||
|
|
1ab819cd81 | ||
|
|
ee50f20c11 | ||
|
|
0f17fe5183 | ||
|
|
650e2da843 | ||
|
|
426311f440 | ||
|
|
5333085c55 | ||
|
|
4c301d47dd | ||
|
|
aea2ba8728 | ||
|
|
594a554e56 | ||
|
|
a0c3a74019 | ||
|
|
efdd34a56c | ||
|
|
c80a0c8af0 | ||
|
|
d98a10f67f | ||
|
|
fb6e7e70c6 | ||
|
|
e49569db60 | ||
|
|
b3055154db | ||
|
|
6718c0edc9 | ||
|
|
28d9405f73 | ||
|
|
05f0aeb61e | ||
|
|
6004a7029c | ||
|
|
16add4a4ae | ||
|
|
ef752d1f45 | ||
|
|
793c037ded | ||
|
|
9f476217f5 | ||
|
|
49111d613b | ||
|
|
26d2e2d786 | ||
|
|
7bd159de1d | ||
|
|
605680d6fd | ||
|
|
f1a479a794 | ||
|
|
fa2d68e80c | ||
|
|
e1730585e9 | ||
|
|
ef55a5d677 | ||
|
|
bc4b7475f7 | ||
|
|
abcb93aacb | ||
|
|
b09a9b3490 | ||
|
|
caaf2b2116 | ||
|
|
382ec368fc | ||
|
|
9dd96144d4 | ||
|
|
efd41ca435 | ||
|
|
7bc12d0202 | ||
|
|
b49cd13d16 | ||
|
|
56e32dfcbb | ||
|
|
833a8e7309 | ||
|
|
a8eb2bd4e7 | ||
|
|
5276d408fd | ||
|
|
95bf7eb285 | ||
|
|
fdf66dc148 | ||
|
|
5ce79ea667 | ||
|
|
8c4b2872b8 | ||
|
|
7be364aa84 | ||
|
|
43858063ca | ||
|
|
739761f0d2 | ||
|
|
a8a3e7d49c | ||
|
|
ff04f76257 | ||
|
|
93216d916c | ||
|
|
256289a30a | ||
|
|
93da795520 | ||
|
|
a14f9d4edc | ||
|
|
fd024387d7 | ||
|
|
4af137f4fa | ||
|
|
01cd81ecf5 | ||
|
|
69eb06f39e | ||
|
|
d1636168b2 | ||
|
|
1e44ace41d | ||
|
|
a9b8caa411 | ||
|
|
6e48c5ebaa | ||
|
|
944581eaef | ||
|
|
3f6572e901 | ||
|
|
6520070a46 | ||
|
|
3b8fdcc892 | ||
|
|
352e2fe5d3 | ||
|
|
37b7b68f1b | ||
|
|
3db20bccef | ||
|
|
560b276f7e | ||
|
|
a19c43f613 | ||
|
|
5b6d7a467d | ||
|
|
b1fdbac5f9 | ||
|
|
cf51ac7860 | ||
|
|
cf5560a100 | ||
|
|
dff965465c | ||
|
|
79de37bbc8 | ||
|
|
e5c2fba0a8 | ||
|
|
04267470b1 | ||
|
|
cf7060ea08 | ||
|
|
f340a96fdf | ||
|
|
798bd752cf | ||
|
|
638f0aa4f9 | ||
|
|
d3b7581669 | ||
|
|
0cd063e40d | ||
|
|
4d97e15eb0 | ||
|
|
a32fe8641a | ||
|
|
c34adb9cab | ||
|
|
86b5df0d2d | ||
|
|
e0caed5534 | ||
|
|
8704c9c80e | ||
|
|
4ec33d0b76 | ||
|
|
3a4c20b443 | ||
|
|
c54a915a90 | ||
|
|
bc269ddb38 | ||
|
|
8c21f09570 | ||
|
|
4df0781989 | ||
|
|
195579cf0b | ||
|
|
849819cb77 | ||
|
|
72aa64495c | ||
|
|
d2de7f2808 | ||
|
|
f4cf4ae6eb | ||
|
|
f524b0b8dc | ||
|
|
f99719a695 | ||
|
|
7d81c1bbd4 | ||
|
|
2ed2c608ab | ||
|
|
d73f4dc22a | ||
|
|
ea297ecf84 | ||
|
|
7d41430e68 | ||
|
|
f095a892b9 | ||
|
|
646a389b53 | ||
|
|
66ef6b5945 | ||
|
|
6c75a06b12 | ||
|
|
b1ca60f5ba | ||
|
|
3d83100fa3 | ||
|
|
8c23e09a60 | ||
|
|
24e285ce02 | ||
|
|
ad709c1eaf | ||
|
|
b61596327b | ||
|
|
de18a72249 | ||
|
|
482bd93e2e | ||
|
|
026ecbafea | ||
|
|
40846bce49 | ||
|
|
b9879cbfc7 | ||
|
|
e61ed4f433 | ||
|
|
589090f79a | ||
|
|
57eef7c412 | ||
|
|
d864c55336 | ||
|
|
1b18ba3a03 | ||
|
|
938fe2a744 | ||
|
|
171432852b | ||
|
|
7608cf2de4 | ||
|
|
e480c4738d | ||
|
|
4cd7e8fd9a | ||
|
|
f1b94abefa | ||
|
|
5a54722848 | ||
|
|
cb0e119bed | ||
|
|
081a055115 | ||
|
|
6e27c2f2e9 | ||
|
|
d1d0bd39ba | ||
|
|
8d0c620257 | ||
|
|
d1884db236 | ||
|
|
73f211ecb1 | ||
|
|
8d2290977b | ||
|
|
25aac53d52 | ||
|
|
6b75568750 | ||
|
|
7cc131880c | ||
|
|
26465e37d7 | ||
|
|
697dcc0f6b | ||
|
|
ac7f866237 | ||
|
|
339ad5c2ed | ||
|
|
48f1e0da0a | ||
|
|
413adbe7bb | ||
|
|
667a8d714b | ||
|
|
2155b83c1f | ||
|
|
e6c17461a3 | ||
|
|
f68d3932c7 | ||
|
|
81f3845f34 | ||
|
|
75820cf62d | ||
|
|
3f0dc6b3af | ||
|
|
db6c500e6d | ||
|
|
578b77072a | ||
|
|
f94e9e9352 | ||
|
|
079ae1e24d | ||
|
|
2f3c189fda | ||
|
|
dec796d52f | ||
|
|
385ba6b51f | ||
|
|
050441922b | ||
|
|
567cbce8a8 | ||
|
|
eee29e0399 | ||
|
|
a0a8aa47a8 | ||
|
|
fe2946f76c | ||
|
|
17aba9322e | ||
|
|
53d803abf3 | ||
|
|
576a230341 | ||
|
|
bdaef6e76d | ||
|
|
e6c73c9974 | ||
|
|
d4316c155c | ||
|
|
a4f4337060 | ||
|
|
32dbfc4712 | ||
|
|
63142e3051 | ||
|
|
464d4d2842 | ||
|
|
b378ff788e | ||
|
|
20f1c0e2ea | ||
|
|
54e175a783 | ||
|
|
30dc1a5ddd | ||
|
|
47460e6e33 | ||
|
|
6fc13a0ff4 | ||
|
|
5751c08627 | ||
|
|
5e97205530 | ||
|
|
0495947604 | ||
|
|
516bd8c2cf | ||
|
|
d1b5f80fe5 | ||
|
|
e2ee24b3ac | ||
|
|
f23c207622 | ||
|
|
649f1bc084 | ||
|
|
17e575e350 | ||
|
|
57a32d98ef | ||
|
|
03340afe88 | ||
|
|
e7cef378e0 | ||
|
|
8da2f44ae2 | ||
|
|
a4a29d1e05 | ||
|
|
89f475cee1 | ||
|
|
96dbabae8d | ||
|
|
d30d272523 | ||
|
|
300f8d23b5 | ||
|
|
b7ac46b98b | ||
|
|
e91ad1b70e |
@@ -10,3 +10,4 @@
|
|||||||
--ignore FILE_PATH_CHANGES
|
--ignore FILE_PATH_CHANGES
|
||||||
--ignore NEW_TYPEDEFS
|
--ignore NEW_TYPEDEFS
|
||||||
--ignore SPDX_LICENSE_TAG
|
--ignore SPDX_LICENSE_TAG
|
||||||
|
--ignore STRNCPY
|
||||||
|
|||||||
37
.clang-tidy
Normal file
37
.clang-tidy
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
Checks: >
|
||||||
|
bugprone-*,
|
||||||
|
clang-analyzer-*,
|
||||||
|
misc-*,
|
||||||
|
performance-*,
|
||||||
|
portability-*,
|
||||||
|
readability-*,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
|
-misc-include-cleaner,
|
||||||
|
-readability-braces-around-statements,
|
||||||
|
-readability-identifier-length,
|
||||||
|
-readability-isolate-declaration,
|
||||||
|
-modernize-*,
|
||||||
|
-performance-*,
|
||||||
|
-portability-*,
|
||||||
|
-readability-implicit-bool-conversion
|
||||||
|
|
||||||
|
CheckOptions:
|
||||||
|
- key: readability-identifier-naming.VariableCase
|
||||||
|
value: lower_case
|
||||||
|
- key: readability-identifier-naming.FunctionCase
|
||||||
|
value: lower_case
|
||||||
|
- key: readability-identifier-naming.StructCase
|
||||||
|
value: lower_case
|
||||||
|
- key: readability-identifier-naming.EnumCase
|
||||||
|
value: lower_case
|
||||||
|
- key: readability-identifier-naming.EnumConstantCase
|
||||||
|
value: UPPER_CASE
|
||||||
|
- key: readability-identifier-naming.MacroDefinitionCase
|
||||||
|
value: UPPER_CASE
|
||||||
|
- key: bugprone-sizeof-expression.WarnOnSizeOfPointer
|
||||||
|
value: true
|
||||||
|
- key: misc-unused-parameters.StrictMode
|
||||||
|
value: true
|
||||||
|
|
||||||
|
FormatStyle: file
|
||||||
|
UseColor: true
|
||||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -25,6 +25,7 @@ compile_commands.json
|
|||||||
#
|
#
|
||||||
**/.detect-openssl.dep.c
|
**/.detect-openssl.dep.c
|
||||||
*.debug
|
*.debug
|
||||||
|
*/autocompletion_generator_host
|
||||||
ap_tools/ap-check
|
ap_tools/ap-check
|
||||||
cmsfs-fuse/cmsfs-fuse
|
cmsfs-fuse/cmsfs-fuse
|
||||||
cpacfstats/cpacfstats
|
cpacfstats/cpacfstats
|
||||||
@@ -34,8 +35,20 @@ cpumf/lscpumf
|
|||||||
cpumf/lshwc
|
cpumf/lshwc
|
||||||
cpumf/lspai
|
cpumf/lspai
|
||||||
cpumf/pai
|
cpumf/pai
|
||||||
|
cpumf/_chcpumf
|
||||||
|
cpumf/_lscpumf
|
||||||
|
cpumf/_lshwc
|
||||||
|
cpumf/_lspai
|
||||||
|
cpumf/_pai
|
||||||
|
cpumf/chcpumf.bash
|
||||||
|
cpumf/lscpumf.bash
|
||||||
|
cpumf/lshwc.bash
|
||||||
|
cpumf/lspai.bash
|
||||||
|
cpumf/pai.bash
|
||||||
cpuplugd/cpuplugd
|
cpuplugd/cpuplugd
|
||||||
dasdfmt/dasdfmt
|
dasdfmt/dasdfmt
|
||||||
|
dasdfmt/_dasdfmt
|
||||||
|
dasdfmt/dasdfmt.bash
|
||||||
dasdinfo/dasdinfo
|
dasdinfo/dasdinfo
|
||||||
dasdview/dasdview
|
dasdview/dasdview
|
||||||
dump2tar/src/dump2tar
|
dump2tar/src/dump2tar
|
||||||
@@ -63,7 +76,7 @@ libekmfweb/check-dep-libekmfweb
|
|||||||
libekmfweb/detect-openssl-version.dep
|
libekmfweb/detect-openssl-version.dep
|
||||||
libekmfweb/libekmfweb.so
|
libekmfweb/libekmfweb.so
|
||||||
libekmfweb/libekmfweb.so.1
|
libekmfweb/libekmfweb.so.1
|
||||||
libekmfweb/libekmfweb.so.1.0
|
libekmfweb/libekmfweb.so.1.1
|
||||||
libkmipclient/check-dep-libkmipclient
|
libkmipclient/check-dep-libkmipclient
|
||||||
libkmipclient/detect-openssl-version.dep
|
libkmipclient/detect-openssl-version.dep
|
||||||
libkmipclient/libkmipclient.so
|
libkmipclient/libkmipclient.so
|
||||||
@@ -77,9 +90,8 @@ libzds/libzds.a
|
|||||||
lsstp/lsstp
|
lsstp/lsstp
|
||||||
mon_tools/mon_fsstatd
|
mon_tools/mon_fsstatd
|
||||||
mon_tools/mon_procd
|
mon_tools/mon_procd
|
||||||
|
opticsmon/opticsmon
|
||||||
osasnmpd/osasnmpd
|
osasnmpd/osasnmpd
|
||||||
pvattest/src/pvattest
|
|
||||||
pvattest/tools/exchange_info
|
|
||||||
qetharp/qetharp
|
qetharp/qetharp
|
||||||
qethqoat/qethqoat
|
qethqoat/qethqoat
|
||||||
systemd/cpacfstatsd.service
|
systemd/cpacfstatsd.service
|
||||||
@@ -120,7 +132,7 @@ ziomon/ziomon_zfcpdd
|
|||||||
ziomon/ziorep_traffic
|
ziomon/ziorep_traffic
|
||||||
ziomon/ziorep_utilization
|
ziomon/ziorep_utilization
|
||||||
zipl/boot/*.bin
|
zipl/boot/*.bin
|
||||||
zipl/boot/*.exec
|
zipl/boot/*.elf
|
||||||
zipl/boot/.loaders
|
zipl/boot/.loaders
|
||||||
zipl/boot/data.h
|
zipl/boot/data.h
|
||||||
zipl/src/chreipl_helper.device-mapper
|
zipl/src/chreipl_helper.device-mapper
|
||||||
@@ -138,4 +150,6 @@ zkey/kmip/libkmipclient.dep
|
|||||||
zkey/kmip/zkey-kmip.so
|
zkey/kmip/zkey-kmip.so
|
||||||
zkey/zkey
|
zkey/zkey
|
||||||
zkey/zkey-cryptsetup
|
zkey/zkey-cryptsetup
|
||||||
|
zmemtopo/zmemtopo
|
||||||
zpcictl/zpcictl
|
zpcictl/zpcictl
|
||||||
|
zpwr/zpwr
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
exclude: \.(bin|crl|crt|key)$
|
exclude: \.(bin|crl|crt|key)$
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.1.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -11,6 +11,9 @@ repos:
|
|||||||
- id: check-executables-have-shebangs
|
- id: check-executables-have-shebangs
|
||||||
- id: check-shebang-scripts-are-executable
|
- id: check-shebang-scripts-are-executable
|
||||||
exclude_types: ['rust']
|
exclude_types: ['rust']
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-json
|
||||||
|
- id: check-toml
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: git-clang-format
|
- id: git-clang-format
|
||||||
@@ -24,7 +27,7 @@ repos:
|
|||||||
minimum_pre_commit_version: "2.9.0"
|
minimum_pre_commit_version: "2.9.0"
|
||||||
types_or: [c++, c]
|
types_or: [c++, c]
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.2.1
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
exclude_types: ['rust']
|
exclude_types: ['rust']
|
||||||
|
|||||||
20
AUTHORS.md
20
AUTHORS.md
@@ -1,16 +1,19 @@
|
|||||||
List of all individuals having contributed content to s390-tools
|
List of all individuals having contributed content to s390-tools
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
- Ajaykumar Rajappa
|
||||||
- Alexander Egorenkov
|
- Alexander Egorenkov
|
||||||
- Alexandra Winter
|
- Alexandra Winter
|
||||||
- Alexey Ishchuk
|
- Alexey Ishchuk
|
||||||
- Andreas Herrmann
|
- Andreas Herrmann
|
||||||
- André Wild
|
- André Wild
|
||||||
|
- Annu Sharma
|
||||||
- Antoinette Kaschner
|
- Antoinette Kaschner
|
||||||
- Arnd Bergmann
|
- Arnd Bergmann
|
||||||
- Axel Wirbser
|
- Axel Wirbser
|
||||||
- Balint Reczey
|
- Balint Reczey
|
||||||
- Benjamin Block
|
- Benjamin Block
|
||||||
|
- Bjoern Walk
|
||||||
- Brian C. Lane
|
- Brian C. Lane
|
||||||
- Carsten Otte
|
- Carsten Otte
|
||||||
- Christian Borntraeger
|
- Christian Borntraeger
|
||||||
@@ -27,6 +30,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Eberhard Pasch
|
- Eberhard Pasch
|
||||||
- Eduard Shishkin
|
- Eduard Shishkin
|
||||||
- Einar Lueck
|
- Einar Lueck
|
||||||
|
- Ellen McIntyre
|
||||||
- Eric Farman
|
- Eric Farman
|
||||||
- Eric Sandeen
|
- Eric Sandeen
|
||||||
- Erwin Vicari
|
- Erwin Vicari
|
||||||
@@ -42,13 +46,14 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Frank Munzert
|
- Frank Munzert
|
||||||
- Frank Pavlic
|
- Frank Pavlic
|
||||||
- Fritz Elfert
|
- Fritz Elfert
|
||||||
|
- Gautam Gala
|
||||||
- Gerald Schaefer
|
- Gerald Schaefer
|
||||||
- Gerhard Tonn
|
- Gerhard Tonn
|
||||||
- Graham Inggs
|
- Graham Inggs
|
||||||
- Guevenc Guelce
|
- Guevenc Guelce
|
||||||
- Hannes Reinecke
|
- Hannes Reinecke
|
||||||
- Hans-Joachim Picht
|
|
||||||
- Hans Wippel
|
- Hans Wippel
|
||||||
|
- Hans-Joachim Picht
|
||||||
- Harald Freudenberger
|
- Harald Freudenberger
|
||||||
- Heiko Carstens
|
- Heiko Carstens
|
||||||
- Hendrik Brueckner
|
- Hendrik Brueckner
|
||||||
@@ -58,9 +63,11 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Horst Hummel
|
- Horst Hummel
|
||||||
- Ingo Franzki
|
- Ingo Franzki
|
||||||
- Ingo Tuchscherer
|
- Ingo Tuchscherer
|
||||||
|
- Jakob Naucke
|
||||||
- Jakub Čajka
|
- Jakub Čajka
|
||||||
- Jan Glauber
|
- Jan Glauber
|
||||||
- Jan Höppner
|
- Jan Höppner
|
||||||
|
- Jan Polensky
|
||||||
- Jan Willeke
|
- Jan Willeke
|
||||||
- Jason J. Herne
|
- Jason J. Herne
|
||||||
- Javier Martinez Canillas
|
- Javier Martinez Canillas
|
||||||
@@ -70,9 +77,11 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Joern Siglen
|
- Joern Siglen
|
||||||
- Juergen Christ
|
- Juergen Christ
|
||||||
- Julian Wiedmann
|
- Julian Wiedmann
|
||||||
|
- Jörn Siglen
|
||||||
- Karsten Graul
|
- Karsten Graul
|
||||||
- Kittipon Meesompop
|
- Kittipon Meesompop
|
||||||
- Klaus-Dieter Wacker
|
- Klaus-Dieter Wacker
|
||||||
|
- L. E. Segovia
|
||||||
- Lakhvich Dmitriy
|
- Lakhvich Dmitriy
|
||||||
- Marc Hartmayer
|
- Marc Hartmayer
|
||||||
- Mario Held
|
- Mario Held
|
||||||
@@ -91,9 +100,11 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Michael Mueller
|
- Michael Mueller
|
||||||
- Mijo Safradin
|
- Mijo Safradin
|
||||||
- Mikhail Zaslonko
|
- Mikhail Zaslonko
|
||||||
|
- Nihar Panda
|
||||||
- Nikita Dubrovskii
|
- Nikita Dubrovskii
|
||||||
- Niklas Schnelle
|
- Niklas Schnelle
|
||||||
- Nikolay Gueorguiev
|
- Nikolay Gueorguiev
|
||||||
|
- Peter Jin
|
||||||
- Peter Oberparleiter
|
- Peter Oberparleiter
|
||||||
- Peter Tiedemann
|
- Peter Tiedemann
|
||||||
- Philipp Kern
|
- Philipp Kern
|
||||||
@@ -103,11 +114,15 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Raimund Schroeder
|
- Raimund Schroeder
|
||||||
- Ralph Wuerthner
|
- Ralph Wuerthner
|
||||||
- Rene Trumpp
|
- Rene Trumpp
|
||||||
|
- Richie Buturla
|
||||||
- Rolf Schaefer
|
- Rolf Schaefer
|
||||||
- Sa Liu
|
- Sa Liu
|
||||||
- Sascha Silbe
|
- Sascha Silbe
|
||||||
- Sebastian Ott
|
- Sebastian Ott
|
||||||
|
- Sertonix
|
||||||
- Seshagiri N. Ippili
|
- Seshagiri N. Ippili
|
||||||
|
- Shalini Chellathurai Saroja
|
||||||
|
- Siglen
|
||||||
- Simon Sturm
|
- Simon Sturm
|
||||||
- Stefan Bader
|
- Stefan Bader
|
||||||
- Stefan Haberland
|
- Stefan Haberland
|
||||||
@@ -117,10 +132,12 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Steffen Eiden
|
- Steffen Eiden
|
||||||
- Steffen Maier
|
- Steffen Maier
|
||||||
- Steffen Thoss
|
- Steffen Thoss
|
||||||
|
- Sumanth Korikkar
|
||||||
- Susanne Wintenberger
|
- Susanne Wintenberger
|
||||||
- Sven Schnelle
|
- Sven Schnelle
|
||||||
- Sven Schuetz
|
- Sven Schuetz
|
||||||
- Swen Schillig
|
- Swen Schillig
|
||||||
|
- Szabina Korbai
|
||||||
- Taraka R. Bodireddy
|
- Taraka R. Bodireddy
|
||||||
- Thomas Heidrich
|
- Thomas Heidrich
|
||||||
- Thomas Huth
|
- Thomas Huth
|
||||||
@@ -133,6 +150,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Ursula Braun
|
- Ursula Braun
|
||||||
- Utz Bacher
|
- Utz Bacher
|
||||||
- Vance Morris
|
- Vance Morris
|
||||||
|
- VasiliyS
|
||||||
- Vasily Gorbik
|
- Vasily Gorbik
|
||||||
- Viktor Mihajlovski
|
- Viktor Mihajlovski
|
||||||
- Vineeth Vijayan
|
- Vineeth Vijayan
|
||||||
|
|||||||
197
CHANGELOG.md
197
CHANGELOG.md
@@ -1,6 +1,203 @@
|
|||||||
Release history for s390-tools (MIT version)
|
Release history for s390-tools (MIT version)
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
* __v2.40.0 (2025-12-11)__
|
||||||
|
|
||||||
|
For Linux kernel version: 6.18
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- Add project-wide .clang-tidy configuration
|
||||||
|
- libutil: Introduce util_time for time related functionality
|
||||||
|
- libutil: Introduce zsh/bash autocompletion tooling based on util_opt
|
||||||
|
- pvinfo: Tool to display Secure Execution system information
|
||||||
|
- pvverify: Tool to verify host-key documents
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- cpumf: Implement zsh and bash autocompletion
|
||||||
|
- dasdfmt: Implement zsh and bash autocompletion
|
||||||
|
- dbginfo.sh: Add NetworkManager and netplan
|
||||||
|
- dbginfo.sh: Add kvm_stat
|
||||||
|
- dbginfo.sh: Adding stp time information
|
||||||
|
- dbginfo.sh: Simplify procfs collection
|
||||||
|
- hyptop: Add physical information row
|
||||||
|
- hyptop: Calculate sample time delta for physical partition
|
||||||
|
- hyptop: Replace long option names using _ with - for consistency
|
||||||
|
For example: --cpu_types -> --cpu-types
|
||||||
|
(Options with _ are still supported for backward compatibility)
|
||||||
|
- libekmfweb: Add function to validate a certificate against the identity key
|
||||||
|
- netboot: Add longer kernel command lines support
|
||||||
|
- udev/rules.d: Make virtio-blk devices non-rotational
|
||||||
|
- udev/rules.d: Set default io scheduler to 'none' for virtio-blk
|
||||||
|
- ziomon: Add support to sample device symlinks (/dev/disk/...)
|
||||||
|
- ziorep_config: Add fcp-lun details to -M option output
|
||||||
|
- ziorep_config: Add port_id and failed attributes to -A option output
|
||||||
|
- netboot: Install on non-s390 architectures
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- lib(ekmfweb|kmipclient): Use ln without -r
|
||||||
|
- s390-tools: Fix various compilation issues with musl libc
|
||||||
|
- zipl/boot: Fix unused loadparm when SCLP line-mode console is absent
|
||||||
|
|
||||||
|
* __v2.39.0 (2025-10-14)__
|
||||||
|
|
||||||
|
For Linux kernel version: 6.16 / 6.17
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- chpstat: Add options to select IEC units for scaling (SI units are default)
|
||||||
|
- chzdev: Introduce --no-module-load option
|
||||||
|
- cpi: Disable CPI for SEL guests by default
|
||||||
|
- dbginfo.sh: Enhance logging on timeout triggered
|
||||||
|
- iucvterm: Install symlink for lsiucvallow.8 man page
|
||||||
|
- lshwc: Add command line flag to specify individual counters
|
||||||
|
- lspai: Add command line flag for delta values
|
||||||
|
- lspai: Add command line flag for short counter names
|
||||||
|
- lspai: Add command line flag to specify individual counters
|
||||||
|
- lspai: Add command line flags for all cpus
|
||||||
|
- lspai: Add command line flags for hexadecimal output
|
||||||
|
- man: Use CR for constant width font
|
||||||
|
- pvimg: Add '--image-key' option
|
||||||
|
- zdev: Allow dynamic control of module load
|
||||||
|
- zipl/boot: Fix EBCDIC code page 500 conversion and decrease size by 200 bytes
|
||||||
|
- zipl: Add support of heterogeneous mirrors (remove technical limitations
|
||||||
|
on mirrored targets, thus allowing mirrored devices consist of partitions
|
||||||
|
at different offsets on disks of different types and geometry).
|
||||||
|
- zkey: Add support for generating and importing exportable secure keys
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- chpstat: Fix scaling of DPU utilization calculation
|
||||||
|
- zdev/dracut: Prevent loading of unused kernel modules
|
||||||
|
- zdev: Fix double device configuration on DPM systems
|
||||||
|
- zdev: Fix double device configuration with rd.dasd
|
||||||
|
- zipl_helper.device-mapper: Fix segfault in an error path
|
||||||
|
|
||||||
|
* __v2.38.0 (2025-06-25)__
|
||||||
|
|
||||||
|
For Linux kernel version: 6.14 / 6.15
|
||||||
|
|
||||||
|
Add new tools:
|
||||||
|
- udev: New rule to set newly hotplugged CPUs online
|
||||||
|
- zmemtopo: Display memory topology information
|
||||||
|
- zpwr: Display power readings of a partition and CPC
|
||||||
|
|
||||||
|
Removed tools / features:
|
||||||
|
- check_hostkeydoc: Remove installation target
|
||||||
|
- scsi_logging_level: Delete SCSI logging script (available in sg3_utils)
|
||||||
|
- zdump: Drop build_arch for s390 DASD dumps
|
||||||
|
- zdump: Drop non-extended multi-volume DASD dump support
|
||||||
|
- zdump: Drop support of 32-bit dump architecture
|
||||||
|
- zdump: Drop support of non-extended single volume DASD dumpers
|
||||||
|
- zdump: Drop support of obsolete dumps and dumpers
|
||||||
|
|
||||||
|
Changes of existing tools / libraries:
|
||||||
|
- Various man-pages fixes
|
||||||
|
- check_hostkeydoc: Add deprecation warning
|
||||||
|
- check_hostkeydoc: Move to scripts directory
|
||||||
|
- cpuplugd: Allow cpu hotplugging on systems without polarization
|
||||||
|
- dbginfo.sh: Add Ubuntu snap tool
|
||||||
|
- dbginfo.sh: Add missing config data and logs
|
||||||
|
- dbginfo.sh: Reworking the container section
|
||||||
|
- dbginfo.sh: Update for network commands
|
||||||
|
- dbginfo.sh: Updating info for disks and lvm
|
||||||
|
- libutil: Add machine type definition for machines 9175 and 9176
|
||||||
|
- lscpumf: Add support for IBM z17 counter sets
|
||||||
|
- lshwc: Add command line flag for run time
|
||||||
|
- lshwc: Add flags to display counter values in hex
|
||||||
|
- lshwc: Add output '--format' option
|
||||||
|
- lshwc: Add support for delta counter value display
|
||||||
|
- lspai: Add output '--format' option
|
||||||
|
- lsreipl: Add secure boot state to output
|
||||||
|
- lswhc: Add short names to lshwc output
|
||||||
|
- pv_tools: Add Bash and Zsh completions
|
||||||
|
- pvapconfig: Add '--unbind' option
|
||||||
|
- pvimg/boot: Print error messages from stage3a bootloader
|
||||||
|
- pvimg: Add support for CCK update
|
||||||
|
- pvsecret: Add support for CCK update
|
||||||
|
- pvsecret: Allow retrieving secrets by index & warn for duplicated entries
|
||||||
|
- pvsecret: Deny adding secrets with duplicated secret IDs
|
||||||
|
- zdev: Add support for virtio devices
|
||||||
|
- zipl: Enhance mirror support
|
||||||
|
- zipl: Implement '--dry-run' option for all dump jobs
|
||||||
|
- zipl_helper.device-mapper: Support mirrors over NVMe devices
|
||||||
|
- zkey/dracut: Add a dracut config file for zkey
|
||||||
|
- zkey/initramfs: Update initramfs hook to correct drivers and include zkey plugins
|
||||||
|
- zkey: Add support for converting a clear-key LUKS2 volume to use a secure key
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- chpstat: Add missing CMG 5 data fields
|
||||||
|
- chpstat: Fix DPU utilization calculation
|
||||||
|
- libutil/util_file: Handle over-read in util_file_read_fd()
|
||||||
|
- pvattest: Fix successful 'check' evaluation
|
||||||
|
- pvsecret: Fix some edge cases for plaintext keys
|
||||||
|
- zipl_helper.device-mapper: Fix imprecise is_device_mapper() predicate
|
||||||
|
- zkey: Fix EP11 secure key reencipher function
|
||||||
|
- zpcictl: Fix command line parsing for invalid options
|
||||||
|
|
||||||
|
* __v2.37.0 (2025-02-07)__
|
||||||
|
|
||||||
|
For Linux kernel version: 6.13
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- dbginfo.sh: Add details on CPU-measurement
|
||||||
|
- dbginfo.sh: Add new crypto command
|
||||||
|
- dbginfo.sh: Add overview commands and crypto update
|
||||||
|
- dbginfo.sh: Adding kdump info
|
||||||
|
- dbginfo.sh: Removing outdated email references
|
||||||
|
- dbginfo.sh: Rework network section
|
||||||
|
- dbginfo.sh: Update copyright 2nd year
|
||||||
|
- pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
|
||||||
|
- pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
|
||||||
|
- pvimg: Add '--hdr-key' command line option to 'pvimg create'
|
||||||
|
- pvimg: Rename '--key' into '--hdr-key' and use '--key' as an alias (for 'pvimg info')
|
||||||
|
- pvsecret: Add support for retrievable secrets
|
||||||
|
- ziorep_config: Add PCHID field to adapter report
|
||||||
|
- ziorep_traffic: Add DEVBUSID column to traffic report
|
||||||
|
- ziorep_utilization: Add --fcp-device parameter to print virtual adapter report
|
||||||
|
- ziorep_utilization: Add PCHID column to physical adapter report
|
||||||
|
- ziorep_utilization: Now prints only physical adapter report by default
|
||||||
|
- ziorep_utilization: Swap Bus-ID and CHPID columns in virtual adapter report
|
||||||
|
- zipl/boot: Increase section size for eckd_mv dumper
|
||||||
|
- zkey: Add support for listing and importing protected virtualization secrets
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- chpstat: Fix invalid utilization data on older kernels
|
||||||
|
- opticsmon: Fix runaway loop in on_link_change()
|
||||||
|
- zipl: Update inline assembly for GCC 15
|
||||||
|
- zipl_helper.device-mapper: Add missed step in logical device resolution
|
||||||
|
|
||||||
|
* __v2.36.0 (2024-12-06)__
|
||||||
|
|
||||||
|
For Linux kernel version: 6.12
|
||||||
|
|
||||||
|
s390-tools: Define Rust MSRV as 1.75.0
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- cpacfinfo: Tool to provide CPACF information
|
||||||
|
- opticsmon: Tools to monitor optical modules for directly attached PCI based NICs
|
||||||
|
- pvimg: Rust rewrite of genprotimg
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- chpstat: Add data bandwidth utilization column
|
||||||
|
- chpstat: Add support for full CMCB
|
||||||
|
- chpstat: Add support for new CMG types
|
||||||
|
- dbginfo.sh: add overview commands and crypto update
|
||||||
|
- genprotimg:
|
||||||
|
1. genprotimg is now a symbolic link to the new tool `pvimg create`
|
||||||
|
2. Breaking API changes in genprotimg:
|
||||||
|
1. An existing output file is no longer silently overwritten; to revert
|
||||||
|
to the old behavior use the `--overwrite` option.
|
||||||
|
2. The Linux kernel component is now checked to determine whether it is a
|
||||||
|
binary s390x kernel. To disable this behavior use the
|
||||||
|
`--no-component-check` option.
|
||||||
|
- hyptop: Support for structured output (json, json-seq, csv)
|
||||||
|
- lszfcp: Add missing fallback marker for non-good fc_host port_state
|
||||||
|
- lszfcp: Improve speed with many SCSI devices
|
||||||
|
- pvattest: Add attestation policy check command
|
||||||
|
- zipl: Add support of partitions of mirror md-devices
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- lszcrypt: Fix wrong state showing up for removed AP queue within SE guest
|
||||||
|
- lszfcp: Show device names line for zfcp_units without SCSI device
|
||||||
|
|
||||||
* __v2.35.0 (2024-10-01)__
|
* __v2.35.0 (2024-10-01)__
|
||||||
|
|
||||||
For Linux kernel version: 6.11
|
For Linux kernel version: 6.11
|
||||||
|
|||||||
7
Makefile
7
Makefile
@@ -9,18 +9,19 @@ include common.mak
|
|||||||
ifeq ($(HOST_ARCH),s390x)
|
ifeq ($(HOST_ARCH),s390x)
|
||||||
BASELIB_DIRS = libutil libseckey
|
BASELIB_DIRS = libutil libseckey
|
||||||
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
||||||
libkmipclient libcpumf libap libpv
|
libkmipclient libcpumf libap libpv libzpci
|
||||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||||
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
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 hsci hsavmcore chreipl-fcp-mpath ap_tools rust
|
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon \
|
||||||
|
zpwr zmemtopo
|
||||||
|
|
||||||
else
|
else
|
||||||
BASELIB_DIRS =
|
BASELIB_DIRS =
|
||||||
LIB_DIRS = libpv
|
LIB_DIRS = libpv
|
||||||
TOOL_DIRS = genprotimg rust
|
TOOL_DIRS = netboot rust
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||||
|
|||||||
85
README.md
85
README.md
@@ -19,12 +19,17 @@ Package contents
|
|||||||
all s390-tools that are written in rust and require external crates.
|
all s390-tools that are written in rust and require external crates.
|
||||||
Disable the compilation of all tools in `rust/` using HAVE_CARGO=0
|
Disable the compilation of all tools in `rust/` using HAVE_CARGO=0
|
||||||
See the `rust/README.md` for Details
|
See the `rust/README.md` for Details
|
||||||
|
- cpacfinfo:
|
||||||
|
Command line interface to get information about CP Assist for
|
||||||
|
Cryptographic Functions (CPACF)
|
||||||
- pvattest:
|
- pvattest:
|
||||||
Create, perform, and verify IBM Secure Execution attestation measurements.
|
Create, perform, and verify IBM Secure Execution attestation measurements.
|
||||||
- pvapconfig:
|
- pvapconfig:
|
||||||
Automatic configure APQNs within an SE KVM guest
|
Automatic configure APQNs within an SE KVM guest
|
||||||
- pvsecret:
|
- pvsecret:
|
||||||
Manage secrets for IBM Secure Execution guests
|
Manage secrets for IBM Secure Execution guests
|
||||||
|
- pvimg:
|
||||||
|
Create and inspect IBM Secure Execution images
|
||||||
|
|
||||||
* dasdfmt:
|
* dasdfmt:
|
||||||
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||||
@@ -42,7 +47,8 @@ Package contents
|
|||||||
Display unique DASD ID, either UID or volser.
|
Display unique DASD ID, either UID or volser.
|
||||||
|
|
||||||
* genprotimg:
|
* genprotimg:
|
||||||
Create a protected virtualization image.
|
Create an IBM Secure Execution (protected virtualization) image. The
|
||||||
|
genprotimg command is a symbolic link to the `pvimg create` command.
|
||||||
|
|
||||||
* udev rules:
|
* udev rules:
|
||||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||||
@@ -122,9 +128,6 @@ Package contents
|
|||||||
* sclpdbf:
|
* sclpdbf:
|
||||||
Display debug data for the sclp kernel component.
|
Display debug data for the sclp kernel component.
|
||||||
|
|
||||||
* scsi_logging_level:
|
|
||||||
Create, get or set the logging level for the SCSI logging facility.
|
|
||||||
|
|
||||||
* zconf:
|
* zconf:
|
||||||
Set of scripts to configure and list status information of Linux on s390
|
Set of scripts to configure and list status information of Linux on s390
|
||||||
devices.
|
devices.
|
||||||
@@ -304,26 +307,27 @@ HAVE_FUSE=0`".
|
|||||||
The following table provides an overview of the used libraries and
|
The following table provides an overview of the used libraries and
|
||||||
build options:
|
build options:
|
||||||
|
|
||||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||||
|----------------|:------------------:|:-------------------------------------:|
|
|--------------|:------------------:|:--------------------------------------:|
|
||||||
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump, |
|
||||||
| | | hsavmcore |
|
| | | hsavmcore |
|
||||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
| openssl | `HAVE_OPENSSL` | zkey, libekmfweb, libkmipclient, |
|
||||||
| | | libkmipclient, pvattest, zgetdump, |
|
| | | zgetdump, rust/pvattest, rust/pvimg, |
|
||||||
| | | rust/pvsecret, |
|
| | | zgetdump/pvsecret, opticsmon |
|
||||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||||
| | | libkmipclient |
|
| | | libkmipclient |
|
||||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest, zgetdump |
|
| glib2 | `HAVE_GLIB2` | zgetdump |
|
||||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
|
| libcurl | `HAVE_LIBCURL` | libekmfweb, libkmipclient, rust/pvimg, |
|
||||||
| | | pvattest, rust/pvsecret, |
|
| | | rust/pvattest, rust/pvsecret, |
|
||||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||||
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||||
|
| libnl3 | `HAVE_LIBNL3` | opticsmon |
|
||||||
|
|
||||||
This table lists additional build or install options:
|
This table lists additional build or install options:
|
||||||
|
|
||||||
@@ -355,13 +359,13 @@ the different tools are provided:
|
|||||||
* dbginfo.sh:
|
* dbginfo.sh:
|
||||||
The tar package is required to archive collected data.
|
The tar package is required to archive collected data.
|
||||||
|
|
||||||
* genprotimg:
|
* rust/pvimg:
|
||||||
For building genprotimg you need OpenSSL version 1.1.0 or newer
|
For building pvimg you need OpenSSL version 1.1.1 or newer
|
||||||
installed (openssl-devel.rpm). Also required is glib2
|
installed (openssl-devel.rpm). Also required is cargo and libcurl.
|
||||||
(glib2-devel.rpm). Tip: you may skip the genprotimg build by adding
|
Tip: you may skip the pvimg build by adding
|
||||||
`HAVE_OPENSSL=0` or `HAVE_GLIB2=0`.
|
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
||||||
|
|
||||||
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl.
|
||||||
|
|
||||||
* rust/pvattest:
|
* rust/pvattest:
|
||||||
For building pvattest you need OpenSSL version 1.1.1 or newer
|
For building pvattest you need OpenSSL version 1.1.1 or newer
|
||||||
@@ -369,8 +373,13 @@ the different tools are provided:
|
|||||||
Tip: you may skip the pvattest build by adding
|
Tip: you may skip the pvattest build by adding
|
||||||
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`.
|
||||||
|
|
||||||
The runtime requirements are: openssl-libs (>= 1.1.1) and
|
The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl.
|
||||||
glib2.56 or newer.
|
|
||||||
|
* opticsmon:
|
||||||
|
For building opticsmon OpenSSL and the Netlink Library Suite (libnl3) are
|
||||||
|
required.
|
||||||
|
Tip: you may skip the opticsmon build by adding
|
||||||
|
`HAVE_OPENSSL=0` or `HAVE_LIBNL3=0`
|
||||||
|
|
||||||
* osasnmpd:
|
* osasnmpd:
|
||||||
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||||
@@ -405,6 +414,18 @@ the different tools are provided:
|
|||||||
dumping).
|
dumping).
|
||||||
Removing the inflate modules and function prototypes are the only major
|
Removing the inflate modules and function prototypes are the only major
|
||||||
modifications made to the kernel version of zlib.
|
modifications made to the kernel version of zlib.
|
||||||
|
For stand-alone NVMe and list-directed DASD dump, the following tools/packages
|
||||||
|
are required to populate dumper's initramfs:
|
||||||
|
- cat
|
||||||
|
- cut
|
||||||
|
- findmnt
|
||||||
|
- logsave
|
||||||
|
- makedumpfile
|
||||||
|
- mount
|
||||||
|
- poweroff
|
||||||
|
- sha256sum
|
||||||
|
- sync
|
||||||
|
- umount
|
||||||
|
|
||||||
* zgetdump
|
* zgetdump
|
||||||
For building zgetdump you need OpenSSL version 1.1.0 or newer
|
For building zgetdump you need OpenSSL version 1.1.0 or newer
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ else # $(ENABLE_DOC) != 1
|
|||||||
# freshness of the man page can be checked, even if `pandoc` is not available,
|
# freshness of the man page can be checked, even if `pandoc` is not available,
|
||||||
# or ENABLE_DOC disabled.
|
# or ENABLE_DOC disabled.
|
||||||
|
|
||||||
MANPAGE_FRESH := $(shell sha256sum --check .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0)
|
MANPAGE_FRESH := $(shell sha256sum -c .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0)
|
||||||
ifeq ($(MANPAGE_FRESH),0)
|
ifeq ($(MANPAGE_FRESH),0)
|
||||||
$(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`)
|
$(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -1,182 +1,170 @@
|
|||||||
.\" Automatically generated by Pandoc 2.16.1
|
.\" Automatically generated by Pandoc 3.1.11.1
|
||||||
.\"
|
.\"
|
||||||
.TH "CHREIPL-FCP-MPATH" "7" "2022-02-24" "s390-tools 2.20.0-build-20220224" "Administrator Manual"
|
.TH "CHREIPL\-FCP\-MPATH" "7" "2025\-03\-12" "s390-tools 2.37.0-build-20250312" "Administrator Manual"
|
||||||
.hy
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.PP
|
chreipl\-fcp\-mpath \- use multipath information for re\-IPL path
|
||||||
chreipl-fcp-mpath - use multipath information for re-IPL path failover
|
failover on a running Linux instance
|
||||||
on a running Linux instance
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.PP
|
The IPL process of Linux on Z or LinuxONE from an FCP\-attached SCSI
|
||||||
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI
|
|
||||||
volume uses exactly one path to the volume.
|
volume uses exactly one path to the volume.
|
||||||
If this path is unavailable, the IPL fails.
|
If this path is unavailable, the IPL fails.
|
||||||
.PP
|
.PP
|
||||||
The \f[B]chreipl-fcp-mpath\f[R] toolset monitors \f[B]udev\f[R] events
|
The \f[B]chreipl\-fcp\-mpath\f[R] toolset monitors \f[B]udev\f[R] events
|
||||||
about paths to the re-IPL volume.
|
about paths to the re\-IPL volume.
|
||||||
If the currently configured re-IPL path becomes unavailable, the toolset
|
If the currently configured re\-IPL path becomes unavailable, the
|
||||||
checks for operational paths to the same volume.
|
toolset checks for operational paths to the same volume.
|
||||||
If available, it reconfigures the re-IPL settings to use an operational
|
If available, it reconfigures the re\-IPL settings to use an operational
|
||||||
path.
|
path.
|
||||||
.PP
|
.PP
|
||||||
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite
|
Thus, re\-IPL from an FCP\-attached SCSI volume can be successful
|
||||||
path failures on a running Linux instance if at least one path to the
|
despite path failures on a running Linux instance if at least one path
|
||||||
re-IPL volume remains operational.
|
to the re\-IPL volume remains operational.
|
||||||
.PP
|
.PP
|
||||||
\f[B]Chreipl-fcp-mpath\f[R] requires \f[B]udev\f[R],
|
\f[B]Chreipl\-fcp\-mpath\f[R] requires \f[B]udev\f[R],
|
||||||
\f[B]multipathd\f[R] and \f[B]dm-multipath\f[R].
|
\f[B]multipathd\f[R] and \f[B]dm\-multipath\f[R].
|
||||||
Once installed, the toolset runs automatically and autonomously.
|
Once installed, the toolset runs automatically and autonomously.
|
||||||
No user intervention is possible or required.
|
No user intervention is possible or required.
|
||||||
.PP
|
.PP
|
||||||
Other than installing the toolset, there is no user interface for
|
Other than installing the toolset, there is no user interface for
|
||||||
\f[B]chreipl-fcp-mpath\f[R].
|
\f[B]chreipl\-fcp\-mpath\f[R].
|
||||||
.SS Requirements
|
.SS Requirements
|
||||||
.PP
|
The \f[B]chreipl\-fcp\-mpath\f[R] tool has the following requirements on
|
||||||
The \f[B]chreipl-fcp-mpath\f[R] tool has the following requirements on
|
|
||||||
the Linux instance that is being monitored:
|
the Linux instance that is being monitored:
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
The Linux instance must have started successfully, during IPL.
|
The Linux instance must have started successfully, during IPL.
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
The running Linux instance must use \f[B]dm-multipath\f[R] and
|
The running Linux instance must use \f[B]dm\-multipath\f[R] and
|
||||||
\f[B]multipathd\f[R] for the configured re-IPL volume - a volume that
|
\f[B]multipathd\f[R] for the configured re\-IPL volume \- a volume that
|
||||||
contains a zipl boot record and has one of its paths used in the re-IPL
|
contains a zipl boot record and has one of its paths used in the re\-IPL
|
||||||
configuration.
|
configuration.
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
\f[B]udev\f[R] must run.
|
\f[B]udev\f[R] must run.
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
The toolset must observe at least one event about the configured re-IPL
|
The toolset must observe at least one event about the configured re\-IPL
|
||||||
path.
|
path.
|
||||||
Examples for such events are: the SCSI disk comes online, or a path of
|
Examples for such events are: the SCSI disk comes online, or a path of
|
||||||
the corresponding multipath device goes down or comes back online.
|
the corresponding multipath device goes down or comes back online.
|
||||||
.RS 2
|
.RS 2
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
The WWID of the re-IPL volume must not change while the Linux instance
|
The WWID of the re\-IPL volume must not change while the Linux instance
|
||||||
is running.
|
is running.
|
||||||
.RE
|
.RE
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
When the configured re-IPL path becomes unavailable while the Linux
|
When the configured re\-IPL path becomes unavailable while the Linux
|
||||||
instance is running, at least one operational path to the re-IPL volume
|
instance is running, at least one operational path to the re\-IPL volume
|
||||||
must be available, or must become available.
|
must be available, or must become available.
|
||||||
If no such path is available when the Linux instance is rebooted, the
|
If no such path is available when the Linux instance is rebooted, the
|
||||||
re-IPL path is not changed.
|
re\-IPL path is not changed.
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
The tool assumes that any manually reconfigured re-IPL device is valid
|
The tool assumes that any manually reconfigured re\-IPL device is valid
|
||||||
and operational.
|
and operational.
|
||||||
.RS 2
|
.RS 2
|
||||||
.PP
|
.PP
|
||||||
The tool treats a newly configured re-IPL device like the initially
|
The tool treats a newly configured re\-IPL device like the initially
|
||||||
configured re-IPL device.
|
configured re\-IPL device.
|
||||||
In particular, if the newly configured re-IPL device fulfills the
|
In particular, if the newly configured re\-IPL device fulfills the
|
||||||
requirements of the tool, re-IPL path failover takes place if the
|
requirements of the tool, re\-IPL path failover takes place if the
|
||||||
configured re-IPL path becomes unavailable.
|
configured re\-IPL path becomes unavailable.
|
||||||
.RE
|
.RE
|
||||||
.SS Caution with Manual Changes to the Configured re-IPL Target
|
.SS Caution with Manual Changes to the Configured re\-IPL Target
|
||||||
.PP
|
\f[B]chreipl\-fcp\-mpath\f[R] is designed to accept operator\-inititated
|
||||||
\f[B]chreipl-fcp-mpath\f[R] is designed to accept operator-inititated
|
changes of the re\-IPL device.
|
||||||
changes of the re-IPL device.
|
|
||||||
However, concurrent changes by the operator and tool driven changes can
|
However, concurrent changes by the operator and tool driven changes can
|
||||||
result in the operator change being overwritten.
|
result in the operator change being overwritten.
|
||||||
.PP
|
.PP
|
||||||
To avoid this problem, change the re-IPL device only during steady-state
|
To avoid this problem, change the re\-IPL device only during
|
||||||
operations, when no path events happen.
|
steady\-state operations, when no path events happen.
|
||||||
Alternatively, make sure that no events are processed while you change
|
Alternatively, make sure that no events are processed while you change
|
||||||
the device.
|
the device.
|
||||||
See EXAMPLES for one way to suspend event processing.
|
See EXAMPLES for one way to suspend event processing.
|
||||||
.SH MESSAGES
|
.SH MESSAGES
|
||||||
.PP
|
During monitoring and event processing, \f[B]chreipl\-fcp\-mpath\f[R]
|
||||||
During monitoring and event processing, \f[B]chreipl-fcp-mpath\f[R]
|
|
||||||
writes messages to the syslog.
|
writes messages to the syslog.
|
||||||
.PP
|
.PP
|
||||||
When the configured re-IPL path is changed to a different path to the
|
When the configured re\-IPL path is changed to a different path to the
|
||||||
same volume (priority \f[I]daemon.notice\f[R]):
|
same volume (priority \f[I]daemon.notice\f[R]):
|
||||||
.RS
|
.RS
|
||||||
.PP
|
.PP
|
||||||
Changed re-IPL path to: <device-bus-id>:<wwpn>:<lun>.
|
Changed re\-IPL path to: <device\-bus\-id>:<wwpn>:<lun>.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
When a path event indicates that the last available path has become
|
When a path event indicates that the last available path has become
|
||||||
non-operational (priority \f[I]daemon.alert\f[R]):
|
non\-operational (priority \f[I]daemon.alert\f[R]):
|
||||||
.RS
|
.RS
|
||||||
.PP
|
.PP
|
||||||
The re-IPL device cannot be changed because no operational path to the
|
The re\-IPL device cannot be changed because no operational path to the
|
||||||
re-IPL volume remains.
|
re\-IPL volume remains.
|
||||||
The next re-IPL might fail unless you re-attach or enable at least one
|
The next re\-IPL might fail unless you re\-attach or enable at least one
|
||||||
valid path to the re-IPL volume.
|
valid path to the re\-IPL volume.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
When changing the configured re-IPL device failed because of an error
|
When changing the configured re\-IPL device failed because of an error
|
||||||
with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]):
|
with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]):
|
||||||
.RS
|
.RS
|
||||||
.PP
|
.PP
|
||||||
Changing the re-IPL device failed.
|
Changing the re\-IPL device failed.
|
||||||
The current re-IPL settings might be inconsistent.
|
The current re\-IPL settings might be inconsistent.
|
||||||
Check and correct the settings (see the README.md of chreipl-fcp-mpath)
|
Check and correct the settings (see the README.md of
|
||||||
to make sure that the current re-IPL device is valid.
|
chreipl\-fcp\-mpath) to make sure that the current re\-IPL device is
|
||||||
|
valid.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
A failure to change the re-IPL device can indicate an inconsistent
|
A failure to change the re\-IPL device can indicate an inconsistent
|
||||||
setting that cannot be corrected automatically by
|
setting that cannot be corrected automatically by
|
||||||
\f[B]chreipl-fcp-mpath\f[R].
|
\f[B]chreipl\-fcp\-mpath\f[R].
|
||||||
As a result, the next re-IPL might fail or might not use the intended
|
As a result, the next re\-IPL might fail or might not use the intended
|
||||||
re-IPL device.
|
re\-IPL device.
|
||||||
.PP
|
.PP
|
||||||
You can use the following tools to check and correct the current
|
You can use the following tools to check and correct the current
|
||||||
settings:
|
settings:
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
\f[B]lsreipl\f[R] to confirm that the intended re-IPL device is
|
\f[B]lsreipl\f[R] to confirm that the intended re\-IPL device is
|
||||||
configured;
|
configured;
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
\f[B]chreipl\f[R] to change the re-IPL device;
|
\f[B]chreipl\f[R] to change the re\-IPL device;
|
||||||
.IP \[bu] 2
|
.IP \[bu] 2
|
||||||
\f[B]lszfcp\f[R] to inspect the state of available paths to the re-IPL
|
\f[B]lszfcp\f[R] to inspect the state of available paths to the re\-IPL
|
||||||
device.
|
device.
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
.SS Manual Changes to the Configured re-IPL Device
|
.SS Manual Changes to the Configured re\-IPL Device
|
||||||
.PP
|
|
||||||
As outlined in DESCRIPTION, be cautious when manually changing the
|
As outlined in DESCRIPTION, be cautious when manually changing the
|
||||||
configured re-IPL device.
|
configured re\-IPL device.
|
||||||
Assure that your reconfiguration actions do not collide with concurrent
|
Assure that your reconfiguration actions do not collide with concurrent
|
||||||
automatic event processing by \f[B]chreipl-fcp-mpath\f[R].
|
automatic event processing by \f[B]chreipl\-fcp\-mpath\f[R].
|
||||||
You can avoid such collisions, by stopping event processing, making your
|
You can avoid such collisions, by stopping event processing, making your
|
||||||
changes, and then re-enabling event processing.
|
changes, and then re\-enabling event processing.
|
||||||
You need \f[I]root\f[R] privileges for running the commands in the
|
You need \f[I]root\f[R] privileges for running the commands in the
|
||||||
following example:
|
following example:
|
||||||
.IP
|
.IP
|
||||||
.nf
|
.EX
|
||||||
\f[C]
|
|
||||||
\[ti] # udevadm settle
|
\[ti] # udevadm settle
|
||||||
\[ti] # udevadm control --stop-exec-queue
|
\[ti] # udevadm control \-\-stop\-exec\-queue
|
||||||
\[ti] # chreipl ...
|
\[ti] # chreipl ...
|
||||||
\[ti] # udevadm control --start-exec-queue
|
\[ti] # udevadm control \-\-start\-exec\-queue
|
||||||
\f[R]
|
.EE
|
||||||
.fi
|
|
||||||
.SS Listing messages with journalctl
|
.SS Listing messages with journalctl
|
||||||
.PP
|
|
||||||
If your Linux instance includes \f[B]journalctl\f[R], use the following
|
If your Linux instance includes \f[B]journalctl\f[R], use the following
|
||||||
command to list all messages that are issued by
|
command to list all messages that are issued by
|
||||||
\f[B]chreipl-fcp-mpath\f[R]:
|
\f[B]chreipl\-fcp\-mpath\f[R]:
|
||||||
.IP
|
.IP
|
||||||
.nf
|
.EX
|
||||||
\f[C]
|
\[ti] # journalctl \-t chreipl\-fcp\-mpath
|
||||||
\[ti] # journalctl -t chreipl-fcp-mpath
|
.EE
|
||||||
\f[R]
|
|
||||||
.fi
|
|
||||||
.PP
|
.PP
|
||||||
To list only messages that were issued since the last IPL, use this
|
To list only messages that were issued since the last IPL, use this
|
||||||
command:
|
command:
|
||||||
.IP
|
.IP
|
||||||
.nf
|
.EX
|
||||||
\f[C]
|
\[ti] # journalctl \-t chreipl\-fcp\-mpath \-b
|
||||||
\[ti] # journalctl -t chreipl-fcp-mpath -b
|
.EE
|
||||||
\f[R]
|
|
||||||
.fi
|
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
.PP
|
|
||||||
Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in
|
Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in
|
||||||
\f[B]chreipl-fcp-mpath\f[R]: s390-tools
|
\f[B]chreipl\-fcp\-mpath\f[R]: \c
|
||||||
Issues (https://github.com/ibm-s390-linux/s390-tools/issues).
|
.UR https://github.com/ibm-s390-linux/s390-tools/issues
|
||||||
|
s390\-tools Issues
|
||||||
|
.UE \c
|
||||||
|
\&.
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.PP
|
|
||||||
\f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1),
|
\f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1),
|
||||||
\f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8),
|
\f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8),
|
||||||
\f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8),
|
\f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8),
|
||||||
|
|||||||
31
common.mak
31
common.mak
@@ -32,7 +32,7 @@ endif
|
|||||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||||
VERSION := 2
|
VERSION := 2
|
||||||
RELEASE := 35
|
RELEASE := 40
|
||||||
PATCHLEVEL := 0
|
PATCHLEVEL := 0
|
||||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||||
@@ -48,7 +48,7 @@ export S390_TEST_LIB_PATH=$(rootdir)/s390-tools-testsuite/lib
|
|||||||
# $ make HOST_ARCH=s390x CROSS_COMPILE="s390x-linux-gnu-"
|
# $ make HOST_ARCH=s390x CROSS_COMPILE="s390x-linux-gnu-"
|
||||||
#
|
#
|
||||||
|
|
||||||
CROSS_COMPILE =
|
CROSS_COMPILE ?=
|
||||||
|
|
||||||
#
|
#
|
||||||
# Commands can be overwritten on the command line with "make <VAR>=<VALUE>":
|
# Commands can be overwritten on the command line with "make <VAR>=<VALUE>":
|
||||||
@@ -119,6 +119,10 @@ ifneq ($(HOST_ARCH),$(BUILD_ARCH))
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# *_FOR_BUILD variables define compilation configurations for binaries used
|
||||||
|
# during the build process (e.g. autocompleteion script generators). This
|
||||||
|
# is especially important for cross-compilation where these binaries need to
|
||||||
|
# be executed on the build system.
|
||||||
$(eval $(call define_toolchain_variables,_FOR_BUILD,))
|
$(eval $(call define_toolchain_variables,_FOR_BUILD,))
|
||||||
$(eval $(call define_toolchain_variables,,$(CROSS_COMPILE)))
|
$(eval $(call define_toolchain_variables,,$(CROSS_COMPILE)))
|
||||||
|
|
||||||
@@ -165,7 +169,7 @@ ifeq ("${ENABLE_WERROR}", "1")
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
DEFAULT_CPPFLAGS = -D_GNU_SOURCE
|
DEFAULT_CPPFLAGS = -D_GNU_SOURCE
|
||||||
DEFAULT_LDFLAGS = -rdynamic
|
DEFAULT_LDFLAGS = -rdynamic -Wl,--as-needed
|
||||||
|
|
||||||
ifeq ("${ASAN}","1")
|
ifeq ("${ASAN}","1")
|
||||||
DEFAULT_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
DEFAULT_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||||
@@ -201,7 +205,7 @@ endef
|
|||||||
# $5: Additional compiler & linker options (optional)
|
# $5: Additional compiler & linker options (optional)
|
||||||
#
|
#
|
||||||
check_dep=\
|
check_dep=\
|
||||||
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -x c - ) > /dev/null 2>&1; \
|
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) -Wl,--no-as-needed $5 -o /dev/null -x c - ) > /dev/null 2>&1; \
|
||||||
if [ $$? != 0 ]; \
|
if [ $$? != 0 ]; \
|
||||||
then \
|
then \
|
||||||
printf " REQCHK %s (%s)\n" $1 $2; \
|
printf " REQCHK %s (%s)\n" $1 $2; \
|
||||||
@@ -265,9 +269,12 @@ LIBDIR = $(INSTALLDIR)/lib
|
|||||||
USRLIBDIR = $(INSTALLDIR)/usr/lib
|
USRLIBDIR = $(INSTALLDIR)/usr/lib
|
||||||
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
||||||
SYSCONFDIR = $(INSTALLDIR)/etc
|
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||||
MANDIR = $(INSTALLDIR)/usr/share/man
|
DATADIR = $(INSTALLDIR)/usr/share
|
||||||
|
MANDIR = $(DATADIR)/man
|
||||||
|
BASHCOMPLETIONDIR = $(DATADIR)/bash-completion/completions
|
||||||
|
ZSHCOMPLETIONDIR = $(DATADIR)/zsh/site-functions
|
||||||
VARDIR = $(INSTALLDIR)/var
|
VARDIR = $(INSTALLDIR)/var
|
||||||
TOOLS_DATADIR = $(INSTALLDIR)/usr/share/s390-tools
|
TOOLS_DATADIR = $(DATADIR)/s390-tools
|
||||||
TOOLS_LIBDIR = $(INSTALLDIR)/lib/s390-tools
|
TOOLS_LIBDIR = $(INSTALLDIR)/lib/s390-tools
|
||||||
ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump
|
ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump
|
||||||
# Systemd support files are installed only if a directory is specified
|
# Systemd support files are installed only if a directory is specified
|
||||||
@@ -337,14 +344,6 @@ endif
|
|||||||
export INSTALL CFLAGS CXXFLAGS \
|
export INSTALL CFLAGS CXXFLAGS \
|
||||||
LDFLAGS CPPFLAGS ALL_CFLAGS ALL_CXXFLAGS ALL_LDFLAGS ALL_CPPFLAGS
|
LDFLAGS CPPFLAGS ALL_CFLAGS ALL_CXXFLAGS ALL_LDFLAGS ALL_CPPFLAGS
|
||||||
|
|
||||||
ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
|
||||||
NO_PIE_CFLAGS := -fno-pie
|
|
||||||
NO_PIE_LDFLAGS := -no-pie
|
|
||||||
else
|
|
||||||
NO_PIE_CFLAGS :=
|
|
||||||
NO_PIE_LDFLAGS :=
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Overwrite implicit makefile rules for having nice compile output
|
# Overwrite implicit makefile rules for having nice compile output
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
ifeq ("${C}","1")
|
ifeq ("${C}","1")
|
||||||
@@ -504,6 +503,10 @@ $(rootdir)/libpv/libpv.a: $(rootdir)/libpv
|
|||||||
$(MAKE) -C $(rootdir)/libpv libpv.a
|
$(MAKE) -C $(rootdir)/libpv libpv.a
|
||||||
.PHONY: $(rootdir)/libpv
|
.PHONY: $(rootdir)/libpv
|
||||||
|
|
||||||
|
$(rootdir)/libzpci/libzpci.a: $(rootdir)/libzpci
|
||||||
|
$(MAKE) -C $(rootdir)/libzpci libzpci.a
|
||||||
|
.PHONY: $(rootdir)/libzpci
|
||||||
|
|
||||||
$(rootdir)/zipl/boot/.loaders:
|
$(rootdir)/zipl/boot/.loaders:
|
||||||
$(MAKE) -C $(rootdir)/zipl/boot/ .loaders
|
$(MAKE) -C $(rootdir)/zipl/boot/ .loaders
|
||||||
|
|
||||||
|
|||||||
81
common_autocomp.mak
Normal file
81
common_autocomp.mak
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
# Copyright IBM Corp. 2025
|
||||||
|
|
||||||
|
|
||||||
|
# This file defines the build process for shell autocompletion binaries
|
||||||
|
#
|
||||||
|
# How to incorporate it into tool Makefiles:
|
||||||
|
#
|
||||||
|
# 0. The file with the autocompletion script generation code should be named
|
||||||
|
# 'autocompletion_generator_host.c'
|
||||||
|
#
|
||||||
|
# 1. Define the 'bash-completions' and 'zsh-completions' variables: both must be a list
|
||||||
|
# of the names of all the shell completion scripts that belong to the tools the Makefile
|
||||||
|
# is responsible for building.
|
||||||
|
#
|
||||||
|
# (The name of a zsh completion script is the same as the name of the tool,
|
||||||
|
# prefixed by an underscore)
|
||||||
|
#
|
||||||
|
# (The name of a bash completion script is the same as the name of the tool,
|
||||||
|
# suffixed by '.bash')
|
||||||
|
#
|
||||||
|
# 2. include this file
|
||||||
|
#
|
||||||
|
# +1: Add the autocompletion scripts to the .gitignore file
|
||||||
|
#
|
||||||
|
# (See cpumf or dasdfmt as example)
|
||||||
|
|
||||||
|
|
||||||
|
autocomplete-bin := autocompletion_generator_host
|
||||||
|
|
||||||
|
autocomp-object := $(rootdir)/libutil/util_autocomp_host.o
|
||||||
|
|
||||||
|
$(autocomp-object): $(rootdir)/libutil
|
||||||
|
$(MAKE) -C $(rootdir)/libutil/ $(notdir $@)
|
||||||
|
|
||||||
|
$(autocomplete-bin).o: $(autocomplete-bin).c
|
||||||
|
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -I $(rootdir)/include -c $< -o $@
|
||||||
|
|
||||||
|
$(autocomplete-bin): $(autocomplete-bin).o $(autocomp-object)
|
||||||
|
$(LINK_FOR_BUILD) $^ -o $@
|
||||||
|
|
||||||
|
$(bash-completions) $(zsh-completions) &: $(autocomplete-bin)
|
||||||
|
./$(autocomplete-bin)
|
||||||
|
|
||||||
|
install-shell-completions: $(bash-completions) $(zsh-completions)
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(ZSHCOMPLETIONDIR)
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BASHCOMPLETIONDIR)
|
||||||
|
for completion in $(bash-completions); do \
|
||||||
|
$(INSTALL) -m 644 $$completion $(DESTDIR)$(BASHCOMPLETIONDIR); \
|
||||||
|
done
|
||||||
|
for completion in $(zsh-completions); do \
|
||||||
|
$(INSTALL) -m 644 $$completion $(DESTDIR)$(ZSHCOMPLETIONDIR); \
|
||||||
|
done
|
||||||
|
|
||||||
|
all: $(zsh-completions) $(bash-completions)
|
||||||
|
|
||||||
|
install: install-shell-completions
|
||||||
|
|
||||||
|
.PHONY: install-shell-completions $(rootdir)/libutil
|
||||||
|
|
||||||
|
clean-autocomplete-bin:
|
||||||
|
$(RM) -- $(autocomplete-bin)
|
||||||
|
|
||||||
|
clean: clean-autocomplete-bin
|
||||||
|
|
||||||
|
ifdef bash-completions
|
||||||
|
clean-bash-completions:
|
||||||
|
$(RM) -- $(bash-completions)
|
||||||
|
|
||||||
|
clean: clean-bash-completions
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ifdef zsh-completions
|
||||||
|
clean-zsh-completions:
|
||||||
|
$(RM) -- $(zsh-completions)
|
||||||
|
|
||||||
|
clean: clean-zsh-completions
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: clean-autocomplete-bin clean-bash-completions clean-zsh-completions
|
||||||
@@ -22,7 +22,7 @@ ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
|||||||
all: check_dep cpacfstats cpacfstatsd
|
all: check_dep cpacfstats cpacfstatsd
|
||||||
|
|
||||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
|
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
|
||||||
$(rootdir)/libutil/libutil.a
|
$(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
|
||||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
|
||||||
|
|
||||||
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o
|
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ All counters are initially disabled and must be switched on to measure
|
|||||||
CPACF activities of the system. There is a slight performance penalty with
|
CPACF activities of the system. There is a slight performance penalty with
|
||||||
CPACF counters enabled.
|
CPACF counters enabled.
|
||||||
|
|
||||||
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
|
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
|
||||||
counters are only available on LPARs and have to be authorized. If
|
counters are only available on LPARs and have to be authorized. If
|
||||||
they are available, the counters
|
they are available, the counters
|
||||||
.B des,
|
.B des,
|
||||||
.B aes,
|
.B aes,
|
||||||
@@ -55,14 +55,133 @@ they are available, the counters
|
|||||||
.B rng,
|
.B rng,
|
||||||
and
|
and
|
||||||
.B ecc
|
.B ecc
|
||||||
are made available. These counters can individually be activated,
|
are made available. These counters can individually be activated,
|
||||||
reset, printed, or deactivated. PAI counters are a lot more detailed.
|
reset, printed, or deactivated. PAI counters are a lot more detailed.
|
||||||
The user interface only offers the counters
|
The user interface only offers the counters
|
||||||
.B pai_user
|
.B pai_user
|
||||||
and
|
and
|
||||||
.B pai_kernel
|
.B pai_kernel
|
||||||
to count CPACF usage in user-space or kernel-space. When printing
|
to count CPACF usage in user-space or kernel-space. When printing
|
||||||
these counters, detailed counters are shown. The detailed names are:
|
these counters, detailed counters are shown.
|
||||||
|
|
||||||
|
A complete list of counters can be found at the end of this manpage.
|
||||||
|
|
||||||
|
Note that the counters starting with PCKMO and Reserved are only
|
||||||
|
available in the pai_kernel set. Also note that the counters are
|
||||||
|
designed to count successful operations. In the case of KMA this
|
||||||
|
means only complete GCM operations including final hashing are
|
||||||
|
counted.
|
||||||
|
|
||||||
|
Note: CPU-MF based CPACF performance counters are available on LPARs
|
||||||
|
only. PAI counters are available on all hypervisors. For security
|
||||||
|
reasons only members of the group \fIcpacfstats\fR are allowed to run
|
||||||
|
the cpacfstats client application.
|
||||||
|
|
||||||
|
Example usage scenario:
|
||||||
|
.P
|
||||||
|
1. Start the cpacfstatsd daemon with root privileges using 'systemctl start
|
||||||
|
cpacfstatsd'.
|
||||||
|
.P
|
||||||
|
2. Check for successful startup by using the 'systemctl status cpacfstatsd'
|
||||||
|
command.
|
||||||
|
.P
|
||||||
|
3. Enable the CPACF counters of interest. For example, enable all counters
|
||||||
|
by issuing cpacfstats -e.
|
||||||
|
.P
|
||||||
|
4. Run your applications.
|
||||||
|
.P
|
||||||
|
5. Display counter values by using the cpacfstats command. Reset the
|
||||||
|
cryptographic counters as required. To reset, use, for example, cpacfstats
|
||||||
|
-r.
|
||||||
|
.P
|
||||||
|
6. Disable all the CPACF measurements, for example, by using cpacfstats
|
||||||
|
-d.
|
||||||
|
.P
|
||||||
|
7. Shutdown the cpacfstatsd daemon by using 'systemctl stop cpacfstatsd'.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR or \fB\-\-help\fR
|
||||||
|
Display help information for the command.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR or \fB\-\-version\fR
|
||||||
|
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,
|
||||||
|
\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,
|
||||||
|
\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,
|
||||||
|
\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 [\fB\-n\fR or \fB\-\-nonzero\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, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
|
||||||
|
\fBall\fR. If the counter argument is omitted or if there is no
|
||||||
|
argument, all performance counters are displayed. If the optional
|
||||||
|
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
|
||||||
|
counters that have a non-zero value are printed.
|
||||||
|
.TP
|
||||||
|
\fB\-j\fR or \fB\-\-json\fR
|
||||||
|
Display all activated counters in JSON format. The JSON contains an
|
||||||
|
array of counter objects. Each object contains the property
|
||||||
|
.B counter
|
||||||
|
specifying either a CPU-MF counter of one of the detailed PAI counter.
|
||||||
|
Additional properties include
|
||||||
|
.B error
|
||||||
|
an error number if the counter could not be read,
|
||||||
|
.B value
|
||||||
|
the counter value if the counter could be read,
|
||||||
|
.B space
|
||||||
|
for PAI counters to specify
|
||||||
|
.B user
|
||||||
|
or
|
||||||
|
.B kernel
|
||||||
|
space counter set, and
|
||||||
|
.B counterid
|
||||||
|
for PAI counters to specify the PAI counter number as specified in the
|
||||||
|
Principles of Operation.
|
||||||
|
.TP
|
||||||
|
The default command is --print all.
|
||||||
|
.
|
||||||
|
.SH FILES
|
||||||
|
.nf
|
||||||
|
/run/cpacfstatsd_socket
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.SH RETURN VALUE
|
||||||
|
.IP 0
|
||||||
|
Successful program execution.
|
||||||
|
.IP 1
|
||||||
|
An error occurred, reasons include: invalid argument, cpacfstatsd could not
|
||||||
|
be reached (check that the daemon is running), insufficient access rights,
|
||||||
|
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 APPENDIX
|
||||||
|
The detailed pai counter names are:
|
||||||
.RS
|
.RS
|
||||||
.IP \(bu
|
.IP \(bu
|
||||||
KM DES,
|
KM DES,
|
||||||
@@ -376,118 +495,39 @@ PCKMO ECC Ed448,
|
|||||||
Reserved 1, and
|
Reserved 1, and
|
||||||
.IP \(bu
|
.IP \(bu
|
||||||
Reserved 2.
|
Reserved 2.
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS (full) 128bit
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS (full) 256bit
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS (full) 128bit protected key
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS (full) 256bit protected key
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 224
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 256
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 384
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 512
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 224 protected key
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 256 protected key
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 384 protected key
|
||||||
|
.IP \(bu
|
||||||
|
KMAC HMAC SHA 512 protected key
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO HMAC 512 protected key
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO HMAC 1024 protected key
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO AES-XTS 128bit double key protected key
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO AES-XTS 256bit double key protected key
|
||||||
.RE
|
.RE
|
||||||
Note that the counters starting with PCKMO and Reserved are only
|
|
||||||
available in the pai_kernel set. Also note that the counters are
|
|
||||||
designed to count successful operations. In the case of KMA this
|
|
||||||
means only complete GCM operations including final hashing are
|
|
||||||
counted.
|
|
||||||
|
|
||||||
Note: CPU-MF based CPACF performance counters are available on LPARs
|
|
||||||
only. PAI counters are available on all hypervisors. For security
|
|
||||||
reasons only members of the group \fIcpacfstats\fR are allowed to run
|
|
||||||
the cpacfstats client application.
|
|
||||||
|
|
||||||
Example usage scenario:
|
|
||||||
.P
|
|
||||||
1. Start the cpacfstatsd daemon with root privileges.
|
|
||||||
.P
|
|
||||||
2. Check for successful startup by using the ps and syslog commands.
|
|
||||||
.P
|
|
||||||
3. Enable the CPACF counters of interest. For example, enable all counters
|
|
||||||
by issuing cpacfstats -e.
|
|
||||||
.P
|
|
||||||
4. Run your applications.
|
|
||||||
.P
|
|
||||||
5. Display counter values by using the cpacfstats command. Reset the
|
|
||||||
cryptographic counters as required. To reset, use, for example, cpacfstats
|
|
||||||
-r.
|
|
||||||
.P
|
|
||||||
6. Disable all the CPACF measurements, for example, by using cpacfstats
|
|
||||||
-d.
|
|
||||||
.P
|
|
||||||
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
|
|
||||||
|
|
||||||
.SH OPTIONS
|
|
||||||
.TP
|
|
||||||
\fB\-h\fR or \fB\-\-help\fR
|
|
||||||
Display help information for the command.
|
|
||||||
.TP
|
|
||||||
\fB\-v\fR or \fB\-\-version\fR
|
|
||||||
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,
|
|
||||||
\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,
|
|
||||||
\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,
|
|
||||||
\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 [\fB\-n\fR or \fB\-\-nonzero\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, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
|
|
||||||
\fBall\fR. If the counter argument is omitted or if there is no
|
|
||||||
argument, all performance counters are displayed. If the optional
|
|
||||||
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
|
|
||||||
counters that have a non-zero value are printed.
|
|
||||||
.TP
|
|
||||||
\fB\-j\fR or \fB\-\-json\fR
|
|
||||||
Display all activated counters in JSON format. The JSON contains an
|
|
||||||
array of counter objects. Each object contains the property
|
|
||||||
.B counter
|
|
||||||
specifying either a CPU-MF counter of one of the detailed PAI counter.
|
|
||||||
Additional properties include
|
|
||||||
.B error
|
|
||||||
an error number if the counter could not be read,
|
|
||||||
.B value
|
|
||||||
the counter value if the counter could be read,
|
|
||||||
.B space
|
|
||||||
for PAI counters to specify
|
|
||||||
.B user
|
|
||||||
or
|
|
||||||
.B kernel
|
|
||||||
space counter set, and
|
|
||||||
.B counterid
|
|
||||||
for PAI counters to specify the PAI counter number as specified in the
|
|
||||||
Principles of Operation.
|
|
||||||
.TP
|
|
||||||
The default command is --print all.
|
|
||||||
.
|
|
||||||
.SH FILES
|
|
||||||
.nf
|
|
||||||
/run/cpacfstatsd_socket
|
|
||||||
.fi
|
|
||||||
.
|
|
||||||
.SH RETURN VALUE
|
|
||||||
.IP 0
|
|
||||||
Successful program execution.
|
|
||||||
.IP 1
|
|
||||||
An error occurred, reasons include: invalid argument, cpacfstatsd could not
|
|
||||||
be reached (check that the daemon is running), insufficient access rights,
|
|
||||||
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
|
.SH SEE ALSO
|
||||||
.BR cpacfstatsd (8)
|
.BR cpacfstatsd (8)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <libudev.h>
|
#include <libudev.h>
|
||||||
|
|
||||||
#include "cpacfstats.h"
|
#include "cpacfstats.h"
|
||||||
|
#include "lib/libcpumf.h"
|
||||||
#include "../include/lib/zt_common.h"
|
#include "../include/lib/zt_common.h"
|
||||||
|
|
||||||
/* correlation between counter and perf counter string */
|
/* correlation between counter and perf counter string */
|
||||||
@@ -123,16 +124,6 @@ static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
static int perf_supported(void)
|
||||||
{
|
{
|
||||||
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
|
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ include ../common.mak
|
|||||||
BIN_FILES = lscpumf chcpumf lshwc pai lspai
|
BIN_FILES = lscpumf chcpumf lshwc pai lspai
|
||||||
MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 lspai.8
|
MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 lspai.8
|
||||||
|
|
||||||
|
zsh-completions := $(addprefix _,$(BIN_FILES))
|
||||||
|
bash-completions := $(addsuffix .bash,$(BIN_FILES))
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
all: $(BIN_FILES)
|
all: $(BIN_FILES)
|
||||||
|
|
||||||
libs = $(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
|
libs = $(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
|
||||||
|
|||||||
28
cpumf/autocompletion_generator_host.c
Normal file
28
cpumf/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
/*
|
||||||
|
* Autocompletion generation - for cpumf family of tools
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "chcpumf_cli.h"
|
||||||
|
#include "lscpumf_cli.h"
|
||||||
|
#include "lshwc_cli.h"
|
||||||
|
#include "lspai_cli.h"
|
||||||
|
#include "pai_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(chcpumf_opt_vec, "chcpumf");
|
||||||
|
generate_autocomp(lscpumf_opt_vec, "lscpumf");
|
||||||
|
generate_autocomp(lshwc_opt_vec, "lshwc");
|
||||||
|
generate_autocomp(lspai_opt_vec, "lspai");
|
||||||
|
generate_autocomp(pai_opt_vec, "pai");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <err.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -16,38 +16,16 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "lib/util_opt.h"
|
|
||||||
#include "lib/util_prg.h"
|
|
||||||
#include "lib/util_base.h"
|
|
||||||
|
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
|
||||||
|
#include "chcpumf_cli.h"
|
||||||
|
|
||||||
static unsigned int verbose;
|
static unsigned int verbose;
|
||||||
static unsigned long min_sdb, max_sdb;
|
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 = {
|
static const struct util_prg prg = {
|
||||||
.desc = "Change CPU Measurement facility charactertics",
|
.desc = "Change CPU Measurement facility charactertics",
|
||||||
.copyright_vec = {
|
.copyright_vec = {
|
||||||
@@ -88,21 +66,22 @@ static long parse_buffersize(char *string)
|
|||||||
static int write_sfb(unsigned int min, unsigned int max)
|
static int write_sfb(unsigned int min, unsigned int max)
|
||||||
{
|
{
|
||||||
int rc = EXIT_SUCCESS;
|
int rc = EXIT_SUCCESS;
|
||||||
char text[64];
|
char text[64], *path;
|
||||||
size_t len;
|
size_t len;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "w");
|
path = util_path_sysfs(S390_CPUMSF_BUFFERSZ);
|
||||||
|
fp = fopen(path, "w");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
err(EXIT_FAILURE, S390_CPUMSF_BUFFERSZ);
|
err(EXIT_FAILURE, "%s", path);
|
||||||
snprintf(text, sizeof(text), "%u,%u", min, max);
|
snprintf(text, sizeof(text), "%u,%u", min, max);
|
||||||
len = strlen(text) + 1;
|
len = strlen(text) + 1;
|
||||||
if (fwrite(text, 1, len, fp) != len) {
|
if (fwrite(text, 1, len, fp) != len) {
|
||||||
warn(S390_CPUMSF_BUFFERSZ);
|
warn("%s", path);
|
||||||
rc = EXIT_FAILURE;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (fclose(fp)) {
|
if (fclose(fp)) {
|
||||||
warn(S390_CPUMSF_BUFFERSZ);
|
warn("%s", path);
|
||||||
rc = EXIT_FAILURE;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (verbose && rc != EXIT_FAILURE)
|
if (verbose && rc != EXIT_FAILURE)
|
||||||
@@ -110,6 +89,7 @@ static int write_sfb(unsigned int min, unsigned int max)
|
|||||||
" Minimum:%7d sample-data-blocks\n"
|
" Minimum:%7d sample-data-blocks\n"
|
||||||
" Maximum:%7d sample-data-blocks\n",
|
" Maximum:%7d sample-data-blocks\n",
|
||||||
min, max);
|
min, max);
|
||||||
|
free(path);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +141,7 @@ int main(int argc, char **argv)
|
|||||||
unsigned long my_min, my_max;
|
unsigned long my_min, my_max;
|
||||||
|
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(chcpumf_opt_vec, NULL);
|
||||||
|
|
||||||
parse_args(argc, argv);
|
parse_args(argc, argv);
|
||||||
if (geteuid())
|
if (geteuid())
|
||||||
|
|||||||
40
cpumf/chcpumf_cli.h
Normal file
40
cpumf/chcpumf_cli.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for chcpumf
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 CHCPUMF_CLI_H
|
||||||
|
#define CHCPUMF_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt chcpumf_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
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
700
cpumf/lscpumf.c
700
cpumf/lscpumf.c
@@ -7,8 +7,7 @@
|
|||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -31,6 +30,8 @@
|
|||||||
|
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
|
#include "lscpumf_cli.h"
|
||||||
|
|
||||||
#define ACTION_NONE 0
|
#define ACTION_NONE 0
|
||||||
#define ACTION_INFO 1
|
#define ACTION_INFO 1
|
||||||
#define ACTION_CNT 2
|
#define ACTION_CNT 2
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
||||||
|
|
||||||
/* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold
|
/* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold
|
||||||
* as payload data. Each SDBT is one PAGE (4096 bytes) and continas 512 eight
|
* as payload data. Each SDBT is one PAGE (4096 bytes) and contains 512 eight
|
||||||
* byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT
|
* byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT
|
||||||
* points to another SDBT and can not store payload.
|
* points to another SDBT and can not store payload.
|
||||||
*/
|
*/
|
||||||
@@ -47,33 +48,6 @@ static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
|||||||
|
|
||||||
/* File names to read data from */
|
/* File names to read data from */
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "list-counters", no_argument, NULL, 'c' },
|
|
||||||
.desc = "Lists counters for which the LPAR is authorized.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "list-all-counters", no_argument, NULL, 'C' },
|
|
||||||
.desc = "Lists counters regardless of LPAR authorization.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "name", no_argument, NULL, 'n' },
|
|
||||||
.desc = "Displays counter names.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "info", no_argument, NULL, 'i' },
|
|
||||||
.desc = "Displays detailed information.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "list-sampling-events", no_argument, NULL, 's' },
|
|
||||||
.desc = "Lists sampling events for which the LPAR is authorized.",
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "List CPU Measurement facility charactertics",
|
.desc = "List CPU Measurement facility charactertics",
|
||||||
.copyright_vec = {
|
.copyright_vec = {
|
||||||
@@ -139,7 +113,7 @@ static struct samples { /* Sample definition for all machines */
|
|||||||
* The CPU Measurement facility has a first and second version number.
|
* The CPU Measurement facility has a first and second version number.
|
||||||
*
|
*
|
||||||
* The first version number governs basic counter set and the
|
* The first version number governs basic counter set and the
|
||||||
* problem state counter set. Currently used are first verion numbers 1 and 3.
|
* problem state counter set. Currently used are first version numbers 1 and 3.
|
||||||
* The counter numbers are identifical for version number 1 and 3, but
|
* The counter numbers are identifical for version number 1 and 3, but
|
||||||
* have different purpose and description.
|
* have different purpose and description.
|
||||||
*
|
*
|
||||||
@@ -162,13 +136,6 @@ static struct samples { /* Sample definition for all machines */
|
|||||||
* Second version number: >3 Range 448 to 495 inclusive (48 counters)
|
* Second version number: >3 Range 448 to 495 inclusive (48 counters)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CPUMF_CTRSET_NONE 0
|
|
||||||
#define CPUMF_CTRSET_BASIC 2
|
|
||||||
#define CPUMF_CTRSET_PROBLEM_STATE 4
|
|
||||||
#define CPUMF_CTRSET_CRYPTO 8
|
|
||||||
#define CPUMF_CTRSET_EXTENDED 1
|
|
||||||
#define CPUMF_CTRSET_MT_DIAG 32
|
|
||||||
|
|
||||||
struct counters {
|
struct counters {
|
||||||
int ctrnum;
|
int ctrnum;
|
||||||
int ctrset;
|
int ctrset;
|
||||||
@@ -634,7 +601,7 @@ static struct counters cpumcf_z10_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1D_L3_LOCAL_WRITES",
|
.name = "L1D_L3_LOCAL_WRITES",
|
||||||
.desc = "A directory write to the Level-1 D-Cache directory"
|
.desc = "A directory write to the Level-1 D-Cache directory"
|
||||||
"\n\t\twhere the installtion cache line was source from"
|
"\n\t\twhere the installation cache line was source from"
|
||||||
"\n\t\tthe Level-3 cache that is on the same book as the"
|
"\n\t\tthe Level-3 cache that is on the same book as the"
|
||||||
"\n\t\tData cache (Local L2 cache)",
|
"\n\t\tData cache (Local L2 cache)",
|
||||||
},
|
},
|
||||||
@@ -1923,7 +1890,7 @@ static struct counters cpumcf_z14_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1931,7 +1898,7 @@ static struct counters cpumcf_z14_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom On-Chip memory",
|
"\n\t\tfrom On-Chip memory",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1939,7 +1906,7 @@ static struct counters cpumcf_z14_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2349,7 +2316,7 @@ static struct counters cpumcf_z15_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2357,7 +2324,7 @@ static struct counters cpumcf_z15_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom On-Chip memory",
|
"\n\t\tfrom On-Chip memory",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2365,7 +2332,7 @@ static struct counters cpumcf_z15_counters[] = {
|
|||||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
||||||
.desc = "A directory write to the Level-1 Instruction cache"
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3136,6 +3103,641 @@ static struct counters cpumcf_z16_counters[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct counters cpumcf_z17_counters[] = {
|
||||||
|
{
|
||||||
|
.ctrnum = 128,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "L1D_RO_EXCL_WRITES",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache where"
|
||||||
|
"\n\t\tthe line was originally in a Read-Only state in the"
|
||||||
|
"\n\t\tcache but has been updated to be in the Exclusive"
|
||||||
|
"\n\t\tstate that allows stores to the cache line.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 129,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DTLB2_WRITES",
|
||||||
|
.desc = "A translation has been written into The Translation"
|
||||||
|
"\n\t\tLookaside Buffer 2 (TLB2) and the request was made"
|
||||||
|
"\n\t\tby the Level-1 Data cache. This is a replacement"
|
||||||
|
"\n\t\tfor what was provided for the DTLB on z13 and prior"
|
||||||
|
"\n\t\tmachines.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 130,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DTLB2_MISSES",
|
||||||
|
.desc = "A TLB2 miss is in progress for a request made by"
|
||||||
|
"\n\t\tthe Level-1 Data cache. Incremented by one for"
|
||||||
|
"\n\t\tevery TLB2 miss in progress for the Level-1 Data"
|
||||||
|
"\n\t\tcache on this cycle. This is a replacement for what"
|
||||||
|
"\n\t\twas provided for the DTLB on z13 and prior"
|
||||||
|
"\n\t\tmachines.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 131,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "CRSTE_1MB_WRITES",
|
||||||
|
.desc = "A translation entry was written into the Combined"
|
||||||
|
"\n\t\tRegion and Segment Table Entry array in the Level-2"
|
||||||
|
"\n\t\tTLB for a one-megabyte page.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 132,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DTLB2_GPAGE_WRITES",
|
||||||
|
.desc = "A translation entry for a two-gigabyte page was"
|
||||||
|
"\n\t\twritten into the Level-2 TLB.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 134,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ITLB2_WRITES",
|
||||||
|
.desc = "A translation entry has been written into the"
|
||||||
|
"\n\t\tTranslation Lookaside Buffer 2 (TLB2) and the"
|
||||||
|
"\n\t\trequest was made by the Level-1 Instruction cache."
|
||||||
|
"\n\t\tThis is a replacement for what was provided for the"
|
||||||
|
"\n\t\tITLB on z13 and prior machines.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 135,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ITLB2_MISSES",
|
||||||
|
.desc = "A TLB2 miss is in progress for a request made by"
|
||||||
|
"\n\t\tthe Level-1 Instruction cache. Incremented by one"
|
||||||
|
"\n\t\tfor every TLB2 miss in progress for the Level-1"
|
||||||
|
"\n\t\tInstruction cache in a cycle. This is a replacement"
|
||||||
|
"\n\t\tfor what was provided for the ITLB on z13 and prior"
|
||||||
|
"\n\t\tmachines.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 137,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TLB2_PTE_WRITES",
|
||||||
|
.desc = "A translation entry was written into the Page Table"
|
||||||
|
"\n\t\tEntry array in the Level-2 TLB.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 138,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TLB2_CRSTE_WRITES",
|
||||||
|
.desc = "Translation entries were written into the Combined"
|
||||||
|
"\n\t\tRegion and Segment Table Entry array and the Page"
|
||||||
|
"\n\t\tTable Entry array in the Level-2 TLB.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 139,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TLB2_ENGINES_BUSY",
|
||||||
|
.desc = "The number of Level-2 TLB translation engines busy"
|
||||||
|
"\n\t\tin a cycle.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 140,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TX_C_TEND",
|
||||||
|
.desc = "A TEND instruction has completed in a constrained"
|
||||||
|
"\n\t\ttransactional-execution mode.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 141,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TX_NC_TEND",
|
||||||
|
.desc = "A TEND instruction has completed in a non-"
|
||||||
|
"\n\t\tconstrained transactional-execution mode.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 143,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "L1C_TLB2_MISSES",
|
||||||
|
.desc = "Increments by one for any cycle where a Level-1"
|
||||||
|
"\n\t\tcache or Level-2 TLB miss is in progress.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 145,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_REQ",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 146,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_REQ_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache with"
|
||||||
|
"\n\t\tintervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 147,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_REQ_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache after using"
|
||||||
|
"\n\t\tchip level horizontal persistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 148,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_REQ_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache after using"
|
||||||
|
"\n\t\tdrawer level horizontal persistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 149,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_CHIP",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 150,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_CHIP_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache with intervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 151,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_CHIP_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache after using chip"
|
||||||
|
"\n\t\tlevel horizontal persistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 152,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_CHIP_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache after using drawer"
|
||||||
|
"\n\t\tlevel horizontal persistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 153,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_MODULE",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Module Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 154,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_DRAWER",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Drawer Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 155,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_OFF_DRAWER",
|
||||||
|
.desc = "A directory write to the Level-1 Data cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an Off-Drawer Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 156,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_CHIP_MEMORY",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from On-Chip memory.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 157,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_MODULE_MEMORY",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from On-Module memory.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 158,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_ON_DRAWER_MEMORY",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from On-Drawer memory.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 159,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DCW_OFF_DRAWER_MEMORY",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from Off-Drawer memory.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 160,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_MODULE_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Module Level-2"
|
||||||
|
"\n\t\tcache with intervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 161,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_MODULE_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Module Level-2"
|
||||||
|
"\n\t\tcache after using chip level horizontal"
|
||||||
|
"\n\t\tpersistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 162,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_MODULE_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Module Level-2"
|
||||||
|
"\n\t\tcache after using drawer level horizontal"
|
||||||
|
"\n\t\tpersistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 163,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_DRAWER_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Drawer Level-2"
|
||||||
|
"\n\t\tcache with intervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 164,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_DRAWER_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tinstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Drawer Level-2"
|
||||||
|
"\n\t\tcache after using chip level horizontal"
|
||||||
|
"\n\t\tpersistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 165,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_ON_DRAWER_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tinstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an On-Drawer Level-2"
|
||||||
|
"\n\t\tcache after using drawer level horizontal"
|
||||||
|
"\n\t\tpersistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 166,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_OFF_DRAWER_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tinstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an Off-Drawer Level-2"
|
||||||
|
"\n\t\tcache with intervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 167,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_OFF_DRAWER_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tinstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an Off-Drawer Level-2"
|
||||||
|
"\n\t\tcache after using chip level horizontal"
|
||||||
|
"\n\t\tpersistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 168,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "IDCW_OFF_DRAWER_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Data or Level-1"
|
||||||
|
"\n\t\tInstruction cache directory where the returned"
|
||||||
|
"\n\t\tcache line was sourced from an Off-Drawer Level-2"
|
||||||
|
"\n\t\tcache after using drawer level horizontal"
|
||||||
|
"\n\t\tpersistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 169,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_REQ",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tthe requestors Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 170,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_REQ_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache with"
|
||||||
|
"\n\t\tintervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 171,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_REQ_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache after using"
|
||||||
|
"\n\t\tchip level horizontal persistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 172,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_REQ_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom the requestors Level-2 cache after using"
|
||||||
|
"\n\t\tdrawer level horizontal persistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 173,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_CHIP",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 174,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_CHIP_IV",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache with intervention.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 175,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_CHIP_CHIP_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip Level-2 cache after using chip"
|
||||||
|
"\n\t\tlevel horizontal persistence, Chip-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 176,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_CHIP_DRAWER_HIT",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Chip level 2 cache after using drawer"
|
||||||
|
"\n\t\tlevel horizontal persistence, Drawer-HP hit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 177,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_MODULE",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Module Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 178,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_ON_DRAWER",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an On-Drawer Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 179,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "ICW_OFF_DRAWER",
|
||||||
|
.desc = "A directory write to the Level-1 Instruction cache"
|
||||||
|
"\n\t\tdirectory where the returned cache line was sourced"
|
||||||
|
"\n\t\tfrom an Off-Drawer Level-2 cache.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 202,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "CYCLES_SAMETHRD",
|
||||||
|
.desc = "The number of cycles the CPU is not in wait state"
|
||||||
|
"\n\t\tand the CPU is running by itself on the Core.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 203,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "CYCLES_DIFFTHRD",
|
||||||
|
.desc = "The number of cycles the CPU is not in wait state"
|
||||||
|
"\n\t\tand the CPU is running with another thread on the"
|
||||||
|
"\n\t\tCore.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 204,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "INST_SAMETHRD",
|
||||||
|
.desc = "The number of instructions executed on the CPU and"
|
||||||
|
"\n\t\tthe CPU is running by itself on the Core.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 205,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "INST_DIFFTHRD",
|
||||||
|
.desc = "The number of instructions executed on the CPU and"
|
||||||
|
"\n\t\tthe CPU is running with another thread on the Core.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 206,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "WRONG_BRANCH_PREDICTION",
|
||||||
|
.desc = "A count of the number of branches that were"
|
||||||
|
"\n\t\tpredicted incorrectly by the branch prediction"
|
||||||
|
"\n\t\tlogic in the Core. This includes incorrectly"
|
||||||
|
"\n\t\tpredicted branches that are executed in Firmware."
|
||||||
|
"\n\t\tExamples of instructions implemented in Firmware"
|
||||||
|
"\n\t\tare complicated instructions like MVCL (Move"
|
||||||
|
"\n\t\tCharacter Long) and PC (Program Call).",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 225,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "VX_BCD_EXECUTION_SLOTS",
|
||||||
|
.desc = "Count of floating point execution slots used for"
|
||||||
|
"\n\t\tfinished vector arithmetic Binary Coded Decimal"
|
||||||
|
"\n\t\tinstructions. Instructions: VAP, VSP, VMP, VMSP,"
|
||||||
|
"\n\t\tVDP, VSDP, VRP, VLIP, VSRP, VPSOP, VCP, VTP, VPKZ,"
|
||||||
|
"\n\t\tVUPKZ, VCVB, VCVBG, VCVD, VCVDG, VSCHP, VSCSHP,"
|
||||||
|
"\n\t\tVCSPH, VCLZDP, VPKZR, VSRPR, VUPKZH, VUPKZL, VTZ,"
|
||||||
|
"\n\t\tVUPH, VUPL, VCVBX, VCVDX.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 226,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DECIMAL_INSTRUCTIONS",
|
||||||
|
.desc = "Decimal instruction dispatched. Instructions: CVB,"
|
||||||
|
"\n\t\tCVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP, TP.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 232,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "LAST_HOST_TRANSLATIONS",
|
||||||
|
.desc = "Last Host Translation done.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 244,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TX_NC_TABORT",
|
||||||
|
.desc = "A transaction abort has occurred in a non-"
|
||||||
|
"\n\t\tconstrained transactional-execution mode.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 245,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TX_C_TABORT_NO_SPECIAL",
|
||||||
|
.desc = "A transaction abort has occurred in a constrained"
|
||||||
|
"\n\t\ttransactional-execution mode and the CPU is not"
|
||||||
|
"\n\t\tusing any special logic to allow the transaction to"
|
||||||
|
"\n\t\tcomplete.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 246,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "TX_C_TABORT_SPECIAL",
|
||||||
|
.desc = "A transaction abort has occurred in a constrained"
|
||||||
|
"\n\t\ttransactional-execution mode and the CPU is using"
|
||||||
|
"\n\t\tspecial logic to allow the transaction to complete.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 248,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DFLT_ACCESS",
|
||||||
|
.desc = "Cycles CPU spent obtaining access to Deflate unit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 253,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DFLT_CYCLES",
|
||||||
|
.desc = "Cycles CPU is using Deflate unit.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 256,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "SORTL",
|
||||||
|
.desc = "Increments by one for every SORT LISTS (SORTL)"
|
||||||
|
"\n\t\tinstruction executed.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 265,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DFLT_CC",
|
||||||
|
.desc = "Increments by one for every DEFLATE CONVERSION CALL"
|
||||||
|
"\n\t\t(DFLTCC) instruction executed.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 266,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "DFLT_CCFINISH",
|
||||||
|
.desc = "Increments by one for every DEFLATE CONVERSION CALL"
|
||||||
|
"\n\t\t(DFLTCC) instruction executed that ended in"
|
||||||
|
"\n\t\tCondition Codes 0, 1 or 2.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 267,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_INVOCATIONS",
|
||||||
|
.desc = "Increments by one for every NEURAL NETWORK"
|
||||||
|
"\n\t\tPROCESSING ASSIST (NNPA) instruction executed.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 268,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_COMPLETIONS",
|
||||||
|
.desc = "Increments by one for every NEURAL NETWORK"
|
||||||
|
"\n\t\tPROCESSING ASSIST (NNPA) instruction executed that"
|
||||||
|
"\n\t\tended in Condition Code 0.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 269,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_WAIT_LOCK",
|
||||||
|
.desc = "Cycles CPU spent obtaining access to IBM Z"
|
||||||
|
"\n\t\tIntegrated Accelerator for AI.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 270,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_HOLD_LOCK",
|
||||||
|
.desc = "Cycles CPU is using IBM Z Integrated Accelerator"
|
||||||
|
"\n\t\tfor AI.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 272,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_INST_ONCHIP",
|
||||||
|
.desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)"
|
||||||
|
"\n\t\tinstruction has used the Local On-Chip IBM Z"
|
||||||
|
"\n\t\tIntegrated Accelerator for AI during its execution",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 273,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_INST_OFFCHIP",
|
||||||
|
.desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)"
|
||||||
|
"\n\t\tinstruction has used an Off-Chip IBM Z Integrated"
|
||||||
|
"\n\t\tAccelerator for AI during its execution.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 274,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_INST_DIFF",
|
||||||
|
.desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)"
|
||||||
|
"\n\t\tinstruction has used a different IBM Z Integrated"
|
||||||
|
"\n\t\tAccelerator for AI since it was last executed.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 276,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_4K_PREFETCH",
|
||||||
|
.desc = "Number of 4K prefetches done for a remote IBM Z"
|
||||||
|
"\n\t\tIntegated Accelerator for AI.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 277,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_COMPL_LOCK",
|
||||||
|
.desc = "A PERFORM LOCKED OPERATION (PLO) has completed.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 278,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_RETRY_LOCK",
|
||||||
|
.desc = "A PERFORM LOCKED OPERATION (PLO) has been retried and"
|
||||||
|
"\n\t\tthe CPU did not use any special logic to allow the"
|
||||||
|
"\n\t\tPLO to complete.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 279,
|
||||||
|
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||||
|
.name = "NNPA_RETRY_LOCK_WITH_PLO",
|
||||||
|
.desc = "A PERFORM LOCKED OPERATION (PLO) has been retried and"
|
||||||
|
"\n\t\tthe CPU is using special logic to allow PLO to"
|
||||||
|
"\n\t\tcomplete.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 448,
|
||||||
|
.ctrset = CPUMF_CTRSET_MT_DIAG,
|
||||||
|
.name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE",
|
||||||
|
.desc = "Cycle count with one thread active",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctrnum = 449,
|
||||||
|
.ctrset = CPUMF_CTRSET_MT_DIAG,
|
||||||
|
.name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE",
|
||||||
|
.desc = "Cycle count with two threads active",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* Return the type number of the CPU Measurement facility from the sysfs file.
|
/* Return the type number of the CPU Measurement facility from the sysfs file.
|
||||||
* If the type number is equal to PERF_TYPE_RAW, then the prefix is 'r' to
|
* If the type number is equal to PERF_TYPE_RAW, then the prefix is 'r' to
|
||||||
* specify the raw counter number by the perf tool.
|
* specify the raw counter number by the perf tool.
|
||||||
@@ -3476,6 +4078,12 @@ static struct counters *get_counter(int ctrset, size_t *len)
|
|||||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||||
cp = cpumcf_z16_counters;
|
cp = cpumcf_z16_counters;
|
||||||
*len = ARRAY_SIZE(cpumcf_z16_counters);
|
*len = ARRAY_SIZE(cpumcf_z16_counters);
|
||||||
|
break;
|
||||||
|
case UTIL_ARCH_MACHINE_TYPE_Z17:
|
||||||
|
case UTIL_ARCH_MACHINE_TYPE_Z17_2:
|
||||||
|
cp = cpumcf_z17_counters;
|
||||||
|
*len = ARRAY_SIZE(cpumcf_z17_counters);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3526,7 +4134,7 @@ int main(int argc, char **argv)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(lscpumf_opt_vec, NULL);
|
||||||
|
|
||||||
ret = parse_args(argc, argv);
|
ret = parse_args(argc, argv);
|
||||||
if (read_info() == EXIT_FAILURE)
|
if (read_info() == EXIT_FAILURE)
|
||||||
|
|||||||
44
cpumf/lscpumf_cli.h
Normal file
44
cpumf/lscpumf_cli.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for lscpumf
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 LSCPUMF_CLI_H
|
||||||
|
#define LSCPUMF_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt lscpumf_opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "list-counters", no_argument, NULL, 'c' },
|
||||||
|
.desc = "Lists counters for which the LPAR is authorized.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "list-all-counters", no_argument, NULL, 'C' },
|
||||||
|
.desc = "Lists counters regardless of LPAR authorization.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "name", no_argument, NULL, 'n' },
|
||||||
|
.desc = "Displays counter names.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "info", no_argument, NULL, 'i' },
|
||||||
|
.desc = "Displays detailed information.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "list-sampling-events", no_argument, NULL, 's' },
|
||||||
|
.desc = "Lists sampling events for which the LPAR is authorized.",
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
359
cpumf/lshwc.c
359
cpumf/lshwc.c
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright IBM Corp. 2021
|
/* Copyright IBM Corp. 2021, 2024
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -37,11 +37,13 @@
|
|||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
#include "lib/util_path.h"
|
#include "lib/util_path.h"
|
||||||
#include "lib/util_scandir.h"
|
#include "lib/util_scandir.h"
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_str.h"
|
||||||
#include "lib/util_file.h"
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
#include "lshwc.h"
|
#include "lshwc.h"
|
||||||
|
#include "lshwc_cli.h"
|
||||||
|
|
||||||
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||||
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||||
@@ -51,18 +53,92 @@
|
|||||||
|
|
||||||
static unsigned int read_interval = IOCTLSLEEP;
|
static unsigned int read_interval = IOCTLSLEEP;
|
||||||
static int cfvn, csvn, authorization;
|
static int cfvn, csvn, authorization;
|
||||||
static unsigned long loop_count = 1;
|
static unsigned long loop_count = 1, timeout;
|
||||||
static unsigned char *ioctlbuffer;
|
static unsigned char *ioctlbuffer;
|
||||||
static bool allcpu;
|
static bool allcpu;
|
||||||
|
static char *ctrformat = "%ld";
|
||||||
|
static bool shortname;
|
||||||
|
static bool hideundef;
|
||||||
|
static bool delta, firstread;
|
||||||
|
static int output_format = FMT_CSV;
|
||||||
|
static bool quote_all;
|
||||||
|
static char *ctrlist; /* Comma separated list of counter to extract */
|
||||||
|
|
||||||
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||||||
static struct ctrname { /* List of defined counters */
|
static struct ctrname { /* List of defined counters */
|
||||||
char *name; /* Counter name */
|
char *name; /* Counter name */
|
||||||
|
char *label; /* Output name */
|
||||||
bool hitcnt; /* Counter number read from ioctl() */
|
bool hitcnt; /* Counter number read from ioctl() */
|
||||||
unsigned long total; /* Total counter value */
|
unsigned long total; /* Total counter value */
|
||||||
unsigned long *ccv; /* Per CPU counter value */
|
unsigned long *ccv; /* Per CPU counter value */
|
||||||
|
unsigned long *ccvprv; /* Per CPU counter value (previous read) */
|
||||||
} ctrname[MAXCTRS];
|
} ctrname[MAXCTRS];
|
||||||
|
|
||||||
|
struct time_formats {
|
||||||
|
char epoch[32];
|
||||||
|
char date_time[32];
|
||||||
|
char date[16];
|
||||||
|
char time[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void mk_labels(void)
|
||||||
|
{
|
||||||
|
char label[64];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
if (shortname) {
|
||||||
|
if (ctrname[i].name)
|
||||||
|
snprintf(label, sizeof(label), "%s", ctrname[i].name);
|
||||||
|
else
|
||||||
|
snprintf(label, sizeof(label), "U%ld", i);
|
||||||
|
} else {
|
||||||
|
if (output_format == FMT_CSV)
|
||||||
|
snprintf(label, sizeof(label), "%s(%ld)",
|
||||||
|
ctrname[i].name ?: "Counter", i);
|
||||||
|
else if (ctrname[i].name)
|
||||||
|
snprintf(label, sizeof(label), "%s", ctrname[i].name);
|
||||||
|
else
|
||||||
|
label[0] = 0;
|
||||||
|
}
|
||||||
|
if (output_format != FMT_CSV)
|
||||||
|
util_str_tolower(label);
|
||||||
|
ctrname[i].label = util_strdup(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *mk_name(int ctr, char *name)
|
||||||
|
{
|
||||||
|
char ctrset[8];
|
||||||
|
|
||||||
|
if (!shortname)
|
||||||
|
return util_strdup(name);
|
||||||
|
|
||||||
|
switch (libcpumf_ctrset(ctr, cfvn, csvn)) {
|
||||||
|
case CPUMF_CTRSET_BASIC:
|
||||||
|
ctrset[0] = 'B';
|
||||||
|
break;
|
||||||
|
case CPUMF_CTRSET_PROBLEM_STATE:
|
||||||
|
ctrset[0] = 'P';
|
||||||
|
break;
|
||||||
|
case CPUMF_CTRSET_CRYPTO:
|
||||||
|
ctrset[0] = 'C';
|
||||||
|
break;
|
||||||
|
case CPUMF_CTRSET_EXTENDED:
|
||||||
|
ctrset[0] = 'E';
|
||||||
|
break;
|
||||||
|
case CPUMF_CTRSET_MT_DIAG:
|
||||||
|
ctrset[0] = 'M';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctrset[0] = 'U';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sprintf(ctrset, "%c%d", ctrset[0], ctr);
|
||||||
|
|
||||||
|
return util_strdup(ctrset);
|
||||||
|
}
|
||||||
|
|
||||||
static bool read_counternames(void)
|
static bool read_counternames(void)
|
||||||
{
|
{
|
||||||
struct dirent **namelist = NULL;
|
struct dirent **namelist = NULL;
|
||||||
@@ -77,9 +153,11 @@ static bool read_counternames(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (i = 0; i < count && ctr >= 0; i++) {
|
for (i = 0; i < count && ctr >= 0; i++) {
|
||||||
|
if (!ctr_in_list(namelist[i]->d_name, ctrlist))
|
||||||
|
continue;
|
||||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||||
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
|
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
|
||||||
ctrname[ctr].name = util_strdup(namelist[i]->d_name);
|
ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name);
|
||||||
else
|
else
|
||||||
warnx("Cannot parse %s", ctrpath);
|
warnx("Cannot parse %s", ctrpath);
|
||||||
free(ctrpath);
|
free(ctrpath);
|
||||||
@@ -93,7 +171,9 @@ static void free_counternames(void)
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
free(ctrname[i].name);
|
free(ctrname[i].name);
|
||||||
|
free(ctrname[i].label);
|
||||||
free(ctrname[i].ccv);
|
free(ctrname[i].ccv);
|
||||||
|
free(ctrname[i].ccvprv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,77 +378,111 @@ static bool check_setpossible(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_header(void)
|
static void safe_strtime(char *dest, size_t size, const char *fmt, const struct tm *tm)
|
||||||
{
|
{
|
||||||
static bool header;
|
if (!strftime(dest, size, fmt, tm))
|
||||||
bool comma = false;
|
dest[0] = 0;
|
||||||
|
|
||||||
if (header)
|
|
||||||
return; /* Printed already */
|
|
||||||
printf("Date,Time,CPU,"); /* Print counter name and number */
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
|
||||||
if (!ctrname[i].hitcnt)
|
|
||||||
continue;
|
|
||||||
if (comma)
|
|
||||||
putchar(',');
|
|
||||||
printf("%s(%ld)", ctrname[i].name ?: "Counter", i);
|
|
||||||
comma = true;
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
header = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line(char *header)
|
static void generate_timestamp(struct time_formats *date)
|
||||||
{
|
{
|
||||||
bool comma;
|
time_t now = time(NULL);
|
||||||
|
struct tm *now_tm = localtime(&now);
|
||||||
|
|
||||||
show_header();
|
safe_strtime(date->date_time, sizeof(date->date_time), "%F %T%z", now_tm);
|
||||||
if (allcpu) {
|
safe_strtime(date->date, sizeof(date->date), "%F", now_tm);
|
||||||
for (unsigned int h = 0; h < max_possible_cpus; ++h) {
|
safe_strtime(date->time, sizeof(date->time), "%T", now_tm);
|
||||||
char txt[16];
|
safe_strtime(date->epoch, sizeof(date->epoch), "%s", now_tm);
|
||||||
|
}
|
||||||
|
|
||||||
if (!check[h].cpu_hit)
|
static void output_times(struct time_formats date)
|
||||||
continue;
|
{
|
||||||
comma = false;
|
if (output_format == FMT_CSV) {
|
||||||
snprintf(txt, sizeof(txt), "CPU%d,", h);
|
util_fmt_pair(FMT_PERSIST, "Date", "%s", date.date);
|
||||||
printf("%s%s", header, txt);
|
util_fmt_pair(FMT_PERSIST, "Time", "%s", date.time);
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
} else {
|
||||||
if (!ctrname[i].hitcnt)
|
util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "date_time", "%s", date.date_time);
|
||||||
continue;
|
util_fmt_pair(FMT_PERSIST, "time_epoch", "%s", date.epoch);
|
||||||
if (comma)
|
|
||||||
putchar(',');
|
|
||||||
printf("%ld", ctrname[i].ccv[h]);
|
|
||||||
comma = true;
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Print total count of all CPUs */
|
static void prepare_counter(size_t id, unsigned long value)
|
||||||
printf("%sTotal,", header);
|
{
|
||||||
comma = false;
|
if (output_format == FMT_CSV) {
|
||||||
|
util_fmt_pair(FMT_PERSIST, ctrname[id].label, ctrformat, value);
|
||||||
|
} else {
|
||||||
|
util_fmt_obj_start(FMT_ROW, NULL);
|
||||||
|
if (strlen(ctrname[id].label))
|
||||||
|
util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "name", ctrname[id].label);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "id", ctrformat, id);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "value", ctrformat, value);
|
||||||
|
util_fmt_obj_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_per_cpu(struct time_formats date)
|
||||||
|
{
|
||||||
|
for (unsigned int h = 0; h < max_possible_cpus; ++h) {
|
||||||
|
if (!check[h].cpu_hit)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char txt[16];
|
||||||
|
|
||||||
|
snprintf(txt, sizeof(txt), "CPU%d", h);
|
||||||
|
util_fmt_obj_start(FMT_ROW, "cpu_%d", h);
|
||||||
|
output_times(date);
|
||||||
|
if (output_format == FMT_CSV) {
|
||||||
|
util_fmt_pair(FMT_PERSIST, "CPU", "CPU%d", h);
|
||||||
|
} else {
|
||||||
|
util_fmt_pair(FMT_PERSIST, "cpu", "%d", h);
|
||||||
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
if (!ctrname[i].hitcnt)
|
||||||
|
continue;
|
||||||
|
if (hideundef && !ctrname[i].name)
|
||||||
|
continue;
|
||||||
|
prepare_counter(i, ctrname[i].ccv[h]);
|
||||||
|
}
|
||||||
|
if (output_format != FMT_CSV)
|
||||||
|
util_fmt_obj_end();
|
||||||
|
util_fmt_obj_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_total(struct time_formats date)
|
||||||
|
{
|
||||||
|
util_fmt_obj_start(FMT_ROW, "total");
|
||||||
|
output_times(date);
|
||||||
|
if (output_format == FMT_CSV) {
|
||||||
|
util_fmt_pair(FMT_PERSIST, "CPU", "%s", delta && !firstread ? "Delta" : "Total");
|
||||||
|
} else {
|
||||||
|
util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "cpu", "%s",
|
||||||
|
delta && !firstread ? "delta" : "total");
|
||||||
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
|
}
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
if (!ctrname[i].hitcnt)
|
if (!ctrname[i].hitcnt)
|
||||||
continue;
|
continue;
|
||||||
if (comma)
|
if (hideundef && !ctrname[i].name)
|
||||||
putchar(',');
|
continue;
|
||||||
printf("%ld", ctrname[i].total);
|
prepare_counter(i, ctrname[i].total);
|
||||||
comma = true;
|
|
||||||
ctrname[i].total = 0;
|
ctrname[i].total = 0;
|
||||||
ctrname[i].hitcnt = false;
|
ctrname[i].hitcnt = false;
|
||||||
}
|
}
|
||||||
putchar('\n');
|
if (output_format != FMT_CSV)
|
||||||
|
util_fmt_obj_end();
|
||||||
|
util_fmt_obj_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show(void)
|
static void show_format(void)
|
||||||
{
|
{
|
||||||
time_t now = time(NULL);
|
struct time_formats now;
|
||||||
struct tm *now_tm;
|
|
||||||
char now_text[32];
|
|
||||||
|
|
||||||
now_tm = localtime(&now);
|
generate_timestamp(&now);
|
||||||
strftime(now_text, sizeof(now_text), "%F,%T,", now_tm);
|
if (allcpu)
|
||||||
line(now_text);
|
output_per_cpu(now);
|
||||||
|
output_total(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return Counter set size numbers (in counters) */
|
/* Return Counter set size numbers (in counters) */
|
||||||
@@ -442,11 +556,18 @@ static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
|||||||
warnx("Invalid CPU number %d", cpu);
|
warnx("Invalid CPU number %d", cpu);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctrname[idx].ccv) /* Unknown counter */
|
if (delta) {
|
||||||
ctrname[idx].ccv = calloc(max_possible_cpus,
|
if (firstread) {
|
||||||
sizeof(unsigned long));
|
ctrname[idx].ccvprv[cpu] = value;
|
||||||
if (ctrname[idx].ccv)
|
ctrname[idx].ccv[cpu] = value;
|
||||||
|
} else {
|
||||||
|
ctrname[idx].ccv[cpu] = value - ctrname[idx].ccvprv[cpu];
|
||||||
|
ctrname[idx].ccvprv[cpu] = value;
|
||||||
|
value = ctrname[idx].ccv[cpu];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ctrname[idx].ccv[cpu] = value;
|
ctrname[idx].ccv[cpu] = value;
|
||||||
|
}
|
||||||
ctrname[idx].total += value;
|
ctrname[idx].total += value;
|
||||||
ctrname[idx].hitcnt = true;
|
ctrname[idx].hitcnt = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -493,7 +614,8 @@ static int test_read(struct s390_hwctr_read *read)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show();
|
show_format();
|
||||||
|
firstread = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,6 +678,7 @@ static void do_sleep(void)
|
|||||||
static int do_it(char *s)
|
static int do_it(char *s)
|
||||||
{
|
{
|
||||||
struct s390_hwctr_start start;
|
struct s390_hwctr_start start;
|
||||||
|
unsigned int flags = FMT_WARN;
|
||||||
int ioctlfd;
|
int ioctlfd;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -575,6 +698,24 @@ static int do_it(char *s)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (output_format == FMT_CSV)
|
||||||
|
flags |= FMT_NOMETA;
|
||||||
|
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ)
|
||||||
|
flags |= FMT_HANDLEINT;
|
||||||
|
if (quote_all)
|
||||||
|
flags |= FMT_QUOTEALL;
|
||||||
|
|
||||||
|
mk_labels();
|
||||||
|
util_fmt_init(stdout, output_format, flags, 1);
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, "lshwc");
|
||||||
|
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ) {
|
||||||
|
util_fmt_obj_start(FMT_ROW, "cpumcf info");
|
||||||
|
util_fmt_pair(FMT_PERSIST, "counter first", "%d", cfvn);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "counter second", "%d", csvn);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "authorization", "%d", authorization);
|
||||||
|
util_fmt_obj_end();
|
||||||
|
}
|
||||||
|
util_fmt_obj_start(FMT_LIST, "measurements");
|
||||||
for (unsigned long i = 0; !rc && i < loop_count; ++i) {
|
for (unsigned long i = 0; !rc && i < loop_count; ++i) {
|
||||||
rc = do_read(ioctlfd);
|
rc = do_read(ioctlfd);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@@ -584,32 +725,14 @@ static int do_it(char *s)
|
|||||||
if (read_interval && i + 1 < loop_count)
|
if (read_interval && i + 1 < loop_count)
|
||||||
do_sleep();
|
do_sleep();
|
||||||
}
|
}
|
||||||
|
util_fmt_obj_end();
|
||||||
|
util_fmt_obj_end();
|
||||||
|
util_fmt_exit();
|
||||||
rc = do_stop(ioctlfd);
|
rc = do_stop(ioctlfd);
|
||||||
close(ioctlfd);
|
close(ioctlfd);
|
||||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "all", no_argument, NULL, 'a' },
|
|
||||||
.desc = "Displays all CPUs in output"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "loop", required_argument, NULL, 'l' },
|
|
||||||
.argument = "NUMBER",
|
|
||||||
.desc = "Specifies loop count for next read"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "interval", required_argument, NULL, 'i' },
|
|
||||||
.argument = "NUMBER",
|
|
||||||
.desc = "Specifies interval between read operations (seconds)"
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "Read CPU Measurement facility counter sets",
|
.desc = "Read CPU Measurement facility counter sets",
|
||||||
.copyright_vec = {
|
.copyright_vec = {
|
||||||
@@ -634,11 +757,13 @@ static void have_support(void)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
enum util_fmt_t fmt;
|
||||||
|
unsigned long no;
|
||||||
char *slash;
|
char *slash;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(lshwc_opt_vec, NULL);
|
||||||
|
|
||||||
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
@@ -665,12 +790,71 @@ int main(int argc, char **argv)
|
|||||||
if (errno || *slash)
|
if (errno || *slash)
|
||||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
break;
|
break;
|
||||||
|
case 'H':
|
||||||
|
hideundef = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
shortname = true;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
ctrformat = "%lx";
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
ctrformat = "%#lx";
|
||||||
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
allcpu = true;
|
allcpu = true;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
delta = true;
|
||||||
|
firstread = true;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
errno = 0;
|
||||||
|
no = strtoul(optarg, &slash, 0);
|
||||||
|
if (errno)
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
switch (*slash) {
|
||||||
|
case 's':
|
||||||
|
case '\0':
|
||||||
|
timeout += no;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
timeout += no * 60;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
timeout += no * 60 * 60;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
timeout += no * 60 * 60 * 24;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
quote_all = true;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (!util_fmt_name_to_type(optarg, &fmt))
|
||||||
|
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
|
||||||
|
output_format = fmt;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
hideundef = true;
|
||||||
|
ctrlist = util_strdup(optarg);
|
||||||
|
util_str_rm_whitespace(optarg, ctrlist);
|
||||||
|
util_str_toupper(ctrlist);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeout && timeout < read_interval)
|
||||||
|
read_interval = timeout;
|
||||||
|
/* If no timeout specified, simply add zero */
|
||||||
|
loop_count += timeout / read_interval;
|
||||||
|
|
||||||
have_support();
|
have_support();
|
||||||
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -681,6 +865,11 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
ctrname[i].ccv = util_zalloc(max_possible_cpus * sizeof(unsigned long));
|
||||||
|
ctrname[i].ccvprv = util_zalloc(max_possible_cpus * sizeof(unsigned long));
|
||||||
|
}
|
||||||
|
|
||||||
if (optind >= argc) {
|
if (optind >= argc) {
|
||||||
ch = do_it(NULL);
|
ch = do_it(NULL);
|
||||||
} else {
|
} else {
|
||||||
@@ -692,5 +881,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
free_counternames();
|
free_counternames();
|
||||||
free(check);
|
free(check);
|
||||||
|
free(ioctlbuffer);
|
||||||
|
free(ctrlist);
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|||||||
78
cpumf/lshwc_cli.h
Normal file
78
cpumf/lshwc_cli.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for lshwc
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 LSHWC_CLI_H
|
||||||
|
#define LSHWC_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt lshwc_opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "all", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Displays all CPUs in output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "loop", required_argument, NULL, 'l' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "Specifies loop count for next read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "interval", required_argument, NULL, 'i' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "Specifies interval between read operations (seconds)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "short", no_argument, NULL, 's' },
|
||||||
|
.desc = "Abbreviate counter name with counter set letter and number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hex0x", no_argument, NULL, 'X' },
|
||||||
|
.desc = "Counter values in hexadecimal format with leading 0x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hex", no_argument, NULL, 'x' },
|
||||||
|
.desc = "Counter values in hexadecimal format"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hide", no_argument, NULL, 'H' },
|
||||||
|
.desc = "Do not display undefined counters of a counter set"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "delta", no_argument, NULL, 'd' },
|
||||||
|
.desc = "Display delta counter values"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "timeout", required_argument, NULL, 't' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "run time in s (seconds) m (minutes) h (hours) and d (days)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "quote-all", no_argument, NULL, 'q' },
|
||||||
|
.desc = "Apply quoting to all output elements"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "format", required_argument, NULL, 'f' },
|
||||||
|
.argument = "FORMAT",
|
||||||
|
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "counters", required_argument, NULL, 'c' },
|
||||||
|
.argument = "LIST",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
.desc = "Specify comma separated list of counters to display"
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
514
cpumf/lspai.c
514
cpumf/lspai.c
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
/* List available Processor Assist Instrumentation (PAI) counters. */
|
/* List available Processor Assist Instrumentation (PAI) counters. */
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -17,33 +16,26 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
|
||||||
#include "lib/util_opt.h"
|
|
||||||
#include "lib/util_prg.h"
|
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
#include "lib/util_path.h"
|
|
||||||
#include "lib/util_scandir.h"
|
|
||||||
#include "lib/util_libc.h"
|
|
||||||
#include "lib/util_file.h"
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
#include "lib/util_list.h"
|
#include "lib/util_list.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
#include "lib/util_scandir.h"
|
||||||
|
#include "lib/util_str.h"
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
#include "lspai_cli.h"
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "numeric", no_argument, NULL, 'n' },
|
|
||||||
.desc = "Sort PAI counters by counter number"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "type", required_argument, NULL, 't' },
|
|
||||||
.argument = "TYPE",
|
|
||||||
.desc = "Type of PAI counters to show: crypto, nnpa"
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "List Processor Assist Information counter sets",
|
.desc = "List Processor Assist Information counter sets",
|
||||||
@@ -51,13 +43,24 @@ static const struct util_prg prg = {
|
|||||||
{
|
{
|
||||||
.owner = "IBM Corp.",
|
.owner = "IBM Corp.",
|
||||||
.pub_first = 2023,
|
.pub_first = 2023,
|
||||||
.pub_last = 2023,
|
.pub_last = 2025,
|
||||||
},
|
},
|
||||||
UTIL_PRG_COPYRIGHT_END
|
UTIL_PRG_COPYRIGHT_END
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool allcpu; /* Show counter values per CPU */
|
||||||
static bool numsort; /* If true sort counter numerically */
|
static bool numsort; /* If true sort counter numerically */
|
||||||
|
static bool shortname; /* Use abbreviated counter names */
|
||||||
|
static bool delta, firstread; /* Display delta values */
|
||||||
|
static int output_format = -1; /* Generate style if >= 0 */
|
||||||
|
static unsigned int max_cpus; /* # of CPUs to read counter values from */
|
||||||
|
static unsigned int max_fds; /* # of file descriptor to read counter values */
|
||||||
|
static unsigned long loops; /* # loops */
|
||||||
|
static unsigned long read_interval = DEFAULT_LOOP_INTERVAL;
|
||||||
|
static cpu_set_t cpu_online_mask;
|
||||||
|
static char *ctrformat = "%ld"; /* Default counter output format */
|
||||||
|
static char *ctrlist; /* Comma separated list of counter to extract */
|
||||||
|
|
||||||
#define PAI_PATH "/bus/event_source/devices/%s"
|
#define PAI_PATH "/bus/event_source/devices/%s"
|
||||||
|
|
||||||
@@ -69,9 +72,18 @@ enum pai_types { /* Bit mask for supported PAI counters */
|
|||||||
|
|
||||||
static int pai_types_show;
|
static int pai_types_show;
|
||||||
|
|
||||||
|
struct pai_cpudata { /* Event data per CPU */
|
||||||
|
int fd; /* Event file descriptor */
|
||||||
|
int cpu; /* CPU number */
|
||||||
|
unsigned long value; /* Event value */
|
||||||
|
unsigned long prev_value; /* Previous value for deltas */
|
||||||
|
};
|
||||||
|
|
||||||
struct pai_ctrname { /* List of defined counters */
|
struct pai_ctrname { /* List of defined counters */
|
||||||
char *name; /* Counter name */
|
char *name; /* Counter name */
|
||||||
unsigned long nr; /* Counter number */
|
unsigned long nr; /* Counter number */
|
||||||
|
unsigned long total; /* Total count on all CPus */
|
||||||
|
struct pai_cpudata *data; /* Counter data per CPU */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pai_node { /* Head for PAI counter sets */
|
struct pai_node { /* Head for PAI counter sets */
|
||||||
@@ -104,6 +116,20 @@ static unsigned long pai_type_base(enum pai_types t)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return character for this counter set. */
|
||||||
|
static char pai_type_char(enum pai_types t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case pai_type_crypto:
|
||||||
|
return 'C';
|
||||||
|
case pai_type_nnpa:
|
||||||
|
return 'N';
|
||||||
|
case pai_type_max:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 'U';
|
||||||
|
}
|
||||||
|
|
||||||
/* Test PAI counter name from command line option. */
|
/* Test PAI counter name from command line option. */
|
||||||
static const char *pai_type_name(enum pai_types t)
|
static const char *pai_type_name(enum pai_types t)
|
||||||
{
|
{
|
||||||
@@ -145,16 +171,6 @@ static int pai_ctrcmp(const void *p1, const void *p2)
|
|||||||
return l->nr > r->nr ? 1 : -1;
|
return l->nr > r->nr ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert string to upper case. */
|
|
||||||
static char *str2uc(const char *s)
|
|
||||||
{
|
|
||||||
char *uc = util_strdup(s), *old_uc = uc;
|
|
||||||
|
|
||||||
for (; *uc; ++uc)
|
|
||||||
*uc = toupper(*uc);
|
|
||||||
return old_uc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read counter names and assigned event number from sysfs file tree.
|
/* Read counter names and assigned event number from sysfs file tree.
|
||||||
* Exit when sysfs directory can not be scanned.
|
* Exit when sysfs directory can not be scanned.
|
||||||
*/
|
*/
|
||||||
@@ -162,7 +178,7 @@ static void read_counternames(struct pai_node *node)
|
|||||||
{
|
{
|
||||||
int i, more = 0, ctr = 0, count = 0;
|
int i, more = 0, ctr = 0, count = 0;
|
||||||
struct dirent **namelist = NULL;
|
struct dirent **namelist = NULL;
|
||||||
char *path, *ctrpath;
|
char *path, *ctrpath, sname[128];
|
||||||
|
|
||||||
/* Read counter names and assigned event number. */
|
/* Read counter names and assigned event number. */
|
||||||
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
|
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
|
||||||
@@ -175,9 +191,18 @@ static void read_counternames(struct pai_node *node)
|
|||||||
for (i = 0; i < count && ctr >= 0; i++) {
|
for (i = 0; i < count && ctr >= 0; i++) {
|
||||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||||
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
|
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
|
||||||
|
snprintf(sname, sizeof(sname), "%c%ld",
|
||||||
|
pai_type_char(node->type), ctr - node->base);
|
||||||
|
if (!ctr_in_list(sname, ctrlist) &&
|
||||||
|
!ctr_in_list(namelist[i]->d_name, ctrlist)) {
|
||||||
|
/* Counter not listed in --counters option */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
node->ctrlist[node->ctridx].data = NULL;
|
||||||
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
|
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
|
||||||
node->ctrlist[node->ctridx++].nr = ctr;
|
node->ctrlist[node->ctridx++].nr = ctr;
|
||||||
more++;
|
more++;
|
||||||
|
max_fds++;
|
||||||
} else {
|
} else {
|
||||||
warnx("Cannot parse %s", ctrpath);
|
warnx("Cannot parse %s", ctrpath);
|
||||||
}
|
}
|
||||||
@@ -190,12 +215,47 @@ static void read_counternames(struct pai_node *node)
|
|||||||
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
|
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_painode(void)
|
static void format_painode(enum util_fmt_t fmt)
|
||||||
|
{
|
||||||
|
struct pai_node *node;
|
||||||
|
|
||||||
|
util_fmt_init(stdout, fmt, FMT_HANDLEINT, 1);
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, "pmu");
|
||||||
|
util_fmt_pair(FMT_PERSIST, "base", "%d", node->base);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "type", "%d", node->pmu);
|
||||||
|
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "pmu-name", "%s", node->sysfs_name);
|
||||||
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
util_fmt_obj_start(FMT_ROW, "counter");
|
||||||
|
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
|
||||||
|
if (shortname)
|
||||||
|
util_fmt_pair(FMT_QUOTE, "name", "%c%d",
|
||||||
|
pai_type_char(node->type),
|
||||||
|
node->ctrlist[i].nr - node->base);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
|
||||||
|
util_fmt_obj_end();
|
||||||
|
}
|
||||||
|
util_fmt_obj_end(); /* Counters */
|
||||||
|
util_fmt_obj_end(); /* PMU */
|
||||||
|
}
|
||||||
|
util_fmt_obj_end();
|
||||||
|
util_fmt_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void list_painode(void)
|
||||||
{
|
{
|
||||||
struct pai_node *node;
|
struct pai_node *node;
|
||||||
int indent = 0;
|
int indent = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
|
if (output_format != -1) {
|
||||||
|
format_painode(output_format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
util_list_iterate(&pai_list, node) {
|
util_list_iterate(&pai_list, node) {
|
||||||
for (int i = 0; i < node->ctridx; ++i)
|
for (int i = 0; i < node->ctridx; ++i)
|
||||||
indent = MAX((size_t)indent, strlen(node->ctrlist[i].name));
|
indent = MAX((size_t)indent, strlen(node->ctrlist[i].name));
|
||||||
@@ -223,8 +283,10 @@ static void free_painode(void)
|
|||||||
|
|
||||||
util_list_iterate_safe(&pai_list, node, next) {
|
util_list_iterate_safe(&pai_list, node, next) {
|
||||||
free(node->name_uc);
|
free(node->name_uc);
|
||||||
for (int i = 0; i < node->ctridx; ++i)
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
free(node->ctrlist[i].name);
|
free(node->ctrlist[i].name);
|
||||||
|
free(node->ctrlist[i].data);
|
||||||
|
}
|
||||||
free(node->ctrlist);
|
free(node->ctrlist);
|
||||||
free(node);
|
free(node);
|
||||||
}
|
}
|
||||||
@@ -238,12 +300,13 @@ static void make_painode(enum pai_types t)
|
|||||||
node->type = t;
|
node->type = t;
|
||||||
node->sysfs_name = pai_type_sysfs(t);
|
node->sysfs_name = pai_type_sysfs(t);
|
||||||
node->name = pai_type_name(t);
|
node->name = pai_type_name(t);
|
||||||
node->name_uc = str2uc(node->name);
|
node->name_uc = util_strdup(node->name);
|
||||||
|
util_str_toupper(node->name_uc);
|
||||||
node->filter_name = pai_type_filter(t);
|
node->filter_name = pai_type_filter(t);
|
||||||
node->base = pai_type_base(t);
|
node->base = pai_type_base(t);
|
||||||
|
|
||||||
/* Read PMU type number. */
|
/* Read PMU type number. */
|
||||||
path = util_path_sysfs(PAI_PATH, node->sysfs_name);
|
util_asprintf(&path, PAI_PATH, node->sysfs_name);
|
||||||
node->pmu = libcpumf_pmutype(path);
|
node->pmu = libcpumf_pmutype(path);
|
||||||
if (node->pmu < 0)
|
if (node->pmu < 0)
|
||||||
errx(EXIT_FAILURE, "Cannot open %s", path);
|
errx(EXIT_FAILURE, "Cannot open %s", path);
|
||||||
@@ -267,6 +330,267 @@ static void sort_painode(void)
|
|||||||
util_list_sort(&pai_list, painode_cmp, NULL);
|
util_list_sort(&pai_list, painode_cmp, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read counter value. */
|
||||||
|
static unsigned long event_read(int fd)
|
||||||
|
{
|
||||||
|
unsigned long count;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = read(fd, &count, sizeof(count));
|
||||||
|
if (rc != sizeof(count))
|
||||||
|
err(EXIT_FAILURE, "Failed to read counter value");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write header. */
|
||||||
|
static void line_header(void)
|
||||||
|
{
|
||||||
|
struct pai_node *node;
|
||||||
|
static bool header;
|
||||||
|
bool comma = false;
|
||||||
|
|
||||||
|
if (header)
|
||||||
|
return; /* Printed already */
|
||||||
|
printf("Date,Time,CPU,"); /* Print counter name and number */
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
if (comma)
|
||||||
|
putchar(',');
|
||||||
|
if (shortname) {
|
||||||
|
printf("%c%ld", pai_type_char(node->type),
|
||||||
|
node->ctrlist[i].nr - node->base);
|
||||||
|
} else {
|
||||||
|
printf("%s(%ld)", node->ctrlist[i].name ?: node->name_uc,
|
||||||
|
node->ctrlist[i].nr - node->base);
|
||||||
|
}
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print CPU specific counter values. */
|
||||||
|
static void line_cpu(char *header)
|
||||||
|
{
|
||||||
|
struct pai_cpudata *data;
|
||||||
|
struct pai_node *node;
|
||||||
|
bool comma;
|
||||||
|
char txt[16];
|
||||||
|
|
||||||
|
for (unsigned int h = 0; h < max_cpus; ++h) {
|
||||||
|
comma = false;
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
data = &node->ctrlist[i].data[h];
|
||||||
|
if (!comma) {
|
||||||
|
snprintf(txt, sizeof(txt), "CPU%d,", data->cpu);
|
||||||
|
printf("%s,%s", header, txt);
|
||||||
|
} else {
|
||||||
|
putchar(',');
|
||||||
|
}
|
||||||
|
printf(ctrformat, data->value);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write an output line. */
|
||||||
|
static void line_out(char *header)
|
||||||
|
{
|
||||||
|
struct pai_node *node;
|
||||||
|
bool comma;
|
||||||
|
|
||||||
|
line_header();
|
||||||
|
if (allcpu)
|
||||||
|
line_cpu(header);
|
||||||
|
|
||||||
|
/* Print total count of all CPUs */
|
||||||
|
printf("%s,%s,", header, delta && !firstread ? "Delta" : "Total");
|
||||||
|
comma = false;
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
if (comma)
|
||||||
|
putchar(',');
|
||||||
|
printf(ctrformat, node->ctrlist[i].total);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a formatted line. */
|
||||||
|
static void format_line_out(time_t now, char *now_text)
|
||||||
|
{
|
||||||
|
static unsigned int called;
|
||||||
|
struct pai_node *node;
|
||||||
|
char cpuid[16];
|
||||||
|
|
||||||
|
if (!called) {
|
||||||
|
util_fmt_init(stdout, output_format, FMT_DEFAULT | FMT_HANDLEINT, 1);
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||||
|
util_fmt_obj_start(FMT_LIST, "measurements");
|
||||||
|
}
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
if (!node->ctridx) /* Counter set not selected */
|
||||||
|
continue;
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, "entry");
|
||||||
|
util_fmt_pair(FMT_PERSIST, "iteration", "%d", called++);
|
||||||
|
util_fmt_pair(FMT_PERSIST, "time_epoch", "%d", now);
|
||||||
|
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "time", "%s", now_text);
|
||||||
|
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "valuetype",
|
||||||
|
(delta && !firstread) ? "delta" : "total");
|
||||||
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
util_fmt_obj_start(FMT_ROW, "counter");
|
||||||
|
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
|
||||||
|
if (shortname)
|
||||||
|
util_fmt_pair(FMT_QUOTE, "shortname", "%c%d",
|
||||||
|
pai_type_char(node->type),
|
||||||
|
node->ctrlist[i].nr - node->base);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "value", ctrformat, node->ctrlist[i].total);
|
||||||
|
if (allcpu) {
|
||||||
|
for (unsigned int j = 0; j < max_cpus; ++j) {
|
||||||
|
snprintf(cpuid, sizeof(cpuid), "cpu%d", j);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, cpuid, ctrformat,
|
||||||
|
node->ctrlist[i].data[j].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
util_fmt_obj_end();
|
||||||
|
}
|
||||||
|
util_fmt_obj_end(); /* Counters */
|
||||||
|
util_fmt_obj_end(); /* Entry */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminate formatted output. */
|
||||||
|
static void format_line_end(void)
|
||||||
|
{
|
||||||
|
util_fmt_obj_end(); /* Iteration */
|
||||||
|
util_fmt_obj_end(); /* Default */
|
||||||
|
util_fmt_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display counter values. */
|
||||||
|
static void show_values(void)
|
||||||
|
{
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *now_tm;
|
||||||
|
char now_text[32];
|
||||||
|
|
||||||
|
now_tm = localtime(&now);
|
||||||
|
if (output_format != -1) {
|
||||||
|
strftime(now_text, sizeof(now_text), "%F %T%z", now_tm);
|
||||||
|
format_line_out(now, now_text);
|
||||||
|
} else {
|
||||||
|
strftime(now_text, sizeof(now_text), "%F,%T", now_tm);
|
||||||
|
line_out(now_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read each counter value. */
|
||||||
|
static void read_painode(void)
|
||||||
|
{
|
||||||
|
struct pai_cpudata *data;
|
||||||
|
struct pai_node *node;
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
node->ctrlist[i].total = 0;
|
||||||
|
for (size_t j = 0; j < max_cpus; ++j) {
|
||||||
|
data = &node->ctrlist[i].data[j];
|
||||||
|
value = event_read(data->fd);
|
||||||
|
if (delta) {
|
||||||
|
data->value = value - data->prev_value;
|
||||||
|
data->prev_value = value;
|
||||||
|
} else {
|
||||||
|
data->value = value;
|
||||||
|
}
|
||||||
|
node->ctrlist[i].total += data->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_painode(void)
|
||||||
|
{
|
||||||
|
for (unsigned long i = 0; i < loops; ++i) {
|
||||||
|
read_painode();
|
||||||
|
show_values();
|
||||||
|
if (i + 1 < loops)
|
||||||
|
sleep(read_interval);
|
||||||
|
firstread = false;
|
||||||
|
}
|
||||||
|
format_line_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Install one event. */
|
||||||
|
static int event_add(int cpu, int idx, struct pai_node *node)
|
||||||
|
{
|
||||||
|
struct perf_event_attr attr;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
memset(&attr, 0, sizeof(attr));
|
||||||
|
attr.size = sizeof(attr);
|
||||||
|
attr.config = node->ctrlist[idx].nr;
|
||||||
|
attr.type = node->pmu;
|
||||||
|
fd = perf_event_open(&attr, -1, cpu, -1, 0);
|
||||||
|
if (fd == -1)
|
||||||
|
err(EXIT_FAILURE, "Failed to open perf event: file descriptor not available");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increase number of file descriptors this process can open. */
|
||||||
|
static void event_fdlimit(void)
|
||||||
|
{
|
||||||
|
unsigned int needed = 3 + max_fds * max_cpus;
|
||||||
|
struct rlimit rlimit;
|
||||||
|
|
||||||
|
if (getrlimit(RLIMIT_NOFILE, &rlimit) == -1)
|
||||||
|
err(EXIT_FAILURE, "Failed to read RLIMIT_NOFILE");
|
||||||
|
if (needed > rlimit.rlim_cur)
|
||||||
|
rlimit.rlim_cur = needed;
|
||||||
|
if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
|
||||||
|
err(EXIT_FAILURE, "Failed to set RLIMIT_NOFILE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Install all events and iterate over requested read operations. */
|
||||||
|
static void event_painode(void)
|
||||||
|
{
|
||||||
|
size_t pai_cpudata_sz = sizeof(struct pai_cpudata) * max_cpus;
|
||||||
|
struct pai_cpudata *data;
|
||||||
|
struct pai_node *node;
|
||||||
|
|
||||||
|
event_fdlimit();
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
node->ctrlist[i].data = util_malloc(pai_cpudata_sz);
|
||||||
|
data = node->ctrlist[i].data;
|
||||||
|
for (unsigned int j = 0; j < CPU_SETSIZE; ++j) {
|
||||||
|
if (CPU_ISSET(j, &cpu_online_mask)) {
|
||||||
|
data->cpu = j;
|
||||||
|
data->fd = event_add(j, i, node);
|
||||||
|
data->value = 0;
|
||||||
|
data->prev_value = 0;
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait_painode();
|
||||||
|
util_list_iterate(&pai_list, node) {
|
||||||
|
for (int i = 0; i < node->ctridx; ++i) {
|
||||||
|
for (unsigned int j = 0; j < max_cpus; ++j)
|
||||||
|
close(node->ctrlist[i].data[j].fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for hardware support and return false if not available. */
|
/* Check for hardware support and return false if not available. */
|
||||||
static bool have_support(enum pai_types t)
|
static bool have_support(enum pai_types t)
|
||||||
{
|
{
|
||||||
@@ -303,19 +627,60 @@ static void check_type_name(const char *type)
|
|||||||
errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
|
errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get list of specified CPUs from command line. Check if these CPUs
|
||||||
|
* exist and are online. Ignore those CPUs which are not available and
|
||||||
|
* issue one warning when CPUs have been specified but are not online.
|
||||||
|
*/
|
||||||
|
static void get_cpulist(char *parm)
|
||||||
|
{
|
||||||
|
bool warned = false;
|
||||||
|
cpu_set_t cpulist;
|
||||||
|
int i, rc;
|
||||||
|
|
||||||
|
CPU_ZERO(&cpulist);
|
||||||
|
rc = libcpumf_cpuset(parm, &cpulist);
|
||||||
|
if (rc)
|
||||||
|
err(EXIT_FAILURE, "Cannot parse cpulist %s", parm);
|
||||||
|
for (i = 0; i < CPU_SETSIZE; ++i) {
|
||||||
|
if (CPU_ISSET(i, &cpulist) && !CPU_ISSET(i, &cpu_online_mask)) {
|
||||||
|
if (!warned) {
|
||||||
|
warnx("some CPU(s) are offline, ignored");
|
||||||
|
warned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!CPU_ISSET(i, &cpulist) && CPU_ISSET(i, &cpu_online_mask))
|
||||||
|
CPU_CLR(i, &cpu_online_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
bool list_only = true;
|
||||||
|
enum util_fmt_t fmt;
|
||||||
|
bool i_flag = false;
|
||||||
|
bool l_flag = false;
|
||||||
|
char *endchar;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
util_list_init(&pai_list, struct pai_node, node);
|
util_list_init(&pai_list, struct pai_node, node);
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(lspai_opt_vec, NULL);
|
||||||
|
|
||||||
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
default:
|
default:
|
||||||
util_opt_print_parse_error(ch, argv);
|
util_opt_print_parse_error(ch, argv);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
case 'a':
|
||||||
|
allcpu = true;
|
||||||
|
list_only = false;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delta = true;
|
||||||
|
firstread = true;
|
||||||
|
list_only = false;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
util_prg_print_help();
|
util_prg_print_help();
|
||||||
util_opt_print_help();
|
util_opt_print_help();
|
||||||
@@ -323,16 +688,80 @@ int main(int argc, char **argv)
|
|||||||
case 'v':
|
case 'v':
|
||||||
util_prg_print_version();
|
util_prg_print_version();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
case 'i':
|
||||||
|
i_flag = true;
|
||||||
|
list_only = false;
|
||||||
|
errno = 0;
|
||||||
|
read_interval = strtoul(optarg, &endchar, 0);
|
||||||
|
if (errno || *endchar)
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
l_flag = true;
|
||||||
|
list_only = false;
|
||||||
|
errno = 0;
|
||||||
|
loops = strtoul(optarg, &endchar, 0);
|
||||||
|
if (errno || *endchar)
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
numsort = true;
|
numsort = true;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
list_only = false;
|
||||||
|
shortname = true;
|
||||||
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
check_type_name(optarg);
|
check_type_name(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
list_only = false;
|
||||||
|
ctrformat = "%lx";
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
list_only = false;
|
||||||
|
ctrformat = "%#lx";
|
||||||
|
break;
|
||||||
|
case OPT_FORMAT:
|
||||||
|
if (!util_fmt_name_to_type(optarg, &fmt))
|
||||||
|
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
|
||||||
|
output_format = fmt;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
ctrlist = util_strdup(optarg);
|
||||||
|
util_str_rm_whitespace(optarg, ctrlist);
|
||||||
|
util_str_toupper(ctrlist);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nothing specified, show all PAI counters */
|
if (i_flag && !l_flag) {
|
||||||
|
util_prg_print_help();
|
||||||
|
util_opt_print_help();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read currently online CPUs and create a bit mask.
|
||||||
|
* This bitmap of online CPUs is used to check command line parameter
|
||||||
|
* for valid CPUs
|
||||||
|
* When any of the flags which set variable list_only to false have
|
||||||
|
* be specified, lets also show the counter value, not just list them.
|
||||||
|
*/
|
||||||
|
if (optind < argc) /* List of CPUs on command line */
|
||||||
|
list_only = false;
|
||||||
|
if (!list_only) { /* Show counter values */
|
||||||
|
ch = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpu_online_mask);
|
||||||
|
if (ch)
|
||||||
|
err(EXIT_FAILURE, "Cannot read file /sys/" S390_CPUS_ONLINE);
|
||||||
|
while (optind < argc)
|
||||||
|
get_cpulist(argv[optind++]);
|
||||||
|
max_cpus = CPU_COUNT(&cpu_online_mask);
|
||||||
|
if (!loops)
|
||||||
|
loops = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nothing specified, use all PAI counters */
|
||||||
if (!pai_types_show)
|
if (!pai_types_show)
|
||||||
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
|
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
|
||||||
|
|
||||||
@@ -346,7 +775,12 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort_painode();
|
sort_painode();
|
||||||
show_painode();
|
ch = EXIT_SUCCESS;
|
||||||
|
if (!list_only)
|
||||||
|
event_painode();
|
||||||
|
else
|
||||||
|
list_painode();
|
||||||
free_painode();
|
free_painode();
|
||||||
|
free(ctrlist);
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|||||||
81
cpumf/lspai_cli.h
Normal file
81
cpumf/lspai_cli.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for lspai
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 LSPAI_CLI_H
|
||||||
|
#define LSPAI_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
#define STR_SUB(x) #x
|
||||||
|
#define STR(x) STR_SUB(x)
|
||||||
|
|
||||||
|
#define OPT_FORMAT 256 /* --format XXX option */
|
||||||
|
#define DEFAULT_LOOP_INTERVAL 60 /* loop interval in seconds */
|
||||||
|
|
||||||
|
static struct util_opt lspai_opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "all", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Displays all CPUs in output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "delta", no_argument, NULL, 'd' },
|
||||||
|
.desc = "Display delta counter values"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "counters", required_argument, NULL, 'c' },
|
||||||
|
.argument = "LIST",
|
||||||
|
.desc = "Specify comma separated list of counters to display"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "format", required_argument, NULL, OPT_FORMAT },
|
||||||
|
.argument = "FORMAT",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "loops", required_argument, NULL, 'l' },
|
||||||
|
.argument = "COUNT",
|
||||||
|
.desc = "Number of read operations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "interval", required_argument, NULL, 'i' },
|
||||||
|
.argument = "SECONDS",
|
||||||
|
.desc = "Time to wait between loop iterations (default "
|
||||||
|
STR(DEFAULT_LOOP_INTERVAL) "s)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "numeric", no_argument, NULL, 'n' },
|
||||||
|
.desc = "Sort PAI counters by counter number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "short", no_argument, NULL, 's' },
|
||||||
|
.desc = "Abbreviate counter name with counter set letter and number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "type", required_argument, NULL, 't' },
|
||||||
|
.argument = "TYPE",
|
||||||
|
.desc = "Type of PAI counters to show: crypto, nnpa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hex0x", no_argument, NULL, 'X' },
|
||||||
|
.desc = "Counter values in hexadecimal format with leading 0x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hex", no_argument, NULL, 'x' },
|
||||||
|
.desc = "Counter values in hexadecimal format"
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,9 +17,9 @@ chcpumf \- manage the CPU-measurement facilities support
|
|||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B chcpumf
|
.B chcpumf
|
||||||
.RB [ \-m | \-\-min
|
.RB [ \-m | \-\-min
|
||||||
.IR num_sdb ]
|
.IR NUM_SDB ]
|
||||||
.RB [ \-x | \-\-max
|
.RB [ \-x | \-\-max
|
||||||
.IR num_sdb ]
|
.IR NUM_SDB ]
|
||||||
.RB [ \-V | \-\-verbose ]
|
.RB [ \-V | \-\-verbose ]
|
||||||
.br
|
.br
|
||||||
.B chcpumf
|
.B chcpumf
|
||||||
@@ -36,14 +36,14 @@ facilities for Linux on System z.
|
|||||||
.
|
.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.BR \-m ", " \-\-min " \fInum_sdb\fP"
|
.BR \-m ", " \-\-min " \fINUM_SDB\fP"
|
||||||
Specifies the minimum sampling facility buffer size in sample-data-blocks
|
Specifies the minimum sampling facility buffer size in sample-data-blocks
|
||||||
(SDB). A sample-data-block consumes about 4 kilobytes.
|
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||||
|
|
||||||
This is the initial buffer size when you start the sampling facility.
|
This is the initial buffer size when you start the sampling facility.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.BR \-x ", " \-\-max " \fInum_sdb\fP"
|
.BR \-x ", " \-\-max " \fINUM_SDB\fP"
|
||||||
Specifies the maximum sampling facility buffer size in sample-data-blocks
|
Specifies the maximum sampling facility buffer size in sample-data-blocks
|
||||||
(SDB). A sample-data-block consumes about 4 kilobytes.
|
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ counters for which the LPAR is not authorized. For counter measurements with
|
|||||||
the perf program, the raw event identifier is displayed.
|
the perf program, the raw event identifier is displayed.
|
||||||
For Linux version 5.5 and later, the raw event identifier is
|
For Linux version 5.5 and later, the raw event identifier is
|
||||||
displayed as <type>:<number>, where type is an integer that the kernel
|
displayed as <type>:<number>, where type is an integer that the kernel
|
||||||
assignes to the CPU Measurement counter facility device driver.
|
assigns to the CPU Measurement counter facility device driver.
|
||||||
For earlier Linux versions the raw event identifier is displayed as r<number>.
|
For earlier Linux versions the raw event identifier is displayed as r<number>.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -7,19 +7,25 @@
|
|||||||
.\" ----------------------------------------------------------------------
|
.\" ----------------------------------------------------------------------
|
||||||
.ds c \fBlshwc\fP
|
.ds c \fBlshwc\fP
|
||||||
.
|
.
|
||||||
.TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs"
|
.TH \*c "8" "Mar 2025" "s390-tools" "CPU-MF management programs"
|
||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\*c \- extract CPU Measurement Facilities counter sets
|
lshwc \- extract CPU Measurement Facilities counter sets
|
||||||
.
|
.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\*c
|
\*c
|
||||||
.RB [ \-a ]
|
.RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ]
|
||||||
.RB [ \-l
|
.RB [ \-l
|
||||||
.IR count ]
|
.IR COUNT ]
|
||||||
.RB [ \-i
|
.RB [ \-i
|
||||||
.IR interval ]
|
.IR INTERVAL ]
|
||||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
.RB [ \-t
|
||||||
|
.IR TIME ]
|
||||||
|
.RB [ \-f
|
||||||
|
.IR FORMAT ]
|
||||||
|
.RB [ \-c
|
||||||
|
.IR LIST ]
|
||||||
|
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
.BR \-h | \-\-help
|
.BR \-h | \-\-help
|
||||||
@@ -51,17 +57,130 @@ Displays counter values from each CPU.
|
|||||||
The default is a total summary line of all counters from all CPUs.
|
The default is a total summary line of all counters from all CPUs.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.BR \-i ", " \-\-interval \fI\ seconds\fP
|
.BR \-d ", " \-\-delta
|
||||||
|
Displays counter values in form of deltas.
|
||||||
|
Each counter value shows the increment to the previous output line.
|
||||||
|
Without this flag the total value of each counter is displayed.
|
||||||
|
See Examples.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
|
||||||
Specifies a time interval, in seconds,
|
Specifies a time interval, in seconds,
|
||||||
that the command waits between read operations.
|
that the command waits between read operations.
|
||||||
The default is 60 seconds.
|
The default is 60 seconds.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.BR \-l ", " \-\-loop \fI\ count\fP
|
.BR \-l ", " \-\-loop \fI\ COUNT\fP
|
||||||
Performs the specified number of read operations.
|
Performs the specified number of read operations.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
.BR \-H ", " \-\-hide
|
||||||
|
Do not display values of undefined counters.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-s ", " \-\-short
|
||||||
|
Displays counter names as short names in the heading.
|
||||||
|
A short name consists of
|
||||||
|
a letter for the counter set followed by a number.
|
||||||
|
For example,
|
||||||
|
this flag displays counter number 0 in the basic counter
|
||||||
|
set as
|
||||||
|
.B B0
|
||||||
|
instead of
|
||||||
|
.BR Cycles .
|
||||||
|
Valid counter set letters are:
|
||||||
|
.RS
|
||||||
|
.IP B
|
||||||
|
Basic counter set
|
||||||
|
.IP P
|
||||||
|
Problem state counter set
|
||||||
|
.IP C
|
||||||
|
Crypto counter set
|
||||||
|
.IP E
|
||||||
|
Extended counter set
|
||||||
|
.IP M
|
||||||
|
MT-Diagnostic counter set
|
||||||
|
.IP U
|
||||||
|
Undefined counter.
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-t ", " \-\-time
|
||||||
|
Specifies the runtime as an integer in days,
|
||||||
|
hours, minutes or seconds.
|
||||||
|
This option can be specified multiple times:
|
||||||
|
the runtime sums up.
|
||||||
|
Specify time as an integer followed by one of the letters
|
||||||
|
.RS
|
||||||
|
.IP d
|
||||||
|
Specifies the runtime in days.
|
||||||
|
.IP h
|
||||||
|
Specifies the runtime in hours.
|
||||||
|
.IP m
|
||||||
|
Specifies the runtime in minutes.
|
||||||
|
.IP s
|
||||||
|
Specifies the runtime in seconds.
|
||||||
|
This is the default when no letter is given.
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-x ", " \-\-hex
|
||||||
|
Displays counter values as hexadecimal values.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-X ", " \-\-hex0x
|
||||||
|
Displays counter values as hexadecimal values with a leading 0x prefix.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-q
|
||||||
|
Apply quoting to every output element, regardless of content or format.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-f ", " \-\-format \fI\ FORMAT\fP
|
||||||
|
Retrieve output in one of the following formats:
|
||||||
|
JSON, CSV, JSON-SEQ or PAIRS.
|
||||||
|
If no format is specified, the output defaults to CSV.
|
||||||
|
.
|
||||||
|
.SS JSON Output Structure
|
||||||
|
The JSON output contains two top-level objects:
|
||||||
|
.B "meta"
|
||||||
|
and
|
||||||
|
.B "lshwc"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B meta
|
||||||
|
General metadata such as API version, host, and timestamp.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B lshwc
|
||||||
|
Measurement data and configuration.
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.IP \fBcpumcf info\fP
|
||||||
|
CPU Measurement Counter Facility information.
|
||||||
|
.IP \fBmeasurements\fP
|
||||||
|
An array of objects, each representing a CPU or "total".
|
||||||
|
Each object includes:
|
||||||
|
.RS
|
||||||
|
.IP \fBcpu\fP
|
||||||
|
CPU identifier, such as 0, 1 or "delta" or "total".
|
||||||
|
.IP \fBdate_time\fP
|
||||||
|
Timestamp in ISO 8601 format.
|
||||||
|
.IP \fBtime_epoch\fP
|
||||||
|
The number of seconds since the epoch: 1970-01-01 00:00:00 UTC.
|
||||||
|
.IP \fBcounters\fP
|
||||||
|
An array of counter objects, each containing \fBname\fP (if available), \fBid\fP,
|
||||||
|
and \fBvalue\fP.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-c ", " \-\-counters \fI\ LIST\fP
|
||||||
|
Specify a comma-separated list of counter names to be printed.
|
||||||
|
If a counter name does not match any valid counter name, it is omitted. (Implies -H)
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
|
||||||
A comma-separated list of CPUs.
|
A comma-separated list of CPUs.
|
||||||
Each CPU can optionally be followed by characters that specify the counter set.
|
Each CPU can optionally be followed by characters that specify the counter set.
|
||||||
See below for details.
|
See below for details.
|
||||||
@@ -94,7 +213,7 @@ Two read operations are performed and a summary line is printed for each
|
|||||||
read operation.
|
read operation.
|
||||||
.sp 1
|
.sp 1
|
||||||
.nf
|
.nf
|
||||||
.ft CW
|
.ft CR
|
||||||
# lshwc -l2 0-1:BP
|
# lshwc -l2 0-1:BP
|
||||||
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
||||||
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||||
@@ -105,10 +224,10 @@ Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES
|
|||||||
.ft
|
.ft
|
||||||
.fi
|
.fi
|
||||||
.sp 1
|
.sp 1
|
||||||
This example shows the counter values of the problem state counter set
|
This example shows the counter values of the problem state counter set per CPU.
|
||||||
per CPU. CPU 0 and CPU 1 is selected.
|
CPU 0 and CPU 1 are selected.
|
||||||
.nf
|
.nf
|
||||||
.ft CW
|
.ft CR
|
||||||
.sp 1
|
.sp 1
|
||||||
# lshwc -l3 -a 0-1:P
|
# lshwc -l3 -a 0-1:P
|
||||||
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||||
@@ -123,6 +242,121 @@ Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
|||||||
2021-04-01,11:56:47,Total,6432163447,2978400903
|
2021-04-01,11:56:47,Total,6432163447,2978400903
|
||||||
.ft
|
.ft
|
||||||
.fi
|
.fi
|
||||||
|
.sp 1
|
||||||
|
This example shows the counter values of the basic counter set
|
||||||
|
using delta output format.
|
||||||
|
.nf
|
||||||
|
.ft CR
|
||||||
|
.sp 1
|
||||||
|
# lshwc -d -l 10 -i 5 -s :b
|
||||||
|
Date,Time,CPU,B0,B1,B2,B3,B4,B5
|
||||||
|
2025-03-26,10:34:19,Total,208075,117287,1950,50548,1082,49609
|
||||||
|
2025-03-26,10:34:24,Delta,85800055,70353492,590286,13228290,364034,12945804
|
||||||
|
2025-03-26,10:34:29,Delta,70654751,60656797,483047,10838672,305703,10570868
|
||||||
|
2025-03-26,10:34:34,Delta,81043162,69476160,587141,13228161,376662,12868298
|
||||||
|
2025-03-26,10:34:39,Delta,73434017,62675417,524857,11787256,333966,11543649
|
||||||
|
2025-03-26,10:34:44,Delta,68367967,58452919,506712,11370740,310785,10589883
|
||||||
|
2025-03-26,10:34:49,Delta,70351947,57607764,507675,11433377,312433,10676243
|
||||||
|
2025-03-26,10:34:54,Delta,77154817,65371168,562153,12671030,349750,12311061
|
||||||
|
2025-03-26,10:34:59,Delta,88871882,75441201,655310,14875963,392530,13773130
|
||||||
|
2025-03-26,10:35:04,Delta,83763472,71730813,609260,13643680,366992,12672405
|
||||||
|
.ft
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
This example shows the counter values of the problem state counter set with
|
||||||
|
CPU 3 selected.
|
||||||
|
.nf
|
||||||
|
.ft CR
|
||||||
|
.sp 1
|
||||||
|
# lshwc -l2 -a 3:P -H --format json
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"api_level": 1,
|
||||||
|
"version": "2.37.0-build-20250616",
|
||||||
|
"host": "b46lp08.lnxne.boe",
|
||||||
|
"time_epoch": 1750094646,
|
||||||
|
"time": "2025-06-16 19:24:06+0200"
|
||||||
|
},
|
||||||
|
"lshwc": {
|
||||||
|
"cpumcf info": {
|
||||||
|
"counter first": 3,
|
||||||
|
"counter second": 8,
|
||||||
|
"authorization": 47
|
||||||
|
},
|
||||||
|
"measurements": [
|
||||||
|
{
|
||||||
|
"date_time": "2025-06-16 19:24:06+0200",
|
||||||
|
"time_epoch": 1750094646,
|
||||||
|
"cpu": 3,
|
||||||
|
"counters": [
|
||||||
|
{
|
||||||
|
"name": "problem_state_cpu_cycles",
|
||||||
|
"id": 32,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "problem_state_instructions",
|
||||||
|
"id": 33,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_time": "2025-06-16 19:24:06+0200",
|
||||||
|
"time_epoch": 1750094646,
|
||||||
|
"cpu": "total",
|
||||||
|
"counters": [
|
||||||
|
{
|
||||||
|
"name": "problem_state_cpu_cycles",
|
||||||
|
"id": 32,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "problem_state_instructions",
|
||||||
|
"id": 33,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_time": "2025-06-16 19:25:06+0200",
|
||||||
|
"time_epoch": 1750094706,
|
||||||
|
"cpu": 3,
|
||||||
|
"counters": [
|
||||||
|
{
|
||||||
|
"name": "problem_state_cpu_cycles",
|
||||||
|
"id": 32,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "problem_state_instructions",
|
||||||
|
"id": 33,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_time": "2025-06-16 19:25:06+0200",
|
||||||
|
"time_epoch": 1750094706,
|
||||||
|
"cpu": "total",
|
||||||
|
"counters": [
|
||||||
|
{
|
||||||
|
"name": "problem_state_cpu_cycles",
|
||||||
|
"id": 32,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "problem_state_instructions",
|
||||||
|
"id": 33,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ft
|
||||||
|
.fi
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
.BR lscpumf (8)
|
.BR lscpumf (8)
|
||||||
.BR chcpumf (8)
|
.BR chcpumf (8)
|
||||||
|
|||||||
@@ -10,13 +10,30 @@
|
|||||||
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
|
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
|
||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\*c \- list Processor Activity Instrumentation (PAI) counters
|
\*c \- list Processor Activity Instrumentation (PAI) counters and values
|
||||||
.
|
.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\*c
|
\*c
|
||||||
|
.RB [ \-\-format
|
||||||
|
.IR FORMAT ]
|
||||||
|
.RB [ \-n ]
|
||||||
|
.RB [ \-t
|
||||||
|
.IR TYPE ]
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.RB [ \-\-format
|
||||||
|
.IR FORMAT ]
|
||||||
.RB [ \-n ]
|
.RB [ \-n ]
|
||||||
.RB [ \-t
|
.RB [ \-t
|
||||||
.IR "\ TYPE" ]
|
.IR "\ TYPE" ]
|
||||||
|
.RB [ \-a ][ \-d ][ \-i
|
||||||
|
.IR SECONDS ]
|
||||||
|
.RB [ \-l
|
||||||
|
.IR COUNT ]
|
||||||
|
.RB [ \-s ][ \-x ][ \-x ]
|
||||||
|
.RB [ \-c
|
||||||
|
.IR LIST ]
|
||||||
|
.RI [ CPULIST ]
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
.BR \-h | \-\-help
|
.BR \-h | \-\-help
|
||||||
@@ -26,10 +43,34 @@
|
|||||||
.
|
.
|
||||||
.
|
.
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
On the first line
|
||||||
\*c displays the Processor Activity Instrumentation (PAI) counters
|
\*c displays the Processor Activity Instrumentation (PAI) counters
|
||||||
for Linux on IBM Z.
|
for Linux on IBM Z.
|
||||||
The output is a human-readable list of available PAI counter
|
The output lists the available PAI counter names
|
||||||
names and numbers.
|
and numbers in various formats.
|
||||||
|
If
|
||||||
|
.I CPULIST
|
||||||
|
is specified or any of the options
|
||||||
|
.BR \-a ,
|
||||||
|
.BR \-d ,
|
||||||
|
.BR \-s ,
|
||||||
|
.BR \-x ,
|
||||||
|
.BR \-X ,
|
||||||
|
.B \-l
|
||||||
|
or
|
||||||
|
.BR \-i,
|
||||||
|
are present on the command line,
|
||||||
|
\*c command extracts counter values from the
|
||||||
|
specified PAI counter sets.
|
||||||
|
Counter sets can be specified and extracted for individual CPUs.
|
||||||
|
By default the output is a comma-separated list of values.
|
||||||
|
Each line starts with a timestamp
|
||||||
|
followed by the extracted counter values.
|
||||||
|
A header line shows each counter name and the
|
||||||
|
counter number in parenthesis.
|
||||||
|
For other formats, use the
|
||||||
|
.B \-\-format
|
||||||
|
option.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.BR \-h ", " \-\-help
|
.BR \-h ", " \-\-help
|
||||||
@@ -40,7 +81,44 @@ Displays help information, then exits.
|
|||||||
Displays version information, then exits.
|
Displays version information, then exits.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.BR \-t ", " \-\-type "\ TYPE"
|
.BR \-a ", " \-\-allcpu
|
||||||
|
Displays counter values from each CPU.
|
||||||
|
The default is a total summary line of all counters from all CPUs.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-d ", " \-\-delta
|
||||||
|
Displays counter values in form of deltas.
|
||||||
|
Each counter value shows the increment to the previous output line.
|
||||||
|
Without this flag the total value of each counter is displayed.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-c ", " \-\-counters \fI\ LIST\fP
|
||||||
|
Specify a comma separated list of counter names to
|
||||||
|
be printed.
|
||||||
|
If a counter name does not match any valid counter
|
||||||
|
name, it is omitted.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-s ", " \-\-short
|
||||||
|
Displays counter names as short names in the heading.
|
||||||
|
A short name consists of
|
||||||
|
a letter for the counter set followed by a number.
|
||||||
|
For example,
|
||||||
|
this flag displays counter number 0 in the PAI crypto counter
|
||||||
|
set as
|
||||||
|
.B C0
|
||||||
|
instead of
|
||||||
|
.BR CRYPTO_ALL .
|
||||||
|
Valid counter set letters are:
|
||||||
|
.RS
|
||||||
|
.IP C
|
||||||
|
PAI crypto counter set
|
||||||
|
.IP N
|
||||||
|
PAI NNPA counter set
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-t ", " \-\-type "\ \fITYPE\fP"
|
||||||
Specifies the PAI counter set to list.
|
Specifies the PAI counter set to list.
|
||||||
Valid counter set values are
|
Valid counter set values are
|
||||||
.I crypto
|
.I crypto
|
||||||
@@ -55,6 +133,32 @@ Crypto refers to the Cryptografic Processing Assist facility counter set.
|
|||||||
Shows the PAI counter sets sorted by counter number.
|
Shows the PAI counter sets sorted by counter number.
|
||||||
Default sort order is PAI counter name.
|
Default sort order is PAI counter name.
|
||||||
.
|
.
|
||||||
|
.TP
|
||||||
|
.BI \-\-format "\ FORMAT"
|
||||||
|
Retrieve output in one of the following formats:
|
||||||
|
JSON, csv, json-seq or pairs.
|
||||||
|
If no format is specified,
|
||||||
|
the output defaults to a human-readable format.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
|
||||||
|
Specifies a time interval, in seconds,
|
||||||
|
that the command waits between read operations.
|
||||||
|
The default is 60 seconds.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-l ", " \-\-loop \fI\ COUNT\fP
|
||||||
|
Performs the specified number of read operations.
|
||||||
|
The default is one.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-x ", " \-\-hex
|
||||||
|
Displays counter values as hexadecimal values.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-X ", " \-\-hex0x
|
||||||
|
Displays counter values as hexadecimal values with a leading 0x prefix.
|
||||||
|
.
|
||||||
.SH "EXAMPLE"
|
.SH "EXAMPLE"
|
||||||
The \*c invocation lists all PAI Neural Network Processing Assist Facility
|
The \*c invocation lists all PAI Neural Network Processing Assist Facility
|
||||||
(NNPA) counters in numeric order:
|
(NNPA) counters in numeric order:
|
||||||
@@ -75,6 +179,58 @@ suitable for
|
|||||||
.IR perf "(8)"
|
.IR perf "(8)"
|
||||||
event specification by name.
|
event specification by name.
|
||||||
The third gives a short explanation, if available.
|
The third gives a short explanation, if available.
|
||||||
|
.sp 1
|
||||||
|
The \*c invocation displays all PAI Neural Network Processing Assist Facility
|
||||||
|
(NNPA) counters values.
|
||||||
|
Ten iterations with a two second delay in between
|
||||||
|
is printed on the screen.
|
||||||
|
.nf
|
||||||
|
# lspai -i 2 -l 10 -t nnpa -n
|
||||||
|
Date,Time,CPU,NNPA_ALL(0),NNPA_ADD(1),NNPA_SUB(2),NNPA_MUL(3),NNPA_DIV(4),
|
||||||
|
NNPA_MIN(5),NNPA_MAX(6),NNPA_LOG(7),NNPA_EXP(8),NNPA_IBM_RESERVED_9(9),
|
||||||
|
NNPA_RELU(10),NNPA_TANH(11),NNPA_SIGMOID(12),NNPA_SOFTMAX(13),NNPA_BATCHNORM(14),
|
||||||
|
NNPA_MAXPOOL2D(15),NNPA_AVGPOOL2D(16),NNPA_LSTMACT(17),NNPA_GRUACT(18),
|
||||||
|
NNPA_CONVOLUTION(19),NNPA_MATMUL_OP(20),NNPA_MATMUL_OP_BCAST23(21),
|
||||||
|
NNPA_SMALLBATCH(22),NNPA_LARGEDIM(23),NNPA_SMALLTENSOR(24),NNPA_1MFRAME(25),
|
||||||
|
NNPA_2GFRAME(26),NNPA_ACCESSEXCEPT(27)
|
||||||
|
2025-05-13,08:18:27,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:29,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:31,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:33,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:35,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:37,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:39,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:41,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:43,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
2025-05-13,08:18:45,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
#
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
This \*c example displays the value of PAI crypto counter number 7.
|
||||||
|
It is field number 116 in the output.
|
||||||
|
This counter is incremented after \*c is started and
|
||||||
|
the table shows the delta values for PAI crypto counter 7.
|
||||||
|
.nf
|
||||||
|
# lspai -d -s -i 5 -l3 -t crypto | \
|
||||||
|
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
|
||||||
|
Date,Time,CPU,C0,C7
|
||||||
|
2025-06-24,13:02:24,Total,0,0
|
||||||
|
2025-06-24,13:02:29,Delta,77122286,77122284
|
||||||
|
2025-06-24,13:02:34,Delta,92574403,92574403
|
||||||
|
#
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
The following table shows the total value for PAI crypto counter 7
|
||||||
|
after \*c is started.
|
||||||
|
.nf
|
||||||
|
# lspai -s -i 5 -l3 -t crypto | \
|
||||||
|
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
|
||||||
|
Date,Time,CPU,C0,C7
|
||||||
|
2025-06-24,13:03:01,Total,0,0
|
||||||
|
2025-06-24,13:03:06,Total,72550754,72566121
|
||||||
|
2025-06-24,13:03:11,Total,164633982,164650377
|
||||||
|
#
|
||||||
|
.fi
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
.BR pai (8)
|
.BR pai (8)
|
||||||
.BR lscpumf (8)
|
.BR lscpumf (8)
|
||||||
|
|||||||
@@ -10,29 +10,29 @@
|
|||||||
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
|
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
|
||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\*c \- record and report Processor Activity Instrumentation (PAI) counters
|
pai \- record and report Processor Activity Instrumentation (PAI) counters
|
||||||
.
|
.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\*c
|
\*c
|
||||||
.RB [ \-V ][ \-m | \-\-mapsize
|
.RB [ \-V ][ \-m | \-\-mapsize
|
||||||
.IR size ]
|
.IR SIZE ]
|
||||||
.RB [ \-i | \-\-interval
|
.RB [ \-i | \-\-interval
|
||||||
.IR ms ]
|
.IR MS ]
|
||||||
.RB [ \-R | \-\-realtime
|
.RB [ \-R | \-\-realtime
|
||||||
.IR prio ]
|
.IR PRIO ]
|
||||||
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
.BR \-c | \-\-crypto [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
.RB [ \-V ][ \-m | \-\-mapsize
|
.RB [ \-V ][ \-m | \-\-mapsize
|
||||||
.IR size ]
|
.IR SIZE ]
|
||||||
.RB [ \-i | \-\-interval
|
.RB [ \-i | \-\-interval
|
||||||
.IR ms ]
|
.IR MS ]
|
||||||
.RB [ \-R | \-\-realtime
|
.RB [ \-R | \-\-realtime
|
||||||
.IR prio ]
|
.IR PRIO ]
|
||||||
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
.BR \-n | \-\-nnpa [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ]
|
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIFILES\fP ]
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
.BR \-h | \-\-help
|
.BR \-h | \-\-help
|
||||||
@@ -73,7 +73,7 @@ Reporting evaluates files that are created by recording.
|
|||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
.BR \-c ", " \-\-crypto "\fR[\fICPULIST\fR][:\fIDATA\fR]"
|
||||||
Records data for all (default) or a specified list of CPUs.
|
Records data for all (default) or a specified list of CPUs.
|
||||||
The CPU list is a comma-separated list of CPU numbers and ranges.
|
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||||
In a range, a hyphen separates the first CPU number
|
In a range, a hyphen separates the first CPU number
|
||||||
@@ -106,7 +106,7 @@ execution.
|
|||||||
.RE
|
.RE
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
.BR \-n ", " \-\-nnpa "\fR[\fICPULIST\fR][:\fIDATA\fR]"
|
||||||
Records data for all (default) or a specified list of CPUs.
|
Records data for all (default) or a specified list of CPUs.
|
||||||
The CPU list is a comma-separated list of CPU numbers and ranges.
|
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||||
In a range, a hyphen separates the first CPU number
|
In a range, a hyphen separates the first CPU number
|
||||||
@@ -241,7 +241,7 @@ The program runs for 10 seconds
|
|||||||
(10 intervals of 1000 milliseconds).
|
(10 intervals of 1000 milliseconds).
|
||||||
.sp 1
|
.sp 1
|
||||||
.nf
|
.nf
|
||||||
.ft CW
|
.ft CR
|
||||||
# \*c -c0 10
|
# \*c -c0 10
|
||||||
.ft R
|
.ft R
|
||||||
.fi
|
.fi
|
||||||
@@ -256,7 +256,7 @@ and a comma-separated list of counter number, colon (:),
|
|||||||
and the counter value n hexadecimal notation.
|
and the counter value n hexadecimal notation.
|
||||||
.sp 1
|
.sp 1
|
||||||
.nf
|
.nf
|
||||||
.ft CW
|
.ft CR
|
||||||
# \*c -r
|
# \*c -r
|
||||||
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
|
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
|
||||||
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1
|
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1
|
||||||
|
|||||||
62
cpumf/pai.c
62
cpumf/pai.c
@@ -40,7 +40,9 @@
|
|||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
#include "lib/util_scandir.h"
|
#include "lib/util_scandir.h"
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
#include "pai.h"
|
#include "pai.h"
|
||||||
|
#include "pai_cli.h"
|
||||||
|
|
||||||
#define S390_EVT_PAI_CRYPTO 0x1000
|
#define S390_EVT_PAI_CRYPTO 0x1000
|
||||||
#define S390_EVT_PAI_NNPA 0x1800
|
#define S390_EVT_PAI_NNPA 0x1800
|
||||||
@@ -59,14 +61,6 @@ static struct util_list list_pai_event;
|
|||||||
static struct util_list list_pmu_event;
|
static struct util_list list_pmu_event;
|
||||||
static bool summary;
|
static bool summary;
|
||||||
|
|
||||||
/* System call to perf_event_open(2) */
|
|
||||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
|
||||||
int cpu, int group_fd, unsigned long flags)
|
|
||||||
{
|
|
||||||
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
|
|
||||||
group_fd, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ev_dealloc(void)
|
static void ev_dealloc(void)
|
||||||
{
|
{
|
||||||
struct pai_event *next, *p;
|
struct pai_event *next, *p;
|
||||||
@@ -743,8 +737,8 @@ static int add_events(int type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
free(pmuname);
|
|
||||||
warnx("failed building event list for %s", pmuname);
|
warnx("failed building event list for %s", pmuname);
|
||||||
|
free(pmuname);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -933,54 +927,6 @@ static void parse_cpulist(int enr, const char *parm)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "crypto", optional_argument, NULL, 'c' },
|
|
||||||
.argument = "CPULIST[:DATA]",
|
|
||||||
.desc = "Collect PAI crypto counters"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "nnpa", optional_argument, NULL, 'n' },
|
|
||||||
.argument = "CPULIST[:DATA]",
|
|
||||||
.desc = "Collect PAI nnpa counters"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "mapsize", required_argument, NULL, 'm' },
|
|
||||||
.argument = "SIZE",
|
|
||||||
.desc = "Specifies number of 4KB pages for event ring buffer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "report", no_argument, NULL, 'r' },
|
|
||||||
.desc = "Report file contents"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "realtime", required_argument, NULL, 'R' },
|
|
||||||
.argument = "PRIO",
|
|
||||||
.desc = "Collect data with this RT SCHED_FIFO priority"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "interval", required_argument, NULL, 'i' },
|
|
||||||
.argument = "NUMBER",
|
|
||||||
.desc = "Specifies interval between read operations in milliseconds"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "verbose", no_argument, NULL, 'V' },
|
|
||||||
.desc = "Verbose output"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "humantime", no_argument, NULL, 'H' },
|
|
||||||
.desc = "Human readable timestamp in seconds.nanoseconds"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "summary", no_argument, NULL, 'S' },
|
|
||||||
.desc = "Print summary of all non-zero counter values"
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "Record and report Processor Activity Instrumentation Facility Counters.",
|
.desc = "Record and report Processor Activity Instrumentation Facility Counters.",
|
||||||
.copyright_vec = {
|
.copyright_vec = {
|
||||||
@@ -1046,7 +992,7 @@ int main(int argc, char **argv)
|
|||||||
util_list_init(&list_pai_event, struct pai_event, node);
|
util_list_init(&list_pai_event, struct pai_event, node);
|
||||||
util_list_init(&list_pmu_event, struct pmu_events, node);
|
util_list_init(&list_pmu_event, struct pmu_events, node);
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(pai_opt_vec, NULL);
|
||||||
|
|
||||||
/* Read currently online CPUs and create a bit mask.
|
/* Read currently online CPUs and create a bit mask.
|
||||||
* This bitmap of online CPUs is used to check command line parameter
|
* This bitmap of online CPUs is used to check command line parameter
|
||||||
|
|||||||
65
cpumf/pai_cli.h
Normal file
65
cpumf/pai_cli.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for pai
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 PAI_CLI_H
|
||||||
|
#define PAI_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt pai_opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "crypto", optional_argument, NULL, 'c' },
|
||||||
|
.argument = "CPULIST[:DATA]",
|
||||||
|
.desc = "Collect PAI crypto counters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "nnpa", optional_argument, NULL, 'n' },
|
||||||
|
.argument = "CPULIST[:DATA]",
|
||||||
|
.desc = "Collect PAI nnpa counters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "mapsize", required_argument, NULL, 'm' },
|
||||||
|
.argument = "SIZE",
|
||||||
|
.desc = "Specifies number of 4KB pages for event ring buffer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "report", no_argument, NULL, 'r' },
|
||||||
|
.desc = "Report file contents"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "realtime", required_argument, NULL, 'R' },
|
||||||
|
.argument = "PRIO",
|
||||||
|
.desc = "Collect data with this RT SCHED_FIFO priority"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "interval", required_argument, NULL, 'i' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "Specifies interval between read operations in milliseconds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Verbose output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "humantime", no_argument, NULL, 'H' },
|
||||||
|
.desc = "Human readable timestamp in seconds.nanoseconds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "summary", no_argument, NULL, 'S' },
|
||||||
|
.desc = "Print summary of all non-zero counter values"
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -314,8 +314,7 @@ void check_config()
|
|||||||
cpuplugd_debug("Daemon started with vertical polarization.\n");
|
cpuplugd_debug("Daemon started with vertical polarization.\n");
|
||||||
cpuplugd_debug("Cpu adjustments won't be made until system ");
|
cpuplugd_debug("Cpu adjustments won't be made until system ");
|
||||||
cpuplugd_debug("is in horizontal polarization\n");
|
cpuplugd_debug("is in horizontal polarization\n");
|
||||||
} else if (saved_polarization == PLR_HORIZONTAL &&
|
} else if (cpu == 1) {
|
||||||
cpu == 1) {
|
|
||||||
apply_cpu_config();
|
apply_cpu_config();
|
||||||
}
|
}
|
||||||
if (memory == 1) {
|
if (memory == 1) {
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ int get_polarization(void)
|
|||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
path = util_path_sysfs("devices/system/cpu/dispatching");
|
path = util_path_sysfs("devices/system/cpu/dispatching");
|
||||||
|
if (!util_path_exists(path))
|
||||||
|
return PLR_NONE;
|
||||||
if (util_file_read_i(&polarization, NUM_BASE, path) < 0) {
|
if (util_file_read_i(&polarization, NUM_BASE, path) < 0) {
|
||||||
polarization = -1;
|
polarization = -1;
|
||||||
cpuplugd_debug("failed to read system polarization\n");
|
cpuplugd_debug("failed to read system polarization\n");
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#define CPUSTATS 10
|
#define CPUSTATS 10
|
||||||
#define PLR_HORIZONTAL 0
|
#define PLR_HORIZONTAL 0
|
||||||
#define PLR_VERTICAL 1
|
#define PLR_VERTICAL 1
|
||||||
|
#define PLR_NONE 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Precedence of C operators
|
* Precedence of C operators
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _dasdfmt
|
||||||
|
bash-completions = dasdfmt.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
all: dasdfmt
|
all: dasdfmt
|
||||||
|
|
||||||
libs = $(rootdir)/libdasd/libdasd.a \
|
libs = $(rootdir)/libdasd/libdasd.a \
|
||||||
|
|||||||
20
dasdfmt/autocompletion_generator_host.c
Normal file
20
dasdfmt/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
/*
|
||||||
|
* Autocompletion generation - for dasdfmt tool
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "dasdfmt_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "dasdfmt");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#include "dasdfmt.h"
|
#include "dasdfmt.h"
|
||||||
|
#include "dasdfmt_cli.h"
|
||||||
|
|
||||||
#define BUSIDSIZE 8
|
#define BUSIDSIZE 8
|
||||||
#define SEC_PER_DAY (60 * 60 * 24)
|
#define SEC_PER_DAY (60 * 60 * 24)
|
||||||
@@ -81,112 +82,6 @@ static struct dasdfmt_globals {
|
|||||||
.dasd_info = { 0 },
|
.dasd_info = { 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 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"),
|
|
||||||
{
|
|
||||||
.option = { "mode", required_argument, NULL, 'M' },
|
|
||||||
.argument = "MODE",
|
|
||||||
.desc = "Specify scope of operation using MODE:\n"
|
|
||||||
" full: Full device (default)\n"
|
|
||||||
" quick: Only the first two tracks\n"
|
|
||||||
" expand: Unformatted tracks at device end",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "check", no_argument, NULL, OPT_CHECK },
|
|
||||||
.desc = "Perform complete format check on device",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("FORMAT OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "blocksize", required_argument, NULL, 'b' },
|
|
||||||
.argument = "SIZE",
|
|
||||||
.desc = "Format blocks to SIZE bytes (default 4096)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "disk_layout", required_argument, NULL, 'd' },
|
|
||||||
.argument = "LAYOUT",
|
|
||||||
.desc = "Specify the disk layout:\n"
|
|
||||||
" cdl: Compatible Disk Layout (default)\n"
|
|
||||||
" ldl: Linux Disk Layout",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "keep_volser", no_argument, NULL, 'k' },
|
|
||||||
.desc = "Do not change the current volume serial",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "label", required_argument, NULL, 'l' },
|
|
||||||
.argument = "VOLSER",
|
|
||||||
.desc = "Specify volume serial number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "no_label", no_argument, NULL, 'L' },
|
|
||||||
.desc = "Don't write a disk label",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "requestsize", required_argument, NULL, 'r' },
|
|
||||||
.argument = "NUM",
|
|
||||||
.desc = "Process NUM cylinders in one formatting step",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "norecordzero", no_argument, NULL, OPT_NOZERO },
|
|
||||||
.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",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("DISPLAY PROGRESS"),
|
|
||||||
{
|
|
||||||
.option = { "hashmarks", required_argument, NULL, 'm' },
|
|
||||||
.argument = "NUM",
|
|
||||||
.desc = "Show a hashmark every NUM cylinders",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "progressbar", no_argument, NULL, 'p' },
|
|
||||||
.desc = "Show a progressbar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "percentage", no_argument, NULL, 'P' },
|
|
||||||
.desc = "Show progress in percent",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("MISC"),
|
|
||||||
{
|
|
||||||
.option = { "check_host_count", no_argument, NULL, 'C' },
|
|
||||||
.desc = "Check if device is in use by other hosts",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "force", no_argument, NULL, 'F' },
|
|
||||||
.desc = "Format without performing sanity checking",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "test", no_argument, NULL, 't' },
|
|
||||||
.desc = "Run in dry-run mode without modifying the DASD",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { NULL, no_argument, NULL, 'v' },
|
|
||||||
.desc = "Print verbose messages when executing",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
{
|
|
||||||
.option = { "version", no_argument, NULL, 'V' },
|
|
||||||
.desc = "Print version information, then exit",
|
|
||||||
},
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Report error, free memory, and exit */
|
/* Report error, free memory, and exit */
|
||||||
static void error(const char *format, ...)
|
static void error(const char *format, ...)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -69,13 +70,13 @@ static void error(const char *format, ...)
|
|||||||
"is in invalid format\n",prog_name);}
|
"is in invalid format\n",prog_name);}
|
||||||
|
|
||||||
typedef struct bootstrap1 {
|
typedef struct bootstrap1 {
|
||||||
u_int32_t key;
|
uint32_t key;
|
||||||
u_int32_t data[6];
|
uint32_t data[6];
|
||||||
} __attribute__ ((packed)) bootstrap1_t;
|
} __attribute__ ((packed)) bootstrap1_t;
|
||||||
|
|
||||||
typedef struct bootstrap2 {
|
typedef struct bootstrap2 {
|
||||||
u_int32_t key;
|
uint32_t key;
|
||||||
u_int32_t data[36];
|
uint32_t data[36];
|
||||||
} __attribute__ ((packed)) bootstrap2_t;
|
} __attribute__ ((packed)) bootstrap2_t;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
123
dasdfmt/dasdfmt_cli.h
Normal file
123
dasdfmt/dasdfmt_cli.h
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* Command line utilities - for dasdfmt
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2025
|
||||||
|
*
|
||||||
|
* 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 DASDFMT_CLI_H
|
||||||
|
#define DASDFMT_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
/* 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"),
|
||||||
|
{
|
||||||
|
.option = { "mode", required_argument, NULL, 'M' },
|
||||||
|
.argument = "MODE",
|
||||||
|
.desc = "Specify scope of operation using MODE:\n"
|
||||||
|
" full: Full device (default)\n"
|
||||||
|
" quick: Only the first two tracks\n"
|
||||||
|
" expand: Unformatted tracks at device end",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "check", no_argument, NULL, OPT_CHECK },
|
||||||
|
.desc = "Perform complete format check on device",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("FORMAT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "blocksize", required_argument, NULL, 'b' },
|
||||||
|
.argument = "SIZE",
|
||||||
|
.desc = "Format blocks to SIZE bytes (default 4096)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "disk_layout", required_argument, NULL, 'd' },
|
||||||
|
.argument = "LAYOUT",
|
||||||
|
.desc = "Specify the disk layout:\n"
|
||||||
|
" cdl: Compatible Disk Layout (default)\n"
|
||||||
|
" ldl: Linux Disk Layout",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "keep_volser", no_argument, NULL, 'k' },
|
||||||
|
.desc = "Do not change the current volume serial",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "label", required_argument, NULL, 'l' },
|
||||||
|
.argument = "VOLSER",
|
||||||
|
.desc = "Specify volume serial number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "no_label", no_argument, NULL, 'L' },
|
||||||
|
.desc = "Don't write a disk label",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "requestsize", required_argument, NULL, 'r' },
|
||||||
|
.argument = "NUM",
|
||||||
|
.desc = "Process NUM cylinders in one formatting step",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "norecordzero", no_argument, NULL, OPT_NOZERO },
|
||||||
|
.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",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("DISPLAY PROGRESS"),
|
||||||
|
{
|
||||||
|
.option = { "hashmarks", required_argument, NULL, 'm' },
|
||||||
|
.argument = "NUM",
|
||||||
|
.desc = "Show a hashmark every NUM cylinders",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "progressbar", no_argument, NULL, 'p' },
|
||||||
|
.desc = "Show a progressbar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "percentage", no_argument, NULL, 'P' },
|
||||||
|
.desc = "Show progress in percent",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("MISC"),
|
||||||
|
{
|
||||||
|
.option = { "check_host_count", no_argument, NULL, 'C' },
|
||||||
|
.desc = "Check if device is in use by other hosts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "force", no_argument, NULL, 'F' },
|
||||||
|
.desc = "Format without performing sanity checking",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "test", no_argument, NULL, 't' },
|
||||||
|
.desc = "Run in dry-run mode without modifying the DASD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { NULL, no_argument, NULL, 'v' },
|
||||||
|
.desc = "Print verbose messages when executing",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
{
|
||||||
|
.option = { "version", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Print version information, then exit",
|
||||||
|
},
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -23,16 +23,21 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "lib/dasd_base.h"
|
#include "lib/dasd_base.h"
|
||||||
#include "lib/util_base.h"
|
|
||||||
#include "lib/util_file.h"
|
#include "lib/util_file.h"
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.h"
|
||||||
#include "lib/util_opt.h"
|
#include "lib/util_opt.h"
|
||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#define RD_BUFFER_SIZE 80
|
#define RD_BUFFER_SIZE 80
|
||||||
#define TEMP_DEV_MAX_RETRIES 1000
|
#define TEMP_DEV_MAX_RETRIES 1000
|
||||||
|
|
||||||
|
#ifndef FTW_CONTINUE
|
||||||
|
#define FTW_CONTINUE 0
|
||||||
|
#define FTW_STOP 1
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "Display DASD volume serial number and ID information",
|
.desc = "Display DASD volume serial number and ID information",
|
||||||
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
|
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
|
||||||
@@ -212,7 +217,7 @@ static int dinfo_read_dasd_vlabel(char *device, struct volume_label *vlabel,
|
|||||||
if (lseek(f, vlabel_start, SEEK_SET) < 0)
|
if (lseek(f, vlabel_start, SEEK_SET) < 0)
|
||||||
goto error_close;
|
goto error_close;
|
||||||
|
|
||||||
bzero(vlabel, vlsize);
|
memset(vlabel, 0, vlsize);
|
||||||
|
|
||||||
if (read(f, vlabel, vlsize) != vlsize) {
|
if (read(f, vlabel, vlsize) != vlsize) {
|
||||||
warnx("Could not read volume label");
|
warnx("Could not read volume label");
|
||||||
@@ -220,15 +225,15 @@ static int dinfo_read_dasd_vlabel(char *device, struct volume_label *vlabel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dasd_info.FBA_layout) {
|
if (dasd_info.FBA_layout) {
|
||||||
bzero(&tmp, vlsize);
|
memset(&tmp, 0, vlsize);
|
||||||
memcpy(&tmp, vlabel, vlsize);
|
memcpy(&tmp, vlabel, vlsize);
|
||||||
memcpy(vlabel->vollbl, &tmp, vlsize - 4);
|
memcpy(vlabel->vollbl, &tmp, vlsize - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(f);
|
close(f);
|
||||||
|
|
||||||
bzero(readbuf, 7);
|
memset(readbuf, 0, 7);
|
||||||
bzero(vollbl, 5);
|
memset(vollbl, 0, 5);
|
||||||
strncpy(vollbl, vlabel->vollbl, 4);
|
strncpy(vollbl, vlabel->vollbl, 4);
|
||||||
dinfo_ebcdic_dec(vollbl, vollbl, 4);
|
dinfo_ebcdic_dec(vollbl, vollbl, 4);
|
||||||
|
|
||||||
@@ -283,7 +288,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
|||||||
char filename[] = "dasdinfo0000";
|
char filename[] = "dasdinfo0000";
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
unsigned int path;
|
unsigned int path;
|
||||||
int retry;
|
unsigned int retry;
|
||||||
int rc;
|
int rc;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
@@ -294,7 +299,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
|||||||
if (pathname[path] == NULL)
|
if (pathname[path] == NULL)
|
||||||
continue;
|
continue;
|
||||||
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||||
sprintf(filename, "dasdinfo%04d", retry);
|
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
|
||||||
result = dinfo_make_path(pathname[path], filename);
|
result = dinfo_make_path(pathname[path], filename);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -334,7 +339,7 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
|
|||||||
char *p = NULL;
|
char *p = NULL;
|
||||||
int ma, mi;
|
int ma, mi;
|
||||||
|
|
||||||
bzero(tmp, RD_BUFFER_SIZE);
|
memset(tmp, 0, RD_BUFFER_SIZE);
|
||||||
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
|
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
|
||||||
p = strchr(tmp, ':');
|
p = strchr(tmp, ':');
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
@@ -354,18 +359,26 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
|
|||||||
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
|
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
|
||||||
{
|
{
|
||||||
char *readbuf = NULL;
|
char *readbuf = NULL;
|
||||||
|
char *path;
|
||||||
|
|
||||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||||
if (!readbuf) {
|
if (!readbuf) {
|
||||||
warnx("Error: Not enough memory to allocate readbuffer");
|
warnx("Error: Not enough memory to allocate readbuffer");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
|
path = util_path_sysfs("block/%s/dev", blockdev);
|
||||||
"/sys/block/%s/dev", blockdev) < 0)
|
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path) < 0) {
|
||||||
|
free(path);
|
||||||
|
free(readbuf);
|
||||||
return -1;
|
return -1;
|
||||||
if (dinfo_extract_dev(dev, readbuf) != 0)
|
}
|
||||||
|
free(path);
|
||||||
|
if (dinfo_extract_dev(dev, readbuf) != 0) {
|
||||||
|
free(readbuf);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(readbuf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,8 +386,9 @@ static int
|
|||||||
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
||||||
int tflag, struct FTW *ftwbuf)
|
int tflag, struct FTW *ftwbuf)
|
||||||
{
|
{
|
||||||
|
enum { LINK_DIR_SIZE = 128 };
|
||||||
|
char linkdir[LINK_DIR_SIZE];
|
||||||
char *tempdir;
|
char *tempdir;
|
||||||
char linkdir[128];
|
|
||||||
ssize_t i;
|
ssize_t i;
|
||||||
|
|
||||||
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
|
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
|
||||||
@@ -387,12 +401,12 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
|||||||
*/
|
*/
|
||||||
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
|
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
i = readlink(tempdir, linkdir, 128);
|
i = readlink(tempdir, linkdir, LINK_DIR_SIZE);
|
||||||
free(tempdir);
|
free(tempdir);
|
||||||
if ((i < 0) || (i >= 128))
|
if (i < 0 || i >= LINK_DIR_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
/* append '\0' because readlink returns non zero terminated string */
|
/* append '\0' because readlink returns non zero terminated string */
|
||||||
tempdir[i + 1] = '\0';
|
linkdir[i] = '\0';
|
||||||
if (strstr(linkdir, "dasd") == NULL)
|
if (strstr(linkdir, "dasd") == NULL)
|
||||||
return FTW_CONTINUE;
|
return FTW_CONTINUE;
|
||||||
free(busiddir);
|
free(busiddir);
|
||||||
@@ -439,7 +453,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
|
|||||||
|
|
||||||
char *tempdir = NULL;
|
char *tempdir = NULL;
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *sysfsdir = "/sys/devices/";
|
char *sysfsdir = util_path_sysfs("devices/");
|
||||||
|
|
||||||
/* dinfo_is_devnode needs to know the busid */
|
/* dinfo_is_devnode needs to know the busid */
|
||||||
searchbusid = busid;
|
searchbusid = busid;
|
||||||
@@ -475,6 +489,7 @@ out:
|
|||||||
out2:
|
out2:
|
||||||
free(busiddir);
|
free(busiddir);
|
||||||
free(result);
|
free(result);
|
||||||
|
free(sysfsdir);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +501,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
DIR *directory = NULL;
|
DIR *directory = NULL;
|
||||||
struct dirent *dir_entry = NULL;
|
struct dirent *dir_entry = NULL;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
char *path;
|
||||||
|
|
||||||
if (stat(devnode, &stat_buffer) != 0) {
|
if (stat(devnode, &stat_buffer) != 0) {
|
||||||
warnx("Error: could not stat %s", devnode);
|
warnx("Error: could not stat %s", devnode);
|
||||||
@@ -495,32 +511,34 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
|
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
|
||||||
minor(stat_buffer.st_rdev));
|
minor(stat_buffer.st_rdev));
|
||||||
|
|
||||||
directory = opendir("/sys/block/");
|
path = util_path_sysfs("block/");
|
||||||
|
directory = opendir(path);
|
||||||
if (directory == NULL) {
|
if (directory == NULL) {
|
||||||
warnx("Error: could not open directory /sys/block");
|
warnx("Error: could not open directory %s", path);
|
||||||
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||||
if (!readbuf) {
|
if (!readbuf) {
|
||||||
warnx("Error: Not enough memory to allocate readbuffer");
|
warnx("Error: Not enough memory to allocate readbuffer");
|
||||||
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((dir_entry = readdir(directory)) != NULL) {
|
while ((dir_entry = readdir(directory)) != NULL) {
|
||||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
|
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "%s%s/dev", path,
|
||||||
"/sys/block/%s/dev",
|
|
||||||
dir_entry->d_name) < 0)
|
dir_entry->d_name) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strncmp(stat_dev, readbuf,
|
if (strncmp(stat_dev, readbuf,
|
||||||
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
|
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
|
||||||
rc = snprintf(*uidfile, RD_BUFFER_SIZE,
|
rc = snprintf(*uidfile, RD_BUFFER_SIZE, "%s%s/device/uid", path,
|
||||||
"/sys/block/%s/device/uid",
|
|
||||||
dir_entry->d_name);
|
dir_entry->d_name);
|
||||||
if (rc >= RD_BUFFER_SIZE) {
|
if (rc >= RD_BUFFER_SIZE) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Error: Device name was truncated\n");
|
"Error: Device name was truncated\n");
|
||||||
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,6 +547,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
closedir(directory);
|
closedir(directory);
|
||||||
|
free(path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,16 +645,18 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||||
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
|
if (!readbuf)
|
||||||
if (!(readbuf && uidfile))
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
/* try to read the uid attribute */
|
/* try to read the uid attribute */
|
||||||
if (busid) {
|
if (busid) {
|
||||||
sprintf(uidfile, "/sys/bus/ccw/devices/%s/uid", busid);
|
uidfile = util_path_sysfs("bus/ccw/devices/%s/uid", busid);
|
||||||
} else if (blockdev) {
|
} else if (blockdev) {
|
||||||
sprintf(uidfile, "/sys/block/%s/device/uid", blockdev);
|
uidfile = util_path_sysfs("block/%s/device/uid", blockdev);
|
||||||
} else if (devnode) {
|
} else if (devnode) {
|
||||||
|
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
|
||||||
|
if (!uidfile)
|
||||||
|
exit(1);
|
||||||
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
|
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _LARGEFILE64_SOURCE /* needed for unistd.h */
|
|
||||||
#define _FILE_OFFSET_BITS 64 /* needed for unistd.h */
|
#define _FILE_OFFSET_BITS 64 /* needed for unistd.h */
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -17,6 +16,7 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -428,7 +428,7 @@ dasdview_read_vlabel(dasdview_info_t *info, volume_label_t *vlabel)
|
|||||||
|
|
||||||
pos = info->dasd_info.label_block * info->blksize;
|
pos = info->dasd_info.label_block * info->blksize;
|
||||||
|
|
||||||
bzero(vlabel, sizeof(volume_label_t));
|
memset(vlabel, 0, sizeof(volume_label_t));
|
||||||
if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
|
if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
|
||||||
!info->dasd_info.FBA_layout) {
|
!info->dasd_info.FBA_layout) {
|
||||||
/* OS/390 and zOS compatible disk layout */
|
/* OS/390 and zOS compatible disk layout */
|
||||||
@@ -466,7 +466,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
printf("\n--- volume label -----------------------------"
|
printf("\n--- volume label -----------------------------"
|
||||||
"---------------------------------\n");
|
"---------------------------------\n");
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.volkey, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.volkey, 4);
|
||||||
printf("volume label key : ascii '%4s'\n", s4);
|
printf("volume label key : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -474,7 +476,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s4, 5); bzero(s4, 5); strncpy((char *)s4, vlabel.vollbl, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.vollbl, 4);
|
||||||
printf("\n\nvolume label identifier : ascii '%4s'\n", s4);
|
printf("\n\nvolume label identifier : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -482,7 +486,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s6, 7); bzero(t6, 7); strncpy((char *)s6, vlabel.volid, 6);
|
memset(s6, 0, 7);
|
||||||
|
memset(t6, 0, 7);
|
||||||
|
strncpy((char *)s6, vlabel.volid, 6);
|
||||||
printf("\n\nvolume identifier : ascii '%6s'\n", s6);
|
printf("\n\nvolume identifier : ascii '%6s'\n", s6);
|
||||||
vtoc_ebcdic_dec((char *)s6, (char *)t6, 6);
|
vtoc_ebcdic_dec((char *)s6, (char *)t6, 6);
|
||||||
printf(" : ebcdic '%6s'\n", t6);
|
printf(" : ebcdic '%6s'\n", t6);
|
||||||
@@ -503,7 +509,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
vtoc_get_cyl_from_cchhb(&vlabel.vtoc),
|
vtoc_get_cyl_from_cchhb(&vlabel.vtoc),
|
||||||
vtoc_get_head_from_cchhb(&vlabel.vtoc), vlabel.vtoc.b);
|
vtoc_get_head_from_cchhb(&vlabel.vtoc), vlabel.vtoc.b);
|
||||||
|
|
||||||
bzero(s5, 6); bzero(t5, 6); strncpy((char *)s5, vlabel.res1, 5);
|
memset(s5, 0, 6);
|
||||||
|
memset(t5, 0, 6);
|
||||||
|
strncpy((char *)s5, vlabel.res1, 5);
|
||||||
printf("reserved : ascii '%5s'\n", s5);
|
printf("reserved : ascii '%5s'\n", s5);
|
||||||
vtoc_ebcdic_dec((char *)s5, (char *)t5, 5);
|
vtoc_ebcdic_dec((char *)s5, (char *)t5, 5);
|
||||||
printf(" : ebcdic '%5s'\n", t5);
|
printf(" : ebcdic '%5s'\n", t5);
|
||||||
@@ -511,7 +519,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
printf("%02x", s5[i]);
|
printf("%02x", s5[i]);
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.cisize, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.cisize, 4);
|
||||||
printf("\n\nCI size for FBA : ascii '%4s'\n", s4);
|
printf("\n\nCI size for FBA : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -519,7 +529,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.blkperci, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.blkperci, 4);
|
||||||
printf("\n\nblocks per CI (FBA) : ascii '%4s'\n", s4);
|
printf("\n\nblocks per CI (FBA) : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -527,7 +539,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.labperci, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.labperci, 4);
|
||||||
printf("\n\nlabels per CI (FBA) : ascii '%4s'\n", s4);
|
printf("\n\nlabels per CI (FBA) : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -535,7 +549,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.res2, 4);
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
strncpy((char *)s4, vlabel.res2, 4);
|
||||||
printf("\n\nreserved : ascii '%4s'\n", s4);
|
printf("\n\nreserved : ascii '%4s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
|
||||||
printf(" : ebcdic '%4s'\n", t4);
|
printf(" : ebcdic '%4s'\n", t4);
|
||||||
@@ -543,7 +559,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
printf("%02x", s4[i]);
|
printf("%02x", s4[i]);
|
||||||
|
|
||||||
bzero(s14, 15); bzero(t14, 15); strncpy(s14, vlabel.lvtoc, 14);
|
memset(s14, 0, 15);
|
||||||
|
memset(t14, 0, 15);
|
||||||
|
strncpy(s14, vlabel.lvtoc, 14);
|
||||||
printf("\n\nowner code for VTOC : ascii '%14s'\n", s14);
|
printf("\n\nowner code for VTOC : ascii '%14s'\n", s14);
|
||||||
vtoc_ebcdic_dec(s14, t14, 14);
|
vtoc_ebcdic_dec(s14, t14, 14);
|
||||||
printf(" ebcdic '%14s'\n", t14);
|
printf(" ebcdic '%14s'\n", t14);
|
||||||
@@ -556,9 +574,10 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
printf(" ");
|
printf(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
bzero(s29, 30); strncpy(s29, vlabel.res3, 28);
|
memset(s29, 0, 30);
|
||||||
|
strncpy(s29, vlabel.res3, 28);
|
||||||
printf("\n\nreserved : ascii '%28s'\n", s29);
|
printf("\n\nreserved : ascii '%28s'\n", s29);
|
||||||
bzero(t29, 30);
|
memset(t29, 0, 30);
|
||||||
vtoc_ebcdic_dec(s29, t29, 28);
|
vtoc_ebcdic_dec(s29, t29, 28);
|
||||||
printf(" ebcdic '%28s'\n", t29);
|
printf(" ebcdic '%28s'\n", t29);
|
||||||
printf(" hex ");
|
printf(" hex ");
|
||||||
@@ -573,7 +592,9 @@ dasdview_print_vlabel(dasdview_info_t *info)
|
|||||||
" ");
|
" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
bzero(s4, 5); bzero(t4, 5); s4[0] = vlabel.ldl_version;
|
memset(s4, 0, 5);
|
||||||
|
memset(t4, 0, 5);
|
||||||
|
s4[0] = vlabel.ldl_version;
|
||||||
printf("\n\nldl_version : ascii '%1s'\n", s4);
|
printf("\n\nldl_version : ascii '%1s'\n", s4);
|
||||||
vtoc_ebcdic_dec((char *)s4, (char *)t4, 1);
|
vtoc_ebcdic_dec((char *)s4, (char *)t4, 1);
|
||||||
printf(" : ebcdic '%1s'\n", t4);
|
printf(" : ebcdic '%1s'\n", t4);
|
||||||
@@ -609,8 +630,8 @@ dasdview_print_volser(dasdview_info_t *info)
|
|||||||
dasdview_read_vlabel(info, &vlabel);
|
dasdview_read_vlabel(info, &vlabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bzero(vollbl, 5);
|
memset(vollbl, 0, 5);
|
||||||
bzero(volser, 7);
|
memset(volser, 0, 7);
|
||||||
strncpy(vollbl, vlabel.vollbl, 4);
|
strncpy(vollbl, vlabel.vollbl, 4);
|
||||||
vtoc_ebcdic_dec(vollbl, vollbl, 4);
|
vtoc_ebcdic_dec(vollbl, vollbl, 4);
|
||||||
|
|
||||||
@@ -631,12 +652,12 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
|||||||
volume_label_t vlabel;
|
volume_label_t vlabel;
|
||||||
format1_label_t tmp;
|
format1_label_t tmp;
|
||||||
unsigned long maxblk, pos;
|
unsigned long maxblk, pos;
|
||||||
u_int64_t vtocblk;
|
uint64_t vtocblk;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pos = info->dasd_info.label_block * info->blksize;
|
pos = info->dasd_info.label_block * info->blksize;
|
||||||
|
|
||||||
bzero(&vlabel, sizeof(vlabel));
|
memset(&vlabel, 0, sizeof(vlabel));
|
||||||
if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
|
if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
|
||||||
!info->dasd_info.FBA_layout) {
|
!info->dasd_info.FBA_layout) {
|
||||||
/* OS/390 and zOS compatible disk layout */
|
/* OS/390 and zOS compatible disk layout */
|
||||||
@@ -648,7 +669,7 @@ dasdview_read_vtoc(dasdview_info_t *info)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
vtocblk = (u_int64_t)vtoc_get_cyl_from_cchhb(&vlabel.vtoc) *
|
vtocblk = (uint64_t)vtoc_get_cyl_from_cchhb(&vlabel.vtoc) *
|
||||||
info->geo.heads * info->geo.sectors +
|
info->geo.heads * info->geo.sectors +
|
||||||
vtoc_get_head_from_cchhb(&vlabel.vtoc) * info->geo.sectors +
|
vtoc_get_head_from_cchhb(&vlabel.vtoc) * info->geo.sectors +
|
||||||
vlabel.vtoc.b;
|
vlabel.vtoc.b;
|
||||||
@@ -747,13 +768,13 @@ static void dasdview_print_format1_8_short_info(format1_label_t *f1,
|
|||||||
char s6[7], s13[14], s44[45];
|
char s6[7], s13[14], s44[45];
|
||||||
unsigned long track_low, track_up;
|
unsigned long track_low, track_up;
|
||||||
|
|
||||||
bzero(s44, 45);
|
memset(s44, 0, 45);
|
||||||
strncpy(s44, f1->DS1DSNAM, 44);
|
strncpy(s44, f1->DS1DSNAM, 44);
|
||||||
vtoc_ebcdic_dec(s44, s44, 44);
|
vtoc_ebcdic_dec(s44, s44, 44);
|
||||||
bzero(s6, 7);
|
memset(s6, 0, 7);
|
||||||
strncpy(s6, (char *)f1->DS1DSSN, 6);
|
strncpy(s6, (char *)f1->DS1DSSN, 6);
|
||||||
vtoc_ebcdic_dec(s6, s6, 6);
|
vtoc_ebcdic_dec(s6, s6, 6);
|
||||||
bzero(s13, 14);
|
memset(s13, 0, 14);
|
||||||
strncpy(s13, (char *)f1->DS1SYSCD, 13);
|
strncpy(s13, (char *)f1->DS1SYSCD, 13);
|
||||||
vtoc_ebcdic_dec(s13, s13, 13);
|
vtoc_ebcdic_dec(s13, s13, 13);
|
||||||
|
|
||||||
@@ -837,13 +858,13 @@ static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1,
|
|||||||
struct dscb *dscb;
|
struct dscb *dscb;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
bzero(s44, 45);
|
memset(s44, 0, 45);
|
||||||
strncpy(s44, f1->DS1DSNAM, 44);
|
strncpy(s44, f1->DS1DSNAM, 44);
|
||||||
vtoc_ebcdic_dec(s44, s44, 44);
|
vtoc_ebcdic_dec(s44, s44, 44);
|
||||||
bzero(s6, 7);
|
memset(s6, 0, 7);
|
||||||
strncpy(s6, (char *)f1->DS1DSSN, 6);
|
strncpy(s6, (char *)f1->DS1DSSN, 6);
|
||||||
vtoc_ebcdic_dec(s6, s6, 6);
|
vtoc_ebcdic_dec(s6, s6, 6);
|
||||||
bzero(s13, 14);
|
memset(s13, 0, 14);
|
||||||
strncpy(s13, (char *)f1->DS1SYSCD, 13);
|
strncpy(s13, (char *)f1->DS1SYSCD, 13);
|
||||||
vtoc_ebcdic_dec(s13, s13, 13);
|
vtoc_ebcdic_dec(s13, s13, 13);
|
||||||
|
|
||||||
@@ -1071,7 +1092,7 @@ static void dasdview_print_vtoc_info_raw(dasdview_info_t *info)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: the explicit cylinder/head conversion for large volume
|
* Note: the explicit cylinder/head conversion for large volume
|
||||||
* adresses should not be necessary for entries that point to
|
* addresses should not be necessary for entries that point to
|
||||||
* vtoc labels, as those must be located in the first 65K-1 tracks,
|
* vtoc labels, as those must be located in the first 65K-1 tracks,
|
||||||
* but we do it anyway to be on the safe side.
|
* but we do it anyway to be on the safe side.
|
||||||
*/
|
*/
|
||||||
@@ -1080,9 +1101,9 @@ static void dasdview_print_format1_8_no_head(format1_label_t *f1)
|
|||||||
char s6[7], s13[14], s44[45];
|
char s6[7], s13[14], s44[45];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
bzero(s6, 7);
|
memset(s6, 0, 7);
|
||||||
bzero(s13, 14);
|
memset(s13, 0, 14);
|
||||||
bzero(s44, 45);
|
memset(s44, 0, 45);
|
||||||
|
|
||||||
strncpy(s44, f1->DS1DSNAM, 44);
|
strncpy(s44, f1->DS1DSNAM, 44);
|
||||||
printf("DS1DSNAM : ascii '%44s'\n", s44);
|
printf("DS1DSNAM : ascii '%44s'\n", s44);
|
||||||
@@ -1865,7 +1886,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
|||||||
|
|
||||||
/* seek in SEEK_STEP steps */
|
/* seek in SEEK_STEP steps */
|
||||||
for (i = 1; i <= j; i++) {
|
for (i = 1; i <= j; i++) {
|
||||||
rc = lseek64(fd, SEEK_STEP, SEEK_CUR);
|
rc = lseek(fd, SEEK_STEP, SEEK_CUR);
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
printf("*** rc: %d (%d) ***\n", rc, errno);
|
printf("*** rc: %d (%d) ***\n", rc, errno);
|
||||||
printf("*** j: %llu ***\n", j);
|
printf("*** j: %llu ***\n", j);
|
||||||
@@ -1918,7 +1939,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
|||||||
|
|
||||||
count = info->begin;
|
count = info->begin;
|
||||||
for (i = 1; i <= j; i++) {
|
for (i = 1; i <= j; i++) {
|
||||||
bzero(dumpstr, DUMP_STRING_SIZE);
|
memset(dumpstr, 0, DUMP_STRING_SIZE);
|
||||||
rc = read(fd, &dumpstr, DUMP_STRING_SIZE);
|
rc = read(fd, &dumpstr, DUMP_STRING_SIZE);
|
||||||
if (rc != DUMP_STRING_SIZE) {
|
if (rc != DUMP_STRING_SIZE) {
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -1937,7 +1958,7 @@ static void dasdview_view_standard(dasdview_info_t *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (k > 0) {
|
if (k > 0) {
|
||||||
bzero(dumpstr, DUMP_STRING_SIZE);
|
memset(dumpstr, 0, DUMP_STRING_SIZE);
|
||||||
rc = read(fd, &dumpstr, k);
|
rc = read(fd, &dumpstr, k);
|
||||||
if (rc != (int)k) {
|
if (rc != (int)k) {
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -1976,8 +1997,8 @@ static void dasdview_print_format_raw(unsigned int size, char *dumpstr)
|
|||||||
while (residual) {
|
while (residual) {
|
||||||
/* we handle at most 16 bytes per line */
|
/* we handle at most 16 bytes per line */
|
||||||
count = MIN(residual, 16u);
|
count = MIN(residual, 16u);
|
||||||
bzero(asc, 17);
|
memset(asc, 0, 17);
|
||||||
bzero(ebc, 17);
|
memset(ebc, 0, 17);
|
||||||
printf("|");
|
printf("|");
|
||||||
memcpy(asc, data, count);
|
memcpy(asc, data, count);
|
||||||
memcpy(ebc, data, count);
|
memcpy(ebc, data, count);
|
||||||
@@ -2066,7 +2087,7 @@ static void dasdview_print_raw_track(char *trackdata,
|
|||||||
{
|
{
|
||||||
struct eckd_count *ecount;
|
struct eckd_count *ecount;
|
||||||
char *data;
|
char *data;
|
||||||
u_int32_t record;
|
uint32_t record;
|
||||||
|
|
||||||
record = 0;
|
record = 0;
|
||||||
data = trackdata;
|
data = trackdata;
|
||||||
@@ -2091,8 +2112,8 @@ static void dasdview_print_raw_track(char *trackdata,
|
|||||||
|
|
||||||
static void dasdview_view_raw(dasdview_info_t *info)
|
static void dasdview_view_raw(dasdview_info_t *info)
|
||||||
{
|
{
|
||||||
u_int64_t residual, trckstart, trckend, track, trckbuffsize;
|
uint64_t residual, trckstart, trckend, track, trckbuffsize;
|
||||||
u_int64_t tracks_to_read, trckcount, i;
|
uint64_t tracks_to_read, trckcount, i;
|
||||||
char *trackdata;
|
char *trackdata;
|
||||||
char *data;
|
char *data;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -2187,7 +2208,7 @@ int main(int argc, char *argv[])
|
|||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
bzero(&info, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
while (1) {
|
while (1) {
|
||||||
oc = util_opt_getopt_long(argc, argv);
|
oc = util_opt_getopt_long(argc, argv);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#define DASDVIEW_H
|
#define DASDVIEW_H
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/********************************************************************************
|
/********************************************************************************
|
||||||
* SECTION: Definitions needed for DASD-API (see dasd.h)
|
* SECTION: Definitions needed for DASD-API (see dasd.h)
|
||||||
@@ -68,7 +69,7 @@ typedef struct dasdview_info
|
|||||||
int dasd_info_version;
|
int dasd_info_version;
|
||||||
unsigned int blksize;
|
unsigned int blksize;
|
||||||
struct hd_geometry geo;
|
struct hd_geometry geo;
|
||||||
u_int32_t hw_cylinders;
|
uint32_t hw_cylinders;
|
||||||
|
|
||||||
unsigned long long begin;
|
unsigned long long begin;
|
||||||
unsigned long long size;
|
unsigned long long size;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
. ds pfont \\n[.f]
|
. ds pfont \\n[.f]
|
||||||
. nh
|
. nh
|
||||||
. na
|
. na
|
||||||
. ft CW
|
. ft CR
|
||||||
\\$*
|
\\$*
|
||||||
. ft \\*[pfont]
|
. ft \\*[pfont]
|
||||||
. ad
|
. ad
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "dref.h"
|
#include "dref.h"
|
||||||
#include "dump.h"
|
#include "dump.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "idcache.h"
|
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
# Load protected key support module on s390 early at boot
|
# Load protected key support module on s390 early at boot
|
||||||
pkey
|
pkey
|
||||||
|
pkey_pckmo
|
||||||
|
pkey_ep11
|
||||||
|
pkey_cca
|
||||||
|
paes_s390
|
||||||
|
|||||||
@@ -18,3 +18,17 @@ CPI_SYSTEM_NAME=""
|
|||||||
# CPI sysplex name
|
# CPI sysplex name
|
||||||
#
|
#
|
||||||
CPI_SYSPLEX_NAME=""
|
CPI_SYSPLEX_NAME=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPI permit on protected virtualization guests
|
||||||
|
#
|
||||||
|
# Important: Set CPI_PERMIT_ON_PVGUEST=1 only if you trust the host system.
|
||||||
|
# Enabling these options allows the host to receive potentially sensitive
|
||||||
|
# Control-Program Identification (CPI) data from the protected virtualization
|
||||||
|
# guest, including:
|
||||||
|
# - system_type
|
||||||
|
# - system_level
|
||||||
|
# - sysplex_name
|
||||||
|
# - system_name
|
||||||
|
#
|
||||||
|
CPI_PERMIT_ON_PVGUEST=
|
||||||
|
|||||||
2
etc/udev/rules.d/59-virtio-blk.rules
Normal file
2
etc/udev/rules.d/59-virtio-blk.rules
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
SUBSYSTEM=="block", ACTION=="add", KERNEL=="vd*[!0-9]", TEST=="queue/rotational", ATTR{queue/rotational}="0"
|
||||||
|
SUBSYSTEM=="block", ACTION=="add", KERNEL=="vd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none"
|
||||||
4
etc/udev/rules.d/80-hotplug-cpu.rules
Normal file
4
etc/udev/rules.d/80-hotplug-cpu.rules
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
# Rules to set online a newly hotplugged cpu on s390x
|
||||||
|
#
|
||||||
|
SUBSYSTEM=="cpu", ACTION=="add", CONST{arch}=="s390*", ATTR{configure}=="1", TEST=="online", ATTR{online}!="1", ATTR{online}="1"
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
|
|
||||||
@@ -211,9 +212,9 @@ static void setpos(fdasd_anchor_t *anc, int dsn, int pos)
|
|||||||
anc->partno[dsn] = pos;
|
anc->partno[dsn] = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int32_t get_usable_cylinders(fdasd_anchor_t *anc)
|
static uint32_t get_usable_cylinders(fdasd_anchor_t *anc)
|
||||||
{
|
{
|
||||||
u_int32_t cyl;
|
uint32_t cyl;
|
||||||
|
|
||||||
/* large volume */
|
/* large volume */
|
||||||
if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL &&
|
if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL &&
|
||||||
@@ -221,7 +222,7 @@ static u_int32_t get_usable_cylinders(fdasd_anchor_t *anc)
|
|||||||
return anc->f4->DS4DCYL;
|
return anc->f4->DS4DCYL;
|
||||||
/* normal volume */
|
/* normal volume */
|
||||||
if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED)
|
if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED)
|
||||||
cyl = anc->f4->DS4DEVCT.DS4DSCYL - (u_int16_t)anc->f4->DS4DEVAC;
|
cyl = anc->f4->DS4DEVCT.DS4DSCYL - (uint16_t)anc->f4->DS4DEVAC;
|
||||||
else
|
else
|
||||||
cyl = anc->f4->DS4DEVCT.DS4DSCYL;
|
cyl = anc->f4->DS4DEVCT.DS4DSCYL;
|
||||||
|
|
||||||
@@ -230,7 +231,7 @@ static u_int32_t get_usable_cylinders(fdasd_anchor_t *anc)
|
|||||||
|
|
||||||
static void get_addr_of_highest_f1_f8_label(fdasd_anchor_t *anc, cchhb_t *addr)
|
static void get_addr_of_highest_f1_f8_label(fdasd_anchor_t *anc, cchhb_t *addr)
|
||||||
{
|
{
|
||||||
u_int8_t record;
|
uint8_t record;
|
||||||
/* We have to count the following labels:
|
/* We have to count the following labels:
|
||||||
* one format 4
|
* one format 4
|
||||||
* one format 5
|
* one format 5
|
||||||
@@ -406,7 +407,7 @@ static void fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str)
|
|||||||
*/
|
*/
|
||||||
static int read_line(void)
|
static int read_line(void)
|
||||||
{
|
{
|
||||||
bzero(line_buffer, LINE_LENGTH);
|
memset(line_buffer, 0, LINE_LENGTH);
|
||||||
line_ptr = line_buffer;
|
line_ptr = line_buffer;
|
||||||
if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
|
if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
|
||||||
clearerr(stdin);
|
clearerr(stdin);
|
||||||
@@ -493,7 +494,7 @@ static void fdasd_initialize_anchor(fdasd_anchor_t *anc)
|
|||||||
volume_label_t *vlabel;
|
volume_label_t *vlabel;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
bzero(anc, sizeof(fdasd_anchor_t));
|
memset(anc, 0, sizeof(fdasd_anchor_t));
|
||||||
|
|
||||||
for (i = 0; i < USABLE_PARTITIONS; i++)
|
for (i = 0; i < USABLE_PARTITIONS; i++)
|
||||||
setpos(anc, i, -1);
|
setpos(anc, i, -1);
|
||||||
@@ -519,17 +520,17 @@ static void fdasd_initialize_anchor(fdasd_anchor_t *anc)
|
|||||||
fdasd_error(anc, malloc_failed,
|
fdasd_error(anc, malloc_failed,
|
||||||
"FMT9 DSCB memory allocation failed.");
|
"FMT9 DSCB memory allocation failed.");
|
||||||
|
|
||||||
bzero(anc->f4, sizeof(format4_label_t));
|
memset(anc->f4, 0, sizeof(format4_label_t));
|
||||||
bzero(anc->f5, sizeof(format5_label_t));
|
memset(anc->f5, 0, sizeof(format5_label_t));
|
||||||
bzero(anc->f7, sizeof(format7_label_t));
|
memset(anc->f7, 0, sizeof(format7_label_t));
|
||||||
bzero(anc->f9, sizeof(format9_label_t));
|
memset(anc->f9, 0, sizeof(format9_label_t));
|
||||||
vtoc_init_format9_label(anc->f9);
|
vtoc_init_format9_label(anc->f9);
|
||||||
|
|
||||||
vlabel = malloc(sizeof(volume_label_t));
|
vlabel = malloc(sizeof(volume_label_t));
|
||||||
if (vlabel == NULL)
|
if (vlabel == NULL)
|
||||||
fdasd_error(anc, malloc_failed,
|
fdasd_error(anc, malloc_failed,
|
||||||
"Volume label memory allocation failed.");
|
"Volume label memory allocation failed.");
|
||||||
bzero(vlabel, sizeof(volume_label_t));
|
memset(vlabel, 0, sizeof(volume_label_t));
|
||||||
anc->vlabel = vlabel;
|
anc->vlabel = vlabel;
|
||||||
|
|
||||||
for (i = 1; i <= USABLE_PARTITIONS; i++) {
|
for (i = 1; i <= USABLE_PARTITIONS; i++) {
|
||||||
@@ -551,7 +552,7 @@ static void fdasd_initialize_anchor(fdasd_anchor_t *anc)
|
|||||||
if (part_info->f1 == NULL)
|
if (part_info->f1 == NULL)
|
||||||
fdasd_error(anc, malloc_failed,
|
fdasd_error(anc, malloc_failed,
|
||||||
"FMT1 DSCB memory allocation failed.");
|
"FMT1 DSCB memory allocation failed.");
|
||||||
bzero(part_info->f1, sizeof(format1_label_t));
|
memset(part_info->f1, 0, sizeof(format1_label_t));
|
||||||
|
|
||||||
if (prev_part_info) {
|
if (prev_part_info) {
|
||||||
prev_part_info->next = part_info;
|
prev_part_info->next = part_info;
|
||||||
@@ -975,7 +976,7 @@ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name)
|
|||||||
* Note:
|
* Note:
|
||||||
* - 'version' and 'help' are priority options.
|
* - 'version' and 'help' are priority options.
|
||||||
* All other parameters are ignored in that case.
|
* All other parameters are ignored in that case.
|
||||||
* - 'silent' and 'verbose' are allways allowed in any
|
* - 'silent' and 'verbose' are always allowed in any
|
||||||
* combination.
|
* combination.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -1052,10 +1053,10 @@ static void fdasd_show_mapping(fdasd_anchor_t *anc)
|
|||||||
int i = 0, j = 0, dev_len;
|
int i = 0, j = 0, dev_len;
|
||||||
|
|
||||||
printf("\ndevice .........: %s\n", options.device);
|
printf("\ndevice .........: %s\n", options.device);
|
||||||
bzero(str, sizeof(str));
|
memset(str, 0, sizeof(str));
|
||||||
vtoc_volume_label_get_label(anc->vlabel, str);
|
vtoc_volume_label_get_label(anc->vlabel, str);
|
||||||
printf("volume label ...: %.4s\n", str);
|
printf("volume label ...: %.4s\n", str);
|
||||||
bzero(str, sizeof(str));
|
memset(str, 0, sizeof(str));
|
||||||
vtoc_volume_label_get_volser(anc->vlabel, str);
|
vtoc_volume_label_get_volser(anc->vlabel, str);
|
||||||
printf("volume serial ..: %s\n\n", str);
|
printf("volume serial ..: %s\n\n", str);
|
||||||
|
|
||||||
@@ -1080,7 +1081,7 @@ static void fdasd_show_mapping(fdasd_anchor_t *anc)
|
|||||||
if (part_info->used != 0x01)
|
if (part_info->used != 0x01)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bzero(dsname, sizeof(dsname));
|
memset(dsname, 0, sizeof(dsname));
|
||||||
strncpy(dsname, part_info->f1->DS1DSNAM, 44);
|
strncpy(dsname, part_info->f1->DS1DSNAM, 44);
|
||||||
vtoc_ebcdic_dec(dsname, dsname, 44);
|
vtoc_ebcdic_dec(dsname, dsname, 44);
|
||||||
|
|
||||||
@@ -1103,7 +1104,7 @@ static void fdasd_print_volser(fdasd_anchor_t *anc)
|
|||||||
{
|
{
|
||||||
char volser[VOLSER_LENGTH + 1];
|
char volser[VOLSER_LENGTH + 1];
|
||||||
|
|
||||||
bzero(volser, VOLSER_LENGTH);
|
memset(volser, 0, VOLSER_LENGTH);
|
||||||
vtoc_ebcdic_dec(anc->vlabel->volid, volser, VOLSER_LENGTH);
|
vtoc_ebcdic_dec(anc->vlabel->volid, volser, VOLSER_LENGTH);
|
||||||
printf("%6.6s\n", volser);
|
printf("%6.6s\n", volser);
|
||||||
}
|
}
|
||||||
@@ -1372,7 +1373,7 @@ static void fdasd_write_vtoc_labels(fdasd_anchor_t *anc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* write empty labels to the rest of the blocks */
|
/* write empty labels to the rest of the blocks */
|
||||||
bzero(&emptyf1, sizeof(emptyf1));
|
memset(&emptyf1, 0, sizeof(emptyf1));
|
||||||
while (blk < maxblk) {
|
while (blk < maxblk) {
|
||||||
vtoc_write_label(options.device, blk, &emptyf1, NULL,
|
vtoc_write_label(options.device, blk, &emptyf1, NULL,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
@@ -1425,7 +1426,7 @@ static void fdasd_recreate_vtoc_unconditional(fdasd_anchor_t *anc)
|
|||||||
anc->formatted_cylinders, geo.heads);
|
anc->formatted_cylinders, geo.heads);
|
||||||
|
|
||||||
while (part_info != NULL) {
|
while (part_info != NULL) {
|
||||||
bzero(part_info->f1, sizeof(format1_label_t));
|
memset(part_info->f1, 0, sizeof(format1_label_t));
|
||||||
|
|
||||||
if (part_info->used == 0x01) {
|
if (part_info->used == 0x01) {
|
||||||
part_info->used = 0x00;
|
part_info->used = 0x00;
|
||||||
@@ -1826,7 +1827,7 @@ static void fdasd_process_valid_vtoc(fdasd_anchor_t *anc, unsigned long blk)
|
|||||||
|
|
||||||
/* go through remaining labels, f4 label already done */
|
/* go through remaining labels, f4 label already done */
|
||||||
for (i = 1; i < geo.sectors; i++) {
|
for (i = 1; i < geo.sectors; i++) {
|
||||||
bzero(&f1_label, f1_size);
|
memset(&f1_label, 0, f1_size);
|
||||||
vtoc_read_label(options.device, blk, &f1_label, NULL, NULL,
|
vtoc_read_label(options.device, blk, &f1_label, NULL, NULL,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
@@ -2470,9 +2471,9 @@ static int fdasd_get_partition_data(fdasd_anchor_t *anc, extent_t *part_extent,
|
|||||||
unsigned long start, stop, limit;
|
unsigned long start, stop, limit;
|
||||||
partition_info_t *part_tmp;
|
partition_info_t *part_tmp;
|
||||||
cchh_t llimit, ulimit;
|
cchh_t llimit, ulimit;
|
||||||
u_int16_t hh, head;
|
uint16_t hh, head;
|
||||||
u_int32_t cc, cyl;
|
uint32_t cc, cyl;
|
||||||
u_int8_t b1, b2;
|
uint8_t b1, b2;
|
||||||
char mesg[48];
|
char mesg[48];
|
||||||
|
|
||||||
start = FIRST_USABLE_TRK;
|
start = FIRST_USABLE_TRK;
|
||||||
@@ -2675,7 +2676,7 @@ static void fdasd_dequeue_old_partition(fdasd_anchor_t *anc,
|
|||||||
part_info->start_trk = 0x0;
|
part_info->start_trk = 0x0;
|
||||||
part_info->end_trk = 0x0;
|
part_info->end_trk = 0x0;
|
||||||
part_info->fspace_trk = 0x0;
|
part_info->fspace_trk = 0x0;
|
||||||
bzero(part_info->f1, sizeof(format1_label_t));
|
memset(part_info->f1, 0, sizeof(format1_label_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2758,7 +2759,7 @@ static void fdasd_remove_partition(fdasd_anchor_t *anc)
|
|||||||
if (anc->used_partitions != 0)
|
if (anc->used_partitions != 0)
|
||||||
get_addr_of_highest_f1_f8_label(anc, &hf1);
|
get_addr_of_highest_f1_f8_label(anc, &hf1);
|
||||||
else
|
else
|
||||||
bzero(&hf1, sizeof(struct cchhb));
|
memset(&hf1, 0, sizeof(struct cchhb));
|
||||||
|
|
||||||
vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC + 1);
|
vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC + 1);
|
||||||
vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose,
|
vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose,
|
||||||
@@ -2777,8 +2778,8 @@ static void fdasd_auto_partition(fdasd_anchor_t *anc)
|
|||||||
{
|
{
|
||||||
partition_info_t *part_info = anc->first;
|
partition_info_t *part_info = anc->first;
|
||||||
cchh_t llimit, ulimit;
|
cchh_t llimit, ulimit;
|
||||||
u_int16_t head;
|
uint16_t head;
|
||||||
u_int32_t cyl;
|
uint32_t cyl;
|
||||||
extent_t ext;
|
extent_t ext;
|
||||||
cchhb_t hf1;
|
cchhb_t hf1;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#ifndef FDASD_H
|
#ifndef FDASD_H
|
||||||
#define FDASD_H
|
#define FDASD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* SECTION: Definitions needed for DASD-API (see dasd.h) *
|
* SECTION: Definitions needed for DASD-API (see dasd.h) *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@@ -67,7 +69,7 @@ static struct fdasd_options options = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct partition_info {
|
typedef struct partition_info {
|
||||||
u_int8_t used;
|
uint8_t used;
|
||||||
unsigned long start_trk;
|
unsigned long start_trk;
|
||||||
unsigned long end_trk;
|
unsigned long end_trk;
|
||||||
unsigned long len_trk;
|
unsigned long len_trk;
|
||||||
@@ -100,7 +102,7 @@ typedef struct fdasd_anchor {
|
|||||||
int option_reuse;
|
int option_reuse;
|
||||||
int option_recreate;
|
int option_recreate;
|
||||||
int partno[USABLE_PARTITIONS];
|
int partno[USABLE_PARTITIONS];
|
||||||
u_int16_t dev_type;
|
uint16_t dev_type;
|
||||||
unsigned int used_partitions;
|
unsigned int used_partitions;
|
||||||
unsigned long label_pos;
|
unsigned long label_pos;
|
||||||
unsigned int blksize;
|
unsigned int blksize;
|
||||||
@@ -113,8 +115,8 @@ typedef struct fdasd_anchor {
|
|||||||
partition_info_t *last;
|
partition_info_t *last;
|
||||||
volume_label_t *vlabel;
|
volume_label_t *vlabel;
|
||||||
config_data_t confdata[USABLE_PARTITIONS];
|
config_data_t confdata[USABLE_PARTITIONS];
|
||||||
u_int32_t hw_cylinders;
|
uint32_t hw_cylinders;
|
||||||
u_int32_t formatted_cylinders;
|
uint32_t formatted_cylinders;
|
||||||
} fdasd_anchor_t;
|
} fdasd_anchor_t;
|
||||||
|
|
||||||
enum offset {lower, upper};
|
enum offset {lower, upper};
|
||||||
|
|||||||
5
genprotimg/.gitignore
vendored
5
genprotimg/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
tags
|
|
||||||
compile_commands.json
|
|
||||||
src/.check-dep-genprotimg
|
|
||||||
src/.detect-openssl.dep.c
|
|
||||||
src/genprotimg
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Common definitions
|
|
||||||
include ../common.mak
|
|
||||||
|
|
||||||
.DEFAULT_GOAL := all
|
|
||||||
|
|
||||||
PKGDATADIR := "$(TOOLS_DATADIR)/genprotimg"
|
|
||||||
TESTS :=
|
|
||||||
SUBDIRS := boot src man
|
|
||||||
RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
|
||||||
|
|
||||||
all: all-recursive
|
|
||||||
|
|
||||||
install: install-recursive
|
|
||||||
$(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
|
||||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(DESTDIR)$(PKGDATADIR)"
|
|
||||||
|
|
||||||
clean: clean-recursive
|
|
||||||
|
|
||||||
$(RECURSIVE_TARGETS):
|
|
||||||
@target=`echo $@ |sed s/-recursive//`; \
|
|
||||||
for d in $(SUBDIRS); do \
|
|
||||||
$(MAKE) -C $$d $$target || exit 1; \
|
|
||||||
done
|
|
||||||
|
|
||||||
.PHONY: all install clean $(RECURSIVE_TARGETS)
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
# 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 |
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/error.h"
|
|
||||||
#include "boot/s390.h"
|
|
||||||
#include "boot/ipl.h"
|
|
||||||
#include "sclp.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))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Common definitions
|
|
||||||
include ../../common.mak
|
|
||||||
|
|
||||||
all:
|
|
||||||
|
|
||||||
install:
|
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
|
|
||||||
$(INSTALL) -m 644 -c genprotimg.1 $(DESTDIR)$(MANDIR)/man1
|
|
||||||
|
|
||||||
clean:
|
|
||||||
|
|
||||||
.PHONY: all install clean
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
.\" 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 1 "May 2022" "s390-tools"
|
|
||||||
.SH NAME
|
|
||||||
genprotimg \- Create a protected virtualization image
|
|
||||||
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.SY
|
|
||||||
.B genprotimg
|
|
||||||
\fB\-k\fR \fIHOST_KEY_DOCUMENT\fR...
|
|
||||||
\fB\-C\fR \fICERTIFICATE\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, certificates for the host-key document
|
|
||||||
verification, and an output file name. Optionally, specify an initial
|
|
||||||
RAM filesystem, and a file containing the kernel parameters. If the
|
|
||||||
command should be run offline, use the \fB\-\-offline\fR option and
|
|
||||||
specify the certificate revocation lists (CRLs) by using the
|
|
||||||
\fB\-\-crl\fR option. Should special circumstances require it, you can
|
|
||||||
optionally specify your own keys for the encryption by using the
|
|
||||||
experimental options. For all certificates, CRLs, and host-key
|
|
||||||
documents, both the PEM and DER input formats are supported. 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\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR
|
|
||||||
Specifies the certificate that is used to establish a chain of trust
|
|
||||||
for the verification of the host-key documents. Specify this option
|
|
||||||
twice to specify the IBM Z signing key and the intermediate CA
|
|
||||||
certificate (signed by the root CA).
|
|
||||||
Required. Ignored if \fB--no-verify\fP is specified.
|
|
||||||
.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\-\-crl\fR=\fI\,FILE\/\fR
|
|
||||||
Specifies the revocation list that is used to check whether a
|
|
||||||
certificate of the chain of trust is revoked. Specify this option
|
|
||||||
multiple times to use multiple CRLs. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-offline\fR
|
|
||||||
Specifies offline mode, in which no attempt is made to download
|
|
||||||
CRLs. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
|
||||||
Specifies the root CA certificate for the verification. If omitted,
|
|
||||||
the system wide root CAs installed on the system is used. Use
|
|
||||||
this only if you trust the specified certificate. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-no-verify\fR
|
|
||||||
Does not require the host-key documents to be valid.
|
|
||||||
Do not use for a production image unless you verified
|
|
||||||
the host-key document before. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-comm\-key\fR=\fI\,FILE\/\fR
|
|
||||||
Specifies the customer communication key (CCK). This key is used for the
|
|
||||||
PV guest dump encryption and to derive the CCK-derived extension secret
|
|
||||||
used for add-secret requests. Use a secure, random, plaintext AES-256
|
|
||||||
GCM key. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-enable\-dump\fR
|
|
||||||
Enable PV guest dumps. Requires the \fB\-\-comm\-key\fR option. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-disable\-dump\fR
|
|
||||||
Disable PV guest dumps. This is the default.
|
|
||||||
.TP
|
|
||||||
\fB\-\-enable\-cck\-extension\-secret\fR
|
|
||||||
Add-secret requests must provide an extension secret that matches the
|
|
||||||
CCK-derived extension secret. Requires the \fB\-\-comm\-key\fR option.
|
|
||||||
Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-\-disable\-cck\-extension\-secret\fR
|
|
||||||
Add-secret requests don't have to provide an extension secret. This is
|
|
||||||
the default.
|
|
||||||
.TP
|
|
||||||
\fB\-\-enable\-pckmo\fR
|
|
||||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
|
||||||
functions. This is the default.
|
|
||||||
.TP
|
|
||||||
\fB\-\-disable\-pckmo\fR
|
|
||||||
Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
|
||||||
functions. Optional.
|
|
||||||
.TP
|
|
||||||
\fB\-v\fR, \fB\-\-version\fR
|
|
||||||
Prints version information, then exits.
|
|
||||||
|
|
||||||
.SH EXAMPLES
|
|
||||||
|
|
||||||
These are examples to generate a protected virtualization image in
|
|
||||||
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, the
|
|
||||||
initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
|
||||||
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the
|
|
||||||
IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
|
||||||
host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in
|
|
||||||
\fI\,comm-key\/\fR, which is used for the PV guest dump support in the second
|
|
||||||
example.
|
|
||||||
|
|
||||||
Generate a protected virtualization image:
|
|
||||||
|
|
||||||
.PP
|
|
||||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR
|
|
||||||
|
|
||||||
Generate a protected virtualization image with PV guest dump support:
|
|
||||||
.PP
|
|
||||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
|
||||||
|
|
||||||
.SH NOTES
|
|
||||||
.IP "1." 4
|
|
||||||
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)
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
# Common definitions
|
|
||||||
include ../../common.mak
|
|
||||||
|
|
||||||
bin_PROGRAM = genprotimg
|
|
||||||
|
|
||||||
PKGDATADIR ?= "$(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 \
|
|
||||||
-Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \
|
|
||||||
-Wpointer-arith -Wno-error=inline \
|
|
||||||
$(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 utils/curl.c \
|
|
||||||
$(NULL)
|
|
||||||
$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
|
||||||
|
|
||||||
ALL_CFLAGS += -DPKGDATADIR=$(PKGDATADIR) \
|
|
||||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
|
||||||
-DOPENSSL_API_COMPAT=0x10100000L \
|
|
||||||
$(WARNINGS) \
|
|
||||||
$(NULL)
|
|
||||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
|
||||||
LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS)
|
|
||||||
|
|
||||||
|
|
||||||
GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0)
|
|
||||||
GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0)
|
|
||||||
LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto)
|
|
||||||
LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto)
|
|
||||||
LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl)
|
|
||||||
LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl)
|
|
||||||
|
|
||||||
BUILD_TARGETS := skip-$(bin_PROGRAM)
|
|
||||||
INSTALL_TARGETS := skip-$(bin_PROGRAM)
|
|
||||||
ifneq (${HAVE_OPENSSL},0)
|
|
||||||
ifneq (${HAVE_GLIB2},0)
|
|
||||||
ifneq (${HAVE_LIBCURL},0)
|
|
||||||
BUILD_TARGETS := $(bin_PROGRAM)
|
|
||||||
INSTALL_TARGETS := install-$(bin_PROGRAM)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: $(BUILD_TARGETS)
|
|
||||||
|
|
||||||
install: $(INSTALL_TARGETS)
|
|
||||||
|
|
||||||
$(bin_PROGRAM): $($(bin_PROGRAM)_OBJS)
|
|
||||||
|
|
||||||
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.")
|
|
||||||
$(call check_dep, \
|
|
||||||
"$(bin_PROGRAM)", \
|
|
||||||
"curl/curl.h", \
|
|
||||||
"libcurl-devel", \
|
|
||||||
"HAVE_LIBCURL=0")
|
|
||||||
touch $@
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corp. 2020, 2022
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef COMMON_H
|
|
||||||
#define COMMON_H
|
|
||||||
|
|
||||||
#define GETTEXT_PACKAGE "genprotimg"
|
|
||||||
#include <glib.h>
|
|
||||||
#include <glib/gi18n.h>
|
|
||||||
|
|
||||||
#include "boot/linux_layout.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
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
#include "utils/crypto.h"
|
|
||||||
#include "utils/curl.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((constructor)) __init(void);
|
|
||||||
static void __attribute__((destructor)) __cleanup(void);
|
|
||||||
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);
|
|
||||||
g_clear_pointer(&img, pv_img_free);
|
|
||||||
g_clear_pointer(&args, pv_args_free);
|
|
||||||
exit(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __init(void)
|
|
||||||
{
|
|
||||||
pv_crypto_init();
|
|
||||||
if (curl_init() != 0)
|
|
||||||
g_abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __cleanup(void)
|
|
||||||
{
|
|
||||||
curl_cleanup();
|
|
||||||
pv_crypto_cleanup();
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
/* IBM signing key subject */
|
|
||||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
|
||||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
|
||||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie"
|
|
||||||
#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk"
|
|
||||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
|
||||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
|
||||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
|
||||||
#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6
|
|
||||||
|
|
||||||
/* Minimum security level for the keys/certificates used to establish a chain of
|
|
||||||
* trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html
|
|
||||||
* for details).
|
|
||||||
*/
|
|
||||||
#define PV_CERTS_SECURITY_LEVEL 2
|
|
||||||
|
|
||||||
union ecdh_pub_key {
|
|
||||||
struct {
|
|
||||||
uint8_t x[80];
|
|
||||||
uint8_t y[80];
|
|
||||||
};
|
|
||||||
uint8_t data[160];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/psw.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
|
|
||||||
|
|
||||||
/* Internal helper macro */
|
|
||||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
|
||||||
|
|
||||||
/* Plaintext control flags */
|
|
||||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */
|
|
||||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */
|
|
||||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
|
||||||
#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */
|
|
||||||
#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */
|
|
||||||
|
|
||||||
/* Secret control flags */
|
|
||||||
#define PV_SCF_CCK_EXTENSION_SECRET_ENFORCMENT \
|
|
||||||
__PV_BIT(1) /* All add-secret requests must provide an extension secret */
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
@@ -1,556 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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. For all certificates, revocation\n"
|
|
||||||
"lists, and host-key documents, both the PEM and DER input formats are\n"
|
|
||||||
"supported.";
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const PvControlFlagsArgs *cf_args = &args->cf_args;
|
|
||||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
|
||||||
|
|
||||||
/* Check for mutually exclusive arguments */
|
|
||||||
if (cf_args->pcf &&
|
|
||||||
!(cf_args->enable_pckmo == PV_NOT_SET && cf_args->enable_dump == PV_NOT_SET)) {
|
|
||||||
g_set_error(
|
|
||||||
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
|
||||||
_("The '--x-pcf' option cannot be used with the '--(enable|disable)-pckmo' or"
|
|
||||||
" '--(enable|disable)-dump' flags.\nUse 'genprotimg --help' for more information"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cf_args->scf && !(cf_args->enable_cck_extension_secret_enforcement == PV_NOT_SET)) {
|
|
||||||
g_set_error(
|
|
||||||
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
|
||||||
_("The '--x-scf' option cannot be used with the '--(enable|disable)-extension-secret-required' flags.\nUse 'genprotimg --help' for more information"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for unused arguments */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for mandatory arguments */
|
|
||||||
if (cf_args->enable_dump == PV_TRUE && !args->cust_comm_key_path) {
|
|
||||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
|
||||||
_("Option '--enable-dump' requires the '--comm-key' option.\nUse 'genprotimg "
|
|
||||||
"--help' for more information"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (cf_args->enable_cck_extension_secret_enforcement == PV_TRUE &&
|
|
||||||
!args->cust_comm_key_path) {
|
|
||||||
g_set_error(
|
|
||||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
|
||||||
_("Option '--enable-cck-extension-secret' requires the '--comm-key' option.\nUse 'genprotimg "
|
|
||||||
"--help' for more information"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!args->output_path) {
|
|
||||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
|
||||||
_("Option '--output' is required.\nUse 'genprotimg --help' for more information"));
|
|
||||||
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 &&
|
|
||||||
(!args->untrusted_cert_paths ||
|
|
||||||
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
|
||||||
g_set_error(
|
|
||||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
|
||||||
_("Either specify the IBM Z signing key and intermediate CA certificate\n"
|
|
||||||
"by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
|
|
||||||
"host-key document verification completely (at your own risk)."));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, "--comm-key"))
|
|
||||||
args_option = &args->cust_comm_key_path;
|
|
||||||
if (g_str_equal(option, "--root-ca"))
|
|
||||||
args_option = &args->root_ca_path;
|
|
||||||
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
|
||||||
args_option = &args->output_path;
|
|
||||||
if (g_str_equal(option, "--x-comp-key"))
|
|
||||||
args_option = &args->xts_key_path;
|
|
||||||
if (g_str_equal(option, "--x-header-key"))
|
|
||||||
args_option = &args->cust_root_key_path;
|
|
||||||
if (g_str_equal(option, "--x-pcf"))
|
|
||||||
args_option = &args->cf_args.pcf;
|
|
||||||
if (g_str_equal(option, "--x-psw"))
|
|
||||||
args_option = &args->psw_addr;
|
|
||||||
if (g_str_equal(option, "--x-scf"))
|
|
||||||
args_option = &args->cf_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 MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE) (cb_##FLAG##_##VALUE)
|
|
||||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, VALUE) \
|
|
||||||
static gboolean MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE)(const gchar *option G_GNUC_UNUSED, \
|
|
||||||
const gchar *value G_GNUC_UNUSED, \
|
|
||||||
PvArgs *args, GError **err) \
|
|
||||||
{ \
|
|
||||||
if (!(args->cf_args.enable_##FLAG == PV_NOT_SET || \
|
|
||||||
args->cf_args.enable_##FLAG == VALUE)) { \
|
|
||||||
g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \
|
|
||||||
"'--enable-" #FLAG "' and '--disable-" #FLAG \
|
|
||||||
"' are mutually exclusive"); \
|
|
||||||
return FALSE; \
|
|
||||||
} \
|
|
||||||
args->cf_args.enable_##FLAG = VALUE; \
|
|
||||||
return TRUE; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CBS(FLAG) \
|
|
||||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_TRUE) \
|
|
||||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_FALSE)
|
|
||||||
|
|
||||||
#define MUT_EXCL_BOOL_FLAG(NAME, FLAG, ENABLE_DESC, DISABLE_DESC) \
|
|
||||||
{ \
|
|
||||||
.long_name = "enable-" #NAME, \
|
|
||||||
.short_name = 0, \
|
|
||||||
.flags = G_OPTION_FLAG_NO_ARG, \
|
|
||||||
.arg = G_OPTION_ARG_CALLBACK, \
|
|
||||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_TRUE), \
|
|
||||||
.description = ENABLE_DESC, \
|
|
||||||
}, \
|
|
||||||
{ \
|
|
||||||
.long_name = "disable-" #NAME, .short_name = 0, .flags = G_OPTION_FLAG_NO_ARG, \
|
|
||||||
.arg = G_OPTION_ARG_CALLBACK, \
|
|
||||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_FALSE), \
|
|
||||||
.description = DISABLE_DESC, \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define INDENT " "
|
|
||||||
|
|
||||||
/* Define the callbacks for mutually exclusive command line flags */
|
|
||||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(dump);
|
|
||||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(pckmo);
|
|
||||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(cck_extension_secret_enforcement);
|
|
||||||
|
|
||||||
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. Specify this option multiple times\n" INDENT
|
|
||||||
"to enable the image to run on more than one host."),
|
|
||||||
.arg_description = _("FILE") },
|
|
||||||
{ .long_name = "cert",
|
|
||||||
.short_name = 'C',
|
|
||||||
.flags = G_OPTION_FLAG_NONE,
|
|
||||||
.arg = G_OPTION_ARG_FILENAME_ARRAY,
|
|
||||||
.arg_data = &args->untrusted_cert_paths,
|
|
||||||
.description = _(
|
|
||||||
"FILE contains a certificate that is used to\n" INDENT
|
|
||||||
"establish a chain of trust for the verification\n" INDENT
|
|
||||||
"of the host-key documents. The IBM Z signing\n" INDENT
|
|
||||||
"key and intermediate CA certificate (signed\n" INDENT
|
|
||||||
"by the root CA) are required."),
|
|
||||||
.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_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_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") },
|
|
||||||
MUT_EXCL_BOOL_FLAG(dump, dump,
|
|
||||||
_("Enable PV guest dumps (optional). This option\n" INDENT
|
|
||||||
"requires the '--comm-key' option."),
|
|
||||||
_("Disable PV guest dumps (default).")),
|
|
||||||
MUT_EXCL_BOOL_FLAG(
|
|
||||||
cck-extension-secret, cck_extension_secret_enforcement,
|
|
||||||
_("Add-secret requests must provide an extension\n" INDENT
|
|
||||||
"secret that matches the CCK-derived extension\n" INDENT
|
|
||||||
"secret (optional). This option requires the\n" INDENT
|
|
||||||
"'--comm-key' option."),
|
|
||||||
_("Add-secret requests don't have to provide\n" INDENT
|
|
||||||
"the CCK-derived extension secret (default).")),
|
|
||||||
MUT_EXCL_BOOL_FLAG(pckmo, pckmo,
|
|
||||||
_("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
|
||||||
"ECC PCKMO key encryption functions (default)."),
|
|
||||||
_("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
|
||||||
"ECC PCKMO key encryption functions (optional).")),
|
|
||||||
{ .long_name = "comm-key",
|
|
||||||
.short_name = 0,
|
|
||||||
.flags = G_OPTION_FLAG_FILENAME,
|
|
||||||
.arg = G_OPTION_ARG_CALLBACK,
|
|
||||||
.arg_data = cb_set_string_option,
|
|
||||||
.description = _(
|
|
||||||
"FILE contains the customer communication key\n" INDENT
|
|
||||||
"(CCK) (optional)."),
|
|
||||||
.arg_description = _("FILE") },
|
|
||||||
{ .long_name = "crl",
|
|
||||||
.short_name = 0,
|
|
||||||
.flags = G_OPTION_FLAG_NONE,
|
|
||||||
.arg = G_OPTION_ARG_FILENAME_ARRAY,
|
|
||||||
.arg_data = &args->crl_paths,
|
|
||||||
.description = _(
|
|
||||||
"FILE contains a certificate revocation list\n" INDENT
|
|
||||||
"(optional)."),
|
|
||||||
.arg_description = _("FILE") },
|
|
||||||
{ .long_name = "offline",
|
|
||||||
.short_name = 0,
|
|
||||||
.flags = G_OPTION_FLAG_NONE,
|
|
||||||
.arg = G_OPTION_ARG_NONE,
|
|
||||||
.arg_data = &args->offline,
|
|
||||||
.description = _("Don't download CRLs (optional)."),
|
|
||||||
.arg_description = NULL },
|
|
||||||
{ .long_name = "root-ca",
|
|
||||||
.short_name = 0,
|
|
||||||
.flags = G_OPTION_FLAG_FILENAME,
|
|
||||||
.arg = G_OPTION_ARG_CALLBACK,
|
|
||||||
.arg_data = cb_set_string_option,
|
|
||||||
.description = _(
|
|
||||||
"Set FILE as the trusted root CA and don't use the\n" INDENT
|
|
||||||
"root CAs that are installed on the system (optional)."),
|
|
||||||
.arg_description = _("FILE") },
|
|
||||||
{ .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-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; mutually exclusive with\n" INDENT
|
|
||||||
"'--(enable|disable)-pckmo'; default: '0xe0'."),
|
|
||||||
.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; mutually exclusive with\n" INDENT
|
|
||||||
"'--(enable|disable)-cck-extension-secret';\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);
|
|
||||||
/* `args->cf_args` is implicitly initialized with zeros since
|
|
||||||
* `g_new0` is used. So there is no reason to explicitly
|
|
||||||
* initialize the values as PV_NOT_SET == 0.
|
|
||||||
*/
|
|
||||||
return g_steal_pointer(&args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pv_args_free(PvArgs *args)
|
|
||||||
{
|
|
||||||
if (!args)
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_free(args->cf_args.pcf);
|
|
||||||
g_free(args->cf_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_free(args->root_ca_path);
|
|
||||||
g_strfreev(args->crl_paths);
|
|
||||||
g_strfreev(args->untrusted_cert_paths);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 enum pv_tristate {
|
|
||||||
PV_NOT_SET = 0,
|
|
||||||
PV_TRUE,
|
|
||||||
PV_FALSE,
|
|
||||||
} PvTristate;
|
|
||||||
/* The value of PV_NOT_SET is not allowed to be changed */
|
|
||||||
STATIC_ASSERT(PV_NOT_SET == 0)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
gchar *pcf;
|
|
||||||
gchar *scf;
|
|
||||||
/* Add-secret requests do require CCK-extension secrets */
|
|
||||||
PvTristate enable_cck_extension_secret_enforcement;
|
|
||||||
PvTristate enable_dump;
|
|
||||||
PvTristate enable_pckmo;
|
|
||||||
} PvControlFlagsArgs;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
gint log_level;
|
|
||||||
gint no_verify;
|
|
||||||
gboolean offline;
|
|
||||||
PvControlFlagsArgs cf_args;
|
|
||||||
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 *root_ca_path; /* Trusted root CA used for the verification of the
|
|
||||||
* chain of trust (if specified).
|
|
||||||
*/
|
|
||||||
gchar **untrusted_cert_paths;
|
|
||||||
gchar **crl_paths;
|
|
||||||
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
|
|
||||||
@@ -1,432 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "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 PvBuffer *buf,
|
|
||||||
GError **err)
|
|
||||||
{
|
|
||||||
g_assert(buf);
|
|
||||||
|
|
||||||
g_autoptr(PvBuffer) dup_buf = pv_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:
|
|
||||||
pv_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(PvBuffer) enc_buf = NULL;
|
|
||||||
|
|
||||||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
|
||||||
g_autoptr(PvBuffer) new = NULL;
|
|
||||||
|
|
||||||
/* create a page aligned copy */
|
|
||||||
new = pv_buffer_dup(component->buf, TRUE);
|
|
||||||
pv_buffer_clear(&component->buf);
|
|
||||||
component->buf = g_steal_pointer(&new);
|
|
||||||
}
|
|
||||||
enc_buf = encrypt_buf(parms, component->buf, err);
|
|
||||||
if (!enc_buf)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
pv_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(PvBuffer) buf = NULL;
|
|
||||||
|
|
||||||
buf = pv_buffer_dup(component->buf, TRUE);
|
|
||||||
pv_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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 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 PvBuffer *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"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 PvBuffer *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();
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
PvBuffer *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 PvBuffer *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
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/psw.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, PvBuffer **pld_digest,
|
|
||||||
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
|
||||||
uint64_t *nep, GError **err)
|
|
||||||
{
|
|
||||||
g_autoptr(PvBuffer) tmp_pld_digest = NULL;
|
|
||||||
g_autoptr(PvBuffer) tmp_ald_digest = NULL;
|
|
||||||
g_autoptr(PvBuffer) tmp_tld_digest = NULL;
|
|
||||||
|
|
||||||
comps->finalized = TRUE;
|
|
||||||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/psw.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, PvBuffer **pld_digest,
|
|
||||||
PvBuffer **ald_digest, PvBuffer **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
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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");
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,
|
|
||||||
PV_ERROR_CURL_INIT_FAILED,
|
|
||||||
PV_ERROR_DOWNLOAD_FAILED,
|
|
||||||
} 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,
|
|
||||||
PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD,
|
|
||||||
PV_CRYPTO_ERROR_EXPIRED,
|
|
||||||
PV_CRYPTO_ERROR_NOT_VALID_YET,
|
|
||||||
PV_CRYPTO_ERROR_LOAD_CRL,
|
|
||||||
PV_CRYPTO_ERROR_NO_PUBLIC_KEY,
|
|
||||||
PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM,
|
|
||||||
PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH,
|
|
||||||
PV_CRYPTO_ERROR_INVALID_URI,
|
|
||||||
PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED,
|
|
||||||
PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID,
|
|
||||||
PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID,
|
|
||||||
PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
|
||||||
PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
|
||||||
PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
|
||||||
PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE,
|
|
||||||
PV_CRYPTO_ERROR_NO_CRL,
|
|
||||||
PV_CRYPTO_ERROR_LOAD_ROOT_CA,
|
|
||||||
PV_CRYPTO_ERROR_LOAD_DEFAULT_CA,
|
|
||||||
PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
|
|
||||||
PV_CRYPTO_ERROR_WRONG_CA_USED,
|
|
||||||
PV_CRYPTO_ERROR_SKID_AKID_MISMATCH,
|
|
||||||
PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND,
|
|
||||||
PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL,
|
|
||||||
PV_CRYPTO_ERROR_NO_CRLDP,
|
|
||||||
PV_CRYPTO_ERROR_CERT_REVOKED,
|
|
||||||
} PvCryptoErrors;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/psw.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_PCF_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, PvBuffer *buf,
|
|
||||||
GError **err)
|
|
||||||
{
|
|
||||||
uint32_t hdr_len = pv_hdr_size(hdr);
|
|
||||||
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
|
||||||
guint tag_len = pv_hdr_tag_size(hdr);
|
|
||||||
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
|
||||||
const PvBuffer aad_part = { .data = buf->data, .size = aad_len };
|
|
||||||
PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
|
||||||
.size = enc_len };
|
|
||||||
PvBuffer 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(PvBuffer) pld = NULL;
|
|
||||||
g_autoptr(PvBuffer) ald = NULL;
|
|
||||||
g_autoptr(PvBuffer) tld = NULL;
|
|
||||||
uint64_t nep = 0;
|
|
||||||
|
|
||||||
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
|
||||||
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 PvBuffer *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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
|
||||||
enum PvCryptoMode mode, GError **err)
|
|
||||||
{
|
|
||||||
uint32_t hdr_size = pv_hdr_size(hdr);
|
|
||||||
g_autoptr(PvBuffer) ret = NULL;
|
|
||||||
|
|
||||||
ret = pv_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);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "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);
|
|
||||||
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
|
||||||
enum PvCryptoMode mode, GError **err);
|
|
||||||
uint32_t pv_hdr_size(const PvHdr *hdr);
|
|
||||||
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
|
||||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
|
||||||
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
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/psw.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 */
|
|
||||||
PvBuffer *stage3a; /* stage3a containing IPIB and PV header */
|
|
||||||
gsize stage3a_bin_size; /* size of stage3a.bin */
|
|
||||||
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
|
||||||
* location 0 of the created image
|
|
||||||
*/
|
|
||||||
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 */
|
|
||||||
PvBuffer *cust_root_key;
|
|
||||||
PvBuffer *gcm_iv;
|
|
||||||
const EVP_CIPHER *gcm_cipher;
|
|
||||||
/* Information for the IPIB and PV header */
|
|
||||||
uint64_t pcf;
|
|
||||||
uint64_t scf;
|
|
||||||
PvBuffer *cust_comm_key;
|
|
||||||
const EVP_CIPHER *cust_comm_cipher;
|
|
||||||
PvBuffer *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, PvBuffer **pld, PvBuffer **ald,
|
|
||||||
PvBuffer **tld, uint64_t *nep, GError **err);
|
|
||||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
|
||||||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
|
||||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
|
||||||
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
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "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 PvBuffer *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_PBT_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 PvBuffer *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);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 PvBuffer *hdr, GError **err);
|
|
||||||
void pv_ipib_free(IplParameterBlock *ipib);
|
|
||||||
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
|
||||||
gsize args_size, gsize data_size,
|
|
||||||
gboolean data_aligned, GError **err)
|
|
||||||
{
|
|
||||||
g_autoptr(GMappedFile) mapped_file = NULL;
|
|
||||||
g_autoptr(PvBuffer) 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 = pv_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
|
||||||
gsize data_size, GError **err)
|
|
||||||
{
|
|
||||||
return loader_getblob(filename, loader_size,
|
|
||||||
sizeof(struct stage3a_args), data_size, TRUE,
|
|
||||||
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(PvBuffer *loader, gsize loader_size,
|
|
||||||
const PvBuffer *hdr, struct ipl_parameter_block *ipib,
|
|
||||||
GError **err)
|
|
||||||
{
|
|
||||||
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
|
||||||
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(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr,
|
|
||||||
struct ipl_parameter_block *ipib, GError **err)
|
|
||||||
{
|
|
||||||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err)
|
|
||||||
{
|
|
||||||
g_autoptr(PvBuffer) 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(PvBuffer *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);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/stage3b.h"
|
|
||||||
#include "utils/buffer.h"
|
|
||||||
|
|
||||||
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
|
||||||
gsize data_size, GError **err);
|
|
||||||
gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr,
|
|
||||||
struct ipl_parameter_block *ipib, GError **err);
|
|
||||||
PvBuffer *stage3b_getblob(const gchar *filename, GError **err);
|
|
||||||
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args);
|
|
||||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "lib/zt_common.h"
|
|
||||||
#include "boot/page.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
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
PvBuffer *pv_buffer_alloc(gsize size)
|
|
||||||
{
|
|
||||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
|
||||||
|
|
||||||
ret->data = g_malloc0(size);
|
|
||||||
ret->size = size;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
PvBuffer *pv_buffer_take(char *data, gsize size)
|
|
||||||
{
|
|
||||||
PvBuffer *ret = g_new0(PvBuffer, 1);
|
|
||||||
|
|
||||||
ret->data = data;
|
|
||||||
ret->size = size;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
|
||||||
{
|
|
||||||
PvBuffer *ret;
|
|
||||||
gsize size;
|
|
||||||
|
|
||||||
if (!buf)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
size = buf->size;
|
|
||||||
if (page_aligned)
|
|
||||||
size = PAGE_ALIGN(size);
|
|
||||||
|
|
||||||
ret = pv_buffer_alloc(size);
|
|
||||||
|
|
||||||
/* content will be 0-right-padded */
|
|
||||||
memcpy(ret->data, buf->data, buf->size);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err)
|
|
||||||
{
|
|
||||||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pv_buffer_free(PvBuffer *buf)
|
|
||||||
{
|
|
||||||
if (!buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_free(buf->data);
|
|
||||||
g_free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pv_buffer_clear(PvBuffer **buf)
|
|
||||||
{
|
|
||||||
if (!buf || !*buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pv_buffer_free(*buf);
|
|
||||||
*buf = NULL;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 PvBuffer {
|
|
||||||
void *data;
|
|
||||||
gsize size; /* in bytes */
|
|
||||||
} PvBuffer;
|
|
||||||
|
|
||||||
PvBuffer *pv_buffer_alloc(gsize size);
|
|
||||||
/* After this call @data belongs to the PvBuffer and must no longer be modified
|
|
||||||
* by the caller.
|
|
||||||
*/
|
|
||||||
PvBuffer *pv_buffer_take(char *data, gsize size);
|
|
||||||
void pv_buffer_free(PvBuffer *buf);
|
|
||||||
void pv_buffer_clear(PvBuffer **buf);
|
|
||||||
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
|
||||||
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned);
|
|
||||||
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/asn1.h>
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/ec.h>
|
|
||||||
#include <openssl/ecdh.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/ossl_typ.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/safestack.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/x509v3.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
|
|
||||||
|
|
||||||
#define CRL_DOWNLOAD_TIMEOUT_MS 3000
|
|
||||||
#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */
|
|
||||||
|
|
||||||
enum PvCryptoMode {
|
|
||||||
PV_ENCRYPT,
|
|
||||||
PV_DECRYPT,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef GSList HostKeyList;
|
|
||||||
|
|
||||||
/* play nice with g_autoptr */
|
|
||||||
typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT;
|
|
||||||
typedef STACK_OF(X509) STACK_OF_X509;
|
|
||||||
typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL;
|
|
||||||
|
|
||||||
void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack);
|
|
||||||
void STACK_OF_X509_free(STACK_OF_X509 *stack);
|
|
||||||
void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
X509 *cert;
|
|
||||||
const gchar *path;
|
|
||||||
} x509_with_path;
|
|
||||||
|
|
||||||
x509_with_path *x509_with_path_new(X509 *cert, const gchar *path);
|
|
||||||
void x509_with_path_free(x509_with_path *cert);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
X509 *cert;
|
|
||||||
STACK_OF_X509_CRL *crls;
|
|
||||||
} x509_pair;
|
|
||||||
|
|
||||||
x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls);
|
|
||||||
void x509_pair_free(x509_pair *pair);
|
|
||||||
|
|
||||||
/* Register auto cleanup functions */
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
|
|
||||||
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(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free);
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free);
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free);
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_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 PvBuffer *key;
|
|
||||||
const PvBuffer *iv_or_tweak;
|
|
||||||
};
|
|
||||||
|
|
||||||
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
|
||||||
gint verify_flags, GError **err);
|
|
||||||
void pv_crypto_init(void);
|
|
||||||
void pv_crypto_cleanup(void);
|
|
||||||
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
|
||||||
gint verify_flags, int level, GError **err);
|
|
||||||
X509 *load_cert_from_file(const char *path, GError **err);
|
|
||||||
X509_CRL *load_crl_from_file(const gchar *path, GError **err);
|
|
||||||
GSList *load_certificates(const gchar *const *cert_paths, GError **err);
|
|
||||||
STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list);
|
|
||||||
X509_STORE *store_setup(const gchar *root_ca_path,
|
|
||||||
const gchar * const *crl_paths,
|
|
||||||
GError **err);
|
|
||||||
int store_set_verify_param(X509_STORE *store, GError **err);
|
|
||||||
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
|
||||||
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
|
||||||
gint check_chain_parameters(const STACK_OF_X509 *chain, GError **err);
|
|
||||||
X509_NAME *c2b_name(const X509_NAME *name);
|
|
||||||
|
|
||||||
STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
|
|
||||||
STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert,
|
|
||||||
GError **err);
|
|
||||||
X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain,
|
|
||||||
GError **err);
|
|
||||||
gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err);
|
|
||||||
X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
|
|
||||||
void store_setup_crl_download(X509_STORE *st);
|
|
||||||
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
|
||||||
|
|
||||||
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
|
||||||
PvBuffer *generate_aes_key(guint size, GError **err);
|
|
||||||
PvBuffer *generate_aes_iv(guint size, GError **err);
|
|
||||||
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
|
||||||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
|
||||||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
|
||||||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
|
||||||
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
|
||||||
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err);
|
|
||||||
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
|
||||||
const struct cipher_parms *parms, PvBuffer *out,
|
|
||||||
PvBuffer *tag, GError **err);
|
|
||||||
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
|
||||||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
|
||||||
GError **err);
|
|
||||||
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
|
||||||
GError **err);
|
|
||||||
G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms,
|
|
||||||
const PvBuffer *in, GError **err);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Libcurl utils
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2020
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <glib/gtypes.h>
|
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
#include "lib/zt_common.h"
|
|
||||||
#include "pv/pv_error.h"
|
|
||||||
|
|
||||||
#include "curl.h"
|
|
||||||
|
|
||||||
struct UserData {
|
|
||||||
GByteArray *buffer;
|
|
||||||
guint max_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
|
|
||||||
{
|
|
||||||
g_assert(userdata);
|
|
||||||
struct UserData *data = (struct UserData *)userdata;
|
|
||||||
GByteArray *buffer = data->buffer;
|
|
||||||
guint64 actual_size;
|
|
||||||
size_t err;
|
|
||||||
|
|
||||||
g_assert(buffer);
|
|
||||||
|
|
||||||
if (!g_uint64_checked_mul(&actual_size, size, nmemb))
|
|
||||||
g_abort();
|
|
||||||
|
|
||||||
/* Signal an error condition by returning a amount that differs
|
|
||||||
* from the amount passed to the callback. This results in a
|
|
||||||
* CURLE_WRITE_ERROR.
|
|
||||||
*/
|
|
||||||
err = actual_size + 1;
|
|
||||||
|
|
||||||
if (actual_size > G_MAXUINT)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size);
|
|
||||||
if (data->buffer->len > data->max_size)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return actual_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
gint curl_init(void)
|
|
||||||
{
|
|
||||||
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void curl_cleanup(void)
|
|
||||||
{
|
|
||||||
curl_global_cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
|
|
||||||
GError **err)
|
|
||||||
{
|
|
||||||
g_autoptr(GByteArray) ret = NULL;
|
|
||||||
g_autoptr(CURL) handle = NULL;
|
|
||||||
g_autofree gchar *agent = NULL;
|
|
||||||
struct UserData userdata;
|
|
||||||
CURLcode rc;
|
|
||||||
|
|
||||||
/* set up curl session */
|
|
||||||
handle = curl_easy_init();
|
|
||||||
if (!handle)
|
|
||||||
g_abort();
|
|
||||||
|
|
||||||
/* follow redirection */
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING);
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
ret = g_byte_array_new();
|
|
||||||
userdata.buffer = ret;
|
|
||||||
userdata.max_size = max_size;
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
rc = curl_easy_setopt(handle, CURLOPT_URL, url);
|
|
||||||
if (rc != CURLE_OK)
|
|
||||||
goto curl_err;
|
|
||||||
|
|
||||||
rc = curl_easy_perform(handle);
|
|
||||||
if (rc != CURLE_OK) {
|
|
||||||
g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED,
|
|
||||||
_("download failed: %s"), curl_easy_strerror(rc));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_steal_pointer(&ret);
|
|
||||||
curl_err:
|
|
||||||
g_set_error(err, PV_ERROR,
|
|
||||||
PV_ERROR_CURL_INIT_FAILED,
|
|
||||||
_("cURL initialization failed: %s"),
|
|
||||||
curl_easy_strerror(rc));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Libcurl utils
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2020
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PV_UTILS_LIBCURL_H
|
|
||||||
#define PV_UTILS_LIBCURL_H
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup)
|
|
||||||
|
|
||||||
GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
|
|
||||||
GError **err);
|
|
||||||
gint curl_init(void);
|
|
||||||
void curl_cleanup(void);
|
|
||||||
|
|
||||||
#endif /* PV_UTILS_LIBCURL_H */
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 PvBuffer *buf, uint64_t offset,
|
|
||||||
GError **err)
|
|
||||||
{
|
|
||||||
if (file_seek(o, offset, err) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (pv_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;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 PvBuffer *buf, uint64_t offset,
|
|
||||||
GError **err);
|
|
||||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
|
||||||
GError **err);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* OpenSSL compatibility utils
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2021
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PV_UTILS_OPENSSL_COMPAT_H
|
|
||||||
#define PV_UTILS_OPENSSL_COMPAT_H
|
|
||||||
|
|
||||||
#include <openssl/opensslv.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/x509_vfy.h>
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
|
||||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
|
||||||
X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx))
|
|
||||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
|
||||||
X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm))
|
|
||||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
|
||||||
X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb))
|
|
||||||
#else
|
|
||||||
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
|
||||||
X509_STORE_CTX_get_current_cert(ctx)
|
|
||||||
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
|
||||||
X509_STORE_CTX_get1_crls(ctx, nm)
|
|
||||||
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
|
||||||
X509_STORE_set_lookup_crls(st, cb)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
#define DBFS_WAIT_TIME_US 10000
|
#define DBFS_WAIT_TIME_US 10000
|
||||||
|
|
||||||
extern int dg_debugfs_init(int exit_on_err);
|
int dg_debugfs_init(int exit_on_err);
|
||||||
extern int dg_debugfs_vm_init(void);
|
int dg_debugfs_vm_init(void);
|
||||||
extern int dg_debugfs_lpar_init(void);
|
int dg_debugfs_lpar_init(void);
|
||||||
extern int dg_debugfs_open(const char *file);
|
int dg_debugfs_open(const char *file);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* z/VM diag 0C prototypes
|
* z/VM diag 0C prototypes
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#define CPU_TYPE_LEN 16
|
#define CPU_TYPE_LEN 16
|
||||||
#define DEBUGFS_FILE "diag_204"
|
#define DEBUGFS_FILE "diag_204"
|
||||||
|
|
||||||
|
static struct sd_sys *l_cur_lpar;
|
||||||
static u64 l_update_time_us;
|
static u64 l_update_time_us;
|
||||||
static long l_204_buf_size;
|
static long l_204_buf_size;
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ static long l_204_buf_size;
|
|||||||
struct l_x_info_blk_hdr {
|
struct l_x_info_blk_hdr {
|
||||||
u8 npar;
|
u8 npar;
|
||||||
u8 flags;
|
u8 flags;
|
||||||
u8 reserved1[6];
|
u8 reserved1[4];
|
||||||
|
u16 this_part;
|
||||||
u64 curtod1;
|
u64 curtod1;
|
||||||
u64 curtod2;
|
u64 curtod2;
|
||||||
u8 reserved[40];
|
u8 reserved[40];
|
||||||
@@ -158,6 +160,21 @@ static void *l_sd_sys_fill(struct sd_sys *lpar, struct l_x_sys_hdr *sys_hdr)
|
|||||||
return cpu_info;
|
return cpu_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the time delta between samples using current partition's
|
||||||
|
* online time.
|
||||||
|
*/
|
||||||
|
static u64 l_sd_cur_lpar_delta_time_get(void)
|
||||||
|
{
|
||||||
|
struct sd_cpu *cpu;
|
||||||
|
|
||||||
|
cpu = sd_cpu_get(l_cur_lpar, "0");
|
||||||
|
if (!cpu->d_prev || !cpu->d_cur)
|
||||||
|
return 0;
|
||||||
|
return l_sub_64(cpu->d_cur->online_time_us,
|
||||||
|
cpu->d_prev->online_time_us);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill one physical CPU with data
|
* Fill one physical CPU with data
|
||||||
*/
|
*/
|
||||||
@@ -241,9 +258,9 @@ static void l_read_debugfs(struct l_debugfs_d204_hdr **hdr,
|
|||||||
*/
|
*/
|
||||||
static void l_sd_sys_root_fill(struct sd_sys *sys)
|
static void l_sd_sys_root_fill(struct sd_sys *sys)
|
||||||
{
|
{
|
||||||
|
struct l_x_sys_hdr *sys_hdr, *this_part;
|
||||||
struct l_x_info_blk_hdr *time_hdr;
|
struct l_x_info_blk_hdr *time_hdr;
|
||||||
struct l_debugfs_d204_hdr *hdr;
|
struct l_debugfs_d204_hdr *hdr;
|
||||||
struct l_x_sys_hdr *sys_hdr;
|
|
||||||
struct sd_sys *lpar;
|
struct sd_sys *lpar;
|
||||||
char lpar_id[10];
|
char lpar_id[10];
|
||||||
int i;
|
int i;
|
||||||
@@ -262,18 +279,23 @@ static void l_sd_sys_root_fill(struct sd_sys *sys)
|
|||||||
usleep(DBFS_WAIT_TIME_US);
|
usleep(DBFS_WAIT_TIME_US);
|
||||||
} while (1);
|
} while (1);
|
||||||
sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr);
|
sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr);
|
||||||
|
this_part = ((void *)time_hdr) + time_hdr->this_part;
|
||||||
for (i = 0; i < time_hdr->npar; i++) {
|
for (i = 0; i < time_hdr->npar; i++) {
|
||||||
l_sys_hdr__sys_name(sys_hdr, lpar_id);
|
l_sys_hdr__sys_name(sys_hdr, lpar_id);
|
||||||
lpar = sd_sys_get(sys, lpar_id);
|
lpar = sd_sys_get(sys, lpar_id);
|
||||||
if (!lpar)
|
if (!lpar)
|
||||||
lpar = sd_sys_new(sys, lpar_id);
|
lpar = sd_sys_new(sys, lpar_id);
|
||||||
|
if (sys_hdr == this_part)
|
||||||
|
l_cur_lpar = lpar;
|
||||||
lpar->threads_per_core = l_thread_cnt(sys_hdr);
|
lpar->threads_per_core = l_thread_cnt(sys_hdr);
|
||||||
sys_hdr = l_sd_sys_fill(lpar, sys_hdr);
|
sys_hdr = l_sd_sys_fill(lpar, sys_hdr);
|
||||||
sd_sys_commit(lpar);
|
sd_sys_commit(lpar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_hdr->flags & LPAR_PHYS_FLG)
|
if (time_hdr->flags & LPAR_PHYS_FLG) {
|
||||||
l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr);
|
l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr);
|
||||||
|
sd_phys_delta_time_us_set(sys, l_sd_cur_lpar_delta_time_get());
|
||||||
|
}
|
||||||
ht_free(hdr);
|
ht_free(hdr);
|
||||||
sd_sys_commit(sys);
|
sd_sys_commit(sys);
|
||||||
}
|
}
|
||||||
@@ -290,6 +312,16 @@ static void l_sd_update(void)
|
|||||||
sd_sys_update_end(root, l_update_time_us);
|
sd_sys_update_end(root, l_update_time_us);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supported physical information items
|
||||||
|
*/
|
||||||
|
static struct sd_sys_item *l_phys_item_vec[] = {
|
||||||
|
&sd_sys_item_core_cnt,
|
||||||
|
&sd_sys_item_phys_mgm_diff,
|
||||||
|
&sd_sys_item_mgm,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Supported system items
|
* Supported system items
|
||||||
*/
|
*/
|
||||||
@@ -364,6 +396,7 @@ static struct sd_cpu_type *l_cpu_type_vec[] = {
|
|||||||
static struct sd_dg l_sd_dg = {
|
static struct sd_dg l_sd_dg = {
|
||||||
.update_sys = l_sd_update,
|
.update_sys = l_sd_update,
|
||||||
.cpu_type_vec = l_cpu_type_vec,
|
.cpu_type_vec = l_cpu_type_vec,
|
||||||
|
.phys_item_vec = l_phys_item_vec,
|
||||||
.sys_item_vec = l_sys_item_vec,
|
.sys_item_vec = l_sys_item_vec,
|
||||||
.sys_item_enable_vec = l_sys_item_enable_vec,
|
.sys_item_enable_vec = l_sys_item_enable_vec,
|
||||||
.cpu_item_vec = l_cpu_item_vec,
|
.cpu_item_vec = l_cpu_item_vec,
|
||||||
@@ -383,6 +416,6 @@ int dg_debugfs_lpar_init(void)
|
|||||||
return fh;
|
return fh;
|
||||||
else
|
else
|
||||||
close(fh);
|
close(fh);
|
||||||
sd_dg_register(&l_sd_dg, 1);
|
sd_dg_register(&l_sd_dg, 1, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,6 +358,6 @@ int dg_debugfs_vm_init(void)
|
|||||||
close(fh);
|
close(fh);
|
||||||
l_2fc_buf_size = sizeof(struct l_debugfs_d2fc_hdr);
|
l_2fc_buf_size = sizeof(struct l_debugfs_d2fc_hdr);
|
||||||
l_guest_name_init();
|
l_guest_name_init();
|
||||||
sd_dg_register(&dg_debugfs_vm_dg, 0);
|
sd_dg_register(&dg_debugfs_vm_dg, 0, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#include <mntent.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -25,6 +24,13 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef _PATH_MOUNTED
|
||||||
|
#define _PATH_MOUNTED "/etc/mtab"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
#include "hyptop.h"
|
#include "hyptop.h"
|
||||||
#include "sd.h"
|
#include "sd.h"
|
||||||
@@ -354,17 +360,6 @@ out:
|
|||||||
ht_reverse_off();
|
ht_reverse_off();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert string to uppercase
|
|
||||||
*/
|
|
||||||
void ht_str_to_upper(char *str)
|
|
||||||
{
|
|
||||||
while (*str) {
|
|
||||||
*str = toupper(*str);
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert ext TOD to microseconds
|
* Convert ext TOD to microseconds
|
||||||
*/
|
*/
|
||||||
@@ -412,3 +407,40 @@ s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_co
|
|||||||
|
|
||||||
return smt_us;
|
return smt_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add two new key value pairs containing the current time as UNIX epoch and formatted string to a
|
||||||
|
* structured output object.
|
||||||
|
*/
|
||||||
|
void ht_fmt_time(void)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
struct tm *tm;
|
||||||
|
char str[30];
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
tm = localtime(&tv.tv_sec);
|
||||||
|
if (!tm)
|
||||||
|
return;
|
||||||
|
util_fmt_pair(FMT_PERSIST, "time_epoch", "%lld", mktime(tm));
|
||||||
|
strftime(str, sizeof(str), "%F %T%z", tm);
|
||||||
|
util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "time", "%s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a new object for available CPU types to a structured output object.
|
||||||
|
*/
|
||||||
|
void ht_fmt_cpu_types(void)
|
||||||
|
{
|
||||||
|
struct sd_cpu_type *cpu_type;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
util_fmt_obj_start(FMT_DEFAULT, "cputypes");
|
||||||
|
sd_cpu_type_iterate(cpu_type, i) {
|
||||||
|
char *cpu_type_str = sd_cpu_type_id(cpu_type);
|
||||||
|
util_str_tolower(cpu_type_str);
|
||||||
|
util_fmt_pair(FMT_PERSIST, cpu_type_str, "%i",
|
||||||
|
sd_cpu_type_cpu_cnt(cpu_type));
|
||||||
|
}
|
||||||
|
util_fmt_obj_end(); /* cpus{} */
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_time.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#define G0(x) MAX(0, (s64) (x))
|
#define G0(x) MAX(0, (s64) (x))
|
||||||
@@ -25,23 +26,23 @@
|
|||||||
/*
|
/*
|
||||||
* Helper Prototypes
|
* Helper Prototypes
|
||||||
*/
|
*/
|
||||||
extern void hyptop_helper_init(void);
|
void hyptop_helper_init(void);
|
||||||
extern char *ht_strstrip(char *str);
|
char *ht_strstrip(char *str);
|
||||||
extern char *ht_strdup(const char *str);
|
char *ht_strdup(const char *str);
|
||||||
extern void ht_print_head(const char *sys);
|
void ht_print_head(const char *sys);
|
||||||
extern void ht_print_help_icon(void);
|
void ht_print_help_icon(void);
|
||||||
extern void ht_ebcdic_to_ascii(char *in, char *out, size_t len);
|
void ht_ebcdic_to_ascii(char *in, char *out, size_t len);
|
||||||
extern char *ht_mount_point_get(const char *fs_type);
|
char *ht_mount_point_get(const char *fs_type);
|
||||||
extern u64 ht_ext_tod_2_us(void *tod_ext);
|
u64 ht_ext_tod_2_us(void *tod_ext);
|
||||||
extern void ht_print_time(void);
|
void ht_print_time(void);
|
||||||
extern s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_core);
|
s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_core);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Memory alloc functions
|
* Memory alloc functions
|
||||||
*/
|
*/
|
||||||
extern void *ht_zalloc(size_t size);
|
void *ht_zalloc(size_t size);
|
||||||
extern void *ht_alloc(size_t size);
|
void *ht_alloc(size_t size);
|
||||||
extern void *ht_realloc(void *ptr, size_t size);
|
void *ht_realloc(void *ptr, size_t size);
|
||||||
static inline void ht_free(void *ptr)
|
static inline void ht_free(void *ptr)
|
||||||
{
|
{
|
||||||
free(ptr);
|
free(ptr);
|
||||||
@@ -66,6 +67,12 @@ void ht_print_scroll_bar(int row_cnt, int row_start, int row_bar_start,
|
|||||||
int row_bar_bottom, int can_scroll_up,
|
int row_bar_bottom, int can_scroll_up,
|
||||||
int can_scroll_down, int with_boder);
|
int can_scroll_down, int with_boder);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* util_fmt helper functions
|
||||||
|
*/
|
||||||
|
void ht_fmt_time(void);
|
||||||
|
void ht_fmt_cpu_types(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error Macros
|
* Error Macros
|
||||||
*/
|
*/
|
||||||
|
|||||||
258
hyptop/hyptop.8
258
hyptop/hyptop.8
@@ -62,19 +62,71 @@ shown.
|
|||||||
Select sort field for current window. To reverse the sort order, specify the
|
Select sort field for current window. To reverse the sort order, specify the
|
||||||
option twice. See FIELDS below for definitions.
|
option twice. See FIELDS below for definitions.
|
||||||
.TP
|
.TP
|
||||||
.BR "\-t <TYPE>,..." " or " "\-\-cpu_types=<TYPE>,..."
|
.BR "\-t <TYPE>,..." " or " "\-\-cpu-types=<TYPE>,..."
|
||||||
Select CPU types that are used for CPU time calculations. See CPU TYPES
|
Select CPU types that are used for CPU time calculations. See CPU TYPES
|
||||||
below for definitions.
|
below for definitions.
|
||||||
.TP
|
.TP
|
||||||
.BR "\-b" " or " "\-\-batch_mode"
|
.BR "\-b" " or " "\-\-batch-mode"
|
||||||
Use batch mode (no curses). This can be useful for sending output from hyptop
|
Use batch mode (no curses). This can be useful for sending output from hyptop
|
||||||
to another program, a file, or a line mode terminal.
|
to another program, a file, or a line mode terminal.
|
||||||
In this mode no user input is accepted.
|
In this mode no user input is accepted.
|
||||||
.TP
|
.TP
|
||||||
|
.BR "\-\-format=<FORMAT>"
|
||||||
|
.RS
|
||||||
|
Use this option to show output in a machine-readable format. FORMAT can be
|
||||||
|
one of:
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
.B json:
|
||||||
|
Single JavaScript Object Notation (JSON) data structure
|
||||||
|
|
||||||
|
Data for all iterations is formatted as one JSON data structure in multiple
|
||||||
|
lines to make them more readable by humans.
|
||||||
|
.BR
|
||||||
|
|
||||||
|
See section "OUTPUT FORMAT" for more details.
|
||||||
|
.BR
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.B json\-seq:
|
||||||
|
Sequence of JSON data structures
|
||||||
|
|
||||||
|
Data for each iteration is formatted as a separate JSON data structure prefixed
|
||||||
|
with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line
|
||||||
|
Feed character (0x0a) in accordance with RFC7464.
|
||||||
|
.BR
|
||||||
|
|
||||||
|
See section "OUTPUT FORMAT" for more details.
|
||||||
|
.BR
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.B pairs:
|
||||||
|
Textual key=value pairs
|
||||||
|
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.B csv:
|
||||||
|
Comma-separated-value (CSV) list
|
||||||
|
.BR
|
||||||
|
|
||||||
|
All values are enclosed in double quotation marks and separated by commas. The
|
||||||
|
first line of output contains a list of headings. Subsequent lines each
|
||||||
|
represent data for one system in one iteration.
|
||||||
|
.PP
|
||||||
|
This option implies the "\-\-batch-mode" option.
|
||||||
|
.RE
|
||||||
|
.BR "\-\-all"
|
||||||
|
.RS
|
||||||
|
Use this option with the "--format" option to include fields without valid
|
||||||
|
values in the output. Fields without valid values are omitted from the output
|
||||||
|
by default. When you use the "--all" option, the invalid values are included
|
||||||
|
and shown as "null".
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
.BR "\-d <SECONDS>" " or " "\-\-delay=<SECONDS>"
|
.BR "\-d <SECONDS>" " or " "\-\-delay=<SECONDS>"
|
||||||
Specifies the delay between screen updates.
|
Specifies the delay between screen updates.
|
||||||
.TP
|
.TP
|
||||||
.BR "\-m <FACTOR>" " or " "\-\-smt_factor=<FACTOR>"
|
.BR "\-m <FACTOR>" " or " "\-\-smt-factor=<FACTOR>"
|
||||||
Specifies a workload dependent SMT speedup factor.
|
Specifies a workload dependent SMT speedup factor.
|
||||||
For IBM z15 servers, the default value is 1.3. If the workload benefits
|
For IBM z15 servers, the default value is 1.3. If the workload benefits
|
||||||
from SMT, you can specify a higher value. If the workload does not benefit
|
from SMT, you can specify a higher value. If the workload does not benefit
|
||||||
@@ -194,7 +246,7 @@ The following units are supported:
|
|||||||
|
|
||||||
.SH CPU TYPES
|
.SH CPU TYPES
|
||||||
Depending on the hypervisor different CPU types are supported. These CPU
|
Depending on the hypervisor different CPU types are supported. These CPU
|
||||||
types can be selected either interactively or with the "--cpu_types"
|
types can be selected either interactively or with the "--cpu-types"
|
||||||
command line option. The calculation of the CPU data only uses CPUs of
|
command line option. The calculation of the CPU data only uses CPUs of
|
||||||
the specified types.
|
the specified types.
|
||||||
|
|
||||||
@@ -216,6 +268,196 @@ For the online time field, the value is the time during which at least
|
|||||||
one CPU has been operational.
|
one CPU has been operational.
|
||||||
Operating CPUs are shown with CPU identifier "0" and stopped CPUs with "1".
|
Operating CPUs are shown with CPU identifier "0" and stopped CPUs with "1".
|
||||||
|
|
||||||
|
.SH OUTPUT FORMATS
|
||||||
|
This section contains additional information for some of the supported
|
||||||
|
output formats.
|
||||||
|
|
||||||
|
.SS json
|
||||||
|
|
||||||
|
JSON output consists of a top-level object with the following properties
|
||||||
|
(key-value pairs):
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR meta :
|
||||||
|
Tool meta-data including API level, version, host name, and time of invocation
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR hyptop :
|
||||||
|
Hypervisor performance data
|
||||||
|
.PP
|
||||||
|
|
||||||
|
Performance data is stored as an array of iteration objects under the "hyptop"
|
||||||
|
property in the top-level object. Iteration objects contain the following properties:
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR iteration :
|
||||||
|
sequential count of the current iteration
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR "time" " and " "time_epoch" :
|
||||||
|
time when the iteration was recorded, in human-readable format and as Unix
|
||||||
|
epoch.
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR cputypes :
|
||||||
|
contains the number of CPUs hyptop detected, per CPU type. Possible properties
|
||||||
|
are
|
||||||
|
.BR "cp" ", " "ifl" " and " "un".
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR systems :
|
||||||
|
when the "sys_list" window is selected, contains an array of system objects
|
||||||
|
that contain the performance data per system. Each system object contains a
|
||||||
|
"system" property and properties for its performance data (see below).
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR cpus :
|
||||||
|
when the "sys" window is selected, contains an array of CPU objects
|
||||||
|
that contain the performance data per CPU of the selected system. Each CPU
|
||||||
|
object contains a "coreid" property and properties for its performance data
|
||||||
|
(see below).
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.BR summary :
|
||||||
|
contains aggregated performance data over all systems in the "systems" or
|
||||||
|
"cpus" array.
|
||||||
|
.PP
|
||||||
|
|
||||||
|
All performance data available for the platform (LPAR, z/VM) hyptop is executed
|
||||||
|
on is shown. Selecting specific fields with \-\-fields is not supported but it
|
||||||
|
is possible to change the units of fields. Properties for performance data are
|
||||||
|
labeled after their respective columns.
|
||||||
|
|
||||||
|
Example JSON output for a single iteration:
|
||||||
|
.br
|
||||||
|
|
||||||
|
.RS
|
||||||
|
{
|
||||||
|
.br
|
||||||
|
"meta": {
|
||||||
|
.br
|
||||||
|
"api_level": 1,
|
||||||
|
.br
|
||||||
|
"version": "2.35.0",
|
||||||
|
.br
|
||||||
|
"host": "mylpar.local",
|
||||||
|
.br
|
||||||
|
"time_epoch": 1730787834,
|
||||||
|
.br
|
||||||
|
"time": "2024-11-05 07:23:54+0100"
|
||||||
|
.br
|
||||||
|
},
|
||||||
|
.br
|
||||||
|
"hyptop": [
|
||||||
|
.br
|
||||||
|
{
|
||||||
|
.br
|
||||||
|
"iteration": 0,
|
||||||
|
.br
|
||||||
|
"time_epoch": 1730787834,
|
||||||
|
.br
|
||||||
|
"time": "2024-11-05 07:23:54+0100",
|
||||||
|
.br
|
||||||
|
"cputypes": {
|
||||||
|
.br
|
||||||
|
"ifl": 124,
|
||||||
|
.br
|
||||||
|
"cp": 1,
|
||||||
|
.br
|
||||||
|
"un": 0
|
||||||
|
.br
|
||||||
|
},
|
||||||
|
.br
|
||||||
|
"systems": [
|
||||||
|
.br
|
||||||
|
{
|
||||||
|
.br
|
||||||
|
"system": "MYLPAR",
|
||||||
|
.br
|
||||||
|
"#core": 30,
|
||||||
|
.br
|
||||||
|
"#the": 60,
|
||||||
|
.br
|
||||||
|
"core": 379.57,
|
||||||
|
.br
|
||||||
|
"the": 382.43,
|
||||||
|
.br
|
||||||
|
"smt": 294.73,
|
||||||
|
.br
|
||||||
|
"mgm": 2.09,
|
||||||
|
.br
|
||||||
|
"core+": "1503:13",
|
||||||
|
.br
|
||||||
|
"the+": "2060:45",
|
||||||
|
.br
|
||||||
|
"mgm+": "22:19",
|
||||||
|
.br
|
||||||
|
"online": "18:18:09"
|
||||||
|
.br
|
||||||
|
}
|
||||||
|
.br
|
||||||
|
],
|
||||||
|
.br
|
||||||
|
"summary": {
|
||||||
|
.br
|
||||||
|
"#core": 30,
|
||||||
|
.br
|
||||||
|
"#the": 60,
|
||||||
|
.br
|
||||||
|
"core": 379.57,
|
||||||
|
.br
|
||||||
|
"the": 382.43,
|
||||||
|
.br
|
||||||
|
"smt": 294.73,
|
||||||
|
.br
|
||||||
|
"mgm": 2.09,
|
||||||
|
.br
|
||||||
|
"core+": "1503:13",
|
||||||
|
.br
|
||||||
|
"the+": "2060:45",
|
||||||
|
.br
|
||||||
|
"mgm+": "22:19",
|
||||||
|
.br
|
||||||
|
"online": "18:18:09"
|
||||||
|
.br
|
||||||
|
}
|
||||||
|
.br
|
||||||
|
}
|
||||||
|
.br
|
||||||
|
]
|
||||||
|
.br
|
||||||
|
}
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SS json\-seq
|
||||||
|
|
||||||
|
The json\-seq output format is a variation of the JSON output format described
|
||||||
|
above with the following differences:
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
Output consists of a sequence of top-level JSON objects, each contained in
|
||||||
|
single line with no indentation
|
||||||
|
.br
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
Each top-level object is prefixed by an ASCII Record Separator character (0x1e)
|
||||||
|
and suffixed with an ASCII Line Feed character (0x0a) in accordance with
|
||||||
|
RFC7464
|
||||||
|
.br
|
||||||
|
.PP
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
The first object contains tool meta-data properties defined in the previous
|
||||||
|
section
|
||||||
|
.br
|
||||||
|
.PP
|
||||||
|
|
||||||
|
.IP \(bu 3
|
||||||
|
Subsequent objects each represent performance data for one iteration
|
||||||
|
.br
|
||||||
|
.PP
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
To start hyptop with the "sys_list" window in interactive mode, enter:
|
To start hyptop with the "sys_list" window in interactive mode, enter:
|
||||||
.br
|
.br
|
||||||
@@ -258,6 +500,14 @@ for CPU time calculation, enter:
|
|||||||
|
|
||||||
# hyptop -t ifl,cp
|
# hyptop -t ifl,cp
|
||||||
|
|
||||||
|
.br
|
||||||
|
To show a single iteration of performance data as a formatted JSON object for
|
||||||
|
system "MYLPAR" and with fields CPU time, thread time, management time and
|
||||||
|
online time in microseconds, enter:
|
||||||
|
.br
|
||||||
|
|
||||||
|
# hyptop -f C:us,E:us,M:us,o:us --format json -n 1 -s MYLPAR
|
||||||
|
|
||||||
.SH ENVIRONMENT
|
.SH ENVIRONMENT
|
||||||
.TP
|
.TP
|
||||||
.B TERM
|
.B TERM
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
|
||||||
#include "dg_debugfs.h"
|
#include "dg_debugfs.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
#include "hyptop.h"
|
#include "hyptop.h"
|
||||||
@@ -110,7 +112,7 @@ static enum hyptop_win_action l_sleep(time_t time_s, long time_us)
|
|||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
|
||||||
ts.tv_sec = time_s;
|
ts.tv_sec = time_s;
|
||||||
ts.tv_nsec = time_us * 1000;
|
ts.tv_nsec = util_usecs_to_nsecs(time_us);
|
||||||
|
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
return WIN_KEEP;
|
return WIN_KEEP;
|
||||||
@@ -218,6 +220,24 @@ static void l_term_check(void)
|
|||||||
" try \"--batch_mode\"\n");
|
" try \"--batch_mode\"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init util_fmt if --format is specified on the command line.
|
||||||
|
*/
|
||||||
|
static void l_fmt_init(void)
|
||||||
|
{
|
||||||
|
unsigned int flags = FMT_WARN;
|
||||||
|
|
||||||
|
if (!g.o.format_specified)
|
||||||
|
return;
|
||||||
|
if (g.o.format == FMT_CSV)
|
||||||
|
flags |= FMT_QUOTEALL;
|
||||||
|
if (g.o.format == FMT_CSV || g.o.format_all)
|
||||||
|
flags |= FMT_KEEPINVAL;
|
||||||
|
if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ)
|
||||||
|
flags |= FMT_HANDLEINT;
|
||||||
|
util_fmt_init(stdout, g.o.format, flags, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init curses
|
* Init curses
|
||||||
*/
|
*/
|
||||||
@@ -296,7 +316,6 @@ static void l_update_term_curses(void)
|
|||||||
static void l_update_term_batch(void)
|
static void l_update_term_batch(void)
|
||||||
{
|
{
|
||||||
g.w.cur->update_term(g.w.cur);
|
g.w.cur->update_term(g.w.cur);
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -360,6 +379,7 @@ void __noreturn hyptop_exit(int rc)
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
opts_parse(argc, argv);
|
opts_parse(argc, argv);
|
||||||
|
l_fmt_init();
|
||||||
hyptop_helper_init();
|
hyptop_helper_init();
|
||||||
sd_init();
|
sd_init();
|
||||||
l_dg_init();
|
l_dg_init();
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
#include "nav_desc.h"
|
#include "nav_desc.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
@@ -52,6 +54,9 @@ struct hyptop_win_opts {
|
|||||||
struct hyptop_opts {
|
struct hyptop_opts {
|
||||||
unsigned int win_specified;
|
unsigned int win_specified;
|
||||||
unsigned int batch_mode_specified;
|
unsigned int batch_mode_specified;
|
||||||
|
unsigned int format_specified;
|
||||||
|
unsigned int format_all;
|
||||||
|
enum util_fmt_t format;
|
||||||
unsigned int iterations_specified;
|
unsigned int iterations_specified;
|
||||||
unsigned int iterations;
|
unsigned int iterations;
|
||||||
unsigned int iterations_act;
|
unsigned int iterations_act;
|
||||||
@@ -179,10 +184,10 @@ enum hyptop_win_action {
|
|||||||
WIN_KEEP,
|
WIN_KEEP,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum hyptop_win_action hyptop_process_input_timeout(void);
|
enum hyptop_win_action hyptop_process_input_timeout(void);
|
||||||
extern enum hyptop_win_action hyptop_process_input(void);
|
enum hyptop_win_action hyptop_process_input(void);
|
||||||
extern enum hyptop_win_action win_switch(struct hyptop_win *w);
|
enum hyptop_win_action win_switch(struct hyptop_win *w);
|
||||||
extern enum hyptop_win_action win_back(void);
|
enum hyptop_win_action win_back(void);
|
||||||
|
|
||||||
struct hyptop_win;
|
struct hyptop_win;
|
||||||
struct hyptop_win {
|
struct hyptop_win {
|
||||||
@@ -201,25 +206,25 @@ struct hyptop_win {
|
|||||||
* Window sys_list
|
* Window sys_list
|
||||||
*/
|
*/
|
||||||
extern struct hyptop_win win_sys_list;
|
extern struct hyptop_win win_sys_list;
|
||||||
extern void win_sys_list_init(void);
|
void win_sys_list_init(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Window sys
|
* Window sys
|
||||||
*/
|
*/
|
||||||
extern struct hyptop_win win_sys;
|
extern struct hyptop_win win_sys;
|
||||||
extern void win_sys_set(const char *sys_id);
|
void win_sys_set(const char *sys_id);
|
||||||
extern void win_sys_init(void);
|
void win_sys_init(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Window cpu_types
|
* Window cpu_types
|
||||||
*/
|
*/
|
||||||
extern void win_cpu_types_init(void);
|
void win_cpu_types_init(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Misc functions
|
* Misc functions
|
||||||
*/
|
*/
|
||||||
extern void hyptop_update_term(void);
|
void hyptop_update_term(void);
|
||||||
extern void __noreturn hyptop_exit(int rc);
|
void __noreturn hyptop_exit(int rc);
|
||||||
extern void hyptop_text_mode(void);
|
void hyptop_text_mode(void);
|
||||||
|
|
||||||
#endif /* HYPTOP_H */
|
#endif /* HYPTOP_H */
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "lib/util_fmt.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#include "getopt.h"
|
#include "getopt.h"
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
#include "hyptop.h"
|
#include "hyptop.h"
|
||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
#include "sd.h"
|
#include "sd.h"
|
||||||
|
#include "table.h"
|
||||||
|
|
||||||
static const char l_copyright_str[] = "Copyright IBM Corp. 2010, 2017";
|
static const char l_copyright_str[] = "Copyright IBM Corp. 2010, 2017";
|
||||||
|
|
||||||
@@ -36,12 +39,29 @@ static char HELP_TEXT[] =
|
|||||||
"-s, --sys SYSTEM[,..] Systems for current window\n"
|
"-s, --sys SYSTEM[,..] Systems for current window\n"
|
||||||
"-f, --fields LETTER[:UNIT][,..] Fields and units for current window\n"
|
"-f, --fields LETTER[:UNIT][,..] Fields and units for current window\n"
|
||||||
"-S, --sort LETTER Sort field for current window\n"
|
"-S, --sort LETTER Sort field for current window\n"
|
||||||
"-t, --cpu_types TYPE[,..] CPU types used for time calculations\n"
|
"-t, --cpu-types TYPE[,..] CPU types used for time calculations\n"
|
||||||
"-b, --batch_mode Use batch mode (no curses)\n"
|
"-b, --batch-mode Use batch mode (no curses)\n"
|
||||||
|
" --format FORMAT Output format (" FMT_TYPE_NAMES "), implies -b\n"
|
||||||
|
" --all Include omitted fields (with values of null)\n"
|
||||||
|
" in the output of --format.\n"
|
||||||
"-d, --delay SECONDS Delay time between screen updates\n"
|
"-d, --delay SECONDS Delay time between screen updates\n"
|
||||||
"-m, --smt_factor FACTOR Machine generation dependent SMT speedup factor.\n"
|
"-m, --smt-factor FACTOR Machine generation dependent SMT speedup factor.\n"
|
||||||
"-n, --iterations NUMBER Number of iterations before ending\n";
|
"-n, --iterations NUMBER Number of iterations before ending\n";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options with long-name only
|
||||||
|
*/
|
||||||
|
#define OPT_FORMAT 256 /* --format */
|
||||||
|
#define OPT_FORMAT_ALL 261 /* --all*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options with underscore to keep compatibility
|
||||||
|
*/
|
||||||
|
#define OPT_BATCH_MODE 257 /* --batch_mode */
|
||||||
|
#define OPT_SORT_FIELD 258 /* --sort | --sort_field */
|
||||||
|
#define OPT_CPU_TYPES 259 /* --cpu_types */
|
||||||
|
#define OPT_SMT_FACTOR 260 /* --smt_factor */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize default settings
|
* Initialize default settings
|
||||||
*/
|
*/
|
||||||
@@ -287,6 +307,32 @@ static void l_batch_mode_set(void)
|
|||||||
g.o.batch_mode_specified = 1;
|
g.o.batch_mode_specified = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the "--format" option
|
||||||
|
*/
|
||||||
|
static void l_format_set(const char *str)
|
||||||
|
{
|
||||||
|
enum util_fmt_t fmt;
|
||||||
|
|
||||||
|
if (!util_fmt_name_to_type(str, &fmt)) {
|
||||||
|
ERR_EXIT("Unknown format '%s', supported formats: "
|
||||||
|
FMT_TYPE_NAMES "\n", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
l_batch_mode_set();
|
||||||
|
g.o.format_specified = 1;
|
||||||
|
g.o.format = fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the "--all" option to display omitted null values
|
||||||
|
* while using "--format"
|
||||||
|
*/
|
||||||
|
static void l_format_all_set(void)
|
||||||
|
{
|
||||||
|
g.o.format_all = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make option consisteny checks at end of command line parsing
|
* Make option consisteny checks at end of command line parsing
|
||||||
*/
|
*/
|
||||||
@@ -294,6 +340,8 @@ static void l_parse_finish(void)
|
|||||||
{
|
{
|
||||||
if (g.o.iterations_specified && g.o.iterations == 0)
|
if (g.o.iterations_specified && g.o.iterations == 0)
|
||||||
hyptop_exit(0);
|
hyptop_exit(0);
|
||||||
|
if (!g.o.format_specified && g.o.format_all)
|
||||||
|
ERR_EXIT("The --all option requires the -–format option\n");
|
||||||
if (g.o.cur_win != &win_sys)
|
if (g.o.cur_win != &win_sys)
|
||||||
return;
|
return;
|
||||||
if (!win_sys.opts.sys.specified)
|
if (!win_sys.opts.sys.specified)
|
||||||
@@ -313,15 +361,21 @@ void opts_parse(int argc, char *argv[])
|
|||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{ "version", no_argument, NULL, 'v'},
|
{ "version", no_argument, NULL, 'v'},
|
||||||
{ "help", no_argument, NULL, 'h'},
|
{ "help", no_argument, NULL, 'h'},
|
||||||
{ "batch_mode", no_argument, NULL, 'b'},
|
{ "batch-mode", no_argument, NULL, 'b'},
|
||||||
|
{ "batch_mode", no_argument, NULL, OPT_BATCH_MODE},
|
||||||
|
{ "all", no_argument, NULL, OPT_FORMAT_ALL },
|
||||||
{ "delay", required_argument, NULL, 'd'},
|
{ "delay", required_argument, NULL, 'd'},
|
||||||
{ "smt_factor", required_argument, NULL, 'm'},
|
{ "smt-factor", required_argument, NULL, 'm'},
|
||||||
|
{ "smt_factor", required_argument, NULL, OPT_SMT_FACTOR},
|
||||||
{ "window", required_argument, NULL, 'w'},
|
{ "window", required_argument, NULL, 'w'},
|
||||||
{ "sys", required_argument, NULL, 's'},
|
{ "sys", required_argument, NULL, 's'},
|
||||||
{ "iterations", required_argument, NULL, 'n'},
|
{ "iterations", required_argument, NULL, 'n'},
|
||||||
{ "fields", required_argument, NULL, 'f'},
|
{ "fields", required_argument, NULL, 'f'},
|
||||||
{ "sort_field", required_argument, NULL, 'S'},
|
{ "sort-field", required_argument, NULL, 'S'},
|
||||||
{ "cpu_types", required_argument, NULL, 't'},
|
{ "sort_field", required_argument, NULL, OPT_SORT_FIELD},
|
||||||
|
{ "cpu-types", required_argument, NULL, 't'},
|
||||||
|
{ "cpu_types", required_argument, NULL, OPT_CPU_TYPES},
|
||||||
|
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
static const char option_string[] = "vhbd:m:w:s:n:f:t:S:";
|
static const char option_string[] = "vhbd:m:w:s:n:f:t:S:";
|
||||||
@@ -339,12 +393,14 @@ void opts_parse(int argc, char *argv[])
|
|||||||
case 'h':
|
case 'h':
|
||||||
l_usage();
|
l_usage();
|
||||||
hyptop_exit(0);
|
hyptop_exit(0);
|
||||||
|
case OPT_BATCH_MODE:
|
||||||
case 'b':
|
case 'b':
|
||||||
l_batch_mode_set();
|
l_batch_mode_set();
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
l_delay_set(optarg);
|
l_delay_set(optarg);
|
||||||
break;
|
break;
|
||||||
|
case OPT_SMT_FACTOR:
|
||||||
case 'm':
|
case 'm':
|
||||||
l_factor_set(optarg);
|
l_factor_set(optarg);
|
||||||
break;
|
break;
|
||||||
@@ -357,15 +413,23 @@ void opts_parse(int argc, char *argv[])
|
|||||||
case 'n':
|
case 'n':
|
||||||
l_iterations_set(optarg);
|
l_iterations_set(optarg);
|
||||||
break;
|
break;
|
||||||
|
case OPT_CPU_TYPES:
|
||||||
case 't':
|
case 't':
|
||||||
l_cpu_types_set(optarg);
|
l_cpu_types_set(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
l_fields_set(optarg);
|
l_fields_set(optarg);
|
||||||
break;
|
break;
|
||||||
|
case OPT_SORT_FIELD:
|
||||||
case 'S':
|
case 'S':
|
||||||
l_sort_field_set(optarg);
|
l_sort_field_set(optarg);
|
||||||
break;
|
break;
|
||||||
|
case OPT_FORMAT:
|
||||||
|
l_format_set(optarg);
|
||||||
|
break;
|
||||||
|
case OPT_FORMAT_ALL:
|
||||||
|
l_format_all_set();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
l_std_usage_exit();
|
l_std_usage_exit();
|
||||||
}
|
}
|
||||||
@@ -404,7 +468,7 @@ static void l_verify_systems(struct hyptop_win *win)
|
|||||||
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i]))
|
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i]))
|
||||||
continue;
|
continue;
|
||||||
sys_name = ht_strdup(win->opts.sys.vec[i]);
|
sys_name = ht_strdup(win->opts.sys.vec[i]);
|
||||||
ht_str_to_upper(win->opts.sys.vec[i]);
|
util_str_toupper(win->opts.sys.vec[i]);
|
||||||
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) {
|
if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) {
|
||||||
ht_free(sys_name);
|
ht_free(sys_name);
|
||||||
continue;
|
continue;
|
||||||
@@ -431,11 +495,11 @@ void opts_iterations_next(void)
|
|||||||
{
|
{
|
||||||
if (g.o.iterations_specified) {
|
if (g.o.iterations_specified) {
|
||||||
g.o.iterations_act++;
|
g.o.iterations_act++;
|
||||||
if (g.o.iterations_act >= g.o.iterations)
|
if (g.o.iterations_act >= g.o.iterations) {
|
||||||
|
if (g.o.format_specified)
|
||||||
|
table_fmt_end();
|
||||||
hyptop_exit(0);
|
hyptop_exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (g.o.batch_mode_specified)
|
|
||||||
printf("---------------------------------------------------"
|
|
||||||
"----------------------------\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user