Compare commits

...

87 Commits

Author SHA1 Message Date
Jörn Siglen
940ce67900 New release s390-tools-2.39.0
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 10:32:38 +02:00
Peter Oberparleiter
c12faf0128 zdev/dracut: Prevent loading of unused kernel modules
zdev provides dracut logic for configuring devices early during boot via
kernel command line parameters rd.dasd and rd.zfcp. A copy of resulting
configuration directives is made available to the booted Linux using
chzdev's export function.

The use of command line option --all in this export step results in
chzdev loading all kernel modules required by supported types, even if
no device of that type exists on the booted system, or if no zdev kernel
parameter was specified.

Loading unused kernel modules during boot increases memory usage,
prolongs boot time, and expands the kernel's attack surface
unnecessarily.

Fix this by replacing command line option --all with --configured to
instruct chzdev to export only data for devices with a persistent
configuration, while not loading any kernel modules.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Shalini Chellathurai Saroja
a3d50487ed systemd: Suppress cpi service error message on SEL guests
On SEL guests, the default configuration does not permit the
Control-Program Identifiation (CPI) data to be sent by the cpi
service, resulting in a failure message in systemctl/log output
on start of the cpi service.

This is not a failure because it is expected behavior and the
cpi service is a one-shot service whose function is already
performed when the exit code is returned. Prevent the error log
by filtering the associated exit code in systemd.

Signed-off-by: Shalini Chellathurai Saroja <shalini@linux.ibm.com>
Acked-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Shalini Chellathurai Saroja
b1d8e40bfe scripts/cpictl: Modify name of the exit code 6
Modify the symbolic name of the exit code 6 to
EXIT_NOTCONFIGURED, which is according to the Linux Standard Base
(LSB) specification.

Signed-off-by: Shalini Chellathurai Saroja <shalini@linux.ibm.com>
Suggested-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Steffen Eiden
8d3949f77b rust: Cargo.lock Update crate dependencies
cargo update -p openssl -p curl -p usafe-libyaml

By default users of the libs will use the version tagged in the
lockfile. Let make things easier for them and update the lock entries
with versins that have some CVEs fixed.

Note that this does not affect code shipped by distros as they ignore the
lockfile.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
2f49025d49 cpumf/man: Unify parameter case
Normalize parameter casing in man pages to improve consistency.

Reviewed-by:  Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
4c9ceeeb64 cpumf/lshwc: Add command line flag to specify individual counters
Add command-line option --counters "AAA,BBB,...,ZZZ" to specify a
comma-separated list of counter names to display. Counter names must
match exactly, ignoring case. All counters not listed are excluded
from output.

Reviewed-by:  Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
3a0c394fa2 libcpumf: Relocate ctr_in_list() for shared use
Move ctr_in_list() from cpumf/lspai.c to a shared location to enable
reuse in other binaries that require counter list filtering.

Reviewed-by:  Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Richie Buturla
20a4ebd83c zipl/boot: Fix undefined behaviour logic in menu
The code within 'menu_param()' previously assumed that a read from sclp
will never fail.

If 'sclp_param()' fails then 'endptr' is never initialised and
'loadparm' is compared with 'endptr' which is undefined behvaiour.

If a sclp read fails, an undefined 'endptr' is never accessed, and upon
a failed read, will return a new error code 'SCLP_ERROR' instead of
returning 'NUMBER_FOUND' which is incorrect logic wise.

Remove compare conditions and assignments of 0 in 'value', as 'value'
is initialised with 0 ('DEFAULT_MENU_ENTRY') and cannot be non zero,
only in the case where a number is found and we go to boot.

Logic:

Check if we got a number and boot from it.

If 'PRINT_PROMPT', break out to menu print logic.

If an 'SCLP_ERROR' occurs, print an error message and boot the default
since 'value' is initialised with 'DEFAULT_MENU_ENTRY'.

If 'NOTHING_FOUND', check if the menu is disabled. If disabled, go to
default boot. Otherwise break out to print logic.

Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Richie Buturla <richie@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Richie Buturla
4a9f66fc31 zipl/boot: Change error codes to enums
Change error codes returned from menu_param to enums.

Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Richie Buturla <richie@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
35d68b2101 dasdinfo: Fix memory leak by freeing readbuf
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
522c67a2c3 dasdinfo: Fix format-overflow warning
Use snprintf() instead of sprintf() to avoid buffer overflow.
Also change the integer type from signed to unsigned.

Error:
	dasdinfo.c: In function 'dinfo_create_devnode':
	dasdinfo.c:297:52: warning: '%04d' directive writing between 4 and 11
	bytes into a region of size 5 [-Wformat-overflow=]
	  297 |                         sprintf(filename, "dasdinfo%04d", retry);
	      |                                                    ^~~~

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
f4ee6c2c8d dasdinfo: Remove unused util_base.h include
The util_base.h header is no longer required in dasdinfo.c and can be
safely removed to reduce unnecessary dependencies.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
377f2a59f5 dasdinfo: Replace hardcoded /sys with util_path_sysfs()
The sysfs path is now constructed dynamically in an allocated buffer to
avoid potential buffer overflows. The default is '/sys', until the
SYSFS_ROOT environment variable is defined.

These modifications significantly improve testability by allowing sysfs
read and write operations to be redirected to an alternative file path,
which enables testing without affecting the active system state.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:57:06 +02:00
Jan Polensky
176f0405b8 .pre-commit-config.yaml: Add check-yaml, check-json and check-toml hooks
These hooks validate syntax of YAML, JSON and TOML files using
pre-commit-hooks.

Link: https://github.com/pre-commit/pre-commit-hooks
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:56:50 +02:00
Jan Polensky
839d34e758 .pre-commit-config.yaml: Bump pre-commit-hooks
Command:

	$ pre-commit autoupdate

Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
a68d1e323b libccw/ccw.c: Include strings.h instead of string.h
Including string.h is incorrect as it doesn't provide the required
function prototype for strncasecmp() used in this file. Note that
the missing function prototype doesn't cause a compilation error
at the moment as the declaration is provided indirectly.

Use strings.h to provide the declaration for strncasecmp() directly,
avoiding unnecessary include dependencies and improving clarity.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
549c7ef9e7 zkey/kms.c: Use correct variable in warnx()
The variable filename was freed inside the loop and then incorrectly
used in the subsequent warnx() call. Replacing it with directory
reflects the indented context and avoids use after free.

Fixes: f25aaf32b8 ("zkey: Add support for key management system plugins")
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
103c9444c9 zkey/kms.c: Fix typos
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
fe7bef3f09 cpumf/pai.c: Fix memory use after free
Preventing a race condition by freeing the pmuname variable only after
its last usage.

Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
8a88ada9c5 dasdinfo/dasdinfo.c: Fix string termination
Use correct buffer and index to terminate string returned by readlink().

Reviewed-by: Juergen Christ <jchrist@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
538bc7b4e8 dasdinfo/dasdinfo.c: Replace buffer size magic number with anonymous enum
Use a local anonymous enum to define the buffer size constant, replacing
the hardcoded magic number and improving readability.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Juergen Christ <jchrist@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
b56451e5a4 cpumf/lspai.c: Use util_str_toupper() instead of str2uc()
Replace the custom str2uc() implementation with util_str_toupper() to
reduce code duplication and improve maintainability.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Thomas Richter
63f9a672d0 cpumf/lspai: Add command line flag to specify individual counters
Add command line option --counters XXX,YYY to specify a comma
separated list of counter names to be displayed. The counter names
have to match exactly beside case sensitivity.
All counters not listed in the list specified by the --counters option
are not shown.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Co-developed-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Thomas Richter
2c5c5ef4b4 cpumf/lspai: Add command line flags for all cpus
Add a command line flag '-a' to include the counter set values
from each individual CPU. Up to now only the total sum from
all CPUs was printed.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Thomas Richter
5a3ffc2036 cpumf/lspai: Add command line flag for delta values
Add command line flag -d to display the counter value in form of
a delta value. This format shows the increase of that counter value
compared to the previous value.
The first line is the base for the delta calculation and always
shows 'Total'. The remaining lines show 'Delta' in the third
column.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Thomas Richter
e576443859 cpumf/lspai: Add command line flag for short counter names
Add command line flag -s to display the counter name in an
abbreviated form. The abbreviated form is a character for the
PAI counter set followed by a number.
PAI crypto counter set is abbreviated to 'C'.
PAI NNPA counter set is abbreviated to 'N'.
The number refers to the defined counter number ranging from
1 to X.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Thomas Richter
43067df78e cpumf/lspai: Add command line flags for hexadecimal output
Add command line flags -x and -X to print counter values in
hexadecimal format with leading 0x prefix (-X) and without (-x).

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
973059438f cpumf/lspai: Show counter values
Add command line flags -i (read interval) and -l (loops) to
show the counter values for the specified number of iterations.
The CPUs to read the counter values from can be specified
on the command line. If none are specified, all online CPUs
are used.

For example the command '# lspai -l 2 -i 10 -t nnpa'
displays all PAI NNPA counter values two times with 10 second
interval.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Co-developed-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
9d3567d9ce libutil/util_libc.c: Move remove_whitespace() to util_str_rm_whitespace()
Relocate remove_whitespace() from zdsfs to libutil/util_str.c, renaming
it to util_str_rm_whitespace() to make the helper available
project-wide.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
7687b0fcda cpumf/lspai: Fix whitespace alignment
Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
6f70f34b79 cpumf/lspai: Rename show_format() -> format_painode()
Renamed the function show_format() to format_painode() to better reflect
its purpose.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:15 +02:00
Jan Polensky
37aed81745 cpumf/lspai: Rename show_painode() -> list_painode()
Renamed the function show_painode() to list_painode() to better reflect
its purpose.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:14 +02:00
Jan Polensky
5aa1824eef libcpumf: Move perf_event_open to reduce code duplication
Relocated the perf_event_open logic into a shared helper to eliminate
redundant implementations across multiple files. Enhances consistency,
reduces maintenance overhead, and lowers structural complexity.

Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:14 +02:00
Jan Polensky
82603e589a libcpumf: Fix typo
Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:14 +02:00
Jan Polensky
3ba897a1be cpumf/lscpumf.c: Fix typos
Acked-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:14 +02:00
Alexander Egorenkov
fa92a7f7c9 README.md: Update dependencies for zipl
Update Dependencies section for zipl with the information of
required tools/packages for stand-alone NVMe and list-directed DASD
dump.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Acked-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-10-14 09:39:14 +02:00
Thomas Huth
f569849e57 zipl/boot: Fix EBCDIC code page 500 conversion and decrease size by 200 bytes
The tables ebcdic_037 and ebcdic_500 are currently exactly the same, though
the 500 should differ at 7 entries (see the tables in the Linux kernel or
https://www.ibm.com/docs/en/db2-for-zos/12.0.0?topic=ebcdic-code-point-differences-between-ccsids
for example). Fix it by removing the ebcdic_500 table and by patching
the other table during start() if necessary. That way we can safe also
about 200 bytes in the stage3.bin binary.

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/125
Closes: https://github.com/ibm-s390-linux/s390-tools/pull/190
Suggested-by: Euan Bourke <euan@linux.ibm.com>
Tested-by: Euan Bourke <euan@linux.ibm.com>
Reviewed-by: Euan Bourke <euan@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:33 +02:00
Ingo Franzki
458c27a0e1 zkey-ekmfweb: Fix a potential compile error
Fix a potential compile error when symbol
EKMF_SUPPORTS_RSA_PSS_CERTIFICATES is defined. With this symbol support
for RSA-PSS can be enabled for the EKMFWeb plugin. Currently this symbol
is never defined.

Fixes: c570f51f5f ("zkey-ekmfweb: Generate certificate or CSR with identity key")
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:33 +02:00
Ingo Franzki
e4dcf084c5 libekmfweb: Fix generation of certificate or CSR with RSA-PSS instead of RSA
Currently a certificate or certificate signing request generated by the
zkey EKMFWeb library erroneously always uses RSA-PSS as signing algorithm,
although EKMFWeb does not support RSA-PSS certificates in all versions.

This bug was introduced with the rework to use libseckey for secure
key crypto operations.

Fixes: 26c34a49b1 ("libekmfweb: Make use of the new libseckey")
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:33 +02:00
Richie Buturla
bed1bc8676 zipl/boot/stage3.c: Fix [-Wnull-dereference] warning
Fix 'volatile' qualifier warning,
indirection of non-volatile null pointer will be deleted, not trap.

Acked-by: Ilya Leoshkevich <iii@linux.ibm.com>
Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Richie Buturla <richie@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:33 +02:00
Richie Buturla
8e315c4401 zipl/src/scan.c: Fix [-Wparentheses] warning
Fix warning of redundant parentheses.

Acked-by: Ilya Leoshkevich <iii@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Richie Buturla <richie@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
ae8fa4371f pvimg: Update manpage
This update adds documentation for the new CLI option '--image-key',
updates the '--cck' description, and escapes dashes.

Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
29ff9408e0 rust/pvimg: Add support for '--image-key'
Add support for '--image-key'. This new option can be used to select the
components encryption key (e.g. kernel, initrd, and kernel command
line). Previously, this was only available as an experimental
option ('--x-comp-key').

Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
a24be25779 pvimg: Add CLI tests for '--x-comp-key'
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
58337d7f90 rust/pvimg: Refactor keys arguments into own struct
This makes it easier to add new user keys related CLI options.

Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
fcaad5a8e2 pvimg: Improve phrasing of '--cck' option documentation
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Ingo Franzki
4d598ade86 zkey: Add support for generating and importing exportable secure keys
Normally, secure keys generated by zkey are intentionally export
restricted. Export restricted keys can not be wrapped with a key
encrypting key (KEK).

However, keys that are generated inside a Secure Execution for Linux
guest that shall also be used outside of the Secure Execution for Linux
guest can only be transported to outside the Secure Execution for Linux
guest by wrapping them with a KEK and unwrapping them outside of the
Secure Execution for Linux guest. For that such keys must be exportable.

Add an option to generate and import exportable secure keys, which then
can be wrapped, and thus transported to outside of a Secure Execution for
Linux guest.

This applies to keys of type 'CCA-AESCIPHER' and 'EP11-AES'. Keys of type
'CCA-AESDATA' are always exportable, and can not be export restricted.

For keys of type 'EP11-AES' additionally allow to set the
'wrap-with-trusted' attribute. This restricts the key so that it only can
be wrapped with a trusted key encrypting key.

For keys of type 'EP11-AES' to be exportable, the access control point
(ACP) XCP_CPB_ALLOW_COMBINED_EXTRACT must be 'ON' on all APQNs used. This
access control point is only supported on newer EP11 firmware levels. If
the access control point is 'OFF' or not supported by the EP11 firmware,
then the IOCTL to generate the key fails with a generic error
(Input/output error - EIO). The zkey tool prints an appropriate error
message in that case.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Ingo Franzki
b455ee9a31 zkey: Fix typo in error message
Fixes: bb6a47db55 ("Add KMS support for 'zkey generate' command")
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Vineeth Vijayan
82b9328e48 zdev: Fix double device configuration with rd.dasd
While parsing the rd.dasd kernel parameter, the dracut module
currently creates two separate udev rules for a single DASD — one
for ECKD type and one for FBA type. Because the kernel parameter
alone does not provide enough information to reliably determine the
DASD type, this dual configuration can lead to inconsistencies.

Update the logic to determine the DASD type dynamically by parsing
the modalias of available devices. If a device is not present during
boot, both udev rules will be generated.

Also add --no-module-load to the chzdev functions, because during this
time, we do not want chzdev to load the dasd module. The goal here is
to generate the right udev-rules only.

Suggested-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Vineeth Vijayan
f45a45c38c zdev: Fix double device configuration on DPM systems
During autoconfig, DPM systems currently configures DASD devices twice:
once for dasd-eckd and once for dasd-fba. Because the firmware
configuration file does not provide the DASD type, this can lead
to inconsistent and redundant configurations.

For example, lszdev may show two devices with the same ID:

    TYPE       ID        ON   PERS  NAMES
    dasd-eckd  0.0.f001  yes  auto  dasda
    dasd-fba   0.0.f001  no   auto

However, only dasd-eckd is actually present on the machine, and
the dasd-fba entry is incorrect.

Modify this configuration logic to determine the DASD type dynamically
by parsing the modalias for each device-ID, and configures only the
correct dasd-type. The resulting lszdev output accurately reflects
the actual DASD devices present.

Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Vineeth Vijayan
d1bf9e68c6 zdev: Introduce --no-module-load option for chzdev
When called with a device parameter, chzdev tries to load the kernel
module of the corresponding device type. This might not be desired in
certain situations like early initial RAM-disk processing.

To support this use cases, add a new command-line option
--no-module-load to prevent the loading of any kernel module by chzdev.

Suggested-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Vineeth Vijayan
0356d78bc2 zdev: Allow dynamic control of module load
module_load_suppress() currently ignores the state parameter. Modify
it to respect the state parameter, so that the module load suppress
can be both enabled or disabled.

Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
a4f48415a0 zipl/boot: Fix typo found by codespell
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
c9f0e3accf zipl/boot: menu: Reset the buffer before doing a retry
The 'temp_area' buffer was not zeroed out for a retry in case of an
invalid input to select a boot menu entry.

Before:

  zIPL v2.38.0-build-20250822 interactive boot menu

   0. default (1TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...

   1. 1TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
   2. 2TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
   3. 3TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
  Note: VM users please use '#cp vi vmsg <input>'

  Please choose (default will boot in 3 seconds):111
  Error: undefined configuration

  Please choose:1
  Error: undefined configuration

After:

  zIPL v2.38.0-build-20250822 interactive boot menu

   0. default (1TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...

   1. 1TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
   2. 2TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
   3. 3TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
  Note: VM users please use '#cp vi vmsg <input>'

  Please choose (default will boot in 3 seconds):111
  Error: undefined configuration

  Please choose:1
  Booting 1TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...

Acked-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Marc Hartmayer
9dd432ca94 zipl/boot: Remove useless cast
'temp_area' is already a 'char *' no reason to cast it to 'char *'.

Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Eduard Shishkin
179bc57cdf zipl/src: Use a matrix of component ranges instead of an array
Fix a design mistake leading to IPL programs corruption.
Use a matrix of component ranges to locate the added components in the
body of bootmap file instead of an array.
Earlier an array of NR_PROGRAM_COMPONENTS was used, which is incorrect
Now a matrix of (NR_PROGRAM_COMPONENTS X BOOT_MENU_ENTRIES) is used.

Don't duplicate environment block for each menu entry. Instead, reuse
the one that was added when processing the first menu entry.

Tested-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reported-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:32 +02:00
Finn Callies
e1d1c6df9c rust-utils: remove unused renameat2 rust wrapper
Remove the unused rust wrapper function renameat2 which calls renameat2
from linux' libc implementation.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:47:29 +02:00
Finn Callies
e894fb61d8 rust-utils: use exists/rename instead of renameat2
Use exists and rename instead of renameat2 which is only available in the
linux implementation of libc. To enable compilation of pvsecret on macos
the rust wrapper function renameat2 which calls the libc renameat2 function
is replaced with rust native std::fs::exists ad std::fs::rename functions
because macos' implementation of libc does not have the renameat2 function.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:41:00 +02:00
Finn Callies
c3a84109b7 rust-utils: add PartialEq for AtomicFileOperation
Derive PartialEq for the AtomicFileOperation enum to allow comparison with
== operator.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:16 +02:00
Eduard Shishkin
1ca5f63dee zipl/src: Use generic infrastructure when adding environment block
Apply previously introduced generic infrastructure to add an
environment block as a "buffer component".


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:16 +02:00
Eduard Shishkin
60bc1e38d0 zipl/src: Reuse data of buffer components in bootmap
Don't duplicate data of components added via add_component_buffer()
and friends to bootmap file. Instead, reuse data that were previously
added when preparing a program table for the first mirror to create
metadata (block lists, program tables, etc) specific for other mirrors.


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:16 +02:00
Eduard Shishkin
431e4542ca zipl/src: Reuse data of file components in bootmap
If the option '--add-files' is specified, don't duplicate data of
components added via add_component_file() and friends to bootmap
file for each mirror. Instead, reuse the data that were added when
preparing a program table for the first mirror to create metadata
(block lists, program tables, etc) specific for other mirrors.


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:16 +02:00
Eduard Shishkin
731f00202c zipl/src: Enable heterogeneous mirrors support
Drop limitations on identical target parameters of base disks per
logical device;

Fix verbose zipl output to include geometry of each mirror and
component load addresses that would be used when booting from each
mirror.


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:16 +02:00
Eduard Shishkin
2945593991 zipl/src: Support (create/boot-with) different program tables per target
Replace struct install_set with a new type containing multiple
program tables and program components. Refactor the code accordingly.

When retrieving device INFO, for each mirror complete a respective
structure disk_info in the INFO.BASE array.

When building a bootmap, for each mirror create a separate program
table (or a pair of tables in case of DASD - for CCW-TYPE IPL and
for LD-IPL), using respective components in the arrays INFO.BASE and
BIS.MIRRORS

Make data of program components added via get_component_buffer() and
get_component_file() be duplicated per each mirror.

Make boot record on each mirror point out to a respective program
table in the bootmap (when booting from different mirrors, different
program tables in the bootmap are used).

This patch doesn't make functional changes. However, test cases
comparing boot meta-data dumps of different mirrors may fail (since
boot records on different mirrors now refer different copies of boot
data). This will be fixed by the next patches in the series which
allow boot data to be reused.


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Eduard Shishkin
f375327085 zipl/src: Support multiple disk_info structures per logical device
Replace struct disk_info with new 2 types.

Old type:

struct disk_info {
  A; /* logical device info */
  B; /* basedisk info */
};

New types:

struct disk_info {
  B; /* basedisk info */
};

and

struct device_info {
  A; /* logical device info */
  struct disk_info C [MAX_TARGETS]; /* array of base disks */
};

Here A (logical device info) is the following:

dev_t device;     /* logical device for bootmap creation */
char *name;       /* name of logical device as reetrieved from
                     "/proc/partitions" */
char *drv_name;   /* name of the driver managing the logical
                     device  as retrieved from "/proc/devices",
                     or evaluated */
int fs_block_size;

Refactor the code respectively, to use only the first element of
the array C, so that this patch represents an equivalent transform.


Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Peter Oberparleiter
338bea49c2 chpstat: Clarify scope of --scale option
Add a man page note to clarify that BPS value scaling only applies to
human-readable output format.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Peter Oberparleiter
d15e36cf24 chpstat: Add options to select IEC units for scaling
Add options to select power-of-two based IEC units such as KiB/s
(1024 B/s) for scaling throughput values in human readable reports.

Option            Scaling factor
================================
--scale ki        1024
--scale mi        1048576
--scale gi        1073741824
--scale auto-iec  IEC units

Reviewed-by: Jimmy Brisson <jbrisson@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Peter Oberparleiter
4b76530788 chpstat: Use SI units for default scaling
chpstat uses power-of-two based IEC units such as KiB/s (1024 B/s) when
scaling throughput values in human readable reports. A user interpreting
these numbers can easily be confused by chpstat's default column header
("B/s") to assume that numbers are scaled using power-of-ten based SI
units such as kB/s (1000 B/s).

In addition, users that have worked with similar z/OS tooling to display
channel path activity are used to SI-based scaling, which increases the
chance of chpstat reports being misinterpreted.

To reduce this confusion, use SI units for throughput scaling:

Option          Old factor      New factor
==========================================
--scale k       1024            1000
--scale m       1048576         1000000
--scale g       1073741824      1000000000
--scale auto    IEC units       SI units

Note that machine-readable format produced via option --format is not
affected by scaling.

Reviewed-by: Jimmy Brison <jbrisson@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Peter Oberparleiter
cb1c002bed chpstat: Ensure consistent table group spacing
In tabular output, chpstat adds an additional space between columns
of different groups (e.g. between READ and WRITE data). Currently this
is done by increasing the column width of the first column in a group.

Depending on the value displayed in this first column of a group, the
spacing may vanish, and under certain circumstances, the precision of
auto-scaled *_PART columns may be higher than the precision of the
related *_TOTAL columns, resulting in a confusing view.

Example:

DPU   READ(B/s)  WRITE(B/s)
 ID  PART TOTAL  PART TOTAL
  6 10.7M 10.7M 10.8M 10.8M
  6  0.00 11.3K  0.00  0.00
   ^^          ^^

Fix this by consistently adding padding between column groups without
increasing the column width of the first column of a group. Also ensure
the same width for *_PART and *_TOTAL columns.

Reviewed-by: Jimmy Brisson <jbrisson@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Eduard Shishkin
d6f7eff5b5 zipl_helper.device-mapper: Fix segmentation fault in an error path
Fix segmentation fault when trying to process not supported dm-targets
Release allocated memory in error paths

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-09-15 11:12:15 +02:00
Peter Oberparleiter
e1692b7079 chpstat: Fix scaling of DPU utilization calculation
chpstat incorrectly reports DPU utilization as fraction instead of
percent. Fix this by scaling utilization values to 100%.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-08-13 13:52:23 +02:00
Mikhail Zaslonko
54b3c9ef24 zipl/ngdump: Bail out if ngdump meta could not be saved
Bail out if the ngdump meta file could not be updated.
Save the boot log also for normal exit.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-08-13 13:52:23 +02:00
Marc Hartmayer
48743aa28b zpwr: Use 'LDLIBS' for specifying the math library
Use 'LDLIBS' to specify the libraries that the tool is linked against.
If it is specified as a dependency, the build system may attempt to
build it and fail.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-08-13 13:52:23 +02:00
Mete Durlu
7ce988ac59 zconf/chccwdev: Fix example descriptions
Match "chccwdev --online" example's description with its function
and improve "--safeoffline" example's wording.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-08-13 13:52:23 +02:00
Marc Hartmayer
f4fec5f120 rust/pvimg/boot: Use 'PVIMG_PKGDATADIR'
Use 'PVIMG_PKGDATADIR' as it is used by the 'pvimg_pkg_data' macro rule.
The problem only shows up if someone changes 'PVIMG_PKGDATADIR' to
something different than the default.

Fixes: f4cf4ae6eb ("rust: Add a new tool called 'pvimg'")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Steffen Eiden
2739b8a488 pv/examples: Add example for calculating host-key hashes
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Shalini Chellathurai Saroja
ce9c518b97 cpi: Disable CPI for SEL guests by default
The cpictl utility sends control-program identification data
from protected virtualization guests to hosts by default.
This behaviour leaks the below potentially sensitive
information to untrusted hosts.
- system_type
- system_level
- sysplex_name
- system_name

To prevent this behaviour, enhance the cpictl utility to stop
setting CPI information on protected virtualization guests by
default. If the user chooses to set the CPI information, it
could be set by one of the below options
- use the command line option --permit-cpi
- set the environment variable CPI_PERMIT_ON_PVGUEST to 1 to
control the CPI service behaviour during boot

Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Shalini Chellathurai Saroja <shalini@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Jörn Siglen
bd2a2cc6a1 dbginfo.sh: Enhance logging on timeout triggered
Rework command type selection and timeout trigger.
Add log entry when the timeout stopps a command or a non "zero" rc happens.

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Jörn Siglen <SIGLEN@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Mikhail Zaslonko
6547d1ae4c zdump/ngdump: Add error messages and debug trace logs
Add debug trace logs for ngdump_read_meta_from_device().

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Jörn Siglen
650265e296 AUTHORS.md: Remove duplication
Same name written in two versions

Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-29 18:23:49 +02:00
Jan Höppner
b122acb422 iucvterm: Install symlink for lsiucvallow.8 man page
lsiucvallow is just a symlink to chiucvallow for 'chiucvallow --list'.
Only a man page for chiucvallow is provided though. A corresponding man
page for lsiucvallow could be expected by the user. Certain linter such
as linitan warn about the missing man page, too.

Install lsiucvallow.8 as a symlink to chiucvallow.8 to make the
documentation of these tools more accessible.

Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/170
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-09 16:59:09 +02:00
Dan Horák
9710f29770 man: Use CR for constant width font
With version 1.23 groff/troff disabled the non-portable font CW and
started to complain about a missing CW font when previewing some of the
man pages, with messages like

$ man --warnings cpumf/man/pai.8 > /dev/null
troff:<standard input>:244: warning: cannot select font 'CW'

Use CR to replace CW.

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/187
[hoeppner@linux.ibm.com: Adapt commit message]
Signed-off-by: Dan Horák <dan@danny.cz>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-08 10:23:08 +02:00
Nikita Dubrovskii
ed01bac8e2 zipl/src: Fix typos
Closes: https://github.com/ibm-s390-linux/s390-tools/pull/186
Signed-off-by: Nikita Dubrovskii <nikita@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-08 10:23:08 +02:00
Nikita Dubrovskii
66d3aa849a zipl/src: remove global sync() call on exit
Remove the final sync(2), as zipl(8) already uses fsync(2), syncfs(2)
and ioctl(BLKFLSBUF) to flush all performed modifications.

Github-ID: https://github.com/ibm-s390-linux/s390-tools/pull/186
Signed-off-by: Nikita Dubrovskii <nikita@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-08 10:23:08 +02:00
Eduard Shishkin
95e4b3413a zipl/src: Prepare for global sync(2) removal
zipl tool calls sync(2) before exit, which may hang on attempts to
flush not relevant problematic mounts (e.g. nfs) [1].

Complete any modification performed by zipl(8) tool with calling
fsync(2), or syncfs(2). This allows to get rid of the mentioned
sync(2) call.

[1] https://github.com/openshift/os/issues/1720
Github-ID: https://github.com/ibm-s390-linux/s390-tools/pull/186
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-07-08 10:23:08 +02:00
Steffen Eiden
06e0d569f1 Prepare for next release
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-06-25 11:43:27 +02:00
97 changed files with 2685 additions and 1134 deletions

View File

@@ -2,7 +2,7 @@
exclude: \.(bin|crl|crt|key)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v6.0.0
hooks:
- id: check-merge-conflict
- id: end-of-file-fixer
@@ -11,6 +11,9 @@ repos:
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
exclude_types: ['rust']
- id: check-yaml
- id: check-json
- id: check-toml
- repo: local
hooks:
- id: git-clang-format
@@ -24,7 +27,7 @@ repos:
minimum_pre_commit_version: "2.9.0"
types_or: [c++, c]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.1
rev: v2.4.1
hooks:
- id: codespell
exclude_types: ['rust']

View File

@@ -110,6 +110,7 @@ List of all individuals having contributed content to s390-tools
- Raimund Schroeder
- Ralph Wuerthner
- Rene Trumpp
- Richie Buturla
- Rolf Schaefer
- Sa Liu
- Sascha Silbe

View File

@@ -1,6 +1,38 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.39.0 (2025-10-14)__
For Linux kernel version: 6.16 / 6.17
Changes of existing tools:
- chpstat: Add options to select IEC units for scaling (SI units are default)
- chzdev: Introduce --no-module-load option
- cpi: Disable CPI for SEL guests by default
- dbginfo.sh: Enhance logging on timeout triggered
- iucvterm: Install symlink for lsiucvallow.8 man page
- lshwc: Add command line flag to specify individual counters
- lspai: Add command line flag for delta values
- lspai: Add command line flag for short counter names
- lspai: Add command line flag to specify individual counters
- lspai: Add command line flags for all cpus
- lspai: Add command line flags for hexadecimal output
- man: Use CR for constant width font
- pvimg: Add '--image-key' option
- zdev: Allow dynamic control of module load
- zipl/boot: Fix EBCDIC code page 500 conversion and decrease size by 200 bytes
- zipl: Add support of heterogeneous mirrors (remove technical limitations
on mirrored targets, thus allowing mirrored devices consist of partitions
at different offsets on disks of different types and geometry).
- zkey: Add support for generating and importing exportable secure keys
Bug Fixes:
- chpstat: Fix scaling of DPU utilization calculation
- zdev/dracut: Prevent loading of unused kernel modules
- zdev: Fix double device configuration on DPM systems
- zdev: Fix double device configuration with rd.dasd
- zipl_helper.device-mapper: Fix segfault in an error path
* __v2.38.0 (2025-06-25)__
For Linux kernel version: 6.14 / 6.15

View File

@@ -414,6 +414,18 @@ the different tools are provided:
dumping).
Removing the inflate modules and function prototypes are the only major
modifications made to the kernel version of zlib.
For stand-alone NVMe and list-directed DASD dump, the following tools/packages
are required to populate dumper's initramfs:
- cat
- cut
- findmnt
- logsave
- makedumpfile
- mount
- poweroff
- sha256sum
- sync
- umount
* zgetdump
For building zgetdump you need OpenSSL version 1.1.0 or newer

View File

@@ -32,7 +32,7 @@ endif
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION := 2
RELEASE := 38
RELEASE := 39
PATCHLEVEL := 0
DISTRELEASE := build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)

View File

@@ -22,7 +22,7 @@ ALL_CPPFLAGS += -DVERSION=$(VERSION)
all: check_dep cpacfstats cpacfstatsd
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
$(rootdir)/libutil/libutil.a
$(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o

View File

@@ -30,6 +30,7 @@
#include <libudev.h>
#include "cpacfstats.h"
#include "lib/libcpumf.h"
#include "../include/lib/zt_common.h"
/* correlation between counter and perf counter string */
@@ -123,16 +124,6 @@ static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
return NULL;
}
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
return ret;
}
static int perf_supported(void)
{
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);

View File

@@ -39,7 +39,7 @@
static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
/* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold
* as payload data. Each SDBT is one PAGE (4096 bytes) and continas 512 eight
* as payload data. Each SDBT is one PAGE (4096 bytes) and contains 512 eight
* byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT
* points to another SDBT and can not store payload.
*/
@@ -139,7 +139,7 @@ static struct samples { /* Sample definition for all machines */
* The CPU Measurement facility has a first and second version number.
*
* The first version number governs basic counter set and the
* problem state counter set. Currently used are first verion numbers 1 and 3.
* problem state counter set. Currently used are first version numbers 1 and 3.
* The counter numbers are identifical for version number 1 and 3, but
* have different purpose and description.
*
@@ -627,7 +627,7 @@ static struct counters cpumcf_z10_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1D_L3_LOCAL_WRITES",
.desc = "A directory write to the Level-1 D-Cache directory"
"\n\t\twhere the installtion cache line was source from"
"\n\t\twhere the installation cache line was source from"
"\n\t\tthe Level-3 cache that is on the same book as the"
"\n\t\tData cache (Local L2 cache)",
},
@@ -1916,7 +1916,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
},
{
@@ -1924,7 +1924,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom On-Chip memory",
},
{
@@ -1932,7 +1932,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
},
{
@@ -2342,7 +2342,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
},
{
@@ -2350,7 +2350,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom On-Chip memory",
},
{
@@ -2358,7 +2358,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
.desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced"
"\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
},
{

View File

@@ -37,7 +37,7 @@
#include "lib/util_base.h"
#include "lib/util_path.h"
#include "lib/util_scandir.h"
#include "lib/util_libc.h"
#include "lib/util_str.h"
#include "lib/util_file.h"
#include "lib/util_fmt.h"
#include "lib/libcpumf.h"
@@ -61,6 +61,7 @@ static bool hideundef;
static bool delta, firstread;
static int output_format = FMT_CSV;
static bool quote_all;
static char *ctrlist; /* Comma separated list of counter to extract */
static unsigned int max_possible_cpus; /* No of possible CPUs */
static struct ctrname { /* List of defined counters */
@@ -151,6 +152,8 @@ static bool read_counternames(void)
return false;
}
for (i = 0; i < count && ctr >= 0; i++) {
if (!ctr_in_list(namelist[i]->d_name, ctrlist))
continue;
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name);
@@ -779,6 +782,12 @@ static struct util_opt opt_vec[] = {
.argument = "FORMAT",
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
},
{
.option = { "counters", required_argument, NULL, 'c' },
.argument = "LIST",
.flags = UTIL_OPT_FLAG_NOSHORT,
.desc = "Specify comma separated list of counters to display"
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
@@ -892,6 +901,12 @@ int main(int argc, char **argv)
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
output_format = fmt;
break;
case 'c':
hideundef = true;
ctrlist = util_strdup(optarg);
util_str_rm_whitespace(optarg, ctrlist);
util_str_toupper(ctrlist);
break;
}
}
@@ -927,5 +942,6 @@ int main(int argc, char **argv)
free_counternames();
free(check);
free(ioctlbuffer);
free(ctrlist);
return ch;
}

View File

@@ -6,7 +6,6 @@
/* List available Processor Assist Instrumentation (PAI) counters. */
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
@@ -17,7 +16,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <linux/perf_event.h>
#include "lib/util_base.h"
#include "lib/util_file.h"
@@ -28,26 +32,68 @@
#include "lib/util_path.h"
#include "lib/util_prg.h"
#include "lib/util_scandir.h"
#include "lib/util_str.h"
#include "lib/libcpumf.h"
#define OPT_FORMAT 256 /* --format XXX option */
#define STR_SUB(x) #x
#define STR(x) STR_SUB(x)
#define OPT_FORMAT 256 /* --format XXX option */
#define DEFAULT_LOOP_INTERVAL 60 /* loop interval in seconds */
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS"),
{
.option = { "all", no_argument, NULL, 'a' },
.desc = "Displays all CPUs in output"
},
{
.option = { "delta", no_argument, NULL, 'd' },
.desc = "Display delta counter values"
},
{
.option = { "counters", required_argument, NULL, 'c' },
.argument = "LIST",
.desc = "Specify comma separated list of counters to display"
},
{
.option = { "format", required_argument, NULL, OPT_FORMAT },
.argument = "FORMAT",
.flags = UTIL_OPT_FLAG_NOSHORT,
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
},
{
.option = { "loops", required_argument, NULL, 'l' },
.argument = "COUNT",
.desc = "Number of read operations"
},
{
.option = { "interval", required_argument, NULL, 'i' },
.argument = "SECONDS",
.desc = "Time to wait between loop iterations (default "
STR(DEFAULT_LOOP_INTERVAL) "s)"
},
{
.option = { "numeric", no_argument, NULL, 'n' },
.desc = "Sort PAI counters by counter number"
},
{
.option = { "short", no_argument, NULL, 's' },
.desc = "Abbreviate counter name with counter set letter and number"
},
{
.option = { "type", required_argument, NULL, 't' },
.argument = "TYPE",
.desc = "Type of PAI counters to show: crypto, nnpa"
},
{
.option = { "hex0x", no_argument, NULL, 'X' },
.desc = "Counter values in hexadecimal format with leading 0x"
},
{
.option = { "hex", no_argument, NULL, 'x' },
.desc = "Counter values in hexadecimal format"
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
@@ -59,14 +105,24 @@ static const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2023,
.pub_last = 2023,
.pub_last = 2025,
},
UTIL_PRG_COPYRIGHT_END
}
};
static bool allcpu; /* Show counter values per CPU */
static bool numsort; /* If true sort counter numerically */
static bool shortname; /* Use abbreviated counter names */
static bool delta, firstread; /* Display delta values */
static int output_format = -1; /* Generate style if >= 0 */
static unsigned int max_cpus; /* # of CPUs to read counter values from */
static unsigned int max_fds; /* # of file descriptor to read counter values */
static unsigned long loops; /* # loops */
static unsigned long read_interval = DEFAULT_LOOP_INTERVAL;
static cpu_set_t cpu_online_mask;
static char *ctrformat = "%ld"; /* Default counter output format */
static char *ctrlist; /* Comma separated list of counter to extract */
#define PAI_PATH "/bus/event_source/devices/%s"
@@ -78,9 +134,18 @@ enum pai_types { /* Bit mask for supported PAI counters */
static int pai_types_show;
struct pai_cpudata { /* Event data per CPU */
int fd; /* Event file descriptor */
int cpu; /* CPU number */
unsigned long value; /* Event value */
unsigned long prev_value; /* Previous value for deltas */
};
struct pai_ctrname { /* List of defined counters */
char *name; /* Counter name */
unsigned long nr; /* Counter number */
unsigned long total; /* Total count on all CPus */
struct pai_cpudata *data; /* Counter data per CPU */
};
struct pai_node { /* Head for PAI counter sets */
@@ -113,6 +178,20 @@ static unsigned long pai_type_base(enum pai_types t)
return 0;
}
/* Return character for this counter set. */
static char pai_type_char(enum pai_types t)
{
switch (t) {
case pai_type_crypto:
return 'C';
case pai_type_nnpa:
return 'N';
case pai_type_max:
break;
}
return 'U';
}
/* Test PAI counter name from command line option. */
static const char *pai_type_name(enum pai_types t)
{
@@ -154,16 +233,6 @@ static int pai_ctrcmp(const void *p1, const void *p2)
return l->nr > r->nr ? 1 : -1;
}
/* Convert string to upper case. */
static char *str2uc(const char *s)
{
char *uc = util_strdup(s), *old_uc = uc;
for (; *uc; ++uc)
*uc = toupper(*uc);
return old_uc;
}
/* Read counter names and assigned event number from sysfs file tree.
* Exit when sysfs directory can not be scanned.
*/
@@ -171,7 +240,7 @@ static void read_counternames(struct pai_node *node)
{
int i, more = 0, ctr = 0, count = 0;
struct dirent **namelist = NULL;
char *path, *ctrpath;
char *path, *ctrpath, sname[128];
/* Read counter names and assigned event number. */
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
@@ -184,9 +253,18 @@ static void read_counternames(struct pai_node *node)
for (i = 0; i < count && ctr >= 0; i++) {
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
snprintf(sname, sizeof(sname), "%c%ld",
pai_type_char(node->type), ctr - node->base);
if (!ctr_in_list(sname, ctrlist) &&
!ctr_in_list(namelist[i]->d_name, ctrlist)) {
/* Counter not listed in --counters option */
continue;
}
node->ctrlist[node->ctridx].data = NULL;
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
node->ctrlist[node->ctridx++].nr = ctr;
more++;
max_fds++;
} else {
warnx("Cannot parse %s", ctrpath);
}
@@ -199,7 +277,7 @@ static void read_counternames(struct pai_node *node)
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
}
static void show_format(enum util_fmt_t fmt)
static void format_painode(enum util_fmt_t fmt)
{
struct pai_node *node;
@@ -214,9 +292,12 @@ static void show_format(enum util_fmt_t fmt)
for (int i = 0; i < node->ctridx; ++i) {
util_fmt_obj_start(FMT_ROW, "counter");
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
if (shortname)
util_fmt_pair(FMT_QUOTE, "name", "%c%d",
pai_type_char(node->type),
node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
util_fmt_pair(FMT_DEFAULT, "number", "%d",
node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
util_fmt_obj_end();
}
util_fmt_obj_end(); /* Counters */
@@ -226,14 +307,14 @@ static void show_format(enum util_fmt_t fmt)
util_fmt_exit();
}
static void show_painode(void)
static void list_painode(void)
{
struct pai_node *node;
int indent = 0;
int offset = 0;
if (output_format != -1) {
show_format(output_format);
format_painode(output_format);
return;
}
@@ -264,8 +345,10 @@ static void free_painode(void)
util_list_iterate_safe(&pai_list, node, next) {
free(node->name_uc);
for (int i = 0; i < node->ctridx; ++i)
for (int i = 0; i < node->ctridx; ++i) {
free(node->ctrlist[i].name);
free(node->ctrlist[i].data);
}
free(node->ctrlist);
free(node);
}
@@ -279,7 +362,8 @@ static void make_painode(enum pai_types t)
node->type = t;
node->sysfs_name = pai_type_sysfs(t);
node->name = pai_type_name(t);
node->name_uc = str2uc(node->name);
node->name_uc = util_strdup(node->name);
util_str_toupper(node->name_uc);
node->filter_name = pai_type_filter(t);
node->base = pai_type_base(t);
@@ -308,6 +392,267 @@ static void sort_painode(void)
util_list_sort(&pai_list, painode_cmp, NULL);
}
/* Read counter value. */
static unsigned long event_read(int fd)
{
unsigned long count;
int rc;
rc = read(fd, &count, sizeof(count));
if (rc != sizeof(count))
err(EXIT_FAILURE, "Failed to read counter value");
return count;
}
/* Write header. */
static void line_header(void)
{
struct pai_node *node;
static bool header;
bool comma = false;
if (header)
return; /* Printed already */
printf("Date,Time,CPU,"); /* Print counter name and number */
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
if (comma)
putchar(',');
if (shortname) {
printf("%c%ld", pai_type_char(node->type),
node->ctrlist[i].nr - node->base);
} else {
printf("%s(%ld)", node->ctrlist[i].name ?: node->name_uc,
node->ctrlist[i].nr - node->base);
}
comma = true;
}
}
putchar('\n');
header = true;
}
/* Print CPU specific counter values. */
static void line_cpu(char *header)
{
struct pai_cpudata *data;
struct pai_node *node;
bool comma;
char txt[16];
for (unsigned int h = 0; h < max_cpus; ++h) {
comma = false;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
data = &node->ctrlist[i].data[h];
if (!comma) {
snprintf(txt, sizeof(txt), "CPU%d,", data->cpu);
printf("%s,%s", header, txt);
} else {
putchar(',');
}
printf(ctrformat, data->value);
comma = true;
}
}
putchar('\n');
}
}
/* Write an output line. */
static void line_out(char *header)
{
struct pai_node *node;
bool comma;
line_header();
if (allcpu)
line_cpu(header);
/* Print total count of all CPUs */
printf("%s,%s,", header, delta && !firstread ? "Delta" : "Total");
comma = false;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
if (comma)
putchar(',');
printf(ctrformat, node->ctrlist[i].total);
comma = true;
}
}
putchar('\n');
}
/* Write a formatted line. */
static void format_line_out(time_t now, char *now_text)
{
static unsigned int called;
struct pai_node *node;
char cpuid[16];
if (!called) {
util_fmt_init(stdout, output_format, FMT_DEFAULT | FMT_HANDLEINT, 1);
util_fmt_obj_start(FMT_DEFAULT, NULL);
util_fmt_obj_start(FMT_LIST, "measurements");
}
util_list_iterate(&pai_list, node) {
if (!node->ctridx) /* Counter set not selected */
continue;
util_fmt_obj_start(FMT_DEFAULT, "entry");
util_fmt_pair(FMT_PERSIST, "iteration", "%d", called++);
util_fmt_pair(FMT_PERSIST, "time_epoch", "%d", now);
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "time", "%s", now_text);
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "valuetype",
(delta && !firstread) ? "delta" : "total");
util_fmt_obj_start(FMT_LIST, "counters");
for (int i = 0; i < node->ctridx; ++i) {
util_fmt_obj_start(FMT_ROW, "counter");
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
if (shortname)
util_fmt_pair(FMT_QUOTE, "shortname", "%c%d",
pai_type_char(node->type),
node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "value", ctrformat, node->ctrlist[i].total);
if (allcpu) {
for (unsigned int j = 0; j < max_cpus; ++j) {
snprintf(cpuid, sizeof(cpuid), "cpu%d", j);
util_fmt_pair(FMT_DEFAULT, cpuid, ctrformat,
node->ctrlist[i].data[j].value);
}
}
util_fmt_obj_end();
}
util_fmt_obj_end(); /* Counters */
util_fmt_obj_end(); /* Entry */
}
}
/* Terminate formatted output. */
static void format_line_end(void)
{
util_fmt_obj_end(); /* Iteration */
util_fmt_obj_end(); /* Default */
util_fmt_exit();
}
/* Display counter values. */
static void show_values(void)
{
time_t now = time(NULL);
struct tm *now_tm;
char now_text[32];
now_tm = localtime(&now);
if (output_format != -1) {
strftime(now_text, sizeof(now_text), "%F %T%z", now_tm);
format_line_out(now, now_text);
} else {
strftime(now_text, sizeof(now_text), "%F,%T", now_tm);
line_out(now_text);
}
}
/* Read each counter value. */
static void read_painode(void)
{
struct pai_cpudata *data;
struct pai_node *node;
unsigned long value;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
node->ctrlist[i].total = 0;
for (size_t j = 0; j < max_cpus; ++j) {
data = &node->ctrlist[i].data[j];
value = event_read(data->fd);
if (delta) {
data->value = value - data->prev_value;
data->prev_value = value;
} else {
data->value = value;
}
node->ctrlist[i].total += data->value;
}
}
}
}
static void wait_painode(void)
{
for (unsigned long i = 0; i < loops; ++i) {
read_painode();
show_values();
if (i + 1 < loops)
sleep(read_interval);
firstread = false;
}
format_line_end();
}
/* Install one event. */
static int event_add(int cpu, int idx, struct pai_node *node)
{
struct perf_event_attr attr;
int fd;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.config = node->ctrlist[idx].nr;
attr.type = node->pmu;
fd = perf_event_open(&attr, -1, cpu, -1, 0);
if (fd == -1)
err(EXIT_FAILURE, "Failed to open perf event: file descriptor not available");
return fd;
}
/* Increase number of file descriptors this process can open. */
static void event_fdlimit(void)
{
unsigned int needed = 3 + max_fds * max_cpus;
struct rlimit rlimit;
if (getrlimit(RLIMIT_NOFILE, &rlimit) == -1)
err(EXIT_FAILURE, "Failed to read RLIMIT_NOFILE");
if (needed > rlimit.rlim_cur)
rlimit.rlim_cur = needed;
if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
err(EXIT_FAILURE, "Failed to set RLIMIT_NOFILE");
}
/* Install all events and iterate over requested read operations. */
static void event_painode(void)
{
size_t pai_cpudata_sz = sizeof(struct pai_cpudata) * max_cpus;
struct pai_cpudata *data;
struct pai_node *node;
event_fdlimit();
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
node->ctrlist[i].data = util_malloc(pai_cpudata_sz);
data = node->ctrlist[i].data;
for (unsigned int j = 0; j < CPU_SETSIZE; ++j) {
if (CPU_ISSET(j, &cpu_online_mask)) {
data->cpu = j;
data->fd = event_add(j, i, node);
data->value = 0;
data->prev_value = 0;
++data;
}
}
}
}
wait_painode();
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
for (unsigned int j = 0; j < max_cpus; ++j)
close(node->ctrlist[i].data[j].fd);
}
}
}
/* Check for hardware support and return false if not available. */
static bool have_support(enum pai_types t)
{
@@ -344,9 +689,40 @@ static void check_type_name(const char *type)
errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
}
/*
* Get list of specified CPUs from command line. Check if these CPUs
* exist and are online. Ignore those CPUs which are not available and
* issue one warning when CPUs have been specified but are not online.
*/
static void get_cpulist(char *parm)
{
bool warned = false;
cpu_set_t cpulist;
int i, rc;
CPU_ZERO(&cpulist);
rc = libcpumf_cpuset(parm, &cpulist);
if (rc)
err(EXIT_FAILURE, "Cannot parse cpulist %s", parm);
for (i = 0; i < CPU_SETSIZE; ++i) {
if (CPU_ISSET(i, &cpulist) && !CPU_ISSET(i, &cpu_online_mask)) {
if (!warned) {
warnx("some CPU(s) are offline, ignored");
warned = true;
}
}
if (!CPU_ISSET(i, &cpulist) && CPU_ISSET(i, &cpu_online_mask))
CPU_CLR(i, &cpu_online_mask);
}
}
int main(int argc, char **argv)
{
bool list_only = true;
enum util_fmt_t fmt;
bool i_flag = false;
bool l_flag = false;
char *endchar;
int ch;
util_list_init(&pai_list, struct pai_node, node);
@@ -358,6 +734,15 @@ int main(int argc, char **argv)
default:
util_opt_print_parse_error(ch, argv);
return EXIT_FAILURE;
case 'a':
allcpu = true;
list_only = false;
break;
case 'd':
delta = true;
firstread = true;
list_only = false;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
@@ -365,21 +750,80 @@ int main(int argc, char **argv)
case 'v':
util_prg_print_version();
return EXIT_SUCCESS;
case 'i':
i_flag = true;
list_only = false;
errno = 0;
read_interval = strtoul(optarg, &endchar, 0);
if (errno || *endchar)
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
break;
case 'l':
l_flag = true;
list_only = false;
errno = 0;
loops = strtoul(optarg, &endchar, 0);
if (errno || *endchar)
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
break;
case 'n':
numsort = true;
break;
case 's':
list_only = false;
shortname = true;
break;
case 't':
check_type_name(optarg);
break;
case 'x':
list_only = false;
ctrformat = "%lx";
break;
case 'X':
list_only = false;
ctrformat = "%#lx";
break;
case OPT_FORMAT:
if (!util_fmt_name_to_type(optarg, &fmt))
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
output_format = fmt;
break;
case 'c':
ctrlist = util_strdup(optarg);
util_str_rm_whitespace(optarg, ctrlist);
util_str_toupper(ctrlist);
break;
}
}
/* Nothing specified, show all PAI counters */
if (i_flag && !l_flag) {
util_prg_print_help();
util_opt_print_help();
return EXIT_FAILURE;
}
/*
* Read currently online CPUs and create a bit mask.
* This bitmap of online CPUs is used to check command line parameter
* for valid CPUs
* When any of the flags which set variable list_only to false have
* be specified, lets also show the counter value, not just list them.
*/
if (optind < argc) /* List of CPUs on command line */
list_only = false;
if (!list_only) { /* Show counter values */
ch = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpu_online_mask);
if (ch)
err(EXIT_FAILURE, "Cannot read file /sys/" S390_CPUS_ONLINE);
while (optind < argc)
get_cpulist(argv[optind++]);
max_cpus = CPU_COUNT(&cpu_online_mask);
if (!loops)
loops = 1;
}
/* Nothing specified, use all PAI counters */
if (!pai_types_show)
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
@@ -393,7 +837,12 @@ int main(int argc, char **argv)
}
}
sort_painode();
show_painode();
ch = EXIT_SUCCESS;
if (!list_only)
event_painode();
else
list_painode();
free_painode();
free(ctrlist);
return ch;
}

View File

@@ -17,9 +17,9 @@ chcpumf \- manage the CPU-measurement facilities support
.SH SYNOPSIS
.B chcpumf
.RB [ \-m | \-\-min
.IR num_sdb ]
.IR NUM_SDB ]
.RB [ \-x | \-\-max
.IR num_sdb ]
.IR NUM_SDB ]
.RB [ \-V | \-\-verbose ]
.br
.B chcpumf
@@ -36,14 +36,14 @@ facilities for Linux on System z.
.
.SH OPTIONS
.TP
.BR \-m ", " \-\-min " \fInum_sdb\fP"
.BR \-m ", " \-\-min " \fINUM_SDB\fP"
Specifies the minimum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes.
This is the initial buffer size when you start the sampling facility.
.
.TP
.BR \-x ", " \-\-max " \fInum_sdb\fP"
.BR \-x ", " \-\-max " \fINUM_SDB\fP"
Specifies the maximum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes.

View File

@@ -16,14 +16,16 @@ lshwc \- extract CPU Measurement Facilities counter sets
\*c
.RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ]
.RB [ \-l
.IR count ]
.IR COUNT ]
.RB [ \-i
.IR interval ]
.IR INTERVAL ]
.RB [ \-t
.IR time ]
.IR TIME ]
.RB [ \-f
.IR format ]
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
.IR FORMAT ]
.RB [ \-c
.IR LIST ]
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
.br
\*c
.BR \-h | \-\-help
@@ -62,13 +64,13 @@ 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,
that the command waits between read operations.
The default is 60 seconds.
.
.TP
.BR \-l ", " \-\-loop \fI\ count\fP
.BR \-l ", " \-\-loop \fI\ COUNT\fP
Performs the specified number of read operations.
.
.TP
@@ -134,7 +136,7 @@ Displays counter values as hexadecimal values with a leading 0x prefix.
Apply quoting to every output element, regardless of content or format.
.
.TP
.BR \-f ", " \-\-format \fI\ format\fP
.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.
@@ -173,7 +175,12 @@ and \fBvalue\fP.
.RE
.
.TP
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
.BR \-c ", " \-\-counters \fI\ LIST\fP
Specify a comma-separated list of counter names to be printed.
If a counter name does not match any valid counter name, it is omitted. (Implies -H)
.
.TP
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
A comma-separated list of CPUs.
Each CPU can optionally be followed by characters that specify the counter set.
See below for details.
@@ -206,7 +213,7 @@ Two read operations are performed and a summary line is printed for each
read operation.
.sp 1
.nf
.ft CW
.ft CR
# lshwc -l2 0-1:BP
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
@@ -220,7 +227,7 @@ Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES
This example shows the counter values of the problem state counter set per CPU.
CPU 0 and CPU 1 are selected.
.nf
.ft CW
.ft CR
.sp 1
# lshwc -l3 -a 0-1:P
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
@@ -239,7 +246,7 @@ Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
This example shows the counter values of the basic counter set
using delta output format.
.nf
.ft CW
.ft CR
.sp 1
# lshwc -d -l 10 -i 5 -s :b
Date,Time,CPU,B0,B1,B2,B3,B4,B5
@@ -259,7 +266,7 @@ Date,Time,CPU,B0,B1,B2,B3,B4,B5
This example shows the counter values of the problem state counter set with
CPU 3 selected.
.nf
.ft CW
.ft CR
.sp 1
# lshwc -l2 -a 3:P -H --format json
{

View File

@@ -10,7 +10,7 @@
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
.
.SH NAME
lspai \- list Processor Activity Instrumentation (PAI) counters
\*c \- list Processor Activity Instrumentation (PAI) counters and values
.
.SH SYNOPSIS
\*c
@@ -18,7 +18,22 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
.IR FORMAT ]
.RB [ \-n ]
.RB [ \-t
.IR TYPE ]
.br
\*c
.RB [ \-\-format
.IR FORMAT ]
.RB [ \-n ]
.RB [ \-t
.IR "\ TYPE" ]
.RB [ \-a ][ \-d ][ \-i
.IR SECONDS ]
.RB [ \-l
.IR COUNT ]
.RB [ \-s ][ \-x ][ \-x ]
.RB [ \-c
.IR LIST ]
.RI [ CPULIST ]
.br
\*c
.BR \-h | \-\-help
@@ -28,10 +43,34 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
.
.
.SH DESCRIPTION
On the first line
\*c displays the Processor Activity Instrumentation (PAI) counters
for Linux on IBM Z.
The output is a human-readable list of available PAI counter
names and numbers.
The output lists the available PAI counter names
and numbers in various formats.
If
.I CPULIST
is specified or any of the options
.BR \-a ,
.BR \-d ,
.BR \-s ,
.BR \-x ,
.BR \-X ,
.B \-l
or
.BR \-i,
are present on the command line,
\*c command extracts counter values from the
specified PAI counter sets.
Counter sets can be specified and extracted for individual CPUs.
By default the output is a comma-separated list of values.
Each line starts with a timestamp
followed by the extracted counter values.
A header line shows each counter name and the
counter number in parenthesis.
For other formats, use the
.B \-\-format
option.
.SH OPTIONS
.TP
.BR \-h ", " \-\-help
@@ -42,7 +81,44 @@ Displays help information, then exits.
Displays version information, then exits.
.
.TP
.BR \-t ", " \-\-type "\ TYPE"
.BR \-a ", " \-\-allcpu
Displays counter values from each CPU.
The default is a total summary line of all counters from all CPUs.
.
.TP
.BR \-d ", " \-\-delta
Displays counter values in form of deltas.
Each counter value shows the increment to the previous output line.
Without this flag the total value of each counter is displayed.
.
.TP
.BR \-c ", " \-\-counters \fI\ LIST\fP
Specify a comma separated list of counter names to
be printed.
If a counter name does not match any valid counter
name, it is omitted.
.
.TP
.BR \-s ", " \-\-short
Displays counter names as short names in the heading.
A short name consists of
a letter for the counter set followed by a number.
For example,
this flag displays counter number 0 in the PAI crypto counter
set as
.B C0
instead of
.BR CRYPTO_ALL .
Valid counter set letters are:
.RS
.IP C
PAI crypto counter set
.IP N
PAI NNPA counter set
.RE
.
.TP
.BR \-t ", " \-\-type "\ \fITYPE\fP"
Specifies the PAI counter set to list.
Valid counter set values are
.I crypto
@@ -63,6 +139,26 @@ Retrieve output in one of the following formats:
JSON, csv, json-seq or pairs.
If no format is specified,
the output defaults to a human-readable format.
.
.TP
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
Specifies a time interval, in seconds,
that the command waits between read operations.
The default is 60 seconds.
.
.TP
.BR \-l ", " \-\-loop \fI\ COUNT\fP
Performs the specified number of read operations.
The default is one.
.
.TP
.BR \-x ", " \-\-hex
Displays counter values as hexadecimal values.
.
.TP
.BR \-X ", " \-\-hex0x
Displays counter values as hexadecimal values with a leading 0x prefix.
.
.SH "EXAMPLE"
The \*c invocation lists all PAI Neural Network Processing Assist Facility
(NNPA) counters in numeric order:
@@ -83,6 +179,58 @@ suitable for
.IR perf "(8)"
event specification by name.
The third gives a short explanation, if available.
.sp 1
The \*c invocation displays all PAI Neural Network Processing Assist Facility
(NNPA) counters values.
Ten iterations with a two second delay in between
is printed on the screen.
.nf
# lspai -i 2 -l 10 -t nnpa -n
Date,Time,CPU,NNPA_ALL(0),NNPA_ADD(1),NNPA_SUB(2),NNPA_MUL(3),NNPA_DIV(4),
NNPA_MIN(5),NNPA_MAX(6),NNPA_LOG(7),NNPA_EXP(8),NNPA_IBM_RESERVED_9(9),
NNPA_RELU(10),NNPA_TANH(11),NNPA_SIGMOID(12),NNPA_SOFTMAX(13),NNPA_BATCHNORM(14),
NNPA_MAXPOOL2D(15),NNPA_AVGPOOL2D(16),NNPA_LSTMACT(17),NNPA_GRUACT(18),
NNPA_CONVOLUTION(19),NNPA_MATMUL_OP(20),NNPA_MATMUL_OP_BCAST23(21),
NNPA_SMALLBATCH(22),NNPA_LARGEDIM(23),NNPA_SMALLTENSOR(24),NNPA_1MFRAME(25),
NNPA_2GFRAME(26),NNPA_ACCESSEXCEPT(27)
2025-05-13,08:18:27,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:29,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:31,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:33,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:35,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:37,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:39,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:41,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:43,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:45,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
#
.fi
.sp 1
This \*c example displays the value of PAI crypto counter number 7.
It is field number 116 in the output.
This counter is incremented after \*c is started and
the table shows the delta values for PAI crypto counter 7.
.nf
# lspai -d -s -i 5 -l3 -t crypto | \
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
Date,Time,CPU,C0,C7
2025-06-24,13:02:24,Total,0,0
2025-06-24,13:02:29,Delta,77122286,77122284
2025-06-24,13:02:34,Delta,92574403,92574403
#
.fi
.sp 1
The following table shows the total value for PAI crypto counter 7
after \*c is started.
.nf
# lspai -s -i 5 -l3 -t crypto | \
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
Date,Time,CPU,C0,C7
2025-06-24,13:03:01,Total,0,0
2025-06-24,13:03:06,Total,72550754,72566121
2025-06-24,13:03:11,Total,164633982,164650377
#
.fi
.SH "SEE ALSO"
.BR pai (8)
.BR lscpumf (8)

View File

@@ -15,24 +15,24 @@ pai \- record and report Processor Activity Instrumentation (PAI) counters
.SH SYNOPSIS
\*c
.RB [ \-V ][ \-m | \-\-mapsize
.IR size ]
.IR SIZE ]
.RB [ \-i | \-\-interval
.IR ms ]
.IR MS ]
.RB [ \-R | \-\-realtime
.IR prio ]
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
.IR PRIO ]
.BR \-c | \-\-crypto [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
.br
\*c
.RB [ \-V ][ \-m | \-\-mapsize
.IR size ]
.IR SIZE ]
.RB [ \-i | \-\-interval
.IR ms ]
.IR MS ]
.RB [ \-R | \-\-realtime
.IR prio ]
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
.IR PRIO ]
.BR \-n | \-\-nnpa [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
.br
\*c
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ]
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIFILES\fP ]
.br
\*c
.BR \-h | \-\-help
@@ -73,7 +73,7 @@ Reporting evaluates files that are created by recording.
.SH OPTIONS
.TP
.BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]"
.BR \-c ", " \-\-crypto "\fR[\fICPULIST\fR][:\fIDATA\fR]"
Records data for all (default) or a specified list of CPUs.
The CPU list is a comma-separated list of CPU numbers and ranges.
In a range, a hyphen separates the first CPU number
@@ -106,7 +106,7 @@ execution.
.RE
.
.TP
.BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]"
.BR \-n ", " \-\-nnpa "\fR[\fICPULIST\fR][:\fIDATA\fR]"
Records data for all (default) or a specified list of CPUs.
The CPU list is a comma-separated list of CPU numbers and ranges.
In a range, a hyphen separates the first CPU number
@@ -241,7 +241,7 @@ The program runs for 10 seconds
(10 intervals of 1000 milliseconds).
.sp 1
.nf
.ft CW
.ft CR
# \*c -c0 10
.ft R
.fi
@@ -256,7 +256,7 @@ and a comma-separated list of counter number, colon (:),
and the counter value n hexadecimal notation.
.sp 1
.nf
.ft CW
.ft CR
# \*c -r
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1

View File

@@ -59,14 +59,6 @@ static struct util_list list_pai_event;
static struct util_list list_pmu_event;
static bool summary;
/* System call to perf_event_open(2) */
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
}
static void ev_dealloc(void)
{
struct pai_event *next, *p;
@@ -743,8 +735,8 @@ static int add_events(int type)
}
if (rc) {
free(pmuname);
warnx("failed building event list for %s", pmuname);
free(pmuname);
}
return rc;
}

View File

@@ -23,11 +23,11 @@
#include <unistd.h>
#include "lib/dasd_base.h"
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "lib/util_path.h"
#include "lib/zt_common.h"
#define RD_BUFFER_SIZE 80
@@ -283,7 +283,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
char filename[] = "dasdinfo0000";
mode_t mode;
unsigned int path;
int retry;
unsigned int retry;
int rc;
int fd;
@@ -294,7 +294,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
if (pathname[path] == NULL)
continue;
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
sprintf(filename, "dasdinfo%04d", retry);
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
result = dinfo_make_path(pathname[path], filename);
if (result == NULL)
return -1;
@@ -354,18 +354,26 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
{
char *readbuf = NULL;
char *path;
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer");
return -1;
}
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
"/sys/block/%s/dev", blockdev) < 0)
path = util_path_sysfs("block/%s/dev", blockdev);
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path) < 0) {
free(path);
free(readbuf);
return -1;
if (dinfo_extract_dev(dev, readbuf) != 0)
}
free(path);
if (dinfo_extract_dev(dev, readbuf) != 0) {
free(readbuf);
return -1;
}
free(readbuf);
return 0;
}
@@ -373,8 +381,9 @@ static int
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
int tflag, struct FTW *ftwbuf)
{
enum { LINK_DIR_SIZE = 128 };
char linkdir[LINK_DIR_SIZE];
char *tempdir;
char linkdir[128];
ssize_t i;
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
@@ -387,12 +396,12 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
*/
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
return -1;
i = readlink(tempdir, linkdir, 128);
i = readlink(tempdir, linkdir, LINK_DIR_SIZE);
free(tempdir);
if ((i < 0) || (i >= 128))
if (i < 0 || i >= LINK_DIR_SIZE)
return -1;
/* append '\0' because readlink returns non zero terminated string */
tempdir[i + 1] = '\0';
linkdir[i] = '\0';
if (strstr(linkdir, "dasd") == NULL)
return FTW_CONTINUE;
free(busiddir);
@@ -439,7 +448,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
char *tempdir = NULL;
char *result = NULL;
char *sysfsdir = "/sys/devices/";
char *sysfsdir = util_path_sysfs("devices/");
/* dinfo_is_devnode needs to know the busid */
searchbusid = busid;
@@ -475,6 +484,7 @@ out:
out2:
free(busiddir);
free(result);
free(sysfsdir);
return rc;
}
@@ -486,6 +496,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
int rc = 0;
char *path;
if (stat(devnode, &stat_buffer) != 0) {
warnx("Error: could not stat %s", devnode);
@@ -495,32 +506,34 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
minor(stat_buffer.st_rdev));
directory = opendir("/sys/block/");
path = util_path_sysfs("block/");
directory = opendir(path);
if (directory == NULL) {
warnx("Error: could not open directory /sys/block");
warnx("Error: could not open directory %s", path);
free(path);
return -1;
}
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer");
free(path);
return -1;
}
while ((dir_entry = readdir(directory)) != NULL) {
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
"/sys/block/%s/dev",
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "%s%s/dev", path,
dir_entry->d_name) < 0)
continue;
if (strncmp(stat_dev, readbuf,
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
rc = snprintf(*uidfile, RD_BUFFER_SIZE,
"/sys/block/%s/device/uid",
rc = snprintf(*uidfile, RD_BUFFER_SIZE, "%s%s/device/uid", path,
dir_entry->d_name);
if (rc >= RD_BUFFER_SIZE) {
fprintf(stderr,
"Error: Device name was truncated\n");
free(path);
return -1;
}
@@ -529,6 +542,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
}
closedir(directory);
free(path);
return 0;
}
@@ -626,16 +640,18 @@ int main(int argc, char *argv[])
}
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
if (!(readbuf && uidfile))
if (!readbuf)
exit(1);
/* try to read the uid attribute */
if (busid) {
sprintf(uidfile, "/sys/bus/ccw/devices/%s/uid", busid);
uidfile = util_path_sysfs("bus/ccw/devices/%s/uid", busid);
} else if (blockdev) {
sprintf(uidfile, "/sys/block/%s/device/uid", blockdev);
uidfile = util_path_sysfs("block/%s/device/uid", blockdev);
} else if (devnode) {
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
if (!uidfile)
exit(1);
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
goto error;
}

View File

@@ -22,7 +22,7 @@
. ds pfont \\n[.f]
. nh
. na
. ft CW
. ft CR
\\$*
. ft \\*[pfont]
. ad

View File

@@ -18,3 +18,17 @@ CPI_SYSTEM_NAME=""
# CPI sysplex name
#
CPI_SYSPLEX_NAME=""
#
# CPI permit on protected virtualization guests
#
# Important: Set CPI_PERMIT_ON_PVGUEST=1 only if you trust the host system.
# Enabling these options allows the host to receive potentially sensitive
# Control-Program Identification (CPI) data from the protected virtualization
# guest, including:
# - system_type
# - system_level
# - sysplex_name
# - system_name
#
CPI_PERMIT_ON_PVGUEST=

View File

@@ -9,6 +9,8 @@
#include <sched.h>
#include <stdbool.h>
#include <asm/unistd.h>
#include <linux/perf_event.h>
#define S390_CPUMF_CF "devices/cpum_cf/"
#define S390_CPUMF_CFDIAG "devices/cpum_cf_diag/"
@@ -115,7 +117,7 @@ bool libcpumf_have_cpumcf(void);
*
* @param[out] min Minimum supported sampling interval
* @param[out] max Maximum supported sampling interval
* @param[out] speed Current CPU speed, number of CPU cylces per
* @param[out] speed Current CPU speed, number of CPU cycles per
* microsecond
* @param[out] basic_sz Basic sample size in bytes
* @param[out] diag_sz Diagnostic sample size in bytes
@@ -182,4 +184,37 @@ bool libcpumf_have_pai_ext(void);
* @retval false PAI_NNPA counter Facility is not available
*/
bool libcpumf_have_pai_nnpa(void);
/**
* Wrapper for the perf_event_open syscall used to configure performance events.
* This function simplifies usage of perf_event_open and provides a consistent
* interface for libcpumf internals.
*
* @param hw_event Pointer to perf_event_attr structure describing the event
* @param pid Target process ID (0 for current process)
* @param cpu Target CPU (-1 for all CPUs)
* @param group_fd File descriptor of event group leader, or -1 if none
* @param flags Additional flags (usually 0)
*
* @return File descriptor for the opened event on success
* @return -1 on failure, errno is set appropriately
*/
long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd,
unsigned long flags);
/**
* Check if a counter name is present in a comma-separated list of counters.
*
* This function checks whether the given counter name appears in the
* provided list. If the list is NULL, the function assumes all counters
* are allowed and returns true.
*
* @param name Name of the counter to search for
* @param ctrlist Comma-separated list of counter names, or NULL
*
* @return true if the counter is in the list or list is NULL
* @return false otherwise
*/
bool ctr_in_list(char *name, char *ctrlist);
#endif

17
include/lib/util_str.h Normal file
View File

@@ -0,0 +1,17 @@
/**
* @defgroup util_str_h util_str: String functions
* @{
* @brief Manipulate and work with strings
*
* Copyright IBM Corp. 2025
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef LIB_UTIL_STR_H
#define LIB_UTIL_STR_H
void util_str_rm_whitespace(const char *src, char *dest);
#endif /** LIB_UTIL_STR_H @} */

View File

@@ -15,6 +15,7 @@ install-man: $(MANS)
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
done
ln -f -s chiucvallow.8 $(DESTDIR)$(MANDIR)/man8/lsiucvallow.8
clean:

View File

@@ -66,7 +66,7 @@ terminal application
To connect to the first z/VM IUCV HVC terminal device on the z/VM guest virtual
machine "LNX1234", run:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvconn LNX1234 lnxhvc0
@@ -215,7 +215,7 @@ For each HVC terminal device, a \fBgetty\fP program must be started.
To configure and start \fBgetty\fP on a HVC terminal, open
.BR /etc/inittab (5),
and add a new entry similar to this one:
.ft CW
.ft CR
.in +0.25in
.nf
@@ -261,7 +261,7 @@ IUCV HVC terminal:
.IP "1." 4
To display the current terminal name, issue the command:
.ft CW
.ft CR
.in +0.25in
.nf
user@host:~$ echo $TERM
@@ -275,7 +275,7 @@ To connect to a z/VM IUCV HVC terminal, run
.BR iucvconn (1)
and log in as usual:
.ft CW
.ft CR
.in +0.25in
.nf
user@host:~$ iucvconn MYLNX01 lnxhvc0
@@ -289,7 +289,7 @@ user@MYLNX01:~$
To assign the terminal name from step 1. to the terminal environment variable,
issue the following command:
.ft CW
.ft CR
.in +0.25in
.nf
user@MYLNX01:~$ export TERM=xterm
@@ -304,7 +304,7 @@ environment variable.
.PP
For getty programs, a sample terminal environment configuration might look like:
.ft CW
.ft CR
.in +0.25in
.nf
h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux

View File

@@ -123,7 +123,7 @@ the characters to the connected terminal.
To access the "lnxterm" terminal on the Linux instance in
z/VM guest virtual machine LNXSYS01:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvconn LNXSYS01 lnxterm
@@ -134,7 +134,7 @@ iucvconn LNXSYS01 lnxterm
To access the first z/VM IUCV HVC terminal on the Linux instance in
z/VM guest virtual machine LNXSYS02:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvconn LNXSYS02 lnxhvc0
@@ -145,7 +145,7 @@ iucvconn LNXSYS02 lnxhvc0
To create a transcript of the terminal session to the Linux instance in
z/VM guest virtual machine LNXSYS99:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0

View File

@@ -94,7 +94,7 @@ additional options.
.SH EXAMPLES
To allow remote logins using the terminal identifier "lnxterm":
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvtty lnxterm
@@ -104,7 +104,7 @@ iucvtty lnxterm
To only allow users from LNXSYS01 to connect to terminal "lnxterm":
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvtty -a LNXSYS01 lnxterm
@@ -115,7 +115,7 @@ iucvtty -a LNXSYS01 lnxterm
To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal
"lnxterm":
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvtty -a "LNXSYS1[0-9]" lnxterm
@@ -125,7 +125,7 @@ iucvtty -a "LNXSYS1[0-9]" lnxterm
To use \fB/sbin/sulogin\fP instead of \fB/bin/login\fP for terminal "suterm":
.PP
.ft CW
.ft CR
.in +0.25in
.nf
iucvtty suterm -- /sbin/sulogin
@@ -136,7 +136,7 @@ iucvtty suterm -- /sbin/sulogin
An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "lnxterm"
with \fB/bin/login\fP could be:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
t1:2345:respawn:/usr/bin/iucvtty lnxterm
@@ -147,7 +147,7 @@ t1:2345:respawn:/usr/bin/iucvtty lnxterm
An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "suterm"
with \fB/sbin/sulogin\fP in single user mode could be:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin

View File

@@ -79,7 +79,7 @@ users".
The output for "list" authorization is a list of z/VM guest virtual machines,
for example:
.ft CW
.ft CR
.in +0.25i
.nf
@@ -94,7 +94,7 @@ guest5
.ft
The output for "regex" authorization is a list of one or more
regular expressions, for example:
.ft CW
.ft CR
.in +0.25i
.nf
@@ -109,7 +109,7 @@ Regular expressions for your authorization:
If \fBts-shell\fP is configured to connect to particular z/VM guest virtual
machines only, the output for "regex" authorization is followed by a list of
the user IDs that match at least one of the regular expressions:
.ft CW
.ft CR
.in +0.25i
.nf
@@ -139,7 +139,7 @@ To change the default terminal identifier, use the \fBterminal\fP command.
In the following example, a user opens a terminal connection to the Linux
instance in z/VM guest virtual machine LNXSYS01:
.ft CW
.ft CR
.in +0.25i
.nf
@@ -164,7 +164,7 @@ If \fBterminal\fP is called with the \fIidentifier\fP being specified,
If \fIidentifier\fP is not specified, the current default terminal identifier
is displayed:
.ft CW
.ft CR
.in +0.25i
.nf
user@ts-shell> terminal
@@ -212,7 +212,7 @@ guest virtual machines.
A typical \fBIUCV\fP authorization statement in the z/VM directory entry of the
terminal server z/VM guest virtual machine might be:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
IUCV ANY
@@ -301,7 +301,7 @@ instance.
For example, to create a list of all z/VM guest virtual machines with names that
start with "LINUX" and are followed by digits, use:
.ft CW
.ft CR
.in +0.25in
.nf
@@ -373,7 +373,7 @@ group name and prefixed with "@".
Here is an example of a Linux user and group authorization:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
alice = list:guest01,guest02
@@ -395,7 +395,7 @@ name on a separate line.
.PP
The following example shows the usage of the \fIfile:\fP prefix:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
@testgrp = file:/etc/iucvterm/auth/test-systems.list
@@ -427,7 +427,7 @@ To authorize user bob for all z/VM guest virtual machines with names that
start with "lnx" and are followed with at least three but not more than five
alphanumeric characters, use:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
bob = regex:lnx\\w{3,5}
@@ -441,7 +441,7 @@ test or production environment: authorize all users in the "testgrp" group for
all systems in the test environment; and respectively, authorize all users in
the "prodgrp" group for all systems in the production environment:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
@testgrp = regex:test\\w+
@@ -461,7 +461,7 @@ of the other type are ignored.
Example:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
@users = list:guest01,guest03,guest05
@@ -484,7 +484,7 @@ To use the \fBts-shell\fP as the login shell for Linux users, follow these steps
Add the path of the \fBts-shell\fP program to the \fI/etc/shells\fP file that
contains the list of valid login shells:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
echo $(which ts-shell) >> /etc/shells
@@ -498,7 +498,7 @@ Change the login shell of a particular Linux user using the
.BR chsh (1)
program:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
chsh -s $(which ts-shell) alice

View File

@@ -116,7 +116,7 @@ specifies an exit status in this range.
.SS inittab
To start \fB/sbin/agetty\fP on terminal device "hvc1", specify:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux
@@ -128,7 +128,7 @@ h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux
To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following
settings to the job file:
.PP
.ft CW
.ft CR
.in +0.25in
.nf
respawn

View File

@@ -10,7 +10,7 @@
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "lib/ccw.h"

View File

@@ -14,6 +14,7 @@
#include "lib/libcpumf.h"
#include "lib/util_path.h"
#include "lib/util_libc.h"
#define SERVICELEVEL "/proc/service_levels"
@@ -169,3 +170,31 @@ bool libcpumf_have_pai_nnpa(void)
{
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_NNPA);
}
long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd,
unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
}
bool ctr_in_list(char *name, char *ctrlist)
{
char *token;
char *list;
if (!ctrlist) /* No --counters means all counters */
return true;
list = util_strdup(ctrlist);
token = strtok(list, ",");
while (token) {
if (strcmp(token, name) == 0) {
free(list);
return true;
}
token = strtok(NULL, ",");
}
free(list);
return false;
}

View File

@@ -5099,14 +5099,15 @@ int ekmf_generate_csr(const struct ekmf_config *config,
}
}
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
if (rsa_pss_params != NULL)
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
rc = SK_OPENSSL_generate_csr(key_blob, key_blob_size,
subject_rdns, num_subject_rdns,
subject_utf8, cert,
extensions, num_extensions,
digest_nid, &pss_params, &req,
digest_nid, rsa_pss_params != NULL ?
&pss_params : NULL, &req,
&ext_lib_info.ext_lib, verbose);
if (rc != 0) {
pr_verbose(verbose, "SK_OPENSSL_generate_csr failed "
@@ -5245,14 +5246,16 @@ int ekmf_generate_ss_cert(const struct ekmf_config *config,
}
}
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
if (rsa_pss_params != NULL)
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
rc = SK_OPENSSL_generate_ss_cert(key_blob, key_blob_size,
subject_rdns, num_subject_rdns,
subject_utf8, rcert,
extensions, num_extensions,
validity_days, digest_nid,
&pss_params, &cert,
rsa_pss_params != NULL ?
&pss_params : NULL, &cert,
&ext_lib_info.ext_lib, verbose);
if (rc != 0) {
pr_verbose(verbose, "SK_OPENSSL_generate_ss_cert failed "

33
libutil/util_str.c Normal file
View File

@@ -0,0 +1,33 @@
/*
* util - Utility function library
*
* Manipulate and work with strings
*
* Copyright IBM Corp. 2025
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <ctype.h>
#include "lib/util_str.h"
/**
* Copy characters from \a src to \a dest, omitting all blank characters
* (spaces and tabs). The resulting string in \a dest will be NUL terminated.
*
* @param[in] src Source string
* @param[out] dest Destination buffer
*/
void util_str_rm_whitespace(const char *src, char *dest)
{
while (*src != '\0') {
if (!isblank(*src)) {
*dest = *src;
dest++;
}
src++;
}
*dest = '\0';
}

View File

@@ -89,7 +89,7 @@ Socket communication with applications utilizing CMS AF_IUCV support
An AF_IUCV socket is represented by the following format:
.PP
.RS 8
.ft CW
.ft CR
.nf
#define AF_IUCV 32
@@ -290,7 +290,7 @@ It must not match any z/VM user ID in your environment.
To set an identifier, issue a command like this:
.PP
.RS 8
.ft CW
.ft CR
echo \fIidentifier\fP > /sys/devices/qeth/\fI<bus-ID>\fP/hsuid
.ft
.RE
@@ -303,7 +303,7 @@ For example, to use "MYHOST01" to bind AF_IUCV sockets to the
HiperSockets device with bus-ID 0.0.8000, run:
.PP
.RS 8
.ft CW
.ft CR
.nf
echo "MYHOST01" > /sys/devices/qeth/0.0.8000/hsuid
.fi
@@ -366,7 +366,7 @@ z/VM guest virtual machine with a maximum of 10\^000 outstanding messages for ea
incoming connection. Your z/VM guest virtual machine is permitted to connect to
all other z/VM guest virtual machines. The total number of connections for your
z/VM guest virtual machine cannot exceed 100.
.ft CW
.ft CR
.in +0.25i
.nf

View File

@@ -22,7 +22,7 @@
. ds pfont \fP
. nh
. na
. ft CW
. ft CR
\\$*
. ft \\*[pfont]
. ad

53
rust/Cargo.lock generated
View File

@@ -47,7 +47,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -205,9 +205,9 @@ dependencies = [
[[package]]
name = "curl"
version = "0.4.47"
version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265"
checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc"
dependencies = [
"curl-sys",
"libc",
@@ -215,14 +215,14 @@ dependencies = [
"openssl-sys",
"schannel",
"socket2",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
name = "curl-sys"
version = "0.4.78+curl-8.11.0"
version = "0.4.83+curl-8.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf"
checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483"
dependencies = [
"cc",
"libc",
@@ -230,7 +230,7 @@ dependencies = [
"openssl-sys",
"pkg-config",
"vcpkg",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@@ -318,7 +318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -501,9 +501,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "openssl"
version = "0.10.70"
version = "0.10.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [
"bitflags",
"cfg-if",
@@ -533,9 +533,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.105"
version = "0.9.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [
"cc",
"libc",
@@ -799,7 +799,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -865,7 +865,7 @@ version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -929,12 +929,12 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@@ -971,7 +971,7 @@ dependencies = [
"getrandom 0.2.16",
"once_cell",
"rustix",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -981,7 +981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [
"rustix",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -1035,9 +1035,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unsafe-libyaml"
version = "0.2.8"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "utf8parse"
@@ -1086,15 +1086,6 @@ dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"

View File

@@ -0,0 +1,28 @@
#![allow(missing_docs)]
use pv::request::EcPubKeyCoord;
use pv_core::misc::encode_hex;
use s390_pv as pv;
use std::env::args;
use pv::misc::{read_certs, read_file};
use pv::{Error, Result};
fn main() -> Result<()> {
let hkd = args().nth(1).expect("Expect one Host-key document");
let hkd_bin = read_file(&hkd, "Host-key document")?;
let certs = read_certs(hkd_bin).map_err(|source| Error::HkdNotPemOrDer { hkd, source })?;
let hkd_cert = certs
.first()
.expect("Expect at least one certificate in the HKD file");
let pc: EcPubKeyCoord = hkd_cert
.public_key()
.expect("Expected a public key in the Host-key document")
.try_into()
.unwrap();
println!("{}", encode_hex(pc.sha256().unwrap()));
Ok(())
}

View File

@@ -7,7 +7,7 @@ DEBUG_FILES := $(addsuffix .debug,$(FILES))
ifeq ($(HOST_ARCH),s390x)
ZIPL_DIR := $(rootdir)/zipl
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot
PKGDATADIR := $(TOOLS_DATADIR)/pvimg
PVIMG_PKGDATADIR ?= $(TOOLS_DATADIR)/pvimg
INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
@@ -86,9 +86,9 @@ stage3b.elf: head.o $(ZIPL_OBJS)
@chmod a-x $@
install: stage3a.bin stage3b_reloc.bin
$(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PKGDATADIR)
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
else
# Don't generate the dependency files (see `common.mak` for the

View File

@@ -1,9 +1,9 @@
.\" Copyright 2024 IBM Corp.
.\" Copyright 2024, 2025 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 "PVIMG-CREATE" "1" "2025-04-24" "s390-tools" "Pvimg Manual"
.TH "PVIMG-CREATE" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
@@ -11,23 +11,23 @@ pvimg-create \- Create an IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
pvimg create [OPTIONS] --kernel <FILE> --output <FILE> --host-key-document <FILE> <--no-verify|--cert <FILE>>
pvimg create [OPTIONS] \-\-kernel <FILE> \-\-output <FILE> \-\-host\-key\-document <FILE> <\-\-no\-verify|\-\-cert <FILE>>
.fam C
.fi
.SH DESCRIPTION
.PP
Use \fBpvimg\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
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
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.
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
@@ -124,7 +124,8 @@ Overwrite an existing Secure Execution boot image.
\-\-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.
contain exactly 32 bytes of data. In previous versions, this option was called
\fB\-\-comm\-key\fR.
.RE
.RE
.PP
@@ -136,6 +137,13 @@ Secure Execution header protection key is a randomly generated key.
.RE
.RE
.PP
\-\-image\-key <FILE>
.RS 4
Use the content of FILE as the image encryption key. The file must contain
exactly 64 bytes of data.
.RE
.RE
.PP
\-\-enable\-dump
.RS 4
Enable Secure Execution guest dump support. This option requires the
@@ -165,8 +173,8 @@ Add\-secret requests don't have to provide the CCK\-derived extension secret
.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.
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
@@ -256,8 +264,8 @@ 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,
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.
@@ -271,11 +279,11 @@ Generate an IBM Secure Execution image with Secure Execution guest dump support:
.B pvimg create \-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.
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.
Remember to re\-run \fBzipl\fR after updating an IBM Secure Execution image.
.SH "SEE ALSO"
.sp
\fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)

View File

@@ -1,9 +1,9 @@
.\" Copyright 2024 IBM Corp.
.\" Copyright 2024, 2025 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 "PVIMG-INFO" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.TH "PVIMG-INFO" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
@@ -11,7 +11,7 @@ pvimg-info \- Print information about the IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
pvimg info [OPTIONS] --format <FORMAT> <INPUT>
pvimg info [OPTIONS] \-\-format <FORMAT> <INPUT>
.fam C
.fi
.SH DESCRIPTION

View File

@@ -1,9 +1,9 @@
.\" Copyright 2024 IBM Corp.
.\" Copyright 2024, 2025 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 "PVIMG-TEST" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.TH "PVIMG-TEST" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
@@ -11,7 +11,7 @@ pvimg-test \- Test different aspects of an existing IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
pvimg test <--host-key-document <FILE>|--key-hashes[=<FILE>]> <INPUT>
pvimg test <\-\-host\-key\-document <FILE>|\-\-key\-hashes[=<FILE>]> <INPUT>
.fam C
.fi
.SH DESCRIPTION

View File

@@ -1,9 +1,9 @@
.\" Copyright 2024 IBM Corp.
.\" Copyright 2024, 2025 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 "PVIMG" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.TH "PVIMG" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
@@ -21,21 +21,21 @@ Execution images.
.SH "PVIMG COMMANDS"
.PP
\fBpvimg-create(1)\fR
\fBpvimg\-create(1)\fR
.RS 4
Create an IBM Secure Execution image
.RE
.PP
\fBpvimg-info(1)\fR
\fBpvimg\-info(1)\fR
.RS 4
Print information about the IBM Secure Execution image
.RE
.PP
\fBpvimg-test(1)\fR
\fBpvimg\-test(1)\fR
.RS 4
Test different aspects of an existing IBM Secure Execution image
.RE
@@ -83,4 +83,4 @@ line flag, or wrong number of arguments.
.RE
.SH "SEE ALSO"
.sp
\fBpvimg-create\fR(1) \fBpvimg-info\fR(1) \fBpvimg-test\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
\fBpvimg\-create\fR(1) \fBpvimg\-info\fR(1) \fBpvimg\-test\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)

View File

@@ -93,6 +93,42 @@ pub struct ComponentPaths {
pub parmfile: Option<PathBuf>,
}
/// CLI Argument collection for handling user-provided keys.
#[derive(Args, Debug)]
#[cfg_attr(test, derive(Default))]
pub struct UserKeys {
/// Use the content of FILE as the customer-communication key (CCK).
///
/// The file must contain exactly 32 bytes of data. In previous versions,
/// this option was called '--comm-key'.
#[arg(
long,
value_name = "FILE",
group = "cck-available",
visible_alias = "comm-key"
)]
pub cck: Option<PathBuf>,
/// 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.
#[arg(long, value_name = "FILE", alias = "x-header-key")]
pub hdr_key: Option<PathBuf>,
/// Use the content of FILE as the image encryption key.
///
/// The file must contain exactly 64 bytes of data.
#[arg(
long,
value_name = "FILE",
conflicts_with = "disable_image_encryption",
alias = "x-comp-key"
)]
pub image_key: Option<PathBuf>,
}
#[derive(Args, Debug)]
#[cfg_attr(test, derive(Default))]
#[command(
@@ -342,25 +378,8 @@ pub struct CreateBootImageArgs {
#[arg(long)]
pub overwrite: bool,
/// Use the content of FILE as the customer-communication key (CCK).
///
/// The file must contain exactly 32 bytes of data. This option used to be
/// called '--comm-key' in previous versions.
#[arg(
long,
value_name = "FILE",
group = "cck-available",
visible_alias = "comm-key"
)]
pub cck: Option<PathBuf>,
/// 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.
#[arg(long, value_name = "FILE", alias = "x-header-key")]
pub hdr_key: Option<PathBuf>,
#[clap(flatten)]
pub keys: UserKeys,
#[clap(flatten)]
pub legacy_flags: CreateBootImageLegacyFlags,
@@ -379,11 +398,6 @@ pub struct CreateBootImageExperimentalArgs {
#[arg(long, value_name = "DIR", hide(true))]
pub x_bootloader_directory: Option<PathBuf>,
/// Manually set the image components encryption key (experimental option).
// Hidden in user documentation.
#[arg(long, value_name = "FILE", hide(true))]
pub x_comp_key: Option<PathBuf>,
/// Manually set the PSW address used for the Secure Execution header (experimental option).
// Hidden in user documentation.
#[arg(long, value_name = "ADDRESS", hide(true))]
@@ -527,6 +541,10 @@ mod test {
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"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("image-key", ["--image-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
];
let invalid_create_args = [
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
@@ -558,6 +576,16 @@ mod test {
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("extension", ["--enable-cck-extension-secret"]),
CliOption::new("update", ["--enable-cck-update"])])),
// Image component key cannot be provided multiple times
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"]),
CliOption::new("x-comp-key2", ["--x-comp-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"]),
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
// Disable image encryption and providing an image-key is mutually
// exclusive.
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"]),
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
];
let mut genprotimg_valid_args = vec![

View File

@@ -2,13 +2,13 @@
//
// Copyright IBM Corp. 2024
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use anyhow::Result;
use log::info;
use pv::{misc::read_file, request::Confidential};
use crate::cli::CreateBootImageExperimentalArgs;
use crate::cli::UserKeys;
#[macro_export]
/// Makes it easier to
@@ -23,13 +23,9 @@ pub struct UserProvidedKeys {
}
/// Reads all user provided keys.
pub fn read_user_provided_keys(
cck_path: Option<&Path>,
hdr_key_path: Option<&Path>,
experimental_args: &CreateBootImageExperimentalArgs,
) -> Result<UserProvidedKeys> {
pub fn read_user_provided_keys(keys: &UserKeys) -> Result<UserProvidedKeys> {
let components_key = {
match &experimental_args.x_comp_key {
match &keys.image_key {
Some(key_path) => {
info!(
"Use file '{}' as the image components protection key",
@@ -44,7 +40,7 @@ pub fn read_user_provided_keys(
}
};
let aead_key = {
match hdr_key_path {
match &keys.hdr_key {
Some(key_path) => {
info!(
"Use file '{}' as the Secure Execution header protection",
@@ -63,7 +59,7 @@ pub fn read_user_provided_keys(
};
let cck = {
match cck_path {
match &keys.cck {
Some(key_path) => {
info!(
"Use file '{}' as the customer communication key (CCK)",

View File

@@ -126,11 +126,7 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
let verified_host_keys = opt
.certificate_args
.get_verified_hkds("Secure Execution image")?;
let user_provided_keys = read_user_provided_keys(
opt.cck.as_deref(),
opt.hdr_key.as_deref(),
&opt.experimental_args,
)?;
let user_provided_keys = read_user_provided_keys(&opt.keys)?;
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {

View File

@@ -3,44 +3,15 @@
// Copyright IBM Corp. 2024
use std::{
ffi::{CString, OsStr},
fs::{File, OpenOptions},
ffi::OsStr,
fs::{rename, File, OpenOptions},
io::{self, Seek, SeekFrom, Write},
os::unix::{ffi::OsStrExt, fs::OpenOptionsExt},
os::unix::fs::OpenOptionsExt,
path::Path,
};
use pv::{Error, FileAccessErrorType, PvCoreError, Result};
/// Rust wrapper for `libc::renameat2`
fn renameat2<P: AsRef<Path>, Q: AsRef<Path>>(oldpath: P, newpath: Q, flags: u32) -> io::Result<()> {
let oldpath_cstr = CString::new(oldpath.as_ref().as_os_str().as_bytes())?;
let oldpath_raw = oldpath_cstr.into_raw();
let newpath_cstr = CString::new(newpath.as_ref().as_os_str().as_bytes())?;
let newpath_raw = newpath_cstr.into_raw();
unsafe {
// SAFETY: oldpath_raw and newpath_raw are valid CStrings because they were
// generated by the `CString::new` function.
let ret = libc::renameat2(
libc::AT_FDCWD,
oldpath_raw,
libc::AT_FDCWD,
newpath_raw,
flags,
);
// SAFETY: libc::renameat2 does not modify `newpath_raw` and is
// therefore still valid.
let _ = CString::from_raw(newpath_raw);
// SAFETY: libc::renameat2 does not modify `oldpath_raw` and is
// therefore still valid.
let _ = CString::from_raw(oldpath_raw);
if ret == -1 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
/// This type helps to perform atomic operations by writing to a temporary file
/// and renaming it to the actual filename when the [`AtomicFile::finish`]
/// function is called. If the [`AtomicFile::finish`] function is never called,
@@ -66,7 +37,7 @@ impl<F> AsMut<F> for AtomicFile<F> {
}
/// Enum used for more verbosity.
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum AtomicFileOperation {
/// Replace existing file
Replace,
@@ -218,17 +189,16 @@ impl TempPath {
}
fn persist(self, operation: AtomicFileOperation) -> Result<()> {
let options = match operation {
AtomicFileOperation::Replace => 0,
AtomicFileOperation::NoReplace => libc::RENAME_NOREPLACE,
};
renameat2(&self.temp_path, &self.path, options).map_err(|e| {
PvCoreError::FileAccessRename {
src: self.temp_path.as_ref().to_str().unwrap().to_string(),
dst: self.path.as_ref().to_str().unwrap().to_string(),
source: e,
if let Ok(true) = self.path.try_exists() {
if operation == AtomicFileOperation::NoReplace {
return Err(Error::Io(io::Error::from(io::ErrorKind::AlreadyExists)));
}
}
rename(&self.temp_path, &self.path).map_err(|e| PvCoreError::FileAccessRename {
src: self.temp_path.as_ref().to_str().unwrap().to_string(),
dst: self.path.as_ref().to_str().unwrap().to_string(),
source: e,
})?;
self.forget();
Ok(())

View File

@@ -32,6 +32,9 @@ declare TYPE
declare NAME
declare SYSPLEX
declare PV_GUEST
declare -i CPI_PERMIT="$CPI_PERMIT_ON_PVGUEST"
declare -i DRYRUN=0
# Exit codes
@@ -40,6 +43,7 @@ readonly EXIT_FAILURE=1
readonly EXIT_ARG_TOO_LONG=3
readonly EXIT_INVALID_CHARS=4
readonly EXIT_INVALID_ARGS=5
readonly EXIT_NOTCONFIGURED=6
# Distro-IDs as supported by SE/HMC firmware
readonly DISTRO_GENERIC=0
@@ -69,6 +73,10 @@ Configure the Control-Program-Information (CPI) settings.
-S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
-T, --type TYPE Set and commit OS type to TYPE
-v, --version Print version information, then exit
--permit-cpi Permit to send Control-Program Identification data of
protected virtualization guest to the host (must be
specified before any commit option). See also the
important note.
--commit Ignore all other options and commit any uncommitted
values
--dry-run Do not actually set or commit anything, but show what
@@ -77,7 +85,17 @@ Configure the Control-Program-Information (CPI) settings.
uncommitted) values
Environment variables used for the --defaults option:
CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME
CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME,
CPI_PERMIT_ON_PVGUEST (See also the important note.)
Important: Set CPI_PERMIT_ON_PVGUEST=1 or use --permit_cpi option only if you
trust the host system. Enabling these options allows the host to receive
potentially sensitive Control-Program Identification (CPI) data from the
protected virtualization guest, including:
- system_type
- system_level
- sysplex_name
- system_name
Available bits for the --set-bit option:
kvm: Indicate that system is a KVM host
@@ -124,6 +142,19 @@ fail_with()
cpi_commit()
{
# Commit Control-Program Identification changes on protected
# virtualization guests only if it is permitted by the guest. This
# prevents leakage of potentially sensitive information to untrusted
# hosts.
if [[ -f "/sys/firmware/uv/prot_virt_guest" ]]; then
read -r PV_GUEST < "/sys/firmware/uv/prot_virt_guest"
if [[ "$PV_GUEST" -eq 1 ]]; then
if [[ -z "$CPI_PERMIT" ]] || [[ "$CPI_PERMIT" -ne 1 ]]; then
echo "Sending CPI data from secure execution Linux guests is disabled. Use --permit-cpi to enable CPI data." >&2
exit "$EXIT_NOTCONFIGURED"
fi
fi
fi
echo 1 > "$CPI_SET" 2> /dev/null
}
@@ -404,7 +435,7 @@ if [ $# -le 0 ]; then
print_parse_error_and_exit
fi
opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,show,version -n $PRG -- "$@")
opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,permit-cpi,show,version -n "$PRG" -- "$@")
if [ $? -ne 0 ]; then
print_parse_error_and_exit
fi
@@ -473,6 +504,10 @@ while [ -n $1 ]; do
cpi_show
exit $EXIT_SUCCESS
;;
--permit-cpi)
CPI_PERMIT=1
shift
;;
--commit)
cpi_commit
exit $EXIT_SUCCESS

View File

@@ -1372,17 +1372,18 @@ call_run_command() {
# extract the raw_command and set cmd_type, as some shell might split into
# several lines restrict the cmd echo to the first line only
local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
local cmd_type=$(type ${raw_cmd} | cut -d' ' -sf4,5)
local cmd_type=$(type -t ${raw_cmd})
echo "#######################################################" >> "${logfile}"
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
echo "${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
# check calling command type
if [ "X${cmd_type}" = "Xshell builtin" ]; then
# command is a builtin (no use of timeout possible)
if [ "X${cmd_type}" = "Xbuiltin" ]; then
# command is a builtin (use of timeout not possible)
eval "${cmd}" >> ${logfile} 2>&1
rc=$?
elif [ "X${cmd_type}" != "Xnot found" ]; then
elif [ "X${cmd_type}" != "X" ]; then
# command was found and has a type
if [ "x${TIMEOUT_OK}" = "xYES" ]; then
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
@@ -1393,18 +1394,23 @@ call_run_command() {
rc=$?
fi
else
echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}"
echo "Command \"${raw_cmd}\" not available" >> "${logfile}"
echo >> "${logfile}"
return 1
fi
# log a warning on rc not 0 and define return
if [ ${rc} ]; then
# add return code or new line and log a warning if rc is not 0 and define return
if [ ${rc} -eq 0 ]; then
echo >> "${logfile}"
return 0
elif [ "x${TIMEOUT_OK}" = "xYES" ] && [ ${rc} -eq 124 ]; then
echo "Warning: \"${cmd}\" cancelled by timeout"
echo "-> command cancelled by timeout" >> "${logfile}"
return 1
else
echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}"
echo >> "${logfile}"
echo "rc=${rc}" >> "${logfile}"
echo "Warning: Command \"${cmd}\" ended with ${rc}"
return 1
fi
}

View File

@@ -37,10 +37,15 @@ EnvironmentFile=@sysconf_path@/sysconfig/cpi
# Environment=CPI_SYSPLEX_NAME=
# Environment=CPI_SYSTEM_LEVEL=
# Environment=CPI_SYSTEM_TYPE=LINUX
# Environment=CPI_PERMIT_ON_PVGUEST=
#
# Sending data to the HMC/SE
ExecStart=@toolslib_path@/cpictl -e
# On SEL guests, do not send data by default and treat the respective
# return code as success.
SuccessExitStatus=NOTCONFIGURED
[Install]
WantedBy=multi-user.target

View File

@@ -456,7 +456,7 @@ Identifies the z/VM spool file to be ordered.
.IP "1." 3
Start z/VM console spooling by issuing:
.ft CW
.ft CR
.in +0.25in
.nf
# vmcp sp cons start
@@ -471,7 +471,7 @@ Close the console file and transfer it to the reader queue, find the spool ID
behind the \f(CWFILE\fP keyword in the corresponding CP message.
In the example below, the spool ID is 398:
.ft CW
.ft CR
.in +0.25in
.nf
# vmcp sp cons clo \(rs* rdr
@@ -484,7 +484,7 @@ RDR FILE 0398 SENT FROM LINUX025 CON WAS 0398 RECS 1872
Read and save the spool file on the Linux file system in the
current working directory:
.ft CW
.ft CR
.in +0.25in
.nf
# vmur re -t 398 linux_cons
@@ -496,7 +496,7 @@ current working directory:
.IP "1." 3
Send parmfile to the z/VM punch queue and transfer it to the reader queue:
.ft CW
.ft CR
.in +0.25in
.nf
# vmur pun -r /boot/parmfile
@@ -509,7 +509,7 @@ Reader file with spoolid 0465 created.
Send the Linux kernel image to the z/VM punch queue and
transfer it to reader queue:
.ft CW
.ft CR
.in +0.25in
.nf
# vmur pun -r /boot/vmlinuz -N image
@@ -522,7 +522,7 @@ Reader file with spoolid 0466 created.
Move the Linux kernel image to the first and parmfile to
the second position in the reader queue:
.ft CW
.ft CR
.in +0.25in
.nf
# vmur or 465
@@ -534,7 +534,7 @@ the second position in the reader queue:
.IP "4."
Prepare re-IPL from the z/VM reader and reboot:
.ft CW
.ft CR
.in +0.25in
.nf
# chreipl ccw 0.0.000c

View File

@@ -101,9 +101,8 @@ Example: "0192" becomes "0.0.0192".
.SH EXAMPLES
\fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR
.RS
After completing all outstanding I/O requests for the devices with bus
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
the device offline.
Tries to set the devices with the bus ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197
and 0.0.0198 online.
.RE
.P
.B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192
@@ -121,9 +120,9 @@ measurement block facility.
.P
.B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198
.RS
Will try to set the devices with bus ID 0.0.0192, 0.0.0195,
0.0.0196, 0.0.0197 and 0.0.0198 offline and finish all outstanding I/O
requests before.
After completing all outstanding I/O requests for the devices with bus
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
the devices offline.
.RE
.SH AUTHOR

View File

@@ -285,12 +285,23 @@ Use this option to select all supported columns and keys for output.
Scale BPS values by UNIT
Use this option to specify a value by which bytes-per-seconds (BPS) values -
such as read and write throughput - are scaled in table output format. Accepted
values are:
such as read and write throughput - are scaled in human-readable output.
Note that scaling is not applied to values in machine-readable output format as
produced with option
.BR \-\-format .
Accepted values are:
.IP \(bu 3
.B auto:
Scale automatically to fit value into each column. This is the default.
Scale automatically to fit value into each column. To indicate the current scaling factor,
an SI-suffix is added to each scaled number (e.g. K for 1000). This is the default.
.PP
.IP \(bu 3
.B auto-iec:
Same as
.B auto
but using power-of-two based IEC-suffixes (e.g. Ki for 1024).
.PP
.IP \(bu 3
.IR number :
@@ -299,18 +310,34 @@ Scale by
.PP
.IP \(bu 3
.B K:
Scale by 1024 (KiB)
Scale by 1000 (KB)
.PP
.IP \(bu 3
.B M:
Scale by 1,048,576 (MiB)
Scale by 1,000,000 (MB)
.PP
.IP \(bu 3
.B G:
Scale by 1,073,741,824 (GiB)
Scale by 1,000,000,000 (GB)
.PP
.IP \(bu 3
.B T:
Scale by 1,000,000,000,000 (TB)
.PP
.IP \(bu 3
.B Ki:
Scale by 1024 (KiB)
.PP
.IP \(bu 3
.B Mi:
Scale by 1,048,576 (MiB)
.PP
.IP \(bu 3
.B Gi:
Scale by 1,073,741,824 (GiB)
.PP
.IP \(bu 3
.B Ti:
Scale by 1,099,511,627,776 (TiB)
.PP
.RE

View File

@@ -265,6 +265,7 @@ static struct {
unsigned long unit;
char unit_suffix;
bool unit_specified;
bool unit_iec;
} opts;
/* Per CHPID run-time data. */
@@ -336,6 +337,7 @@ static void init_opts(void)
opts.unit = UNIT_AUTO;
opts.unit_suffix = 0;
opts.unit_specified = false;
opts.unit_iec = false;
}
/*
@@ -459,6 +461,37 @@ static void parse_cmgs(char *arg)
}
}
static bool suffix_to_unit(char *arg, unsigned long *unit_ptr, char *suffix_ptr,
bool *iec_ptr)
{
const char *suffixes = "KMGT";
unsigned long unit, base;
size_t len = strlen(arg);
char suffix;
bool iec;
int i;
if (len == 1)
iec = false;
else if (len == 2 && toupper(arg[1]) == 'I')
iec = true;
else
return false;
suffix = (char)toupper(*arg);
base = iec ? UNIT_BIN : UNIT_DEC;
unit = base;
for (i = 0; suffixes[i]; i++) {
if (suffix == suffixes[i]) {
*unit_ptr = unit;
*suffix_ptr = suffix;
*iec_ptr = iec;
return true;
}
unit *= base;
}
return false;
}
/*
* Parse a scale unit value in @arg and return the resulting scaling factor.
*/
@@ -467,22 +500,12 @@ static unsigned long parse_unit(char *arg)
unsigned long unit;
char *endptr;
if (strlen(arg) == 1) {
opts.unit_suffix = (char)toupper(*arg);
switch (opts.unit_suffix) {
case 'K':
return UNIT_BIN;
case 'M':
return UNIT_BIN * UNIT_BIN;
case 'G':
return UNIT_BIN * UNIT_BIN * UNIT_BIN;
case 'T':
return UNIT_BIN * UNIT_BIN * UNIT_BIN * UNIT_BIN;
default:
break;
}
if (suffix_to_unit(arg, &unit, &opts.unit_suffix, &opts.unit_iec))
return unit;
if (strcmp(arg, "auto-iec") == 0) {
opts.unit_iec = true;
return UNIT_AUTO;
}
opts.unit_suffix = 0;
if (strcmp(arg, "auto") == 0)
return UNIT_AUTO;
/* Parse as number. */
@@ -1278,7 +1301,7 @@ static struct util_rec *define_table(void)
rec = util_rec_new_wide(NULL);
column_for_each_selected(col) {
util_rec_def(rec, col->name, UTIL_REC_ALIGN_RIGHT,
(int)col->width, "");
(int)col->width + (int)col->pad, "");
}
return rec;
@@ -1343,7 +1366,7 @@ static void scale_fixed(struct cmg_pair_t *p, unsigned long unit)
static void add_pair_value(struct util_rec *table, struct column_t *col,
struct cmg_pair_t *pair)
{
char suffix = 0, str[16];
char suffix = 0, suffix2 = opts.unit_iec ? 'i' : 0, str[16];
int p;
if (!pair->valid) {
@@ -1355,23 +1378,25 @@ static void add_pair_value(struct util_rec *table, struct column_t *col,
suffix = scale_auto(pair, UNIT_DEC);
} else if (pair->unit == CMG_BPS) {
if (opts.unit == UNIT_AUTO)
suffix = scale_auto(pair, UNIT_BIN);
suffix = scale_auto(pair, opts.unit_iec ? UNIT_BIN : UNIT_DEC);
else
scale_fixed(pair, opts.unit);
}
switch (pair->type) {
case CMG_U32:
snprintf(str, sizeof(str), "%u%c", pair->value_u32, suffix);
snprintf(str, sizeof(str), "%u%c%c", pair->value_u32, suffix,
suffix2);
break;
case CMG_U64:
snprintf(str, sizeof(str), "%llu%c", pair->value_u64, suffix);
snprintf(str, sizeof(str), "%llu%c%c", pair->value_u64, suffix,
suffix2);
break;
case CMG_FLOAT:
/* Find highest precision that fits into @width characters. */
for (p = 2; p >= 0; p--) {
snprintf(str, sizeof(str), "%.*f%c", p,
pair->value_double, suffix);
snprintf(str, sizeof(str), "%.*f%c%c", p,
pair->value_double, suffix, suffix2);
if (strlen(str) <= col->width)
break;
}
@@ -1539,11 +1564,15 @@ static void print_table_header(void)
{
struct column_t *col;
unsigned int i, next_i, num, width, hdr_width;
const char *last_hdr1 = NULL;
/* Print first header line and update column width based on heading. */
for (i = 0; (col = column_get_by_index(i, true)); i = next_i) {
if (i > 0)
printf(" ");
if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0) {
col->pad = 1;
printf("%*s", col->pad + 1, "");
}
last_hdr1 = col->hdr1_group;
get_hdr_group_size(i, &num, &width);
if (num == 1) {
/* Update column width in case heading is wider. */
@@ -1571,7 +1600,7 @@ static void print_table_header(void)
i = 0;
column_for_each_selected(col) {
if (i++ > 0)
printf(" ");
printf("%*s", col->pad + 1, "");
printf("%*s", col->width, col->hdr2);
}
printf("\n");
@@ -1667,8 +1696,8 @@ static void calc_column_widths(void)
width = 4; /* "999K" */
} else {
v = MAX_BPS / (double)opts.unit;
snprintf(str, sizeof(str) - 1, "%.0f%c", v,
opts.unit_suffix);
snprintf(str, sizeof(str) - 1, "%.0f%c%c", v,
opts.unit_suffix, opts.unit_iec ? 'i' : 0);
width = strlen_u(str);
}
break;
@@ -1678,7 +1707,7 @@ static void calc_column_widths(void)
col->width = MAX(col->width, width);
/* Double space between groups. */
if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0)
col->width++;
col->pad++;
last_hdr1 = col->hdr1_group;
}
}
@@ -1693,7 +1722,8 @@ static void cmd_table(void)
apply_column_selection();
calc_column_widths();
column_update_bps_suffix(opts.unit == UNIT_AUTO, opts.unit_suffix);
column_update_bps_suffix(opts.unit == UNIT_AUTO, opts.unit_iec,
opts.unit_suffix);
printf("Collecting initial utilization data\n");
update_util_all(true);

View File

@@ -281,13 +281,17 @@ static void calc_ext_metrics(struct cmg_data_t *data, u32 ticks)
if (cmcb->dpu_num_cores == 0)
return;
/* dpu_util = dpu_exec_time_cpc / (t * dpu_num_cores) */
/* dpu_util = 100 * dpu_exec_time_cpc / (t * dpu_num_cores) */
delta = field_delta(dpu_exec_time_cpc, ext_a, ext_b);
m->dpu_util = delta / ((double)ticks * cmcb->dpu_num_cores);
m->dpu_util = 100.0 * delta / ((double)ticks * cmcb->dpu_num_cores);
/* dpu_util_total = dpu_channel_exec_time_cpc / (t * dpu_num_cores) */
/*
* dpu_util_total = 100 * dpu_channel_exec_time_cpc /
* (t * dpu_num_cores)
*/
delta = field_delta(dpu_channel_exec_time_cpc, ext_a, ext_b);
m->dpu_util_total = delta / ((double)ticks * cmcb->dpu_num_cores);
m->dpu_util_total = 100.0 * delta /
((double)ticks * cmcb->dpu_num_cores);
/* dpu_util_part = dpu_util_total * channel_work_units /
* channel_work_units_cpc */

View File

@@ -350,13 +350,17 @@ static void calc_ext_metrics(struct cmg_data_t *data, double seconds, u32 ticks)
if (cmcb->dpu_num_cores == 0)
return;
/* dpu_util = dpu_exec_time_cpc / (t * dpu_num_cores) */
/* dpu_util = 100 * dpu_exec_time_cpc / (t * dpu_num_cores) */
delta = field_delta(dpu_exec_time_cpc, ext_a, ext_b);
m->dpu_util = delta / ((double)ticks * cmcb->dpu_num_cores);
m->dpu_util = 100.0 * delta / ((double)ticks * cmcb->dpu_num_cores);
/* dpu_util_total = dpu_channel_exec_time_cpc / (t * dpu_num_cores) */
/*
* dpu_util_total = 100 * dpu_channel_exec_time_cpc /
* (t * dpu_num_cores)
*/
delta = field_delta(dpu_channel_exec_time_cpc, ext_a, ext_b);
m->dpu_util_total = delta / ((double)ticks * cmcb->dpu_num_cores);
m->dpu_util_total = 100.0 * delta /
((double)ticks * cmcb->dpu_num_cores);
/* dpu_util_part = dpu_util_total * channel_work_units /
* channel_work_units_cpc */

View File

@@ -53,6 +53,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_TYPE,
@@ -63,6 +64,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_CMG,
@@ -73,6 +75,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_SPEED,
@@ -83,6 +86,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_SHARED,
@@ -93,6 +97,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_INTERVAL,
@@ -103,6 +108,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
0,
},
{
COL_UTIL_PART,
@@ -112,6 +118,7 @@ static struct column_t columns[] = {
"PART",
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
5,
0,
},
{
@@ -122,6 +129,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
5,
0,
},
{
@@ -133,6 +141,7 @@ static struct column_t columns[] = {
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
0,
0,
},
{
COL_UTIL_DATA,
@@ -143,6 +152,7 @@ static struct column_t columns[] = {
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
0,
0,
},
{
COL_READ_PART,
@@ -152,6 +162,7 @@ static struct column_t columns[] = {
"PART",
HDR1_READ_SINGLE,
HDR1_READ_GROUP,
5,
0,
},
{
@@ -162,6 +173,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_READ_SINGLE,
HDR1_READ_GROUP,
5,
0,
},
{
@@ -172,6 +184,7 @@ static struct column_t columns[] = {
"PART",
HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP,
5,
0,
},
{
@@ -182,6 +195,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP,
5,
0,
},
{
@@ -193,6 +207,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
0,
},
{
COL_FICON_ACTIVE,
@@ -203,6 +218,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
0,
},
{
COL_FICON_DEFER,
@@ -213,6 +229,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
0,
},
{
COL_HPF_RATE,
@@ -223,6 +240,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
0,
},
{
COL_HPF_ACTIVE,
@@ -233,6 +251,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
0,
},
{
COL_HPF_DEFER,
@@ -243,6 +262,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
0,
},
{
COL_MSG_RATE_PART,
@@ -252,6 +272,7 @@ static struct column_t columns[] = {
"PART",
HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP,
5,
0,
},
{
@@ -262,6 +283,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP,
5,
0,
},
{
@@ -272,6 +294,7 @@ static struct column_t columns[] = {
"PART",
HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP,
5,
0,
},
{
@@ -282,6 +305,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP,
5,
0,
},
{
@@ -293,6 +317,7 @@ static struct column_t columns[] = {
"SNDFAIL",
"SNDFAIL",
0,
0,
},
{
COL_RCV_FAIL_PART,
@@ -302,6 +327,7 @@ static struct column_t columns[] = {
"PART",
HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP,
5,
0,
},
{
@@ -312,6 +338,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP,
5,
0,
},
{
@@ -323,6 +350,7 @@ static struct column_t columns[] = {
HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP,
0,
0,
},
{
COL_DPU_UTIL,
@@ -333,6 +361,7 @@ static struct column_t columns[] = {
HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP,
0,
0,
},
{
COL_DPU_UTIL_PART,
@@ -342,6 +371,7 @@ static struct column_t columns[] = {
"PART",
HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP,
5,
0,
},
{
@@ -352,6 +382,7 @@ static struct column_t columns[] = {
"TOTAL",
HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP,
5,
0,
},
};
@@ -447,15 +478,17 @@ void column_select_all(void)
column_select(col);
}
void column_update_bps_suffix(bool auto_scale, char suffix_char)
void column_update_bps_suffix(bool auto_scale, bool iec, char suffix_char)
{
struct column_t *col;
char *str;
if (auto_scale)
util_asprintf(&str, "(B/s)");
else if (suffix_char)
else if (suffix_char && iec)
util_asprintf(&str, "(%ciB/s)", suffix_char);
else if (suffix_char)
util_asprintf(&str, "(%cB/s)", suffix_char);
else
str = util_strdup("(*)");

View File

@@ -66,6 +66,7 @@ struct column_t {
const char *hdr1_single;
char *hdr1_group;
unsigned int width;
unsigned int pad;
};
#define column_for_each(c) \
@@ -83,7 +84,7 @@ void column_select_id_list(const int *ids);
void column_select_default(void);
void column_select_all(void);
void column_update_bps_suffix(bool auto_scale, char suffix_char);
void column_update_bps_suffix(bool auto_scale, bool iec, char suffix_char);
void column_exit(void);
#endif /* COLUMN_H */

View File

@@ -9,7 +9,7 @@
# Copy zdev persistent config from initrd to root-fs.
#
chzdev --export /run/zdev.initrd.config --all --type --persistent --quiet
chzdev --export /run/zdev.initrd.config --configured --type --persistent --quiet
# Apart from debugging purposes, this is useful for distro installers,
# which can import the early config into their own environment early

View File

@@ -22,7 +22,7 @@
. ds pfont \fP
. nh
. na
. ft CW
. ft CR
\\$*
. ft \\*[pfont]
. ad
@@ -636,6 +636,15 @@ option unless chzdev is run in an environment where udev is not fully
functional (such as in the early phase of an initial RAM disk).
.PP
.
.OD no-module-load "" ""
Do not load kernel modules during chzdev execution.
In some situations \fBchzdev\fR will attempt to load kernel modules to
complete a configuration action. This behavior may be undesirable in some
scenarios. The \fB\-\-no-module-load\fR option suppresses kernel module loading
during \fBchzdev\fR execution.
.PP
.
.OD persistent "p" ""
Apply changes to persistent configuration only.

View File

@@ -22,7 +22,7 @@
. ds pfont \fP
. nh
. na
. ft CW
. ft CR
\\$*
. ft \\*[pfont]
. ad

View File

@@ -109,6 +109,7 @@ struct options {
unsigned int quiet:1;
unsigned int no_settle:1;
unsigned int site_id;
unsigned int no_module_load:1;
};
/* Makefile converts chzdev_usage.txt into C file which we include here. */
@@ -156,6 +157,7 @@ enum {
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_SITE = 's',
OPT_NO_MODULE_LOAD = (OPT_ANONYMOUS_BASE+__COUNTER__),
};
static struct opts_conflict conflict_list[] = {
@@ -260,6 +262,7 @@ static const struct option opt_list[] = {
{ "quiet", no_argument, NULL, OPT_QUIET },
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
{ "site", required_argument, NULL, OPT_SITE },
{ "no-module-load", no_argument, NULL, OPT_NO_MODULE_LOAD },
{ NULL, no_argument, NULL, 0 },
};
@@ -1018,6 +1021,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->no_settle = 1;
break;
case OPT_NO_MODULE_LOAD:
/* --no-module-load */
opts->no_module_load = 1;
break;
case OPT_SITE:
/* --site */
/* 1. User can specify only site-ids from 0 to 9
@@ -3177,6 +3185,7 @@ int main(int argc, char *argv[])
dryrun = opts.dryrun;
udev_no_settle = opts.no_settle;
path_set_base(opts.base);
module_load_suppress(opts.no_module_load);
if (dryrun)
info("Starting dry-run, configuration will not be changed\n");

View File

@@ -57,6 +57,7 @@ OPTIONS
--dry-run Display changes without applying
--base PATH Use PATH as base for accessing files
--no-settle Do not wait for udev to settle
--no-module-load Do not load kernel modules during execution
--auto-conf Apply changes to auto-configuration only
-s, --site ID Apply changes to the specified site only
--is-owner FILE Examine whether the file is generated by zdev

View File

@@ -29,6 +29,7 @@
#include "export.h"
#include "firmware.h"
#include "misc.h"
#include "path.h"
#include "qeth.h"
#include "subtype.h"
#include "zfcp_host.h"
@@ -143,6 +144,13 @@ struct fw_qeth {
char settings[];
} __packed;
/* Dasd types definitions for rd.dasd parser */
enum dasd_type {
DASD_NO_DEVICE,
DASD_ECKD,
DASD_NO_ECKD,
};
/* Emit a warning that refers to a position in a firmware file. */
static void fwwarn(struct fw_file *f, const char *fmt, ...)
{
@@ -491,13 +499,49 @@ static struct device *add_device(struct fw_file *f, struct subtype *st,
return dev;
}
/* Return the device-type of the provided device-id by analysing modalias */
static enum dasd_type is_eckd(const char *id)
{
const char * const eckd_type[] = { "3390", "3380", "9345" };
size_t i;
char *device_path, *buffer;
int rc = DASD_NO_ECKD;
/* Remove the device-id from the blacklist */
ccw_unblacklist_id(id);
/* Do a cio_settle before trying to read the modalias */
cio_settle(1);
device_path = path_get_ccw_device(NULL, id);
/* Read the modalias value */
buffer = path_read_text_file(1, err_ignore, "%s/modalias",
device_path);
if (!buffer) {
rc = DASD_NO_DEVICE;
goto out;
}
for (i = 0; i < ARRAY_SIZE(eckd_type); i++) {
if (strstr(buffer, eckd_type[i])) {
rc = DASD_ECKD;
goto out;
}
}
out:
free(buffer);
free(device_path);
return rc;
}
/* Parse a DASD device entry. */
static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config,
struct util_list *objects)
{
struct fw_dasd *dasd = (struct fw_dasd *) de;
struct ccw_devid devid;
struct device *dev_eckd, *dev_fba;
struct device *dev_eckd = NULL, *dev_fba = NULL;
char *id;
if (!check_de_size(f, de, sizeof(struct fw_dasd)))
@@ -508,8 +552,22 @@ static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config,
/* Could be either dasd_eckd or dasd_fba - add both entries */
io_to_ccw(&devid, &dasd->id);
id = ccw_devid_to_str(&devid);
dev_eckd = add_device(f, &dasd_subtype_eckd, id, config, objects);
dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects);
switch (is_eckd(id)) {
case DASD_ECKD:
dev_eckd = add_device(f, &dasd_subtype_eckd, id, config,
objects);
break;
case DASD_NO_ECKD:
dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects);
break;
case DASD_NO_DEVICE:
fwwarn(f, "DASD device %s does not exist", id);
break;
default:
break;
}
free(id);
if (dasd->hdr.len > sizeof(struct fw_dasd)) {

View File

@@ -28,7 +28,7 @@ static int suppress_module_load;
/* Allow suppression of module loading. */
void module_load_suppress(int state)
{
suppress_module_load = 1;
suppress_module_load = state;
}
/* Check if a module is currently loaded. */

View File

@@ -25,7 +25,7 @@
# generates persistent configuration that can be transferred from initrd to
# another root files system such as in a distro installer environment.
zdev_dasd_base_args="--no-settle --yes --no-root-update --force"
zdev_dasd_base_args="--no-settle --yes --no-root-update --no-module-load --quiet"
zdev_parse_dasd_list() {
sed 's/,/\n/g' | while read -r _zdev_dasditem; do
@@ -41,7 +41,7 @@ zdev_parse_dasd_list() {
echo "rd.dasd ...,${_zdev_dasditem},... :"
# shellcheck disable=SC2086
chzdev dasd --type "${_zdev_dasditem}=1" --persistent \
$zdev_dasd_base_args
$zdev_dasd_base_args --force
;;
"") continue ;; # empty range
*) # currently no support for a device-spec "ipldev", only devbusid
@@ -72,15 +72,50 @@ EOF
$_zdev_dasd_features
EOF
fi
# Without dynamic (active) config zdev cannot infer
# the actual dasd type (eckd, fba) so configure for both.
# the actual dasd type (eckd, fba). Add logic to generate the
# udev rules based on the availability of modalias entry.
echo "rd.dasd ...,${_zdev_dasditem},... :"
# shellcheck disable=SC2086
chzdev dasd-eckd --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \
$zdev_dasd_base_args
# shellcheck disable=SC2086
chzdev dasd-fba --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \
$zdev_dasd_base_args
# free the dev-ids from blacklist to analyse the modalias
echo "free $_zdev_dasd_range" > /proc/cio_ignore
echo 1 > /proc/cio_settle
# Now, Configure both DASD types. While doing so, ensure that
# module loading is suppressed and that the --force option
# is not used with chzdev.
# As a result:
# - For non-existent devices, udev rules are generated for
# both DASD types.
# - For existing devices identified as generic-ccw, chzdev
# skips generating udev rules.
for dasd_type in dasd-eckd dasd-fba; do
chzdev "$dasd_type" --enable --persistent "$_zdev_dasd_range" \
$_zdev_dasd_attrs $zdev_dasd_base_args 2>/dev/null \
| sed -e 's/^/Non-existent /g'
done
# For the existing devices, configure the right dasd-type based
# on modalias
lszdev "$_zdev_dasd_range" --active --no-headings --columns \
ID,attr:modalias | \
while read -r dev_id modalias_val; do
case "$modalias_val" in
# ECKD device types
*3390*|*3380*|*9345*)
chzdev dasd-eckd --enable --persistent "$dev_id" \
$_zdev_dasd_attrs $zdev_dasd_base_args --force
;;
# FBA device types
*3370*|*9336*)
chzdev dasd-fba --enable --persistent "$dev_id" \
$_zdev_dasd_attrs $zdev_dasd_base_args --force
;;
esac
done
;;
esac
done # input redir w/ process substitution causes syntax error in dracut env

View File

@@ -33,6 +33,7 @@
#include "lib/libzds.h"
#include "lib/util_libc.h"
#include "lib/util_str.h"
#include "lib/zt_common.h"
#define COMP "zdsfs: "
@@ -1369,18 +1370,6 @@ static void zdsfs_process_device_file(const char *devfile)
free(buffer);
}
void remove_whitespace(const char *s, char *t)
{
while (*s != '\0') {
if (!isblank(*s)) {
*t = *s;
t++;
}
s++;
}
*t = '\0';
}
static void zdsfs_process_config_file(const char *config)
{
char line[MAX_LINE_LENGTH];
@@ -1403,7 +1392,7 @@ static void zdsfs_process_config_file(const char *config)
/* remove all whitespaces */
tmp = util_malloc(strlen(line) + 1);
remove_whitespace(line, tmp);
util_str_rm_whitespace(line, tmp);
key = strtok(tmp, delimiter);
if (strcmp(key, "restserver") == 0) {
@@ -1496,7 +1485,7 @@ static int zdsfs_process_dataset_conf(const char *config)
linecount++;
/* remove all whitespaces */
tmp = util_malloc(strlen(line) + 1);
remove_whitespace(line, tmp);
util_str_rm_whitespace(line, tmp);
/* skip empty lines */
if (*tmp == '\n' || *tmp == '#') {
free(tmp);

View File

@@ -36,8 +36,10 @@ static int open_dump_file(void)
mount_point = util_strdup("/tmp/zdump-ngdump-XXXXXX");
/* Create a mount point directory */
if (mkdtemp(mount_point) == NULL)
if (mkdtemp(mount_point) == NULL) {
warnx("Could not create directory \"%s\"", mount_point);
goto fail_free;
}
if (mount(l.device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL)) {
warnx("Could not mount \"%s\" (%s)", l.device, strerror(errno));

View File

@@ -42,11 +42,19 @@ static int read_meta_from_file(const char *filename, struct ngdump_meta *meta)
FILE *fp = NULL;
char *line = NULL;
util_log_print(UTIL_LOG_TRACE,
"%s: Reading meta file \"%s\"\n",
__func__, filename);
memset(meta, 0, sizeof(*meta));
fp = fopen(filename, "r");
if (!fp)
if (!fp) {
util_log_print(UTIL_LOG_TRACE,
"%s: Could not open \"%s\" (%s)\n",
__func__, filename, strerror(errno));
return -1;
}
while (fscanf(fp, "%m[^\n]\n", &line) == 1) {
char *ptr, *param = NULL, *value = NULL;
@@ -154,8 +162,11 @@ static int validate_meta(const char *mount_point, struct ngdump_meta *meta)
* that the given partition is a valid NGDump partition but with no
* dump present.
*/
if (!meta->file)
if (!meta->file) {
util_log_print(UTIL_LOG_TRACE,
"%s: No dump file present\n", __func__);
return 0;
}
if (!meta->sha256sum) {
warnx("Invalid NGDump SHA256 checksum");
return -1;
@@ -189,13 +200,18 @@ int ngdump_read_meta_from_device(const char *device, struct ngdump_meta *meta)
/* Create a mount point directory */
if (mkdtemp(mount_point) == NULL) {
warnx("Could not create directory \"%s\"", mount_point);
rc = -1;
goto out;
}
rc = mount(device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL);
if (rc)
if (rc) {
util_log_print(UTIL_LOG_TRACE,
"%s: Could not mount \"%s\" (%s)\n",
__func__, device, strerror(errno));
goto out_rmdir;
}
util_asprintf(&filename, "%s/%s", mount_point, NGDUMP_META_FILENAME);

View File

@@ -13,7 +13,8 @@
#include "boot/s390.h"
static unsigned char ebcdic_037[256] = {
/* EBCDIC-to-ASCII table, using EBCDIC code page 037 by default */
static unsigned char ebcdic[256] = {
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
@@ -84,84 +85,25 @@ static unsigned char ebcdic_037[256] = {
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
static unsigned char ebcdic_500[256] = {
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
-ENP ->LF */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
-IUS */
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
-INP */
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
-SW */
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
/* 0x40 SP RSP ä ---- */
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
/* 0x48 . < ( + | */
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
/* 0x50 & ---- */
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
/* 0x58 ß ! $ * ) ; */
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
/* 0x60 - / ---- Ä ---- ---- ---- */
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
/* 0x68 ---- , % _ > ? */
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x78 * ` : # @ ' = " */
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
/* 0x80 * a b c d e f g */
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/* 0x88 h i ---- ---- ---- */
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
/* 0x90 ° j k l m n o p */
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
/* 0x98 q r ---- ---- */
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
/* 0xA0 ~ s t u v w x */
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
/* 0xA8 y z ---- ---- ---- ---- */
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
/* 0xB0 ^ ---- § ---- */
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
/* 0xC0 { A B C D E F G */
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
/* 0xC8 H I ---- ö ---- */
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
/* 0xD0 } J K L M N O P */
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
/* 0xD8 Q R ---- ü */
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
/* 0xE0 \ S T U V W X */
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
/* 0xE8 Y Z ---- Ö ---- ---- ---- */
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
/* 0xF0 0 1 2 3 4 5 6 7 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
/* Convert 'ebcdic' table from code page 037 to code page 500 if necessary */
void ebcdic_update_table(void)
{
if (!is_zvm()) {
ebcdic[74] = 0x5B; /* ¢ -> [ */
ebcdic[79] = 0x21; /* | -> ! */
ebcdic[90] = 0x5D; /* ! -> ] */
ebcdic[95] = 0x5E; /* ¬ -> ^ */
ebcdic[176] = 0xA2; /* ^ -> ¢ */
ebcdic[186] = 0xAC; /* [ -> ¬ */
ebcdic[187] = 0x7C; /* ] -> | */
}
}
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
unsigned int l)
{
unsigned char *ebc;
unsigned int i;
ebc = is_zvm() ? ebcdic_037 : ebcdic_500;
for (i = 0; i < l; i++)
target[i] = ebc[source[i]];
target[i] = ebcdic[source[i]];
}

View File

@@ -14,6 +14,7 @@
#ifndef __ASSEMBLER__
void ebcdic_update_table(void);
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
unsigned int l);

View File

@@ -363,7 +363,7 @@ static char *number(char *buf, char *end, unsigned long val,
/*
* vsnprintf - Format string and place in a buffer
*
* This funcion only supports a subset of format options defined in the
* This function only supports a subset of format options defined in the
* C standard, i.e.
* specifiers:
* * %s (strings)

View File

@@ -59,7 +59,7 @@ static int menu_read(void)
if (is_zvm())
for (i = 0; i < count; i++)
temp_area[i] = ebcdic_tolower(temp_area[i]);
value = ebcdic_strtoul((char *)temp_area, &endptr, 10);
value = ebcdic_strtoul(temp_area, &endptr, 10);
if ((endptr != temp_area) && (value < BOOT_MENU_ENTRIES - 1) &&
(__stage2_params.config[value] != 0)) {
@@ -68,6 +68,7 @@ static int menu_read(void)
} else {
/* no valid config retry */
printf(msg_econfig);
memset(temp_area, 0, PAGE_SIZE);
count = 0;
}
}
@@ -97,6 +98,13 @@ static int menu_list(void)
return 0;
}
enum param_result {
NUMBER_FOUND = 0,
PRINT_PROMPT = 1,
NOTHING_FOUND = 2,
SCLP_ERROR = 3,
};
/*
* Interpret loadparm
*
@@ -107,15 +115,20 @@ static int menu_list(void)
* 0 - found number to boot, stored in value
* 1 - print prompt
* 2 - nothing found
* 3 - sclp error
*/
static int menu_param(unsigned long *value)
static enum param_result menu_param(unsigned long *value)
{
char loadparm[PARAM_SIZE];
char *endptr;
int i;
if (!sclp_param(loadparm))
*value = ebcdic_strtoul(loadparm, &endptr, 10);
/* try to fetch loadparms from sclp into 'loadparm' */
if (sclp_param(loadparm) != 0)
return SCLP_ERROR;
/* parse number from loadparm */
*value = ebcdic_strtoul(loadparm, &endptr, 10);
/* got number, done */
if (endptr != loadparm)
@@ -127,39 +140,44 @@ static int menu_param(unsigned long *value)
while ((i < PARAM_SIZE) && ecbdic_isspace(loadparm[i]))
i++;
if (!strncmp(&loadparm[i], "PROMPT", 6)) {
*value = 0;
if (!strncmp(&loadparm[i], "PROMPT", 6))
return PRINT_PROMPT;
}
return NOTHING_FOUND;
}
int menu(void)
{
unsigned long value = 0;
enum { DEFAULT_MENU_ENTRY = 0 };
unsigned long value = DEFAULT_MENU_ENTRY;
char *cmd_line_extra;
char endstring[15];
int rc;
cmd_line_extra = (char *)COMMAND_LINE_EXTRA;
memset(cmd_line_extra, 0, COMMAND_LINE_EXTRA_SIZE);
rc = sclp_setup(SCLP_INIT);
if (rc)
if (sclp_setup(SCLP_INIT) != 0) {
/* sclp setup failed boot default */
goto boot;
}
rc = menu_param(&value);
if (rc == 0) {
switch (menu_param(&value)) {
case NUMBER_FOUND:
/* got number from loadparm, boot it */
goto boot;
} else if (rc == 1 && value == 0) {
/* keyword "prompt", show menu */
} else if (__stage2_params.flag == 0) {
/* menu disabled, boot default */
value = 0;
case PRINT_PROMPT:
/* print menu */
break;
case SCLP_ERROR:
/* failed to read from sclp, boot default */
printf("SCLP_ERROR\n");
goto boot;
case NOTHING_FOUND:
if (__stage2_params.flag == 0) {
/* menu disabled, boot default */
goto boot;
}
break;
}
/* print banner */

View File

@@ -18,10 +18,6 @@
#define PARAM_SIZE 8
#define TEXT_OFFSET 4
#define NUMBER_FOUND 0
#define PRINT_PROMPT 1
#define NOTHING_FOUND 2
struct boot_stage2_params {
uint16_t flag;
uint16_t timeout;

View File

@@ -507,6 +507,8 @@ void start(void)
if (secure_boot_enabled())
verify_secure_boot();
ebcdic_update_table();
/*
* cut the kernel header
*/
@@ -525,8 +527,12 @@ void start(void)
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;
/* store address of new kernel to 0 to be able to start it */
*(unsigned long long *)0 = _stage3_parms.load_psw;
/*
* store address of new kernel to 0 to be able to start it.
* -fno-delete-null-pointer-checks allows us to dereference 0 here,
* which otherwise would be an UB.
*/
*(volatile unsigned long long *)0 = _stage3_parms.load_psw;
kdump_stage3();

View File

@@ -66,7 +66,8 @@ makedumpfile -f -E --message-level 7 -d 31 "$VMCORE" "$MNTDIR/$DUMP"
# Create a file containing meta information for zgetdump
#
save_meta
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
save_logs
cleanup
exit 0

View File

@@ -68,24 +68,33 @@ typedef enum {
undefined
} definition_t;
/* Disk information type */
/* Physical disk information type */
struct disk_info {
dev_t disk;
disk_type_t type;
dev_t device; /* logical device for bootmap creation */
dev_t partition;
int devno;
int partnum;
int phy_block_size;
int fs_block_size;
uint64_t phy_blocks;
struct hd_geometry geo;
char* name;
char* drv_name;
definition_t targetbase_def;
int is_nvme;
dev_t basedisks[MAX_TARGETS]; /* array of physical disks for
* bootstrap blocks recording
*/
};
/* Logical device information type */
struct device_info {
dev_t device; /* logical device for bootmap creation */
char *name; /* name of the logical device as retrieved
* from "/proc/partitions"
*/
char *drv_name; /* name of the driver managing the logical device
* as retrieved from "/proc/devices", or evaluated
*/
int fs_block_size;
struct disk_info base[MAX_TARGETS]; /* array of physical disks for
* bootstrap blocks recording
*/
};
struct file_range {
@@ -95,51 +104,55 @@ struct file_range {
struct job_target_data;
int disk_get_info(const char *device, struct job_target_data *target,
struct disk_info **info);
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type);
int device_get_info(const char *device, struct job_target_data *target,
struct device_info **info);
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type,
int disk_id);
int disk_is_tape(const char *device);
int disk_type_is_scsi(struct disk_ext_type *ext_type);
int disk_type_is_eckd_ldl(struct disk_ext_type *ext_type);
int disk_type_is_nvme(struct disk_ext_type *ext_type);
int disk_type_is_eckd(disk_type_t type);
int disk_info_set_fs_block(const char *filename, struct disk_info *info);
int disk_get_info_from_file(const char* filename,
struct job_target_data* target,
struct disk_info** info);
void disk_free_info(struct disk_info* info);
char* disk_get_type_name(disk_type_t type);
int device_info_set_fs_block(const char *filename, struct device_info *info);
int device_get_info_from_file(const char *filename,
struct job_target_data *target,
struct device_info **info);
void device_free_info(struct device_info *info);
char *disk_get_type_name(disk_type_t type);
char *disk_get_ipl_type(disk_type_t type, int is_dump);
int disk_is_large_volume(struct disk_info* info);
int disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info);
int disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info);
int disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info);
void disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
struct disk_info* info);
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount,
disk_blockptr_t *block, struct disk_info *info);
struct disk_info *info);
int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
size_t bytecount, disk_blockptr_t *block,
int fs_block_size, struct disk_info *info);
blocknum_t disk_write_block_buffer(struct misc_fd *fd, int fd_is_basedisk,
const void* buffer, size_t bytecount,
disk_blockptr_t** blocklist,
struct disk_info *info);
int fs_block_size, struct disk_info *info);
blocknum_t disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist,
int fs_block_size,
struct disk_info *info, int align,
off_t *offset);
void disk_print_devt(dev_t d);
void disk_print_devname(dev_t d);
void prepare_footnote_ptr(int source, char *ptr);
void print_footnote_ref(int source, const char *prefix);
void disk_print_info(struct disk_info *info, int source);
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
void device_print_info(struct device_info *info, struct job_target_data *td);
int disk_is_zero_block(disk_blockptr_t *block, struct disk_info *info);
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
struct disk_info* info);
struct disk_info *info);
blocknum_t disk_get_blocklist_from_file(const char* filename,
struct file_range *reg,
disk_blockptr_t** blocklist,
struct disk_info* pinfo);
disk_blockptr_t **blocklist,
int fs_block_size,
struct disk_info *info);
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);

View File

@@ -16,6 +16,7 @@
#include "job.h"
#include "misc.h"
#include "zipl.h"
#include "boot.h"
#include "boot/boot_defs.h"
#define NR_PROGRAM_TABLES NR_BLKPTR_FORMATS
@@ -52,6 +53,11 @@ struct component_loc {
struct component_footer {
component_type type;
const char *desc;
int fs_block_aligned; /* if true, then the component should
* be aligned to fs_block_size boundary
* in the bootmap file. Otherwise, to
* phy_block_size boundary.
*/
};
struct program_component {
@@ -66,18 +72,25 @@ struct program_table {
blocknum_t stage1b_count;
};
/* Bootloader Installation Set */
struct install_set {
/* A part of Bootloader Installation Set specific for an individual mirror */
struct install_set_mirror {
struct program_table tables[NR_PROGRAM_TABLES];
struct program_component *components[NR_PROGRAM_COMPONENTS];
char *basetmp;
unsigned int print_details:1;
unsigned int skip_prepare_blocklist:1;
};
/* Bootloader Installation Set */
struct install_set {
int nr_menu_entries;
struct misc_fd mfd;
char *basetmp[MAX_TARGETS];
struct install_set_mirror mirrors[MAX_TARGETS];
struct file_range comp_reg[NR_PROGRAM_COMPONENTS][BOOT_MENU_ENTRIES];
char *filename;
unsigned int skip_prepare_device:1;
unsigned int tmp_filename_created:1;
unsigned int skip_prepare:1;
unsigned int print_details:1;
struct disk_info *info;
struct device_info *info;
disk_blockptr_t scsi_dump_sb_blockptr;
};
@@ -106,16 +119,36 @@ static inline const char *component_desc_by_id(enum program_component_id id)
return component_footers[id].desc;
}
static inline int fs_block_aligned_by_id(enum program_component_id id)
{
return component_footers[id].fs_block_aligned;
}
static inline struct program_component *get_component(struct install_set *bis,
int mirror_id,
int i, int j)
{
return bis->components[i] + j;
return bis->mirrors[mirror_id].components[i] + j;
}
/*
* Get a component as a region in the bootmap file
*/
static inline struct file_range *get_component_range(struct install_set *bis,
int comp_id, int menu_idx)
{
return &bis->comp_reg[comp_id][menu_idx];
}
static inline struct file_range *get_envblk_range(struct install_set *bis)
{
return get_component_range(bis, COMPONENT_ID_ENVBLK, 0);
}
int prepare_bootloader(struct job_data *job, struct install_set *bis);
int install_bootloader(struct job_data *job, struct install_set *bis);
int post_install_bootloader(struct job_data *job, struct install_set *bis);
void free_bootloader(struct install_set *bis);
void free_bootloader(struct install_set *bis, struct job_data *job);
int install_tapeloader(const char* device, const char* image,
const char* parmline, const char* ramdisk,
address_t image_addr, address_t parm_addr,
@@ -127,11 +160,13 @@ int install_mvdump(char* const device[], struct job_target_data* target,
int install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
blocknum_t stage2_count, struct disk_info *info);
blocknum_t stage2_count, int fs_block_size,
struct disk_info *info);
int install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count,
disk_blockptr_t *stage2_list,
blocknum_t stage2_count, struct disk_info *info);
blocknum_t stage2_count, int fs_block_size,
struct disk_info *info);
int rewind_tape(int fd);
#endif /* INSTALL_H */

View File

@@ -34,6 +34,7 @@ int misc_asprintf(char **out, const char *fmt, ...);
void* misc_calloc(size_t n, size_t size);
char* misc_strdup(const char* s);
int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate);
int misc_fsync(struct misc_fd *mfd, const char *filename);
int misc_read(int fd, void* buffer, size_t count);
int misc_read_file(const char* filename, char** buffer, size_t* size,
int nil_terminate);

View File

@@ -44,6 +44,7 @@
#define MAX_DUMP_VOLUMES 32
#define MAX_TARGETS 32
#define FIRST_MIRROR_ID 0
#define SECURE_BOOT_UNDEFINED -1
#define SECURE_BOOT_DISABLED 0

View File

@@ -104,5 +104,7 @@ logsave "$MNTDIR/makedumpfile.log" makedumpfile -f -E --message-level 7 -d 31 "$
# Create a file containing meta information for zgetdump
#
save_meta
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
save_logs
quit

File diff suppressed because it is too large Load Diff

View File

@@ -104,12 +104,13 @@ read_block_by_offset(int fd, int blksize, uint64_t offset, char *buffer)
return 0;
}
static int determine_virtblk_type(struct disk_info *data,
static int determine_virtblk_type(struct device_info *dev_info,
const struct stat *stats)
{
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
int fd, rc, shift, sb;
char *device;
char *buffer;
int fd, rc, shift, sb;
rc = 0;
buffer = (char *) malloc(data->phy_block_size);
@@ -131,9 +132,9 @@ static int determine_virtblk_type(struct disk_info *data,
if (data->geo.heads == 15) {
/* assume DASD */
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
rc = misc_temp_dev(data->device, 1, &device);
rc = misc_temp_dev(dev_info->device, 1, &device);
if (rc)
goto out_err;
@@ -167,7 +168,7 @@ static int determine_virtblk_type(struct disk_info *data,
} else {
data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
dev_info->device = stats->st_rdev & ~SCSI_PARTN_MASK;
}
out_err:
@@ -195,6 +196,8 @@ static int set_target_parameters(FILE *fh, struct job_target_data *td)
char *error;
int i;
/* reset array of target parameters */
memset(td->targets, 0, sizeof(td->targets));
/**
* Process a stream of 'key=value' pairs and distribute
* them into groups.
@@ -284,65 +287,28 @@ error:
return -1;
}
static void print_base_disk_params(struct job_target_data *td, int index)
{
disk_type_t type = get_targettype(td, index);
if (!verbose)
return;
{
fprintf(stderr, "Base disk '%s':\n", get_targetbase(td, index));
fprintf(stderr, " layout........: %s\n", disk_get_type_name(type));
}
if (disk_type_is_eckd(type)) {
fprintf(stderr, " heads.........: %u\n", get_targetheads(td, index));
fprintf(stderr, " sectors.......: %u\n", get_targetsectors(td, index));
fprintf(stderr, " cylinders.....: %u\n", get_targetcylinders(td, index));
}
{
fprintf(stderr, " start.........: %lu\n", get_targetoffset(td, index));
fprintf(stderr, " blksize.......: %u\n", get_targetblocksize(td, index));
}
}
/**
* Set disk info using ready target parameters provided either by
* user, or by script
*/
static int disk_set_info_by_hint(struct job_target_data *td,
struct disk_info *data, int fd)
static int device_set_info_by_hint(struct job_target_data *td,
struct device_info *data, int fd)
{
int majnum, minnum;
struct stat stats;
int i;
/*
* Currently multiple base disks with different parameters
* are not supported
*/
data->devno = -1;
data->phy_block_size = get_targetblocksize(td, 0);
data->type = get_targettype(td, 0);
assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) {
if (data->type != get_targettype(td, i) ||
data->phy_block_size != get_targetblocksize(td, i)) {
print_base_disk_params(td, 0);
print_base_disk_params(td, i);
error_reason("Inconsistent base disk geometry in target device");
return -1;
}
}
data->partnum = 0;
data->targetbase_def = undefined;
/* set devno, type, phy_block_size */
for (i = 0; i < td->nr_targets; i++) {
definition_t defined_as;
data->base[i].devno = -1;
data->base[i].type = get_targettype(td, i);
data->base[i].phy_block_size = get_targetblocksize(td, i);
if (sscanf(get_targetbase(td, i),
"%d:%d", &majnum, &minnum) == 2) {
data->basedisks[i] = makedev(majnum, minnum);
defined_as = defined_as_device;
data->base[i].disk = makedev(majnum, minnum);
data->base[i].targetbase_def = defined_as_device;
} else {
if (stat(get_targetbase(td, i), &stats)) {
error_reason(strerror(errno));
@@ -356,47 +322,51 @@ static int disk_set_info_by_hint(struct job_target_data *td,
get_targetbase(td, i));
return -1;
}
data->basedisks[i] = stats.st_rdev;
defined_as = defined_as_name;
data->base[i].disk = stats.st_rdev;
data->base[i].targetbase_def = defined_as_name;
}
if (data->targetbase_def != undefined &&
data->targetbase_def != defined_as) {
if (data->base[i].targetbase_def !=
data->base[0].targetbase_def) {
error_reason("Target base disks are defined by different ways");
return -1;
}
data->targetbase_def = defined_as;
data->base[i].partnum = 0;
if (data->base[i].type == disk_type_scsi &&
ioctl(fd, NVME_IOCTL_ID) >= 0)
data->base[i].is_nvme = 1;
}
if (data->type == disk_type_scsi && ioctl(fd, NVME_IOCTL_ID) >= 0)
data->is_nvme = 1;
return 0;
}
/**
* Calculate target parameters in the case when no hints were provided
*/
static int disk_set_info_auto(struct disk_info *data,
static int disk_set_info_auto(struct device_info *dev_info,
const struct stat *stats, int fd)
{
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
struct dasd_information dasd_info;
if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) {
error_reason("Could not get blocksize");
return -1;
}
if (!data->drv_name) {
if (!dev_info->drv_name) {
/* Driver name cannot be read */
if (ioctl(fd, BIODASDINFO, &dasd_info)) {
data->devno = -1;
if (data->geo.start) {
/* SCSI partition */
data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
data->partnum =
stats->st_rdev & SCSI_PARTN_MASK;
dev_info->device =
stats->st_rdev & ~SCSI_PARTN_MASK;
} else {
/* SCSI disk */
data->type = disk_type_scsi;
data->partnum = 0;
data->device = stats->st_rdev;
dev_info->device = stats->st_rdev;
}
} else {
/* DASD */
@@ -404,9 +374,9 @@ static int disk_set_info_auto(struct disk_info *data,
if (disk_determine_dasd_type(data, dasd_info))
return -1;
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
}
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_DASD) == 0) {
} else if (strcmp(dev_info->drv_name, UTIL_PROC_DEV_ENTRY_DASD) == 0) {
/* Driver name is 'dasd' */
if (ioctl(fd, BIODASDINFO, &dasd_info)) {
error_reason("Could not determine DASD type");
@@ -416,27 +386,29 @@ static int disk_set_info_auto(struct disk_info *data,
if (disk_determine_dasd_type(data, dasd_info))
return -1;
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_SD) == 0) {
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
} else if (strcmp(dev_info->drv_name, UTIL_PROC_DEV_ENTRY_SD) == 0) {
/* Driver name is 'sd' */
data->devno = -1;
data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
dev_info->device = stats->st_rdev & ~SCSI_PARTN_MASK;
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_VIRTBLK) == 0) {
} else if (strcmp(dev_info->drv_name,
UTIL_PROC_DEV_ENTRY_VIRTBLK) == 0) {
/* Driver name is 'virtblk' */
if (ioctl(fd, HDIO_GETGEO, &data->geo) != 0)
perror("Could not retrieve disk geometry information.");
if (ioctl(fd, BLKSSZGET, &data->phy_block_size) != 0)
perror("Could not retrieve blocksize information.");
if (determine_virtblk_type(data, stats)) {
if (determine_virtblk_type(dev_info, stats)) {
error_reason("Virtblk device type not clearly "
"determined.");
return -1;
}
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 &&
} else if (strcmp(dev_info->drv_name,
UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 &&
ioctl(fd, NVME_IOCTL_ID) >= 0) {
/* NVMe path, driver name is 'blkext' */
data->devno = -1;
@@ -444,18 +416,20 @@ static int disk_set_info_auto(struct disk_info *data,
data->is_nvme = 1;
if (util_sys_dev_is_partition(stats->st_rdev)) {
if (util_sys_get_base_dev(stats->st_rdev, &data->device))
if (util_sys_get_base_dev(stats->st_rdev,
&dev_info->device))
return -1;
data->partnum = util_sys_get_partnum(stats->st_rdev);
if (data->partnum == -1)
return -1;
} else {
data->device = stats->st_rdev;
dev_info->device = stats->st_rdev;
data->partnum = 0;
}
} else {
/* Driver name is unknown */
error_reason("Unsupported device driver '%s'", data->drv_name);
error_reason("Unsupported device driver '%s'",
dev_info->drv_name);
return -1;
}
return 0;
@@ -488,7 +462,7 @@ static void set_source_type(struct job_target_data *td,
td->source = source_auto;
}
static void set_driver_name(int fd, struct disk_info *info, dev_t device)
static void set_driver_name(int fd, struct device_info *info, dev_t device)
{
struct util_proc_dev_entry dev_entry;
@@ -555,30 +529,17 @@ static int run_targetbase_script(struct job_target_data *td,
* Note: geo.start contains a sector number offset measured in
* physical blocks, not sectors (512 bytes)
*/
static int disk_set_geometry_by_hint(struct job_target_data *td,
struct disk_info *data)
static int device_set_geometry_by_hint(struct job_target_data *td,
struct device_info *data)
{
int i;
/*
* Currently multiple base disks with different parameters
* are not supported
*/
data->geo.heads = get_targetheads(td, 0);
data->geo.sectors = get_targetsectors(td, 0);
data->geo.cylinders = get_targetcylinders(td, 0);
data->geo.start = get_targetoffset(td, 0);
assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) {
if (data->geo.heads != get_targetheads(td, i) ||
data->geo.sectors != get_targetsectors(td, i) ||
data->geo.cylinders != get_targetcylinders(td, i) ||
data->geo.start != get_targetoffset(td, i)) {
print_base_disk_params(td, 0);
print_base_disk_params(td, i);
error_reason("Inconsistent base disk geometry in target device");
return -1;
}
for (i = 0; i < td->nr_targets; i++) {
data->base[i].geo.heads = get_targetheads(td, i);
data->base[i].geo.sectors = get_targetsectors(td, i);
data->base[i].geo.cylinders = get_targetcylinders(td, i);
data->base[i].geo.start = get_targetoffset(td, i);
}
return 0;
}
@@ -603,7 +564,6 @@ static int disk_set_info_complete(struct job_target_data *td,
struct disk_info *data,
struct stat *stats, int fd)
{
struct util_proc_part_entry part_entry;
long devsize;
/* Get size of device in sectors (512 byte) */
@@ -631,15 +591,22 @@ static int disk_set_info_complete(struct job_target_data *td,
data->geo.start / (data->phy_block_size / 512);
if (data->partnum != 0)
data->partition = stats->st_rdev;
return 0;
}
static int device_set_info_complete(struct device_info *dev_info)
{
struct util_proc_part_entry part_entry;
/* Try to get device name */
if (util_proc_part_get_entry(data->device, &part_entry) == 0) {
data->name = misc_strdup(part_entry.name);
if (util_proc_part_get_entry(dev_info->device, &part_entry) == 0) {
dev_info->name = misc_strdup(part_entry.name);
util_proc_part_free_entry(&part_entry);
if (data->name == NULL)
if (!dev_info->name)
return -1;
}
/* Initialize file system block size with invalid value */
data->fs_block_size = -1;
dev_info->fs_block_size = -1;
return 0;
}
@@ -663,16 +630,17 @@ static int disk_set_info_complete(struct job_target_data *td,
* To exclude any user assumptions about the DEVICE, this function
* should be called with TD pointing to a zeroed structure.
*
* DEVICE: logical, or physical device, optionally formated with a
* DEVICE: logical, or physical device, optionally formatted with a
* file system.
*/
int disk_get_info(const char *device, struct job_target_data *td,
struct disk_info **info)
int device_get_info(const char *device, struct job_target_data *td,
struct device_info **info)
{
char *script_file = NULL;
struct disk_info *data;
struct device_info *data;
struct stat stats;
int fd;
int i;
if (stat(device, &stats)) {
error_reason(strerror(errno));
@@ -683,10 +651,10 @@ int disk_get_info(const char *device, struct job_target_data *td,
error_reason(strerror(errno));
return -1;
}
data = (struct disk_info *)misc_malloc(sizeof(struct disk_info));
data = (struct device_info *)misc_malloc(sizeof(struct device_info));
if (!data)
goto error;
memset((void *)data, 0, sizeof(struct disk_info));
memset((void *)data, 0, sizeof(struct device_info));
set_driver_name(fd, data, stats.st_rdev);
set_source_type(td, data->drv_name, &script_file);
switch (td->source) {
@@ -696,9 +664,9 @@ int disk_get_info(const char *device, struct job_target_data *td,
/* target parameters were set by the script output */
assert(target_parameters_are_set(td));
if (disk_set_geometry_by_hint(td, data))
if (device_set_geometry_by_hint(td, data))
goto error;
if (disk_set_info_by_hint(td, data, fd))
if (device_set_info_by_hint(td, data, fd))
goto error;
data->device = stats.st_rdev;
break;
@@ -709,20 +677,20 @@ int disk_get_info(const char *device, struct job_target_data *td,
*/
assert(target_parameters_are_set(td));
if (disk_set_geometry_by_hint(td, data))
if (device_set_geometry_by_hint(td, data))
goto error;
if (disk_set_info_by_hint(td, data, fd))
if (device_set_info_by_hint(td, data, fd))
goto error;
/*
* multiple base disks are not supported
* with this source type
*/
assert(td->nr_targets == 1);
data->device = data->basedisks[0];
data->device = data->base[FIRST_MIRROR_ID].disk;
break;
case source_auto:
/* no ready target parameters are available */
if (disk_set_geometry_auto(fd, data))
if (disk_set_geometry_auto(fd, &data->base[FIRST_MIRROR_ID]))
goto error;
if (disk_set_info_auto(data, &stats, fd))
goto error;
@@ -730,13 +698,17 @@ int disk_get_info(const char *device, struct job_target_data *td,
* multiple base disks are not supported
* with this source type
*/
data->basedisks[0] = data->device;
data->base[FIRST_MIRROR_ID].disk = data->device;
td->nr_targets = 1;
break;
default:
assert(0);
}
if (disk_set_info_complete(td, data, &stats, fd))
for (i = 0; i < td->nr_targets; i++) {
if (disk_set_info_complete(td, &data->base[i], &stats, fd))
goto error;
}
if (device_set_info_complete(data))
goto error;
free(script_file);
close(fd);
@@ -772,17 +744,20 @@ disk_is_tape(const char* device)
* partition, etc). In case of success the resulted disk type is
* stored in EXT_TYPE.
*/
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type)
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type,
int disk_idx)
{
struct job_target_data tmp = {.source = source_unknown};
struct device_info *dev_info;
struct disk_info *info;
if (disk_get_info(device, &tmp, &info))
if (device_get_info(device, &tmp, &dev_info))
return -1;
info = &dev_info->base[disk_idx];
ext_type->type = info->type;
ext_type->is_nvme = info->is_nvme;
disk_free_info(info);
device_free_info(dev_info);
free_target_data(&tmp);
return 0;
}
@@ -811,7 +786,7 @@ int disk_type_is_eckd(disk_type_t type)
/**
* Retrieve and set block size of the file system which contains FILENAME
*/
int disk_info_set_fs_block(const char *filename, struct disk_info *info)
int device_info_set_fs_block(const char *filename, struct device_info *dinfo)
{
int blocksize;
int fd;
@@ -830,7 +805,7 @@ int disk_info_set_fs_block(const char *filename, struct disk_info *info)
filename);
return -1;
}
info->fs_block_size = blocksize;
dinfo->fs_block_size = blocksize;
return 0;
}
@@ -838,9 +813,9 @@ int disk_info_set_fs_block(const char *filename, struct disk_info *info)
* Retrieve disk info of the device which contains FILENAME
* and set the filesystem block size
*/
int disk_get_info_from_file(const char *filename,
struct job_target_data *target,
struct disk_info **info)
int device_get_info_from_file(const char *filename,
struct job_target_data *target,
struct device_info **info)
{
struct stat stats;
char *device;
@@ -851,15 +826,15 @@ int disk_get_info_from_file(const char *filename,
}
if (misc_temp_dev(stats.st_dev, 1, &device))
return -1;
if (disk_get_info(device, target, info)) {
if (device_get_info(device, target, info)) {
misc_free_temp_dev(device);
return -1;
}
misc_free_temp_dev(device);
return disk_info_set_fs_block(filename, *info);
return device_info_set_fs_block(filename, *info);
}
void disk_free_info(struct disk_info *info)
void device_free_info(struct device_info *info)
{
if (!info)
return;
@@ -877,28 +852,29 @@ void disk_free_info(struct disk_info *info)
* otherwise. */
static int
disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
blocknum_t* physical, struct disk_info* info)
blocknum_t *physical, int fs_block_size,
struct disk_info *disk_info)
{
blocknum_t phy_per_fs;
blocknum_t mapped;
int subblock;
/* No file system: partition or raw disk */
if (info->fs_block_size == -1) {
if (fs_block_size == -1) {
if (fd_is_basedisk)
*physical = logical;
else
*physical = logical + info->geo.start;
*physical = logical + disk_info->geo.start;
return 0;
}
/*
* Get mapping in file system blocks
*/
phy_per_fs = info->fs_block_size / info->phy_block_size;
phy_per_fs = fs_block_size / disk_info->phy_block_size;
subblock = logical % phy_per_fs;
if (fs_map(fd, logical * info->phy_block_size,
&mapped, info->fs_block_size) != 0)
if (fs_map(fd, logical * disk_info->phy_block_size,
&mapped, fs_block_size) != 0)
return -1;
if (mapped == 0) {
/* This is a hole in the file */
@@ -907,7 +883,7 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
/* Convert file system block to physical */
*physical = mapped * phy_per_fs + subblock;
/* Add partition start */
*physical += info->geo.start;
*physical += disk_info->geo.start;
}
return 0;
}
@@ -916,7 +892,7 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
/* Return the cylinder on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */
int
disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info)
disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info *info)
{
return blocknum / (info->geo.heads * info->geo.sectors);
}
@@ -925,7 +901,7 @@ disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info)
/* Return the head on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */
int
disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info)
disk_head_from_blocknum(blocknum_t blocknum, struct disk_info *info)
{
return (blocknum / info->geo.sectors) % info->geo.heads;
}
@@ -934,7 +910,7 @@ disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info)
/* Return the sector on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */
int
disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info *info)
{
return blocknum % info->geo.sectors + 1;
}
@@ -945,7 +921,7 @@ disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
* layout. */
void
disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
struct disk_info* info)
struct disk_info *info)
{
switch (info->type) {
case disk_type_scsi:
@@ -986,8 +962,9 @@ disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
* otherwise. On success OFFSET contains offset of the first written byte
*/
static int
disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void *data,
size_t bytecount, disk_blockptr_t *block,
disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk,
const void *data, size_t bytecount,
disk_blockptr_t *block, int fs_block_size,
struct disk_info *info, int align, off_t *offset)
{
blocknum_t current_block;
@@ -1021,7 +998,7 @@ disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void
if (block != NULL) {
/* Store block pointer */
if (disk_get_blocknum(mfd->fd, is_base_disk, current_block,
&blocknum, info))
&blocknum, fs_block_size, info))
return -1;
disk_blockptr_from_blocknum(block, blocknum, info);
}
@@ -1030,11 +1007,13 @@ disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void
return 0;
}
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount,
disk_blockptr_t *block, struct disk_info *info)
int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
size_t bytecount, disk_blockptr_t *block,
int fs_block_size, struct disk_info *info)
{
return disk_write_block_aligned_base(mfd, 0, data, bytecount, block,
info, info->phy_block_size, NULL);
fs_block_size, info,
info->phy_block_size, NULL);
}
/**
@@ -1047,8 +1026,9 @@ int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytec
* otherwise.
*/
blocknum_t
disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer,
size_t bytecount, disk_blockptr_t **blocklist,
disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info *info, int align, off_t *offset)
{
blocknum_t count;
@@ -1061,6 +1041,7 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
*blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) *
count);
if (*blocklist == NULL) {
close(mfd->fd);
return 0;
@@ -1073,6 +1054,7 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
rc = disk_write_block_aligned_base(mfd, fd_is_basedisk,
VOID_ADD(buffer, written),
chunk_size, &(*blocklist)[i],
fs_block_size,
info,
i == 0 ? align : info->phy_block_size,
&pos);
@@ -1085,12 +1067,14 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
}
blocknum_t
disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer,
size_t bytecount, disk_blockptr_t **blocklist,
disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk,
const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info *info)
{
return disk_write_block_buffer_align(mfd, fd_is_basedisk, buffer,
bytecount, blocklist, info,
bytecount, blocklist,
fs_block_size, info,
info->phy_block_size, NULL);
}
@@ -1167,83 +1151,95 @@ char *disk_get_ipl_type(disk_type_t type, int is_dump)
/* Return non-zero for ECKD large volumes. */
int
disk_is_large_volume(struct disk_info* info)
disk_is_large_volume(struct disk_info *info)
{
return (info->type == disk_type_eckd_ldl ||
info->type == disk_type_eckd_cdl) &&
info->geo.cylinders == 0xfffe;
}
/* Print textual representation of INFO contents. */
void disk_print_info(struct disk_info *info, int source)
static void disk_print_info(struct disk_info *info, int source)
{
const char *prefix = " ";
char footnote[4] = "";
prepare_footnote_ptr(source, footnote);
printf(" Device..........................: ");
disk_print_devt(info->device);
printf("%sDisk............................: ", prefix);
disk_print_devt(info->disk);
if (info->targetbase_def == defined_as_device)
printf("%s", footnote);
printf("\n");
if (info->partnum != 0) {
printf(" Partition.......................: ");
printf("%sPartition.......................: ", prefix);
disk_print_devt(info->partition);
printf("\n");
}
if (info->name) {
printf(" Device name.....................: %s",
info->name);
if (info->targetbase_def == defined_as_name)
printf("%s", footnote);
printf("\n");
}
if (info->drv_name) {
printf(" Device driver name..............: %s\n",
info->drv_name);
}
if (((info->type == disk_type_fba) ||
(info->type == disk_type_diag) ||
(info->type == disk_type_eckd_ldl) ||
(info->type == disk_type_eckd_cdl)) &&
(source == source_auto)) {
printf(" DASD device number..............: %04x\n",
info->devno);
printf("%sDASD device number..............: %04x\n",
prefix, info->devno);
}
printf(" Type............................: disk %s\n",
(info->partnum != 0) ? "partition" : "device");
printf(" Disk layout.....................: %s%s\n",
disk_get_type_name(info->type), footnote);
printf("%sType............................: disk %s\n",
prefix, (info->partnum != 0) ? "partition" : "device");
printf("%sDisk layout.....................: %s%s\n",
prefix, disk_get_type_name(info->type), footnote);
if (disk_type_is_eckd(info->type)) {
printf(" Geometry - heads................: %d%s\n",
info->geo.heads, footnote);
printf(" Geometry - sectors..............: %d%s\n",
info->geo.sectors, footnote);
printf("%sGeometry - heads................: %d%s\n",
prefix, info->geo.heads, footnote);
printf("%sGeometry - sectors..............: %d%s\n",
prefix, info->geo.sectors, footnote);
if (disk_is_large_volume(info)) {
/* ECKD large volume. There is not enough information
* available in INFO to calculate disk cylinder size. */
printf(" Geometry - cylinders............: > 65534\n");
printf("%sGeometry - cylinders............: > 65534\n",
prefix);
} else {
printf(" Geometry - cylinders............: %d%s\n",
info->geo.cylinders, footnote);
printf("%sGeometry - cylinders............: %d%s\n",
prefix, info->geo.cylinders, footnote);
}
}
printf(" Geometry - start................: %ld%s\n",
info->geo.start, footnote);
if (info->fs_block_size >= 0)
printf(" File system block size..........: %d\n",
info->fs_block_size);
printf(" Physical block size.............: %d%s\n",
info->phy_block_size, footnote);
printf(" Device size in physical blocks..: %ld\n",
(long) info->phy_blocks);
print_footnote_ref(source, " ");
printf("%sGeometry - start................: %ld%s\n",
prefix, info->geo.start, footnote);
printf("%sPhysical block size.............: %d%s\n",
prefix, info->phy_block_size, footnote);
printf("%sDisk size in physical blocks....: %ld\n",
prefix, (long)info->phy_blocks);
print_footnote_ref(source, prefix);
}
void device_print_info(struct device_info *this, struct job_target_data *td)
{
int i;
printf(" Device............................: ");
disk_print_devt(this->device);
printf("\n");
if (this->name) {
printf(" Device name.......................: %s",
this->name);
printf("\n");
}
if (this->drv_name) {
printf(" Device driver name................: %s\n",
this->drv_name);
}
if (this->fs_block_size >= 0)
printf(" File system block size............: %d\n",
this->fs_block_size);
for (i = 0; i < td->nr_targets; i++) {
printf(" Base %d:\n", i + 1);
disk_print_info(&this->base[i], td->source);
}
}
/* Check whether a block is a zero block which identifies a hole in a file.
* Return non-zero if BLOCK is a zero block, 0 otherwise. */
int
disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info)
disk_is_zero_block(disk_blockptr_t *block, struct disk_info *info)
{
switch (info->type) {
case disk_type_scsi:
@@ -1269,7 +1265,7 @@ disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info)
* blocks can be merged, 0 otherwise. */
static int
can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
struct disk_info* info)
struct disk_info *info)
{
int max_count;
@@ -1315,7 +1311,7 @@ can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
* type. */
static void
merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
struct disk_info* info)
struct disk_info *info)
{
switch (info->type) {
case disk_type_scsi:
@@ -1338,7 +1334,7 @@ merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
* number of elements in the list. */
blocknum_t
disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
struct disk_info* info)
struct disk_info *info)
{
blocknum_t i;
blocknum_t last;
@@ -1365,8 +1361,8 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
*/
blocknum_t
disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
disk_blockptr_t **blocklist,
struct disk_info* info)
disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info *info)
{
struct stat stats;
int fd;
@@ -1419,7 +1415,8 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
}
/* Build list */
for (i = 0; i < blk_count; i++) {
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum, info)) {
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum,
fs_block_size, info)) {
close(fd);
return 0;
}

View File

@@ -172,7 +172,8 @@ install_fba_ccw(int fd, disk_blockptr_t *program_table,
int
install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
blocknum_t stage2_count, struct disk_info *info)
blocknum_t stage2_count, int fs_block_size,
struct disk_info *info)
{
struct boot_eckd_stage1b *stage1b;
int stage1b_size, rc = -1;
@@ -187,7 +188,8 @@ install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
if (boot_init_eckd_stage1b(stage1b, stage2_list, stage2_count))
goto out_free_stage1b;
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
stage1b_list, info);
stage1b_list, fs_block_size,
info);
if (*stage1b_count == 0)
goto out_free_stage1b;
rc = 0;
@@ -291,7 +293,7 @@ static int install_eckd_cdl_ld(int fd, disk_blockptr_t *br,
return -1;
rc = DRY_RUN_FUNC(misc_write(fd, &vl, sizeof(vl)));
if (rc)
error_text("Could not update volume lablel 'vol1'");
error_text("Could not update volume label 'vol1'");
return 0;
}
@@ -430,47 +432,39 @@ static int install_bootloader_dump(struct program_table *tables,
int install_bootloader(struct job_data *job, struct install_set *bis)
{
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
struct disk_info *info = bis->info;
char footnote[4];
int rc;
int i;
if (!info)
if (!bis->info)
return 0;
prepare_footnote_ptr(job->target.source, footnote);
/* Inform user about what we're up to */
printf("Preparing boot device for %s%s: ",
disk_get_ipl_type(info->type,
job->id == job_dump_partition),
job->id == job_dump_partition ? "dump" : "IPL");
if (info->name) {
printf("%s", info->name);
if (info->devno >= 0)
printf(" (%04x)", info->devno);
printf(".\n");
} else if (info->devno >= 0) {
printf("%04x.\n", info->devno);
} else {
disk_print_devt(info->device);
printf(".\n");
}
/* Install independently on each physical target base */
printf("Preparing boot device: ");
if (bis->info->name)
printf("%s", bis->info->name);
else
disk_print_devt(bis->info->device);
printf(".\n");
/* Install independently on each physical target base */
for (i = 0; i < job_get_nr_targets(job); i++) {
struct disk_info *info;
int fd;
info = &bis->info->base[i];
if (verbose) {
printf("Installing on base disk: ");
disk_print_devname(info->basedisks[i]);
disk_print_devname(info->disk);
printf("%s.\n", footnote);
}
/* Open device file */
fd = open(bis->basetmp[i], O_RDWR);
fd = open(bis->mirrors[i].basetmp, O_RDWR);
if (fd == -1) {
error_reason(strerror(errno));
error_text("Could not open temporary device file '%s'",
bis->basetmp[i]);
bis->mirrors[i].basetmp);
return -1;
}
/* Ensure that potential cache inconsistencies between disk and
@@ -488,38 +482,41 @@ int install_bootloader(struct job_data *job, struct install_set *bis)
* picture in comments above)
*/
if (job->id == job_dump_partition) {
rc = install_bootloader_dump(bis->tables, info,
rc = install_bootloader_dump(bis->mirrors[i].tables,
info,
scsi_dump_sb_blockptr,
job_dump_is_ngdump(job),
fd);
} else {
rc = install_bootloader_ipl(bis->tables, info,
rc = install_bootloader_ipl(bis->mirrors[i].tables,
info,
fd);
}
if (fsync(fd))
error_text("Could not sync device file '%s'",
bis->basetmp[i]);
bis->mirrors[i].basetmp);
if (close(fd))
error_text("Could not close device file '%s'",
bis->basetmp[i]);
bis->mirrors[i].basetmp);
if (rc)
break;
if (!dry_run && rc == 0) {
if (info->devno >= 0)
syslog(LOG_INFO, "Boot loader written to %s (%04x) - %02x:%02x",
(bis->info->name ? bis->info->name : "-"),
info->devno,
major(bis->info->device),
minor(bis->info->device));
else
syslog(LOG_INFO, "Boot loader written to %s - %02x:%02x",
(bis->info->name ? bis->info->name : "-"),
major(bis->info->device),
minor(bis->info->device));
}
}
if (verbose)
print_footnote_ref(job->target.source, "");
if (!dry_run && rc == 0) {
if (info->devno >= 0)
syslog(LOG_INFO, "Boot loader written to %s (%04x) - "
"%02x:%02x",
(info->name ? info->name : "-"), info->devno,
major(info->device), minor(info->device));
else
syslog(LOG_INFO, "Boot loader written to %s - "
"%02x:%02x",
(info->name ? info->name : "-"),
major(info->device), minor(info->device));
}
return rc;
}
@@ -784,6 +781,8 @@ install_tapeloader(const char* device, const char* image, const char* parmline,
error_text("Could not rewind tape device '%s' to tape", device);
rc = -1;
}
if (!dry_run && fsync(fd))
error_text("Could not sync device file '%s'", device);
close(fd);
return rc;
}
@@ -791,7 +790,7 @@ install_tapeloader(const char* device, const char* image, const char* parmline,
/* Write 64k null bytes with dump signature at offset 512 to
* start of dump partition */
static int
overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic)
overwrite_partition_start(int fd, struct disk_info *info, int mv_dump_magic)
{
int rc;
unsigned int bytes = 65536;
@@ -824,7 +823,7 @@ overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic)
* Force block size of 4KiB because otherwise there is not enough space
* to write the dump tool.
*/
static int check_eckd_dump_partition(struct disk_info* info)
static int check_eckd_dump_partition(struct disk_info *info)
{
unsigned long long end_blk = info->geo.start + info->phy_blocks - 1;
@@ -854,8 +853,10 @@ static void eckd_dump_store_param(struct eckd_dump_param *param,
param->bpt = info->geo.sectors;
}
static int install_svdump_eckd_ldl(struct misc_fd *mfd, struct disk_info *info,
const struct stage2dump_parm_tail *stage2dump_parms)
static int
install_svdump_eckd_ldl(struct misc_fd *mfd, int fs_block_size,
struct disk_info *info,
const struct stage2dump_parm_tail *stage2dump_parms)
{
disk_blockptr_t *stage2_list, *stage1b_list;
blocknum_t stage2_count, stage1b_count;
@@ -878,11 +879,13 @@ static int install_svdump_eckd_ldl(struct misc_fd *mfd, struct disk_info *info,
if (misc_seek(mfd->fd, info->geo.start * info->phy_block_size))
goto out_free_stage2;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info);
&stage2_list,
fs_block_size, info);
if (stage2_count == 0)
goto out_free_stage2_list;
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count,
stage2_list, stage2_count, info))
stage2_list, stage2_count, fs_block_size,
info))
goto out_free_stage2_list;
/* Install stage 0 - afterwards we are at stage 1 position*/
boot_init_eckd_ldl_stage0(&stage0);
@@ -909,8 +912,9 @@ out:
return rc;
}
static int install_dump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
void *stage2, size_t stage2_size, int mvdump,
static int install_dump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
struct disk_info *info, void *stage2,
size_t stage2_size, int mvdump,
int force)
{
blocknum_t count, stage2_count, stage1b_count;
@@ -934,12 +938,14 @@ static int install_dump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
if (misc_seek(mfd->fd, ECKD_CDL_DUMP_REC * info->phy_block_size))
goto out;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info);
&stage2_list, fs_block_size,
info);
if (stage2_count == 0)
goto out;
/* Install stage 1b behind stage 2*/
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count,
stage2_list, stage2_count, info))
stage2_list, stage2_count, fs_block_size,
info))
goto out_free_stage2_list;
/* Install stage 0 */
boot_init_eckd_cdl_stage0(&stage0_cdl);
@@ -966,7 +972,8 @@ out:
}
static int
install_svdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
install_svdump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
struct disk_info *info,
const struct stage2dump_parm_tail *stage2dump_parms)
{
size_t stage2_size;
@@ -975,13 +982,15 @@ install_svdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
if (boot_get_eckd_dump_stage2(&stage2, &stage2_size, stage2dump_parms))
return -1;
rc = install_dump_eckd_cdl(mfd, info, stage2, stage2_size, 0, 0);
rc = install_dump_eckd_cdl(mfd, fs_block_size,
info, stage2, stage2_size, 0, 0);
free(stage2);
return rc;
}
static int
install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
install_mvdump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
struct disk_info *info,
const struct stage2dump_parm_tail *stage2dump_parms,
const struct mvdump_parm_table *mv_parm_table)
{
@@ -993,7 +1002,8 @@ install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
if (boot_get_eckd_mvdump_stage2(&stage2, &stage2_size, stage2dump_parms,
mv_parm_table))
return -1;
rc = install_dump_eckd_cdl(mfd, info, stage2, stage2_size, 1,
rc = install_dump_eckd_cdl(mfd, fs_block_size, info, stage2,
stage2_size, 1,
stage2dump_parms->mvdump_force);
free(stage2);
return rc;
@@ -1002,7 +1012,8 @@ install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
int
install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
blocknum_t stage2_count, struct disk_info *info)
blocknum_t stage2_count, int fs_block_size,
struct disk_info *info)
{
struct boot_fba_stage1b *stage1b;
int stage1b_size, rc = -1;
@@ -1017,7 +1028,8 @@ install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
if (boot_init_fba_stage1b(stage1b, stage2_list, stage2_count))
goto out_free_stage1b;
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
stage1b_list, info);
stage1b_list, fs_block_size,
info);
if (*stage1b_count == 0)
goto out_free_stage1b;
rc = 0;
@@ -1028,7 +1040,8 @@ out:
}
static int
install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
install_svdump_fba(struct misc_fd *mfd, int fs_block_size,
struct disk_info *info,
const struct stage2dump_parm_tail *stage2dump_parms)
{
blocknum_t stage1b_count, stage2_count, blk;
@@ -1053,7 +1066,8 @@ install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
if (misc_seek(mfd->fd, blk * info->phy_block_size))
goto out_free_stage2;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info);
&stage2_list, fs_block_size,
info);
if (stage2_count == 0)
goto out_free_stage2;
/* Install stage 1b in front of stage 2 */
@@ -1061,7 +1075,7 @@ install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
if (misc_seek(mfd->fd, blk * info->phy_block_size))
goto out_free_stage2_list;
if (install_fba_stage1b(mfd, &stage1b_list, &stage1b_count,
stage2_list, stage2_count, info))
stage2_list, stage2_count, fs_block_size, info))
goto out_free_stage2_list;
/* Install stage 0/1 fill in dump partition parameter */
if (boot_init_fba_stage0(&stage0, stage1b_list, stage1b_count))
@@ -1109,7 +1123,8 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
{
struct stage2dump_parm_tail stage2dump_parms = {0};
struct misc_fd mfd = {0};
struct disk_info* info;
struct device_info *dev_info;
struct disk_info *info;
uint64_t part_size;
char *tempdev;
int rc;
@@ -1139,7 +1154,7 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
error_text("Could not install dump record on tape "
"device '%s'", device);
} else {
if (verbose) {
if (!misc_fsync(&mfd, device) && verbose) {
printf("Dump record successfully installed on "
"tape device '%s'.\n", device);
}
@@ -1149,32 +1164,33 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
}
close(mfd.fd);
/* This is a disk device */
rc = disk_get_info(device, target, &info);
rc = device_get_info(device, target, &dev_info);
if (rc) {
error_text("Could not get information for dump target "
"'%s'", device);
return rc;
}
info = &dev_info->base[0];
if (info->partnum == 0) {
error_reason("Dump target '%s' is not a disk partition",
device);
disk_free_info(info);
device_free_info(dev_info);
return -1;
}
if (verbose) {
printf("Target device information\n");
disk_print_info(info, target->source);
device_print_info(dev_info, target);
}
rc = misc_temp_dev(info->device, 1, &tempdev);
rc = misc_temp_dev(dev_info->device, 1, &tempdev);
if (rc) {
disk_free_info(info);
device_free_info(dev_info);
return -1;
}
if (misc_open_device(tempdev, &mfd, dry_run) == -1) {
error_text("Could not open temporary device node '%s'",
tempdev);
misc_free_temp_dev(tempdev);
disk_free_info(info);
device_free_info(dev_info);
return -1;
}
switch (info->type) {
@@ -1201,11 +1217,16 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
disk_get_type_name(info->type));
}
if (info->type == disk_type_eckd_ldl)
rc = install_svdump_eckd_ldl(&mfd, info, &stage2dump_parms);
rc = install_svdump_eckd_ldl(&mfd,
dev_info->fs_block_size,
info, &stage2dump_parms);
else if (info->type == disk_type_eckd_cdl)
rc = install_svdump_eckd_cdl(&mfd, info, &stage2dump_parms);
rc = install_svdump_eckd_cdl(&mfd,
dev_info->fs_block_size,
info, &stage2dump_parms);
else
rc = install_svdump_fba(&mfd, info, &stage2dump_parms);
rc = install_svdump_fba(&mfd, dev_info->fs_block_size,
info, &stage2dump_parms);
break;
case disk_type_scsi:
error_reason("%s: Unsupported disk type '%s' (try --dumptofs)",
@@ -1219,7 +1240,7 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
break;
}
misc_free_temp_dev(tempdev);
disk_free_info(info);
device_free_info(dev_info);
if (fsync(mfd.fd))
error_text("Could not sync device file '%s'", device);
if (close(mfd.fd))
@@ -1232,8 +1253,9 @@ int
install_mvdump(char* const device[], struct job_target_data* target, int count,
uint64_t mem, uint8_t force)
{
struct disk_info *info[MAX_DUMP_VOLUMES] = {0};
struct device_info *dev_info[MAX_DUMP_VOLUMES] = {0};
struct stage2dump_parm_tail stage2dump_parms = {0};
struct disk_info *info[MAX_DUMP_VOLUMES] = {0};
struct mvdump_parm_table mvdump_parms;
uint64_t total_size = 0;
struct timeval time;
@@ -1267,12 +1289,13 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
}
close(mfd.fd);
/* This is a disk device */
rc = disk_get_info(device[i], target, &info[i]);
rc = device_get_info(device[i], target, &dev_info[i]);
if (rc) {
error_text("Could not get information for dump target "
"'%s'", device[i]);
goto out;
}
info[i] = &dev_info[i]->base[0];
if (info[i]->partnum == 0) {
error_reason("Dump target '%s' is not a disk partition",
device[i]);
@@ -1321,7 +1344,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
if (verbose) {
for (i = 0; i < count; i++) {
printf("Multi-volume dump target %d:\n", i + 1);
disk_print_info(info[i], target->source);
device_print_info(dev_info[i], target);
printf("-------------------------------------------\n");
}
}
@@ -1343,7 +1366,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
for (i = 0; i < count; i++) {
struct misc_fd mfd = {0};
rc = misc_temp_dev(info[i]->device, 1, &tempdev);
rc = misc_temp_dev(dev_info[i]->device, 1, &tempdev);
if (rc) {
rc = -1;
goto out;
@@ -1358,7 +1381,9 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
if (verbose)
printf("Installing dump record on target partition "
"'%s'\n", device[i]);
rc = install_mvdump_eckd_cdl(&mfd, info[i], &stage2dump_parms, &mvdump_parms);
rc = install_mvdump_eckd_cdl(&mfd, dev_info[i]->fs_block_size,
info[i], &stage2dump_parms,
&mvdump_parms);
misc_free_temp_dev(tempdev);
if (fsync(mfd.fd))
@@ -1372,6 +1397,6 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
out:
for (i = 0; i < count; i++)
if (info[i] != NULL)
disk_free_info(info[i]);
device_free_info(dev_info[i]);
return rc;
}

View File

@@ -131,6 +131,16 @@ int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate)
return mfd->fd;
}
int misc_fsync(struct misc_fd *mfd, const char *filename)
{
if (fsync(mfd->fd)) {
error_reason(strerror(errno));
error_text("Could not sync file '%s'", filename);
return -1;
}
return 0;
}
/* Read COUNT bytes of data from file identified by file descriptor FD to
* memory at location BUFFER. Return 0 when all bytes were successfully read,
* non-zero otherwise. */
@@ -434,7 +444,7 @@ misc_write(int fd, const void* data, size_t count)
}
/**
* Instead of writing, upadte only current position in the file
* Instead of writing, update only current position in the file
*/
static int misc_simulate_write(int fd, size_t count)
{

View File

@@ -1200,7 +1200,7 @@ scan_check_target_data(char* keyword[], int* line)
return -1;
case target_type_scsi:
case target_type_fba:
if ((keyword[(int) scan_keyword_targetgeometry] == 0))
if (keyword[(int) scan_keyword_targetgeometry] == 0)
break;
if (line != NULL)
error_reason("Line %d: keyword "

View File

@@ -174,7 +174,8 @@ main(int argc, char* argv[])
/* Do it */
switch (job->id) {
case job_dump_partition:
rc = disk_get_ext_type(job->data.dump.device, &ext_type);
rc = disk_get_ext_type(job->data.dump.device, &ext_type,
0 /* disk index */);
if (rc)
break;
job_dump_check_set_ngdump(job, &ext_type);
@@ -210,16 +211,16 @@ main(int argc, char* argv[])
case job_menu:
rc = prepare_bootloader(job, &bis);
if (rc) {
free_bootloader(&bis);
free_bootloader(&bis, job);
break;
}
rc = install_bootloader(job, &bis);
if (rc) {
free_bootloader(&bis);
free_bootloader(&bis, job);
break;
}
rc = post_install_bootloader(job, &bis);
free_bootloader(&bis);
free_bootloader(&bis, job);
break;
case job_ipl_tape:
rc = install_tapeloader(job->data.ipl_tape.device,
@@ -244,10 +245,6 @@ main(int argc, char* argv[])
}
switch (rc) {
case 0: /* Operation completed successfully */
if (verbose)
printf("Syncing disks...\n");
if (!dry_run)
sync();
printf("Done.\n");
break;
case -2: /* Operation canceled by user */
@@ -306,7 +303,8 @@ struct component_footer component_footers[NR_PROGRAM_COMPONENTS] = {
},
[COMPONENT_ID_ENVBLK] = {
.type = COMPONENT_TYPE_LOAD,
.desc = "environment blk"
.desc = "environment blk",
.fs_block_aligned = 1
},
[COMPONENT_ID_SEGMENT_FILE] = {
.type = COMPONENT_TYPE_EXECUTE,

View File

@@ -1045,6 +1045,8 @@ static int get_table(dev_t dev, struct util_list **table)
&start, &length, &type, &args) < 4) {
ERR("Unrecognized device-mapper table format for device '%s'\n",
devname);
free(type);
free(args);
goto out;
}
tops = find_target_ops(type);
@@ -1052,6 +1054,9 @@ static int get_table(dev_t dev, struct util_list **table)
ERR("Unsupported setup: Unsupported device-mapper "
"target type '%s' for device '%s'\n",
type, devname);
free(type);
free(args);
goto out;
}
data = tops->get_target_data(devname, args);
free(type);

View File

@@ -3292,8 +3292,9 @@ int kms_configure(const kms_handle_t handle,
opts.cert_digest,
#ifdef EKMF_SUPPORTS_RSA_PSS_CERTIFICATES
opts.cert_rsa_pss,
#endif
#else
false);
#endif
else
rc = _error_gen_csr_sscert_opts(ph, &opts);
if (rc != 0)
@@ -3937,7 +3938,7 @@ static int _restrict_key(struct plugin_handle *ph, unsigned char *key_blob,
return rc;
}
rc = check_aes_cipher_key(key_blob, key_blob_length);
rc = check_aes_cipher_key(key_blob, key_blob_length, false);
if (rc != 0) {
warnx("The secure key retrieved from EKMF Web might not be "
"secure");

View File

@@ -121,6 +121,12 @@ typedef struct {
#define CKR_OK 0x00000000
#define XCP_BLOB_EXTRACTABLE 0x00000001
#define XCP_BLOB_DECRYPT 0x00000400
#define XCP_BLOB_ENCRYPT 0x00000800
#define XCP_BLOB_WRAP_W_TRUSTED 0x00040000
#define XCP_BLOB_PROTKEY_EXTRACTABLE 0x00200000
typedef int (*m_init_t) (void);
typedef int (*m_add_module_t) (XCP_Module_t module, target_t *target);
typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target);

View File

@@ -1983,6 +1983,9 @@ out:
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL)
* @param[in] exportable if true the key shall be exportable
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
* trusted KEK only
* @param[in] pkey_fd the file descriptor of /dev/pkey
*
* @returns 0 for success or a negative errno in case of an error
@@ -1993,7 +1996,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type,
const char *key_type, bool gen_passphrase,
const char *passphrase_file, int pkey_fd)
const char *passphrase_file, bool exportable,
bool wrap_with_trusted, int pkey_fd)
{
struct key_filenames file_names = { 0 };
struct properties *key_props = NULL;
@@ -2043,6 +2047,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
file_names.skey_filename,
keybits, xts, key_type,
(const char **)apqn_list,
exportable, wrap_with_trusted,
keystore->verbose);
else
rc = generate_secure_key_clear(pkey_fd,
@@ -2050,6 +2055,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
keybits, xts, clear_key_file,
key_type,
(const char **)apqn_list,
exportable, wrap_with_trusted,
keystore->verbose);
if (rc != 0)
goto out_free_props;
@@ -2286,6 +2292,7 @@ out_free_key_filenames:
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL)
* @param[in] exportable if true the key shall be exportable
* @param[in] lib the external library struct
*
* @returns 0 for success or a negative errno in case of an error
@@ -2295,7 +2302,8 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase,
const char *passphrase_file, struct ext_lib *lib)
const char *passphrase_file, bool exportable,
struct ext_lib *lib)
{
struct key_filenames file_names = { 0 };
struct properties *key_props = NULL;
@@ -2375,17 +2383,22 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
goto out_free_props;
}
rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
keystore->verbose);
if (rc != 0) {
warnx("Failed to export-restrict the imported secure "
"key: %s", strerror(-rc));
if (!selected)
print_msg_for_cca_envvars("secure AES key");
goto out_free_props;
if (!exportable) {
rc = restrict_key_export(lib->cca, secure_key,
secure_key_size,
keystore->verbose);
if (rc != 0) {
warnx("Failed to export-restrict the imported "
"secure key: %s", strerror(-rc));
if (!selected)
print_msg_for_cca_envvars("secure AES "
"key");
goto out_free_props;
}
}
rc = check_aes_cipher_key(secure_key, secure_key_size);
rc = check_aes_cipher_key(secure_key, secure_key_size,
exportable);
if (rc != 0) {
warnx("The secure key to import might not be secure");
printf("%s: Do you want to import it anyway [y/N]? ",
@@ -2455,6 +2468,7 @@ out_free_key_filenames:
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL)
* @param[in] exportable if true the key shall be exportable
* @param[in] lib the external library struct
*
* @returns 0 for success or a negative errno in case of an error
@@ -2464,7 +2478,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file,
struct ext_lib *lib)
bool exportable, struct ext_lib *lib)
{
size_t secure_key_size;
u8 *secure_key;
@@ -2480,7 +2494,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
rc = keystore_import(keystore, secure_key, secure_key_size, name,
description, volumes, apqns, noapqncheck,
sector_size, volume_type, gen_passphrase,
passphrase_file, lib);
passphrase_file, exportable, lib);
if (secure_key != NULL)
free(secure_key);
@@ -4797,6 +4811,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
* @param[in] key_type the type of the key to convert it to
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
* existence and type.
* @param[in] exportable if true the key shall be exportable
* @param[in] pkey_fd the file descriptor of /dev/pkey
* @param[in] lib the external library struct
*
@@ -4804,7 +4819,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
*/
int keystore_convert_key(struct keystore *keystore, const char *name,
const char *key_type, bool noapqncheck, bool quiet,
int pkey_fd, struct ext_lib *lib)
bool exportable, int pkey_fd, struct ext_lib *lib)
{
struct key_filenames file_names = { 0 };
u8 output_key[2 * MAX_SECURE_KEY_SIZE];
@@ -4941,14 +4956,16 @@ int keystore_convert_key(struct keystore *keystore, const char *name,
goto out;
}
rc = restrict_key_export(lib->cca, output_key, output_key_size,
keystore->verbose);
if (rc != 0) {
warnx("Export restricting the converted secure key '%s' has "
"failed", name);
if (!selected)
print_msg_for_cca_envvars("secure AES key");
goto out;
if (!exportable) {
rc = restrict_key_export(lib->cca, output_key, output_key_size,
keystore->verbose);
if (rc != 0) {
warnx("Export restricting the converted secure key "
"'%s' has failed", name);
if (!selected)
print_msg_for_cca_envvars("secure AES key");
goto out;
}
}
rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true);

View File

@@ -55,7 +55,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type,
const char *key_type, bool gen_passphrase,
const char *passphrase_file, int pkey_fd);
const char *passphrase_file, bool exportable,
bool wrap_with_trusted, int pkey_fd);
int keystore_generate_key_kms(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
@@ -70,14 +71,15 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase,
const char *passphrase_file, struct ext_lib *lib);
const char *passphrase_file, bool exportable,
struct ext_lib *lib);
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file,
struct ext_lib *lib);
bool exportable, struct ext_lib *lib);
int keystore_change_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
@@ -126,7 +128,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
int keystore_convert_key(struct keystore *keystore, const char *name,
const char *key_type, bool noapqncheck, bool quiet,
int pkey_fd, struct ext_lib *lib);
bool exportable, int pkey_fd, struct ext_lib *lib);
int keystore_kms_keys_set_property(struct keystore *keystore,
const char *key_type,

View File

@@ -745,7 +745,7 @@ static int remove_directory_recursively(const char *directory)
if (rmdir(directory) != 0) {
rc = -errno;
warnx("Failed to remove '%s': %s", filename, strerror(-rc));
warnx("Failed to remove '%s': %s", directory, strerror(-rc));
goto out;
}
@@ -2448,7 +2448,7 @@ int set_kms_key_properties(struct kms_info *kms_info,
ADD_KMS_PROPS(kms_props, num_kms_props,
KMS_KEY_PROP_SECTOR_SIZE, sector_size);
if (passphrase_file != NULL) {
/* *passphrase_file is NULL to remove the propoerty */
/* *passphrase_file is NULL to remove the property */
if (*passphrase_file != NULL) {
passphrase =
read_passphrase_as_base64(*passphrase_file,
@@ -2797,7 +2797,7 @@ static int _process_kms_keys_cb(const char *key_id, const char *key_label,
* @param[in] name_filter the name filter. Can contain wild cards.
* NULL means no name filter.
* @param[in] volume_filter the volume filter. Can contain wild cards, and
* mutliple volume filters separated by commas.
* multiple volume filters separated by commas.
* If the filter does not contain the ':dm-name'
* part, then the volumes are matched without the
* dm-name part. If the filter contains the
@@ -2989,7 +2989,7 @@ static int _list_kms_keys_cb(const char *UNUSED(key1_id),
* @param[in] name_filter the name filter. Can contain wild cards.
* NULL means no name filter.
* @param[in] volume_filter the volume filter. Can contain wild cards, and
* mutliple volume filters separated by commas.
* multiple volume filters separated by commas.
* If the filter does not contain the ':dm-name'
* part, then the volumes are matched without the
* dm-name part. If the filter contains the

View File

@@ -313,6 +313,35 @@ static enum pkey_key_size keybits_to_keysize(u32 keybits)
}
}
static u32 flags_for_pkey_type(enum pkey_key_type type, bool exportable,
bool wrap_with_trusted)
{
u32 flags = 0;
if (!exportable)
return 0;
switch (type) {
case PKEY_TYPE_CCA_CIPHER:
flags = PKEY_KEYGEN_XPRT_SYM | PKEY_KEYGEN_XPRT_UASY |
PKEY_KEYGEN_XPRT_AASY | PKEY_KEYGEN_XPRT_RAW |
PKEY_KEYGEN_XPRT_CPAC | PKEY_KEYGEN_XPRT_DES |
PKEY_KEYGEN_XPRT_AES | PKEY_KEYGEN_XPRT_RSA;
break;
case PKEY_TYPE_EP11:
case PKEY_TYPE_EP11_AES:
flags = XCP_BLOB_EXTRACTABLE | XCP_BLOB_DECRYPT |
XCP_BLOB_ENCRYPT | XCP_BLOB_PROTKEY_EXTRACTABLE;
if (wrap_with_trusted)
flags |= XCP_BLOB_WRAP_W_TRUSTED;
break;
default:
break;
}
return flags;
}
/*
* Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure
* key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported
@@ -887,6 +916,16 @@ static size_t key_size_for_type(enum pkey_key_type type)
}
}
static void print_error_for_ep11_exportable(void)
{
util_print_indented("Generating an exportable key of type "
KEY_TYPE_EP11_AES " requires that access control "
"point (ACP) 'XCP_CPB_ALLOW_COMBINED_EXTRACT' is "
"'ON' on all used APQNs. This access control point "
"is only supported on newer EP11 firmware levels.",
0);
}
/**
* Generate a secure key by random
*
@@ -897,13 +936,17 @@ static size_t key_size_for_type(enum pkey_key_type type)
* @param[in] key_type the type of the key
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
* or NULL for AUTOSELECT
* @param[in] exportable if true the key shall be exportable
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
* trusted KEK only
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
*/
int generate_secure_key_random(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, const char *key_type,
const char **apqns, bool verbose)
const char **apqns, bool exportable,
bool wrap_with_trusted, bool verbose)
{
struct pkey_genseck2 genseck2;
size_t secure_key_size, size;
@@ -960,6 +1003,9 @@ retry:
genseck2.key = secure_key;
genseck2.keylen = size;
genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable,
wrap_with_trusted);
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) {
/*
@@ -979,6 +1025,9 @@ retry:
}
if (rc != 0) {
warnx("Failed to generate a secure key: %s", strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out;
}
if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) {
@@ -1016,10 +1065,17 @@ retry:
genseck2.key = secure_key + size;
genseck2.keylen = size;
genseck2.keygenflags = flags_for_pkey_type(genseck2.type,
exportable,
wrap_with_trusted);
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
if (rc != 0) {
warnx("Failed to generate a secure key: %s",
strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out;
}
}
@@ -1048,6 +1104,9 @@ out:
* @param[in] key_type the type of the key
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
* or NULL for AUTOSELECT
* @param[in] exportable if true the key shall be exportable
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
* trusted KEK only
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
@@ -1055,7 +1114,8 @@ out:
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts,
const char *clearkeyfile, const char *key_type,
const char **apqns, bool verbose)
const char **apqns, bool exportable,
bool wrap_with_trusted, bool verbose)
{
struct pkey_clr2seck2 clr2seck2;
size_t secure_key_size;
@@ -1123,6 +1183,9 @@ retry:
clr2seck2.key = secure_key;
clr2seck2.keylen = size;
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable,
wrap_with_trusted);
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) {
/*
@@ -1142,6 +1205,9 @@ retry:
}
if (rc != 0) {
warnx("Failed to generate a secure key: %s", strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out;
}
if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) {
@@ -1182,10 +1248,17 @@ retry:
clr2seck2.key = secure_key + size;
clr2seck2.keylen = size;
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type,
exportable,
wrap_with_trusted);
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
if (rc != 0) {
warnx("Failed to generate a secure key: %s",
strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out;
}
}
@@ -2044,10 +2117,11 @@ enum card_type get_card_type_for_keytype(const char *key_type)
*
* @param[in] key the secure key token
* @param[in] key_size the size of the secure key
* @param[in] exportable if true the key shall be exportable
*
* @returns 0 on success, a negative errno in case of an error
*/
int check_aes_cipher_key(const u8 *key, size_t key_size)
int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable)
{
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
bool mismatch = false;
@@ -2072,22 +2146,22 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
mismatch = true;
}
if (cipherkey->kmf1 & 0x8000) {
if (!exportable && (cipherkey->kmf1 & 0x8000)) {
printf("WARNING: The secure key can be exported using a "
"symmetric key\n");
mismatch = true;
}
if (cipherkey->kmf1 & 0x4000) {
if (!exportable && (cipherkey->kmf1 & 0x4000)) {
printf("WARNING: The secure key can be exported using an "
"unauthenticated asymmetric key\n");
mismatch = true;
}
if (cipherkey->kmf1 & 0x2000) {
if (!exportable && (cipherkey->kmf1 & 0x2000)) {
printf("WARNING: The secure key can be exported using an "
"authenticated asymmetric key\n");
mismatch = true;
}
if (cipherkey->kmf1 & 0x1000) {
if (!exportable && (cipherkey->kmf1 & 0x1000)) {
printf("WARNING: The secure key can be exported using a RAW "
"key\n");
mismatch = true;
@@ -2097,17 +2171,17 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
"CPACF protected key\n");
mismatch = true;
}
if ((cipherkey->kmf1 & 0x0080) == 0) {
if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) {
printf("WARNING: The secure key can be exported using a DES "
"key\n");
mismatch = true;
}
if ((cipherkey->kmf1 & 0x0040) == 0) {
if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) {
printf("WARNING: The secure key can be exported using an AES "
"key\n");
mismatch = true;
}
if ((cipherkey->kmf1 & 0x0008) == 0) {
if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) {
printf("WARNING: The secure key can be exported using an RSA "
"key\n");
mismatch = true;
@@ -2161,7 +2235,7 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
"value\n");
mismatch = true;
}
if ((cipherkey->kmf3 & 0x00FF) == 0x0012) {
if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) {
printf("WARNING: The secure key was converted from a CCA "
"key-token that had no export control attributes\n");
mismatch = true;

View File

@@ -330,12 +330,14 @@ int open_pkey_device(bool verbose);
int generate_secure_key_random(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, const char *key_type,
const char **apqns, bool verbose);
const char **apqns, bool exportable,
bool wrap_with_trusted, bool verbose);
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts,
const char *clearkeyfile, const char *key_type,
const char **apqns, bool verbose);
const char **apqns, bool exportable,
bool wrap_with_trusted, bool verbose);
u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
bool verbose);
@@ -373,7 +375,7 @@ bool is_secure_key_type(const char *key_type);
int get_min_card_level_for_keytype(const char *key_type);
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type);
enum card_type get_card_type_for_keytype(const char *key_type);
int check_aes_cipher_key(const u8 *key, size_t key_size);
int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable);
enum reencipher_method {
REENCIPHER_OLD_TO_CURRENT = 1,

View File

@@ -674,7 +674,7 @@ int pvsecrets_import(struct keystore *keystore, int uv_fd,
rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token,
sizeof(build_blob_data.token), name, description,
volumes, NULL, false, sector_size, volume_type,
gen_passphrase, passphrase_file, NULL);
gen_passphrase, passphrase_file, false, NULL);
return rc;
}

View File

@@ -100,6 +100,8 @@ key repository.
.IR clear\-key\-file ]
.RB [ \-\-key\-type | \-K
.IR type ]
.RB [ \-\-exportable ]
.RB [ \-\-wrap\-with\-trusted ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -129,6 +131,8 @@ key repository.
.RB [ \-\-gen\-dummy\-passphrase ]
.RB [ \-\-set\-dummy\-passphrase
.IR passphrase\-file ]
.RB [ \-\-exportable ]
.RB [ \-\-wrap\-with\-trusted ]
.RB [ KMS-plugin\ specific\ options ]
.RB [ \-\-verbose | \-V ]
.PP
@@ -183,6 +187,29 @@ Secure keys of type \fBCCA\-AESCIPHER\fP require an IBM cryptographic
adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C.
Secure keys of type \fBEP11\-AES\fP require an IBM cryptographic
adapter in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P.
.PP
By default, secure keys of type \fBCCA\-AESCIPHER\fP and \fBEP11\-AES\fP are
export restricted when generated by zkey. Keys of type \fBCCA\-AESDATA\fP can
not be export restricted. Export restricted means that the secure keys can not
be wrapped by a key encrypting key (KEK), and thus can not escape the
protection of the IBM cryptographic adapter (HSM). If exportability is required
for a secure key of type \fBCCA\-AESCIPHER\fP or \fBEP11\-AES\fP, specify
option \fB\-\-exportable\fP. This makes the generated secure keys exportable.
Additionally, for keys of type \fBEP11\-AES\fP option
\fB\-\-wrap\-with\-trusted\fP can be specified to allow wrapping only with a
trusted KEK. When the secure key repository is bound to a key management system
plugin (KMS plugin), options \fB\-\-exportable\fP and
\fB\-\-wrap\-with\-trusted\fP are not allowed, except the \fB\-\-local\fP option
is specified.
.PP
.B Note:
For keys of type \fBEP11\-AES\fP to be exportable, the access control point
(ACP) \fBXCP_CPB_ALLOW_COMBINED_EXTRACT\fP must be \fBON\fP on all APQNs used.
This access control point is only supported on newer EP11 firmware levels. If
the access control point is \fBOFF\fP or not supported by the EP11 firmware,
key generation fails with a generic error (Input/output error). The \fBzkey\fP
tool prints an appropriate error message in that case.
.
.SS "Validating secure AES keys"
.
@@ -391,6 +418,7 @@ EP11\-AES to be installed. For the supported environments and downloads, see:
.RB [ \-\-gen\-dummy\-passphrase ]
.RB [ \-\-set\-dummy\-passphrase
.IR passphrase\-file ]
.RB [ \-\-exportable ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -416,6 +444,13 @@ The \fBimport\fP command requires the CCA host library (libcsulcca.so)
to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
For the supported environments and downloads, see:
\fIhttp://www.ibm.com/security/cryptocards\fP
.PP
By default, secure keys of type \fBCCA\-AESCIPHER\fP are changed to be export
restricted during the import operation. Export restricted means that the secure
keys can not be wrapped by a key encrypting key (KEK), and thus can not escape
the protection of the IBM cryptographic adapter (HSM). If exportability of an
imported key is required, specify option \fB\-\-exportable\fP. The import
operation will then not change the exportability of the secure key.
.
.SS "Export AES secure keys from the secure key repository"
.
@@ -798,6 +833,7 @@ same way as with \fBcryptsetup\fP.
.IR type
.RB [ \-\-no\-apqn\-check ]
.RB [ \-\-force | \-F ]
.RB [ \-\-exportable ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -844,6 +880,13 @@ The \fBconvert\fP command requires the CCA host library (libcsulcca.so)
to be installed. The required CCA IBM cryptographic adapter firmware version
is 6.3.27 or later. For the supported environments and downloads, see:
\fIhttp://www.ibm.com/security/cryptocards\fP
.PP
By default, secure keys are changed to be export restricted during the convert
operation. Export restricted means that the secure keys can not be wrapped by a
key encrypting key (KEK), and thus can not escape the protection of the IBM
cryptographic adapter (HSM). If exportability of a converted key is required,
specify option \fB\-\-exportable\fP. The convert operation will then not change
the exportability of the secure key.
.
.
.SH COMMANDS FOR KEY MANAGEMENT SYSTEM INTEGRATION

View File

@@ -83,6 +83,8 @@ static struct zkey_globals {
bool gen_passphrase;
char *passphrase_file;
bool remove_passphrase;
bool exportable;
bool wrap_with_trusted;
bool kms_bound;
bool run;
bool batch_mode;
@@ -173,6 +175,8 @@ static struct zkey_globals {
#define OPT_GEN_DUMMY_PASSPHRASE 265
#define OPT_SET_DUMMY_PASSPHRASE 266
#define OPT_REMOVE_DUMMY_PASSPHRASE 267
#define OPT_EXPORTABLE 268
#define OPT_WRAP_WITH_TRUSTED 269
/*
* Configuration of command line options
@@ -313,6 +317,28 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT,
.command = COMMAND_GENERATE,
},
{
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
.desc = "Generate a secure AES key that is not export "
"restricted. Exportable keys can be exported by means "
"of wrapping them with a key encryption key (KEK). By "
"default keys of type " KEY_TYPE_CCA_AESCIPHER " and "
KEY_TYPE_EP11_AES " are export restricted, and thus can "
"not be wrapped by a KEK. Keys of type "
KEY_TYPE_CCA_AESDATA " are always exportable, and can "
"not be export restricted.",
.command = COMMAND_GENERATE,
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = {"wrap-with-trusted", 0, NULL, OPT_WRAP_WITH_TRUSTED},
.desc = "Generate a secure AES key that can only be wrapped "
"with a trusted key encryption key (KEK). This option "
"is only valid for keys of type " KEY_TYPE_EP11_AES
" and only if option '--exportable' is also specified.",
.command = COMMAND_GENERATE,
.flags = UTIL_OPT_FLAG_NOSHORT,
},
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -496,6 +522,19 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT,
.command = COMMAND_IMPORT,
},
{
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
.desc = "Allow to import a secure AES key that is not export "
"restricted. Exportable keys can be exported by means "
"of wrapping them with a key encryption key (KEK). By "
"default keys of type " KEY_TYPE_CCA_AESCIPHER " and "
KEY_TYPE_EP11_AES " are export restricted, and thus can "
"not be wrapped by a KEK. Keys of type "
KEY_TYPE_CCA_AESDATA " are always exportable, and can "
"not be export restricted.",
.command = COMMAND_IMPORT,
.flags = UTIL_OPT_FLAG_NOSHORT,
},
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -969,6 +1008,14 @@ static struct util_opt opt_vec[] = {
"key",
.command = COMMAND_CONVERT,
},
{
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
.desc = "Convert to an secure AES key that is not export "
"restricted. Exportable keys can be exported by means "
"of wrapping them with a key encryption key (KEK).",
.command = COMMAND_CONVERT,
.flags = UTIL_OPT_FLAG_NOSHORT,
},
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -1891,7 +1938,8 @@ static int command_generate_clear(void)
rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg,
g.keybits, g.xts,
g.clearkeyfile, g.key_type,
NULL, g.verbose);
NULL, g.exportable, g.wrap_with_trusted,
g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1907,7 +1955,8 @@ static int command_generate_random(void)
rc = generate_secure_key_random(g.pkey_fd, g.pos_arg,
g.keybits, g.xts, g.key_type,
NULL, g.verbose);
NULL, g.exportable, g.wrap_with_trusted,
g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1941,13 +1990,24 @@ static int command_generate_repository(void)
}
if (g.clearkeyfile != NULL) {
warnx("Option '----clearkey|-c' is not valid for "
warnx("Option '--clearkey|-c' is not valid for "
"generating a key in a KMS-bound repository, "
"unless option '--local|-L' is also specified");
util_prg_print_parse_error();
return EXIT_FAILURE;
}
if (g.exportable) {
warnx("Option '--exportable' is not supported for "
"generating a key in a KMS-bound repository");
return EXIT_FAILURE;
}
if (g.wrap_with_trusted) {
warnx("Option '--wrap-with-trusted' is not supported "
"for generating a key in a KMS-bound repository");
return EXIT_FAILURE;
}
rc = perform_kms_login(&g.kms_info, g.verbose);
if (rc != 0)
rc = EXIT_FAILURE;
@@ -1966,11 +2026,25 @@ static int command_generate_repository(void)
if (g.key_type == NULL)
g.key_type = KEY_TYPE_CCA_AESDATA;
if (g.wrap_with_trusted) {
if (!g.exportable) {
warnx("Option '--wrap-with-trusted' is only valid "
"together with the '--exportable' option");
return EXIT_FAILURE;
}
if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) {
warnx("Option '--wrap-with-trusted' is only valid for "
"keys of type '%s'", KEY_TYPE_EP11_AES);
return EXIT_FAILURE;
}
}
rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes,
g.apqns, g.noapqncheck, g.sector_size,
g.keybits, g.xts, g.clearkeyfile,
g.volume_type, g.key_type, g.gen_passphrase,
g.passphrase_file, g.pkey_fd);
g.passphrase_file, g.exportable,
g.wrap_with_trusted, g.pkey_fd);
out:
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
@@ -2007,6 +2081,18 @@ static int command_generate(void)
"pvsecret import' instead", g.key_type);
return -EXIT_FAILURE;
}
if (g.wrap_with_trusted) {
if (!g.exportable) {
warnx("Option '--wrap-with-trusted' is only valid "
"together with the '--exportable' option");
return EXIT_FAILURE;
}
if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) {
warnx("Option '--wrap-with-trusted' is only valid for "
"keys of type '%s'", KEY_TYPE_EP11_AES);
return EXIT_FAILURE;
}
}
if (g.pos_arg != NULL) {
if (g.volumes != NULL) {
warnx("Option '--volumes|-l' is not valid for "
@@ -2463,7 +2549,7 @@ static int command_import(void)
rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes,
g.apqns, g.noapqncheck, g.sector_size,
g.pos_arg, g.volume_type, g.gen_passphrase,
g.passphrase_file, &g.lib);
g.passphrase_file, g.exportable, &g.lib);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -2816,14 +2902,17 @@ static int command_convert_file(void)
goto out;
}
rc = restrict_key_export(&g.cca, output_key, output_key_size,
g.verbose);
if (rc != 0) {
warnx("Export restricting the converted secure key has failed");
if (!selected)
print_msg_for_cca_envvars("secure AES key");
rc = EXIT_FAILURE;
goto out;
if (!g.exportable) {
rc = restrict_key_export(&g.cca, output_key, output_key_size,
g.verbose);
if (rc != 0) {
warnx("Export restricting the converted secure key "
"has failed");
if (!selected)
print_msg_for_cca_envvars("secure AES key");
rc = EXIT_FAILURE;
goto out;
}
}
pr_verbose("Secure key was converted successfully");
@@ -2853,7 +2942,7 @@ static int command_convert_repository(void)
}
rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck,
g.force, g.pkey_fd, &g.lib);
g.force, g.exportable, g.pkey_fd, &g.lib);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -3519,6 +3608,12 @@ int main(int argc, char *argv[])
case OPT_REMOVE_DUMMY_PASSPHRASE:
g.remove_passphrase = 1;
break;
case OPT_EXPORTABLE:
g.exportable = 1;
break;
case OPT_WRAP_WITH_TRUSTED:
g.wrap_with_trusted = 1;
break;
case 'A':
g.all = 1;
break;

View File

@@ -88,7 +88,7 @@ Without any options zmemtopo displays the tree view with nesting level 3.
$ zmemtopo
.nf
.ft CW
.ft CR
LPAR/LEVEL SIZE
LPAR003 8G
└LEVEL4_0 8G
@@ -126,7 +126,7 @@ Display table format, by default nesting level 3 is displayed.
$ zmemtopo -t
.nf
.ft CW
.ft CR
LEVEL 4 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3
LEVEL 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
NR LPAR SUM
@@ -150,7 +150,7 @@ Display reverse tree with nesting level 4.
$ zmemtopo -r -l 4
.nf
.ft CW
.ft CR
LEVEL/LPAR SIZE
LEVEL4_0 388G
 ├LPAR003 8G
@@ -178,7 +178,7 @@ Display reverse full tree. Entries that have no memory increments are visible.
$ zmemtopo -rf
.nf
.ft CW
.ft CR
.ft
LEVEL/LPAR SIZE
LEVEL4_0 388G
@@ -240,7 +240,7 @@ Display tree with entries which have no memory increments.
$ zmemtopo -f
.nf
.ft CW
.ft CR
LPAR/LEVEL SIZE
LPAR086 2G
├LEVEL4_0 -
@@ -287,7 +287,7 @@ Display json format.
$ zmemtopo --format=json
.nf
.ft CW
.ft CR
{
"meta": {
"api_level": "1",
@@ -349,7 +349,7 @@ Display csv format.
$ zmemtopo --format=csv
.nf
.ft CW
.ft CR
"report_tod","report_partition_nr","increment_size","partition_nr","partition_name","parent_level","parent_entry_idx","level","entry_idx","increment_count"
"0x0e082f818b066ce82000","74","1073741824","3","LPAR003","-","-","4","0","8"
"0x0e082f818b066ce82000","74","1073741824","3","LPAR003","4","0","3","0","2"

View File

@@ -10,8 +10,9 @@ include ../common.mak
all: zpwr
OBJECTS = zpwr.o
LDLIBS += -lm
zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a -lm
zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)

View File

@@ -154,7 +154,7 @@ CPU, storage or I/O resources to partitions.
.SH "EXAMPLES"
1. Display power readings in human readable format.
.nf
.ft CW
.ft CR
# zpwr
LPAR CPU: 140.00 W
LPAR Storage: 1.72 W
@@ -171,7 +171,7 @@ Update interval: 10.00 s
2. Perform two power measurements with a delay of 10 seconds and output the
data in json format.
.nf
.ft CW
.ft CR
# zpwr --format json --delay 10 --count 2
{
"meta": {
@@ -223,7 +223,7 @@ data in json format.
.sp 1
3. Display power readings in csv format and in stream mode.
.nf
.ft CW
.ft CR
# zpwr --format csv --stream
"iteration","time","time_epoch_sec","time_epoch_nsec","update_interval","cpu","storage","io","total","unassigned_resources","infrastructure"
"0","2025-01-08 07:17:05+0100","1736317025","592784684","10000000000","143000000","1708200","40000000","15550000000","7659162700","5337186700"