mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -137,4 +137,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
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ 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
|
||||||
@@ -63,6 +64,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- 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,6 +72,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Jens Remus
|
- Jens Remus
|
||||||
- Jochen Roehrig
|
- Jochen Roehrig
|
||||||
- Joern Siglen
|
- Joern Siglen
|
||||||
|
- Jörn Siglen
|
||||||
- Juergen Christ
|
- Juergen Christ
|
||||||
- Julian Wiedmann
|
- Julian Wiedmann
|
||||||
- Karsten Graul
|
- Karsten Graul
|
||||||
@@ -97,6 +100,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- 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
|
||||||
@@ -112,6 +116,8 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Sebastian Ott
|
- Sebastian Ott
|
||||||
- Sertonix
|
- Sertonix
|
||||||
- Seshagiri N. Ippili
|
- Seshagiri N. Ippili
|
||||||
|
- Shalini Chellathurai Saroja
|
||||||
|
- Siglen
|
||||||
- Simon Sturm
|
- Simon Sturm
|
||||||
- Stefan Bader
|
- Stefan Bader
|
||||||
- Stefan Haberland
|
- Stefan Haberland
|
||||||
@@ -121,6 +127,7 @@ 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
|
||||||
|
|||||||
@@ -1,6 +1,68 @@
|
|||||||
Release history for s390-tools (MIT version)
|
Release history for s390-tools (MIT version)
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
* __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)__
|
* __v2.37.0 (2025-02-07)__
|
||||||
|
|
||||||
For Linux kernel version: 6.13
|
For Linux kernel version: 6.13
|
||||||
@@ -49,6 +111,14 @@ Release history for s390-tools (MIT version)
|
|||||||
- chpstat: Add support for full CMCB
|
- chpstat: Add support for full CMCB
|
||||||
- chpstat: Add support for new CMG types
|
- chpstat: Add support for new CMG types
|
||||||
- dbginfo.sh: add overview commands and crypto update
|
- 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)
|
- hyptop: Support for structured output (json, json-seq, csv)
|
||||||
- lszfcp: Add missing fallback marker for non-good fc_host port_state
|
- lszfcp: Add missing fallback marker for non-good fc_host port_state
|
||||||
- lszfcp: Improve speed with many SCSI devices
|
- lszfcp: Improve speed with many SCSI devices
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
|||||||
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 \
|
||||||
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon
|
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon \
|
||||||
|
zpwr zmemtopo
|
||||||
|
|
||||||
else
|
else
|
||||||
BASELIB_DIRS =
|
BASELIB_DIRS =
|
||||||
|
|||||||
@@ -128,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.
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
+6
-3
@@ -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 := 37
|
RELEASE := 38
|
||||||
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)
|
||||||
@@ -265,9 +265,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
|
||||||
|
|||||||
+11
-9
@@ -16,11 +16,11 @@
|
|||||||
#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_base.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
|
||||||
static unsigned int verbose;
|
static unsigned int verbose;
|
||||||
static unsigned long min_sdb, max_sdb;
|
static unsigned long min_sdb, max_sdb;
|
||||||
@@ -88,21 +88,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 +111,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+641
-7
@@ -162,13 +162,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;
|
||||||
@@ -3136,6 +3129,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 +4104,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+296
-61
@@ -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.
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "lib/util_scandir.h"
|
#include "lib/util_scandir.h"
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.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"
|
||||||
@@ -51,18 +52,91 @@
|
|||||||
|
|
||||||
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 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;
|
||||||
@@ -79,7 +153,7 @@ static bool read_counternames(void)
|
|||||||
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)
|
||||||
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 +167,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 +374,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 +552,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 +610,8 @@ static int test_read(struct s390_hwctr_read *read)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show();
|
show_format();
|
||||||
|
firstread = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,6 +674,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 +694,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,6 +721,9 @@ 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;
|
||||||
@@ -605,6 +745,40 @@ static struct util_opt opt_vec[] = {
|
|||||||
.argument = "NUMBER",
|
.argument = "NUMBER",
|
||||||
.desc = "Specifies interval between read operations (seconds)"
|
.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 ")"
|
||||||
|
},
|
||||||
UTIL_OPT_HELP,
|
UTIL_OPT_HELP,
|
||||||
UTIL_OPT_VERSION,
|
UTIL_OPT_VERSION,
|
||||||
UTIL_OPT_END
|
UTIL_OPT_END
|
||||||
@@ -634,6 +808,8 @@ 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;
|
||||||
|
|
||||||
@@ -665,12 +841,65 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 +910,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 +926,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
free_counternames();
|
free_counternames();
|
||||||
free(check);
|
free(check);
|
||||||
|
free(ioctlbuffer);
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-6
@@ -19,18 +19,26 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.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/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
|
#define OPT_FORMAT 256 /* --format XXX option */
|
||||||
static struct util_opt opt_vec[] = {
|
static struct util_opt opt_vec[] = {
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "format", required_argument, NULL, OPT_FORMAT },
|
||||||
|
.argument = "FORMAT",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.option = { "numeric", no_argument, NULL, 'n' },
|
.option = { "numeric", no_argument, NULL, 'n' },
|
||||||
.desc = "Sort PAI counters by counter number"
|
.desc = "Sort PAI counters by counter number"
|
||||||
@@ -58,6 +66,7 @@ static const struct util_prg prg = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static bool numsort; /* If true sort counter numerically */
|
static bool numsort; /* If true sort counter numerically */
|
||||||
|
static int output_format = -1; /* Generate style if >= 0 */
|
||||||
|
|
||||||
#define PAI_PATH "/bus/event_source/devices/%s"
|
#define PAI_PATH "/bus/event_source/devices/%s"
|
||||||
|
|
||||||
@@ -190,12 +199,44 @@ 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_format(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);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
|
||||||
|
util_fmt_pair(FMT_DEFAULT, "number", "%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 show_painode(void)
|
static void show_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) {
|
||||||
|
show_format(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));
|
||||||
@@ -243,7 +284,7 @@ static void make_painode(enum pai_types 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);
|
||||||
@@ -305,6 +346,7 @@ static void check_type_name(const char *type)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
enum util_fmt_t fmt;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
util_list_init(&pai_list, struct pai_node, node);
|
util_list_init(&pai_list, struct pai_node, node);
|
||||||
@@ -329,6 +371,11 @@ int main(int argc, char **argv)
|
|||||||
case 't':
|
case 't':
|
||||||
check_type_name(optarg);
|
check_type_name(optarg);
|
||||||
break;
|
break;
|
||||||
|
case OPT_FORMAT:
|
||||||
|
if (!util_fmt_name_to_type(optarg, &fmt))
|
||||||
|
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
|
||||||
|
output_format = fmt;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
+231
-4
@@ -7,18 +7,22 @@
|
|||||||
.\" ----------------------------------------------------------------------
|
.\" ----------------------------------------------------------------------
|
||||||
.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
|
||||||
lshwc \- 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 ]
|
||||||
|
.RB [ \-t
|
||||||
|
.IR time ]
|
||||||
|
.RB [ \-f
|
||||||
|
.IR format ]
|
||||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||||
.br
|
.br
|
||||||
\*c
|
\*c
|
||||||
@@ -51,6 +55,13 @@ 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 \-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
|
.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.
|
||||||
@@ -61,6 +72,107 @@ The default is 60 seconds.
|
|||||||
Performs the specified number of read operations.
|
Performs the specified number of read operations.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
.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
|
||||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
\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.
|
||||||
@@ -105,8 +217,8 @@ 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 CW
|
||||||
.sp 1
|
.sp 1
|
||||||
@@ -123,6 +235,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 CW
|
||||||
|
.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 CW
|
||||||
|
.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)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
|
|||||||
.
|
.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\*c
|
\*c
|
||||||
|
.RB [ \-\-format
|
||||||
|
.IR FORMAT ]
|
||||||
.RB [ \-n ]
|
.RB [ \-n ]
|
||||||
.RB [ \-t
|
.RB [ \-t
|
||||||
.IR "\ TYPE" ]
|
.IR "\ TYPE" ]
|
||||||
@@ -55,6 +57,12 @@ 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.
|
||||||
.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:
|
||||||
|
|||||||
+1
-2
@@ -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,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
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -31,11 +31,9 @@
|
|||||||
#define ECKD2DUMP_STACK_SIZE _AC(0x2000, UL)
|
#define ECKD2DUMP_STACK_SIZE _AC(0x2000, UL)
|
||||||
#define STAGE2_MAX_SIZE _AC(0x3000, UL)
|
#define STAGE2_MAX_SIZE _AC(0x3000, UL)
|
||||||
|
|
||||||
#define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL)
|
#define STAGE2_DUMPER_SIZE_SV _AC(0x3000, UL)
|
||||||
#define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL)
|
|
||||||
#define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL)
|
|
||||||
#define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL)
|
#define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL)
|
||||||
#define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL)
|
#define STAGE2_DUMPER_SIZE_SV_ZLIB _AC(0x8000, UL)
|
||||||
|
|
||||||
#define STAGE3_ENTRY _AC(0xa000, UL)
|
#define STAGE3_ENTRY _AC(0xa000, UL)
|
||||||
|
|
||||||
|
|||||||
@@ -27,16 +27,9 @@
|
|||||||
#define DF_S390_CPU_MAX 512
|
#define DF_S390_CPU_MAX 512
|
||||||
#define DF_S390_MAGIC_BLK_ECKD 3
|
#define DF_S390_MAGIC_BLK_ECKD 3
|
||||||
#define DF_S390_DUMPER_MAGIC_SIZE 7
|
#define DF_S390_DUMPER_MAGIC_SIZE 7
|
||||||
#define DF_S390_DUMPER_MAGIC32 "ZECKD31"
|
|
||||||
#define DF_S390_DUMPER_MAGIC64 "ZECKD64"
|
|
||||||
#define DF_S390_DUMPER_MAGIC_EXT "XECKD64"
|
#define DF_S390_DUMPER_MAGIC_EXT "XECKD64"
|
||||||
#define DF_S390_DUMPER_MAGIC32_FBA "ZDFBA31"
|
|
||||||
#define DF_S390_DUMPER_MAGIC64_FBA "ZDFBA64"
|
|
||||||
#define DF_S390_DUMPER_MAGIC_FBA_EXT "XDFBA64"
|
#define DF_S390_DUMPER_MAGIC_FBA_EXT "XDFBA64"
|
||||||
#define DF_S390_DUMPER_MAGIC_MV "ZMULT64"
|
|
||||||
#define DF_S390_DUMPER_MAGIC_MV_EXT "XMULT64"
|
#define DF_S390_DUMPER_MAGIC_MV_EXT "XMULT64"
|
||||||
#define OLD_DUMPER_HEX_INSTR1 "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */
|
|
||||||
#define OLD_DUMPER_HEX_INSTR2 "\x0d\xd0" /* BASR 13,0 */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Architecture of dumped system
|
* Architecture of dumped system
|
||||||
|
|||||||
+27
-8
@@ -10,16 +10,35 @@
|
|||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define S390_CPUMF_CF "/sys/devices/cpum_cf/"
|
#define S390_CPUMF_CF "devices/cpum_cf/"
|
||||||
#define S390_CPUMF_CFDIAG "/sys/devices/cpum_cf_diag/"
|
#define S390_CPUMF_CFDIAG "devices/cpum_cf_diag/"
|
||||||
#define S390_CPUMF_SF "/sys/devices/cpum_sf/"
|
#define S390_CPUMF_SF "devices/cpum_sf/"
|
||||||
#define S390_CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
#define S390_CPUS_ONLINE "devices/system/cpu/online"
|
||||||
#define S390_CPUS_ONLINE "/sys/devices/system/cpu/online"
|
#define S390_CPUMSF_BUFFERSZ "module/kernel/parameters/cpum_sfb_size"
|
||||||
#define S390_CPUMSF_BUFFERSZ "/sys/module/kernel/parameters/cpum_sfb_size"
|
#define S390_SYSFS_PAI_CRYPTO "devices/pai_crypto/"
|
||||||
#define S390_SYSFS_PAI_CRYPTO "/sys/devices/pai_crypto/"
|
#define S390_SYSFS_PAI_EXT "devices/pai_ext/"
|
||||||
#define S390_SYSFS_PAI_EXT "/sys/devices/pai_ext/"
|
|
||||||
#define S390_SYSFS_PAI_NNPA S390_SYSFS_PAI_EXT "events/NNPA_ALL"
|
#define S390_SYSFS_PAI_NNPA S390_SYSFS_PAI_EXT "events/NNPA_ALL"
|
||||||
|
|
||||||
|
#define CPUMF_CTRSET_NONE 0
|
||||||
|
#define CPUMF_CTRSET_EXTENDED 1
|
||||||
|
#define CPUMF_CTRSET_BASIC 2
|
||||||
|
#define CPUMF_CTRSET_PROBLEM_STATE 4
|
||||||
|
#define CPUMF_CTRSET_CRYPTO 8
|
||||||
|
#define CPUMF_CTRSET_MT_DIAG 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return counter set a counter belongs to.
|
||||||
|
*
|
||||||
|
* Return the counter set a given counter belongs to, given the
|
||||||
|
* CPU Measurement facility counter version first and second number.
|
||||||
|
*
|
||||||
|
* @param[in] ctr Counter number
|
||||||
|
* @param[in] cfvn CPUM Counter facility first version number
|
||||||
|
* @param[in] csvn CPUM Counter facility second version number
|
||||||
|
* @retval >=0 Counter set number to counter belongs to
|
||||||
|
*/
|
||||||
|
int libcpumf_ctrset(int ctr, int cfvn, int csvn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read out the PMU type from a given file.
|
* Read out the PMU type from a given file.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ enum util_arch_machine_type {
|
|||||||
UTIL_ARCH_MACHINE_TYPE_Z15_T02 = 8562,
|
UTIL_ARCH_MACHINE_TYPE_Z15_T02 = 8562,
|
||||||
UTIL_ARCH_MACHINE_TYPE_Z16 = 3931,
|
UTIL_ARCH_MACHINE_TYPE_Z16 = 3931,
|
||||||
UTIL_ARCH_MACHINE_TYPE_Z16_A02 = 3932,
|
UTIL_ARCH_MACHINE_TYPE_Z16_A02 = 3932,
|
||||||
|
UTIL_ARCH_MACHINE_TYPE_Z17 = 9175,
|
||||||
|
UTIL_ARCH_MACHINE_TYPE_Z17_2 = 9176,
|
||||||
};
|
};
|
||||||
|
|
||||||
int util_arch_machine_type(void);
|
int util_arch_machine_type(void);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ struct util_proc_dev_entry {
|
|||||||
#define UTIL_PROC_DEV_ENTRY_SD "sd"
|
#define UTIL_PROC_DEV_ENTRY_SD "sd"
|
||||||
#define UTIL_PROC_DEV_ENTRY_BLKEXT "blkext"
|
#define UTIL_PROC_DEV_ENTRY_BLKEXT "blkext"
|
||||||
#define UTIL_PROC_DEV_ENTRY_MD "md"
|
#define UTIL_PROC_DEV_ENTRY_MD "md"
|
||||||
|
#define UTIL_PROC_DEV_ENTRY_DM "device-mapper"
|
||||||
|
|
||||||
int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry);
|
int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry);
|
||||||
void util_proc_part_free_entry(struct util_proc_part_entry *entry);
|
void util_proc_part_free_entry(struct util_proc_part_entry *entry);
|
||||||
|
|||||||
+29
-68
@@ -9,7 +9,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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -49,19 +48,19 @@ static int device_sysfs_path(const char *device, char *path, const size_t path_s
|
|||||||
*/
|
*/
|
||||||
int ccw_is_device(const char *busid)
|
int ccw_is_device(const char *busid)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
static const char *const driver_paths[] = { "dasd-eckd", "virtio_ccw", "dasd-fba" };
|
||||||
|
char *path;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(driver_paths); i++) {
|
||||||
|
path = util_path_sysfs("bus/ccw/drivers/%s/%s", driver_paths[i], busid);
|
||||||
|
if (access(path, R_OK) == 0) {
|
||||||
|
free(path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(path, sizeof(path),
|
|
||||||
"/sys/bus/ccw/drivers/dasd-eckd/%s", busid);
|
|
||||||
if (access(path, R_OK) == 0)
|
|
||||||
return 1;
|
|
||||||
snprintf(path, sizeof(path),
|
|
||||||
"/sys/bus/ccw/drivers/virtio_ccw/%s", busid);
|
|
||||||
if (access(path, R_OK) == 0)
|
|
||||||
return 1;
|
|
||||||
snprintf(path, sizeof(path), "/sys/bus/ccw/drivers/dasd-fba/%s", busid);
|
|
||||||
if (access(path, R_OK) == 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +71,7 @@ int ccw_is_virtio_device(const char *device)
|
|||||||
{
|
{
|
||||||
char path[PATH_MAX] = { '\0' };
|
char path[PATH_MAX] = { '\0' };
|
||||||
unsigned virtio = 0;
|
unsigned virtio = 0;
|
||||||
|
char *path_pattern;
|
||||||
|
|
||||||
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -80,76 +80,37 @@ int ccw_is_virtio_device(const char *device)
|
|||||||
* The output has the following format:
|
* The output has the following format:
|
||||||
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
||||||
*/
|
*/
|
||||||
if (sscanf(path, "/sys/devices/css0/%*[0-9a-f.]/%*[0-9a-f.]/virtio%u", &virtio) != 1)
|
path_pattern = util_path_sysfs("devices/css0/%%*[0-9a-f.]/%%*[0-9a-f.]/virtio%%u");
|
||||||
|
if (sscanf(path, path_pattern, &virtio) != 1) {
|
||||||
|
free(path_pattern);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
free(path_pattern);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return CCW Bus ID (old sysfs)
|
* Return CCW Bus ID
|
||||||
*/
|
*/
|
||||||
static int ccw_busid_get_sysfs_old(const char *device, char *busid)
|
void ccw_busid_get(const char *device, char *busid)
|
||||||
{
|
|
||||||
char path[PATH_MAX];
|
|
||||||
char buf[4096];
|
|
||||||
int rc = 0;
|
|
||||||
FILE *fh;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/block/%s/uevent", device);
|
|
||||||
fh = fopen(path, "r");
|
|
||||||
if (fh == NULL)
|
|
||||||
return -1;
|
|
||||||
/*
|
|
||||||
* The uevent file contains an entry like this:
|
|
||||||
* PHYSDEVPATH=/devices/css0/0.0.206a/0.0.7e78
|
|
||||||
*/
|
|
||||||
while (fscanf(fh, "%s", buf) >= 0) {
|
|
||||||
if (strstr(buf, "PHYSDEVPATH") != NULL) {
|
|
||||||
strcpy(busid, strrchr(buf, '/') + 1);
|
|
||||||
goto out_fclose;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rc = -1;
|
|
||||||
out_fclose:
|
|
||||||
fclose(fh);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return CCW Bus ID (new sysfs)
|
|
||||||
*/
|
|
||||||
static int ccw_busid_get_sysfs_new(const char *device, char *busid)
|
|
||||||
{
|
{
|
||||||
char path[PATH_MAX] = { '\0' };
|
char path[PATH_MAX] = { '\0' };
|
||||||
|
char *path_pattern;
|
||||||
|
|
||||||
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
if (device_sysfs_path(device, path, sizeof(path)) != 0)
|
||||||
return -1;
|
ERR_EXIT("Could not lookup device number for \"%s\"", device);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The output has the following format:
|
* The output has the following format:
|
||||||
* /sys/devices/css0/0.0.0119/0.0.3f19/block/dasda
|
* /sys/devices/css0/0.0.0119/0.0.3f19/block/dasda
|
||||||
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
* /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda
|
||||||
*/
|
*/
|
||||||
if (sscanf(path, "/sys/devices/css0/%*[0-9a-f.]/%[0-9a-f.]", busid) != 1)
|
path_pattern = util_path_sysfs("devices/css0/%%*[0-9a-f.]/%%[0-9a-f.]");
|
||||||
return -1;
|
if (sscanf(path, path_pattern, busid) != 1) {
|
||||||
return 0;
|
free(path_pattern);
|
||||||
}
|
ERR_EXIT("Could not lookup device number for \"%s\"", device);
|
||||||
|
}
|
||||||
|
free(path_pattern);
|
||||||
|
|
||||||
/*
|
return;
|
||||||
* Return the device number for a device
|
|
||||||
* dasda can be found in /sys/block/dasda/uevent or in a
|
|
||||||
* symbolic link in the same directory. the first file only
|
|
||||||
* contains the relevant information if we run on a kernel with
|
|
||||||
* has the following kernel option enabled:
|
|
||||||
* CONFIG_SYSFS_DEPRECATED
|
|
||||||
*
|
|
||||||
* This does not work when booting from tape
|
|
||||||
*/
|
|
||||||
void ccw_busid_get(const char *device, char *busid)
|
|
||||||
{
|
|
||||||
if (ccw_busid_get_sysfs_old(device, busid) == 0)
|
|
||||||
return;
|
|
||||||
if (ccw_busid_get_sysfs_new(device, busid) == 0)
|
|
||||||
return;
|
|
||||||
ERR_EXIT("Could not lookup device number for \"%s\"", device);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-11
@@ -12,12 +12,17 @@
|
|||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_proc.h"
|
||||||
|
#include "lib/util_base.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
#include <linux/raid/md_u.h>
|
||||||
|
|
||||||
#define BOOTPARMS_NSS_MAX 56
|
#define BOOTPARMS_NSS_MAX 56
|
||||||
#define BOOTPARMS_CCW_MAX 64
|
#define BOOTPARMS_CCW_MAX 64
|
||||||
@@ -25,6 +30,8 @@
|
|||||||
|
|
||||||
#define OPT_BRCHR 0x80
|
#define OPT_BRCHR 0x80
|
||||||
|
|
||||||
|
#define MD_MAJOR 9
|
||||||
|
|
||||||
enum target_type {
|
enum target_type {
|
||||||
TT_CCW,
|
TT_CCW,
|
||||||
TT_FCP,
|
TT_FCP,
|
||||||
@@ -414,15 +421,38 @@ static int set_reipl_type(const char *dev_name)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_chreipl_helper_cmd(dev_t dev, char cmd[PATH_MAX])
|
static int is_md_device(const char *dev_name)
|
||||||
|
{
|
||||||
|
char abs_dev_name[PATH_MAX];
|
||||||
|
mdu_array_info_t array;
|
||||||
|
int is_md_device = 0;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (snprintf(abs_dev_name, PATH_MAX, "/dev/%s", dev_name) >= PATH_MAX)
|
||||||
|
return 0;
|
||||||
|
fd = open(abs_dev_name, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return 0;
|
||||||
|
if (ioctl(fd, GET_ARRAY_INFO, &array) >= 0)
|
||||||
|
is_md_device = 1;
|
||||||
|
close(fd);
|
||||||
|
return is_md_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_chreipl_helper_cmd(dev_t dev, char *dev_name, char cmd[PATH_MAX])
|
||||||
{
|
{
|
||||||
char *chreipl_helper;
|
|
||||||
struct proc_dev_entry pde;
|
struct proc_dev_entry pde;
|
||||||
|
char *chreipl_helper;
|
||||||
|
char *driver_name;
|
||||||
|
|
||||||
if (proc_dev_get_entry(dev, 1, &pde) != 0)
|
if (proc_dev_get_entry(dev, 1, &pde) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
driver_name = pde.name;
|
||||||
|
if (strcmp(driver_name, UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 &&
|
||||||
|
is_md_device(dev_name))
|
||||||
|
driver_name = UTIL_PROC_DEV_ENTRY_MD;
|
||||||
util_asprintf(&chreipl_helper,
|
util_asprintf(&chreipl_helper,
|
||||||
"%s/%s.%s", TOOLS_LIBDIR, "chreipl_helper", pde.name);
|
"%s.%s", util_libdir_path("chreipl_helper"), driver_name);
|
||||||
if (access(chreipl_helper, X_OK) != 0) {
|
if (access(chreipl_helper, X_OK) != 0) {
|
||||||
proc_dev_free_entry(&pde);
|
proc_dev_free_entry(&pde);
|
||||||
free(chreipl_helper);
|
free(chreipl_helper);
|
||||||
@@ -437,7 +467,7 @@ static int get_chreipl_helper_cmd(dev_t dev, char cmd[PATH_MAX])
|
|||||||
/*
|
/*
|
||||||
* Use chreipl_helper (E.g. for device mapper devices)
|
* Use chreipl_helper (E.g. for device mapper devices)
|
||||||
*/
|
*/
|
||||||
static int set_reipl_type_helper(int maj, int min)
|
static int set_reipl_type_helper(int maj, int min, char *dev_name)
|
||||||
{
|
{
|
||||||
char helper_cmd[PATH_MAX], buf[4096];
|
char helper_cmd[PATH_MAX], buf[4096];
|
||||||
struct proc_part_entry ppe;
|
struct proc_part_entry ppe;
|
||||||
@@ -445,7 +475,7 @@ static int set_reipl_type_helper(int maj, int min)
|
|||||||
dev_t dev;
|
dev_t dev;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
|
|
||||||
if (get_chreipl_helper_cmd(makedev(maj, min), helper_cmd) != 0)
|
if (get_chreipl_helper_cmd(makedev(maj, min), dev_name, helper_cmd) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
fh = popen(helper_cmd, "r");
|
fh = popen(helper_cmd, "r");
|
||||||
if (fh == NULL)
|
if (fh == NULL)
|
||||||
@@ -499,7 +529,7 @@ static void parse_node_args(char *nargv[], int nargc)
|
|||||||
ERR_EXIT("Invalid device node \"%s\" specified", path);
|
ERR_EXIT("Invalid device node \"%s\" specified", path);
|
||||||
if (set_reipl_type(ppe.name) == 0)
|
if (set_reipl_type(ppe.name) == 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (set_reipl_type_helper(major(dev), minor(dev)) == 0)
|
if (set_reipl_type_helper(major(dev), minor(dev), ppe.name) == 0)
|
||||||
goto out;
|
goto out;
|
||||||
ERR_EXIT("Unsupported device node \"%s\" specified", path);
|
ERR_EXIT("Unsupported device node \"%s\" specified", path);
|
||||||
out:
|
out:
|
||||||
@@ -719,11 +749,12 @@ static void parse_chreipl_options(int argc, char *argv[])
|
|||||||
|
|
||||||
static void check_exists(const char *path, const char *attr)
|
static void check_exists(const char *path, const char *attr)
|
||||||
{
|
{
|
||||||
char fpath[PATH_MAX];
|
char *fpath;
|
||||||
|
|
||||||
snprintf(fpath, sizeof(fpath), "/sys/firmware/%s", path);
|
fpath = util_path_sysfs("firmware/%s", path);
|
||||||
if (access(fpath, F_OK) != 0)
|
if (access(fpath, F_OK) != 0)
|
||||||
ERR_EXIT("System does not allow one to set %s", attr);
|
ERR_EXIT("System does not allow one to set %s", attr);
|
||||||
|
free(fpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_str_optional(char *string, char *file, int exit_on_fail,
|
static void write_str_optional(char *string, char *file, int exit_on_fail,
|
||||||
@@ -739,7 +770,7 @@ static void write_str_optional(char *string, char *file, int exit_on_fail,
|
|||||||
* IMPLEMENTATION:
|
* IMPLEMENTATION:
|
||||||
*
|
*
|
||||||
* "cio_ignore --is-ignored <busid>" returns 0 if the device is ignored,
|
* "cio_ignore --is-ignored <busid>" returns 0 if the device is ignored,
|
||||||
* 1 for internal errrors, and 2 if the device is not ignored.
|
* 1 for internal errors, and 2 if the device is not ignored.
|
||||||
*
|
*
|
||||||
* We get the "cio_ignore" exit status by the return code of the system()
|
* We get the "cio_ignore" exit status by the return code of the system()
|
||||||
* function via WEXITSTATUS().
|
* function via WEXITSTATUS().
|
||||||
@@ -920,13 +951,14 @@ static void chreipl_nss(void)
|
|||||||
|
|
||||||
static void chreipl_node(void)
|
static void chreipl_node(void)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
char *path;
|
||||||
|
|
||||||
if (!l.dev_set)
|
if (!l.dev_set)
|
||||||
ERR_EXIT("No device node specified");
|
ERR_EXIT("No device node specified");
|
||||||
snprintf(path, sizeof(path), "/sys/block/%s/device", l.dev);
|
path = util_path_sysfs("block/%s/device", l.dev);
|
||||||
if (chdir(path) != 0)
|
if (chdir(path) != 0)
|
||||||
ERR_EXIT("Could not find device \"%s\"", l.dev);
|
ERR_EXIT("Could not find device \"%s\"", l.dev);
|
||||||
|
free(path);
|
||||||
|
|
||||||
switch (l.reipl_type) {
|
switch (l.reipl_type) {
|
||||||
case REIPL_CCW:
|
case REIPL_CCW:
|
||||||
|
|||||||
+58
-44
@@ -9,6 +9,10 @@
|
|||||||
* 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 "lib/util_path.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@@ -42,24 +46,23 @@ static const char *get_ipl_banner(int show_ipl)
|
|||||||
void print_nss(int show_ipl)
|
void print_nss(int show_ipl)
|
||||||
{
|
{
|
||||||
char *dir = show_ipl ? "ipl" : "reipl/nss";
|
char *dir = show_ipl ? "ipl" : "reipl/nss";
|
||||||
char *path_bootparms = show_ipl ? "/sys/firmware/ipl/parm" :
|
char *path_bootparms = util_path_sysfs("firmware/%s/parm", dir);
|
||||||
"/sys/firmware/reipl/nss/parm";
|
|
||||||
|
|
||||||
printf("%-12s nss\n", get_ipl_banner(show_ipl));
|
printf("%-12s nss\n", get_ipl_banner(show_ipl));
|
||||||
print_fw_str("Name: %s\n", dir, "name");
|
print_fw_str("Name: %s\n", dir, "name");
|
||||||
if (access(path_bootparms, R_OK) == 0)
|
if (access(path_bootparms, R_OK) == 0)
|
||||||
print_fw_str("Bootparms: \"%s\"\n", dir, "parm");
|
print_fw_str("Bootparms: \"%s\"\n", dir, "parm");
|
||||||
|
free(path_bootparms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_fcp(int show_ipl, int dump)
|
void print_fcp(int show_ipl, int dump)
|
||||||
{
|
{
|
||||||
char *dir = show_ipl ? "ipl" : "reipl/fcp";
|
char *dir = show_ipl ? "ipl" : "reipl/fcp";
|
||||||
char *path_bootparms = show_ipl ? "/sys/firmware/ipl/scp_data" :
|
char *path_bootparms = util_path_sysfs("firmware/%s/scp_data", dir);
|
||||||
"/sys/firmware/reipl/fcp/scp_data";
|
char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir);
|
||||||
char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" :
|
char *path_reipl_clear = util_path_sysfs("firmware/reipl/fcp/clear");
|
||||||
"/sys/firmware/reipl/fcp/loadparm";
|
char *path_secure_boot = util_path_sysfs("firmware/ipl/secure");
|
||||||
char loadparm[9], loadparm_path[PATH_MAX];
|
char *loadparm;
|
||||||
char *path_reipl_clear = "/sys/firmware/reipl/fcp/clear";
|
|
||||||
|
|
||||||
if (dump)
|
if (dump)
|
||||||
printf("%-12s fcp_dump\n", get_ipl_banner(show_ipl));
|
printf("%-12s fcp_dump\n", get_ipl_banner(show_ipl));
|
||||||
@@ -72,27 +75,31 @@ void print_fcp(int show_ipl, int dump)
|
|||||||
print_fw_str("bootprog: %s\n", dir, "bootprog");
|
print_fw_str("bootprog: %s\n", dir, "bootprog");
|
||||||
print_fw_str("br_lba: %s\n", dir, "br_lba");
|
print_fw_str("br_lba: %s\n", dir, "br_lba");
|
||||||
if (access(path_loadparm, R_OK) == 0) {
|
if (access(path_loadparm, R_OK) == 0) {
|
||||||
sprintf(loadparm_path, "%s/%s", dir, "loadparm");
|
loadparm = util_file_read_text_file(path_loadparm, 1);
|
||||||
read_fw_str(loadparm, loadparm_path, sizeof(loadparm));
|
util_strstrip(loadparm);
|
||||||
if (strcmp(loadparm, " ") == 0)
|
|
||||||
loadparm[0] = 0;
|
|
||||||
printf("Loadparm: \"%s\"\n", loadparm);
|
printf("Loadparm: \"%s\"\n", loadparm);
|
||||||
|
free(loadparm);
|
||||||
}
|
}
|
||||||
if (access(path_bootparms, R_OK) == 0)
|
if (access(path_bootparms, R_OK) == 0)
|
||||||
print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data");
|
print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data");
|
||||||
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
||||||
print_fw_str("clear: %s\n", dir, "clear");
|
print_fw_str("clear: %s\n", dir, "clear");
|
||||||
|
if (access(path_secure_boot, R_OK) == 0)
|
||||||
|
print_fw_str("Secure boot: %s\n", "ipl", "secure");
|
||||||
|
free(path_bootparms);
|
||||||
|
free(path_loadparm);
|
||||||
|
free(path_reipl_clear);
|
||||||
|
free(path_secure_boot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_nvme(int show_ipl, int dump)
|
void print_nvme(int show_ipl, int dump)
|
||||||
{
|
{
|
||||||
char *dir = show_ipl ? "ipl" : "reipl/nvme";
|
char *dir = show_ipl ? "ipl" : "reipl/nvme";
|
||||||
char *path_bootparms = show_ipl ? "/sys/firmware/ipl/scp_data" :
|
char *path_bootparms = util_path_sysfs("firmware/%s/scp_data", dir);
|
||||||
"/sys/firmware/reipl/nvme/scp_data";
|
char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir);
|
||||||
char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" :
|
char *path_reipl_clear = util_path_sysfs("firmware/reipl/nvme/clear");
|
||||||
"/sys/firmware/reipl/nvme/loadparm";
|
char *path_secure_boot = util_path_sysfs("firmware/ipl/secure");
|
||||||
char loadparm[9], loadparm_path[PATH_MAX];
|
char *loadparm;
|
||||||
char *path_reipl_clear = "/sys/firmware/reipl/nvme/clear";
|
|
||||||
|
|
||||||
if (dump)
|
if (dump)
|
||||||
printf("%-12s nvme_dump\n", get_ipl_banner(show_ipl));
|
printf("%-12s nvme_dump\n", get_ipl_banner(show_ipl));
|
||||||
@@ -104,49 +111,54 @@ void print_nvme(int show_ipl, int dump)
|
|||||||
print_fw_str("bootprog: %s\n", dir, "bootprog");
|
print_fw_str("bootprog: %s\n", dir, "bootprog");
|
||||||
print_fw_str("br_lba: %s\n", dir, "br_lba");
|
print_fw_str("br_lba: %s\n", dir, "br_lba");
|
||||||
if (access(path_loadparm, R_OK) == 0) {
|
if (access(path_loadparm, R_OK) == 0) {
|
||||||
sprintf(loadparm_path, "%s/%s", dir, "loadparm");
|
loadparm = util_file_read_text_file(path_loadparm, 1);
|
||||||
read_fw_str(loadparm, loadparm_path, sizeof(loadparm));
|
util_strstrip(loadparm);
|
||||||
if (strcmp(loadparm, " ") == 0)
|
|
||||||
loadparm[0] = 0;
|
|
||||||
printf("Loadparm: \"%s\"\n", loadparm);
|
printf("Loadparm: \"%s\"\n", loadparm);
|
||||||
|
free(loadparm);
|
||||||
}
|
}
|
||||||
if (access(path_bootparms, R_OK) == 0)
|
if (access(path_bootparms, R_OK) == 0)
|
||||||
print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data");
|
print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data");
|
||||||
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
||||||
print_fw_str("clear: %s\n", dir, "clear");
|
print_fw_str("clear: %s\n", dir, "clear");
|
||||||
|
if (access(path_secure_boot, R_OK) == 0)
|
||||||
|
print_fw_str("Secure boot: %s\n", "ipl", "secure");
|
||||||
|
free(path_bootparms);
|
||||||
|
free(path_loadparm);
|
||||||
|
free(path_reipl_clear);
|
||||||
|
free(path_secure_boot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_ccw(int show_ipl)
|
void print_ccw(int show_ipl)
|
||||||
{
|
{
|
||||||
char loadparm[9], loadparm_path[PATH_MAX];
|
|
||||||
char *dir = show_ipl ? "ipl" : "reipl/ccw";
|
char *dir = show_ipl ? "ipl" : "reipl/ccw";
|
||||||
char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" :
|
char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir);
|
||||||
"/sys/firmware/reipl/ccw/loadparm";
|
char *path_bootparms = util_path_sysfs("firmware/%s/parm", dir);
|
||||||
char *path_bootparms = show_ipl ? "/sys/firmware/ipl/parm" :
|
char *path_reipl_clear = util_path_sysfs("firmware/reipl/ccw/clear");
|
||||||
"/sys/firmware/reipl/ccw/parm";
|
char *loadparm;
|
||||||
char *path_reipl_clear = "/sys/firmware/reipl/ccw/clear";
|
|
||||||
|
|
||||||
printf("%-12s ccw\n", get_ipl_banner(show_ipl));
|
printf("%-12s ccw\n", get_ipl_banner(show_ipl));
|
||||||
print_fw_str("Device: %s\n", dir, "device");
|
print_fw_str("Device: %s\n", dir, "device");
|
||||||
if (access(path_loadparm, R_OK) == 0) {
|
if (access(path_loadparm, R_OK) == 0) {
|
||||||
sprintf(loadparm_path, "%s/%s", dir, "loadparm");
|
loadparm = util_file_read_text_file(path_loadparm, 1);
|
||||||
read_fw_str(loadparm, loadparm_path, sizeof(loadparm));
|
util_strstrip(loadparm);
|
||||||
if (strcmp(loadparm, " ") == 0)
|
|
||||||
loadparm[0] = 0;
|
|
||||||
printf("Loadparm: \"%s\"\n", loadparm);
|
printf("Loadparm: \"%s\"\n", loadparm);
|
||||||
|
free(loadparm);
|
||||||
}
|
}
|
||||||
if (access(path_bootparms, R_OK) == 0)
|
if (access(path_bootparms, R_OK) == 0)
|
||||||
print_fw_str("Bootparms: \"%s\"\n", dir, "parm");
|
print_fw_str("Bootparms: \"%s\"\n", dir, "parm");
|
||||||
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
if (!show_ipl && access(path_reipl_clear, R_OK) == 0)
|
||||||
print_fw_str("clear: %s\n", dir, "clear");
|
print_fw_str("clear: %s\n", dir, "clear");
|
||||||
|
free(path_loadparm);
|
||||||
|
free(path_bootparms);
|
||||||
|
free(path_reipl_clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_eckd(int show_ipl, const char *name)
|
void print_eckd(int show_ipl, const char *name)
|
||||||
{
|
{
|
||||||
char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" :
|
|
||||||
"/sys/firmware/reipl/eckd/loadparm";
|
|
||||||
char *dir = show_ipl ? "ipl" : "reipl/eckd";
|
char *dir = show_ipl ? "ipl" : "reipl/eckd";
|
||||||
char loadparm[9], loadparm_path[PATH_MAX];
|
char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir);
|
||||||
|
char *path_secure_boot = util_path_sysfs("firmware/ipl/secure");
|
||||||
|
char *loadparm;
|
||||||
|
|
||||||
printf("%-12s %s\n", get_ipl_banner(show_ipl), name);
|
printf("%-12s %s\n", get_ipl_banner(show_ipl), name);
|
||||||
|
|
||||||
@@ -155,14 +167,17 @@ void print_eckd(int show_ipl, const char *name)
|
|||||||
print_fw_str("br_chr: %s\n", dir, "br_chr");
|
print_fw_str("br_chr: %s\n", dir, "br_chr");
|
||||||
print_fw_str("Bootparm: \"%s\"\n", dir, "scp_data");
|
print_fw_str("Bootparm: \"%s\"\n", dir, "scp_data");
|
||||||
if (access(path_loadparm, R_OK) == 0) {
|
if (access(path_loadparm, R_OK) == 0) {
|
||||||
sprintf(loadparm_path, "%s/%s", dir, "loadparm");
|
loadparm = util_file_read_text_file(path_loadparm, 1);
|
||||||
read_fw_str(loadparm, loadparm_path, sizeof(loadparm));
|
util_strstrip(loadparm);
|
||||||
if (strcmp(loadparm, " ") == 0)
|
|
||||||
loadparm[0] = 0;
|
|
||||||
printf("Loadparm: \"%s\"\n", loadparm);
|
printf("Loadparm: \"%s\"\n", loadparm);
|
||||||
|
free(loadparm);
|
||||||
}
|
}
|
||||||
if (!show_ipl)
|
if (!show_ipl)
|
||||||
print_fw_str("clear: %s\n", dir, "clear");
|
print_fw_str("clear: %s\n", dir, "clear");
|
||||||
|
if (access(path_secure_boot, R_OK) == 0)
|
||||||
|
print_fw_str("Secure boot: %s\n", "ipl", "secure");
|
||||||
|
free(path_loadparm);
|
||||||
|
free(path_secure_boot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_lsreipl_options(int argc, char *argv[])
|
static void parse_lsreipl_options(int argc, char *argv[])
|
||||||
@@ -196,16 +211,14 @@ static void parse_lsreipl_options(int argc, char *argv[])
|
|||||||
|
|
||||||
void cmd_lsreipl(int argc, char *argv[])
|
void cmd_lsreipl(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char reipl_type_str[1024];
|
char *reipl_type_str;
|
||||||
|
|
||||||
parse_lsreipl_options(argc, argv);
|
parse_lsreipl_options(argc, argv);
|
||||||
|
|
||||||
if (l.ipl_set)
|
if (l.ipl_set)
|
||||||
read_fw_str(reipl_type_str, "ipl/ipl_type",
|
reipl_type_str = read_fw_str("ipl/ipl_type");
|
||||||
sizeof(reipl_type_str));
|
|
||||||
else
|
else
|
||||||
read_fw_str(reipl_type_str, "reipl/reipl_type",
|
reipl_type_str = read_fw_str("reipl/reipl_type");
|
||||||
sizeof(reipl_type_str));
|
|
||||||
|
|
||||||
if (strcmp(reipl_type_str, "fcp") == 0)
|
if (strcmp(reipl_type_str, "fcp") == 0)
|
||||||
print_fcp(l.ipl_set, 0);
|
print_fcp(l.ipl_set, 0);
|
||||||
@@ -225,5 +238,6 @@ void cmd_lsreipl(int argc, char *argv[])
|
|||||||
else
|
else
|
||||||
printf("%s: %s (unknown)\n", get_ipl_banner(l.ipl_set),
|
printf("%s: %s (unknown)\n", get_ipl_banner(l.ipl_set),
|
||||||
reipl_type_str);
|
reipl_type_str);
|
||||||
|
free(reipl_type_str);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-7
@@ -9,6 +9,8 @@
|
|||||||
* 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 "lib/util_path.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
|
|
||||||
static const char *const usage_lsshut =
|
static const char *const usage_lsshut =
|
||||||
@@ -60,12 +62,13 @@ static void parse_lsshut_options(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
static void read_vmcmd(char *str, const char *path)
|
static void read_vmcmd(char *str, const char *path)
|
||||||
{
|
{
|
||||||
char buf[128], tmp[512];
|
|
||||||
char *ptr_old, *ptr;
|
char *ptr_old, *ptr;
|
||||||
|
char tmp[512];
|
||||||
|
char *buf;
|
||||||
|
|
||||||
*str = 0;
|
*str = 0;
|
||||||
|
buf = read_fw_str(path);
|
||||||
ptr_old = ptr = buf;
|
ptr_old = ptr = buf;
|
||||||
read_fw_str(buf, path, sizeof(buf));
|
|
||||||
while ((ptr = strchr(ptr_old, '\n'))) {
|
while ((ptr = strchr(ptr_old, '\n'))) {
|
||||||
*ptr = 0;
|
*ptr = 0;
|
||||||
sprintf(tmp, "\"%s\",", ptr_old);
|
sprintf(tmp, "\"%s\",", ptr_old);
|
||||||
@@ -74,23 +77,31 @@ static void read_vmcmd(char *str, const char *path)
|
|||||||
}
|
}
|
||||||
sprintf(tmp, "\"%s\"", ptr_old);
|
sprintf(tmp, "\"%s\"", ptr_old);
|
||||||
strcat(str, tmp);
|
strcat(str, tmp);
|
||||||
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_kdump(void)
|
static void print_kdump(void)
|
||||||
{
|
{
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
char tmp[1024];
|
char *path;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
if (stat("/sys/kernel/kexec_crash_loaded", &sb) != 0)
|
path = util_path_sysfs("kernel/kexec_crash_loaded");
|
||||||
|
if (stat(path, &sb) != 0) {
|
||||||
|
free(path);
|
||||||
return;
|
return;
|
||||||
read_str(tmp, "/sys/kernel/kexec_crash_loaded", sizeof(tmp));
|
}
|
||||||
|
tmp = util_file_read_text_file(path, 1);
|
||||||
if (strncmp(tmp, "1", 1) == 0)
|
if (strncmp(tmp, "1", 1) == 0)
|
||||||
printf("kdump,");
|
printf("kdump,");
|
||||||
|
free(path);
|
||||||
|
free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void shutdown_trigger_print(struct shutdown_trigger *trigger)
|
static void shutdown_trigger_print(struct shutdown_trigger *trigger)
|
||||||
{
|
{
|
||||||
char tmp[1024], cmd[1024], path[PATH_MAX];
|
char cmd[1024], path[PATH_MAX];
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
sprintf(path, "shutdown_actions/%s", trigger->name_sysfs);
|
sprintf(path, "shutdown_actions/%s", trigger->name_sysfs);
|
||||||
|
|
||||||
@@ -99,7 +110,7 @@ static void shutdown_trigger_print(struct shutdown_trigger *trigger)
|
|||||||
if ((trigger == &shutdown_trigger_panic ||
|
if ((trigger == &shutdown_trigger_panic ||
|
||||||
trigger == &shutdown_trigger_restart))
|
trigger == &shutdown_trigger_restart))
|
||||||
print_kdump();
|
print_kdump();
|
||||||
read_fw_str(tmp, path, sizeof(tmp));
|
tmp = read_fw_str(path);
|
||||||
if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
|
if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
|
||||||
sprintf(path, "vmcmd/%s", trigger->name_sysfs);
|
sprintf(path, "vmcmd/%s", trigger->name_sysfs);
|
||||||
read_vmcmd(cmd, path);
|
read_vmcmd(cmd, path);
|
||||||
@@ -107,6 +118,7 @@ static void shutdown_trigger_print(struct shutdown_trigger *trigger)
|
|||||||
} else {
|
} else {
|
||||||
printf("%s\n", tmp);
|
printf("%s\n", tmp);
|
||||||
}
|
}
|
||||||
|
free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_lsshut(int argc, char *argv[])
|
void cmd_lsshut(int argc, char *argv[])
|
||||||
|
|||||||
+19
-10
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -18,11 +19,14 @@
|
|||||||
*/
|
*/
|
||||||
int fcp_is_device(const char *devno)
|
int fcp_is_device(const char *devno)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
char *path;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/bus/ccw/drivers/zfcp/%s", devno);
|
path = util_path_sysfs("bus/ccw/drivers/zfcp/%s", devno);
|
||||||
if (chdir(path) != 0)
|
if (chdir(path) != 0) {
|
||||||
|
free(path);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,11 +35,12 @@ int fcp_is_device(const char *devno)
|
|||||||
*/
|
*/
|
||||||
void fcp_wwpn_get(const char *device, char *wwpn)
|
void fcp_wwpn_get(const char *device, char *wwpn)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], buf[20];
|
char buf[20];
|
||||||
|
char *path;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/block/%s/device/wwpn", device);
|
path = util_path_sysfs("block/%s/device/wwpn", device);
|
||||||
fh = fopen(path, "r");
|
fh = fopen(path, "r");
|
||||||
if (fh == NULL)
|
if (fh == NULL)
|
||||||
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
||||||
@@ -44,19 +49,20 @@ void fcp_wwpn_get(const char *device, char *wwpn)
|
|||||||
ERR_EXIT("Could not lookup WWPN \"%s\"", path);
|
ERR_EXIT("Could not lookup WWPN \"%s\"", path);
|
||||||
util_strlcpy(wwpn, buf, 20);
|
util_strlcpy(wwpn, buf, 20);
|
||||||
fclose(fh);
|
fclose(fh);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the lun of a device
|
* Return the lun of a device
|
||||||
*/
|
*/
|
||||||
void fcp_lun_get(const char *device, char *lun)
|
void fcp_lun_get(const char *device, char *lun)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], buf[20];
|
char buf[20];
|
||||||
|
char *path;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/block/%s/device/fcp_lun", device);
|
path = util_path_sysfs("block/%s/device/fcp_lun", device);
|
||||||
fh = fopen(path, "r");
|
fh = fopen(path, "r");
|
||||||
if (fh == NULL)
|
if (fh == NULL)
|
||||||
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
||||||
@@ -65,6 +71,7 @@ void fcp_lun_get(const char *device, char *lun)
|
|||||||
ERR_EXIT("Could not lookup LUN \"%s\"", path);
|
ERR_EXIT("Could not lookup LUN \"%s\"", path);
|
||||||
util_strlcpy(lun, buf, 20);
|
util_strlcpy(lun, buf, 20);
|
||||||
fclose(fh);
|
fclose(fh);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -72,11 +79,12 @@ void fcp_lun_get(const char *device, char *lun)
|
|||||||
*/
|
*/
|
||||||
void fcp_busid_get(const char *device, char *devno)
|
void fcp_busid_get(const char *device, char *devno)
|
||||||
{
|
{
|
||||||
char buf[4096], path[PATH_MAX];
|
char buf[4096];
|
||||||
|
char *path;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/block/%s/device/hba_id", device);
|
path = util_path_sysfs("block/%s/device/hba_id", device);
|
||||||
fh = fopen(path, "r");
|
fh = fopen(path, "r");
|
||||||
if (fh == NULL)
|
if (fh == NULL)
|
||||||
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
||||||
@@ -85,4 +93,5 @@ void fcp_busid_get(const char *device, char *devno)
|
|||||||
ERR_EXIT("Could not find device \"%s\"", path);
|
ERR_EXIT("Could not find device \"%s\"", path);
|
||||||
strcpy(devno, buf);
|
strcpy(devno, buf);
|
||||||
fclose(fh);
|
fclose(fh);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-30
@@ -12,7 +12,6 @@
|
|||||||
#ifndef IPL_TOOLS_H
|
#ifndef IPL_TOOLS_H
|
||||||
#define IPL_TOOLS_H
|
#define IPL_TOOLS_H
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@@ -36,41 +35,38 @@ extern struct globals {
|
|||||||
/*
|
/*
|
||||||
* Commands
|
* Commands
|
||||||
*/
|
*/
|
||||||
extern void cmd_lsshut(int argc, char *argv[]);
|
void cmd_lsshut(int argc, char *argv[]);
|
||||||
extern void cmd_chshut(int argc, char *argv[]);
|
void cmd_chshut(int argc, char *argv[]);
|
||||||
extern void cmd_lsreipl(int argc, char *argv[]);
|
void cmd_lsreipl(int argc, char *argv[]);
|
||||||
extern void cmd_chreipl(int argc, char *argv[]);
|
void cmd_chreipl(int argc, char *argv[]);
|
||||||
|
|
||||||
extern void print_ccw(int show_ipl);
|
void print_ccw(int show_ipl);
|
||||||
extern void print_fcp(int show_ipl, int dump);
|
void print_fcp(int show_ipl, int dump);
|
||||||
extern void print_nvme(int show_ipl, int dump);
|
void print_nvme(int show_ipl, int dump);
|
||||||
extern void print_nss(int show_ipl);
|
void print_nss(int show_ipl);
|
||||||
extern void print_eckd(int show_ipl, const char *name);
|
void print_eckd(int show_ipl, const char *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper
|
* Helper
|
||||||
*/
|
*/
|
||||||
extern int is_lpar(void);
|
int is_lpar(void);
|
||||||
extern int is_root(void);
|
int is_root(void);
|
||||||
|
|
||||||
extern void strlow(char *s);
|
void write_str(char *string, char *file);
|
||||||
|
int write_str_errno(char *string, char *file);
|
||||||
|
char *read_fw_str(const char *file);
|
||||||
|
void print_fw_str(const char *fmt, const char *dir, const char *file);
|
||||||
|
|
||||||
extern void write_str(char *string, char *file);
|
void __noreturn print_version_exit(void);
|
||||||
extern int write_str_errno(char *string, char *file);
|
void __noreturn print_help_hint_exit(void);
|
||||||
extern void read_str(char *string, const char *file, size_t len);
|
|
||||||
extern void read_fw_str(char *string, const char *file, size_t len);
|
|
||||||
extern void print_fw_str(const char *fmt, const char *dir, const char *file);
|
|
||||||
|
|
||||||
extern void __noreturn print_version_exit(void);
|
|
||||||
extern void __noreturn print_help_hint_exit(void);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FCP
|
* FCP
|
||||||
*/
|
*/
|
||||||
extern int fcp_is_device(const char *devno);
|
int fcp_is_device(const char *devno);
|
||||||
extern void fcp_lun_get(const char *device, char *lun);
|
void fcp_lun_get(const char *device, char *lun);
|
||||||
extern void fcp_wwpn_get(const char *device, char *wwpn);
|
void fcp_wwpn_get(const char *device, char *wwpn);
|
||||||
extern void fcp_busid_get(const char *device, char *devno);
|
void fcp_busid_get(const char *device, char *devno);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NVME
|
* NVME
|
||||||
@@ -79,16 +75,16 @@ extern void fcp_busid_get(const char *device, char *devno);
|
|||||||
#define NVME_DEV_MAX_LEN 15 /* "nvme" + u32 in decimal + null */
|
#define NVME_DEV_MAX_LEN 15 /* "nvme" + u32 in decimal + null */
|
||||||
#define NVME_PATH_MAX (PATH_MAX + NAME_MAX + 1)
|
#define NVME_PATH_MAX (PATH_MAX + NAME_MAX + 1)
|
||||||
|
|
||||||
extern void nvme_fid_get(const char *device, char *fid);
|
void nvme_fid_get(const char *device, char *fid);
|
||||||
extern void nvme_nsid_get(const char *device, char *nsid);
|
void nvme_nsid_get(const char *device, char *nsid);
|
||||||
int nvme_is_device(char *fid_str, char *nsid_str);
|
int nvme_is_device(char *fid_str, char *nsid_str);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CCW
|
* CCW
|
||||||
*/
|
*/
|
||||||
extern int ccw_is_device(const char *devno);
|
int ccw_is_device(const char *devno);
|
||||||
extern int ccw_is_virtio_device(const char *device);
|
int ccw_is_virtio_device(const char *device);
|
||||||
extern void ccw_busid_get(const char *device, char *devno);
|
void ccw_busid_get(const char *device, char *devno);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shutdown trigger
|
* Shutdown trigger
|
||||||
|
|||||||
+13
-6
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "lib/util_libc.h"
|
#include "lib/util_libc.h"
|
||||||
#include "lib/util_file.h"
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
|
|
||||||
static void nvme_dev_from_bdev(char *dev_name)
|
static void nvme_dev_from_bdev(char *dev_name)
|
||||||
@@ -31,8 +32,9 @@ static void nvme_dev_from_bdev(char *dev_name)
|
|||||||
*/
|
*/
|
||||||
void nvme_fid_get(const char *device, char *fid)
|
void nvme_fid_get(const char *device, char *fid)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], buf[FID_MAX_LEN];
|
|
||||||
char nvme_dev[NVME_DEV_MAX_LEN];
|
char nvme_dev[NVME_DEV_MAX_LEN];
|
||||||
|
char buf[FID_MAX_LEN];
|
||||||
|
char *path;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An NVMe may present multiple namespaces and thus block devices, even
|
* An NVMe may present multiple namespaces and thus block devices, even
|
||||||
@@ -42,24 +44,27 @@ void nvme_fid_get(const char *device, char *fid)
|
|||||||
util_strlcpy(nvme_dev, device, sizeof(nvme_dev));
|
util_strlcpy(nvme_dev, device, sizeof(nvme_dev));
|
||||||
nvme_dev_from_bdev(nvme_dev);
|
nvme_dev_from_bdev(nvme_dev);
|
||||||
|
|
||||||
snprintf(path, PATH_MAX, "/sys/class/nvme/%s/device/function_id", nvme_dev);
|
path = util_path_sysfs("class/nvme/%s/device/function_id", nvme_dev);
|
||||||
if (util_file_read_line(buf, FID_MAX_LEN, path))
|
if (util_file_read_line(buf, FID_MAX_LEN, path))
|
||||||
ERR_EXIT_ERRNO("Could not read from \"%s\"", path);
|
ERR_EXIT_ERRNO("Could not read from \"%s\"", path);
|
||||||
|
|
||||||
util_strlcpy(fid, buf, FID_MAX_LEN);
|
util_strlcpy(fid, buf, FID_MAX_LEN);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Return the nsid of a device
|
* Return the nsid of a device
|
||||||
*/
|
*/
|
||||||
void nvme_nsid_get(const char *device, char *nsid)
|
void nvme_nsid_get(const char *device, char *nsid)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], buf[FID_MAX_LEN];
|
char buf[FID_MAX_LEN];
|
||||||
|
char *path;
|
||||||
|
|
||||||
snprintf(path, PATH_MAX, "/sys/block/%s/nsid", device);
|
path = util_path_sysfs("block/%s/nsid", device);
|
||||||
if (util_file_read_line(buf, FID_MAX_LEN, path))
|
if (util_file_read_line(buf, FID_MAX_LEN, path))
|
||||||
ERR_EXIT_ERRNO("Could not read from \"%s\"", path);
|
ERR_EXIT_ERRNO("Could not read from \"%s\"", path);
|
||||||
|
|
||||||
util_strlcpy(nsid, buf, FID_MAX_LEN);
|
util_strlcpy(nsid, buf, FID_MAX_LEN);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int next_entry(DIR *dir, char *in_path, char *out_path,
|
static int next_entry(DIR *dir, char *in_path, char *out_path,
|
||||||
@@ -93,13 +98,14 @@ static int next_entry(DIR *dir, char *in_path, char *out_path,
|
|||||||
|
|
||||||
static int nvme_getdev_by_fid(char *fidstr, char *devpath)
|
static int nvme_getdev_by_fid(char *fidstr, char *devpath)
|
||||||
{
|
{
|
||||||
char temp_path[PATH_MAX+19], real_path[PATH_MAX];
|
char temp_path[PATH_MAX + 19], real_path[PATH_MAX];
|
||||||
char *sys_path = "/sys/class/nvme";
|
|
||||||
u_int64_t target_fid, curfid;
|
u_int64_t target_fid, curfid;
|
||||||
|
char *sys_path;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
char *end;
|
char *end;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
|
||||||
|
sys_path = util_path_sysfs("class/nvme");
|
||||||
target_fid = strtoul(fidstr, &end, 16);
|
target_fid = strtoul(fidstr, &end, 16);
|
||||||
if (*end)
|
if (*end)
|
||||||
ERR_EXIT("Invalid function_id given %s", fidstr);
|
ERR_EXIT("Invalid function_id given %s", fidstr);
|
||||||
@@ -126,6 +132,7 @@ static int nvme_getdev_by_fid(char *fidstr, char *devpath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
free(sys_path);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#ifndef PROC_H
|
#ifndef PROC_H
|
||||||
#define PROC_H
|
#define PROC_H
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
struct proc_part_entry {
|
struct proc_part_entry {
|
||||||
|
|||||||
+19
-42
@@ -9,7 +9,8 @@
|
|||||||
* 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 "lib/util_libc.h"
|
#include "lib/util_path.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
#include "ipl_tools.h"
|
#include "ipl_tools.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -46,47 +47,18 @@ int is_root(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert a string to lower case
|
|
||||||
*/
|
|
||||||
void strlow(char *s)
|
|
||||||
{
|
|
||||||
while (*s) {
|
|
||||||
*s = tolower(*s);
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read a string from a particular file
|
|
||||||
*/
|
|
||||||
void read_str(char *string, const char *path, size_t len)
|
|
||||||
{
|
|
||||||
size_t rc;
|
|
||||||
FILE *fh;
|
|
||||||
|
|
||||||
fh = fopen(path, "rb");
|
|
||||||
if (fh == NULL)
|
|
||||||
ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
|
||||||
rc = fread(string, 1, len - 1, fh);
|
|
||||||
if (rc == 0 && ferror(fh))
|
|
||||||
ERR_EXIT_ERRNO("Could not read \"%s\"", path);
|
|
||||||
fclose(fh);
|
|
||||||
string[rc] = 0;
|
|
||||||
if (string[strlen(string) - 1] == '\n')
|
|
||||||
string[strlen(string) - 1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read a string from a particular /sys/firmware file
|
* Read a string from a particular /sys/firmware file
|
||||||
*/
|
*/
|
||||||
void read_fw_str(char *string, const char *file, size_t len)
|
char *read_fw_str(const char *file)
|
||||||
{
|
{
|
||||||
|
char *string;
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
util_asprintf(&path, "/sys/firmware/%s", file);
|
path = util_path_sysfs("firmware/%s", file);
|
||||||
read_str(string, path, len);
|
string = util_file_read_text_file(path, 1);
|
||||||
free(path);
|
free(path);
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -94,11 +66,13 @@ void read_fw_str(char *string, const char *file, size_t len)
|
|||||||
*/
|
*/
|
||||||
void print_fw_str(const char *fmt, const char *dir, const char *file)
|
void print_fw_str(const char *fmt, const char *dir, const char *file)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], str[4096];
|
char path[PATH_MAX];
|
||||||
|
char *str;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, file);
|
snprintf(path, sizeof(path), "%s/%s", dir, file);
|
||||||
read_fw_str(str, path, sizeof(str));
|
str = read_fw_str(path);
|
||||||
printf(fmt, str);
|
printf(fmt, str);
|
||||||
|
free(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -106,17 +80,19 @@ void print_fw_str(const char *fmt, const char *dir, const char *file)
|
|||||||
*/
|
*/
|
||||||
void write_str(char *string, char *file)
|
void write_str(char *string, char *file)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], value[4096];
|
char value[4096];
|
||||||
|
char *path;
|
||||||
int fh;
|
int fh;
|
||||||
|
|
||||||
|
path = util_path_sysfs("firmware/%s", file);
|
||||||
snprintf(value, sizeof(value), "%s\n", string);
|
snprintf(value, sizeof(value), "%s\n", string);
|
||||||
snprintf(path, sizeof(path), "/sys/firmware/%s", file);
|
|
||||||
fh = open(path, O_WRONLY);
|
fh = open(path, O_WRONLY);
|
||||||
if (fh < 0)
|
if (fh < 0)
|
||||||
ERR_EXIT_ERRNO("Could not open \"%s\"", file);
|
ERR_EXIT_ERRNO("Could not open \"%s\"", file);
|
||||||
if (write(fh, value, strlen(value)) < 0)
|
if (write(fh, value, strlen(value)) < 0)
|
||||||
ERR_EXIT_ERRNO("Could not set \"%s\"", file);
|
ERR_EXIT_ERRNO("Could not set \"%s\"", file);
|
||||||
close(fh);
|
close(fh);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -124,17 +100,18 @@ void write_str(char *string, char *file)
|
|||||||
*/
|
*/
|
||||||
int write_str_errno(char *string, char *file)
|
int write_str_errno(char *string, char *file)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX], value[4096];
|
char value[4096];
|
||||||
|
char *path;
|
||||||
int fh;
|
int fh;
|
||||||
|
|
||||||
|
path = util_path_sysfs("firmware/%s", file);
|
||||||
snprintf(value, sizeof(value), "%s\n", string);
|
snprintf(value, sizeof(value), "%s\n", string);
|
||||||
snprintf(path, sizeof(path), "/sys/firmware/%s", file);
|
|
||||||
fh = open(path, O_WRONLY);
|
fh = open(path, O_WRONLY);
|
||||||
if (fh < 0)
|
if (fh < 0)
|
||||||
return errno;
|
return errno;
|
||||||
if (write(fh, value, strlen(value)) < 0)
|
if (write(fh, value, strlen(value)) < 0)
|
||||||
return errno;
|
return errno;
|
||||||
close(fh);
|
close(fh);
|
||||||
|
free(path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@ examples = libcpumf_example
|
|||||||
all: $(lib)
|
all: $(lib)
|
||||||
examples: $(lib) $(examples)
|
examples: $(lib) $(examples)
|
||||||
|
|
||||||
objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o
|
objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o \
|
||||||
|
libcpumf_ctrset.o
|
||||||
|
|
||||||
$(lib): $(objects)
|
$(lib): $(objects)
|
||||||
|
|
||||||
|
|||||||
+17
-19
@@ -5,20 +5,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <limits.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
|
||||||
int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
||||||
{
|
{
|
||||||
char *cp, *buffer = strdup(parm);
|
char *cp, *buffer = util_strdup(parm);
|
||||||
|
char *cp2 = buffer;
|
||||||
int to, from, rc;
|
int to, from, rc;
|
||||||
|
|
||||||
if (!buffer) /* Errno set to ENOMEM */
|
|
||||||
return -1;
|
|
||||||
/* Check for invalid characters, such as 11.12 instead 11-12
|
/* Check for invalid characters, such as 11.12 instead 11-12
|
||||||
* but allow blanks and newline. Newline is appended
|
* but allow blanks and newline. Newline is appended
|
||||||
* when the string is taken from sysfs files, for example
|
* when the string is taken from sysfs files, for example
|
||||||
@@ -26,7 +29,8 @@ int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
|||||||
*/
|
*/
|
||||||
if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) {
|
if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
rc = -1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
CPU_ZERO(mask);
|
CPU_ZERO(mask);
|
||||||
for (; (cp = strtok(buffer, ",")); buffer = NULL) {
|
for (; (cp = strtok(buffer, ",")); buffer = NULL) {
|
||||||
@@ -51,25 +55,19 @@ int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
|||||||
}
|
}
|
||||||
rc = 0;
|
rc = 0;
|
||||||
out:
|
out:
|
||||||
free(buffer);
|
free(cp2);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask)
|
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask)
|
||||||
{
|
{
|
||||||
char *txt = NULL;
|
char *path = util_path_sysfs(filename);
|
||||||
ssize_t ret = -1;
|
char txt[PATH_MAX];
|
||||||
size_t len = 0;
|
int ret = util_file_read_line(txt, sizeof(txt), "%s", path);
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
fp = fopen(filename, "r");
|
free(path);
|
||||||
if (!fp)
|
|
||||||
return ret;
|
|
||||||
/* Read out file, one line expected */
|
/* Read out file, one line expected */
|
||||||
ret = getline(&txt, &len, fp);
|
if (!ret)
|
||||||
fclose(fp);
|
|
||||||
if (ret > 0)
|
|
||||||
ret = libcpumf_cpuset(txt, mask);
|
ret = libcpumf_cpuset(txt, mask);
|
||||||
free(txt);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/* Copyright IBM Corp. 2022, 2024
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
|
int libcpumf_ctrset(int ctr, int cfvn, int csvn)
|
||||||
|
{
|
||||||
|
/* Governs basic and problem state counters */
|
||||||
|
switch (cfvn) {
|
||||||
|
case 1:
|
||||||
|
if (ctr >= 0 && ctr < 32)
|
||||||
|
return CPUMF_CTRSET_BASIC;
|
||||||
|
if (ctr >= 32 && ctr < 38)
|
||||||
|
return CPUMF_CTRSET_PROBLEM_STATE;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (ctr >= 0 && ctr < 32)
|
||||||
|
return CPUMF_CTRSET_BASIC;
|
||||||
|
if (ctr >= 32 && ctr < 34)
|
||||||
|
return CPUMF_CTRSET_PROBLEM_STATE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Governs crypto, extended and MT-Diagnositc counters */
|
||||||
|
switch (csvn) {
|
||||||
|
case 1 ... 5:
|
||||||
|
if (ctr >= 64 && ctr < 80)
|
||||||
|
return CPUMF_CTRSET_CRYPTO;
|
||||||
|
if ((csvn == 1 && ctr >= 128 && ctr < 160) ||
|
||||||
|
(csvn == 2 && ctr >= 128 && ctr < 176) ||
|
||||||
|
(ctr >= 128 && ctr < 256))
|
||||||
|
return CPUMF_CTRSET_EXTENDED;
|
||||||
|
break;
|
||||||
|
case 6 ... 8:
|
||||||
|
if (ctr >= 64 && ctr < 84)
|
||||||
|
return CPUMF_CTRSET_CRYPTO;
|
||||||
|
if (ctr >= 128 && ctr < 288)
|
||||||
|
return CPUMF_CTRSET_EXTENDED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (csvn >= 3 && ctr >= 448 && ctr < 496)
|
||||||
|
return CPUMF_CTRSET_MT_DIAG;
|
||||||
|
return CPUMF_CTRSET_NONE;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright IBM Corp. 2022
|
/* Copyright IBM Corp. 2022, 2025
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
@@ -18,16 +18,13 @@
|
|||||||
|
|
||||||
int libcpumf_pmutype(const char *dirname)
|
int libcpumf_pmutype(const char *dirname)
|
||||||
{
|
{
|
||||||
|
int ret = -1;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
char *fn;
|
char *fn;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = asprintf(&fn, "%s/type", dirname);
|
fn = util_path_sysfs("%s/type", dirname);
|
||||||
if (ret == -1) /* No memory, errno set */
|
|
||||||
return ret;
|
|
||||||
file = fopen(fn, "r");
|
file = fopen(fn, "r");
|
||||||
free(fn);
|
free(fn);
|
||||||
ret = -1; /* Errno set on file open error */
|
|
||||||
if (file) {
|
if (file) {
|
||||||
/* Read out a single number from that file */
|
/* Read out a single number from that file */
|
||||||
if (fscanf(file, "%u", &ret) != 1)
|
if (fscanf(file, "%u", &ret) != 1)
|
||||||
|
|||||||
+20
-11
@@ -13,6 +13,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "lib/libcpumf.h"
|
#include "lib/libcpumf.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
|
||||||
#define SERVICELEVEL "/proc/service_levels"
|
#define SERVICELEVEL "/proc/service_levels"
|
||||||
|
|
||||||
@@ -129,34 +130,42 @@ bool libcpumf_have_sfb(void)
|
|||||||
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max)
|
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max)
|
||||||
{
|
{
|
||||||
int rc = false;
|
int rc = false;
|
||||||
|
char *path;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "r");
|
path = util_path_sysfs(S390_CPUMSF_BUFFERSZ);
|
||||||
|
fp = fopen(path, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
err(EXIT_FAILURE, "%s", S390_CPUMSF_BUFFERSZ);
|
err(EXIT_FAILURE, "%s", path);
|
||||||
if (fscanf(fp, "%lu,%lu", min, max) == 2)
|
if (fscanf(fp, "%lu,%lu", min, max) == 2)
|
||||||
rc = true;
|
rc = true;
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
free(path);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool libcpumf_have_pai_sysfs(char *p)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
path = util_path_sysfs(p);
|
||||||
|
ret = util_path_exists(path);
|
||||||
|
free(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool libcpumf_have_pai_crypto(void)
|
bool libcpumf_have_pai_crypto(void)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_CRYPTO);
|
||||||
|
|
||||||
return (stat(S390_SYSFS_PAI_CRYPTO, &statbuf) == -1) ? false : true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool libcpumf_have_pai_ext(void)
|
bool libcpumf_have_pai_ext(void)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_EXT);
|
||||||
|
|
||||||
return (stat(S390_SYSFS_PAI_EXT, &statbuf) == -1) ? false : true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool libcpumf_have_pai_nnpa(void)
|
bool libcpumf_have_pai_nnpa(void)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_NNPA);
|
||||||
|
|
||||||
return (stat(S390_SYSFS_PAI_NNPA, &statbuf) == -1) ? false : true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2432,7 +2432,7 @@ out:
|
|||||||
kmip_node_free(iv);
|
kmip_node_free(iv);
|
||||||
kmip_node_free(tag);
|
kmip_node_free(tag);
|
||||||
kmip_node_free(ffl);
|
kmip_node_free(ffl);
|
||||||
kmip_node_free(ffl);
|
kmip_node_free(ifl);
|
||||||
kmip_node_free(cnt);
|
kmip_node_free(cnt);
|
||||||
kmip_node_free(icv);
|
kmip_node_free(icv);
|
||||||
kmip_node_free(salt);
|
kmip_node_free(salt);
|
||||||
|
|||||||
@@ -1471,7 +1471,7 @@ retry:
|
|||||||
chain = SSL_get_peer_cert_chain(conn->plain_tls.ssl);
|
chain = SSL_get_peer_cert_chain(conn->plain_tls.ssl);
|
||||||
if (chain == NULL) {
|
if (chain == NULL) {
|
||||||
kmip_debug(debug, "SSL_get_peer_cert_chain failed");
|
kmip_debug(debug, "SSL_get_peer_cert_chain failed");
|
||||||
return -EIO;
|
rc = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ struct kmip_node *kmip_new_request_header(const struct kmip_version *version,
|
|||||||
out:
|
out:
|
||||||
kmip_node_free(ver);
|
kmip_node_free(ver);
|
||||||
kmip_node_free(max);
|
kmip_node_free(max);
|
||||||
|
kmip_node_free(ccorr);
|
||||||
|
kmip_node_free(scorr);
|
||||||
kmip_node_free(async);
|
kmip_node_free(async);
|
||||||
kmip_node_free(err);
|
kmip_node_free(err);
|
||||||
kmip_node_free(ord);
|
kmip_node_free(ord);
|
||||||
|
|||||||
@@ -1369,7 +1369,7 @@ int kmip_get_get_response_payload(const struct kmip_node *node,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (*unique_id != NULL) {
|
if (unique_id != NULL && *unique_id != NULL) {
|
||||||
kmip_node_free(*unique_id);
|
kmip_node_free(*unique_id);
|
||||||
*unique_id = NULL;
|
*unique_id = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,7 +351,6 @@ int kmip_connection_tls_init(struct kmip_connection *conn, bool debug)
|
|||||||
if (tok == NULL) {
|
if (tok == NULL) {
|
||||||
kmip_debug(debug, "malformed IPv6 address");
|
kmip_debug(debug, "malformed IPv6 address");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
free(hostname);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
tok++;
|
tok++;
|
||||||
|
|||||||
@@ -1501,6 +1501,9 @@ int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib,
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&rb, 0, sizeof(rb));
|
||||||
|
memset(&lrb, 0, sizeof(lrb));
|
||||||
|
|
||||||
rb.domain = domain;
|
rb.domain = domain;
|
||||||
lrb.domain = domain;
|
lrb.domain = domain;
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ const char *util_arch_machine_type_to_str(int type)
|
|||||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||||
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
case UTIL_ARCH_MACHINE_TYPE_Z16_A02:
|
||||||
return "IBM z16";
|
return "IBM z16";
|
||||||
|
case UTIL_ARCH_MACHINE_TYPE_Z17:
|
||||||
|
case UTIL_ARCH_MACHINE_TYPE_Z17_2:
|
||||||
|
return "IBM z17";
|
||||||
default:
|
default:
|
||||||
return "Unknown machine type";
|
return "Unknown machine type";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -616,6 +616,10 @@ char *util_file_read_fd(FILE *fd, int chomp)
|
|||||||
if (util_file_read_fd_buf(fd, (void **) &buffer, &done))
|
if (util_file_read_fd_buf(fd, (void **) &buffer, &done))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* Prevent over-read if buffer is larger than amount of read characters */
|
||||||
|
if (buffer)
|
||||||
|
done = MIN(done, strnlen(buffer, done));
|
||||||
|
|
||||||
/* Check if this is a text file at all (required to filter out
|
/* Check if this is a text file at all (required to filter out
|
||||||
* binary sysfs attributes).
|
* binary sysfs attributes).
|
||||||
*/
|
*/
|
||||||
|
|||||||
+7
-3
@@ -307,9 +307,13 @@ static void add_key(const char *name, bool persist)
|
|||||||
key.persist = persist;
|
key.persist = persist;
|
||||||
util_add_array(&f.keys, &f.num_keys, key);
|
util_add_array(&f.keys, &f.num_keys, key);
|
||||||
if (f.type == FMT_CSV) {
|
if (f.type == FMT_CSV) {
|
||||||
hdr = csv_quote(name);
|
if (f.quote_all) {
|
||||||
util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, hdr);
|
hdr = csv_quote(name);
|
||||||
free(hdr);
|
util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, hdr);
|
||||||
|
free(hdr);
|
||||||
|
} else {
|
||||||
|
util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, name);
|
||||||
|
}
|
||||||
util_rec_set(f.csv_rec, name, "\"\"");
|
util_rec_set(f.csv_rec, name, "\"\"");
|
||||||
f.csv_hdr = true;
|
f.csv_hdr = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-2
@@ -16,6 +16,19 @@
|
|||||||
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
||||||
. PD
|
. PD
|
||||||
..
|
..
|
||||||
|
.\" Macro for inserting code line.
|
||||||
|
.\" .CL <text>
|
||||||
|
.de CL
|
||||||
|
. ds pfont \fP
|
||||||
|
. nh
|
||||||
|
. na
|
||||||
|
. ft CW
|
||||||
|
\\$*
|
||||||
|
. ft \\*[pfont]
|
||||||
|
. ad
|
||||||
|
. hy
|
||||||
|
. br
|
||||||
|
..
|
||||||
.
|
.
|
||||||
.TH opticsmon 8 "Oct 2024" s390-tools zpcictl
|
.TH opticsmon 8 "Oct 2024" s390-tools zpcictl
|
||||||
.
|
.
|
||||||
@@ -67,8 +80,7 @@ on".
|
|||||||
|
|
||||||
.B Example: Extract module information for the first adapter
|
.B Example: Extract module information for the first adapter
|
||||||
|
|
||||||
.CL
|
.CL opticsmon --module-info | jq -r '.adapters[0].netdevs[0].optics.module_info' | base64 -d | hexdump -C
|
||||||
opticsmon --module-info | jq -r '.adapters[0].netdevs[0].optics.module_info' | base64 -d | hexdump -C
|
|
||||||
.PP
|
.PP
|
||||||
.PP
|
.PP
|
||||||
.
|
.
|
||||||
|
|||||||
Generated
+102
-37
@@ -190,7 +190,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpacfinfo"
|
name = "cpacfinfo"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -356,13 +356,25 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.15"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi 0.14.2+wasi-0.2.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -429,9 +441,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.172"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
@@ -539,9 +551,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
@@ -557,9 +569,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.93"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -575,8 +587,8 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_xorshift",
|
"rand_xorshift",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
"rusty-fork",
|
"rusty-fork",
|
||||||
@@ -586,13 +598,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pvapconfig"
|
name = "pvapconfig"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"openssl",
|
"openssl",
|
||||||
"rand",
|
"rand 0.9.1",
|
||||||
"regex",
|
"regex",
|
||||||
"s390_pv_core",
|
"s390_pv_core",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -602,7 +614,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pvattest"
|
name = "pvattest"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -622,7 +634,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pvimg"
|
name = "pvimg"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -642,7 +654,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pvsecret"
|
name = "pvsecret"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -661,13 +673,19 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.38"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "radium"
|
name = "radium"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -681,8 +699,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -692,7 +720,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -701,7 +739,16 @@ version = "0.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.16",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -710,7 +757,7 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -781,7 +828,7 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s390_pv"
|
name = "s390_pv"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"curl",
|
"curl",
|
||||||
@@ -799,12 +846,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s390_pv_core"
|
name = "s390_pv_core"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_test",
|
"serde_test",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -897,9 +945,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.96"
|
version = "2.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -920,7 +968,7 @@ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
"getrandom 0.2.16",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@@ -981,9 +1029,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.14"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsafe-libyaml"
|
name = "unsafe-libyaml"
|
||||||
@@ -999,7 +1047,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utils"
|
name = "utils"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1029,6 +1077,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.14.2+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -1120,6 +1177,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -1131,19 +1197,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.8.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.7.35"
|
version = "0.8.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+15
-3
@@ -14,7 +14,7 @@ ifneq (${HAVE_CARGO},0)
|
|||||||
CARGO_TARGETS :=
|
CARGO_TARGETS :=
|
||||||
|
|
||||||
BUILD_TARGETS = $(CARGO_TARGETS)
|
BUILD_TARGETS = $(CARGO_TARGETS)
|
||||||
INSTALL_TARGETS := install-rust-tools install-man
|
INSTALL_TARGETS := install-rust-tools install-man install-shell-completions
|
||||||
CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS))
|
CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS))
|
||||||
|
|
||||||
ifeq ($(HOST_ARCH),s390x)
|
ifeq ($(HOST_ARCH),s390x)
|
||||||
@@ -86,7 +86,6 @@ install: $(INSTALL_TARGETS)
|
|||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr $(DESTDIR)$(USRBINDIR)
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr $(DESTDIR)$(USRBINDIR)
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
|
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvimg/tools/check_hostkeydoc $(DESTDIR)$(PVIMG_PKGDATADIR)
|
|
||||||
$(MAKE) -C pvimg/boot install
|
$(MAKE) -C pvimg/boot install
|
||||||
ln -sf pvimg $(DESTDIR)$(USRBINDIR)/genprotimg
|
ln -sf pvimg $(DESTDIR)$(USRBINDIR)/genprotimg
|
||||||
|
|
||||||
@@ -115,8 +114,21 @@ install-man:
|
|||||||
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
|
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
|
||||||
$(foreach target,$(PV_TARGETS),\
|
$(foreach target,$(PV_TARGETS),\
|
||||||
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
|
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
|
||||||
|
ln -sf pvimg-create.1 $(DESTDIR)$(MANDIR)/man1/genprotimg.1
|
||||||
|
|
||||||
.PHONY: all install clean skip-build install-rust-tools print-rust-targets install-man rust-test
|
install-shell-completions: install-bash-completion install-zsh-completion
|
||||||
|
|
||||||
|
install-bash-completion: $(PV_TARGETS)
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BASHCOMPLETIONDIR)
|
||||||
|
$(foreach target,$(PV_TARGETS),\
|
||||||
|
$(INSTALL) -m 0644 --preserve-timestamps -- $(shell find $(shell ls --sort=time --reverse --directory target/release/build/$(target)-*/out/ |tail -n1) -name '*.bash') $(DESTDIR)$(BASHCOMPLETIONDIR);)
|
||||||
|
|
||||||
|
install-zsh-completion: $(PV_TARGETS)
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(ZSHCOMPLETIONDIR)
|
||||||
|
$(foreach target,$(PV_TARGETS),\
|
||||||
|
$(INSTALL) -m 0644 --preserve-timestamps -- $(shell find $(shell ls --sort=time --reverse --directory target/release/build/$(target)-*/out/ |tail -n1) -regex '.*/_[a-zA-Z0-9]+') $(DESTDIR)$(ZSHCOMPLETIONDIR);)
|
||||||
|
|
||||||
|
.PHONY: all install clean skip-build install-rust-tools print-rust-targets install-man rust-test install-bash-completion install-zsh-completion install-shell-completions
|
||||||
|
|
||||||
.check-cargo:
|
.check-cargo:
|
||||||
ifeq ($(shell command -v $(CARGO)),)
|
ifeq ($(shell command -v $(CARGO)),)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cpacfinfo"
|
name = "cpacfinfo"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
@@ -12,7 +12,7 @@ libc = "0.2"
|
|||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
utils = { path = "../utils" }
|
utils = { path = "../utils" }
|
||||||
zerocopy = { version="0.7", features = ["derive"] }
|
zerocopy = { version="0.8", features = ["derive"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.2"
|
cc = "1.2"
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ Possible values for
|
|||||||
Displays the CPACF subfunctions available on the system.
|
Displays the CPACF subfunctions available on the system.
|
||||||
.B cpacfinfo \-f/\-\-functions
|
.B cpacfinfo \-f/\-\-functions
|
||||||
is the same as
|
is the same as
|
||||||
.B cpacfinfo \-f/\-\-functions \-a/\-\-available
|
\fBcpacfinfo \-f/\-\-functions \-a/\-\-available\fR.
|
||||||
. If
|
If
|
||||||
.B -n/\-\-not-available
|
.B -n/\-\-not-available
|
||||||
is specified no available functions are shown. To get available as well as functions not available use
|
is specified no available functions are shown. To get available as well as functions not available use
|
||||||
.B cpacfinfo \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available.
|
.B cpacfinfo \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available.
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use std::ops::Index;
|
|||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use zerocopy::FromBytes;
|
use zerocopy::FromBytes;
|
||||||
use zerocopy::FromZeroes;
|
|
||||||
|
|
||||||
use crate::msa::InstructionKind;
|
use crate::msa::InstructionKind;
|
||||||
use crate::msa::QueryAuthInfo;
|
use crate::msa::QueryAuthInfo;
|
||||||
@@ -33,7 +32,7 @@ pub const QAI_PARAM_SIZE_IN_BYTES: usize = 256;
|
|||||||
/// Query authentication information format identifier
|
/// Query authentication information format identifier
|
||||||
const FORMAT_0: u8 = 0;
|
const FORMAT_0: u8 = 0;
|
||||||
|
|
||||||
#[derive(FromBytes, FromZeroes)]
|
#[derive(FromBytes)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct QaiFmt0 {
|
struct QaiFmt0 {
|
||||||
res00: [u8; 6],
|
res00: [u8; 6],
|
||||||
@@ -175,7 +174,7 @@ pub fn check_sysfs() -> bool {
|
|||||||
/// -----------------------------------------------------------------
|
/// -----------------------------------------------------------------
|
||||||
fn parse_qai_format_0(qai: &mut QueryAuthInfo, param: &[u8]) {
|
fn parse_qai_format_0(qai: &mut QueryAuthInfo, param: &[u8]) {
|
||||||
// parse param to temporary struct to ease further conversion
|
// parse param to temporary struct to ease further conversion
|
||||||
let tmp = QaiFmt0::read_from_prefix(param).expect("programming error");
|
let (tmp, _) = QaiFmt0::read_from_prefix(param).expect("programming error");
|
||||||
|
|
||||||
// parse from temporary struct
|
// parse from temporary struct
|
||||||
qai.hash_len = tmp.hash_length;
|
qai.hash_len = tmp.hash_length;
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "s390_pv"
|
name = "s390_pv"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
@@ -23,9 +23,9 @@ openssl = "0.10.70"
|
|||||||
openssl-sys = "0.9.105"
|
openssl-sys = "0.9.105"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
zerocopy = { version="0.7", features = ["derive"] }
|
zerocopy = { version="0.8", features = ["derive"] }
|
||||||
|
|
||||||
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.11.0" }
|
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_test = "1.0.177"
|
serde_test = "1.0.177"
|
||||||
|
|||||||
+11
-11
@@ -8,7 +8,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
|
||||||
|
|
||||||
// (SE) boot request control block aka SE header
|
// (SE) boot request control block aka SE header
|
||||||
use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE};
|
use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE};
|
||||||
@@ -21,7 +21,7 @@ use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGE
|
|||||||
/// Tweak List Digest (tld)
|
/// Tweak List Digest (tld)
|
||||||
/// SE-Header Tag (tag)
|
/// SE-Header Tag (tag)
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, AsBytes, PartialEq, Eq, FromBytes, FromZeroes)]
|
#[derive(Debug, Clone, Copy, IntoBytes, PartialEq, Eq, FromBytes, Immutable, KnownLayout)]
|
||||||
pub struct BootHdrTags {
|
pub struct BootHdrTags {
|
||||||
pld: [u8; BootHdrHead::DIGEST_SIZE],
|
pld: [u8; BootHdrHead::DIGEST_SIZE],
|
||||||
ald: [u8; BootHdrHead::DIGEST_SIZE],
|
ald: [u8; BootHdrHead::DIGEST_SIZE],
|
||||||
@@ -40,8 +40,8 @@ impl TryFrom<Vec<u8>> for BootHdrTags {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
Self::ref_from(&value)
|
Self::ref_from_bytes(&value)
|
||||||
.ok_or_else(|| Error::InvBootHdrSize(value.len()))
|
.map_err(|_| Error::InvBootHdrSize(value.len()))
|
||||||
.copied()
|
.copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ impl TryFrom<Vec<u8>> for BootHdrTags {
|
|||||||
/// Struct representing the Secure Execution boot image metadata
|
/// Struct representing the Secure Execution boot image metadata
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Debug, Clone, FromBytes, FromZeroes, AsBytes, PartialEq, Eq)]
|
#[derive(Debug, Clone, FromBytes, IntoBytes, PartialEq, Eq, Immutable, KnownLayout)]
|
||||||
pub struct SeImgMetaData {
|
pub struct SeImgMetaData {
|
||||||
/// Magic value
|
/// Magic value
|
||||||
magic: [u8; 8],
|
magic: [u8; 8],
|
||||||
@@ -106,7 +106,7 @@ impl SeImgMetaData {
|
|||||||
/// Gets the bytes of this value.
|
/// Gets the bytes of this value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
<Self as AsBytes>::as_bytes(self)
|
<Self as IntoBytes>::as_bytes(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the version of this [`SeImgMetaData`].
|
/// Returns the version of this [`SeImgMetaData`].
|
||||||
@@ -154,7 +154,7 @@ where
|
|||||||
// read in the header
|
// read in the header
|
||||||
img.read_exact(&mut img_metadata_bytes)?;
|
img.read_exact(&mut img_metadata_bytes)?;
|
||||||
// Cannot fail because the buffer has the same size as SeImgMetaData.
|
// Cannot fail because the buffer has the same size as SeImgMetaData.
|
||||||
let img_metadata = SeImgMetaData::ref_from(&img_metadata_bytes).unwrap();
|
let img_metadata = SeImgMetaData::ref_from_bytes(&img_metadata_bytes).unwrap();
|
||||||
let img_metadata_version = img_metadata.version();
|
let img_metadata_version = img_metadata.version();
|
||||||
if img_metadata_version != SeImgMetaData::V1 {
|
if img_metadata_version != SeImgMetaData::V1 {
|
||||||
warn!("Unknown Secure Execution boot image version {img_metadata_version}");
|
warn!("Unknown Secure Execution boot image version {img_metadata_version}");
|
||||||
@@ -226,8 +226,8 @@ impl BootHdrTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hdr_head = match BootHdrHead::read_from_prefix(hdr.as_mut_slice()) {
|
let hdr_head = match BootHdrHead::read_from_prefix(hdr.as_mut_slice()) {
|
||||||
Some(hdr) => hdr,
|
Ok((hdr, _)) => hdr,
|
||||||
None => {
|
Err(_) => {
|
||||||
debug!("Boot hdr is too small");
|
debug!("Boot hdr is too small");
|
||||||
return Err(Error::InvBootHdr);
|
return Err(Error::InvBootHdr);
|
||||||
}
|
}
|
||||||
@@ -260,7 +260,7 @@ impl BootHdrTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, FromBytes, FromZeroes)]
|
#[derive(Debug, Clone, FromBytes)]
|
||||||
struct BootHdrHead {
|
struct BootHdrHead {
|
||||||
magic: U64<BigEndian>,
|
magic: U64<BigEndian>,
|
||||||
version: U32<BigEndian>,
|
version: U32<BigEndian>,
|
||||||
@@ -388,7 +388,7 @@ mod tests {
|
|||||||
0, 1, 96, 0,
|
0, 1, 96, 0,
|
||||||
];
|
];
|
||||||
assert_eq!(metadata.as_bytes(), &data);
|
assert_eq!(metadata.as_bytes(), &data);
|
||||||
assert_eq!(SeImgMetaData::ref_from(&data), Some(&metadata));
|
assert_eq!(SeImgMetaData::ref_from_bytes(&data), Ok(&metadata));
|
||||||
|
|
||||||
assert_eq!(metadata.version(), SeImgMetaData::V1);
|
assert_eq!(metadata.version(), SeImgMetaData::V1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ pub fn encrypt_aead(
|
|||||||
aad: &[u8],
|
aad: &[u8],
|
||||||
conf: &[u8],
|
conf: &[u8],
|
||||||
) -> Result<AeadEncryptionResult> {
|
) -> Result<AeadEncryptionResult> {
|
||||||
let tag_len = key.key_type().tag_len().ok_or_else(|| Error::NoAeadKey)?;
|
let tag_len = key.key_type().tag_len().ok_or(Error::NoAeadKey)?;
|
||||||
|
|
||||||
let nid = key.key_type().into();
|
let nid = key.key_type().into();
|
||||||
let cipher = Cipher::from_nid(nid).ok_or(Error::UnsupportedCipher(nid))?;
|
let cipher = Cipher::from_nid(nid).ok_or(Error::UnsupportedCipher(nid))?;
|
||||||
|
|||||||
+6
-6
@@ -12,7 +12,7 @@ use openssl::{
|
|||||||
pkey::{PKey, PKeyRef, Private, Public},
|
pkey::{PKey, PKeyRef, Private, Public},
|
||||||
};
|
};
|
||||||
use pv_core::request::{RequestMagic, RequestVersion};
|
use pv_core::request::{RequestMagic, RequestVersion};
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_size,
|
assert_size,
|
||||||
@@ -358,7 +358,7 @@ ecdh_from!(Public);
|
|||||||
/// Representation of the shared parts of the request header.
|
/// Representation of the shared parts of the request header.
|
||||||
/// Used by [`ReqEncrCtx`]
|
/// Used by [`ReqEncrCtx`]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, AsBytes, FromBytes, FromZeroes)]
|
#[derive(Debug, Copy, Clone, IntoBytes, FromBytes, Immutable)]
|
||||||
struct RequestHdr {
|
struct RequestHdr {
|
||||||
magic: [u8; 8],
|
magic: [u8; 8],
|
||||||
rqvn: U32<BigEndian>,
|
rqvn: U32<BigEndian>,
|
||||||
@@ -449,7 +449,7 @@ impl<'a> BinReqValues<'a> {
|
|||||||
/// Does minimal sanity test, just tests to prevent panics.
|
/// Does minimal sanity test, just tests to prevent panics.
|
||||||
/// `req` may be larger than the actual request.
|
/// `req` may be larger than the actual request.
|
||||||
pub(crate) fn get(req: &'a [u8]) -> Result<Self> {
|
pub(crate) fn get(req: &'a [u8]) -> Result<Self> {
|
||||||
let hdr = RequestHdr::read_from_prefix(req).ok_or(Error::BinRequestSmall)?;
|
let (hdr, _) = RequestHdr::read_from_prefix(req).map_err(|_| Error::BinRequestSmall)?;
|
||||||
let rql = hdr.rql.get() as usize;
|
let rql = hdr.rql.get() as usize;
|
||||||
let sea = hdr.sea.get() as usize;
|
let sea = hdr.sea.get() as usize;
|
||||||
|
|
||||||
@@ -506,9 +506,9 @@ impl<'a> BinReqValues<'a> {
|
|||||||
/// [`FromBytes::ref_from_prefix`]
|
/// [`FromBytes::ref_from_prefix`]
|
||||||
pub(crate) fn req_dep_aad<T>(&self) -> Option<&T>
|
pub(crate) fn req_dep_aad<T>(&self) -> Option<&T>
|
||||||
where
|
where
|
||||||
T: FromBytes + Sized,
|
T: FromBytes + Sized + Immutable + KnownLayout,
|
||||||
{
|
{
|
||||||
T::ref_from_prefix(self.req_dep_aad)
|
T::ref_from_prefix(self.req_dep_aad).map(|s| s.0).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the tag of this [`BinReqValues`].
|
/// Returns a reference to the tag of this [`BinReqValues`].
|
||||||
@@ -615,7 +615,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn req_hdr2() {
|
fn req_hdr2() {
|
||||||
let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC));
|
let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC));
|
||||||
let hdr_bin = hdr.as_bytes_mut();
|
let hdr_bin = hdr.as_mut_bytes();
|
||||||
let hdr_bin_exp = [
|
let hdr_bin_exp = [
|
||||||
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic
|
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic
|
||||||
0, 0, 2, 0, // vers
|
0, 0, 2, 0, // vers
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use openssl::pkey::{PKey, Public};
|
use openssl::pkey::{PKey, Public};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_size,
|
assert_size,
|
||||||
@@ -276,7 +276,7 @@ impl From<AttestationVersion> for RequestVersion {
|
|||||||
|
|
||||||
/// Authenticated additional Data of an [`AttestationRequest`]
|
/// Authenticated additional Data of an [`AttestationRequest`]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes, FromZeroes, FromBytes, Clone, Copy)]
|
#[derive(Debug, IntoBytes, FromBytes, Clone, Copy, Immutable, KnownLayout)]
|
||||||
pub struct AttestationAuthenticated {
|
pub struct AttestationAuthenticated {
|
||||||
flags: AttestationFlags,
|
flags: AttestationFlags,
|
||||||
mai: U32<BigEndian>,
|
mai: U32<BigEndian>,
|
||||||
@@ -310,7 +310,7 @@ impl AttestationAuthenticated {
|
|||||||
|
|
||||||
/// Attestation flags
|
/// Attestation flags
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Debug, AsBytes, FromZeroes, FromBytes, Clone, Copy)]
|
#[derive(Default, Debug, IntoBytes, FromBytes, Clone, Copy, Immutable)]
|
||||||
pub struct AttestationFlags(UvFlags);
|
pub struct AttestationFlags(UvFlags);
|
||||||
static_assert!(AttestationFlags::FLAG_TO_ADD_SIZE.len() < 64);
|
static_assert!(AttestationFlags::FLAG_TO_ADD_SIZE.len() < 64);
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ impl AttestationFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes)]
|
#[derive(Debug, IntoBytes, Immutable)]
|
||||||
struct ReqConfData {
|
struct ReqConfData {
|
||||||
meas_key: [u8; 64],
|
meas_key: [u8; 64],
|
||||||
nonce: AttNonce,
|
nonce: AttNonce,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use openssl::{
|
|||||||
pkey::{PKeyRef, Private},
|
pkey::{PKeyRef, Private},
|
||||||
};
|
};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use zerocopy::{AsBytes, BigEndian, U16, U32};
|
use zerocopy::{BigEndian, IntoBytes, U16, U32};
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::attest::AttestationRequest;
|
use crate::attest::AttestationRequest;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
use super::user_data::UserData;
|
use super::{guest_secret::ListableSecretHdr, user_data::UserData};
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_size,
|
assert_size,
|
||||||
crypto::{hkdf_rfc_5869, AeadEncryptionResult},
|
crypto::{hkdf_rfc_5869, AeadEncryptionResult},
|
||||||
@@ -17,12 +17,12 @@ use openssl::{
|
|||||||
md::Md,
|
md::Md,
|
||||||
pkey::{PKey, Private, Public},
|
pkey::{PKey, Private, Public},
|
||||||
};
|
};
|
||||||
use pv_core::{request::RequestVersion, secret::AddSecretMagic};
|
use pv_core::{request::RequestVersion, secret::AddSecretMagic, uv::SecretId};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::{Immutable, IntoBytes};
|
||||||
|
|
||||||
/// Authenticated data w/o user data
|
/// Authenticated data w/o user data
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, AsBytes)]
|
#[derive(Debug, Clone, Copy, IntoBytes, Immutable)]
|
||||||
struct ReqAuthData {
|
struct ReqAuthData {
|
||||||
flags: UvFlags,
|
flags: UvFlags,
|
||||||
boot_tags: BootHdrTags,
|
boot_tags: BootHdrTags,
|
||||||
@@ -283,6 +283,13 @@ impl AddSecretRequest {
|
|||||||
.map(|res| res.into_buf())
|
.map(|res| res.into_buf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a copy of the secret ID if any
|
||||||
|
pub fn bin_id(asrcb: &[u8]) -> Result<Option<SecretId>> {
|
||||||
|
AddSecretMagic::try_from_bytes(asrcb)?;
|
||||||
|
BinReqValues::get(asrcb)
|
||||||
|
.map(|req| req.req_dep_aad::<ListableSecretHdr>().map(|a| a.id.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a copy of the add secret request tag
|
/// Get a copy of the add secret request tag
|
||||||
pub fn bin_tag(asrcb: &[u8]) -> Result<Vec<u8>> {
|
pub fn bin_tag(asrcb: &[u8]) -> Result<Vec<u8>> {
|
||||||
AddSecretMagic::try_from_bytes(asrcb)?;
|
AddSecretMagic::try_from_bytes(asrcb)?;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Error, Result,
|
Error, Result,
|
||||||
};
|
};
|
||||||
use byteorder::BigEndian;
|
|
||||||
use openssl::{
|
use openssl::{
|
||||||
hash::MessageDigest,
|
hash::MessageDigest,
|
||||||
nid::Nid,
|
nid::Nid,
|
||||||
@@ -26,9 +25,11 @@ use openssl::{
|
|||||||
use pv_core::static_assert;
|
use pv_core::static_assert;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use zerocopy::{AsBytes, U16, U32};
|
use zerocopy::{BigEndian, KnownLayout};
|
||||||
|
use zerocopy::{FromBytes, Immutable, IntoBytes, U16, U32};
|
||||||
|
|
||||||
const ASSOC_SECRET_SIZE: usize = 32;
|
const ASSOC_SECRET_SIZE: usize = 32;
|
||||||
|
const CCK_SIZE: usize = 32;
|
||||||
/// Maximum size of a plain-text secret payload (8190)
|
/// Maximum size of a plain-text secret payload (8190)
|
||||||
pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
|
pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
|
||||||
static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
|
static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
|
||||||
@@ -65,6 +66,14 @@ pub enum GuestSecret {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
secret: Confidential<Vec<u8>>,
|
secret: Confidential<Vec<u8>>,
|
||||||
},
|
},
|
||||||
|
/// CCK update
|
||||||
|
///
|
||||||
|
/// Create CCK updates using [`GuestSecret::update_cck`]
|
||||||
|
UpdateCck {
|
||||||
|
/// Confidential actual CCK (32 bytes)
|
||||||
|
#[serde(skip)]
|
||||||
|
secret: Confidential<[u8; CCK_SIZE]>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! retr_constructor {
|
macro_rules! retr_constructor {
|
||||||
@@ -136,10 +145,19 @@ impl GuestSecret {
|
|||||||
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
||||||
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
||||||
|
|
||||||
|
/// Create a new [`GuestSecret::UpdateCck`].
|
||||||
|
///
|
||||||
|
/// * `secret` - New CCK.
|
||||||
|
pub fn update_cck(secret: [u8; CCK_SIZE]) -> Self {
|
||||||
|
Self::UpdateCck {
|
||||||
|
secret: secret.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Use the name as ID, do not hash it
|
/// Use the name as ID, do not hash it
|
||||||
pub fn no_hash_name(&mut self) {
|
pub fn no_hash_name(&mut self) {
|
||||||
match self {
|
match self {
|
||||||
Self::Null => (),
|
Self::Null | Self::UpdateCck { .. } => (),
|
||||||
Self::Association {
|
Self::Association {
|
||||||
name, ref mut id, ..
|
name, ref mut id, ..
|
||||||
}
|
}
|
||||||
@@ -155,6 +173,7 @@ impl GuestSecret {
|
|||||||
Self::Null => &[],
|
Self::Null => &[],
|
||||||
Self::Association { secret, .. } => secret.value().as_slice(),
|
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||||
Self::Retrievable { secret, .. } => secret.value(),
|
Self::Retrievable { secret, .. } => secret.value(),
|
||||||
|
Self::UpdateCck { secret, .. } => secret.value(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +181,8 @@ impl GuestSecret {
|
|||||||
pub(crate) fn auth(&self) -> SecretAuth {
|
pub(crate) fn auth(&self) -> SecretAuth {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Null => SecretAuth::Null,
|
Self::Null => SecretAuth::Null,
|
||||||
// Panic: every non null secret type is list-able -> no panic
|
Self::UpdateCck { .. } => SecretAuth::UpdateCck,
|
||||||
|
// Panic: other secret types are list-able -> no panic
|
||||||
listable => {
|
listable => {
|
||||||
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
|
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
|
||||||
}
|
}
|
||||||
@@ -176,6 +196,7 @@ impl GuestSecret {
|
|||||||
Self::Null => ListableSecretType::NULL,
|
Self::Null => ListableSecretType::NULL,
|
||||||
Self::Association { .. } => ListableSecretType::ASSOCIATION,
|
Self::Association { .. } => ListableSecretType::ASSOCIATION,
|
||||||
Self::Retrievable { kind, .. } => kind.into(),
|
Self::Retrievable { kind, .. } => kind.into(),
|
||||||
|
Self::UpdateCck { .. } => ListableSecretType::UPDATE_CCK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,13 +206,14 @@ impl GuestSecret {
|
|||||||
Self::Null => 0,
|
Self::Null => 0,
|
||||||
Self::Association { secret, .. } => secret.value().len() as u32,
|
Self::Association { secret, .. } => secret.value().len() as u32,
|
||||||
Self::Retrievable { secret, .. } => secret.value().len() as u32,
|
Self::Retrievable { secret, .. } => secret.value().len() as u32,
|
||||||
|
Self::UpdateCck { secret } => secret.value().len() as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ID of the secret type (if any)
|
/// Returns the ID of the secret type (if any)
|
||||||
fn id(&self) -> Option<SecretId> {
|
fn id(&self) -> Option<SecretId> {
|
||||||
match self {
|
match self {
|
||||||
Self::Null => None,
|
Self::Null | Self::UpdateCck { .. } => None,
|
||||||
Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
|
Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +239,7 @@ fn extend_to_multiple(mut key: Vec<u8>, multiple: usize) -> Confidential<Vec<u8>
|
|||||||
/// ```
|
/// ```
|
||||||
fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||||
let key_len = inp.value().len();
|
let key_len = inp.value().len();
|
||||||
if key_len > RetrieveCmd::MAX_SIZE {
|
if key_len > MAX_SIZE_PLAIN_PAYLOAD {
|
||||||
return Err(Error::RetrInvKey {
|
return Err(Error::RetrInvKey {
|
||||||
what: "key size",
|
what: "key size",
|
||||||
value: key_len.to_string(),
|
value: key_len.to_string(),
|
||||||
@@ -368,25 +390,51 @@ impl Display for GuestSecret {
|
|||||||
pub(crate) enum SecretAuth {
|
pub(crate) enum SecretAuth {
|
||||||
Null,
|
Null,
|
||||||
Listable(ListableSecretHdr),
|
Listable(ListableSecretHdr),
|
||||||
|
UpdateCck,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretAuth {
|
impl SecretAuth {
|
||||||
|
const NULL_HDR: NullSecretHdr = NullSecretHdr::new();
|
||||||
|
const UPDATE_CCK_HDR: UpdateCckHdr = UpdateCckHdr::new();
|
||||||
|
|
||||||
pub fn get(&self) -> &[u8] {
|
pub fn get(&self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
Self::Null => &[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
Self::Null => Self::NULL_HDR.as_bytes(),
|
||||||
Self::Listable(h) => h.as_bytes(),
|
Self::Listable(h) => h.as_bytes(),
|
||||||
|
Self::UpdateCck => Self::UPDATE_CCK_HDR.as_bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes)]
|
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||||
|
struct NullSecretHdr {
|
||||||
|
res0: u16,
|
||||||
|
kind: U16<BigEndian>,
|
||||||
|
secret_len: U32<BigEndian>,
|
||||||
|
res8: u64,
|
||||||
|
}
|
||||||
|
assert_size!(NullSecretHdr, 0x10);
|
||||||
|
|
||||||
|
impl NullSecretHdr {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
res0: 0,
|
||||||
|
kind: U16::new(ListableSecretType::NULL),
|
||||||
|
secret_len: U32::ZERO,
|
||||||
|
res8: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||||
pub(crate) struct ListableSecretHdr {
|
pub(crate) struct ListableSecretHdr {
|
||||||
res0: u16,
|
res0: u16,
|
||||||
kind: U16<BigEndian>,
|
kind: U16<BigEndian>,
|
||||||
secret_len: U32<BigEndian>,
|
secret_len: U32<BigEndian>,
|
||||||
res8: u64,
|
res8: u64,
|
||||||
id: SecretId,
|
pub(crate) id: SecretId,
|
||||||
}
|
}
|
||||||
assert_size!(ListableSecretHdr, 0x30);
|
assert_size!(ListableSecretHdr, 0x30);
|
||||||
|
|
||||||
@@ -402,6 +450,29 @@ impl ListableSecretHdr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, IntoBytes, Default, Immutable)]
|
||||||
|
struct UpdateCckHdr {
|
||||||
|
res0: u16,
|
||||||
|
kind: U16<BigEndian>,
|
||||||
|
secret_len: U32<BigEndian>,
|
||||||
|
res8: u64,
|
||||||
|
res10: [u8; 0x20],
|
||||||
|
}
|
||||||
|
assert_size!(UpdateCckHdr, 0x30);
|
||||||
|
|
||||||
|
impl UpdateCckHdr {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
res0: 0,
|
||||||
|
kind: U16::new(ListableSecretType::UPDATE_CCK),
|
||||||
|
secret_len: U32::new(CCK_SIZE as u32),
|
||||||
|
res8: 0,
|
||||||
|
res10: [0; 0x20],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
@@ -461,6 +532,16 @@ mod test {
|
|||||||
retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
|
retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
|
||||||
retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
|
retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_cck() {
|
||||||
|
let new_cck = [11; 32];
|
||||||
|
let req = GuestSecret::update_cck(new_cck);
|
||||||
|
let exp = GuestSecret::UpdateCck {
|
||||||
|
secret: new_cck.into(),
|
||||||
|
};
|
||||||
|
assert_eq!(req, exp);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plaintext_no_pad() {
|
fn plaintext_no_pad() {
|
||||||
let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
||||||
@@ -602,6 +683,24 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_cck_parse() {
|
||||||
|
let cck = GuestSecret::UpdateCck {
|
||||||
|
secret: [0; 32].into(),
|
||||||
|
};
|
||||||
|
assert_tokens(
|
||||||
|
&cck,
|
||||||
|
&[
|
||||||
|
Token::StructVariant {
|
||||||
|
name: "GuestSecret",
|
||||||
|
variant: "UpdateCck",
|
||||||
|
len: 0,
|
||||||
|
},
|
||||||
|
Token::StructVariantEnd,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn guest_secret_bin_null() {
|
fn guest_secret_bin_null() {
|
||||||
let gs = GuestSecret::Null;
|
let gs = GuestSecret::Null;
|
||||||
@@ -643,4 +742,17 @@ mod test {
|
|||||||
assert_eq!(exp, gs_bytes_auth);
|
assert_eq!(exp, gs_bytes_auth);
|
||||||
assert_eq!(&[2; 32], gs.confidential());
|
assert_eq!(&[2; 32], gs.confidential());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guest_secret_bin_cck() {
|
||||||
|
let gs = GuestSecret::UpdateCck {
|
||||||
|
secret: [2; 32].into(),
|
||||||
|
};
|
||||||
|
let gs_bytes_auth = gs.auth();
|
||||||
|
let mut exp = vec![0u8, 0, 0, 0x16, 0, 0, 0, 0x20];
|
||||||
|
exp.extend([0; 40]);
|
||||||
|
|
||||||
|
assert_eq!(exp, gs_bytes_auth.get());
|
||||||
|
assert_eq!(&[2; 32], gs.confidential());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use crate::{pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
|
use crate::{crypto::SymKeyType, pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
|
||||||
|
|
||||||
use byteorder::BigEndian;
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use pv_core::{
|
use pv_core::{
|
||||||
request::Confidential,
|
request::Confidential,
|
||||||
uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
|
uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
|
||||||
};
|
};
|
||||||
|
use zerocopy::BigEndian;
|
||||||
use zerocopy::{FromBytes, U16};
|
use zerocopy::{FromBytes, U16};
|
||||||
|
|
||||||
/// An IBM Protected Key
|
/// An IBM Protected Key
|
||||||
@@ -76,14 +76,16 @@ impl From<RetrieveCmd> for RetrievedSecret {
|
|||||||
// minimum size
|
// minimum size
|
||||||
let len = U16::<BigEndian>::read_from_prefix(key.value())
|
let len = U16::<BigEndian>::read_from_prefix(key.value())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
.0
|
||||||
.get() as usize;
|
.get() as usize;
|
||||||
|
|
||||||
// Test if the plain text secret has a size:
|
// Test if the plain text secret has a size:
|
||||||
// 1. len <= 8190
|
// 1. len <= 8190
|
||||||
// 2. first two bytes are max 15 less than buffer-size+2
|
// 2. first two bytes are max 15 less than buffer-size+2 i.e. smaller than the
|
||||||
|
// block length
|
||||||
// 3. bytes after len + 2 are zero
|
// 3. bytes after len + 2 are zero
|
||||||
match len <= MAX_SIZE_PLAIN_PAYLOAD
|
match len <= MAX_SIZE_PLAIN_PAYLOAD
|
||||||
&& key.value().len() - (len + 2) < 15
|
&& key.value().len() - (len + 2) < SymKeyType::AES_256_GCM_BLOCK_LEN
|
||||||
&& key.value()[len + 2..].iter().all(|c| *c == 0)
|
&& key.value()[len + 2..].iter().all(|c| *c == 0)
|
||||||
{
|
{
|
||||||
false => Self::Plaintext(key),
|
false => Self::Plaintext(key),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use openssl::hash::MessageDigest;
|
use openssl::hash::MessageDigest;
|
||||||
use openssl::nid::Nid;
|
use openssl::nid::Nid;
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U16};
|
use zerocopy::{BigEndian, FromBytes, IntoBytes, KnownLayout, U16};
|
||||||
|
|
||||||
/// User data.
|
/// User data.
|
||||||
///
|
///
|
||||||
@@ -59,7 +59,7 @@ pub(super) enum UserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes, FromBytes, FromZeroes)]
|
#[derive(Debug, IntoBytes, FromBytes, KnownLayout)]
|
||||||
struct EcUserData {
|
struct EcUserData {
|
||||||
data: [u8; 256],
|
data: [u8; 256],
|
||||||
signature: [u8; EC_SIGN_MAX_SIZE],
|
signature: [u8; EC_SIGN_MAX_SIZE],
|
||||||
@@ -175,7 +175,7 @@ impl UserData {
|
|||||||
// insert signature
|
// insert signature
|
||||||
if let UserDataType::SgnEcSECP521R1 = self.data_type() {
|
if let UserDataType::SgnEcSECP521R1 = self.data_type() {
|
||||||
// Panic: will not panic buffer is 512+ bytes long
|
// Panic: will not panic buffer is 512+ bytes long
|
||||||
let buf_ec = EcUserData::mut_from_prefix(&mut buf[user_data_offset..]).unwrap();
|
let (buf_ec, _) = EcUserData::mut_from_prefix(&mut buf[user_data_offset..]).unwrap();
|
||||||
buf_ec.set_signature(&sgn);
|
buf_ec.set_signature(&sgn);
|
||||||
} else {
|
} else {
|
||||||
// Panic: will not panic buffer is 512+ bytes long
|
// Panic: will not panic buffer is 512+ bytes long
|
||||||
@@ -328,12 +328,15 @@ impl VerifiedUserData {
|
|||||||
|
|
||||||
let (ret, sgn) = match kind {
|
let (ret, sgn) = match kind {
|
||||||
UserDataType::SgnEcSECP521R1 => {
|
UserDataType::SgnEcSECP521R1 => {
|
||||||
let EcUserData {
|
let (
|
||||||
data,
|
EcUserData {
|
||||||
signature,
|
data,
|
||||||
sgn_size,
|
signature,
|
||||||
..
|
sgn_size,
|
||||||
} = EcUserData::mut_from_prefix(buf).unwrap();
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
) = EcUserData::mut_from_prefix(buf).unwrap();
|
||||||
let data_len: usize = data.len();
|
let data_len: usize = data.len();
|
||||||
let data = data.to_vec();
|
let data = data.to_vec();
|
||||||
let mut signature = signature.to_vec();
|
let mut signature = signature.to_vec();
|
||||||
@@ -480,7 +483,7 @@ mod test {
|
|||||||
buf[..0x80].copy_from_slice(data);
|
buf[..0x80].copy_from_slice(data);
|
||||||
|
|
||||||
user_data.sign(&mut buf, 0).unwrap();
|
user_data.sign(&mut buf, 0).unwrap();
|
||||||
let buf_ec = EcUserData::mut_from(&mut buf).unwrap();
|
let buf_ec = EcUserData::mut_from_bytes(&mut buf).unwrap();
|
||||||
let EcUserData {
|
let EcUserData {
|
||||||
data,
|
data,
|
||||||
signature,
|
signature,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "s390_pv_core"
|
name = "s390_pv_core"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
@@ -19,9 +19,10 @@ workspace = true
|
|||||||
libc = "0.2.169"
|
libc = "0.2.169"
|
||||||
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
zerocopy = {version = "0.7", features = ["derive"]}
|
zerocopy = {version = "0.8", features = ["derive"]}
|
||||||
serde = { version = "1.0.217", features = ["derive"]}
|
serde = { version = "1.0.217", features = ["derive"]}
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
|
regex = "1.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_test = "1.0.177"
|
serde_test = "1.0.177"
|
||||||
|
|||||||
@@ -0,0 +1,468 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp. 2023
|
||||||
|
//
|
||||||
|
//! AP support functions
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utils::{pv_guest_bit_set, read_file_string, write_file},
|
||||||
|
Error, Result,
|
||||||
|
};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::fmt;
|
||||||
|
use std::thread;
|
||||||
|
use std::time;
|
||||||
|
|
||||||
|
const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap";
|
||||||
|
|
||||||
|
/// Regular expression for AP queue directories
|
||||||
|
pub const RE_QUEUE_DIR: &str = r"^([[:xdigit:]]{2})\.([[:xdigit:]]{4})$";
|
||||||
|
const RE_CARD_TYPE: &str = r"^CEX([3-8])([ACP])$";
|
||||||
|
const RE_EP11_MKVP: &str = r"WK\s+CUR:\s+(\S+)\s+(\S+)";
|
||||||
|
const RE_CCA_AES_MKVP: &str = r"AES\s+CUR:\s+(\S+)\s+(\S+)";
|
||||||
|
const RE_CCA_APKA_MKVP: &str = r"APKA\s+CUR:\s+(\S+)\s+(\S+)";
|
||||||
|
|
||||||
|
const SYS_BUS_AP_BIND_POLL_MS: u64 = 500;
|
||||||
|
const SYS_BUS_AP_BIND_TIMEOUT_MS: u64 = 10000;
|
||||||
|
|
||||||
|
const SYS_BUS_AP_ASSOC_POLL_MS: u64 = 500;
|
||||||
|
const SYS_BUS_AP_ASSOC_TIMEOUT_MS: u64 = 10000;
|
||||||
|
|
||||||
|
/// APQN mode
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ApqnMode {
|
||||||
|
/// Accelerator mode
|
||||||
|
Accel,
|
||||||
|
/// EP11 (Enterprise PKCS #11) coprocessor mode
|
||||||
|
Ep11,
|
||||||
|
/// Common Cryptographic Architecture (CCA) coprocessor mode
|
||||||
|
Cca,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info on an APQN configured for accelerator
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ApqnInfoAccel {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info on an APQN configured for EP11 coprocessor
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ApqnInfoEp11 {
|
||||||
|
/// Serial number of the Crypto Express adapter as a case-sensitive ASCII string
|
||||||
|
pub serialnr: String,
|
||||||
|
/// Master key verification pattern as hex string
|
||||||
|
pub mkvp: String, // may be an empty string if no WK set
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info on an APQN configured for CCA coprocessor
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct ApqnInfoCca {
|
||||||
|
/// Serial number of the Crypto Express adapter as a case-sensitive ASCII string
|
||||||
|
pub serialnr: String,
|
||||||
|
/// Master key verification pattern as hex string for AES
|
||||||
|
pub mkvp_aes: String, // may be an empty string if no MK set
|
||||||
|
/// Master key verification pattern as hex string for asymmetric public key algorithms
|
||||||
|
pub mkvp_apka: String, // may be an empty string if no MK set
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info for an APQN's mode
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ApqnInfo {
|
||||||
|
/// Info on an APQN configured for accelerator
|
||||||
|
Accel(ApqnInfoAccel),
|
||||||
|
/// Info on an APQN configured for EP11 coprocessor
|
||||||
|
Ep11(ApqnInfoEp11),
|
||||||
|
/// Info on an APQN configured for CCA coprocessor
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Cca(ApqnInfoCca),
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! parse_error {
|
||||||
|
($subject:expr, $content:expr) => {
|
||||||
|
Error::ParseError {
|
||||||
|
subject: $subject,
|
||||||
|
content: $content,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApqnInfo {
|
||||||
|
fn accel_info(_carddir: &str, _queuedir: &str) -> Result<Self> {
|
||||||
|
Ok(Self::Accel(ApqnInfoAccel {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cca_info(carddir: &str, queuedir: &str) -> Result<Self> {
|
||||||
|
let serialnr_str = read_file_string(format!("{carddir}/serialnr"), "serialnr")?;
|
||||||
|
let serialnr = serialnr_str.trim().to_string();
|
||||||
|
let mkvps = read_file_string(format!("{carddir}/{queuedir}/mkvps"), "mkvps")?;
|
||||||
|
let mut aes_mkvp = String::new();
|
||||||
|
let re_cca_aes_mkvp = Regex::new(RE_CCA_AES_MKVP).unwrap();
|
||||||
|
if !re_cca_aes_mkvp.is_match(&mkvps) {
|
||||||
|
return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps));
|
||||||
|
} else {
|
||||||
|
let caps = re_cca_aes_mkvp.captures(&mkvps).unwrap();
|
||||||
|
if caps.get(1).unwrap().as_str().to_lowercase() == "valid" {
|
||||||
|
aes_mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
||||||
|
if aes_mkvp.starts_with("0x") {
|
||||||
|
aes_mkvp = String::from(&aes_mkvp[2..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut apka_mkvp = String::new();
|
||||||
|
let re_cca_apka_mkvp = Regex::new(RE_CCA_APKA_MKVP).unwrap();
|
||||||
|
if !re_cca_apka_mkvp.is_match(&mkvps) {
|
||||||
|
return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps));
|
||||||
|
} else {
|
||||||
|
let caps = re_cca_apka_mkvp.captures(&mkvps).unwrap();
|
||||||
|
if caps.get(1).unwrap().as_str().to_lowercase() == "valid" {
|
||||||
|
apka_mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
||||||
|
if apka_mkvp.starts_with("0x") {
|
||||||
|
apka_mkvp = String::from(&apka_mkvp[2..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self::Cca(ApqnInfoCca {
|
||||||
|
serialnr,
|
||||||
|
mkvp_aes: aes_mkvp,
|
||||||
|
mkvp_apka: apka_mkvp,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ep11_info(carddir: &str, queuedir: &str) -> Result<Self> {
|
||||||
|
let serialnr_str = read_file_string(format!("{carddir}/serialnr"), "serialnr")?;
|
||||||
|
let serialnr = serialnr_str.trim().to_string();
|
||||||
|
let mkvps = read_file_string(format!("{carddir}/{queuedir}/mkvps"), "mkvps")?;
|
||||||
|
let mut mkvp = String::new();
|
||||||
|
let re_ep11_mkvp = Regex::new(RE_EP11_MKVP).unwrap();
|
||||||
|
if !re_ep11_mkvp.is_match(&mkvps) {
|
||||||
|
return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps));
|
||||||
|
} else {
|
||||||
|
let caps = re_ep11_mkvp.captures(&mkvps).unwrap();
|
||||||
|
if caps.get(1).unwrap().as_str().to_lowercase() == "valid" {
|
||||||
|
mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
||||||
|
if mkvp.starts_with("0x") {
|
||||||
|
mkvp = String::from(&mkvp[2..]);
|
||||||
|
}
|
||||||
|
if mkvp.len() > 32 {
|
||||||
|
mkvp = String::from(&mkvp[..32])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self::Ep11(ApqnInfoEp11 { serialnr, mkvp }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get mode-specific info
|
||||||
|
pub fn info(mode: &ApqnMode, carddir: &str, queuedir: &str) -> Result<Self> {
|
||||||
|
match mode {
|
||||||
|
ApqnMode::Accel => Self::accel_info(carddir, queuedir),
|
||||||
|
ApqnMode::Cca => Self::cca_info(carddir, queuedir),
|
||||||
|
ApqnMode::Ep11 => Self::ep11_info(carddir, queuedir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Apqn` encodes an adjunct processor queue number.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Apqn {
|
||||||
|
/// Name of the APQN
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub name: String,
|
||||||
|
/// Card number
|
||||||
|
pub card: u32,
|
||||||
|
/// Domain number
|
||||||
|
pub domain: u32,
|
||||||
|
/// CryptoExpress generation
|
||||||
|
pub gen: u32,
|
||||||
|
/// Mode that adapter is configured to use
|
||||||
|
pub mode: ApqnMode,
|
||||||
|
/// Mode-specific info
|
||||||
|
pub info: Option<ApqnInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Apqn {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
/// Create an `Apqn` struct from a CARD.DOMAIN-formatted APQN
|
||||||
|
/// string, such as `28.0014`. Will not populate `info` upon
|
||||||
|
/// failure to read it. Other failures to read required information
|
||||||
|
/// are treated as an Error.
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if the compilation of a static regular expression fails
|
||||||
|
/// or a regex capture that is already format-checked does not
|
||||||
|
/// parse, e.g. when the capture `([[:xdigit:]]{2})` does not
|
||||||
|
/// parse as hex string.
|
||||||
|
fn try_from(name: &str) -> Result<Self> {
|
||||||
|
let re_card_type = Regex::new(RE_CARD_TYPE).unwrap();
|
||||||
|
let re_queue_dir = Regex::new(RE_QUEUE_DIR).unwrap();
|
||||||
|
|
||||||
|
let caps = re_queue_dir
|
||||||
|
.captures(name)
|
||||||
|
.ok_or_else(|| parse_error!("queue".to_string(), name.to_string()))?;
|
||||||
|
let cardstr = caps.get(1).unwrap().as_str();
|
||||||
|
let card = u32::from_str_radix(cardstr, 16).unwrap();
|
||||||
|
let domstr = caps.get(2).unwrap().as_str();
|
||||||
|
let domain = u32::from_str_radix(domstr, 16).unwrap();
|
||||||
|
|
||||||
|
let path = format!("{PATH_SYS_DEVICES_AP}/card{cardstr}");
|
||||||
|
let card_type =
|
||||||
|
read_file_string(format!("{path}/type"), "card type").map(|s| s.trim().to_string())?;
|
||||||
|
let caps = re_card_type
|
||||||
|
.captures(&card_type)
|
||||||
|
.ok_or_else(|| parse_error!("card type".to_string(), card_type.to_string()))?;
|
||||||
|
let gen = caps.get(1).unwrap().as_str().parse::<u32>().unwrap();
|
||||||
|
let mode = match caps.get(2).unwrap().as_str().parse::<char>().unwrap() {
|
||||||
|
'A' => ApqnMode::Accel,
|
||||||
|
'C' => ApqnMode::Cca,
|
||||||
|
'P' => ApqnMode::Ep11,
|
||||||
|
_ => unreachable!("Code inconsistency between regex RE_CARD_TYPE and evaluation code."),
|
||||||
|
};
|
||||||
|
// the UV blocks requests to CCA cards within SE guest with AP
|
||||||
|
// pass-through support. However, filter out CCA cards as
|
||||||
|
// these cards cause hangs during information gathering.
|
||||||
|
if mode == ApqnMode::Cca && pv_guest_bit_set() {
|
||||||
|
return Err(Error::CcaSeIncompatible(card));
|
||||||
|
}
|
||||||
|
|
||||||
|
match read_file_string(format!("{path}/{name}/online"), "AP queue online status")
|
||||||
|
.map(|s| s.trim().parse::<i32>())
|
||||||
|
{
|
||||||
|
Ok(Ok(1)) => {}
|
||||||
|
_ => return Err(Error::ApOffline { card, domain }),
|
||||||
|
}
|
||||||
|
// For the MKVP and serialnr to fetch from the APQN within a SE
|
||||||
|
// guest the APQN needs to be bound to the guest. So if the APQN
|
||||||
|
// is not bound, temporarily bind it here until the info has
|
||||||
|
// been retrieved.
|
||||||
|
let mut tempbound = false;
|
||||||
|
if pv_guest_bit_set() {
|
||||||
|
let cbs = get_apqn_bind_state(card, domain)?;
|
||||||
|
if cbs == BindState::Unbound {
|
||||||
|
set_apqn_bind_state(card, domain, BindState::Bound)?;
|
||||||
|
tempbound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let info = ApqnInfo::info(&mode, &path, name).ok();
|
||||||
|
if tempbound {
|
||||||
|
set_apqn_bind_state(card, domain, BindState::Unbound)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Apqn {
|
||||||
|
name: name.to_string(),
|
||||||
|
card,
|
||||||
|
domain,
|
||||||
|
gen,
|
||||||
|
mode,
|
||||||
|
info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Apqn {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({},{})", self.card, self.domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Apqn {
|
||||||
|
/// Read bind state of the APQN.
|
||||||
|
pub fn bind_state(&self) -> Result<BindState> {
|
||||||
|
get_apqn_bind_state(self.card, self.domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set bind state of the APQN.
|
||||||
|
pub fn set_bind_state(&self, state: BindState) -> Result<()> {
|
||||||
|
set_apqn_bind_state(self.card, self.domain, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read associate state of the APQN.
|
||||||
|
pub fn associate_state(&self) -> Result<AssocState> {
|
||||||
|
get_apqn_associate_state(self.card, self.domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set associate state of the APQN.
|
||||||
|
pub fn set_associate_state(&self, state: AssocState) -> Result<()> {
|
||||||
|
set_apqn_associate_state(self.card, self.domain, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind state of an APQN
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum BindState {
|
||||||
|
/// APQN is bound
|
||||||
|
Bound,
|
||||||
|
/// APQN is unbound
|
||||||
|
Unbound,
|
||||||
|
/// APQN does not support bind
|
||||||
|
NotSupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query bind state for this APQN.
|
||||||
|
///
|
||||||
|
/// Returns a BindState enum as defined above or on failure
|
||||||
|
/// an error string. Does NOT print any error messages.
|
||||||
|
pub fn get_apqn_bind_state(card: u32, dom: u32) -> Result<BindState> {
|
||||||
|
let path = format!(
|
||||||
|
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
||||||
|
PATH_SYS_DEVICES_AP, card, card, dom
|
||||||
|
);
|
||||||
|
let state_str = read_file_string(path, "se_bind attribute")?;
|
||||||
|
let state = state_str.trim();
|
||||||
|
match state {
|
||||||
|
"bound" => Ok(BindState::Bound),
|
||||||
|
"unbound" => Ok(BindState::Unbound),
|
||||||
|
"-" => Ok(BindState::NotSupported),
|
||||||
|
_ => Err(Error::UnknownBindState(state.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind or unbind an APQN.
|
||||||
|
///
|
||||||
|
/// The action is determined by the BindState given in.
|
||||||
|
/// But of course only Bound and Unbound is supported - otherwise
|
||||||
|
/// this function panics!
|
||||||
|
/// The function actively loops over the bind state until
|
||||||
|
/// the requested bind state is reached or a timeout has
|
||||||
|
/// occurred (SYS_BUS_AP_BIND_TIMEOUT_MS).
|
||||||
|
/// On success () is returned, on failure an error string
|
||||||
|
/// is returned. Does NOT print any error messages.
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if a desired bind state other than Bound or Unbound is given.
|
||||||
|
pub fn set_apqn_bind_state(card: u32, dom: u32, state: BindState) -> Result<()> {
|
||||||
|
let ctx = "bind APQN";
|
||||||
|
let path = format!(
|
||||||
|
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
||||||
|
PATH_SYS_DEVICES_AP, card, card, dom
|
||||||
|
);
|
||||||
|
match state {
|
||||||
|
BindState::Bound => write_file(path, 1.to_string(), ctx),
|
||||||
|
BindState::Unbound => write_file(path, 0.to_string(), ctx),
|
||||||
|
_ => panic!("set_apqn_bind_state called with invalid BindState."),
|
||||||
|
}?;
|
||||||
|
let mut ms: u64 = 0;
|
||||||
|
loop {
|
||||||
|
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BIND_POLL_MS));
|
||||||
|
ms += SYS_BUS_AP_BIND_POLL_MS;
|
||||||
|
if ms >= SYS_BUS_AP_BIND_TIMEOUT_MS {
|
||||||
|
break Err(Error::Timeout(format!(
|
||||||
|
"setting APQN({card},{dom}) bind state"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let newstate = get_apqn_bind_state(card, dom)?;
|
||||||
|
if newstate == state {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Association state of an APQN
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum AssocState {
|
||||||
|
/// Associated with index
|
||||||
|
Associated(u16),
|
||||||
|
/// Association pending
|
||||||
|
AssociationPending,
|
||||||
|
/// Not associated
|
||||||
|
Unassociated,
|
||||||
|
/// APQN does not support association
|
||||||
|
NotSupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query association state for this APQN.
|
||||||
|
///
|
||||||
|
/// Returns an AssocState enum as defined above or on failure
|
||||||
|
/// an error string. Does NOT print any error messages.
|
||||||
|
pub fn get_apqn_associate_state(card: u32, dom: u32) -> Result<AssocState> {
|
||||||
|
let path = format!(
|
||||||
|
"{}/card{:02x}/{:02x}.{:04x}/se_associate",
|
||||||
|
PATH_SYS_DEVICES_AP, card, card, dom
|
||||||
|
);
|
||||||
|
let state_str = read_file_string(path, "se_associate attribute")?;
|
||||||
|
let state = state_str.trim();
|
||||||
|
match state.strip_prefix("associated ") {
|
||||||
|
Some(prefix) => Ok(AssocState::Associated(prefix.parse()?)),
|
||||||
|
_ => match state {
|
||||||
|
"association pending" => Ok(AssocState::AssociationPending),
|
||||||
|
"unassociated" => Ok(AssocState::Unassociated),
|
||||||
|
"-" => Ok(AssocState::NotSupported),
|
||||||
|
_ => Err(Error::UnknownAssocState(state.to_string())),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_apqn_associate_state_associate(card: u32, dom: u32, idx: u16) -> Result<()> {
|
||||||
|
let path = format!(
|
||||||
|
"{}/card{:02x}/{:02x}.{:04x}/se_associate",
|
||||||
|
PATH_SYS_DEVICES_AP, card, card, dom
|
||||||
|
);
|
||||||
|
write_file(path, idx.to_string(), "associate APQN")?;
|
||||||
|
let mut ms: u64 = 0;
|
||||||
|
loop {
|
||||||
|
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS));
|
||||||
|
ms += SYS_BUS_AP_ASSOC_POLL_MS;
|
||||||
|
if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS {
|
||||||
|
break Err(Error::Timeout(format!(
|
||||||
|
"setting APQN({card},{dom}) association index {idx} state",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
match get_apqn_associate_state(card, dom)? {
|
||||||
|
AssocState::Associated(i) if i == idx => return Ok(()),
|
||||||
|
AssocState::Associated(i) => {
|
||||||
|
return Err(Error::WrongAssocState {
|
||||||
|
card,
|
||||||
|
domain: dom,
|
||||||
|
desired: idx,
|
||||||
|
actual: i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_apqn_associate_state_unbind(card: u32, dom: u32) -> Result<()> {
|
||||||
|
let bindpath = format!(
|
||||||
|
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
||||||
|
PATH_SYS_DEVICES_AP, card, card, dom
|
||||||
|
);
|
||||||
|
write_file(bindpath, 0.to_string(), "unbind APQN")?;
|
||||||
|
let mut ms: u64 = 0;
|
||||||
|
loop {
|
||||||
|
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS));
|
||||||
|
ms += SYS_BUS_AP_ASSOC_POLL_MS;
|
||||||
|
if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS {
|
||||||
|
break Err(Error::Timeout(format!(
|
||||||
|
"setting APQN({card},{dom}) association unbind state",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let newstate = get_apqn_associate_state(card, dom)?;
|
||||||
|
if newstate == AssocState::Unassociated {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associate or Unassociate an APQN.
|
||||||
|
///
|
||||||
|
/// The action is determined by the AssocState given in.
|
||||||
|
/// But of course only Associated and Unassociated is supported
|
||||||
|
/// otherwise this function panics!
|
||||||
|
/// The function actively loops over the association state until
|
||||||
|
/// the requested state is reached or a timeout has
|
||||||
|
/// occurred (SYS_BUS_AP_ASSOC_TIMEOUT_MS).
|
||||||
|
/// The unassociate is in fact a unbind. So the code triggers
|
||||||
|
/// an unbind and then loops over the sysfs se_associate until
|
||||||
|
/// "unassociated" is reached.
|
||||||
|
/// On success () is returned, on failure an error string
|
||||||
|
/// is returned. Does NOT print any error messages.
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if a desired bind state other than Associated or
|
||||||
|
/// Unassociated is given.
|
||||||
|
pub fn set_apqn_associate_state(card: u32, dom: u32, state: AssocState) -> Result<()> {
|
||||||
|
match state {
|
||||||
|
AssocState::Associated(idx) => set_apqn_associate_state_associate(card, dom, idx),
|
||||||
|
AssocState::Unassociated => set_apqn_associate_state_unbind(card, dom),
|
||||||
|
_ => panic!("set_apqn_associate_state called with invalid AssocState."),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,36 @@ pub enum Error {
|
|||||||
)]
|
)]
|
||||||
InvalidRetrievableSecretType { id: SecretId, size: usize },
|
InvalidRetrievableSecretType { id: SecretId, size: usize },
|
||||||
|
|
||||||
|
#[error("Unknown bind state '{0}'.")]
|
||||||
|
UnknownBindState(String),
|
||||||
|
|
||||||
|
#[error("Unknown association state '{0}'.")]
|
||||||
|
UnknownAssocState(String),
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"APQN({card:02x},{domain:04x}) is associated with {actual} but it should be {desired}."
|
||||||
|
)]
|
||||||
|
WrongAssocState {
|
||||||
|
card: u32,
|
||||||
|
domain: u32,
|
||||||
|
desired: u16,
|
||||||
|
actual: u16,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Timeout on {0}.")]
|
||||||
|
Timeout(String),
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"CCA card {0:02x} cannot be used with Secure Execution, as this combination is unsupported"
|
||||||
|
)]
|
||||||
|
CcaSeIncompatible(u32),
|
||||||
|
|
||||||
|
#[error("APQN({card:02x}{domain:04x}) is offline.")]
|
||||||
|
ApOffline { card: u32, domain: u32 },
|
||||||
|
|
||||||
|
#[error("Failure parsing {subject} '{content}'.")]
|
||||||
|
ParseError { subject: String, content: String },
|
||||||
|
|
||||||
// errors from other crates
|
// errors from other crates
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|||||||
+29
-2
@@ -2,6 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023, 2024
|
// Copyright IBM Corp. 2023, 2024
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
mod apdevice;
|
||||||
mod confidential;
|
mod confidential;
|
||||||
mod error;
|
mod error;
|
||||||
mod macros;
|
mod macros;
|
||||||
@@ -20,9 +21,11 @@ pub mod attest {
|
|||||||
/// Miscellaneous functions and definitions
|
/// Miscellaneous functions and definitions
|
||||||
pub mod misc {
|
pub mod misc {
|
||||||
pub use crate::utils::pv_guest_bit_set;
|
pub use crate::utils::pv_guest_bit_set;
|
||||||
pub use crate::utils::{create_file, open_file, read_exact_file, read_file, write_file};
|
pub use crate::utils::{create_file, open_file};
|
||||||
pub use crate::utils::{decode_hex, parse_hex, to_u16, to_u32, try_parse_u128, try_parse_u64};
|
pub use crate::utils::{decode_hex, encode_hex, parse_hex};
|
||||||
pub use crate::utils::{read, write};
|
pub use crate::utils::{read, write};
|
||||||
|
pub use crate::utils::{read_exact_file, read_file, read_file_string, write_file};
|
||||||
|
pub use crate::utils::{to_u16, to_u32, try_parse_u128, try_parse_u64};
|
||||||
pub use crate::utils::{Flags, Lsb0Flags64, Msb0Flags64};
|
pub use crate::utils::{Flags, Lsb0Flags64, Msb0Flags64};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,5 +73,29 @@ pub mod secret {
|
|||||||
pub use crate::uvsecret::UserDataType;
|
pub use crate::uvsecret::UserDataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Functionalities for the AP bus
|
||||||
|
pub mod ap {
|
||||||
|
pub use crate::apdevice::Apqn;
|
||||||
|
pub use crate::apdevice::RE_QUEUE_DIR;
|
||||||
|
pub use crate::apdevice::{get_apqn_bind_state, set_apqn_bind_state};
|
||||||
|
/// AP modes
|
||||||
|
pub mod apqn_mode {
|
||||||
|
pub use crate::apdevice::ApqnMode::{self, *};
|
||||||
|
}
|
||||||
|
/// AP info for each state
|
||||||
|
pub mod apqn_info {
|
||||||
|
pub use crate::apdevice::ApqnInfo::{self, *};
|
||||||
|
pub use crate::apdevice::{ApqnInfoAccel, ApqnInfoCca, ApqnInfoEp11};
|
||||||
|
}
|
||||||
|
/// AP bind states
|
||||||
|
pub mod bind_state {
|
||||||
|
pub use crate::apdevice::BindState::{self, *};
|
||||||
|
}
|
||||||
|
/// AP association states
|
||||||
|
pub mod assoc_state {
|
||||||
|
pub use crate::apdevice::AssocState::{self, *};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal definitions/ imports
|
// Internal definitions/ imports
|
||||||
const PAGESIZE: usize = 0x1000;
|
const PAGESIZE: usize = 0x1000;
|
||||||
|
|||||||
+37
-22
@@ -7,11 +7,11 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U64};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, U64};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
macros::{bail_spec, file_error},
|
macros::{bail_spec, file_error},
|
||||||
Error, FileAccessErrorType, FileIoErrorType, Result,
|
Error, FileAccessErrorType, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait that describes bitflags, represented by `T`.
|
/// Trait that describes bitflags, represented by `T`.
|
||||||
@@ -34,7 +34,7 @@ pub trait Flags<T>: From<T> + for<'a> From<&'a T> {
|
|||||||
///
|
///
|
||||||
/// Wraps an u64 to set/get individual bits
|
/// Wraps an u64 to set/get individual bits
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, Default, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Debug, Clone, Copy, Default, IntoBytes, FromBytes, Eq, PartialEq, Immutable)]
|
||||||
pub struct Msb0Flags64(U64<BigEndian>);
|
pub struct Msb0Flags64(U64<BigEndian>);
|
||||||
impl Flags<u64> for Msb0Flags64 {
|
impl Flags<u64> for Msb0Flags64 {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@@ -82,7 +82,7 @@ impl From<Msb0Flags64> for u64 {
|
|||||||
///
|
///
|
||||||
/// Wraps an u64 to set/get individual bits
|
/// Wraps an u64 to set/get individual bits
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, Default, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Debug, Clone, Copy, Default, IntoBytes, FromBytes, Immutable)]
|
||||||
pub struct Lsb0Flags64(U64<BigEndian>);
|
pub struct Lsb0Flags64(U64<BigEndian>);
|
||||||
impl Flags<u64> for Lsb0Flags64 {
|
impl Flags<u64> for Lsb0Flags64 {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@@ -262,6 +262,17 @@ pub fn read_exact_file<P: AsRef<Path>, const COUNT: usize>(
|
|||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read content from a file as string and add context in case of an error
|
||||||
|
///
|
||||||
|
/// * `path` - Path to file
|
||||||
|
/// * `ctx` - Error context string in case of an error
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Passes through any kind of error `std::fs::read` produces
|
||||||
|
pub fn read_file_string<P: AsRef<Path>>(path: P, ctx: &str) -> Result<String> {
|
||||||
|
std::fs::read_to_string(&path).map_err(|e| file_error!(Read, ctx, path, e))
|
||||||
|
}
|
||||||
|
|
||||||
/// Read content from a file and add context in case of an error
|
/// Read content from a file and add context in case of an error
|
||||||
///
|
///
|
||||||
/// * `path` - Path to file
|
/// * `path` - Path to file
|
||||||
@@ -284,12 +295,8 @@ pub fn read_file<P: AsRef<Path>>(path: P, ctx: &str) -> Result<Vec<u8>> {
|
|||||||
/// Passes through any kind of error `std::fs::read` produces
|
/// Passes through any kind of error `std::fs::read` produces
|
||||||
pub fn read<R: Read, P: AsRef<Path>>(rd: &mut R, path: P, ctx: &str) -> Result<Vec<u8>> {
|
pub fn read<R: Read, P: AsRef<Path>>(rd: &mut R, path: P, ctx: &str) -> Result<Vec<u8>> {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
rd.read_to_end(&mut buf).map_err(|e| Error::FileIo {
|
rd.read_to_end(&mut buf)
|
||||||
ty: FileIoErrorType::Write,
|
.map_err(|e| file_error!(Read, ctx, path, e))?;
|
||||||
ctx: ctx.to_string(),
|
|
||||||
path: path.as_ref().to_path_buf(),
|
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +309,7 @@ pub fn read<R: Read, P: AsRef<Path>>(rd: &mut R, path: P, ctx: &str) -> Result<V
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Passes through any kind of error `std::fs::write` produces
|
/// Passes through any kind of error `std::fs::write` produces
|
||||||
pub fn write_file<D: AsRef<[u8]>, P: AsRef<Path>>(path: P, data: D, ctx: &str) -> Result<()> {
|
pub fn write_file<D: AsRef<[u8]>, P: AsRef<Path>>(path: P, data: D, ctx: &str) -> Result<()> {
|
||||||
std::fs::write(path.as_ref(), data.as_ref()).map_err(|e| Error::FileIo {
|
std::fs::write(path.as_ref(), data.as_ref()).map_err(|e| file_error!(Write, ctx, path, e))
|
||||||
ty: FileIoErrorType::Write,
|
|
||||||
ctx: ctx.to_string(),
|
|
||||||
path: path.as_ref().to_path_buf(),
|
|
||||||
source: e,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write content to a [`std::io::Write`] and add context in case of an error
|
/// Write content to a [`std::io::Write`] and add context in case of an error
|
||||||
@@ -324,12 +326,8 @@ pub fn write<D: AsRef<[u8]>, P: AsRef<Path>, W: Write>(
|
|||||||
path: P,
|
path: P,
|
||||||
ctx: &str,
|
ctx: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
wr.write_all(data.as_ref()).map_err(|e| Error::FileIo {
|
wr.write_all(data.as_ref())
|
||||||
ty: FileIoErrorType::Write,
|
.map_err(|e| file_error!(Write, ctx, path, e))
|
||||||
ctx: ctx.to_string(),
|
|
||||||
path: path.as_ref().to_path_buf(),
|
|
||||||
source: e,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! usize_to_ui {
|
macro_rules! usize_to_ui {
|
||||||
@@ -355,6 +353,15 @@ usize_to_ui! {
|
|||||||
#[doc = r"u16"]
|
#[doc = r"u16"]
|
||||||
=> u16, to_u16}
|
=> u16, to_u16}
|
||||||
|
|
||||||
|
/// Converts the u8 slice into (lowercase) hexstring
|
||||||
|
pub fn encode_hex<S: AsRef<[u8]>>(s: S) -> String {
|
||||||
|
let slice = s.as_ref();
|
||||||
|
let string = String::with_capacity(2 * slice.len());
|
||||||
|
slice
|
||||||
|
.iter()
|
||||||
|
.fold(string, |acc, e| acc + &format!("{e:02x}"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts the hexstring into a byte vector.
|
/// Converts the hexstring into a byte vector.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -504,6 +511,14 @@ mod tests {
|
|||||||
fn lsb_flags_unset_panic() {
|
fn lsb_flags_unset_panic() {
|
||||||
Lsb0Flags64::default().unset_bit(64)
|
Lsb0Flags64::default().unset_bit(64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encode_hex() {
|
||||||
|
let arr = [0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0];
|
||||||
|
let exp = "123456acbef0";
|
||||||
|
assert_eq!(super::encode_hex(arr), exp);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_hex() {
|
fn parse_hex() {
|
||||||
let s = "123456acbef0";
|
let s = "123456acbef0";
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use crate::{request::MagicValue, Error};
|
use crate::{request::MagicValue, Error};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
|
||||||
use zerocopy::U32;
|
use zerocopy::U32;
|
||||||
|
use zerocopy::{BigEndian, ByteOrder};
|
||||||
|
|
||||||
/// The magic value used to identify an attestation request
|
/// The magic value used to identify an attestation request
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
use super::{ffi, AttestationUserData, ConfigUid, UvCmd};
|
use super::{ffi, AttestationUserData, ConfigUid, UvCmd};
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use zerocopy::{AsBytes, FromZeroes};
|
use zerocopy::{FromZeros, IntoBytes};
|
||||||
|
|
||||||
/// _Retrieve Attestation Measurement_ UVC
|
/// _Retrieve Attestation Measurement_ UVC
|
||||||
///
|
///
|
||||||
@@ -222,7 +222,7 @@ impl UvCmd for AttestationCmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn data(&mut self) -> Option<&mut [u8]> {
|
fn data(&mut self) -> Option<&mut [u8]> {
|
||||||
Some(self.uvio_attest.as_bytes_mut())
|
Some(self.uvio_attest.as_mut_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::{assert_size, static_assert};
|
use crate::{assert_size, static_assert};
|
||||||
use zerocopy::{AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{FromBytes, IntoBytes};
|
||||||
|
|
||||||
pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
|
pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
|
||||||
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
|
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
|
||||||
@@ -65,7 +65,7 @@ assert_size!(uvio_ioctl_cb, 0x40);
|
|||||||
/// Note that bit 0 (`UVIO_IOCTL_UVDEV_INFO_NR`) is always zero for `supp_uv_cmds`
|
/// Note that bit 0 (`UVIO_IOCTL_UVDEV_INFO_NR`) is always zero for `supp_uv_cmds`
|
||||||
/// as there is no corresponding UV-call.
|
/// as there is no corresponding UV-call.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Debug, Copy, Clone, IntoBytes, FromBytes)]
|
||||||
pub struct uvio_uvdev_info {
|
pub struct uvio_uvdev_info {
|
||||||
pub supp_uvio_cmds: u64,
|
pub supp_uvio_cmds: u64,
|
||||||
pub supp_uv_cmds: u64,
|
pub supp_uv_cmds: u64,
|
||||||
@@ -95,7 +95,7 @@ pub const UVIO_ATT_UID_LEN: usize = 0x10;
|
|||||||
///
|
///
|
||||||
/// All numbers are in big-endian!
|
/// All numbers are in big-endian!
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Debug, IntoBytes, FromBytes)]
|
||||||
pub struct uvio_attest {
|
pub struct uvio_attest {
|
||||||
pub arcb_addr: u64, // in
|
pub arcb_addr: u64, // in
|
||||||
pub meas_addr: u64, // out
|
pub meas_addr: u64, // out
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use zerocopy::{AsBytes, FromZeroes};
|
use zerocopy::{FromZeros, IntoBytes};
|
||||||
|
|
||||||
/// Information of supported functions by the uvdevice
|
/// Information of supported functions by the uvdevice
|
||||||
///
|
///
|
||||||
@@ -74,7 +74,7 @@ impl UvCmd for uvio_uvdev_info {
|
|||||||
const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_UVDEV_INFO_NR;
|
const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_UVDEV_INFO_NR;
|
||||||
|
|
||||||
fn data(&mut self) -> Option<&mut [u8]> {
|
fn data(&mut self) -> Option<&mut [u8]> {
|
||||||
Some(self.as_bytes_mut())
|
Some(self.as_mut_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rc_fmt(&self, _: u16, _: u16) -> Option<&'static str> {
|
fn rc_fmt(&self, _: u16, _: u16) -> Option<&'static str> {
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for RetrievableSecret {
|
|||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct RetrSecretVisitor;
|
struct RetrSecretVisitor;
|
||||||
impl<'de> serde::de::Visitor<'de> for RetrSecretVisitor {
|
impl serde::de::Visitor<'_> for RetrSecretVisitor {
|
||||||
type Value = RetrievableSecret;
|
type Value = RetrievableSecret;
|
||||||
|
|
||||||
fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::{io::Read, mem::size_of_val};
|
use std::{io::Read, mem::size_of_val};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::IntoBytes;
|
||||||
|
|
||||||
/// _List Secrets_ Ultravisor command.
|
/// _List Secrets_ Ultravisor command.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use crate::{
|
|||||||
uvdevice::UvCmd,
|
uvdevice::UvCmd,
|
||||||
Error, Result,
|
Error, Result,
|
||||||
};
|
};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
@@ -19,13 +18,14 @@ use std::{
|
|||||||
slice::Iter,
|
slice::Iter,
|
||||||
vec::IntoIter,
|
vec::IntoIter,
|
||||||
};
|
};
|
||||||
use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
|
use zerocopy::{BigEndian, ByteOrder};
|
||||||
|
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, U16, U32};
|
||||||
|
|
||||||
/// The 32 byte long ID of an UV secret
|
/// The 32 byte long ID of an UV secret
|
||||||
///
|
///
|
||||||
/// (de)serializes itself in/from a hex-string
|
/// (de)serializes itself in/from a hex-string
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone, Default)]
|
#[derive(PartialEq, Eq, IntoBytes, FromBytes, Debug, Clone, Default, Immutable, KnownLayout)]
|
||||||
pub struct SecretId([u8; Self::ID_SIZE]);
|
pub struct SecretId([u8; Self::ID_SIZE]);
|
||||||
assert_size!(SecretId, SecretId::ID_SIZE);
|
assert_size!(SecretId, SecretId::ID_SIZE);
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
|
|||||||
|
|
||||||
/// A secret in a [`SecretList`]
|
/// A secret in a [`SecretList`]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, IntoBytes, FromBytes, Serialize, Immutable)]
|
||||||
pub struct SecretEntry {
|
pub struct SecretEntry {
|
||||||
#[serde(serialize_with = "ser_u16")]
|
#[serde(serialize_with = "ser_u16")]
|
||||||
index: U16<BigEndian>,
|
index: U16<BigEndian>,
|
||||||
@@ -183,7 +183,7 @@ impl SecretEntry {
|
|||||||
/// The slice is guaranteed to be 32 bytes long.
|
/// The slice is guaranteed to be 32 bytes long.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use s390_pv_core::uv::SecretEntry;
|
/// # use s390_pv_core::uv::SecretEntry;
|
||||||
/// # use zerocopy::FromZeroes;
|
/// # use zerocopy::FromZeros;
|
||||||
/// # let secr = SecretEntry::new_zeroed();
|
/// # let secr = SecretEntry::new_zeroed();
|
||||||
/// # assert_eq!(secr.id().len(), 32);
|
/// # assert_eq!(secr.id().len(), 32);
|
||||||
/// ```
|
/// ```
|
||||||
@@ -211,7 +211,9 @@ impl Display for SecretEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, FromBytes, AsBytes, FromZeroes, Clone, PartialEq, Eq, Default, Serialize)]
|
#[derive(
|
||||||
|
Debug, FromBytes, IntoBytes, Clone, PartialEq, Eq, Default, Serialize, Immutable, KnownLayout,
|
||||||
|
)]
|
||||||
struct SecretListHdr {
|
struct SecretListHdr {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
num_secrets_stored: U16<BigEndian>,
|
num_secrets_stored: U16<BigEndian>,
|
||||||
@@ -325,7 +327,8 @@ impl SecretList {
|
|||||||
|
|
||||||
/// Encodes the list in the same binary format the UV would do
|
/// Encodes the list in the same binary format the UV would do
|
||||||
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||||
w.write_all(self.hdr.as_bytes())?;
|
let hdr = self.hdr.as_bytes();
|
||||||
|
w.write_all(hdr)?;
|
||||||
for secret in &self.secrets {
|
for secret in &self.secrets {
|
||||||
w.write_all(secret.as_bytes())?;
|
w.write_all(secret.as_bytes())?;
|
||||||
}
|
}
|
||||||
@@ -336,14 +339,14 @@ impl SecretList {
|
|||||||
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
|
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
|
||||||
let mut buf = [0u8; size_of::<SecretListHdr>()];
|
let mut buf = [0u8; size_of::<SecretListHdr>()];
|
||||||
r.read_exact(&mut buf)?;
|
r.read_exact(&mut buf)?;
|
||||||
let hdr = SecretListHdr::ref_from(&buf).unwrap();
|
let hdr = SecretListHdr::ref_from_bytes(&buf).unwrap();
|
||||||
|
|
||||||
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
|
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
|
||||||
let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
|
let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
|
||||||
for _ in 0..hdr.num_secrets_stored.get() {
|
for _ in 0..hdr.num_secrets_stored.get() {
|
||||||
r.read_exact(&mut buf)?;
|
r.read_exact(&mut buf)?;
|
||||||
// cannot fail. buffer has the same size as the secret entry
|
// cannot fail. buffer has the same size as the secret entry
|
||||||
let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
|
let secr = SecretEntry::read_from_bytes(buf.as_slice()).unwrap();
|
||||||
v.push(secr);
|
v.push(secr);
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -395,6 +398,7 @@ pub enum ListableSecretType {
|
|||||||
///
|
///
|
||||||
/// 0 is reserved
|
/// 0 is reserved
|
||||||
/// 1 is Null secret, with no id and not list-able
|
/// 1 is Null secret, with no id and not list-able
|
||||||
|
/// 21 is Update CCK secret, with no id and not list-able
|
||||||
Invalid(u16),
|
Invalid(u16),
|
||||||
/// Unknown secret type
|
/// Unknown secret type
|
||||||
Unknown(u16),
|
Unknown(u16),
|
||||||
@@ -433,6 +437,8 @@ impl ListableSecretType {
|
|||||||
pub const ECDSA_ED25519_KEY: u16 = 0x0014;
|
pub const ECDSA_ED25519_KEY: u16 = 0x0014;
|
||||||
/// UV secret-type id for an ed448-private-key secret
|
/// UV secret-type id for an ed448-private-key secret
|
||||||
pub const ECDSA_ED448_KEY: u16 = 0x0015;
|
pub const ECDSA_ED448_KEY: u16 = 0x0015;
|
||||||
|
/// UV secret-type id for a new customer communication key
|
||||||
|
pub const UPDATE_CCK: u16 = 0x0016;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ListableSecretType {
|
impl Display for ListableSecretType {
|
||||||
@@ -479,6 +485,7 @@ impl From<u16> for ListableSecretType {
|
|||||||
Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
|
Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
|
||||||
Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
|
Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
|
||||||
Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
|
Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
|
||||||
|
Self::UPDATE_CCK => Self::Invalid(Self::UPDATE_CCK),
|
||||||
n => Self::Unknown(n),
|
n => Self::Unknown(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,10 +545,13 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
use std::io::{BufReader, BufWriter, Cursor};
|
||||||
|
|
||||||
use serde_test::{assert_ser_tokens, assert_tokens, Token};
|
use serde_test::{assert_ser_tokens, assert_tokens, Token};
|
||||||
|
use zerocopy::FromZeros;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io::{BufReader, BufWriter, Cursor};
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_secret_entry() {
|
fn dump_secret_entry() {
|
||||||
const EXP: &[u8] = &[
|
const EXP: &[u8] = &[
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ use crate::{
|
|||||||
request::{MagicValue, RequestMagic},
|
request::{MagicValue, RequestMagic},
|
||||||
Error, Result,
|
Error, Result,
|
||||||
};
|
};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::ByteOrder;
|
||||||
use std::{fmt::Display, mem::size_of};
|
use std::{fmt::Display, mem::size_of};
|
||||||
use zerocopy::{AsBytes, U16};
|
use zerocopy::{BigEndian, Immutable, IntoBytes, U16};
|
||||||
|
|
||||||
/// The magic value used to identify an `AddSecretRequest`.
|
/// The magic value used to identify an `AddSecretRequest`.
|
||||||
///
|
///
|
||||||
@@ -25,7 +25,7 @@ use zerocopy::{AsBytes, U16};
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, AsBytes)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable)]
|
||||||
pub struct AddSecretMagic {
|
pub struct AddSecretMagic {
|
||||||
magic: [u8; 6], // [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D]
|
magic: [u8; 6], // [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D]
|
||||||
kind: U16<BigEndian>,
|
kind: U16<BigEndian>,
|
||||||
@@ -56,7 +56,7 @@ impl AddSecretMagic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Panic: Will not panic, bytes is at least 8 elements long
|
// Panic: Will not panic, bytes is at least 8 elements long
|
||||||
let kind = BigEndian::read_u16(&bytes[6..8]);
|
let kind = byteorder::BigEndian::read_u16(&bytes[6..8]);
|
||||||
let kind = UserDataType::try_from(kind)?;
|
let kind = UserDataType::try_from(kind)?;
|
||||||
Ok(Self::from(kind))
|
Ok(Self::from(kind))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "pvapconfig"
|
name = "pvapconfig"
|
||||||
description = "A tool to configure the AP resources inside a SE guest based on UV secrets and an AP config file."
|
description = "A tool to configure the AP resources inside a SE guest based on UV secrets and an AP config file."
|
||||||
authors = ["Harald Freudenberger <freude@linux.ibm.com>"]
|
authors = ["Harald Freudenberger <freude@linux.ibm.com>"]
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
@@ -12,7 +12,7 @@ clap = { version ="4.5", features = ["derive", "wrap_help"]}
|
|||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
openssl = { version = "0.10.70" }
|
openssl = { version = "0.10.70" }
|
||||||
pv_core = { path = "../pv_core", package = "s390_pv_core"}
|
pv_core = { path = "../pv_core", package = "s390_pv_core"}
|
||||||
rand = "0.8"
|
rand = "0.9"
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ All AP config entries need to be satisfied to have pvapconfig
|
|||||||
terminate with success. Without this option one applied AP config
|
terminate with success. Without this option one applied AP config
|
||||||
entry is enough to meet the expectations.
|
entry is enough to meet the expectations.
|
||||||
.TP 8
|
.TP 8
|
||||||
|
.B \-\-unbind
|
||||||
|
Unbind all available APQNs.
|
||||||
|
.TP 8
|
||||||
.B \-v, \-\-verbose
|
.B \-v, \-\-verbose
|
||||||
Print out informational messages about what pvapconfig is actually
|
Print out informational messages about what pvapconfig is actually
|
||||||
doing.
|
doing.
|
||||||
@@ -104,7 +107,7 @@ entries. Each entry may hold this information:
|
|||||||
- mode: AP queue mode information, required, either "EP11" or "Accel".
|
- mode: AP queue mode information, required, either "EP11" or "Accel".
|
||||||
.TP 2
|
.TP 2
|
||||||
- mkvp: AP queue Master Key Verification Pattern (MKVP), required for
|
- mkvp: AP queue Master Key Verification Pattern (MKVP), required for
|
||||||
EP11, hex string optional prepented with 0x. The MKVP hex string value
|
EP11, hex string optional prepended with 0x. The MKVP hex string value
|
||||||
may hold either 16 bytes (32 hex characters) or 32 bytes (64 hex
|
may hold either 16 bytes (32 hex characters) or 32 bytes (64 hex
|
||||||
characters) but only the leftmost 16 bytes hold MKVP information and
|
characters) but only the leftmost 16 bytes hold MKVP information and
|
||||||
thus the rest is ignored.
|
thus the rest is ignored.
|
||||||
@@ -117,7 +120,7 @@ lowercase character(s) count different.
|
|||||||
"CEX7" or "CEX8" for Accelerator, string "CEC8" for EP11, optional. If
|
"CEX7" or "CEX8" for Accelerator, string "CEC8" for EP11, optional. If
|
||||||
given specifies the minimal accepted Crypto card generation.
|
given specifies the minimal accepted Crypto card generation.
|
||||||
.TP 2
|
.TP 2
|
||||||
- secretid: Secret id, hex string with optional 0x prepented, required
|
- secretid: Secret id, hex string with optional 0x prepended, required
|
||||||
for EP11, ignored for Accel. Details see the following text.
|
for EP11, ignored for Accel. Details see the following text.
|
||||||
.TP 2
|
.TP 2
|
||||||
- name: ASCII string, optional, but see details below.
|
- name: ASCII string, optional, but see details below.
|
||||||
|
|||||||
+46
-547
@@ -6,33 +6,22 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use crate::helper::*;
|
use crate::helper::*;
|
||||||
use regex::Regex;
|
use pv_core::ap::*;
|
||||||
use std::fmt;
|
use pv_core::misc::read_file_string;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
|
const RE_CARD_DIR: &str = r"^card([[:xdigit:]]{2})$";
|
||||||
|
|
||||||
const PATH_SYS_BUS_AP: &str = "/sys/bus/ap";
|
const PATH_SYS_BUS_AP: &str = "/sys/bus/ap";
|
||||||
const PATH_SYS_BUS_AP_FEATURES: &str = "/sys/bus/ap/features";
|
const PATH_SYS_BUS_AP_FEATURES: &str = "/sys/bus/ap/features";
|
||||||
const PATH_SYS_BUS_AP_BINDINGS: &str = "/sys/bus/ap/bindings";
|
const PATH_SYS_BUS_AP_BINDINGS: &str = "/sys/bus/ap/bindings";
|
||||||
const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap";
|
const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap";
|
||||||
|
|
||||||
const RE_CARD_DIR: &str = r"^card([[:xdigit:]]{2})$";
|
|
||||||
const RE_QUEUE_DIR: &str = r"^([[:xdigit:]]{2})\.([[:xdigit:]]{4})$";
|
|
||||||
const RE_CARD_TYPE: &str = r"^CEX([3-8])([ACP])$";
|
|
||||||
const RE_EP11_MKVP: &str = r"WK\s+CUR:\s+(\S+)\s+(\S+)";
|
|
||||||
const RE_CCA_AES_MKVP: &str = r"AES\s+CUR:\s+(\S+)\s+(\S+)";
|
|
||||||
const RE_CCA_APKA_MKVP: &str = r"APKA\s+CUR:\s+(\S+)\s+(\S+)";
|
|
||||||
|
|
||||||
const SYS_BUS_AP_BINDINGS_POLL_MS: u64 = 500;
|
const SYS_BUS_AP_BINDINGS_POLL_MS: u64 = 500;
|
||||||
|
|
||||||
const SYS_BUS_AP_BIND_POLL_MS: u64 = 500;
|
|
||||||
const SYS_BUS_AP_BIND_TIMEOUT_MS: u64 = 10000;
|
|
||||||
|
|
||||||
const SYS_BUS_AP_ASSOC_POLL_MS: u64 = 500;
|
|
||||||
const SYS_BUS_AP_ASSOC_TIMEOUT_MS: u64 = 10000;
|
|
||||||
|
|
||||||
/// Check if AP bus support is available.
|
/// Check if AP bus support is available.
|
||||||
/// Returns Result with Ok(()) or Err(failurestring).
|
/// Returns Result with Ok(()) or Err(failurestring).
|
||||||
pub fn check_ap_bus_support() -> Result<(), String> {
|
pub fn check_ap_bus_support() -> Result<(), String> {
|
||||||
@@ -49,14 +38,8 @@ pub fn check_ap_bus_support() -> Result<(), String> {
|
|||||||
/// When APSB support is available returns Result
|
/// When APSB support is available returns Result
|
||||||
/// with Ok(()) or otherwise Err(failurestring).
|
/// with Ok(()) or otherwise Err(failurestring).
|
||||||
pub fn ap_bus_has_apsb_support() -> Result<(), String> {
|
pub fn ap_bus_has_apsb_support() -> Result<(), String> {
|
||||||
if !Path::new(PATH_SYS_BUS_AP_FEATURES).is_file() {
|
let features =
|
||||||
return Err(format!(
|
read_file_string(PATH_SYS_BUS_AP_FEATURES, "AP bus features").map_err(|e| e.to_string())?;
|
||||||
"AP bus features support missing (file {PATH_SYS_BUS_AP_FEATURES} does not exist)."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let features = sysfs_read_string(PATH_SYS_BUS_AP_FEATURES).map_err(|err| {
|
|
||||||
format!("Failure reading AP bus features from {PATH_SYS_BUS_AP_FEATURES} ({err:?}).")
|
|
||||||
})?;
|
|
||||||
match features.find("APSB") {
|
match features.find("APSB") {
|
||||||
Some(_) => Ok(()),
|
Some(_) => Ok(()),
|
||||||
None => Err("Missing AP bus feature APSB (SE AP pass-through not enabled ?).".to_string()),
|
None => Err("Missing AP bus feature APSB (SE AP pass-through not enabled ?).".to_string()),
|
||||||
@@ -79,17 +62,14 @@ pub fn ap_bus_has_apsb_support() -> Result<(), String> {
|
|||||||
pub fn wait_for_ap_bus_bindings_complete() -> bool {
|
pub fn wait_for_ap_bus_bindings_complete() -> bool {
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
loop {
|
loop {
|
||||||
match sysfs_read_string(PATH_SYS_BUS_AP_BINDINGS) {
|
match read_file_string(PATH_SYS_BUS_AP_BINDINGS, "AP bus bindings") {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
if s.contains("complete") {
|
if s.contains("complete") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!(
|
eprintln!("{err}");
|
||||||
"Failure reading AP bus bindings from {} ({:?}).",
|
|
||||||
PATH_SYS_BUS_AP_BINDINGS, err
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,225 +81,34 @@ pub fn wait_for_ap_bus_bindings_complete() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ApqnMode {
|
|
||||||
Accel,
|
|
||||||
Ep11,
|
|
||||||
Cca,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ApqnInfoAccel {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ApqnInfoEp11 {
|
|
||||||
pub serialnr: String,
|
|
||||||
pub mkvp: String, // may be an empty string if no WK set
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct ApqnInfoCca {
|
|
||||||
pub serialnr: String,
|
|
||||||
pub mkvp_aes: String, // may be an empty string if no MK set
|
|
||||||
pub mkvp_apka: String, // may be an empty string if no MK set
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ApqnInfo {
|
|
||||||
Accel(ApqnInfoAccel),
|
|
||||||
Ep11(ApqnInfoEp11),
|
|
||||||
#[allow(dead_code)]
|
|
||||||
Cca(ApqnInfoCca),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApqnInfo {
|
|
||||||
fn accel_info(_carddir: &str, _queuedir: &str) -> Result<Self, String> {
|
|
||||||
Ok(Self::Accel(ApqnInfoAccel {}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cca_info(carddir: &str, queuedir: &str) -> Result<Self, String> {
|
|
||||||
let serialnr = match sysfs_read_string(&format!("{carddir}/serialnr")) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure reading serialnr from {carddir}/serialnr: {:?}.",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mkvps = match sysfs_read_string(&format!("{carddir}/{queuedir}/mkvps")) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure reading mkvps from {carddir}/{queuedir}/mkvps: {:?}.",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut aes_mkvp = String::new();
|
|
||||||
let re_cca_aes_mkvp = Regex::new(RE_CCA_AES_MKVP).unwrap();
|
|
||||||
if !re_cca_aes_mkvp.is_match(&mkvps) {
|
|
||||||
return Err(format!(
|
|
||||||
"APQN {} failure parsing mkvps string '{}'.",
|
|
||||||
queuedir, mkvps
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let caps = re_cca_aes_mkvp.captures(&mkvps).unwrap();
|
|
||||||
let valid = caps.get(1).unwrap().as_str().to_lowercase();
|
|
||||||
if valid != "valid" {
|
|
||||||
eprintln!(
|
|
||||||
"Warning: APQN {} has no valid AES master key set.",
|
|
||||||
queuedir
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
aes_mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
|
||||||
if aes_mkvp.starts_with("0x") {
|
|
||||||
aes_mkvp = String::from(&aes_mkvp[2..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut apka_mkvp = String::new();
|
|
||||||
let re_cca_apka_mkvp = Regex::new(RE_CCA_APKA_MKVP).unwrap();
|
|
||||||
if !re_cca_apka_mkvp.is_match(&mkvps) {
|
|
||||||
return Err(format!(
|
|
||||||
"APQN {} failure parsing mkvps string '{}'.",
|
|
||||||
queuedir, mkvps
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let caps = re_cca_apka_mkvp.captures(&mkvps).unwrap();
|
|
||||||
let valid = caps.get(1).unwrap().as_str().to_lowercase();
|
|
||||||
if valid != "valid" {
|
|
||||||
eprintln!(
|
|
||||||
"Warning: APQN {} has no valid APKA master key set.",
|
|
||||||
queuedir
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
apka_mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
|
||||||
if apka_mkvp.starts_with("0x") {
|
|
||||||
apka_mkvp = String::from(&apka_mkvp[2..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self::Cca(ApqnInfoCca {
|
|
||||||
serialnr,
|
|
||||||
mkvp_aes: aes_mkvp,
|
|
||||||
mkvp_apka: apka_mkvp,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ep11_info(carddir: &str, queuedir: &str) -> Result<Self, String> {
|
|
||||||
let serialnr = match sysfs_read_string(&format!("{carddir}/serialnr")) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure reading serialnr from {carddir}/serialnr: {:?}.",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mkvps = match sysfs_read_string(&format!("{carddir}/{queuedir}/mkvps")) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure reading mkvps from {carddir}/{queuedir}/mkvps: {:?}.",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut mkvp = String::new();
|
|
||||||
let re_ep11_mkvp = Regex::new(RE_EP11_MKVP).unwrap();
|
|
||||||
if !re_ep11_mkvp.is_match(&mkvps) {
|
|
||||||
return Err(format!(
|
|
||||||
"APQN {} failure parsing mkvps string '{}'.",
|
|
||||||
queuedir, mkvps
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let caps = re_ep11_mkvp.captures(&mkvps).unwrap();
|
|
||||||
let valid = caps.get(1).unwrap().as_str().to_lowercase();
|
|
||||||
if valid != "valid" {
|
|
||||||
eprintln!("Warning: APQN {} has no valid wrapping key set.", queuedir);
|
|
||||||
} else {
|
|
||||||
mkvp = caps.get(2).unwrap().as_str().to_lowercase();
|
|
||||||
if mkvp.starts_with("0x") {
|
|
||||||
mkvp = String::from(&mkvp[2..]);
|
|
||||||
}
|
|
||||||
if mkvp.len() > 32 {
|
|
||||||
mkvp = String::from(&mkvp[..32])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self::Ep11(ApqnInfoEp11 { serialnr, mkvp }))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(mode: &ApqnMode, carddir: &str, queuedir: &str) -> Result<Self, String> {
|
|
||||||
match mode {
|
|
||||||
ApqnMode::Accel => Self::accel_info(carddir, queuedir),
|
|
||||||
ApqnMode::Cca => Self::cca_info(carddir, queuedir),
|
|
||||||
ApqnMode::Ep11 => Self::ep11_info(carddir, queuedir),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Apqn {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub name: String,
|
|
||||||
pub card: u32,
|
|
||||||
pub domain: u32,
|
|
||||||
pub gen: u32,
|
|
||||||
pub mode: ApqnMode,
|
|
||||||
pub info: Option<ApqnInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Apqn {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "({},{})", self.card, self.domain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Apqn {
|
|
||||||
pub fn bind_state(&self) -> Result<BindState, String> {
|
|
||||||
get_apqn_bind_state(self.card, self.domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_bind_state(&self, state: BindState) -> Result<(), String> {
|
|
||||||
set_apqn_bind_state(self.card, self.domain, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn associate_state(&self) -> Result<AssocState, String> {
|
|
||||||
get_apqn_associate_state(self.card, self.domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_associate_state(&self, state: AssocState) -> Result<(), String> {
|
|
||||||
set_apqn_associate_state(self.card, self.domain, state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper object around Vector of Apqns
|
/// Wrapper object around Vector of Apqns
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ApqnList(Vec<Apqn>);
|
pub struct ApqnList(Vec<Apqn>);
|
||||||
|
|
||||||
impl ApqnList {
|
impl ApqnList {
|
||||||
|
/// Create from APQN vector.
|
||||||
#[cfg(test)] // only used in test code
|
#[cfg(test)] // only used in test code
|
||||||
pub fn from_apqn_vec(apqns: Vec<Apqn>) -> Self {
|
pub fn from_apqn_vec(apqns: Vec<Apqn>) -> Self {
|
||||||
Self(apqns)
|
Self(apqns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts to an APQN vector.
|
||||||
#[cfg(test)] // only used in test code
|
#[cfg(test)] // only used in test code
|
||||||
pub fn to_apqn_vec(&self) -> Vec<Apqn> {
|
pub fn to_apqn_vec(&self) -> Vec<Apqn> {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iter over APQN list
|
||||||
pub fn iter(&self) -> Iter<'_, Apqn> {
|
pub fn iter(&self) -> Iter<'_, Apqn> {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Length of the APQN list
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if APQN list is empty.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
@@ -327,19 +116,12 @@ impl ApqnList {
|
|||||||
/// Scan AP bus devices in sysfs and construct the Apqnlist.
|
/// Scan AP bus devices in sysfs and construct the Apqnlist.
|
||||||
///
|
///
|
||||||
/// The list is a vector of struct Apqn for each APQN found in sysfs
|
/// The list is a vector of struct Apqn for each APQN found in sysfs
|
||||||
/// which is online and the card type matches to the regular expression
|
/// that this struct can be created from.
|
||||||
/// RE_CARD_TYPE.
|
|
||||||
/// On success a vector of struct Apqn is returned. This list may be
|
/// On success a vector of struct Apqn is returned. This list may be
|
||||||
/// empty if there are no APQNs available or do not match to the conditions.
|
/// empty if there are no APQNs available or do not match to the conditions.
|
||||||
/// On failure None is returned.
|
/// On failure None is returned.
|
||||||
/// Fatal errors which should never happened like unable to compile a
|
|
||||||
/// static regular expression will result in calling panic.
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the compilation of a static regular expression fails.
|
|
||||||
pub fn gather_apqns() -> Option<Self> {
|
pub fn gather_apqns() -> Option<Self> {
|
||||||
let mut apqns: Vec<Apqn> = Vec::new();
|
let mut apqns: Vec<Apqn> = Vec::new();
|
||||||
let re_card_type = Regex::new(RE_CARD_TYPE).unwrap();
|
|
||||||
let re_queue_dir = Regex::new(RE_QUEUE_DIR).unwrap();
|
|
||||||
let card_dirs =
|
let card_dirs =
|
||||||
match sysfs_get_list_of_subdirs_matching_regex(PATH_SYS_DEVICES_AP, RE_CARD_DIR) {
|
match sysfs_get_list_of_subdirs_matching_regex(PATH_SYS_DEVICES_AP, RE_CARD_DIR) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
@@ -353,33 +135,6 @@ impl ApqnList {
|
|||||||
};
|
};
|
||||||
for dir in card_dirs {
|
for dir in card_dirs {
|
||||||
let path = format!("{PATH_SYS_DEVICES_AP}/{dir}");
|
let path = format!("{PATH_SYS_DEVICES_AP}/{dir}");
|
||||||
let card_type = match sysfs_read_string(&format!("{path}/type")) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Failure reading card type from {} ({:?}).", path, err);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if !re_card_type.is_match(&card_type) {
|
|
||||||
eprintln!("Failure parsing card type string '{}'.", card_type);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let caps = re_card_type.captures(&card_type).unwrap();
|
|
||||||
let gen = caps.get(1).unwrap().as_str().parse::<u32>().unwrap();
|
|
||||||
let mode = match caps.get(2).unwrap().as_str().parse::<char>().unwrap() {
|
|
||||||
'A' => ApqnMode::Accel,
|
|
||||||
'C' => ApqnMode::Cca,
|
|
||||||
'P' => ApqnMode::Ep11,
|
|
||||||
_ => panic!("Code inconsistence between regex RE_CARD_TYPE and evaluation code."),
|
|
||||||
};
|
|
||||||
if pv_core::misc::pv_guest_bit_set() {
|
|
||||||
// the UV blocks requests to CCA cards within SE guest with
|
|
||||||
// AP pass-through support. However, filter out CCA cards as these
|
|
||||||
// cards cause hangs during information gathering.
|
|
||||||
if mode == ApqnMode::Cca {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let queue_dirs = match sysfs_get_list_of_subdirs_matching_regex(&path, RE_QUEUE_DIR) {
|
let queue_dirs = match sysfs_get_list_of_subdirs_matching_regex(&path, RE_QUEUE_DIR) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -391,76 +146,31 @@ impl ApqnList {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
for queue_dir in queue_dirs {
|
for queue_dir in queue_dirs {
|
||||||
let _online = match sysfs_read_i32(&format!("{path}/{queue_dir}/online")) {
|
let apqn: Apqn = match (&queue_dir as &str).try_into() {
|
||||||
Ok(1) => true,
|
Ok(apqn) => apqn,
|
||||||
_ => continue,
|
Err(e) => {
|
||||||
};
|
eprintln!("{e}");
|
||||||
let caps = re_queue_dir.captures(&queue_dir).unwrap();
|
continue;
|
||||||
let cardstr = caps.get(1).unwrap().as_str();
|
|
||||||
let card = u32::from_str_radix(cardstr, 16).unwrap();
|
|
||||||
let domstr = caps.get(2).unwrap().as_str();
|
|
||||||
let dom = u32::from_str_radix(domstr, 16).unwrap();
|
|
||||||
// For the mpvk and serialnr to fetch from the APQN within a SE
|
|
||||||
// guest the APQN needs to be bound to the guest. So if the APQN
|
|
||||||
// is not bound, temporarily bind it here until the info has
|
|
||||||
// been retrieved.
|
|
||||||
let mut tempbound = false;
|
|
||||||
if pv_core::misc::pv_guest_bit_set() {
|
|
||||||
let cbs = match get_apqn_bind_state(card, dom) {
|
|
||||||
Ok(bs) => bs,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!(
|
|
||||||
"Error: Failure reading APQN ({},{}) bind state: {}",
|
|
||||||
card, dom, err
|
|
||||||
);
|
|
||||||
BindState::NotSupported
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if cbs == BindState::Unbound {
|
|
||||||
let r = set_apqn_bind_state(card, dom, BindState::Bound);
|
|
||||||
if r.is_err() {
|
|
||||||
eprintln!(
|
|
||||||
"Warning: Failure to temp. bind APQN ({},{}): {}",
|
|
||||||
card,
|
|
||||||
dom,
|
|
||||||
r.unwrap_err()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
tempbound = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let info = match ApqnInfo::info(&mode, &path, &queue_dir) {
|
|
||||||
Err(err) => {
|
|
||||||
// print the error but continue with info set to None
|
|
||||||
eprintln!(
|
|
||||||
"Warning: Failure to gather info for APQN ({},{}): {}",
|
|
||||||
card, dom, err
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Ok(i) => Some(i),
|
|
||||||
};
|
|
||||||
if tempbound {
|
|
||||||
let r = set_apqn_bind_state(card, dom, BindState::Unbound);
|
|
||||||
if r.is_err() {
|
|
||||||
eprintln!(
|
|
||||||
"Warning: Failure to unbind temp. bound APQN ({},{}): {}",
|
|
||||||
card,
|
|
||||||
dom,
|
|
||||||
r.unwrap_err()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
apqns.push(Apqn {
|
// Warn about non-fatal errors
|
||||||
name: queue_dir.clone(),
|
if apqn.info.is_none() {
|
||||||
card,
|
eprintln!("Warning: Failure gathering info for APQN {queue_dir}");
|
||||||
domain: dom,
|
}
|
||||||
gen,
|
if let Some(apqn_info::Cca(ref cca_info)) = apqn.info {
|
||||||
mode: mode.clone(),
|
if cca_info.mkvp_aes.is_empty() {
|
||||||
info,
|
eprintln!("Warning: APQN {queue_dir} has no valid AES master key set.");
|
||||||
});
|
}
|
||||||
|
if cca_info.mkvp_apka.is_empty() {
|
||||||
|
eprintln!("Warning: APQN {queue_dir} has no valid APKA master key set.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(apqn_info::Ep11(ref ep11_info)) = apqn.info {
|
||||||
|
if ep11_info.mkvp.is_empty() {
|
||||||
|
eprintln!("Warning: APQN {queue_dir} has no valid wrapping key set.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apqns.push(apqn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Self(apqns))
|
Some(Self(apqns))
|
||||||
@@ -483,16 +193,16 @@ impl ApqnList {
|
|||||||
for a2 in self.0.iter() {
|
for a2 in self.0.iter() {
|
||||||
if a1.card == a2.card
|
if a1.card == a2.card
|
||||||
&& a1.domain < a2.domain
|
&& a1.domain < a2.domain
|
||||||
&& a1.mode == ApqnMode::Ep11
|
&& a1.mode == apqn_mode::Ep11
|
||||||
&& a1.info.is_some()
|
&& a1.info.is_some()
|
||||||
&& a2.info.is_some()
|
&& a2.info.is_some()
|
||||||
{
|
{
|
||||||
let i1 = match a1.info.as_ref().unwrap() {
|
let i1 = match a1.info.as_ref().unwrap() {
|
||||||
ApqnInfo::Ep11(i) => i,
|
apqn_info::Ep11(i) => i,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
let i2 = match a2.info.as_ref().unwrap() {
|
let i2 = match a2.info.as_ref().unwrap() {
|
||||||
ApqnInfo::Ep11(i) => i,
|
apqn_info::Ep11(i) => i,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if i1.mkvp.is_empty() || i2.mkvp.is_empty() {
|
if i1.mkvp.is_empty() || i2.mkvp.is_empty() {
|
||||||
@@ -509,217 +219,6 @@ impl ApqnList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
pub enum BindState {
|
|
||||||
Bound,
|
|
||||||
Unbound,
|
|
||||||
NotSupported,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query bind state for this APQN.
|
|
||||||
///
|
|
||||||
/// Returns a BindState enum as defined above or on failure
|
|
||||||
/// an error string. Does NOT print any error messages.
|
|
||||||
pub fn get_apqn_bind_state(card: u32, dom: u32) -> Result<BindState, String> {
|
|
||||||
let path = format!(
|
|
||||||
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
|
||||||
PATH_SYS_DEVICES_AP, card, card, dom
|
|
||||||
);
|
|
||||||
match sysfs_read_string(&path) {
|
|
||||||
Err(err) => Err(format!(
|
|
||||||
"Failure reading se_bind attribute for APQN({},{}): {:?}.",
|
|
||||||
card, dom, err
|
|
||||||
)),
|
|
||||||
Ok(str) => match str.as_str() {
|
|
||||||
"bound" => Ok(BindState::Bound),
|
|
||||||
"unbound" => Ok(BindState::Unbound),
|
|
||||||
"-" => Ok(BindState::NotSupported),
|
|
||||||
_ => Err(format!("Unknown bind state '{str}'.")),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bind or unbind an APQN.
|
|
||||||
///
|
|
||||||
/// The action is determined by the BindState given in.
|
|
||||||
/// But of course only Bound and Unbound is supported - otherwise
|
|
||||||
/// this function panics!
|
|
||||||
/// The function actively loops over the bind state until
|
|
||||||
/// the requested bind state is reached or a timeout has
|
|
||||||
/// occurred (SYS_BUS_AP_BIND_TIMEOUT_MS).
|
|
||||||
/// On success () is returned, on failure an error string
|
|
||||||
/// is returned. Does NOT print any error messages.
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if a desired bind state other than Bound or Unbound is given.
|
|
||||||
pub fn set_apqn_bind_state(card: u32, dom: u32, state: BindState) -> Result<(), String> {
|
|
||||||
let path = format!(
|
|
||||||
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
|
||||||
PATH_SYS_DEVICES_AP, card, card, dom
|
|
||||||
);
|
|
||||||
let r = match state {
|
|
||||||
BindState::Bound => sysfs_write_i32(&path, 1),
|
|
||||||
BindState::Unbound => sysfs_write_i32(&path, 0),
|
|
||||||
_ => panic!("set_apqn_bind_state called with invalid BindState."),
|
|
||||||
};
|
|
||||||
if r.is_err() {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure writing se_bind attribute for APQN({},{}): {:?}.",
|
|
||||||
card,
|
|
||||||
dom,
|
|
||||||
r.unwrap_err()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let mut ms: u64 = 0;
|
|
||||||
loop {
|
|
||||||
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BIND_POLL_MS));
|
|
||||||
ms += SYS_BUS_AP_BIND_POLL_MS;
|
|
||||||
if ms >= SYS_BUS_AP_BIND_TIMEOUT_MS {
|
|
||||||
break Err(format!(
|
|
||||||
"Timeout setting APQN({},{}) bind state.",
|
|
||||||
card, dom
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let newstate = get_apqn_bind_state(card, dom)?;
|
|
||||||
if newstate == state {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
pub enum AssocState {
|
|
||||||
Associated(u16),
|
|
||||||
AssociationPending,
|
|
||||||
Unassociated,
|
|
||||||
NotSupported,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query association state for this APQN.
|
|
||||||
///
|
|
||||||
/// Returns an AssocState enum as defined above or on failure
|
|
||||||
/// an error string. Does NOT print any error messages.
|
|
||||||
pub fn get_apqn_associate_state(card: u32, dom: u32) -> Result<AssocState, String> {
|
|
||||||
let path = format!(
|
|
||||||
"{}/card{:02x}/{:02x}.{:04x}/se_associate",
|
|
||||||
PATH_SYS_DEVICES_AP, card, card, dom
|
|
||||||
);
|
|
||||||
match sysfs_read_string(&path) {
|
|
||||||
Err(err) => Err(format!(
|
|
||||||
"Failure reading se_associate attribute for APQN({},{}: {:?}",
|
|
||||||
card, dom, err
|
|
||||||
)),
|
|
||||||
Ok(str) => {
|
|
||||||
if let Some(prefix) = str.strip_prefix("associated ") {
|
|
||||||
let value = &prefix.parse::<u16>();
|
|
||||||
match value {
|
|
||||||
Ok(v) => Ok(AssocState::Associated(*v)),
|
|
||||||
Err(_) => Err(format!("Invalid association index in '{str}'.")),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match str.as_str() {
|
|
||||||
"association pending" => Ok(AssocState::AssociationPending),
|
|
||||||
"unassociated" => Ok(AssocState::Unassociated),
|
|
||||||
"-" => Ok(AssocState::NotSupported),
|
|
||||||
_ => Err(format!("Unknown association state '{str}'.")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_apqn_associate_state_associate(card: u32, dom: u32, idx: u16) -> Result<(), String> {
|
|
||||||
let path = format!(
|
|
||||||
"{}/card{:02x}/{:02x}.{:04x}/se_associate",
|
|
||||||
PATH_SYS_DEVICES_AP, card, card, dom
|
|
||||||
);
|
|
||||||
let r = sysfs_write_i32(&path, idx as i32);
|
|
||||||
if r.is_err() {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure writing se_associate attribute for APQN({},{}): {:?}.",
|
|
||||||
card,
|
|
||||||
dom,
|
|
||||||
r.unwrap_err()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let mut ms: u64 = 0;
|
|
||||||
loop {
|
|
||||||
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS));
|
|
||||||
ms += SYS_BUS_AP_ASSOC_POLL_MS;
|
|
||||||
if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS {
|
|
||||||
break Err(format!(
|
|
||||||
"Timeout setting APQN({},{}) association idx {} state.",
|
|
||||||
card, dom, idx
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let newstate = get_apqn_associate_state(card, dom)?;
|
|
||||||
if let AssocState::Associated(i) = newstate {
|
|
||||||
if idx == i {
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure: APQN({},{}) is associated with {} but it should be {}.",
|
|
||||||
card, dom, i, idx
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_apqn_associate_state_unbind(card: u32, dom: u32) -> Result<(), String> {
|
|
||||||
let bindpath = format!(
|
|
||||||
"{}/card{:02x}/{:02x}.{:04x}/se_bind",
|
|
||||||
PATH_SYS_DEVICES_AP, card, card, dom
|
|
||||||
);
|
|
||||||
let r = sysfs_write_i32(&bindpath, 0);
|
|
||||||
if r.is_err() {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure writing se_bind attribute for APQN({},{}): {:?}.",
|
|
||||||
card,
|
|
||||||
dom,
|
|
||||||
r.unwrap_err()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let mut ms: u64 = 0;
|
|
||||||
loop {
|
|
||||||
thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS));
|
|
||||||
ms += SYS_BUS_AP_ASSOC_POLL_MS;
|
|
||||||
if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS {
|
|
||||||
break Err(format!(
|
|
||||||
"Timeout setting APQN({},{}) association unbind state.",
|
|
||||||
card, dom
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let newstate = get_apqn_associate_state(card, dom)?;
|
|
||||||
if newstate == AssocState::Unassociated {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associate or Unassociate an APQN.
|
|
||||||
///
|
|
||||||
/// The action is determined by the AssocState given in.
|
|
||||||
/// But of course only Associated and Unassociated is supported
|
|
||||||
/// otherwise this function panics!
|
|
||||||
/// The function actively loops over the association state until
|
|
||||||
/// the requested state is reached or a timeout has
|
|
||||||
/// occurred (SYS_BUS_AP_ASSOC_TIMEOUT_MS).
|
|
||||||
/// The unassociate is in fact a unbind. So the code triggers
|
|
||||||
/// an unbind and then loops over the sysfs se_associate until
|
|
||||||
/// "unassociated" is reached.
|
|
||||||
/// On success () is returned, on failure an error string
|
|
||||||
/// is returned. Does NOT print any error messages.
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if a desired bind state other than Associated or
|
|
||||||
/// Unassociated is given.
|
|
||||||
pub fn set_apqn_associate_state(card: u32, dom: u32, state: AssocState) -> Result<(), String> {
|
|
||||||
match state {
|
|
||||||
AssocState::Associated(idx) => set_apqn_associate_state_associate(card, dom, idx),
|
|
||||||
AssocState::Unassociated => set_apqn_associate_state_unbind(card, dom),
|
|
||||||
_ => panic!("set_apqn_associate_state called with invalid AssocState."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
@@ -771,16 +270,16 @@ mod tests {
|
|||||||
let v = l.to_apqn_vec();
|
let v = l.to_apqn_vec();
|
||||||
for a in v {
|
for a in v {
|
||||||
match a.mode {
|
match a.mode {
|
||||||
ApqnMode::Accel => {
|
apqn_mode::Accel => {
|
||||||
// fail if no ApqnInfo is attached
|
// fail if no ApqnInfo is attached
|
||||||
assert!(a.info.is_some());
|
assert!(a.info.is_some());
|
||||||
}
|
}
|
||||||
ApqnMode::Ep11 => {
|
apqn_mode::Ep11 => {
|
||||||
// fail if no ApqnInfo is attached
|
// fail if no ApqnInfo is attached
|
||||||
assert!(a.info.is_some());
|
assert!(a.info.is_some());
|
||||||
let info = a.info.unwrap();
|
let info = a.info.unwrap();
|
||||||
let i = match &info {
|
let i = match &info {
|
||||||
ApqnInfo::Ep11(i) => i,
|
apqn_info::Ep11(i) => i,
|
||||||
_ => panic!("ApqnInfo attached onto Ep11 APQN is NOT ApqnInfoEp11 ?!?"),
|
_ => panic!("ApqnInfo attached onto Ep11 APQN is NOT ApqnInfoEp11 ?!?"),
|
||||||
};
|
};
|
||||||
// fail if no serialnr
|
// fail if no serialnr
|
||||||
@@ -788,12 +287,12 @@ mod tests {
|
|||||||
// mkvp is either empty (no WK set) or has exact 32 characters
|
// mkvp is either empty (no WK set) or has exact 32 characters
|
||||||
assert!(i.mkvp.is_empty() || i.mkvp.len() == 32);
|
assert!(i.mkvp.is_empty() || i.mkvp.len() == 32);
|
||||||
}
|
}
|
||||||
ApqnMode::Cca => {
|
apqn_mode::Cca => {
|
||||||
// fail if no ApqnInfo is attached
|
// fail if no ApqnInfo is attached
|
||||||
assert!(a.info.is_some());
|
assert!(a.info.is_some());
|
||||||
let info = a.info.unwrap();
|
let info = a.info.unwrap();
|
||||||
let i = match &info {
|
let i = match &info {
|
||||||
ApqnInfo::Cca(i) => i,
|
apqn_info::Cca(i) => i,
|
||||||
_ => panic!("ApqnInfo attached onto Cca APQN is NOT ApqnInfoCca ?!?"),
|
_ => panic!("ApqnInfo attached onto Cca APQN is NOT ApqnInfoCca ?!?"),
|
||||||
};
|
};
|
||||||
// fail if no serialnr
|
// fail if no serialnr
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ pub struct Cli {
|
|||||||
#[arg(long = "strict")]
|
#[arg(long = "strict")]
|
||||||
pub strict: bool,
|
pub strict: bool,
|
||||||
|
|
||||||
|
/// Unbind all available APQNs.
|
||||||
|
#[arg(long, conflicts_with_all = ["config", "strict"])]
|
||||||
|
pub unbind: bool,
|
||||||
|
|
||||||
/// Provide more detailed output.
|
/// Provide more detailed output.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use openssl::sha::sha256;
|
use openssl::sha::sha256;
|
||||||
|
use pv_core::misc::encode_hex;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::{self};
|
use serde_yaml::{self};
|
||||||
@@ -48,7 +49,7 @@ impl ApConfigEntry {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let hash = sha256(self.name.as_bytes());
|
let hash = sha256(self.name.as_bytes());
|
||||||
let hashstr = crate::helper::u8_to_hexstring(&hash);
|
let hashstr = encode_hex(hash);
|
||||||
// if there is a secretid given, this must match to the hash
|
// if there is a secretid given, this must match to the hash
|
||||||
if !self.secretid.is_empty() {
|
if !self.secretid.is_empty() {
|
||||||
if self.secretid != hashstr {
|
if self.secretid != hashstr {
|
||||||
@@ -149,6 +150,7 @@ impl ApConfigEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper object around Vector of ApConfigEntry
|
/// Wrapper object around Vector of ApConfigEntry
|
||||||
|
#[derive(Default)]
|
||||||
pub struct ApConfigList(Vec<ApConfigEntry>);
|
pub struct ApConfigList(Vec<ApConfigEntry>);
|
||||||
|
|
||||||
impl ApConfigList {
|
impl ApConfigList {
|
||||||
@@ -377,15 +379,15 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sha256() {
|
fn test_sha256() {
|
||||||
assert!(
|
assert!(
|
||||||
crate::helper::u8_to_hexstring(&sha256("Hello".as_bytes()))
|
encode_hex(sha256("Hello".as_bytes()))
|
||||||
== "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"
|
== "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
crate::helper::u8_to_hexstring(&sha256("SECRET1".as_bytes()))
|
encode_hex(sha256("SECRET1".as_bytes()))
|
||||||
== "03153249db7ce46b0330ffb1a760b59710531af08ec4d7f8424a6870fae49360"
|
== "03153249db7ce46b0330ffb1a760b59710531af08ec4d7f8424a6870fae49360"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
crate::helper::u8_to_hexstring(&sha256("SECRET2".as_bytes()))
|
encode_hex(sha256("SECRET2".as_bytes()))
|
||||||
== "258499e710e0bd3bb878d6bac7e478b30f3f3e72566989f638c4143d14f6c0b6"
|
== "258499e710e0bd3bb878d6bac7e478b30f3f3e72566989f638c4143d14f6c0b6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,81 +8,12 @@
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Read, Write};
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub const PATH_PVAPCONFIG_LOCK: &str = "/run/lock/pvapconfig.lock";
|
pub const PATH_PVAPCONFIG_LOCK: &str = "/run/lock/pvapconfig.lock";
|
||||||
|
|
||||||
/// Convert u8 slice to (lowercase) hex string
|
|
||||||
pub fn u8_to_hexstring(slice: &[u8]) -> String {
|
|
||||||
let s = String::with_capacity(2 * slice.len());
|
|
||||||
slice.iter().fold(s, |acc, e| acc + &format!("{e:02x}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert hexstring to u8 vector
|
|
||||||
/// The hexstring may contain whitespaces which are ignored.
|
|
||||||
/// If there are other characters in there or if the number
|
|
||||||
/// of hex characters is uneven panic() is called.
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the given string contains characters other than
|
|
||||||
/// hex digits and whitespace. Panics if the number of hex digits
|
|
||||||
/// is not even.
|
|
||||||
#[cfg(test)] // currently only used in test code
|
|
||||||
pub fn hexstring_to_u8(hex: &str) -> Vec<u8> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in hex.chars() {
|
|
||||||
if c.is_ascii_hexdigit() {
|
|
||||||
s.push(c);
|
|
||||||
} else if c.is_whitespace() {
|
|
||||||
// ignore
|
|
||||||
} else {
|
|
||||||
panic!("Invalid character '{c}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s.len() % 2 == 1 {
|
|
||||||
panic!("Uneven # of hex characters in '{s}'");
|
|
||||||
}
|
|
||||||
let mut hex_bytes = s.as_bytes().iter().map_while(|b| match b {
|
|
||||||
b'0'..=b'9' => Some(b - b'0'),
|
|
||||||
b'a'..=b'f' => Some(b - b'a' + 10),
|
|
||||||
b'A'..=b'F' => Some(b - b'A' + 10),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
let mut bytes = Vec::with_capacity(s.len());
|
|
||||||
while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) {
|
|
||||||
bytes.push(h << 4 | l)
|
|
||||||
}
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read sysfs file into string
|
|
||||||
pub fn sysfs_read_string(fname: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let mut file = File::open(fname)?;
|
|
||||||
let mut content = String::new();
|
|
||||||
file.read_to_string(&mut content)?;
|
|
||||||
let trimmed_content = String::from(content.trim());
|
|
||||||
Ok(trimmed_content)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write string into sysfs file
|
|
||||||
pub fn sysfs_write_string(fname: &str, value: &str) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut file = OpenOptions::new().write(true).open(fname)?;
|
|
||||||
file.write_all(value.as_bytes())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read sysfs file content and parse as i32 value
|
|
||||||
pub fn sysfs_read_i32(fname: &str) -> Result<i32, Box<dyn Error>> {
|
|
||||||
let content = sysfs_read_string(fname)?;
|
|
||||||
Ok(content.parse::<i32>()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write an i32 value into a sysfs file
|
|
||||||
pub fn sysfs_write_i32(fname: &str, value: i32) -> Result<(), Box<dyn Error>> {
|
|
||||||
sysfs_write_string(fname, &value.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For a given (sysfs) directory construct a list of all subdirs
|
/// For a given (sysfs) directory construct a list of all subdirs
|
||||||
/// and give it back as a vector of strings. If there is no subdir,
|
/// and give it back as a vector of strings. If there is no subdir,
|
||||||
/// the vector is empty.
|
/// the vector is empty.
|
||||||
@@ -203,29 +134,6 @@ mod tests {
|
|||||||
|
|
||||||
// Only very simple tests
|
// Only very simple tests
|
||||||
|
|
||||||
const TEST_BYTES: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
|
|
||||||
const TEST_HEXSTR: &str = "0123456789abcdef";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_u8_to_hexstring() {
|
|
||||||
let str = u8_to_hexstring(&TEST_BYTES);
|
|
||||||
assert!(str == TEST_HEXSTR);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_hexstring_to_u8() {
|
|
||||||
let bytes = hexstring_to_u8(TEST_HEXSTR);
|
|
||||||
assert!(bytes.as_slice() == TEST_BYTES);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_sysfs_read_string() {
|
|
||||||
let r = sysfs_read_string("/proc/cpuinfo");
|
|
||||||
assert!(r.is_ok());
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_sysfs_read_i32() {
|
|
||||||
let r = sysfs_read_i32("/proc/sys/kernel/random/entropy_avail");
|
|
||||||
assert!(r.is_ok());
|
|
||||||
}
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sysfs_get_list_of_subdirs() {
|
fn test_sysfs_get_list_of_subdirs() {
|
||||||
let r = sysfs_get_list_of_subdirs("/proc/self");
|
let r = sysfs_get_list_of_subdirs("/proc/self");
|
||||||
@@ -244,24 +152,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sysfs_write_i32() {
|
|
||||||
let temp_dir =
|
|
||||||
TemporaryDirectory::new().expect("creating a temporary directory should work");
|
|
||||||
let test_path = temp_dir.path().join("test");
|
|
||||||
let test_path = test_path.as_os_str().to_str().expect("should work");
|
|
||||||
let mut file = File::create(test_path).unwrap();
|
|
||||||
let _ = file.write_all(b"XYZ");
|
|
||||||
drop(file);
|
|
||||||
let r = sysfs_read_i32(test_path);
|
|
||||||
assert!(r.is_err());
|
|
||||||
let r = sysfs_write_i32(test_path, 999);
|
|
||||||
assert!(r.is_ok());
|
|
||||||
let r = sysfs_read_i32(test_path);
|
|
||||||
assert!(r.is_ok());
|
|
||||||
let v = r.unwrap();
|
|
||||||
assert!(v == 999);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_lockfile() {
|
fn test_lockfile() {
|
||||||
let temp_dir =
|
let temp_dir =
|
||||||
TemporaryDirectory::new().expect("creating a temporary directory should work");
|
TemporaryDirectory::new().expect("creating a temporary directory should work");
|
||||||
|
|||||||
+80
-71
@@ -12,10 +12,12 @@ mod config;
|
|||||||
mod helper;
|
mod helper;
|
||||||
mod uv;
|
mod uv;
|
||||||
|
|
||||||
use ap::{Apqn, ApqnList};
|
use ap::ApqnList;
|
||||||
use cli::ARGS;
|
use cli::ARGS;
|
||||||
use config::{ApConfigEntry, ApConfigList};
|
use config::{ApConfigEntry, ApConfigList};
|
||||||
use helper::{LockFile, PATH_PVAPCONFIG_LOCK};
|
use helper::{LockFile, PATH_PVAPCONFIG_LOCK};
|
||||||
|
use pv_core::ap::{self as pvap, Apqn};
|
||||||
|
use pv_core::misc::encode_hex;
|
||||||
use pv_core::uv::{ListableSecretType, SecretList};
|
use pv_core::uv::{ListableSecretType, SecretList};
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use utils::print_version;
|
use utils::print_version;
|
||||||
@@ -84,42 +86,48 @@ fn main() -> ExitCode {
|
|||||||
on_error_print_and_exit!(r);
|
on_error_print_and_exit!(r);
|
||||||
info!("UV support and environment is ok.\n");
|
info!("UV support and environment is ok.\n");
|
||||||
|
|
||||||
// read configuration
|
let mut apconfig: ApConfigList = Default::default();
|
||||||
let configfile: &str = match &cli::ARGS.config {
|
if !cli::ARGS.unbind {
|
||||||
Some(f) => f,
|
// read configuration
|
||||||
_ => cli::PATH_DEFAULT_CONFIG_FILE,
|
let configfile: &str = match &cli::ARGS.config {
|
||||||
};
|
Some(f) => f,
|
||||||
info!(
|
_ => cli::PATH_DEFAULT_CONFIG_FILE,
|
||||||
"Reading AP configuration entries from file '{}'...\n",
|
};
|
||||||
configfile
|
info!(
|
||||||
);
|
"Reading AP configuration entries from file '{}'...\n",
|
||||||
let apconfig: ApConfigList = match ApConfigList::read_and_validate_yaml_file(configfile) {
|
|
||||||
Ok(apcfg) => apcfg,
|
|
||||||
Err(err) => println_and_exit_failure!("{}", err),
|
|
||||||
};
|
|
||||||
if apconfig.is_empty() {
|
|
||||||
println!(
|
|
||||||
"No AP configuration entries in config file '{}': Nothing to do.",
|
|
||||||
configfile
|
configfile
|
||||||
);
|
);
|
||||||
return ExitCode::SUCCESS;
|
apconfig = match ApConfigList::read_and_validate_yaml_file(configfile) {
|
||||||
}
|
Ok(apcfg) => apcfg,
|
||||||
info!("Found {} AP configuration entries.\n", apconfig.len());
|
Err(err) => println_and_exit_failure!("{}", err),
|
||||||
|
};
|
||||||
|
if apconfig.is_empty() {
|
||||||
|
println!(
|
||||||
|
"No AP configuration entries in config file '{}': Nothing to do.",
|
||||||
|
configfile
|
||||||
|
);
|
||||||
|
return ExitCode::SUCCESS;
|
||||||
|
}
|
||||||
|
info!("Found {} AP configuration entries.\n", apconfig.len());
|
||||||
|
};
|
||||||
|
|
||||||
// get list of secrets from UV
|
// get list of secrets from UV
|
||||||
info!("Fetching list of secrets from UV...\n");
|
let mut secrets = SecretList::new(0, Vec::new());
|
||||||
let secrets: SecretList = match uv::gather_secrets() {
|
if !cli::ARGS.unbind {
|
||||||
Err(e) => println_and_exit_failure!("{}", e),
|
info!("Fetching list of secrets from UV...\n");
|
||||||
Ok(los) => los,
|
secrets = match uv::gather_secrets() {
|
||||||
};
|
Err(e) => println_and_exit_failure!("{}", e),
|
||||||
info!("Fetched {} Secret entries from UV.\n", secrets.len());
|
Ok(los) => los,
|
||||||
|
};
|
||||||
|
info!("Fetched {} Secret entries from UV.\n", secrets.len());
|
||||||
|
}
|
||||||
|
|
||||||
// Warning if no UV secrets given but AP config entries require it
|
// Warning if no UV secrets given but AP config entries require it
|
||||||
let non_accel_apc = apconfig
|
let non_accel_apc = apconfig
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|apc| apc.mode != config::STR_MODE_ACCEL)
|
.filter(|apc| apc.mode != config::STR_MODE_ACCEL)
|
||||||
.count();
|
.count();
|
||||||
if non_accel_apc > 0 && secrets.is_empty() {
|
if !cli::ARGS.unbind && non_accel_apc > 0 && secrets.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"Warning: No UV Secrets given but at least one AP config entry requires a Secret."
|
"Warning: No UV Secrets given but at least one AP config entry requires a Secret."
|
||||||
);
|
);
|
||||||
@@ -151,7 +159,7 @@ fn main() -> ExitCode {
|
|||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
};
|
};
|
||||||
|
|
||||||
if n == 0 {
|
if !cli::ARGS.unbind && n == 0 {
|
||||||
println_and_exit_failure!(
|
println_and_exit_failure!(
|
||||||
"None out of {} AP config entries could be applied.",
|
"None out of {} AP config entries could be applied.",
|
||||||
apconfig.len()
|
apconfig.len()
|
||||||
@@ -164,11 +172,13 @@ fn main() -> ExitCode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
if !cli::ARGS.unbind {
|
||||||
"Successfully applied {} out of {} AP config entries.\n",
|
info!(
|
||||||
n,
|
"Successfully applied {} out of {} AP config entries.\n",
|
||||||
apconfig.len()
|
n,
|
||||||
);
|
apconfig.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
@@ -224,14 +234,14 @@ fn do_ap_config(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match apqn.mode {
|
match apqn.mode {
|
||||||
ap::ApqnMode::Accel => {
|
pvap::apqn_mode::Accel => {
|
||||||
// check bind state of this APQN
|
// check bind state of this APQN
|
||||||
let bind_state_ok = match apqn.bind_state() {
|
let bind_state_ok = match apqn.bind_state() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}");
|
eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Ok(ap::BindState::Bound) => true,
|
Ok(pvap::bind_state::Bound) => true,
|
||||||
Ok(_) => false,
|
Ok(_) => false,
|
||||||
};
|
};
|
||||||
if !bind_state_ok {
|
if !bind_state_ok {
|
||||||
@@ -246,7 +256,7 @@ fn do_ap_config(
|
|||||||
resolved_entries += 1;
|
resolved_entries += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ap::ApqnMode::Ep11 => {
|
pvap::apqn_mode::Ep11 => {
|
||||||
// check association state of this APQN
|
// check association state of this APQN
|
||||||
let (assoc_state_ok, assoc_idx) = match apqn.associate_state() {
|
let (assoc_state_ok, assoc_idx) = match apqn.associate_state() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -255,7 +265,7 @@ fn do_ap_config(
|
|||||||
);
|
);
|
||||||
(false, 0)
|
(false, 0)
|
||||||
}
|
}
|
||||||
Ok(ap::AssocState::Associated(idx)) => (true, idx),
|
Ok(pvap::assoc_state::Associated(idx)) => (true, idx),
|
||||||
Ok(_) => (false, 0),
|
Ok(_) => (false, 0),
|
||||||
};
|
};
|
||||||
if !assoc_state_ok {
|
if !assoc_state_ok {
|
||||||
@@ -266,7 +276,7 @@ fn do_ap_config(
|
|||||||
se.stype() == ListableSecretType::Association
|
se.stype() == ListableSecretType::Association
|
||||||
&& se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE
|
&& se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE
|
||||||
&& se.index() == assoc_idx
|
&& se.index() == assoc_idx
|
||||||
&& helper::u8_to_hexstring(se.id()) == apc.secretid
|
&& encode_hex(se.id()) == apc.secretid
|
||||||
});
|
});
|
||||||
if r.is_none() {
|
if r.is_none() {
|
||||||
continue;
|
continue;
|
||||||
@@ -297,10 +307,10 @@ fn do_ap_config(
|
|||||||
}
|
}
|
||||||
match apqn.bind_state() {
|
match apqn.bind_state() {
|
||||||
Err(err) => eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"),
|
Err(err) => eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"),
|
||||||
Ok(ap::BindState::Bound) => {
|
Ok(pvap::bind_state::Bound) => {
|
||||||
info!("Unbind APQN {apqn} as this bind/associate does not match to any AP config entry.\n");
|
info!("Unbind APQN {apqn} as this bind/associate does not match to any AP config entry.\n");
|
||||||
if !ARGS.dryrun() {
|
if !ARGS.dryrun() {
|
||||||
if let Err(err) = apqn.set_bind_state(ap::BindState::Unbound) {
|
if let Err(err) = apqn.set_bind_state(pvap::bind_state::Unbound) {
|
||||||
return Err(format!("Failure unbinding APQN {apqn}: {err}"));
|
return Err(format!("Failure unbinding APQN {apqn}: {err}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,15 +340,15 @@ fn do_ap_config(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match apqn.mode {
|
match apqn.mode {
|
||||||
ap::ApqnMode::Accel => {
|
pvap::apqn_mode::Accel => {
|
||||||
// try to bind this accelerator APQN
|
// try to bind this accelerator APQN
|
||||||
if ARGS.verbose() || fntest {
|
if ARGS.verbose() || fntest {
|
||||||
println!("Bind APQN {apqn} to match to AP config entry {cistr}.");
|
println!("Bind APQN {apqn} to match to AP config entry {cistr}.");
|
||||||
}
|
}
|
||||||
if !(ARGS.dryrun() || fntest) {
|
if !(ARGS.dryrun() || fntest) {
|
||||||
if let Err(err) = apqn.set_bind_state(ap::BindState::Bound) {
|
if let Err(err) = apqn.set_bind_state(pvap::bind_state::Bound) {
|
||||||
// bind failed, unbind/reset this apqn, return with failure
|
// bind failed, unbind/reset this apqn, return with failure
|
||||||
let _ = apqn.set_bind_state(ap::BindState::Unbound);
|
let _ = apqn.set_bind_state(pvap::bind_state::Unbound);
|
||||||
return Err(format!("Failure binding APQN {apqn}: {err}"));
|
return Err(format!("Failure binding APQN {apqn}: {err}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,13 +357,13 @@ fn do_ap_config(
|
|||||||
resolved_entries += 1;
|
resolved_entries += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ap::ApqnMode::Ep11 => {
|
pvap::apqn_mode::Ep11 => {
|
||||||
// EP11 needs bind and associate, but before doing this let's
|
// EP11 needs bind and associate, but before doing this let's
|
||||||
// check out which secret index to use with the associate
|
// check out which secret index to use with the associate
|
||||||
let se = match secrets.iter().find(|&se| {
|
let se = match secrets.iter().find(|&se| {
|
||||||
se.stype() == ListableSecretType::Association
|
se.stype() == ListableSecretType::Association
|
||||||
&& se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE
|
&& se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE
|
||||||
&& helper::u8_to_hexstring(se.id()) == apc.secretid
|
&& encode_hex(se.id()) == apc.secretid
|
||||||
}) {
|
}) {
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Warning: Secret id '{}' from config entry {} not found in UV secrets list.",
|
eprintln!("Warning: Secret id '{}' from config entry {} not found in UV secrets list.",
|
||||||
@@ -369,9 +379,9 @@ fn do_ap_config(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !(ARGS.dryrun() || fntest) {
|
if !(ARGS.dryrun() || fntest) {
|
||||||
if let Err(err) = apqn.set_bind_state(ap::BindState::Bound) {
|
if let Err(err) = apqn.set_bind_state(pvap::bind_state::Bound) {
|
||||||
// bind failed, unbind/reset this apqn, return with failure
|
// bind failed, unbind/reset this apqn, return with failure
|
||||||
let _ = apqn.set_bind_state(ap::BindState::Unbound);
|
let _ = apqn.set_bind_state(pvap::bind_state::Unbound);
|
||||||
return Err(format!("Failure binding APQN {}: {}", apqn, err));
|
return Err(format!("Failure binding APQN {}: {}", apqn, err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -383,7 +393,7 @@ fn do_ap_config(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !(ARGS.dryrun() || fntest) {
|
if !(ARGS.dryrun() || fntest) {
|
||||||
let apas = ap::AssocState::Associated(se.index());
|
let apas = pvap::assoc_state::Associated(se.index());
|
||||||
apqn.set_associate_state(apas)
|
apqn.set_associate_state(apas)
|
||||||
.map_err(|err| format!("Failure associating APQN {apqn}: {err}"))?;
|
.map_err(|err| format!("Failure associating APQN {apqn}: {err}"))?;
|
||||||
}
|
}
|
||||||
@@ -408,7 +418,7 @@ fn do_ap_config(
|
|||||||
/// Please note this can not happen, as mingen is already checked via RE
|
/// Please note this can not happen, as mingen is already checked via RE
|
||||||
/// during storing the value into mingen.
|
/// during storing the value into mingen.
|
||||||
fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool {
|
fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool {
|
||||||
if apc.mode == config::STR_MODE_ACCEL && apqn.mode == ap::ApqnMode::Accel {
|
if apc.mode == config::STR_MODE_ACCEL && apqn.mode == pvap::apqn_mode::Accel {
|
||||||
// config and apqn are accelerators
|
// config and apqn are accelerators
|
||||||
// maybe check mingen
|
// maybe check mingen
|
||||||
if !apc.mingen.is_empty() {
|
if !apc.mingen.is_empty() {
|
||||||
@@ -418,10 +428,10 @@ fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if apc.mode == config::STR_MODE_EP11 && apqn.mode == ap::ApqnMode::Ep11 {
|
} else if apc.mode == config::STR_MODE_EP11 && apqn.mode == pvap::apqn_mode::Ep11 {
|
||||||
// config and apqn are ep11
|
// config and apqn are ep11
|
||||||
let info = match &apqn.info {
|
let info = match &apqn.info {
|
||||||
Some(ap::ApqnInfo::Ep11(i)) => i,
|
Some(pvap::apqn_info::Ep11(i)) => i,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
// maybe check mingen
|
// maybe check mingen
|
||||||
@@ -450,79 +460,78 @@ fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool {
|
|||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use helper::hexstring_to_u8;
|
use pv_core::{misc::decode_hex, uv::SecretEntry};
|
||||||
use pv_core::uv::SecretEntry;
|
|
||||||
|
|
||||||
// This is more or less only a test for the do_ap_config() function
|
// This is more or less only a test for the do_ap_config() function
|
||||||
// However, this is THE main functionality of the whole application.
|
// However, this is THE main functionality of the whole application.
|
||||||
|
|
||||||
fn make_test_apqns() -> Vec<Apqn> {
|
fn make_test_apqns() -> Vec<Apqn> {
|
||||||
vec![
|
vec![
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("10.0007"),
|
name: String::from("10.0007"),
|
||||||
card: 16,
|
card: 16,
|
||||||
domain: 7,
|
domain: 7,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Accel,
|
mode: pvap::apqn_mode::Accel,
|
||||||
info: Option::Some(ap::ApqnInfo::Accel(ap::ApqnInfoAccel {})),
|
info: Option::Some(pvap::apqn_info::Accel(pvap::apqn_info::ApqnInfoAccel {})),
|
||||||
},
|
},
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("11.0008"),
|
name: String::from("11.0008"),
|
||||||
card: 17,
|
card: 17,
|
||||||
domain: 8,
|
domain: 8,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Ep11,
|
mode: pvap::apqn_mode::Ep11,
|
||||||
info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 {
|
info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 {
|
||||||
serialnr: String::from("93AADFK719460083"),
|
serialnr: String::from("93AADFK719460083"),
|
||||||
mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"),
|
mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("12.0009"),
|
name: String::from("12.0009"),
|
||||||
card: 18,
|
card: 18,
|
||||||
domain: 9,
|
domain: 9,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Ep11,
|
mode: pvap::apqn_mode::Ep11,
|
||||||
info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 {
|
info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 {
|
||||||
serialnr: String::from("93AADHZU42082261"),
|
serialnr: String::from("93AADHZU42082261"),
|
||||||
mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"),
|
mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("12.000a"),
|
name: String::from("12.000a"),
|
||||||
card: 18,
|
card: 18,
|
||||||
domain: 10,
|
domain: 10,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Ep11,
|
mode: pvap::apqn_mode::Ep11,
|
||||||
info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 {
|
info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 {
|
||||||
serialnr: String::from("93AADHZU42082261"),
|
serialnr: String::from("93AADHZU42082261"),
|
||||||
mkvp: String::from("383d2a9ab781f35343554c5b3d9337cd"),
|
mkvp: String::from("383d2a9ab781f35343554c5b3d9337cd"),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("13.000d"),
|
name: String::from("13.000d"),
|
||||||
card: 19,
|
card: 19,
|
||||||
domain: 13,
|
domain: 13,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Ep11,
|
mode: pvap::apqn_mode::Ep11,
|
||||||
info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 {
|
info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 {
|
||||||
serialnr: String::from("87HU397G150TZGR"),
|
serialnr: String::from("87HU397G150TZGR"),
|
||||||
mkvp: String::new(),
|
mkvp: String::new(),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
ap::Apqn {
|
pvap::Apqn {
|
||||||
name: String::from("13.000f"),
|
name: String::from("13.000f"),
|
||||||
card: 19,
|
card: 19,
|
||||||
domain: 15,
|
domain: 15,
|
||||||
gen: 8,
|
gen: 8,
|
||||||
mode: ap::ApqnMode::Ep11,
|
mode: pvap::apqn_mode::Ep11,
|
||||||
info: Option::None,
|
info: Option::None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_assoc_secretentry(idx: u16, hexidstr: &str) -> SecretEntry {
|
fn make_assoc_secretentry(idx: u16, hexidstr: &str) -> SecretEntry {
|
||||||
let id = hexstring_to_u8(hexidstr);
|
let id = decode_hex(hexidstr).unwrap();
|
||||||
let idlen: u32 = id.len().try_into().unwrap();
|
let idlen: u32 = id.len().try_into().unwrap();
|
||||||
let idarray: [u8; 32] = id.try_into().unwrap();
|
let idarray: [u8; 32] = id.try_into().unwrap();
|
||||||
SecretEntry::new(idx, ListableSecretType::Association, idarray.into(), idlen)
|
SecretEntry::new(idx, ListableSecretType::Association, idarray.into(), idlen)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
//! UV related functions for pvapconfig
|
//! UV related functions for pvapconfig
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use pv_core::misc::read_file_string;
|
||||||
use pv_core::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
use pv_core::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
/// The byte size of association secret of type 2 in struct SecretEntry
|
/// The byte size of association secret of type 2 in struct SecretEntry
|
||||||
pub const AP_ASSOC_SECRET_ID_SIZE: usize = 32;
|
pub const AP_ASSOC_SECRET_ID_SIZE: usize = 32;
|
||||||
@@ -25,21 +25,8 @@ const RE_UV_FAC_BIT_LIST_SECRETS: u32 = 30;
|
|||||||
/// Panics if the compilation of a static regular expression fails.
|
/// Panics if the compilation of a static regular expression fails.
|
||||||
/// Panics if RE_UV_FACILITIES does not match.
|
/// Panics if RE_UV_FACILITIES does not match.
|
||||||
pub fn has_list_secrets_facility() -> Result<(), String> {
|
pub fn has_list_secrets_facility() -> Result<(), String> {
|
||||||
if !Path::new(PATH_SYS_FW_UV_FACILITIES).is_file() {
|
let facstr =
|
||||||
return Err(format!(
|
read_file_string(PATH_SYS_FW_UV_FACILITIES, "UV facilities").map_err(|e| e.to_string())?;
|
||||||
"UV facilities sysfs attribute not found (file {} does not exist).",
|
|
||||||
PATH_SYS_FW_UV_FACILITIES
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let facstr = match crate::helper::sysfs_read_string(PATH_SYS_FW_UV_FACILITIES) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Failure reading UV facilities from {PATH_SYS_FW_UV_FACILITIES} ({:?}).",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let re_uv_facilities = Regex::new(RE_UV_FACILITIES).unwrap();
|
let re_uv_facilities = Regex::new(RE_UV_FACILITIES).unwrap();
|
||||||
if !re_uv_facilities.is_match(&facstr) {
|
if !re_uv_facilities.is_match(&facstr) {
|
||||||
Err(format!("Failure parsing UV facilities entry '{facstr}'."))
|
Err(format!("Failure parsing UV facilities entry '{facstr}'."))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pvattest"
|
name = "pvattest"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
@@ -19,7 +19,7 @@ openssl = "0.10.70"
|
|||||||
serde = { version = "1.0.217", features = ["derive"]}
|
serde = { version = "1.0.217", features = ["derive"]}
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
zerocopy = { version="0.7", features = ["derive"] }
|
zerocopy = { version="0.8", features = ["derive"] }
|
||||||
|
|
||||||
pv = { path = "../pv", package = "s390_pv" }
|
pv = { path = "../pv", package = "s390_pv" }
|
||||||
utils = { path = "../utils" }
|
utils = { path = "../utils" }
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
.\" Copyright 2024 IBM Corp.
|
.\" Copyright 2024, 2025 IBM Corp.
|
||||||
.\" 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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH pvattest-check 1 "2024-12-05" "s390-tools" "Attestation Manual"
|
.TH "PVATTEST-CHECK" "1" "2025-03-12" "s390-tools" "Attestation Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBpvattest check\fP - Check if the attestation result matches defined policies
|
pvattest-check \- Check if the attestation result matches defined policies
|
||||||
\fB
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.nf
|
.nf
|
||||||
.fam C
|
.fam C
|
||||||
@@ -17,7 +16,7 @@ pvattest check [OPTIONS] <IN> <OUT>
|
|||||||
.fi
|
.fi
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
After the attestation verification, check whether the attestation result
|
After the attestation verification, check whether the attestation result
|
||||||
complies with user-defined policies.
|
complies with user\-defined policies.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.PP
|
.PP
|
||||||
<IN>
|
<IN>
|
||||||
@@ -40,37 +39,37 @@ Define the output format.
|
|||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
.RS 4
|
.RS 4
|
||||||
- \fByaml\fP: Use yaml format.
|
\- \fByaml\fP: Use yaml format.
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-k, \-\-host-key-document <FILE>
|
\-k, \-\-host\-key\-document <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Use FILE to check for a host-key document. Verifies that the attestation
|
Use FILE to check for a host\-key document. Verifies that the attestation
|
||||||
response contains the host-key hash of one of the specified host keys. The check
|
response contains the host\-key hash of one of the specified host keys. The
|
||||||
fails if none of the host-keys match the hash in the response. This parameter
|
check fails if none of the host\-keys match the hash in the response. This
|
||||||
can be specified multiple times.
|
parameter can be specified multiple times.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-host-key-check <HOST_KEY_CHECKS>
|
\-\-host\-key\-check <HOST_KEY_CHECKS>
|
||||||
.RS 4
|
.RS 4
|
||||||
Define the host-key check policy By default, all host-key hashes are checked,
|
Define the host\-key check policy By default, all host\-key hashes are checked,
|
||||||
and it is not considered a failure if a hash is missing from the attestation
|
and it is not considered a failure if a hash is missing from the attestation
|
||||||
response. Use this policy switch to trigger a failure if no corresponding hash
|
response. Use this policy switch to trigger a failure if no corresponding hash
|
||||||
is found. Requires at least one host-key document.
|
is found. Requires at least one host\-key document.
|
||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
.RS 4
|
.RS 4
|
||||||
- \fBatt-key-hash\fP: Check the host-key used for the attestation request.
|
\- \fBatt-key-hash\fP: Check the host-key used for the attestation request.
|
||||||
|
|
||||||
- \fBboot-key-hash\fP: Check the host-key used to the boot the image.
|
\- \fBboot-key-hash\fP: Check the host-key used to the boot the image.
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-u, \-\-user-data <FILE>
|
\-u, \-\-user\-data <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Check if the provided user data matches the data from the attestation response.
|
Check if the provided user data matches the data from the attestation response.
|
||||||
.RE
|
.RE
|
||||||
@@ -78,23 +77,23 @@ Check if the provided user data matches the data from the attestation response.
|
|||||||
.PP
|
.PP
|
||||||
\-\-secret <FILE>
|
\-\-secret <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Use FILE to include as successful Add-secret request. Checks if the Attestation
|
Use FILE to include as successful Add\-secret request. Checks if the Attestation
|
||||||
response contains the hash of all specified add secret requests-tags. The hash
|
response contains the hash of all specified add secret requests\-tags. The hash
|
||||||
is sensible to the order in which the secrets where added. This means that if
|
is sensible to the order in which the secrets where added. This means that if
|
||||||
the order of adding here different from the order the add-secret requests where
|
the order of adding here different from the order the add\-secret requests where
|
||||||
sent to the UV this check will fail even though the same secrets are included in
|
sent to the UV this check will fail even though the same secrets are included in
|
||||||
the UV secret store. Can be specified multiple times.
|
the UV secret store. Can be specified multiple times.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-secret-store-locked <BOOL>
|
\-\-secret\-store\-locked <BOOL>
|
||||||
.RS 4
|
.RS 4
|
||||||
Check whether the guests secret store is locked or not. Compares the hash of the
|
Check whether the guests secret store is locked or not. Compares the hash of the
|
||||||
secret store state to the one calculated by this option and optionally specified
|
secret store state to the one calculated by this option and optionally specified
|
||||||
add-secret-requests in the correct order. If the attestation response does not
|
add\-secret\-requests in the correct order. If the attestation response does not
|
||||||
contain a secret store hash, this check fails.
|
contain a secret store hash, this check fails.
|
||||||
|
|
||||||
Required if add-secret-requests are specified.
|
Required if add\-secret\-requests are specified.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
@@ -104,16 +103,16 @@ Check whether the firmware is supported by IBM. Requires internet access.
|
|||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-firmware-verify-url <URL>
|
\-\-firmware\-verify\-url <URL>
|
||||||
.RS 4
|
.RS 4
|
||||||
Specify the endpoint to use for firmware version verification. Use an endpoint
|
Specify the endpoint to use for firmware version verification. Use an endpoint
|
||||||
you trust. Requires the \fB--firmware\fR option.
|
you trust. Requires the \-\-firmware option.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-h, \-\-help
|
\-h, \-\-help
|
||||||
.RS 4
|
.RS 4
|
||||||
Print help (see a summary with '-h').
|
Print help (see a summary with \fB\-h\fR).
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
.\" Copyright 2024 IBM Corp.
|
.\" Copyright 2024, 2025 IBM Corp.
|
||||||
.\" 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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH pvattest-create 1 "2024-12-05" "s390-tools" "Attestation Manual"
|
.TH "PVATTEST-CREATE" "1" "2025-03-12" "s390-tools" "Attestation Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBpvattest create\fP - Create an attestation measurement request
|
pvattest-create \- Create an attestation measurement request
|
||||||
\fB
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.nf
|
.nf
|
||||||
.fam C
|
.fam C
|
||||||
@@ -20,28 +19,28 @@ Create attestation measurement requests to attest an IBM Secure Execution guest.
|
|||||||
Only build attestation requests in a trusted environment such as your
|
Only build attestation requests in a trusted environment such as your
|
||||||
Workstation. To avoid compromising the attestation do not publish the
|
Workstation. To avoid compromising the attestation do not publish the
|
||||||
attestation request protection key and shred it after verification. Every
|
attestation request protection key and shred it after verification. Every
|
||||||
'create' will generate a new, random protection key.
|
\fBcreate\fR will generate a new, random protection key.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.PP
|
.PP
|
||||||
\-k, \-\-host-key-document <FILE>
|
\-k, \-\-host\-key\-document <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Use FILE as a host-key document. Can be specified multiple times and must be
|
Use FILE as a host\-key document. Can be specified multiple times and must be
|
||||||
specified at least once.
|
specified at least once.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-no-verify
|
\-\-no\-verify
|
||||||
.RS 4
|
.RS 4
|
||||||
Disable the host-key document verification. Does not require the host-key
|
Disable the host\-key document verification. Does not require the host\-key
|
||||||
documents to be valid. Do not use for a production request unless you verified
|
documents to be valid. Do not use for a production request unless you verified
|
||||||
the host-key document beforehand.
|
the host\-key document beforehand.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-C, \-\-cert <FILE>
|
\-C, \-\-cert <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Use FILE as a certificate to verify the host-key or keys. The certificates are
|
Use FILE as a certificate to verify the host\-key or keys. The certificates are
|
||||||
used to establish a chain of trust for the verification of the host-key
|
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
|
documents. Specify this option twice to specify the IBM Z signing key and the
|
||||||
intermediate CA certificate (signed by the root CA).
|
intermediate CA certificate (signed by the root CA).
|
||||||
.RE
|
.RE
|
||||||
@@ -61,11 +60,11 @@ Make no attempt to download CRLs.
|
|||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-root-ca <ROOT_CA>
|
\-\-root\-ca <ROOT_CA>
|
||||||
.RS 4
|
.RS 4
|
||||||
Use FILE as the root-CA certificate for the verification. If omitted, the system
|
Use FILE as the root\-CA certificate for the verification. If omitted, the
|
||||||
wide-root CAs installed on the system are used. Use this only if you trust the
|
system wide\-root CAs installed on the system are used. Use this only if you
|
||||||
specified certificate.
|
trust the specified certificate.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
@@ -77,12 +76,12 @@ Write the generated request to FILE.
|
|||||||
.PP
|
.PP
|
||||||
\-a, \-\-arpk <FILE>
|
\-a, \-\-arpk <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Save the protection key as unencrypted GCM-AES256 key in FILE Do not publish
|
Save the protection key as unencrypted GCM\-AES256 key in FILE Do not publish
|
||||||
this key, otherwise your attestation is compromised.
|
this key, otherwise your attestation is compromised.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-\-add-data <FLAGS>
|
\-\-add\-data <FLAGS>
|
||||||
.RS 4
|
.RS 4
|
||||||
Specify additional data for the request. Additional data is provided by the
|
Specify additional data for the request. Additional data is provided by the
|
||||||
Ultravisor and returned during the attestation request and is covered by the
|
Ultravisor and returned during the attestation request and is covered by the
|
||||||
@@ -90,20 +89,20 @@ attestation measurement. Can be specified multiple times. Optional.
|
|||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
.RS 4
|
.RS 4
|
||||||
- \fBphkh-img\fP: Request the public host-key-hash of the key that decrypted the SE-image as additional-data.
|
\- \fBphkh-img\fP: Request the public host-key-hash of the key that decrypted the SE-image as additional-data.
|
||||||
|
|
||||||
- \fBphkh-att\fP: Request the public host-key-hash of the key that decrypted the attestation request as additional-data.
|
\- \fBphkh-att\fP: Request the public host-key-hash of the key that decrypted the attestation request as additional-data.
|
||||||
|
|
||||||
- \fBsecret-store-hash\fP: Request a hash over all successful Add-secret requests and the lock state as additional-data.
|
\- \fBsecret-store-hash\fP: Request a hash over all successful Add-secret requests and the lock state as additional-data.
|
||||||
|
|
||||||
- \fBfirmware-state\fP: Request the state of the firmware as additional-data.
|
\- \fBfirmware-state\fP: Request the state of the firmware as additional-data.
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-h, \-\-help
|
\-h, \-\-help
|
||||||
.RS 4
|
.RS 4
|
||||||
Print help (see a summary with '-h').
|
Print help (see a summary with \fB\-h\fR).
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
.\" Copyright 2024 IBM Corp.
|
.\" Copyright 2024, 2025 IBM Corp.
|
||||||
.\" 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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH pvattest-perform 1 "2024-11-18" "s390-tools" "Attestation Manual"
|
.TH "PVATTEST-PERFORM" "1" "2025-03-12" "s390-tools" "Attestation Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBpvattest perform\fP - Send the attestation request to the Ultravisor
|
pvattest-perform \- Send the attestation request to the Ultravisor
|
||||||
\fB
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.nf
|
.nf
|
||||||
.fam C
|
.fam C
|
||||||
@@ -35,9 +34,9 @@ Write the result to FILE.
|
|||||||
.RE
|
.RE
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\-u, \-\-user-data <File>
|
\-u, \-\-user\-data <File>
|
||||||
.RS 4
|
.RS 4
|
||||||
Provide up to 256 bytes of user input User-data is arbitrary user-defined data
|
Provide up to 256 bytes of user input User\-data is arbitrary user\-defined data
|
||||||
appended to the Attestation measurement. It is verified during the Attestation
|
appended to the Attestation measurement. It is verified during the Attestation
|
||||||
measurement verification. May be any arbitrary data, as long as it is less or
|
measurement verification. May be any arbitrary data, as long as it is less or
|
||||||
equal to 256 bytes
|
equal to 256 bytes
|
||||||
@@ -46,7 +45,7 @@ equal to 256 bytes
|
|||||||
.PP
|
.PP
|
||||||
\-h, \-\-help
|
\-h, \-\-help
|
||||||
.RS 4
|
.RS 4
|
||||||
Print help (see a summary with '-h').
|
Print help (see a summary with \fB\-h\fR).
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
.\" Copyright 2024 IBM Corp.
|
.\" Copyright 2024, 2025 IBM Corp.
|
||||||
.\" 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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH pvattest-verify 1 "2024-12-05" "s390-tools" "Attestation Manual"
|
.TH "PVATTEST-VERIFY" "1" "2025-03-12" "s390-tools" "Attestation Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBpvattest verify\fP - Verify an attestation response
|
pvattest-verify \- Verify an attestation response
|
||||||
\fB
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.nf
|
.nf
|
||||||
.fam C
|
.fam C
|
||||||
@@ -62,23 +61,23 @@ Define the output format.
|
|||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
.RS 4
|
.RS 4
|
||||||
- \fByaml\fP: Use yaml format.
|
\- \fByaml\fP: Use yaml format.
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-u, \-\-user-data <FILE>
|
\-u, \-\-user\-data <FILE>
|
||||||
.RS 4
|
.RS 4
|
||||||
Write the user data to the FILE if any. Writes the user data, if the response
|
Write the user data to the FILE if any. Writes the user data, if the response
|
||||||
contains any, to FILE The user-data is part of the attestation measurement. If
|
contains any, to FILE The user\-data is part of the attestation measurement. If
|
||||||
the user-data is written to FILE the user-data was part of the measurement and
|
the user\-data is written to FILE the user\-data was part of the measurement and
|
||||||
verified. Emits a warning if the response contains no user-data.
|
verified. Emits a warning if the response contains no user\-data.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-h, \-\-help
|
\-h, \-\-help
|
||||||
.RS 4
|
.RS 4
|
||||||
Print help (see a summary with '-h').
|
Print help (see a summary with \fB\-h\fR).
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
.\" Copyright 2024 IBM Corp.
|
.\" Copyright 2024, 2025 IBM Corp.
|
||||||
.\" 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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH pvattest 1 "2024-11-18" "s390-tools" "Attestation Manual"
|
.TH "PVATTEST" "1" "2025-03-12" "s390-tools" "Attestation Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBpvattest\fP - create, perform, and verify attestation measurements
|
pvattest \- create, perform, and verify attestation measurements
|
||||||
\fB
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.nf
|
.nf
|
||||||
.fam C
|
.fam C
|
||||||
@@ -21,28 +20,28 @@ guest systems.
|
|||||||
.SH "PVATTEST COMMANDS"
|
.SH "PVATTEST COMMANDS"
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
\fBcreate\fR
|
\fBpvattest-create(1)\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Create an attestation measurement request
|
Create an attestation measurement request
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
\fBperform\fR
|
\fBpvattest-perform(1)\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Send the attestation request to the Ultravisor
|
Send the attestation request to the Ultravisor
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
\fBverify\fR
|
\fBpvattest-verify(1)\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Verify an attestation response
|
Verify an attestation response
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
\fBcheck\fR
|
\fBpvattest-check(1)\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Check if the attestation result matches defined policies
|
Check if the attestation result matches defined policies
|
||||||
.RE
|
.RE
|
||||||
@@ -69,7 +68,7 @@ Print version information and exit.
|
|||||||
.PP
|
.PP
|
||||||
\-h, \-\-help
|
\-h, \-\-help
|
||||||
.RS 4
|
.RS 4
|
||||||
Print help (see a summary with '-h').
|
Print help (see a summary with \fB\-h\fR).
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ pub fn check(opt: &CheckOpt) -> Result<ExitCode> {
|
|||||||
firmware_check.check(&mut issues);
|
firmware_check.check(&mut issues);
|
||||||
|
|
||||||
let res = CheckResult {
|
let res = CheckResult {
|
||||||
successful: !issues.is_empty(),
|
successful: issues.is_empty(),
|
||||||
issues,
|
issues,
|
||||||
image_host_key,
|
image_host_key,
|
||||||
attest_host_key,
|
attest_host_key,
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
use anyhow::{anyhow, bail, Error, Result};
|
use anyhow::{anyhow, bail, Error, Result};
|
||||||
use byteorder::ByteOrder;
|
|
||||||
use pv::{assert_size, request::MagicValue, uv::AttestationCmd, uv::ConfigUid};
|
use pv::{assert_size, request::MagicValue, uv::AttestationCmd, uv::ConfigUid};
|
||||||
use std::{
|
use std::{
|
||||||
io::{ErrorKind, Read, Seek, SeekFrom, Write},
|
io::{ErrorKind, Read, Seek, SeekFrom, Write},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
};
|
};
|
||||||
use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64};
|
use zerocopy::ByteOrder;
|
||||||
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
|
||||||
|
|
||||||
const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:";
|
const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:";
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes, PartialEq, Eq, Default, FromZeroes, FromBytes)]
|
#[derive(Debug, IntoBytes, PartialEq, Eq, Default, FromBytes, Immutable, KnownLayout)]
|
||||||
struct Entry {
|
struct Entry {
|
||||||
size: U32<BigEndian>,
|
size: U32<BigEndian>,
|
||||||
offset: U32<BigEndian>,
|
offset: U32<BigEndian>,
|
||||||
@@ -76,7 +76,7 @@ impl Entry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
|
||||||
struct ExchangeFormatV1Hdr {
|
struct ExchangeFormatV1Hdr {
|
||||||
magic: U64<BigEndian>,
|
magic: U64<BigEndian>,
|
||||||
version: U32<BigEndian>,
|
version: U32<BigEndian>,
|
||||||
@@ -352,8 +352,8 @@ impl ExchangeFormatRequest {
|
|||||||
bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value.",);
|
bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value.",);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hdr = ExchangeFormatV1Hdr::ref_from(buf.as_slice())
|
let hdr = ExchangeFormatV1Hdr::ref_from_bytes(buf.as_slice())
|
||||||
.ok_or(anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?;
|
.map_err(|_| anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?;
|
||||||
|
|
||||||
match TryInto::<ExchangeFormatVersion>::try_into(hdr.version)? {
|
match TryInto::<ExchangeFormatVersion>::try_into(hdr.version)? {
|
||||||
ExchangeFormatVersion::One => (),
|
ExchangeFormatVersion::One => (),
|
||||||
@@ -515,8 +515,8 @@ impl ExchangeFormatResponse {
|
|||||||
bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value.");
|
bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let hdr = ExchangeFormatV1Hdr::ref_from(buf.as_slice())
|
let hdr = ExchangeFormatV1Hdr::ref_from_bytes(buf.as_slice())
|
||||||
.ok_or(anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?;
|
.map_err(|_| anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?;
|
||||||
|
|
||||||
match TryInto::<ExchangeFormatVersion>::try_into(hdr.version)? {
|
match TryInto::<ExchangeFormatVersion>::try_into(hdr.version)? {
|
||||||
ExchangeFormatVersion::One => (),
|
ExchangeFormatVersion::One => (),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pvimg"
|
name = "pvimg"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|||||||
+168
-5
@@ -16,12 +16,177 @@
|
|||||||
#include "boot/ipl.h"
|
#include "boot/ipl.h"
|
||||||
#include "sclp.h"
|
#include "sclp.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following UV RC and RRC codes correspond to the errors
|
||||||
|
* that occur often or may be fixeable directly by the user when
|
||||||
|
* applying DIAG308 subcode 10 but configuration is unable to
|
||||||
|
* enter the secure mode. Note that it is not an exhaustive list
|
||||||
|
* of all possible UV RCs and RRCs.
|
||||||
|
*/
|
||||||
|
enum UV_RC {
|
||||||
|
UNPACK_VERIFY_MISMATCH = 0x0102,
|
||||||
|
SSC_HDR_VER_MISMATCH = 0x0104,
|
||||||
|
SSC_UNSUPPORTED_PCF = 0x0106,
|
||||||
|
SSC_HOSTKEY_HASH_ERR = 0x0108,
|
||||||
|
SSC_UNSUPPORTED_SCF = 0x0109,
|
||||||
|
SSC_HDR_CORRUPT = 0x010a,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_UNPACK_VERIFY_MISMATCH {
|
||||||
|
ALD_MISMATCH = 0x001A,
|
||||||
|
PLD_MISMATCH = 0x001B,
|
||||||
|
TLD_MISMATCH = 0x001C,
|
||||||
|
NUM_ENC_PAGES_MISMATCH = 0x001D,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_SSC_HDR_VER_MISMATCH {
|
||||||
|
HDR_VER_MISMATCH = 0x0001,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_SSC_UNSUPPORTED_PCF {
|
||||||
|
UNSUPPORTED_PCF = 0x0030,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_SSC_HOSTKEY_HASH_ERR {
|
||||||
|
HOSTKEY_MISMATCH = 0x0005,
|
||||||
|
INVAL_ECDH_KEY_HDR = 0x000B,
|
||||||
|
BACKUP_HOSTKEY_MISMATCH = 0x0034,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_SSC_UNSUPPORTED_SCF {
|
||||||
|
UNSUPPORTED_SCF = 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UV_RRC_SSC_HDR_CORRUPT {
|
||||||
|
HDR_LEN_MISMATCH = 0x0031,
|
||||||
|
HDR_SIZE_ENC_INVAL = 0x0039,
|
||||||
|
KEY_SLOT_EMPTY = 0x0040,
|
||||||
|
};
|
||||||
|
|
||||||
static volatile struct stage3a_args __section(".loader_parms") loader_parms;
|
static volatile struct stage3a_args __section(".loader_parms") loader_parms;
|
||||||
|
|
||||||
|
static void print_error_message(enum UV_RC pv_rc, uint16_t pv_rrc)
|
||||||
|
{
|
||||||
|
switch (pv_rc) {
|
||||||
|
case UNPACK_VERIFY_MISMATCH:
|
||||||
|
switch ((enum UV_RRC_UNPACK_VERIFY_MISMATCH)pv_rrc) {
|
||||||
|
case ALD_MISMATCH:
|
||||||
|
printf("Address digest list (ALD) mismatch.\n");
|
||||||
|
break;
|
||||||
|
case PLD_MISMATCH:
|
||||||
|
printf("Page digest list (PLD) mismatch.\n");
|
||||||
|
break;
|
||||||
|
case TLD_MISMATCH:
|
||||||
|
printf("Tweak digest list (TLD) mismatch.\n");
|
||||||
|
break;
|
||||||
|
case NUM_ENC_PAGES_MISMATCH:
|
||||||
|
printf("Mismatch in number of encrypted pages.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SSC_HDR_VER_MISMATCH:
|
||||||
|
if (pv_rrc == HDR_VER_MISMATCH)
|
||||||
|
printf("Mismatch in IBM Secure Execution image header version.\n");
|
||||||
|
break;
|
||||||
|
case SSC_UNSUPPORTED_PCF:
|
||||||
|
if (pv_rrc == UNSUPPORTED_PCF)
|
||||||
|
printf("An unsupported plaintext control flag is set.\n");
|
||||||
|
break;
|
||||||
|
case SSC_HOSTKEY_HASH_ERR:
|
||||||
|
switch ((enum UV_RRC_SSC_HOSTKEY_HASH_ERR)pv_rrc) {
|
||||||
|
case HOSTKEY_MISMATCH:
|
||||||
|
printf("The host key hash of the IBM Secure Execution image ");
|
||||||
|
printf("does not match the host key of the installed key bundle.\n");
|
||||||
|
break;
|
||||||
|
case INVAL_ECDH_KEY_HDR:
|
||||||
|
printf("The public customer ECDH key in the ");
|
||||||
|
printf("IBM Secure Execution image is not valid.\n");
|
||||||
|
break;
|
||||||
|
case BACKUP_HOSTKEY_MISMATCH:
|
||||||
|
printf("The secondary host key hash of the ");
|
||||||
|
printf("IBM Secure Execution image does not match the ");
|
||||||
|
printf("host key of the installed key bundle.\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Ensure that the image is ");
|
||||||
|
printf("correctly encrypted for this host.\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SSC_UNSUPPORTED_SCF:
|
||||||
|
if (pv_rrc == UNSUPPORTED_SCF)
|
||||||
|
printf("An unsupported secret control flag is set.\n");
|
||||||
|
break;
|
||||||
|
case SSC_HDR_CORRUPT:
|
||||||
|
switch ((enum UV_RRC_SSC_HDR_CORRUPT)pv_rrc) {
|
||||||
|
case HDR_LEN_MISMATCH:
|
||||||
|
printf("Mismatch in IBM Secure Execution image header size.\n");
|
||||||
|
break;
|
||||||
|
case HDR_SIZE_ENC_INVAL:
|
||||||
|
printf("The size of the encrypted area in the ");
|
||||||
|
printf("IBM Secure Execution image is invalid.\n");
|
||||||
|
break;
|
||||||
|
case KEY_SLOT_EMPTY:
|
||||||
|
printf("There are no host keys in");
|
||||||
|
printf("the IBM Secure Execution image.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_cmd_name(uint16_t pv_cmd)
|
||||||
|
{
|
||||||
|
char *cmd_name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QEMU returns command code IDs 2, 3 or 4 corresponding
|
||||||
|
* to the UV commands (SSC, UNPACK or UNPACK VERIFY) when
|
||||||
|
* DIAG 308 subcode is applied and the configuration is
|
||||||
|
* unable to enter the secure mode.
|
||||||
|
*/
|
||||||
|
switch (pv_cmd) {
|
||||||
|
case 2:
|
||||||
|
cmd_name = "KVM_PV_SET_SEC_PARMS";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
cmd_name = "KVM_PV_UNPACK";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
cmd_name = "KVM_PV_VERIFY";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// should not reach here
|
||||||
|
cmd_name = "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_diag308_unpack_pv_error(uint64_t rc)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t pv_cmd;
|
||||||
|
uint16_t pv_rrc;
|
||||||
|
uint16_t pv_rc;
|
||||||
|
uint16_t diag_rc;
|
||||||
|
};
|
||||||
|
uint64_t regs;
|
||||||
|
} resp = { .regs = rc };
|
||||||
|
|
||||||
|
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||||
|
print_error_message(resp.pv_rc, resp.pv_rrc);
|
||||||
|
panic(EPV,
|
||||||
|
"Protected boot failed: 0x%x, "
|
||||||
|
"%s - RC: 0x%x, RRC:0x%x\n",
|
||||||
|
resp.diag_rc, get_cmd_name(resp.pv_cmd), resp.pv_rc, resp.pv_rrc);
|
||||||
|
}
|
||||||
|
|
||||||
void __noreturn start(void)
|
void __noreturn start(void)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
volatile struct stage3a_args *args = &loader_parms;
|
volatile struct stage3a_args *args = &loader_parms;
|
||||||
|
uint64_t rc;
|
||||||
|
|
||||||
/* calculate the IPIB memory address */
|
/* calculate the IPIB memory address */
|
||||||
struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs);
|
struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs);
|
||||||
|
|
||||||
@@ -46,10 +211,8 @@ void __noreturn start(void)
|
|||||||
panic(EPV, "Protected boot setup has failed: 0x%x\n", rc);
|
panic(EPV, "Protected boot setup has failed: 0x%x\n", rc);
|
||||||
|
|
||||||
rc = diag308(DIAG308_UNPACK_PV, 0x0);
|
rc = diag308(DIAG308_UNPACK_PV, 0x0);
|
||||||
if (rc != DIAG308_RC_OK) {
|
if (rc != DIAG308_RC_OK)
|
||||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
report_diag308_unpack_pv_error(rc);
|
||||||
panic(EPV, "Protected boot has failed: 0x%x\n", rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::{Parser, ValueEnum, ValueHint};
|
||||||
|
use log::info;
|
||||||
|
use pv::{
|
||||||
|
misc::{open_file, parse_hex, read_file},
|
||||||
|
request::SymKey,
|
||||||
|
};
|
||||||
|
use pvimg::{
|
||||||
|
error::Result,
|
||||||
|
uvdata::{
|
||||||
|
KeyExchangeTrait, SeHdr, SeHdrBinV1, SeHdrData, SeHdrDataV1, SeHdrVersioned,
|
||||||
|
UvDataPlainTrait, UvDataTrait,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::{fs::File, io::Write, path::PathBuf};
|
||||||
|
use utils::{PvLogger, VerbosityOptions};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
struct Cli {
|
||||||
|
/// Use INPUT as the Secure Execution image.
|
||||||
|
#[arg(short, long, value_name = "INPUT", value_hint = ValueHint::FilePath,)]
|
||||||
|
infile: PathBuf,
|
||||||
|
|
||||||
|
/// Use INPUT as the Secure Execution image.
|
||||||
|
#[arg(short, long, value_name = "OUTPUT", value_hint = ValueHint::FilePath,)]
|
||||||
|
outfile: PathBuf,
|
||||||
|
|
||||||
|
/// Use the key in FILE to decrypt the Secure Execution header.
|
||||||
|
/// It is the key that was specified with the command line option
|
||||||
|
/// '--hdr-key' at the Secure Execution image creation.
|
||||||
|
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
|
||||||
|
hdr_key: PathBuf,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
verbosity: VerbosityOptions,
|
||||||
|
|
||||||
|
/// Hdr Value to tamper with
|
||||||
|
#[arg(long, value_enum)]
|
||||||
|
tamp: TampVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||||
|
pub enum TampVal {
|
||||||
|
/// ALD.
|
||||||
|
AddressListDigest,
|
||||||
|
/// PLD.
|
||||||
|
PageListDigest,
|
||||||
|
/// TLD.
|
||||||
|
TweakListDigest,
|
||||||
|
/// Change lenght of secure header
|
||||||
|
HdrSize,
|
||||||
|
/// customer ECDH key
|
||||||
|
CustomerPublicKey,
|
||||||
|
/// host key hash
|
||||||
|
HostKey,
|
||||||
|
/// Secret control flags
|
||||||
|
SecretControlFlag,
|
||||||
|
/// Plaintext control flags
|
||||||
|
PlaintextControlFlag,
|
||||||
|
/// Number of keyslots
|
||||||
|
NumKeySlots,
|
||||||
|
/// Size encrypted area
|
||||||
|
SizeEncArea,
|
||||||
|
/// Number of encrypted pages
|
||||||
|
NumEncPages,
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOGGER: PvLogger = PvLogger;
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let opt = Cli::parse();
|
||||||
|
LOGGER
|
||||||
|
.start(opt.verbosity.to_level_filter())
|
||||||
|
.with_context(|| "Failed to set-up logger")?;
|
||||||
|
info!("Reading Secure Execution header {}", opt.infile.display());
|
||||||
|
let mut input = open_file(&opt.infile)?;
|
||||||
|
|
||||||
|
SeHdr::seek_sehdr(&mut input, None)?;
|
||||||
|
let hdr = SeHdr::try_from_io(&mut input)?;
|
||||||
|
let mut hdr_encr_v1: SeHdrBinV1 =
|
||||||
|
<SeHdrVersioned as TryInto<SeHdrBinV1>>::try_into(hdr.clone().data).expect("SE-header V1");
|
||||||
|
let mut decryption_required = false;
|
||||||
|
let mut hdr_encr: SeHdr;
|
||||||
|
|
||||||
|
// Tamper with parts of SE header that doesn't require decryption
|
||||||
|
match &opt.tamp {
|
||||||
|
TampVal::HdrSize => hdr_encr_v1.aad.sehs += 1,
|
||||||
|
TampVal::HostKey => hdr_encr_v1.aad.keyslots[0].phkh[..32].copy_from_slice(&[0; 32]),
|
||||||
|
TampVal::PlaintextControlFlag => {
|
||||||
|
let hex_str = String::from_utf8(
|
||||||
|
read_file("/sys/firmware/uv/query/supp_se_hdr_pcf", "input file").unwrap(),
|
||||||
|
)
|
||||||
|
.expect("PCF support not found");
|
||||||
|
let hex = parse_hex(&hex_str);
|
||||||
|
let mut hex_pad = [0u8; 8];
|
||||||
|
hex_pad[(8 - hex.len())..].copy_from_slice(&hex);
|
||||||
|
let supported_pcf: u64 = u64::from_be_bytes(hex_pad);
|
||||||
|
hdr_encr_v1.aad.pcf = 0xFFFF_FFFF_FFFF_D5FF - supported_pcf;
|
||||||
|
}
|
||||||
|
TampVal::NumKeySlots => hdr_encr_v1.aad.nks = 0,
|
||||||
|
TampVal::SizeEncArea => hdr_encr_v1.aad.sea = 0,
|
||||||
|
_ => decryption_required = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if decryption_required {
|
||||||
|
let key = SymKey::try_from_data(
|
||||||
|
hdr.key_type(),
|
||||||
|
read_file(&opt.hdr_key, "Reading key")?.into(),
|
||||||
|
)?;
|
||||||
|
let mut hdr_plain = hdr.decrypt(&key)?;
|
||||||
|
let mut hdr_v1: SeHdrDataV1 =
|
||||||
|
<SeHdrData as TryInto<SeHdrDataV1>>::try_into(hdr_plain.clone().data)
|
||||||
|
.expect("SE-header V1");
|
||||||
|
|
||||||
|
// Tamper with the SE header data
|
||||||
|
match &opt.tamp {
|
||||||
|
TampVal::AddressListDigest => hdr_v1.aad.ald[..8].copy_from_slice(&[0; 8]),
|
||||||
|
TampVal::PageListDigest => hdr_v1.aad.pld[..8].copy_from_slice(&[0; 8]),
|
||||||
|
TampVal::TweakListDigest => hdr_v1.aad.tld[..8].copy_from_slice(&[0; 8]),
|
||||||
|
TampVal::CustomerPublicKey => {
|
||||||
|
hdr_v1.aad.cust_pub_key.coord[..160].copy_from_slice(&[0; 160])
|
||||||
|
}
|
||||||
|
TampVal::SecretControlFlag => hdr_v1.data.value_mut().scf = 0xFFFF_FFFF_FFFF_FFFF,
|
||||||
|
TampVal::NumEncPages => hdr_v1.aad.nep = 0,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
hdr_plain.data = hdr_v1.into();
|
||||||
|
hdr_encr = hdr_plain.encrypt(&key)?;
|
||||||
|
} else {
|
||||||
|
hdr_encr = hdr.clone();
|
||||||
|
hdr_encr.data = hdr_encr_v1.clone().into();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::copy(&opt.infile, &opt.outfile)?;
|
||||||
|
match tamper_image_file(&opt.outfile, hdr_encr) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
std::fs::remove_file(&opt.outfile)?;
|
||||||
|
panic!("Could not seek SE header: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tamper_image_file(outfile: &PathBuf, hdr_encr: SeHdr) -> Result<()> {
|
||||||
|
let mut output = File::options().read(true).write(true).open(outfile)?;
|
||||||
|
SeHdr::seek_sehdr(&mut output, None)?;
|
||||||
|
output.write_all(&hdr_encr.as_bytes()?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
.\" Copyright 2024 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" "2024-12-19" "s390-tools" "Genprotimg Manual"
|
|
||||||
.nh
|
|
||||||
.ad l
|
|
||||||
.SH NAME
|
|
||||||
genprotimg \- Create an IBM Secure Execution image
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.nf
|
|
||||||
.fam C
|
|
||||||
genprotimg [OPTIONS] --kernel <FILE> --output <FILE> --host-key-document <FILE> <--no-verify|--cert <FILE>>
|
|
||||||
.fam C
|
|
||||||
.fi
|
|
||||||
.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.
|
|
||||||
.PP
|
|
||||||
|
|
||||||
.SH OPTIONS
|
|
||||||
.PP
|
|
||||||
\-i, \-\-kernel, \-\-image <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a
|
|
||||||
raw binary s390x Linux kernel. The ELF format is not supported.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-r, \-\-ramdisk <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use the content of FILE as the Linux initial RAM disk.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-p, \-\-parmfile <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use the content of FILE as the Linux kernel command line. The Linux kernel
|
|
||||||
command line must be shorter than the maximum kernel command line size supported
|
|
||||||
by the given Linux kernel.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-o, \-\-output <FILE>
|
|
||||||
.RS 4
|
|
||||||
Write the generated Secure Execution boot image to FILE.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-k, \-\-host\-key\-document <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use FILE as a host\-key document. Can be specified multiple times and must be
|
|
||||||
specified at least once.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-no\-verify
|
|
||||||
.RS 4
|
|
||||||
Disable the host\-key document verification. Does not require the host\-key
|
|
||||||
documents to be valid. Do not use for a production request unless you verified
|
|
||||||
the host\-key document beforehand.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-C, \-\-cert <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use FILE as a certificate to verify the host\-key or keys. The certificates are
|
|
||||||
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).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-crl <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use FILE as a certificate revocation list (CRL). The list is used to check
|
|
||||||
whether a certificate of the chain of trust is revoked. Specify this option
|
|
||||||
multiple times to use multiple CRLs.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-offline
|
|
||||||
.RS 4
|
|
||||||
Make no attempt to download CRLs.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-root\-ca <ROOT_CA>
|
|
||||||
.RS 4
|
|
||||||
Use FILE as the root\-CA certificate for the verification. If omitted, the
|
|
||||||
system wide\-root CAs installed on the system are used. Use this only if you
|
|
||||||
trust the specified certificate.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-no\-component\-check
|
|
||||||
.RS 4
|
|
||||||
Disable all input component checks. For example, for the Linux kernel, it tests
|
|
||||||
if the given kernel looks like a raw binary s390x kernel.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-overwrite
|
|
||||||
.RS 4
|
|
||||||
Overwrite an existing Secure Execution boot image.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-cck, \-\-comm\-key <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
|
||||||
contain exactly 32 bytes of data.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-hdr\-key <FILE>
|
|
||||||
.RS 4
|
|
||||||
Use the content of FILE as the Secure Execution header protection key. The file
|
|
||||||
must contain exactly 32 bytes of data. If the option is not specified, the
|
|
||||||
Secure Execution header protection key is a randomly generated key.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-dump
|
|
||||||
.RS 4
|
|
||||||
Enable Secure Execution guest dump support. This option requires the
|
|
||||||
\fB\-\-cck\fR option.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-dump
|
|
||||||
.RS 4
|
|
||||||
Disable Secure Execution guest dump support (default).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-cck\-extension\-secret
|
|
||||||
.RS 4
|
|
||||||
Add\-secret requests must provide an extension secret that matches the
|
|
||||||
CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-cck\-extension\-secret
|
|
||||||
.RS 4
|
|
||||||
Add\-secret requests don't have to provide the CCK\-derived extension secret
|
|
||||||
(default).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-pckmo
|
|
||||||
.RS 4
|
|
||||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
|
||||||
functions (default).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-pckmo
|
|
||||||
.RS 4
|
|
||||||
Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
|
||||||
functions.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-pckmo\-hmac
|
|
||||||
.RS 4
|
|
||||||
Enable the support for the HMAC PCKMO key encryption function.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-pckmo\-hmac
|
|
||||||
.RS 4
|
|
||||||
Disable the support for the HMAC PCKMO key encryption function (default).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-backup\-keys
|
|
||||||
.RS 4
|
|
||||||
Enable the support for backup target keys.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-backup\-keys
|
|
||||||
.RS 4
|
|
||||||
Disable the support for backup target keys (default).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-enable\-image\-encryption
|
|
||||||
.RS 4
|
|
||||||
Enable encryption of the image components (default). The image components are:
|
|
||||||
the kernel, ramdisk, and kernel command line.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-disable\-image\-encryption
|
|
||||||
.RS 4
|
|
||||||
Disable encryption of the image components. The image components are: the
|
|
||||||
kernel, ramdisk, and kernel command line. Use only if the components used do not
|
|
||||||
contain any confidential content (for example, secrets like non\-public
|
|
||||||
cryptographic keys).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-v, \-\-verbose
|
|
||||||
.RS 4
|
|
||||||
Provide more detailed output.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-q, \-\-quiet
|
|
||||||
.RS 4
|
|
||||||
Provide less output.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-\-version
|
|
||||||
.RS 4
|
|
||||||
Print version information and exit.
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
.PP
|
|
||||||
\-h, \-\-help
|
|
||||||
.RS 4
|
|
||||||
Print help (see a summary with \fB\-h\fR).
|
|
||||||
.RE
|
|
||||||
.RE
|
|
||||||
|
|
||||||
.SH EXIT STATUS
|
|
||||||
.TP 8
|
|
||||||
.B 0 \- Program finished successfully
|
|
||||||
The command was executed successfully.
|
|
||||||
.RE
|
|
||||||
.TP 8
|
|
||||||
.B 1 \- Generic error
|
|
||||||
Something went wrong during the operation. Refer to the error
|
|
||||||
message.
|
|
||||||
.RE
|
|
||||||
.TP 8
|
|
||||||
.B 2 \- Usage error
|
|
||||||
The command was used incorrectly, for example: unsupported command
|
|
||||||
line flag, or wrong number of arguments.
|
|
||||||
.RE
|
|
||||||
.SH EXAMPLES
|
|
||||||
These are examples of how to generate an IBM Secure Execution image in
|
|
||||||
\fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
|
|
||||||
initrd in \fI\,/boot/initrd.img\/\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 when creating a Secure Execution image with guest dump support
|
|
||||||
enabled in the second example.
|
|
||||||
|
|
||||||
Generate an IBM Secure Execution image:
|
|
||||||
|
|
||||||
.PP
|
|
||||||
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\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/secure\-linux\/\fR
|
|
||||||
|
|
||||||
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
|
||||||
.PP
|
|
||||||
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\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/secure\-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
|
||||||
.SH NOTES
|
|
||||||
.IP "1." 4
|
|
||||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
|
||||||
.IP "2." 4
|
|
||||||
An ELF file cannot be used as a Linux kernel image.
|
|
||||||
.IP "3." 4
|
|
||||||
Remember to re-run \fBzipl\fR after updating an IBM Secure Execution image.
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.sp
|
|
||||||
\fBpvimg-create\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
|
|
||||||
@@ -3,7 +3,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.
|
||||||
.\"
|
.\"
|
||||||
|
|
||||||
.TH "PVIMG-CREATE" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
|
.TH "PVIMG-CREATE" "1" "2025-04-24" "s390-tools" "Pvimg Manual"
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@@ -139,7 +139,7 @@ Secure Execution header protection key is a randomly generated key.
|
|||||||
\-\-enable\-dump
|
\-\-enable\-dump
|
||||||
.RS 4
|
.RS 4
|
||||||
Enable Secure Execution guest dump support. This option requires the
|
Enable Secure Execution guest dump support. This option requires the
|
||||||
\fB\-\-cck\fR option.
|
\fB\-\-cck\fR or \fB\-\-enable\-cck\-update\fR option.
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
@@ -163,6 +163,19 @@ Add\-secret requests don't have to provide the CCK\-derived extension secret
|
|||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
|
\-\-enable\-cck\-update
|
||||||
|
.RS 4
|
||||||
|
Enable CCK update support. Requires z17 or up. This option cannot be
|
||||||
|
used in conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-\-disable\-cck\-update
|
||||||
|
.RS 4
|
||||||
|
Disable CCK update support (default).
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
\-\-enable\-pckmo
|
\-\-enable\-pckmo
|
||||||
.RS 4
|
.RS 4
|
||||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||||
|
|||||||
+33
-6
@@ -72,6 +72,7 @@ pub fn validate_cli(opts: &CliOptions) -> Result<(), clap::error::Error> {
|
|||||||
|
|
||||||
/// CLI Argument collection for handling input components.
|
/// CLI Argument collection for handling input components.
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
|
#[cfg_attr(test, derive(Default))]
|
||||||
pub struct ComponentPaths {
|
pub struct ComponentPaths {
|
||||||
/// Use the content of FILE as a raw binary Linux kernel.
|
/// Use the content of FILE as a raw binary Linux kernel.
|
||||||
///
|
///
|
||||||
@@ -93,11 +94,14 @@ pub struct ComponentPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
|
#[cfg_attr(test, derive(Default))]
|
||||||
|
#[command(
|
||||||
|
group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])),
|
||||||
|
group(ArgGroup::new("cck-available").multiple(true)))]
|
||||||
pub struct CreateBootImageLegacyFlags {
|
pub struct CreateBootImageLegacyFlags {
|
||||||
/// Enable Secure Execution guest dump support. This option requires the
|
/// Enable Secure Execution guest dump support. This option requires the
|
||||||
/// '--cck' option.
|
/// '--cck' or '--enable-cck-update' option.
|
||||||
#[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
#[arg(long, action = clap::ArgAction::SetTrue, requires = "cck-available", group="header-flags")]
|
||||||
pub enable_dump: Option<bool>,
|
pub enable_dump: Option<bool>,
|
||||||
|
|
||||||
/// Disable Secure Execution guest dump support (default).
|
/// Disable Secure Execution guest dump support (default).
|
||||||
@@ -115,6 +119,15 @@ pub struct CreateBootImageLegacyFlags {
|
|||||||
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_extension_secret", group="header-flags")]
|
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_extension_secret", group="header-flags")]
|
||||||
pub disable_cck_extension_secret: Option<bool>,
|
pub disable_cck_extension_secret: Option<bool>,
|
||||||
|
|
||||||
|
/// Enable CCK update support. Requires z17 or up. This option cannot be
|
||||||
|
/// used in conjunction with the '--enable-cck-extension-secret' option.
|
||||||
|
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_extension_secret", group="cck-available", group="header-flags")]
|
||||||
|
pub enable_cck_update: Option<bool>,
|
||||||
|
|
||||||
|
/// Disable CCK update support (default).
|
||||||
|
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_update", group="header-flags")]
|
||||||
|
pub disable_cck_update: Option<bool>,
|
||||||
|
|
||||||
/// Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
/// Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||||
/// functions (default).
|
/// functions (default).
|
||||||
#[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
|
#[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
|
||||||
@@ -306,6 +319,7 @@ impl GenprotimgCliOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
#[cfg_attr(test, derive(Default))]
|
||||||
pub struct CreateBootImageArgs {
|
pub struct CreateBootImageArgs {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub component_paths: ComponentPaths,
|
pub component_paths: ComponentPaths,
|
||||||
@@ -332,7 +346,12 @@ pub struct CreateBootImageArgs {
|
|||||||
///
|
///
|
||||||
/// The file must contain exactly 32 bytes of data. This option used to be
|
/// The file must contain exactly 32 bytes of data. This option used to be
|
||||||
/// called '--comm-key' in previous versions.
|
/// called '--comm-key' in previous versions.
|
||||||
#[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
#[arg(
|
||||||
|
long,
|
||||||
|
value_name = "FILE",
|
||||||
|
group = "cck-available",
|
||||||
|
visible_alias = "comm-key"
|
||||||
|
)]
|
||||||
pub cck: Option<PathBuf>,
|
pub cck: Option<PathBuf>,
|
||||||
|
|
||||||
/// Use the content of FILE as the Secure Execution header protection key.
|
/// Use the content of FILE as the Secure Execution header protection key.
|
||||||
@@ -352,6 +371,7 @@ pub struct CreateBootImageArgs {
|
|||||||
|
|
||||||
/// Experimental options
|
/// Experimental options
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
|
#[cfg_attr(test, derive(Default))]
|
||||||
pub struct CreateBootImageExperimentalArgs {
|
pub struct CreateBootImageExperimentalArgs {
|
||||||
/// Manually set the directory used to load the Secure Execution bootloaders
|
/// Manually set the directory used to load the Secure Execution bootloaders
|
||||||
/// (stage3a and stage3b) (experimental option).
|
/// (stage3a and stage3b) (experimental option).
|
||||||
@@ -491,7 +511,8 @@ mod test {
|
|||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||||
CliOption::new("comm-key", ["--cck", "/dev/null"])])),
|
CliOption::new("comm-key", ["--cck", "/dev/null"])])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||||
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
CliOption::new("comm-key", ["--comm-key", "/dev/null"]),
|
||||||
|
CliOption::new("enable-cck-update", ["--enable-cck-update"])])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
|
||||||
CliOption::new("x-scf", ["--x-scf", "0x0"])])),
|
CliOption::new("x-scf", ["--x-scf", "0x0"])])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-psw", ["--x-psw", "0x0"])])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-psw", ["--x-psw", "0x0"])])),
|
||||||
@@ -503,6 +524,9 @@ mod test {
|
|||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--x-header-key", "/dev/null"]),])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--x-header-key", "/dev/null"]),])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key", "/dev/null"]),])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key", "/dev/null"]),])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-cck-update", ["--enable-cck-update"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-cck-update", ["--disable-cck-update"])])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("multiple-cck", ["--disable-cck-update", "--cck", "/dev/null"])])),
|
||||||
];
|
];
|
||||||
let invalid_create_args = [
|
let invalid_create_args = [
|
||||||
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
||||||
@@ -510,7 +534,7 @@ mod test {
|
|||||||
flat_map_collect(remove(mvcanv.clone(), "hkd")),
|
flat_map_collect(remove(mvcanv.clone(), "hkd")),
|
||||||
flat_map_collect(remove(mvcanv, "output")),
|
flat_map_collect(remove(mvcanv, "output")),
|
||||||
|
|
||||||
// missing `--comm-key`
|
// missing both `--cck' and `--enable-cck-update'
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"])])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"])])),
|
||||||
|
|
||||||
// -v and -q cannot be combined
|
// -v and -q cannot be combined
|
||||||
@@ -531,6 +555,9 @@ mod test {
|
|||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
||||||
CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key"]),])),
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key"]),])),
|
||||||
|
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("extension", ["--enable-cck-extension-secret"]),
|
||||||
|
CliOption::new("update", ["--enable-cck-update"])])),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut genprotimg_valid_args = vec![
|
let mut genprotimg_valid_args = vec![
|
||||||
|
|||||||
@@ -49,43 +49,32 @@ fn parse_flags(
|
|||||||
args: &CreateBootImageArgs,
|
args: &CreateBootImageArgs,
|
||||||
) -> Result<(PlaintextControlFlagsV1, SecretControlFlagsV1)> {
|
) -> Result<(PlaintextControlFlagsV1, SecretControlFlagsV1)> {
|
||||||
let lf = &args.legacy_flags;
|
let lf = &args.legacy_flags;
|
||||||
|
macro_rules! flag_disabled {
|
||||||
|
($cli_flag:expr, $control_flags:expr) => {
|
||||||
|
$cli_flag
|
||||||
|
.filter(|x| *x)
|
||||||
|
.and(Some(ControlFlagTrait::all_disabled($control_flags)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! flag_enabled {
|
||||||
|
($cli_flag:expr, $control_flags:expr) => {
|
||||||
|
$cli_flag
|
||||||
|
.filter(|x| *x)
|
||||||
|
.and(Some(ControlFlagTrait::all_enabled($control_flags)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let plaintext_flags: Vec<FlagData<PcfV1>> = [
|
let plaintext_flags: Vec<FlagData<PcfV1>> = [
|
||||||
lf.disable_dump
|
flag_disabled!(lf.disable_dump, [PcfV1::AllowDumping]),
|
||||||
.filter(|x| *x)
|
flag_enabled!(lf.enable_dump, [PcfV1::AllowDumping]),
|
||||||
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
|
flag_disabled!(lf.disable_pckmo, PlaintextControlFlagsV1::PCKMO),
|
||||||
lf.enable_dump
|
flag_enabled!(lf.enable_pckmo, PlaintextControlFlagsV1::PCKMO),
|
||||||
.filter(|x| *x)
|
flag_disabled!(lf.disable_pckmo_hmac, [PcfV1::PckmoHmac]),
|
||||||
.and(Some(PcfV1::all_enabled([PcfV1::AllowDumping]))),
|
flag_enabled!(lf.enable_pckmo_hmac, [PcfV1::PckmoHmac]),
|
||||||
lf.disable_pckmo
|
flag_disabled!(lf.disable_backup_keys, [PcfV1::BackupTargetKeys]),
|
||||||
.filter(|x| *x)
|
flag_enabled!(lf.enable_backup_keys, [PcfV1::BackupTargetKeys]),
|
||||||
.and(Some(PcfV1::all_disabled([
|
flag_enabled!(lf.disable_image_encryption, [PcfV1::NoComponentEncryption]),
|
||||||
PcfV1::PckmoAes,
|
flag_disabled!(lf.enable_image_encryption, [PcfV1::NoComponentEncryption]),
|
||||||
PcfV1::PckmoDeaTdea,
|
|
||||||
PcfV1::PckmoEcc,
|
|
||||||
]))),
|
|
||||||
lf.enable_pckmo.filter(|x| *x).and(Some(PcfV1::all_enabled([
|
|
||||||
PcfV1::PckmoAes,
|
|
||||||
PcfV1::PckmoDeaTdea,
|
|
||||||
PcfV1::PckmoEcc,
|
|
||||||
]))),
|
|
||||||
lf.disable_pckmo_hmac
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_disabled([PcfV1::PckmoHmac]))),
|
|
||||||
lf.enable_pckmo_hmac
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_enabled([PcfV1::PckmoHmac]))),
|
|
||||||
lf.disable_backup_keys
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_disabled([PcfV1::BackupTargetKeys]))),
|
|
||||||
lf.enable_backup_keys
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))),
|
|
||||||
lf.disable_image_encryption
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_enabled([PcfV1::NoComponentEncryption]))),
|
|
||||||
lf.enable_image_encryption
|
|
||||||
.filter(|x| *x)
|
|
||||||
.and(Some(PcfV1::all_disabled([PcfV1::NoComponentEncryption]))),
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -95,16 +84,16 @@ fn parse_flags(
|
|||||||
assert!(PlaintextControlFlagsV1::no_duplicates(&plaintext_flags));
|
assert!(PlaintextControlFlagsV1::no_duplicates(&plaintext_flags));
|
||||||
|
|
||||||
let secret_flags: Vec<FlagData<ScfV1>> = [
|
let secret_flags: Vec<FlagData<ScfV1>> = [
|
||||||
lf.disable_cck_extension_secret
|
flag_disabled!(
|
||||||
.filter(|x| *x)
|
lf.disable_cck_extension_secret,
|
||||||
.and(Some(ScfV1::all_disabled([
|
[ScfV1::CckExtensionSecretEnforcement]
|
||||||
ScfV1::CCKExtensionSecretEnforcment,
|
),
|
||||||
]))),
|
flag_enabled!(
|
||||||
lf.enable_cck_extension_secret
|
lf.enable_cck_extension_secret,
|
||||||
.filter(|x| *x)
|
[ScfV1::CckExtensionSecretEnforcement]
|
||||||
.and(Some(ScfV1::all_enabled([
|
),
|
||||||
ScfV1::CCKExtensionSecretEnforcment,
|
flag_disabled!(lf.disable_cck_update, [ScfV1::CckUpdateAllowed]),
|
||||||
]))),
|
flag_enabled!(lf.enable_cck_update, [ScfV1::CckUpdateAllowed]),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -219,3 +208,29 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
|
|||||||
warn!("Successfully generated the Secure Execution image.");
|
warn!("Successfully generated the Secure Execution image.");
|
||||||
Ok(OwnExitCode::Success)
|
Ok(OwnExitCode::Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::cli::CreateBootImageLegacyFlags;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_flags() {
|
||||||
|
let args = CreateBootImageArgs {
|
||||||
|
legacy_flags: CreateBootImageLegacyFlags {
|
||||||
|
enable_dump: Some(true),
|
||||||
|
enable_cck_update: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let parsed_flags = super::parse_flags(&args).expect("Failed to parse flags {args:?}");
|
||||||
|
let mut exp_pcf = Vec::from(PlaintextControlFlagsV1::PCKMO);
|
||||||
|
exp_pcf.push(PcfV1::AllowDumping);
|
||||||
|
let pcf = PlaintextControlFlagsV1::from_flags(PcfV1::all_enabled(exp_pcf));
|
||||||
|
assert_eq!(parsed_flags.0, pcf);
|
||||||
|
let exp_scf = vec![ScfV1::CckUpdateAllowed];
|
||||||
|
let scf = SecretControlFlagsV1::from_flags(ScfV1::all_enabled(exp_scf));
|
||||||
|
assert_eq!(parsed_flags.1, scf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user