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
+5 -2
View File
@@ -2,7 +2,7 @@
exclude: \.(bin|crl|crt|key)$ exclude: \.(bin|crl|crt|key)$
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 rev: v6.0.0
hooks: hooks:
- id: check-merge-conflict - id: check-merge-conflict
- id: end-of-file-fixer - id: end-of-file-fixer
@@ -11,6 +11,9 @@ repos:
- id: check-executables-have-shebangs - id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable - id: check-shebang-scripts-are-executable
exclude_types: ['rust'] exclude_types: ['rust']
- id: check-yaml
- id: check-json
- id: check-toml
- repo: local - repo: local
hooks: hooks:
- id: git-clang-format - id: git-clang-format
@@ -24,7 +27,7 @@ repos:
minimum_pre_commit_version: "2.9.0" minimum_pre_commit_version: "2.9.0"
types_or: [c++, c] types_or: [c++, c]
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.2.1 rev: v2.4.1
hooks: hooks:
- id: codespell - id: codespell
exclude_types: ['rust'] exclude_types: ['rust']
+1
View File
@@ -110,6 +110,7 @@ List of all individuals having contributed content to s390-tools
- Raimund Schroeder - Raimund Schroeder
- Ralph Wuerthner - Ralph Wuerthner
- Rene Trumpp - Rene Trumpp
- Richie Buturla
- Rolf Schaefer - Rolf Schaefer
- Sa Liu - Sa Liu
- Sascha Silbe - Sascha Silbe
+32
View File
@@ -1,6 +1,38 @@
Release history for s390-tools (MIT version) 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)__ * __v2.38.0 (2025-06-25)__
For Linux kernel version: 6.14 / 6.15 For Linux kernel version: 6.14 / 6.15
+12
View File
@@ -414,6 +414,18 @@ the different tools are provided:
dumping). dumping).
Removing the inflate modules and function prototypes are the only major Removing the inflate modules and function prototypes are the only major
modifications made to the kernel version of zlib. modifications made to the kernel version of zlib.
For stand-alone NVMe and list-directed DASD dump, the following tools/packages
are required to populate dumper's initramfs:
- cat
- cut
- findmnt
- logsave
- makedumpfile
- mount
- poweroff
- sha256sum
- sync
- umount
* zgetdump * zgetdump
For building zgetdump you need OpenSSL version 1.1.0 or newer For building zgetdump you need OpenSSL version 1.1.0 or newer
+1 -1
View File
@@ -32,7 +32,7 @@ endif
# The variable "DISTRELEASE" should be overwritten in rpm spec files with: # The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}" # "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION := 2 VERSION := 2
RELEASE := 38 RELEASE := 39
PATCHLEVEL := 0 PATCHLEVEL := 0
DISTRELEASE := build-$(shell date +%Y%m%d) DISTRELEASE := build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE) S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
+1 -1
View File
@@ -22,7 +22,7 @@ ALL_CPPFLAGS += -DVERSION=$(VERSION)
all: check_dep cpacfstats cpacfstatsd all: check_dep cpacfstats cpacfstatsd
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \ cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
$(rootdir)/libutil/libutil.a $(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o
+1 -10
View File
@@ -30,6 +30,7 @@
#include <libudev.h> #include <libudev.h>
#include "cpacfstats.h" #include "cpacfstats.h"
#include "lib/libcpumf.h"
#include "../include/lib/zt_common.h" #include "../include/lib/zt_common.h"
/* correlation between counter and perf counter string */ /* correlation between counter and perf counter string */
@@ -123,16 +124,6 @@ static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
return NULL; return NULL;
} }
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
return ret;
}
static int perf_supported(void) static int perf_supported(void)
{ {
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK); return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
+9 -9
View File
@@ -39,7 +39,7 @@
static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */ static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
/* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold /* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold
* as payload data. Each SDBT is one PAGE (4096 bytes) and continas 512 eight * as payload data. Each SDBT is one PAGE (4096 bytes) and contains 512 eight
* byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT * byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT
* points to another SDBT and can not store payload. * points to another SDBT and can not store payload.
*/ */
@@ -139,7 +139,7 @@ static struct samples { /* Sample definition for all machines */
* The CPU Measurement facility has a first and second version number. * The CPU Measurement facility has a first and second version number.
* *
* The first version number governs basic counter set and the * The first version number governs basic counter set and the
* problem state counter set. Currently used are first verion numbers 1 and 3. * problem state counter set. Currently used are first version numbers 1 and 3.
* The counter numbers are identifical for version number 1 and 3, but * The counter numbers are identifical for version number 1 and 3, but
* have different purpose and description. * have different purpose and description.
* *
@@ -627,7 +627,7 @@ static struct counters cpumcf_z10_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1D_L3_LOCAL_WRITES", .name = "L1D_L3_LOCAL_WRITES",
.desc = "A directory write to the Level-1 D-Cache directory" .desc = "A directory write to the Level-1 D-Cache directory"
"\n\t\twhere the installtion cache line was source from" "\n\t\twhere the installation cache line was source from"
"\n\t\tthe Level-3 cache that is on the same book as the" "\n\t\tthe Level-3 cache that is on the same book as the"
"\n\t\tData cache (Local L2 cache)", "\n\t\tData cache (Local L2 cache)",
}, },
@@ -1916,7 +1916,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES", .name = "L1I_ONCHIP_L3_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache without intervention", "\n\t\tfrom an On-Chip Level-3 cache without intervention",
}, },
{ {
@@ -1924,7 +1924,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES", .name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom On-Chip memory", "\n\t\tfrom On-Chip memory",
}, },
{ {
@@ -1932,7 +1932,7 @@ static struct counters cpumcf_z14_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache with intervention", "\n\t\tfrom an On-Chip Level-3 cache with intervention",
}, },
{ {
@@ -2342,7 +2342,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES", .name = "L1I_ONCHIP_L3_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache without intervention", "\n\t\tfrom an On-Chip Level-3 cache without intervention",
}, },
{ {
@@ -2350,7 +2350,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES", .name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom On-Chip memory", "\n\t\tfrom On-Chip memory",
}, },
{ {
@@ -2358,7 +2358,7 @@ static struct counters cpumcf_z15_counters[] = {
.ctrset = CPUMF_CTRSET_EXTENDED, .ctrset = CPUMF_CTRSET_EXTENDED,
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
.desc = "A directory write to the Level-1 Instruction cache" .desc = "A directory write to the Level-1 Instruction cache"
"\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tdirectory where the returned cache line was sourced"
"\n\t\tfrom an On-Chip Level-3 cache with intervention", "\n\t\tfrom an On-Chip Level-3 cache with intervention",
}, },
{ {
+17 -1
View File
@@ -37,7 +37,7 @@
#include "lib/util_base.h" #include "lib/util_base.h"
#include "lib/util_path.h" #include "lib/util_path.h"
#include "lib/util_scandir.h" #include "lib/util_scandir.h"
#include "lib/util_libc.h" #include "lib/util_str.h"
#include "lib/util_file.h" #include "lib/util_file.h"
#include "lib/util_fmt.h" #include "lib/util_fmt.h"
#include "lib/libcpumf.h" #include "lib/libcpumf.h"
@@ -61,6 +61,7 @@ static bool hideundef;
static bool delta, firstread; static bool delta, firstread;
static int output_format = FMT_CSV; static int output_format = FMT_CSV;
static bool quote_all; static bool quote_all;
static char *ctrlist; /* Comma separated list of counter to extract */
static unsigned int max_possible_cpus; /* No of possible CPUs */ static unsigned int max_possible_cpus; /* No of possible CPUs */
static struct ctrname { /* List of defined counters */ static struct ctrname { /* List of defined counters */
@@ -151,6 +152,8 @@ static bool read_counternames(void)
return false; return false;
} }
for (i = 0; i < count && ctr >= 0; i++) { for (i = 0; i < count && ctr >= 0; i++) {
if (!ctr_in_list(namelist[i]->d_name, ctrlist))
continue;
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name); ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name);
@@ -779,6 +782,12 @@ static struct util_opt opt_vec[] = {
.argument = "FORMAT", .argument = "FORMAT",
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")" .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_HELP,
UTIL_OPT_VERSION, UTIL_OPT_VERSION,
UTIL_OPT_END UTIL_OPT_END
@@ -892,6 +901,12 @@ int main(int argc, char **argv)
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES); errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
output_format = fmt; output_format = fmt;
break; 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_counternames();
free(check); free(check);
free(ioctlbuffer); free(ioctlbuffer);
free(ctrlist);
return ch; return ch;
} }
+472 -23
View File
@@ -6,7 +6,6 @@
/* List available Processor Assist Instrumentation (PAI) counters. */ /* List available Processor Assist Instrumentation (PAI) counters. */
#include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
@@ -17,7 +16,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <linux/perf_event.h>
#include "lib/util_base.h" #include "lib/util_base.h"
#include "lib/util_file.h" #include "lib/util_file.h"
@@ -28,26 +32,68 @@
#include "lib/util_path.h" #include "lib/util_path.h"
#include "lib/util_prg.h" #include "lib/util_prg.h"
#include "lib/util_scandir.h" #include "lib/util_scandir.h"
#include "lib/util_str.h"
#include "lib/libcpumf.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[] = { static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS"), 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 }, .option = { "format", required_argument, NULL, OPT_FORMAT },
.argument = "FORMAT", .argument = "FORMAT",
.flags = UTIL_OPT_FLAG_NOSHORT, .flags = UTIL_OPT_FLAG_NOSHORT,
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")" .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' }, .option = { "numeric", no_argument, NULL, 'n' },
.desc = "Sort PAI counters by counter number" .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' }, .option = { "type", required_argument, NULL, 't' },
.argument = "TYPE", .argument = "TYPE",
.desc = "Type of PAI counters to show: crypto, nnpa" .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_HELP,
UTIL_OPT_VERSION, UTIL_OPT_VERSION,
UTIL_OPT_END UTIL_OPT_END
@@ -59,14 +105,24 @@ static const struct util_prg prg = {
{ {
.owner = "IBM Corp.", .owner = "IBM Corp.",
.pub_first = 2023, .pub_first = 2023,
.pub_last = 2023, .pub_last = 2025,
}, },
UTIL_PRG_COPYRIGHT_END UTIL_PRG_COPYRIGHT_END
} }
}; };
static bool allcpu; /* Show counter values per CPU */
static bool numsort; /* If true sort counter numerically */ static bool numsort; /* If true sort counter numerically */
static bool shortname; /* Use abbreviated counter names */
static bool delta, firstread; /* Display delta values */
static int output_format = -1; /* Generate style if >= 0 */ static int output_format = -1; /* Generate style if >= 0 */
static unsigned int max_cpus; /* # of CPUs to read counter values from */
static unsigned int max_fds; /* # of file descriptor to read counter values */
static unsigned long loops; /* # loops */
static unsigned long read_interval = DEFAULT_LOOP_INTERVAL;
static cpu_set_t cpu_online_mask;
static char *ctrformat = "%ld"; /* Default counter output format */
static char *ctrlist; /* Comma separated list of counter to extract */
#define PAI_PATH "/bus/event_source/devices/%s" #define PAI_PATH "/bus/event_source/devices/%s"
@@ -78,9 +134,18 @@ enum pai_types { /* Bit mask for supported PAI counters */
static int pai_types_show; static int pai_types_show;
struct pai_cpudata { /* Event data per CPU */
int fd; /* Event file descriptor */
int cpu; /* CPU number */
unsigned long value; /* Event value */
unsigned long prev_value; /* Previous value for deltas */
};
struct pai_ctrname { /* List of defined counters */ struct pai_ctrname { /* List of defined counters */
char *name; /* Counter name */ char *name; /* Counter name */
unsigned long nr; /* Counter number */ unsigned long nr; /* Counter number */
unsigned long total; /* Total count on all CPus */
struct pai_cpudata *data; /* Counter data per CPU */
}; };
struct pai_node { /* Head for PAI counter sets */ struct pai_node { /* Head for PAI counter sets */
@@ -113,6 +178,20 @@ static unsigned long pai_type_base(enum pai_types t)
return 0; return 0;
} }
/* Return character for this counter set. */
static char pai_type_char(enum pai_types t)
{
switch (t) {
case pai_type_crypto:
return 'C';
case pai_type_nnpa:
return 'N';
case pai_type_max:
break;
}
return 'U';
}
/* Test PAI counter name from command line option. */ /* Test PAI counter name from command line option. */
static const char *pai_type_name(enum pai_types t) static const char *pai_type_name(enum pai_types t)
{ {
@@ -154,16 +233,6 @@ static int pai_ctrcmp(const void *p1, const void *p2)
return l->nr > r->nr ? 1 : -1; return l->nr > r->nr ? 1 : -1;
} }
/* Convert string to upper case. */
static char *str2uc(const char *s)
{
char *uc = util_strdup(s), *old_uc = uc;
for (; *uc; ++uc)
*uc = toupper(*uc);
return old_uc;
}
/* Read counter names and assigned event number from sysfs file tree. /* Read counter names and assigned event number from sysfs file tree.
* Exit when sysfs directory can not be scanned. * Exit when sysfs directory can not be scanned.
*/ */
@@ -171,7 +240,7 @@ static void read_counternames(struct pai_node *node)
{ {
int i, more = 0, ctr = 0, count = 0; int i, more = 0, ctr = 0, count = 0;
struct dirent **namelist = NULL; struct dirent **namelist = NULL;
char *path, *ctrpath; char *path, *ctrpath, sname[128];
/* Read counter names and assigned event number. */ /* Read counter names and assigned event number. */
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name); path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
@@ -184,9 +253,18 @@ static void read_counternames(struct pai_node *node)
for (i = 0; i < count && ctr >= 0; i++) { for (i = 0; i < count && ctr >= 0; i++) {
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) { if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
snprintf(sname, sizeof(sname), "%c%ld",
pai_type_char(node->type), ctr - node->base);
if (!ctr_in_list(sname, ctrlist) &&
!ctr_in_list(namelist[i]->d_name, ctrlist)) {
/* Counter not listed in --counters option */
continue;
}
node->ctrlist[node->ctridx].data = NULL;
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name); node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
node->ctrlist[node->ctridx++].nr = ctr; node->ctrlist[node->ctridx++].nr = ctr;
more++; more++;
max_fds++;
} else { } else {
warnx("Cannot parse %s", ctrpath); warnx("Cannot parse %s", ctrpath);
} }
@@ -199,7 +277,7 @@ static void read_counternames(struct pai_node *node)
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp); qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
} }
static void show_format(enum util_fmt_t fmt) static void format_painode(enum util_fmt_t fmt)
{ {
struct pai_node *node; 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) { for (int i = 0; i < node->ctridx; ++i) {
util_fmt_obj_start(FMT_ROW, "counter"); util_fmt_obj_start(FMT_ROW, "counter");
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name); 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, "config", "%d", node->ctrlist[i].nr);
util_fmt_pair(FMT_DEFAULT, "number", "%d", util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
node->ctrlist[i].nr - node->base);
util_fmt_obj_end(); util_fmt_obj_end();
} }
util_fmt_obj_end(); /* Counters */ util_fmt_obj_end(); /* Counters */
@@ -226,14 +307,14 @@ static void show_format(enum util_fmt_t fmt)
util_fmt_exit(); util_fmt_exit();
} }
static void show_painode(void) static void list_painode(void)
{ {
struct pai_node *node; struct pai_node *node;
int indent = 0; int indent = 0;
int offset = 0; int offset = 0;
if (output_format != -1) { if (output_format != -1) {
show_format(output_format); format_painode(output_format);
return; return;
} }
@@ -264,8 +345,10 @@ static void free_painode(void)
util_list_iterate_safe(&pai_list, node, next) { util_list_iterate_safe(&pai_list, node, next) {
free(node->name_uc); free(node->name_uc);
for (int i = 0; i < node->ctridx; ++i) for (int i = 0; i < node->ctridx; ++i) {
free(node->ctrlist[i].name); free(node->ctrlist[i].name);
free(node->ctrlist[i].data);
}
free(node->ctrlist); free(node->ctrlist);
free(node); free(node);
} }
@@ -279,7 +362,8 @@ static void make_painode(enum pai_types t)
node->type = t; node->type = t;
node->sysfs_name = pai_type_sysfs(t); node->sysfs_name = pai_type_sysfs(t);
node->name = pai_type_name(t); node->name = pai_type_name(t);
node->name_uc = str2uc(node->name); node->name_uc = util_strdup(node->name);
util_str_toupper(node->name_uc);
node->filter_name = pai_type_filter(t); node->filter_name = pai_type_filter(t);
node->base = pai_type_base(t); node->base = pai_type_base(t);
@@ -308,6 +392,267 @@ static void sort_painode(void)
util_list_sort(&pai_list, painode_cmp, NULL); util_list_sort(&pai_list, painode_cmp, NULL);
} }
/* Read counter value. */
static unsigned long event_read(int fd)
{
unsigned long count;
int rc;
rc = read(fd, &count, sizeof(count));
if (rc != sizeof(count))
err(EXIT_FAILURE, "Failed to read counter value");
return count;
}
/* Write header. */
static void line_header(void)
{
struct pai_node *node;
static bool header;
bool comma = false;
if (header)
return; /* Printed already */
printf("Date,Time,CPU,"); /* Print counter name and number */
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
if (comma)
putchar(',');
if (shortname) {
printf("%c%ld", pai_type_char(node->type),
node->ctrlist[i].nr - node->base);
} else {
printf("%s(%ld)", node->ctrlist[i].name ?: node->name_uc,
node->ctrlist[i].nr - node->base);
}
comma = true;
}
}
putchar('\n');
header = true;
}
/* Print CPU specific counter values. */
static void line_cpu(char *header)
{
struct pai_cpudata *data;
struct pai_node *node;
bool comma;
char txt[16];
for (unsigned int h = 0; h < max_cpus; ++h) {
comma = false;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
data = &node->ctrlist[i].data[h];
if (!comma) {
snprintf(txt, sizeof(txt), "CPU%d,", data->cpu);
printf("%s,%s", header, txt);
} else {
putchar(',');
}
printf(ctrformat, data->value);
comma = true;
}
}
putchar('\n');
}
}
/* Write an output line. */
static void line_out(char *header)
{
struct pai_node *node;
bool comma;
line_header();
if (allcpu)
line_cpu(header);
/* Print total count of all CPUs */
printf("%s,%s,", header, delta && !firstread ? "Delta" : "Total");
comma = false;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
if (comma)
putchar(',');
printf(ctrformat, node->ctrlist[i].total);
comma = true;
}
}
putchar('\n');
}
/* Write a formatted line. */
static void format_line_out(time_t now, char *now_text)
{
static unsigned int called;
struct pai_node *node;
char cpuid[16];
if (!called) {
util_fmt_init(stdout, output_format, FMT_DEFAULT | FMT_HANDLEINT, 1);
util_fmt_obj_start(FMT_DEFAULT, NULL);
util_fmt_obj_start(FMT_LIST, "measurements");
}
util_list_iterate(&pai_list, node) {
if (!node->ctridx) /* Counter set not selected */
continue;
util_fmt_obj_start(FMT_DEFAULT, "entry");
util_fmt_pair(FMT_PERSIST, "iteration", "%d", called++);
util_fmt_pair(FMT_PERSIST, "time_epoch", "%d", now);
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "time", "%s", now_text);
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "valuetype",
(delta && !firstread) ? "delta" : "total");
util_fmt_obj_start(FMT_LIST, "counters");
for (int i = 0; i < node->ctridx; ++i) {
util_fmt_obj_start(FMT_ROW, "counter");
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
if (shortname)
util_fmt_pair(FMT_QUOTE, "shortname", "%c%d",
pai_type_char(node->type),
node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
util_fmt_pair(FMT_DEFAULT, "value", ctrformat, node->ctrlist[i].total);
if (allcpu) {
for (unsigned int j = 0; j < max_cpus; ++j) {
snprintf(cpuid, sizeof(cpuid), "cpu%d", j);
util_fmt_pair(FMT_DEFAULT, cpuid, ctrformat,
node->ctrlist[i].data[j].value);
}
}
util_fmt_obj_end();
}
util_fmt_obj_end(); /* Counters */
util_fmt_obj_end(); /* Entry */
}
}
/* Terminate formatted output. */
static void format_line_end(void)
{
util_fmt_obj_end(); /* Iteration */
util_fmt_obj_end(); /* Default */
util_fmt_exit();
}
/* Display counter values. */
static void show_values(void)
{
time_t now = time(NULL);
struct tm *now_tm;
char now_text[32];
now_tm = localtime(&now);
if (output_format != -1) {
strftime(now_text, sizeof(now_text), "%F %T%z", now_tm);
format_line_out(now, now_text);
} else {
strftime(now_text, sizeof(now_text), "%F,%T", now_tm);
line_out(now_text);
}
}
/* Read each counter value. */
static void read_painode(void)
{
struct pai_cpudata *data;
struct pai_node *node;
unsigned long value;
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
node->ctrlist[i].total = 0;
for (size_t j = 0; j < max_cpus; ++j) {
data = &node->ctrlist[i].data[j];
value = event_read(data->fd);
if (delta) {
data->value = value - data->prev_value;
data->prev_value = value;
} else {
data->value = value;
}
node->ctrlist[i].total += data->value;
}
}
}
}
static void wait_painode(void)
{
for (unsigned long i = 0; i < loops; ++i) {
read_painode();
show_values();
if (i + 1 < loops)
sleep(read_interval);
firstread = false;
}
format_line_end();
}
/* Install one event. */
static int event_add(int cpu, int idx, struct pai_node *node)
{
struct perf_event_attr attr;
int fd;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.config = node->ctrlist[idx].nr;
attr.type = node->pmu;
fd = perf_event_open(&attr, -1, cpu, -1, 0);
if (fd == -1)
err(EXIT_FAILURE, "Failed to open perf event: file descriptor not available");
return fd;
}
/* Increase number of file descriptors this process can open. */
static void event_fdlimit(void)
{
unsigned int needed = 3 + max_fds * max_cpus;
struct rlimit rlimit;
if (getrlimit(RLIMIT_NOFILE, &rlimit) == -1)
err(EXIT_FAILURE, "Failed to read RLIMIT_NOFILE");
if (needed > rlimit.rlim_cur)
rlimit.rlim_cur = needed;
if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
err(EXIT_FAILURE, "Failed to set RLIMIT_NOFILE");
}
/* Install all events and iterate over requested read operations. */
static void event_painode(void)
{
size_t pai_cpudata_sz = sizeof(struct pai_cpudata) * max_cpus;
struct pai_cpudata *data;
struct pai_node *node;
event_fdlimit();
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
node->ctrlist[i].data = util_malloc(pai_cpudata_sz);
data = node->ctrlist[i].data;
for (unsigned int j = 0; j < CPU_SETSIZE; ++j) {
if (CPU_ISSET(j, &cpu_online_mask)) {
data->cpu = j;
data->fd = event_add(j, i, node);
data->value = 0;
data->prev_value = 0;
++data;
}
}
}
}
wait_painode();
util_list_iterate(&pai_list, node) {
for (int i = 0; i < node->ctridx; ++i) {
for (unsigned int j = 0; j < max_cpus; ++j)
close(node->ctrlist[i].data[j].fd);
}
}
}
/* Check for hardware support and return false if not available. */ /* Check for hardware support and return false if not available. */
static bool have_support(enum pai_types t) static bool have_support(enum pai_types t)
{ {
@@ -344,9 +689,40 @@ static void check_type_name(const char *type)
errx(EXIT_FAILURE, "Invalid argument for -t %s", type); errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
} }
/*
* Get list of specified CPUs from command line. Check if these CPUs
* exist and are online. Ignore those CPUs which are not available and
* issue one warning when CPUs have been specified but are not online.
*/
static void get_cpulist(char *parm)
{
bool warned = false;
cpu_set_t cpulist;
int i, rc;
CPU_ZERO(&cpulist);
rc = libcpumf_cpuset(parm, &cpulist);
if (rc)
err(EXIT_FAILURE, "Cannot parse cpulist %s", parm);
for (i = 0; i < CPU_SETSIZE; ++i) {
if (CPU_ISSET(i, &cpulist) && !CPU_ISSET(i, &cpu_online_mask)) {
if (!warned) {
warnx("some CPU(s) are offline, ignored");
warned = true;
}
}
if (!CPU_ISSET(i, &cpulist) && CPU_ISSET(i, &cpu_online_mask))
CPU_CLR(i, &cpu_online_mask);
}
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
bool list_only = true;
enum util_fmt_t fmt; enum util_fmt_t fmt;
bool i_flag = false;
bool l_flag = false;
char *endchar;
int ch; int ch;
util_list_init(&pai_list, struct pai_node, node); util_list_init(&pai_list, struct pai_node, node);
@@ -358,6 +734,15 @@ int main(int argc, char **argv)
default: default:
util_opt_print_parse_error(ch, argv); util_opt_print_parse_error(ch, argv);
return EXIT_FAILURE; return EXIT_FAILURE;
case 'a':
allcpu = true;
list_only = false;
break;
case 'd':
delta = true;
firstread = true;
list_only = false;
break;
case 'h': case 'h':
util_prg_print_help(); util_prg_print_help();
util_opt_print_help(); util_opt_print_help();
@@ -365,21 +750,80 @@ int main(int argc, char **argv)
case 'v': case 'v':
util_prg_print_version(); util_prg_print_version();
return EXIT_SUCCESS; return EXIT_SUCCESS;
case 'i':
i_flag = true;
list_only = false;
errno = 0;
read_interval = strtoul(optarg, &endchar, 0);
if (errno || *endchar)
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
break;
case 'l':
l_flag = true;
list_only = false;
errno = 0;
loops = strtoul(optarg, &endchar, 0);
if (errno || *endchar)
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
break;
case 'n': case 'n':
numsort = true; numsort = true;
break; break;
case 's':
list_only = false;
shortname = true;
break;
case 't': case 't':
check_type_name(optarg); check_type_name(optarg);
break; break;
case 'x':
list_only = false;
ctrformat = "%lx";
break;
case 'X':
list_only = false;
ctrformat = "%#lx";
break;
case OPT_FORMAT: case OPT_FORMAT:
if (!util_fmt_name_to_type(optarg, &fmt)) if (!util_fmt_name_to_type(optarg, &fmt))
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES); errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
output_format = fmt; output_format = fmt;
break; break;
case 'c':
ctrlist = util_strdup(optarg);
util_str_rm_whitespace(optarg, ctrlist);
util_str_toupper(ctrlist);
break;
} }
} }
/* Nothing specified, show all PAI counters */ if (i_flag && !l_flag) {
util_prg_print_help();
util_opt_print_help();
return EXIT_FAILURE;
}
/*
* Read currently online CPUs and create a bit mask.
* This bitmap of online CPUs is used to check command line parameter
* for valid CPUs
* When any of the flags which set variable list_only to false have
* be specified, lets also show the counter value, not just list them.
*/
if (optind < argc) /* List of CPUs on command line */
list_only = false;
if (!list_only) { /* Show counter values */
ch = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpu_online_mask);
if (ch)
err(EXIT_FAILURE, "Cannot read file /sys/" S390_CPUS_ONLINE);
while (optind < argc)
get_cpulist(argv[optind++]);
max_cpus = CPU_COUNT(&cpu_online_mask);
if (!loops)
loops = 1;
}
/* Nothing specified, use all PAI counters */
if (!pai_types_show) if (!pai_types_show)
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa); pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
@@ -393,7 +837,12 @@ int main(int argc, char **argv)
} }
} }
sort_painode(); sort_painode();
show_painode(); ch = EXIT_SUCCESS;
if (!list_only)
event_painode();
else
list_painode();
free_painode(); free_painode();
free(ctrlist);
return ch; return ch;
} }
+4 -4
View File
@@ -17,9 +17,9 @@ chcpumf \- manage the CPU-measurement facilities support
.SH SYNOPSIS .SH SYNOPSIS
.B chcpumf .B chcpumf
.RB [ \-m | \-\-min .RB [ \-m | \-\-min
.IR num_sdb ] .IR NUM_SDB ]
.RB [ \-x | \-\-max .RB [ \-x | \-\-max
.IR num_sdb ] .IR NUM_SDB ]
.RB [ \-V | \-\-verbose ] .RB [ \-V | \-\-verbose ]
.br .br
.B chcpumf .B chcpumf
@@ -36,14 +36,14 @@ facilities for Linux on System z.
. .
.SH OPTIONS .SH OPTIONS
.TP .TP
.BR \-m ", " \-\-min " \fInum_sdb\fP" .BR \-m ", " \-\-min " \fINUM_SDB\fP"
Specifies the minimum sampling facility buffer size in sample-data-blocks Specifies the minimum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes. (SDB). A sample-data-block consumes about 4 kilobytes.
This is the initial buffer size when you start the sampling facility. This is the initial buffer size when you start the sampling facility.
. .
.TP .TP
.BR \-x ", " \-\-max " \fInum_sdb\fP" .BR \-x ", " \-\-max " \fINUM_SDB\fP"
Specifies the maximum sampling facility buffer size in sample-data-blocks Specifies the maximum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes. (SDB). A sample-data-block consumes about 4 kilobytes.
+20 -13
View File
@@ -16,14 +16,16 @@ lshwc \- extract CPU Measurement Facilities counter sets
\*c \*c
.RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ] .RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ]
.RB [ \-l .RB [ \-l
.IR count ] .IR COUNT ]
.RB [ \-i .RB [ \-i
.IR interval ] .IR INTERVAL ]
.RB [ \-t .RB [ \-t
.IR time ] .IR TIME ]
.RB [ \-f .RB [ \-f
.IR format ] .IR FORMAT ]
\fR[\fIcpulist\fR][:\fIsets\fR]\fP .RB [ \-c
.IR LIST ]
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
.br .br
\*c \*c
.BR \-h | \-\-help .BR \-h | \-\-help
@@ -62,13 +64,13 @@ Without this flag the total value of each counter is displayed.
See Examples. See Examples.
. .
.TP .TP
.BR \-i ", " \-\-interval \fI\ seconds\fP .BR \-i ", " \-\-interval \fI\ SECONDS\fP
Specifies a time interval, in seconds, Specifies a time interval, in seconds,
that the command waits between read operations. that the command waits between read operations.
The default is 60 seconds. The default is 60 seconds.
. .
.TP .TP
.BR \-l ", " \-\-loop \fI\ count\fP .BR \-l ", " \-\-loop \fI\ COUNT\fP
Performs the specified number of read operations. Performs the specified number of read operations.
. .
.TP .TP
@@ -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. Apply quoting to every output element, regardless of content or format.
. .
.TP .TP
.BR \-f ", " \-\-format \fI\ format\fP .BR \-f ", " \-\-format \fI\ FORMAT\fP
Retrieve output in one of the following formats: Retrieve output in one of the following formats:
JSON, CSV, JSON-SEQ or PAIRS. JSON, CSV, JSON-SEQ or PAIRS.
If no format is specified, the output defaults to CSV. If no format is specified, the output defaults to CSV.
@@ -173,7 +175,12 @@ and \fBvalue\fP.
.RE .RE
. .
.TP .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. A comma-separated list of CPUs.
Each CPU can optionally be followed by characters that specify the counter set. Each CPU can optionally be followed by characters that specify the counter set.
See below for details. See below for details.
@@ -206,7 +213,7 @@ Two read operations are performed and a summary line is printed for each
read operation. read operation.
.sp 1 .sp 1
.nf .nf
.ft CW .ft CR
# lshwc -l2 0-1:BP # lshwc -l2 0-1:BP
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4), Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33) L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
@@ -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. This example shows the counter values of the problem state counter set per CPU.
CPU 0 and CPU 1 are selected. CPU 0 and CPU 1 are selected.
.nf .nf
.ft CW .ft CR
.sp 1 .sp 1
# lshwc -l3 -a 0-1:P # lshwc -l3 -a 0-1:P
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33) Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
@@ -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 This example shows the counter values of the basic counter set
using delta output format. using delta output format.
.nf .nf
.ft CW .ft CR
.sp 1 .sp 1
# lshwc -d -l 10 -i 5 -s :b # lshwc -d -l 10 -i 5 -s :b
Date,Time,CPU,B0,B1,B2,B3,B4,B5 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 This example shows the counter values of the problem state counter set with
CPU 3 selected. CPU 3 selected.
.nf .nf
.ft CW .ft CR
.sp 1 .sp 1
# lshwc -l2 -a 3:P -H --format json # lshwc -l2 -a 3:P -H --format json
{ {
+152 -4
View File
@@ -10,7 +10,7 @@
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs" .TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
. .
.SH NAME .SH NAME
lspai \- list Processor Activity Instrumentation (PAI) counters \*c \- list Processor Activity Instrumentation (PAI) counters and values
. .
.SH SYNOPSIS .SH SYNOPSIS
\*c \*c
@@ -18,7 +18,22 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
.IR FORMAT ] .IR FORMAT ]
.RB [ \-n ] .RB [ \-n ]
.RB [ \-t .RB [ \-t
.IR TYPE ]
.br
\*c
.RB [ \-\-format
.IR FORMAT ]
.RB [ \-n ]
.RB [ \-t
.IR "\ TYPE" ] .IR "\ TYPE" ]
.RB [ \-a ][ \-d ][ \-i
.IR SECONDS ]
.RB [ \-l
.IR COUNT ]
.RB [ \-s ][ \-x ][ \-x ]
.RB [ \-c
.IR LIST ]
.RI [ CPULIST ]
.br .br
\*c \*c
.BR \-h | \-\-help .BR \-h | \-\-help
@@ -28,10 +43,34 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
. .
. .
.SH DESCRIPTION .SH DESCRIPTION
On the first line
\*c displays the Processor Activity Instrumentation (PAI) counters \*c displays the Processor Activity Instrumentation (PAI) counters
for Linux on IBM Z. for Linux on IBM Z.
The output is a human-readable list of available PAI counter The output lists the available PAI counter names
names and numbers. and numbers in various formats.
If
.I CPULIST
is specified or any of the options
.BR \-a ,
.BR \-d ,
.BR \-s ,
.BR \-x ,
.BR \-X ,
.B \-l
or
.BR \-i,
are present on the command line,
\*c command extracts counter values from the
specified PAI counter sets.
Counter sets can be specified and extracted for individual CPUs.
By default the output is a comma-separated list of values.
Each line starts with a timestamp
followed by the extracted counter values.
A header line shows each counter name and the
counter number in parenthesis.
For other formats, use the
.B \-\-format
option.
.SH OPTIONS .SH OPTIONS
.TP .TP
.BR \-h ", " \-\-help .BR \-h ", " \-\-help
@@ -42,7 +81,44 @@ Displays help information, then exits.
Displays version information, then exits. Displays version information, then exits.
. .
.TP .TP
.BR \-t ", " \-\-type "\ TYPE" .BR \-a ", " \-\-allcpu
Displays counter values from each CPU.
The default is a total summary line of all counters from all CPUs.
.
.TP
.BR \-d ", " \-\-delta
Displays counter values in form of deltas.
Each counter value shows the increment to the previous output line.
Without this flag the total value of each counter is displayed.
.
.TP
.BR \-c ", " \-\-counters \fI\ LIST\fP
Specify a comma separated list of counter names to
be printed.
If a counter name does not match any valid counter
name, it is omitted.
.
.TP
.BR \-s ", " \-\-short
Displays counter names as short names in the heading.
A short name consists of
a letter for the counter set followed by a number.
For example,
this flag displays counter number 0 in the PAI crypto counter
set as
.B C0
instead of
.BR CRYPTO_ALL .
Valid counter set letters are:
.RS
.IP C
PAI crypto counter set
.IP N
PAI NNPA counter set
.RE
.
.TP
.BR \-t ", " \-\-type "\ \fITYPE\fP"
Specifies the PAI counter set to list. Specifies the PAI counter set to list.
Valid counter set values are Valid counter set values are
.I crypto .I crypto
@@ -63,6 +139,26 @@ Retrieve output in one of the following formats:
JSON, csv, json-seq or pairs. JSON, csv, json-seq or pairs.
If no format is specified, If no format is specified,
the output defaults to a human-readable format. the output defaults to a human-readable format.
.
.TP
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
Specifies a time interval, in seconds,
that the command waits between read operations.
The default is 60 seconds.
.
.TP
.BR \-l ", " \-\-loop \fI\ COUNT\fP
Performs the specified number of read operations.
The default is one.
.
.TP
.BR \-x ", " \-\-hex
Displays counter values as hexadecimal values.
.
.TP
.BR \-X ", " \-\-hex0x
Displays counter values as hexadecimal values with a leading 0x prefix.
.
.SH "EXAMPLE" .SH "EXAMPLE"
The \*c invocation lists all PAI Neural Network Processing Assist Facility The \*c invocation lists all PAI Neural Network Processing Assist Facility
(NNPA) counters in numeric order: (NNPA) counters in numeric order:
@@ -83,6 +179,58 @@ suitable for
.IR perf "(8)" .IR perf "(8)"
event specification by name. event specification by name.
The third gives a short explanation, if available. The third gives a short explanation, if available.
.sp 1
The \*c invocation displays all PAI Neural Network Processing Assist Facility
(NNPA) counters values.
Ten iterations with a two second delay in between
is printed on the screen.
.nf
# lspai -i 2 -l 10 -t nnpa -n
Date,Time,CPU,NNPA_ALL(0),NNPA_ADD(1),NNPA_SUB(2),NNPA_MUL(3),NNPA_DIV(4),
NNPA_MIN(5),NNPA_MAX(6),NNPA_LOG(7),NNPA_EXP(8),NNPA_IBM_RESERVED_9(9),
NNPA_RELU(10),NNPA_TANH(11),NNPA_SIGMOID(12),NNPA_SOFTMAX(13),NNPA_BATCHNORM(14),
NNPA_MAXPOOL2D(15),NNPA_AVGPOOL2D(16),NNPA_LSTMACT(17),NNPA_GRUACT(18),
NNPA_CONVOLUTION(19),NNPA_MATMUL_OP(20),NNPA_MATMUL_OP_BCAST23(21),
NNPA_SMALLBATCH(22),NNPA_LARGEDIM(23),NNPA_SMALLTENSOR(24),NNPA_1MFRAME(25),
NNPA_2GFRAME(26),NNPA_ACCESSEXCEPT(27)
2025-05-13,08:18:27,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:29,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:31,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:33,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:35,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:37,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:39,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:41,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:43,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2025-05-13,08:18:45,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
#
.fi
.sp 1
This \*c example displays the value of PAI crypto counter number 7.
It is field number 116 in the output.
This counter is incremented after \*c is started and
the table shows the delta values for PAI crypto counter 7.
.nf
# lspai -d -s -i 5 -l3 -t crypto | \
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
Date,Time,CPU,C0,C7
2025-06-24,13:02:24,Total,0,0
2025-06-24,13:02:29,Delta,77122286,77122284
2025-06-24,13:02:34,Delta,92574403,92574403
#
.fi
.sp 1
The following table shows the total value for PAI crypto counter 7
after \*c is started.
.nf
# lspai -s -i 5 -l3 -t crypto | \
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
Date,Time,CPU,C0,C7
2025-06-24,13:03:01,Total,0,0
2025-06-24,13:03:06,Total,72550754,72566121
2025-06-24,13:03:11,Total,164633982,164650377
#
.fi
.SH "SEE ALSO" .SH "SEE ALSO"
.BR pai (8) .BR pai (8)
.BR lscpumf (8) .BR lscpumf (8)
+13 -13
View File
@@ -15,24 +15,24 @@ pai \- record and report Processor Activity Instrumentation (PAI) counters
.SH SYNOPSIS .SH SYNOPSIS
\*c \*c
.RB [ \-V ][ \-m | \-\-mapsize .RB [ \-V ][ \-m | \-\-mapsize
.IR size ] .IR SIZE ]
.RB [ \-i | \-\-interval .RB [ \-i | \-\-interval
.IR ms ] .IR MS ]
.RB [ \-R | \-\-realtime .RB [ \-R | \-\-realtime
.IR prio ] .IR PRIO ]
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ] .BR \-c | \-\-crypto [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
.br .br
\*c \*c
.RB [ \-V ][ \-m | \-\-mapsize .RB [ \-V ][ \-m | \-\-mapsize
.IR size ] .IR SIZE ]
.RB [ \-i | \-\-interval .RB [ \-i | \-\-interval
.IR ms ] .IR MS ]
.RB [ \-R | \-\-realtime .RB [ \-R | \-\-realtime
.IR prio ] .IR PRIO ]
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ] .BR \-n | \-\-nnpa [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
.br .br
\*c \*c
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ] .RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIFILES\fP ]
.br .br
\*c \*c
.BR \-h | \-\-help .BR \-h | \-\-help
@@ -73,7 +73,7 @@ Reporting evaluates files that are created by recording.
.SH OPTIONS .SH OPTIONS
.TP .TP
.BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]" .BR \-c ", " \-\-crypto "\fR[\fICPULIST\fR][:\fIDATA\fR]"
Records data for all (default) or a specified list of CPUs. Records data for all (default) or a specified list of CPUs.
The CPU list is a comma-separated list of CPU numbers and ranges. The CPU list is a comma-separated list of CPU numbers and ranges.
In a range, a hyphen separates the first CPU number In a range, a hyphen separates the first CPU number
@@ -106,7 +106,7 @@ execution.
.RE .RE
. .
.TP .TP
.BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]" .BR \-n ", " \-\-nnpa "\fR[\fICPULIST\fR][:\fIDATA\fR]"
Records data for all (default) or a specified list of CPUs. Records data for all (default) or a specified list of CPUs.
The CPU list is a comma-separated list of CPU numbers and ranges. The CPU list is a comma-separated list of CPU numbers and ranges.
In a range, a hyphen separates the first CPU number In a range, a hyphen separates the first CPU number
@@ -241,7 +241,7 @@ The program runs for 10 seconds
(10 intervals of 1000 milliseconds). (10 intervals of 1000 milliseconds).
.sp 1 .sp 1
.nf .nf
.ft CW .ft CR
# \*c -c0 10 # \*c -c0 10
.ft R .ft R
.fi .fi
@@ -256,7 +256,7 @@ and a comma-separated list of counter number, colon (:),
and the counter value n hexadecimal notation. and the counter value n hexadecimal notation.
.sp 1 .sp 1
.nf .nf
.ft CW .ft CR
# \*c -r # \*c -r
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18 0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1 0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1
+1 -9
View File
@@ -59,14 +59,6 @@ static struct util_list list_pai_event;
static struct util_list list_pmu_event; static struct util_list list_pmu_event;
static bool summary; static bool summary;
/* System call to perf_event_open(2) */
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
}
static void ev_dealloc(void) static void ev_dealloc(void)
{ {
struct pai_event *next, *p; struct pai_event *next, *p;
@@ -743,8 +735,8 @@ static int add_events(int type)
} }
if (rc) { if (rc) {
free(pmuname);
warnx("failed building event list for %s", pmuname); warnx("failed building event list for %s", pmuname);
free(pmuname);
} }
return rc; return rc;
} }
+37 -21
View File
@@ -23,11 +23,11 @@
#include <unistd.h> #include <unistd.h>
#include "lib/dasd_base.h" #include "lib/dasd_base.h"
#include "lib/util_base.h"
#include "lib/util_file.h" #include "lib/util_file.h"
#include "lib/util_libc.h" #include "lib/util_libc.h"
#include "lib/util_opt.h" #include "lib/util_opt.h"
#include "lib/util_prg.h" #include "lib/util_prg.h"
#include "lib/util_path.h"
#include "lib/zt_common.h" #include "lib/zt_common.h"
#define RD_BUFFER_SIZE 80 #define RD_BUFFER_SIZE 80
@@ -283,7 +283,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
char filename[] = "dasdinfo0000"; char filename[] = "dasdinfo0000";
mode_t mode; mode_t mode;
unsigned int path; unsigned int path;
int retry; unsigned int retry;
int rc; int rc;
int fd; int fd;
@@ -294,7 +294,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
if (pathname[path] == NULL) if (pathname[path] == NULL)
continue; continue;
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) { for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
sprintf(filename, "dasdinfo%04d", retry); snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
result = dinfo_make_path(pathname[path], filename); result = dinfo_make_path(pathname[path], filename);
if (result == NULL) if (result == NULL)
return -1; return -1;
@@ -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) static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
{ {
char *readbuf = NULL; char *readbuf = NULL;
char *path;
readbuf = dinfo_malloc(RD_BUFFER_SIZE); readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) { if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer"); warnx("Error: Not enough memory to allocate readbuffer");
return -1; return -1;
} }
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path = util_path_sysfs("block/%s/dev", blockdev);
"/sys/block/%s/dev", blockdev) < 0) if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path) < 0) {
free(path);
free(readbuf);
return -1; return -1;
if (dinfo_extract_dev(dev, readbuf) != 0) }
free(path);
if (dinfo_extract_dev(dev, readbuf) != 0) {
free(readbuf);
return -1; return -1;
}
free(readbuf);
return 0; return 0;
} }
@@ -373,8 +381,9 @@ static int
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb), dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
int tflag, struct FTW *ftwbuf) int tflag, struct FTW *ftwbuf)
{ {
enum { LINK_DIR_SIZE = 128 };
char linkdir[LINK_DIR_SIZE];
char *tempdir; char *tempdir;
char linkdir[128];
ssize_t i; ssize_t i;
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid, if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
@@ -387,12 +396,12 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
*/ */
if (asprintf(&tempdir, "%s/driver", fpath) < 0) if (asprintf(&tempdir, "%s/driver", fpath) < 0)
return -1; return -1;
i = readlink(tempdir, linkdir, 128); i = readlink(tempdir, linkdir, LINK_DIR_SIZE);
free(tempdir); free(tempdir);
if ((i < 0) || (i >= 128)) if (i < 0 || i >= LINK_DIR_SIZE)
return -1; return -1;
/* append '\0' because readlink returns non zero terminated string */ /* append '\0' because readlink returns non zero terminated string */
tempdir[i + 1] = '\0'; linkdir[i] = '\0';
if (strstr(linkdir, "dasd") == NULL) if (strstr(linkdir, "dasd") == NULL)
return FTW_CONTINUE; return FTW_CONTINUE;
free(busiddir); free(busiddir);
@@ -439,7 +448,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
char *tempdir = NULL; char *tempdir = NULL;
char *result = NULL; char *result = NULL;
char *sysfsdir = "/sys/devices/"; char *sysfsdir = util_path_sysfs("devices/");
/* dinfo_is_devnode needs to know the busid */ /* dinfo_is_devnode needs to know the busid */
searchbusid = busid; searchbusid = busid;
@@ -475,6 +484,7 @@ out:
out2: out2:
free(busiddir); free(busiddir);
free(result); free(result);
free(sysfsdir);
return rc; return rc;
} }
@@ -486,6 +496,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
DIR *directory = NULL; DIR *directory = NULL;
struct dirent *dir_entry = NULL; struct dirent *dir_entry = NULL;
int rc = 0; int rc = 0;
char *path;
if (stat(devnode, &stat_buffer) != 0) { if (stat(devnode, &stat_buffer) != 0) {
warnx("Error: could not stat %s", devnode); warnx("Error: could not stat %s", devnode);
@@ -495,32 +506,34 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev), sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
minor(stat_buffer.st_rdev)); minor(stat_buffer.st_rdev));
directory = opendir("/sys/block/"); path = util_path_sysfs("block/");
directory = opendir(path);
if (directory == NULL) { if (directory == NULL) {
warnx("Error: could not open directory /sys/block"); warnx("Error: could not open directory %s", path);
free(path);
return -1; return -1;
} }
readbuf = dinfo_malloc(RD_BUFFER_SIZE); readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) { if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer"); warnx("Error: Not enough memory to allocate readbuffer");
free(path);
return -1; return -1;
} }
while ((dir_entry = readdir(directory)) != NULL) { while ((dir_entry = readdir(directory)) != NULL) {
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "%s%s/dev", path,
"/sys/block/%s/dev",
dir_entry->d_name) < 0) dir_entry->d_name) < 0)
continue; continue;
if (strncmp(stat_dev, readbuf, if (strncmp(stat_dev, readbuf,
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) { MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
rc = snprintf(*uidfile, RD_BUFFER_SIZE, rc = snprintf(*uidfile, RD_BUFFER_SIZE, "%s%s/device/uid", path,
"/sys/block/%s/device/uid",
dir_entry->d_name); dir_entry->d_name);
if (rc >= RD_BUFFER_SIZE) { if (rc >= RD_BUFFER_SIZE) {
fprintf(stderr, fprintf(stderr,
"Error: Device name was truncated\n"); "Error: Device name was truncated\n");
free(path);
return -1; return -1;
} }
@@ -529,6 +542,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
} }
closedir(directory); closedir(directory);
free(path);
return 0; return 0;
} }
@@ -626,16 +640,18 @@ int main(int argc, char *argv[])
} }
readbuf = dinfo_malloc(RD_BUFFER_SIZE); readbuf = dinfo_malloc(RD_BUFFER_SIZE);
uidfile = dinfo_malloc(RD_BUFFER_SIZE); if (!readbuf)
if (!(readbuf && uidfile))
exit(1); exit(1);
/* try to read the uid attribute */ /* try to read the uid attribute */
if (busid) { if (busid) {
sprintf(uidfile, "/sys/bus/ccw/devices/%s/uid", busid); uidfile = util_path_sysfs("bus/ccw/devices/%s/uid", busid);
} else if (blockdev) { } else if (blockdev) {
sprintf(uidfile, "/sys/block/%s/device/uid", blockdev); uidfile = util_path_sysfs("block/%s/device/uid", blockdev);
} else if (devnode) { } else if (devnode) {
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
if (!uidfile)
exit(1);
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0) if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
goto error; goto error;
} }
+1 -1
View File
@@ -22,7 +22,7 @@
. ds pfont \\n[.f] . ds pfont \\n[.f]
. nh . nh
. na . na
. ft CW . ft CR
\\$* \\$*
. ft \\*[pfont] . ft \\*[pfont]
. ad . ad
+14
View File
@@ -18,3 +18,17 @@ CPI_SYSTEM_NAME=""
# CPI sysplex name # CPI sysplex name
# #
CPI_SYSPLEX_NAME="" CPI_SYSPLEX_NAME=""
#
# CPI permit on protected virtualization guests
#
# Important: Set CPI_PERMIT_ON_PVGUEST=1 only if you trust the host system.
# Enabling these options allows the host to receive potentially sensitive
# Control-Program Identification (CPI) data from the protected virtualization
# guest, including:
# - system_type
# - system_level
# - sysplex_name
# - system_name
#
CPI_PERMIT_ON_PVGUEST=
+36 -1
View File
@@ -9,6 +9,8 @@
#include <sched.h> #include <sched.h>
#include <stdbool.h> #include <stdbool.h>
#include <asm/unistd.h>
#include <linux/perf_event.h>
#define S390_CPUMF_CF "devices/cpum_cf/" #define S390_CPUMF_CF "devices/cpum_cf/"
#define S390_CPUMF_CFDIAG "devices/cpum_cf_diag/" #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] min Minimum supported sampling interval
* @param[out] max Maximum 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 * microsecond
* @param[out] basic_sz Basic sample size in bytes * @param[out] basic_sz Basic sample size in bytes
* @param[out] diag_sz Diagnostic 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 * @retval false PAI_NNPA counter Facility is not available
*/ */
bool libcpumf_have_pai_nnpa(void); 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 #endif
+17
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 @} */
+1
View File
@@ -15,6 +15,7 @@ install-man: $(MANS)
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \ msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
done done
ln -f -s chiucvallow.8 $(DESTDIR)$(MANDIR)/man8/lsiucvallow.8
clean: clean:
+6 -6
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 To connect to the first z/VM IUCV HVC terminal device on the z/VM guest virtual
machine "LNX1234", run: machine "LNX1234", run:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvconn LNX1234 lnxhvc0 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 To configure and start \fBgetty\fP on a HVC terminal, open
.BR /etc/inittab (5), .BR /etc/inittab (5),
and add a new entry similar to this one: and add a new entry similar to this one:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
@@ -261,7 +261,7 @@ IUCV HVC terminal:
.IP "1." 4 .IP "1." 4
To display the current terminal name, issue the command: To display the current terminal name, issue the command:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
user@host:~$ echo $TERM user@host:~$ echo $TERM
@@ -275,7 +275,7 @@ To connect to a z/VM IUCV HVC terminal, run
.BR iucvconn (1) .BR iucvconn (1)
and log in as usual: and log in as usual:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
user@host:~$ iucvconn MYLNX01 lnxhvc0 user@host:~$ iucvconn MYLNX01 lnxhvc0
@@ -289,7 +289,7 @@ user@MYLNX01:~$
To assign the terminal name from step 1. to the terminal environment variable, To assign the terminal name from step 1. to the terminal environment variable,
issue the following command: issue the following command:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
user@MYLNX01:~$ export TERM=xterm user@MYLNX01:~$ export TERM=xterm
@@ -304,7 +304,7 @@ environment variable.
.PP .PP
For getty programs, a sample terminal environment configuration might look like: For getty programs, a sample terminal environment configuration might look like:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux
+3 -3
View File
@@ -123,7 +123,7 @@ the characters to the connected terminal.
To access the "lnxterm" terminal on the Linux instance in To access the "lnxterm" terminal on the Linux instance in
z/VM guest virtual machine LNXSYS01: z/VM guest virtual machine LNXSYS01:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvconn LNXSYS01 lnxterm iucvconn LNXSYS01 lnxterm
@@ -134,7 +134,7 @@ iucvconn LNXSYS01 lnxterm
To access the first z/VM IUCV HVC terminal on the Linux instance in To access the first z/VM IUCV HVC terminal on the Linux instance in
z/VM guest virtual machine LNXSYS02: z/VM guest virtual machine LNXSYS02:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvconn LNXSYS02 lnxhvc0 iucvconn LNXSYS02 lnxhvc0
@@ -145,7 +145,7 @@ iucvconn LNXSYS02 lnxhvc0
To create a transcript of the terminal session to the Linux instance in To create a transcript of the terminal session to the Linux instance in
z/VM guest virtual machine LNXSYS99: z/VM guest virtual machine LNXSYS99:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0 iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0
+6 -6
View File
@@ -94,7 +94,7 @@ additional options.
.SH EXAMPLES .SH EXAMPLES
To allow remote logins using the terminal identifier "lnxterm": To allow remote logins using the terminal identifier "lnxterm":
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvtty lnxterm iucvtty lnxterm
@@ -104,7 +104,7 @@ iucvtty lnxterm
To only allow users from LNXSYS01 to connect to terminal "lnxterm": To only allow users from LNXSYS01 to connect to terminal "lnxterm":
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvtty -a LNXSYS01 lnxterm iucvtty -a LNXSYS01 lnxterm
@@ -115,7 +115,7 @@ iucvtty -a LNXSYS01 lnxterm
To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal
"lnxterm": "lnxterm":
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvtty -a "LNXSYS1[0-9]" lnxterm 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": To use \fB/sbin/sulogin\fP instead of \fB/bin/login\fP for terminal "suterm":
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
iucvtty suterm -- /sbin/sulogin 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" An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "lnxterm"
with \fB/bin/login\fP could be: with \fB/bin/login\fP could be:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
t1:2345:respawn:/usr/bin/iucvtty lnxterm 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" 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: with \fB/sbin/sulogin\fP in single user mode could be:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin
+14 -14
View File
@@ -79,7 +79,7 @@ users".
The output for "list" authorization is a list of z/VM guest virtual machines, The output for "list" authorization is a list of z/VM guest virtual machines,
for example: for example:
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .nf
@@ -94,7 +94,7 @@ guest5
.ft .ft
The output for "regex" authorization is a list of one or more The output for "regex" authorization is a list of one or more
regular expressions, for example: regular expressions, for example:
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .nf
@@ -109,7 +109,7 @@ Regular expressions for your authorization:
If \fBts-shell\fP is configured to connect to particular z/VM guest virtual 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 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: the user IDs that match at least one of the regular expressions:
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .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 In the following example, a user opens a terminal connection to the Linux
instance in z/VM guest virtual machine LNXSYS01: instance in z/VM guest virtual machine LNXSYS01:
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .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 If \fIidentifier\fP is not specified, the current default terminal identifier
is displayed: is displayed:
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .nf
user@ts-shell> terminal 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 A typical \fBIUCV\fP authorization statement in the z/VM directory entry of the
terminal server z/VM guest virtual machine might be: terminal server z/VM guest virtual machine might be:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
IUCV ANY IUCV ANY
@@ -301,7 +301,7 @@ instance.
For example, to create a list of all z/VM guest virtual machines with names that 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: start with "LINUX" and are followed by digits, use:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
@@ -373,7 +373,7 @@ group name and prefixed with "@".
Here is an example of a Linux user and group authorization: Here is an example of a Linux user and group authorization:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
alice = list:guest01,guest02 alice = list:guest01,guest02
@@ -395,7 +395,7 @@ name on a separate line.
.PP .PP
The following example shows the usage of the \fIfile:\fP prefix: The following example shows the usage of the \fIfile:\fP prefix:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
@testgrp = file:/etc/iucvterm/auth/test-systems.list @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 start with "lnx" and are followed with at least three but not more than five
alphanumeric characters, use: alphanumeric characters, use:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
bob = regex:lnx\\w{3,5} 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 all systems in the test environment; and respectively, authorize all users in
the "prodgrp" group for all systems in the production environment: the "prodgrp" group for all systems in the production environment:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
@testgrp = regex:test\\w+ @testgrp = regex:test\\w+
@@ -461,7 +461,7 @@ of the other type are ignored.
Example: Example:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
@users = list:guest01,guest03,guest05 @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 Add the path of the \fBts-shell\fP program to the \fI/etc/shells\fP file that
contains the list of valid login shells: contains the list of valid login shells:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
echo $(which ts-shell) >> /etc/shells echo $(which ts-shell) >> /etc/shells
@@ -498,7 +498,7 @@ Change the login shell of a particular Linux user using the
.BR chsh (1) .BR chsh (1)
program: program:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
chsh -s $(which ts-shell) alice chsh -s $(which ts-shell) alice
+2 -2
View File
@@ -116,7 +116,7 @@ specifies an exit status in this range.
.SS inittab .SS inittab
To start \fB/sbin/agetty\fP on terminal device "hvc1", specify: To start \fB/sbin/agetty\fP on terminal device "hvc1", specify:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux 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 To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following
settings to the job file: settings to the job file:
.PP .PP
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
respawn respawn
+1 -1
View File
@@ -10,7 +10,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <strings.h>
#include "lib/ccw.h" #include "lib/ccw.h"
+29
View File
@@ -14,6 +14,7 @@
#include "lib/libcpumf.h" #include "lib/libcpumf.h"
#include "lib/util_path.h" #include "lib/util_path.h"
#include "lib/util_libc.h"
#define SERVICELEVEL "/proc/service_levels" #define SERVICELEVEL "/proc/service_levels"
@@ -169,3 +170,31 @@ bool libcpumf_have_pai_nnpa(void)
{ {
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_NNPA); 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;
}
+8 -5
View File
@@ -5099,14 +5099,15 @@ int ekmf_generate_csr(const struct ekmf_config *config,
} }
} }
if (rsa_pss_params != NULL)
_ekmf_copy_pss_params(rsa_pss_params, &pss_params); _ekmf_copy_pss_params(rsa_pss_params, &pss_params);
rc = SK_OPENSSL_generate_csr(key_blob, key_blob_size, rc = SK_OPENSSL_generate_csr(key_blob, key_blob_size,
subject_rdns, num_subject_rdns, subject_rdns, num_subject_rdns,
subject_utf8, cert, subject_utf8, cert,
extensions, num_extensions, extensions, num_extensions,
digest_nid, &pss_params, &req, digest_nid, rsa_pss_params != NULL ?
&pss_params : NULL, &req,
&ext_lib_info.ext_lib, verbose); &ext_lib_info.ext_lib, verbose);
if (rc != 0) { if (rc != 0) {
pr_verbose(verbose, "SK_OPENSSL_generate_csr failed " 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, rc = SK_OPENSSL_generate_ss_cert(key_blob, key_blob_size,
subject_rdns, num_subject_rdns, subject_rdns, num_subject_rdns,
subject_utf8, rcert, subject_utf8, rcert,
extensions, num_extensions, extensions, num_extensions,
validity_days, digest_nid, validity_days, digest_nid,
&pss_params, &cert, rsa_pss_params != NULL ?
&pss_params : NULL, &cert,
&ext_lib_info.ext_lib, verbose); &ext_lib_info.ext_lib, verbose);
if (rc != 0) { if (rc != 0) {
pr_verbose(verbose, "SK_OPENSSL_generate_ss_cert failed " pr_verbose(verbose, "SK_OPENSSL_generate_ss_cert failed "
+33
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';
}
+4 -4
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: An AF_IUCV socket is represented by the following format:
.PP .PP
.RS 8 .RS 8
.ft CW .ft CR
.nf .nf
#define AF_IUCV 32 #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: To set an identifier, issue a command like this:
.PP .PP
.RS 8 .RS 8
.ft CW .ft CR
echo \fIidentifier\fP > /sys/devices/qeth/\fI<bus-ID>\fP/hsuid echo \fIidentifier\fP > /sys/devices/qeth/\fI<bus-ID>\fP/hsuid
.ft .ft
.RE .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: HiperSockets device with bus-ID 0.0.8000, run:
.PP .PP
.RS 8 .RS 8
.ft CW .ft CR
.nf .nf
echo "MYHOST01" > /sys/devices/qeth/0.0.8000/hsuid echo "MYHOST01" > /sys/devices/qeth/0.0.8000/hsuid
.fi .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 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 all other z/VM guest virtual machines. The total number of connections for your
z/VM guest virtual machine cannot exceed 100. z/VM guest virtual machine cannot exceed 100.
.ft CW .ft CR
.in +0.25i .in +0.25i
.nf .nf
+1 -1
View File
@@ -22,7 +22,7 @@
. ds pfont \fP . ds pfont \fP
. nh . nh
. na . na
. ft CW . ft CR
\\$* \\$*
. ft \\*[pfont] . ft \\*[pfont]
. ad . ad
+22 -31
View File
@@ -47,7 +47,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -205,9 +205,9 @@ dependencies = [
[[package]] [[package]]
name = "curl" name = "curl"
version = "0.4.47" version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265" checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc"
dependencies = [ dependencies = [
"curl-sys", "curl-sys",
"libc", "libc",
@@ -215,14 +215,14 @@ dependencies = [
"openssl-sys", "openssl-sys",
"schannel", "schannel",
"socket2", "socket2",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
name = "curl-sys" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -230,7 +230,7 @@ dependencies = [
"openssl-sys", "openssl-sys",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -318,7 +318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -501,9 +501,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.70" version = "0.10.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if", "cfg-if",
@@ -533,9 +533,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.105" version = "0.9.109"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -799,7 +799,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -865,7 +865,7 @@ version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -929,12 +929,12 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.8" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -971,7 +971,7 @@ dependencies = [
"getrandom 0.2.16", "getrandom 0.2.16",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -981,7 +981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [ dependencies = [
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -1035,9 +1035,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
version = "0.2.8" version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@@ -1086,15 +1086,6 @@ dependencies = [
"wit-bindgen-rt", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
+28
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(())
}
+4 -4
View File
@@ -7,7 +7,7 @@ DEBUG_FILES := $(addsuffix .debug,$(FILES))
ifeq ($(HOST_ARCH),s390x) ifeq ($(HOST_ARCH),s390x)
ZIPL_DIR := $(rootdir)/zipl ZIPL_DIR := $(rootdir)/zipl
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot 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_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS)) INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
@@ -86,9 +86,9 @@ stage3b.elf: head.o $(ZIPL_OBJS)
@chmod a-x $@ @chmod a-x $@
install: stage3a.bin stage3b_reloc.bin install: stage3a.bin stage3b_reloc.bin
$(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR) $(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PKGDATADIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PKGDATADIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
else else
# Don't generate the dependency files (see `common.mak` for the # Don't generate the dependency files (see `common.mak` for the
+22 -14
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 .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH "PVIMG-CREATE" "1" "2025-04-24" "s390-tools" "Pvimg Manual" .TH "PVIMG-CREATE" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
@@ -11,23 +11,23 @@ pvimg-create \- Create an IBM Secure Execution image
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .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 .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
Use \fBpvimg\fR to generate a single bootable image file with encrypted and 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 integrity\-protected parts. The command requires a kernel image, a host\-key
document, certificates for the host-key document verification, and an output document, certificates for the host\-key document verification, and an output
file name. Optionally, specify an initial RAM filesystem, and a file containing file name. Optionally, specify an initial RAM filesystem, and a file containing
the kernel parameters. If the command should be run offline, use the the kernel parameters. If the command should be run offline, use the
\fB\-\-offline\fR option and specify the certificate revocation lists (CRLs) by \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 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 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 DER input formats are supported. In the resulting image file, a plain text boot
loader, the encrypted components for kernel, initial RAM disk, kernel 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. The header contains metadata necessary for running the guest in protected mode.
.PP .PP
Use this image file as a kernel image for zipl or for a direct kernel boot using 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> \-\-cck, \-\-comm\-key <FILE>
.RS 4 .RS 4
Use the content of FILE as the customer\-communication key (CCK). The file must 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
.RE .RE
.PP .PP
@@ -136,6 +137,13 @@ Secure Execution header protection key is a randomly generated key.
.RE .RE
.RE .RE
.PP .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 \-\-enable\-dump
.RS 4 .RS 4
Enable Secure Execution guest dump support. This option requires the Enable Secure Execution guest dump support. This option requires the
@@ -165,8 +173,8 @@ Add\-secret requests don't have to provide the CCK\-derived extension secret
.PP .PP
\-\-enable\-cck\-update \-\-enable\-cck\-update
.RS 4 .RS 4
Enable CCK update support. Requires z17 or up. This option cannot be Enable CCK update support. Requires z17 or up. This option cannot be used in
used in conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option. conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option.
.RE .RE
.RE .RE
.PP .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 \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 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 \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 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, 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 which is used when creating a Secure Execution image with guest dump support
enabled in the second example. 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 .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 .SH NOTES
.IP "1." 4 .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 .IP "2." 4
An ELF file cannot be used as a Linux kernel image. An ELF file cannot be used as a Linux kernel image.
.IP "3." 4 .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" .SH "SEE ALSO"
.sp .sp
\fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1) \fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
+3 -3
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 .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH "PVIMG-INFO" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .TH "PVIMG-INFO" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
@@ -11,7 +11,7 @@ pvimg-info \- Print information about the IBM Secure Execution image
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
pvimg info [OPTIONS] --format <FORMAT> <INPUT> pvimg info [OPTIONS] \-\-format <FORMAT> <INPUT>
.fam C .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
+3 -3
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 .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH "PVIMG-TEST" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .TH "PVIMG-TEST" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
@@ -11,7 +11,7 @@ pvimg-test \- Test different aspects of an existing IBM Secure Execution image
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
pvimg test <--host-key-document <FILE>|--key-hashes[=<FILE>]> <INPUT> pvimg test <\-\-host\-key\-document <FILE>|\-\-key\-hashes[=<FILE>]> <INPUT>
.fam C .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
+6 -6
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 .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH "PVIMG" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .TH "PVIMG" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
@@ -21,21 +21,21 @@ Execution images.
.SH "PVIMG COMMANDS" .SH "PVIMG COMMANDS"
.PP .PP
\fBpvimg-create(1)\fR \fBpvimg\-create(1)\fR
.RS 4 .RS 4
Create an IBM Secure Execution image Create an IBM Secure Execution image
.RE .RE
.PP .PP
\fBpvimg-info(1)\fR \fBpvimg\-info(1)\fR
.RS 4 .RS 4
Print information about the IBM Secure Execution image Print information about the IBM Secure Execution image
.RE .RE
.PP .PP
\fBpvimg-test(1)\fR \fBpvimg\-test(1)\fR
.RS 4 .RS 4
Test different aspects of an existing IBM Secure Execution image Test different aspects of an existing IBM Secure Execution image
.RE .RE
@@ -83,4 +83,4 @@ line flag, or wrong number of arguments.
.RE .RE
.SH "SEE ALSO" .SH "SEE ALSO"
.sp .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)
+52 -24
View File
@@ -93,6 +93,42 @@ pub struct ComponentPaths {
pub parmfile: Option<PathBuf>, 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)] #[derive(Args, Debug)]
#[cfg_attr(test, derive(Default))] #[cfg_attr(test, derive(Default))]
#[command( #[command(
@@ -342,25 +378,8 @@ pub struct CreateBootImageArgs {
#[arg(long)] #[arg(long)]
pub overwrite: bool, pub overwrite: bool,
/// Use the content of FILE as the customer-communication key (CCK). #[clap(flatten)]
/// pub keys: UserKeys,
/// 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)] #[clap(flatten)]
pub legacy_flags: CreateBootImageLegacyFlags, pub legacy_flags: CreateBootImageLegacyFlags,
@@ -379,11 +398,6 @@ pub struct CreateBootImageExperimentalArgs {
#[arg(long, value_name = "DIR", hide(true))] #[arg(long, value_name = "DIR", hide(true))]
pub x_bootloader_directory: Option<PathBuf>, 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). /// Manually set the PSW address used for the Secure Execution header (experimental option).
// Hidden in user documentation. // Hidden in user documentation.
#[arg(long, value_name = "ADDRESS", hide(true))] #[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("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("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("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 = [ let invalid_create_args = [
flat_map_collect(remove(mvcanv.clone(), "no-verify")), 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"]), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("extension", ["--enable-cck-extension-secret"]),
CliOption::new("update", ["--enable-cck-update"])])), 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![ let mut genprotimg_valid_args = vec![
+6 -10
View File
@@ -2,13 +2,13 @@
// //
// Copyright IBM Corp. 2024 // Copyright IBM Corp. 2024
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use log::info; use log::info;
use pv::{misc::read_file, request::Confidential}; use pv::{misc::read_file, request::Confidential};
use crate::cli::CreateBootImageExperimentalArgs; use crate::cli::UserKeys;
#[macro_export] #[macro_export]
/// Makes it easier to /// Makes it easier to
@@ -23,13 +23,9 @@ pub struct UserProvidedKeys {
} }
/// Reads all user provided keys. /// Reads all user provided keys.
pub fn read_user_provided_keys( pub fn read_user_provided_keys(keys: &UserKeys) -> Result<UserProvidedKeys> {
cck_path: Option<&Path>,
hdr_key_path: Option<&Path>,
experimental_args: &CreateBootImageExperimentalArgs,
) -> Result<UserProvidedKeys> {
let components_key = { let components_key = {
match &experimental_args.x_comp_key { match &keys.image_key {
Some(key_path) => { Some(key_path) => {
info!( info!(
"Use file '{}' as the image components protection key", "Use file '{}' as the image components protection key",
@@ -44,7 +40,7 @@ pub fn read_user_provided_keys(
} }
}; };
let aead_key = { let aead_key = {
match hdr_key_path { match &keys.hdr_key {
Some(key_path) => { Some(key_path) => {
info!( info!(
"Use file '{}' as the Secure Execution header protection", "Use file '{}' as the Secure Execution header protection",
@@ -63,7 +59,7 @@ pub fn read_user_provided_keys(
}; };
let cck = { let cck = {
match cck_path { match &keys.cck {
Some(key_path) => { Some(key_path) => {
info!( info!(
"Use file '{}' as the customer communication key (CCK)", "Use file '{}' as the customer communication key (CCK)",
+1 -5
View File
@@ -126,11 +126,7 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
let verified_host_keys = opt let verified_host_keys = opt
.certificate_args .certificate_args
.get_verified_hkds("Secure Execution image")?; .get_verified_hkds("Secure Execution image")?;
let user_provided_keys = read_user_provided_keys( let user_provided_keys = read_user_provided_keys(&opt.keys)?;
opt.cck.as_deref(),
opt.hdr_key.as_deref(),
&opt.experimental_args,
)?;
let (plaintext_flags, secret_flags) = parse_flags(opt)?; let (plaintext_flags, secret_flags) = parse_flags(opt)?;
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) { if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
+13 -43
View File
@@ -3,44 +3,15 @@
// Copyright IBM Corp. 2024 // Copyright IBM Corp. 2024
use std::{ use std::{
ffi::{CString, OsStr}, ffi::OsStr,
fs::{File, OpenOptions}, fs::{rename, File, OpenOptions},
io::{self, Seek, SeekFrom, Write}, io::{self, Seek, SeekFrom, Write},
os::unix::{ffi::OsStrExt, fs::OpenOptionsExt}, os::unix::fs::OpenOptionsExt,
path::Path, path::Path,
}; };
use pv::{Error, FileAccessErrorType, PvCoreError, Result}; 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 /// This type helps to perform atomic operations by writing to a temporary file
/// and renaming it to the actual filename when the [`AtomicFile::finish`] /// and renaming it to the actual filename when the [`AtomicFile::finish`]
/// function is called. If the [`AtomicFile::finish`] function is never called, /// 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. /// Enum used for more verbosity.
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
pub enum AtomicFileOperation { pub enum AtomicFileOperation {
/// Replace existing file /// Replace existing file
Replace, Replace,
@@ -218,17 +189,16 @@ impl TempPath {
} }
fn persist(self, operation: AtomicFileOperation) -> Result<()> { fn persist(self, operation: AtomicFileOperation) -> Result<()> {
let options = match operation { if let Ok(true) = self.path.try_exists() {
AtomicFileOperation::Replace => 0, if operation == AtomicFileOperation::NoReplace {
AtomicFileOperation::NoReplace => libc::RENAME_NOREPLACE, return Err(Error::Io(io::Error::from(io::ErrorKind::AlreadyExists)));
};
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,
} }
}
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(); self.forget();
Ok(()) Ok(())
+37 -2
View File
@@ -32,6 +32,9 @@ declare TYPE
declare NAME declare NAME
declare SYSPLEX declare SYSPLEX
declare PV_GUEST
declare -i CPI_PERMIT="$CPI_PERMIT_ON_PVGUEST"
declare -i DRYRUN=0 declare -i DRYRUN=0
# Exit codes # Exit codes
@@ -40,6 +43,7 @@ readonly EXIT_FAILURE=1
readonly EXIT_ARG_TOO_LONG=3 readonly EXIT_ARG_TOO_LONG=3
readonly EXIT_INVALID_CHARS=4 readonly EXIT_INVALID_CHARS=4
readonly EXIT_INVALID_ARGS=5 readonly EXIT_INVALID_ARGS=5
readonly EXIT_NOTCONFIGURED=6
# Distro-IDs as supported by SE/HMC firmware # Distro-IDs as supported by SE/HMC firmware
readonly DISTRO_GENERIC=0 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 -S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
-T, --type TYPE Set and commit OS type to TYPE -T, --type TYPE Set and commit OS type to TYPE
-v, --version Print version information, then exit -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 --commit Ignore all other options and commit any uncommitted
values values
--dry-run Do not actually set or commit anything, but show what --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 uncommitted) values
Environment variables used for the --defaults option: 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: Available bits for the --set-bit option:
kvm: Indicate that system is a KVM host kvm: Indicate that system is a KVM host
@@ -124,6 +142,19 @@ fail_with()
cpi_commit() 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 echo 1 > "$CPI_SET" 2> /dev/null
} }
@@ -404,7 +435,7 @@ if [ $# -le 0 ]; then
print_parse_error_and_exit print_parse_error_and_exit
fi 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 if [ $? -ne 0 ]; then
print_parse_error_and_exit print_parse_error_and_exit
fi fi
@@ -473,6 +504,10 @@ while [ -n $1 ]; do
cpi_show cpi_show
exit $EXIT_SUCCESS exit $EXIT_SUCCESS
;; ;;
--permit-cpi)
CPI_PERMIT=1
shift
;;
--commit) --commit)
cpi_commit cpi_commit
exit $EXIT_SUCCESS exit $EXIT_SUCCESS
+15 -9
View File
@@ -1372,17 +1372,18 @@ call_run_command() {
# extract the raw_command and set cmd_type, as some shell might split into # 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 # 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 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 "#######################################################" >> "${logfile}"
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}" echo "${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
# check calling command type # check calling command type
if [ "X${cmd_type}" = "Xshell builtin" ]; then if [ "X${cmd_type}" = "Xbuiltin" ]; then
# command is a builtin (no use of timeout possible) # command is a builtin (use of timeout not possible)
eval "${cmd}" >> ${logfile} 2>&1 eval "${cmd}" >> ${logfile} 2>&1
rc=$? 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 if [ "x${TIMEOUT_OK}" = "xYES" ]; then
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1 eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
@@ -1393,18 +1394,23 @@ call_run_command() {
rc=$? rc=$?
fi fi
else else
echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}" echo "Command \"${raw_cmd}\" not available" >> "${logfile}"
echo >> "${logfile}" echo >> "${logfile}"
return 1 return 1
fi fi
# log a warning on rc not 0 and define return # add return code or new line and log a warning if rc is not 0 and define return
if [ ${rc} ]; then if [ ${rc} -eq 0 ]; then
echo >> "${logfile}" echo >> "${logfile}"
return 0 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 else
echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}" echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}"
echo >> "${logfile}" echo "rc=${rc}" >> "${logfile}"
echo "Warning: Command \"${cmd}\" ended with ${rc}"
return 1 return 1
fi fi
} }
+5
View File
@@ -37,10 +37,15 @@ EnvironmentFile=@sysconf_path@/sysconfig/cpi
# Environment=CPI_SYSPLEX_NAME= # Environment=CPI_SYSPLEX_NAME=
# Environment=CPI_SYSTEM_LEVEL= # Environment=CPI_SYSTEM_LEVEL=
# Environment=CPI_SYSTEM_TYPE=LINUX # Environment=CPI_SYSTEM_TYPE=LINUX
# Environment=CPI_PERMIT_ON_PVGUEST=
# #
# Sending data to the HMC/SE # Sending data to the HMC/SE
ExecStart=@toolslib_path@/cpictl -e 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+7 -7
View File
@@ -456,7 +456,7 @@ Identifies the z/VM spool file to be ordered.
.IP "1." 3 .IP "1." 3
Start z/VM console spooling by issuing: Start z/VM console spooling by issuing:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmcp sp cons start # 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. behind the \f(CWFILE\fP keyword in the corresponding CP message.
In the example below, the spool ID is 398: In the example below, the spool ID is 398:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmcp sp cons clo \(rs* rdr # 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 Read and save the spool file on the Linux file system in the
current working directory: current working directory:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmur re -t 398 linux_cons # vmur re -t 398 linux_cons
@@ -496,7 +496,7 @@ current working directory:
.IP "1." 3 .IP "1." 3
Send parmfile to the z/VM punch queue and transfer it to the reader queue: Send parmfile to the z/VM punch queue and transfer it to the reader queue:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmur pun -r /boot/parmfile # 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 Send the Linux kernel image to the z/VM punch queue and
transfer it to reader queue: transfer it to reader queue:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmur pun -r /boot/vmlinuz -N image # 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 Move the Linux kernel image to the first and parmfile to
the second position in the reader queue: the second position in the reader queue:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# vmur or 465 # vmur or 465
@@ -534,7 +534,7 @@ the second position in the reader queue:
.IP "4." .IP "4."
Prepare re-IPL from the z/VM reader and reboot: Prepare re-IPL from the z/VM reader and reboot:
.ft CW .ft CR
.in +0.25in .in +0.25in
.nf .nf
# chreipl ccw 0.0.000c # chreipl ccw 0.0.000c
+5 -6
View File
@@ -101,9 +101,8 @@ Example: "0192" becomes "0.0.0192".
.SH EXAMPLES .SH EXAMPLES
\fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR \fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR
.RS .RS
After completing all outstanding I/O requests for the devices with bus Tries to set the devices with the bus ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set and 0.0.0198 online.
the device offline.
.RE .RE
.P .P
.B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192 .B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192
@@ -121,9 +120,9 @@ measurement block facility.
.P .P
.B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198 .B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198
.RS .RS
Will try to set the devices with bus ID 0.0.0192, 0.0.0195, After completing all outstanding I/O requests for the devices with bus
0.0.0196, 0.0.0197 and 0.0.0198 offline and finish all outstanding I/O ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
requests before. the devices offline.
.RE .RE
.SH AUTHOR .SH AUTHOR
+33 -6
View File
@@ -285,12 +285,23 @@ Use this option to select all supported columns and keys for output.
Scale BPS values by UNIT Scale BPS values by UNIT
Use this option to specify a value by which bytes-per-seconds (BPS) values - 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 such as read and write throughput - are scaled in human-readable output.
values are: 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 .IP \(bu 3
.B auto: .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 .PP
.IP \(bu 3 .IP \(bu 3
.IR number : .IR number :
@@ -299,18 +310,34 @@ Scale by
.PP .PP
.IP \(bu 3 .IP \(bu 3
.B K: .B K:
Scale by 1024 (KiB) Scale by 1000 (KB)
.PP .PP
.IP \(bu 3 .IP \(bu 3
.B M: .B M:
Scale by 1,048,576 (MiB) Scale by 1,000,000 (MB)
.PP .PP
.IP \(bu 3 .IP \(bu 3
.B G: .B G:
Scale by 1,073,741,824 (GiB) Scale by 1,000,000,000 (GB)
.PP .PP
.IP \(bu 3 .IP \(bu 3
.B T: .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) Scale by 1,099,511,627,776 (TiB)
.PP .PP
.RE .RE
+59 -29
View File
@@ -265,6 +265,7 @@ static struct {
unsigned long unit; unsigned long unit;
char unit_suffix; char unit_suffix;
bool unit_specified; bool unit_specified;
bool unit_iec;
} opts; } opts;
/* Per CHPID run-time data. */ /* Per CHPID run-time data. */
@@ -336,6 +337,7 @@ static void init_opts(void)
opts.unit = UNIT_AUTO; opts.unit = UNIT_AUTO;
opts.unit_suffix = 0; opts.unit_suffix = 0;
opts.unit_specified = false; 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. * 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; unsigned long unit;
char *endptr; char *endptr;
if (strlen(arg) == 1) { if (suffix_to_unit(arg, &unit, &opts.unit_suffix, &opts.unit_iec))
opts.unit_suffix = (char)toupper(*arg); return unit;
switch (opts.unit_suffix) { if (strcmp(arg, "auto-iec") == 0) {
case 'K': opts.unit_iec = true;
return UNIT_BIN; return UNIT_AUTO;
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;
}
} }
opts.unit_suffix = 0;
if (strcmp(arg, "auto") == 0) if (strcmp(arg, "auto") == 0)
return UNIT_AUTO; return UNIT_AUTO;
/* Parse as number. */ /* Parse as number. */
@@ -1278,7 +1301,7 @@ static struct util_rec *define_table(void)
rec = util_rec_new_wide(NULL); rec = util_rec_new_wide(NULL);
column_for_each_selected(col) { column_for_each_selected(col) {
util_rec_def(rec, col->name, UTIL_REC_ALIGN_RIGHT, util_rec_def(rec, col->name, UTIL_REC_ALIGN_RIGHT,
(int)col->width, ""); (int)col->width + (int)col->pad, "");
} }
return rec; 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, static void add_pair_value(struct util_rec *table, struct column_t *col,
struct cmg_pair_t *pair) struct cmg_pair_t *pair)
{ {
char suffix = 0, str[16]; char suffix = 0, suffix2 = opts.unit_iec ? 'i' : 0, str[16];
int p; int p;
if (!pair->valid) { 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); suffix = scale_auto(pair, UNIT_DEC);
} else if (pair->unit == CMG_BPS) { } else if (pair->unit == CMG_BPS) {
if (opts.unit == UNIT_AUTO) if (opts.unit == UNIT_AUTO)
suffix = scale_auto(pair, UNIT_BIN); suffix = scale_auto(pair, opts.unit_iec ? UNIT_BIN : UNIT_DEC);
else else
scale_fixed(pair, opts.unit); scale_fixed(pair, opts.unit);
} }
switch (pair->type) { switch (pair->type) {
case CMG_U32: 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; break;
case CMG_U64: 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; break;
case CMG_FLOAT: case CMG_FLOAT:
/* Find highest precision that fits into @width characters. */ /* Find highest precision that fits into @width characters. */
for (p = 2; p >= 0; p--) { for (p = 2; p >= 0; p--) {
snprintf(str, sizeof(str), "%.*f%c", p, snprintf(str, sizeof(str), "%.*f%c%c", p,
pair->value_double, suffix); pair->value_double, suffix, suffix2);
if (strlen(str) <= col->width) if (strlen(str) <= col->width)
break; break;
} }
@@ -1539,11 +1564,15 @@ static void print_table_header(void)
{ {
struct column_t *col; struct column_t *col;
unsigned int i, next_i, num, width, hdr_width; 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. */ /* Print first header line and update column width based on heading. */
for (i = 0; (col = column_get_by_index(i, true)); i = next_i) { for (i = 0; (col = column_get_by_index(i, true)); i = next_i) {
if (i > 0) if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0) {
printf(" "); col->pad = 1;
printf("%*s", col->pad + 1, "");
}
last_hdr1 = col->hdr1_group;
get_hdr_group_size(i, &num, &width); get_hdr_group_size(i, &num, &width);
if (num == 1) { if (num == 1) {
/* Update column width in case heading is wider. */ /* Update column width in case heading is wider. */
@@ -1571,7 +1600,7 @@ static void print_table_header(void)
i = 0; i = 0;
column_for_each_selected(col) { column_for_each_selected(col) {
if (i++ > 0) if (i++ > 0)
printf(" "); printf("%*s", col->pad + 1, "");
printf("%*s", col->width, col->hdr2); printf("%*s", col->width, col->hdr2);
} }
printf("\n"); printf("\n");
@@ -1667,8 +1696,8 @@ static void calc_column_widths(void)
width = 4; /* "999K" */ width = 4; /* "999K" */
} else { } else {
v = MAX_BPS / (double)opts.unit; v = MAX_BPS / (double)opts.unit;
snprintf(str, sizeof(str) - 1, "%.0f%c", v, snprintf(str, sizeof(str) - 1, "%.0f%c%c", v,
opts.unit_suffix); opts.unit_suffix, opts.unit_iec ? 'i' : 0);
width = strlen_u(str); width = strlen_u(str);
} }
break; break;
@@ -1678,7 +1707,7 @@ static void calc_column_widths(void)
col->width = MAX(col->width, width); col->width = MAX(col->width, width);
/* Double space between groups. */ /* Double space between groups. */
if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0) if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0)
col->width++; col->pad++;
last_hdr1 = col->hdr1_group; last_hdr1 = col->hdr1_group;
} }
} }
@@ -1693,7 +1722,8 @@ static void cmd_table(void)
apply_column_selection(); apply_column_selection();
calc_column_widths(); 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"); printf("Collecting initial utilization data\n");
update_util_all(true); update_util_all(true);
+8 -4
View File
@@ -281,13 +281,17 @@ static void calc_ext_metrics(struct cmg_data_t *data, u32 ticks)
if (cmcb->dpu_num_cores == 0) if (cmcb->dpu_num_cores == 0)
return; 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); 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); 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 / /* dpu_util_part = dpu_util_total * channel_work_units /
* channel_work_units_cpc */ * channel_work_units_cpc */
+8 -4
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) if (cmcb->dpu_num_cores == 0)
return; 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); 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); 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 / /* dpu_util_part = dpu_util_total * channel_work_units /
* channel_work_units_cpc */ * channel_work_units_cpc */
+35 -2
View File
@@ -53,6 +53,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_TYPE, COL_TYPE,
@@ -63,6 +64,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_CMG, COL_CMG,
@@ -73,6 +75,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_SPEED, COL_SPEED,
@@ -83,6 +86,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_SHARED, COL_SHARED,
@@ -93,6 +97,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_INTERVAL, COL_INTERVAL,
@@ -103,6 +108,7 @@ static struct column_t columns[] = {
HDR1_CHP_SINGLE, HDR1_CHP_SINGLE,
HDR1_CHP_GROUP, HDR1_CHP_GROUP,
0, 0,
0,
}, },
{ {
COL_UTIL_PART, COL_UTIL_PART,
@@ -112,6 +118,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_UTIL_SINGLE, HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP, HDR1_UTIL_GROUP,
5,
0, 0,
}, },
{ {
@@ -122,6 +129,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_UTIL_SINGLE, HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP, HDR1_UTIL_GROUP,
5,
0, 0,
}, },
{ {
@@ -133,6 +141,7 @@ static struct column_t columns[] = {
HDR1_UTIL_SINGLE, HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP, HDR1_UTIL_GROUP,
0, 0,
0,
}, },
{ {
COL_UTIL_DATA, COL_UTIL_DATA,
@@ -143,6 +152,7 @@ static struct column_t columns[] = {
HDR1_UTIL_SINGLE, HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP, HDR1_UTIL_GROUP,
0, 0,
0,
}, },
{ {
COL_READ_PART, COL_READ_PART,
@@ -152,6 +162,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_READ_SINGLE, HDR1_READ_SINGLE,
HDR1_READ_GROUP, HDR1_READ_GROUP,
5,
0, 0,
}, },
{ {
@@ -162,6 +173,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_READ_SINGLE, HDR1_READ_SINGLE,
HDR1_READ_GROUP, HDR1_READ_GROUP,
5,
0, 0,
}, },
{ {
@@ -172,6 +184,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_WRITE_SINGLE, HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP, HDR1_WRITE_GROUP,
5,
0, 0,
}, },
{ {
@@ -182,6 +195,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_WRITE_SINGLE, HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP, HDR1_WRITE_GROUP,
5,
0, 0,
}, },
{ {
@@ -193,6 +207,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE, HDR1_FICON_SINGLE,
HDR1_FICON_GROUP, HDR1_FICON_GROUP,
0, 0,
0,
}, },
{ {
COL_FICON_ACTIVE, COL_FICON_ACTIVE,
@@ -203,6 +218,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE, HDR1_FICON_SINGLE,
HDR1_FICON_GROUP, HDR1_FICON_GROUP,
0, 0,
0,
}, },
{ {
COL_FICON_DEFER, COL_FICON_DEFER,
@@ -213,6 +229,7 @@ static struct column_t columns[] = {
HDR1_FICON_SINGLE, HDR1_FICON_SINGLE,
HDR1_FICON_GROUP, HDR1_FICON_GROUP,
0, 0,
0,
}, },
{ {
COL_HPF_RATE, COL_HPF_RATE,
@@ -223,6 +240,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE, HDR1_HPF_SINGLE,
HDR1_HPF_GROUP, HDR1_HPF_GROUP,
0, 0,
0,
}, },
{ {
COL_HPF_ACTIVE, COL_HPF_ACTIVE,
@@ -233,6 +251,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE, HDR1_HPF_SINGLE,
HDR1_HPF_GROUP, HDR1_HPF_GROUP,
0, 0,
0,
}, },
{ {
COL_HPF_DEFER, COL_HPF_DEFER,
@@ -243,6 +262,7 @@ static struct column_t columns[] = {
HDR1_HPF_SINGLE, HDR1_HPF_SINGLE,
HDR1_HPF_GROUP, HDR1_HPF_GROUP,
0, 0,
0,
}, },
{ {
COL_MSG_RATE_PART, COL_MSG_RATE_PART,
@@ -252,6 +272,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_MSGR_SINGLE, HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP, HDR1_MSGR_GROUP,
5,
0, 0,
}, },
{ {
@@ -262,6 +283,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_MSGR_SINGLE, HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP, HDR1_MSGR_GROUP,
5,
0, 0,
}, },
{ {
@@ -272,6 +294,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_MSGSZ_SINGLE, HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP, HDR1_MSGSZ_GROUP,
5,
0, 0,
}, },
{ {
@@ -282,6 +305,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_MSGSZ_SINGLE, HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP, HDR1_MSGSZ_GROUP,
5,
0, 0,
}, },
{ {
@@ -293,6 +317,7 @@ static struct column_t columns[] = {
"SNDFAIL", "SNDFAIL",
"SNDFAIL", "SNDFAIL",
0, 0,
0,
}, },
{ {
COL_RCV_FAIL_PART, COL_RCV_FAIL_PART,
@@ -302,6 +327,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_MSG_RCVF_SINGLE, HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP, HDR1_MSG_RCVF_GROUP,
5,
0, 0,
}, },
{ {
@@ -312,6 +338,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_MSG_RCVF_SINGLE, HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP, HDR1_MSG_RCVF_GROUP,
5,
0, 0,
}, },
{ {
@@ -323,6 +350,7 @@ static struct column_t columns[] = {
HDR1_DPU_UTIL_SINGLE, HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP, HDR1_DPU_UTIL_GROUP,
0, 0,
0,
}, },
{ {
COL_DPU_UTIL, COL_DPU_UTIL,
@@ -333,6 +361,7 @@ static struct column_t columns[] = {
HDR1_DPU_UTIL_SINGLE, HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP, HDR1_DPU_UTIL_GROUP,
0, 0,
0,
}, },
{ {
COL_DPU_UTIL_PART, COL_DPU_UTIL_PART,
@@ -342,6 +371,7 @@ static struct column_t columns[] = {
"PART", "PART",
HDR1_DPU_UTIL_SINGLE, HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP, HDR1_DPU_UTIL_GROUP,
5,
0, 0,
}, },
{ {
@@ -352,6 +382,7 @@ static struct column_t columns[] = {
"TOTAL", "TOTAL",
HDR1_DPU_UTIL_SINGLE, HDR1_DPU_UTIL_SINGLE,
HDR1_DPU_UTIL_GROUP, HDR1_DPU_UTIL_GROUP,
5,
0, 0,
}, },
}; };
@@ -447,15 +478,17 @@ void column_select_all(void)
column_select(col); 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; struct column_t *col;
char *str; char *str;
if (auto_scale) if (auto_scale)
util_asprintf(&str, "(B/s)"); util_asprintf(&str, "(B/s)");
else if (suffix_char) else if (suffix_char && iec)
util_asprintf(&str, "(%ciB/s)", suffix_char); util_asprintf(&str, "(%ciB/s)", suffix_char);
else if (suffix_char)
util_asprintf(&str, "(%cB/s)", suffix_char);
else else
str = util_strdup("(*)"); str = util_strdup("(*)");
+2 -1
View File
@@ -66,6 +66,7 @@ struct column_t {
const char *hdr1_single; const char *hdr1_single;
char *hdr1_group; char *hdr1_group;
unsigned int width; unsigned int width;
unsigned int pad;
}; };
#define column_for_each(c) \ #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_default(void);
void column_select_all(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); void column_exit(void);
#endif /* COLUMN_H */ #endif /* COLUMN_H */
+1 -1
View File
@@ -9,7 +9,7 @@
# Copy zdev persistent config from initrd to root-fs. # 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, # Apart from debugging purposes, this is useful for distro installers,
# which can import the early config into their own environment early # which can import the early config into their own environment early
+10 -1
View File
@@ -22,7 +22,7 @@
. ds pfont \fP . ds pfont \fP
. nh . nh
. na . na
. ft CW . ft CR
\\$* \\$*
. ft \\*[pfont] . ft \\*[pfont]
. ad . 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). functional (such as in the early phase of an initial RAM disk).
.PP .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" "" .OD persistent "p" ""
Apply changes to persistent configuration only. Apply changes to persistent configuration only.
+1 -1
View File
@@ -22,7 +22,7 @@
. ds pfont \fP . ds pfont \fP
. nh . nh
. na . na
. ft CW . ft CR
\\$* \\$*
. ft \\*[pfont] . ft \\*[pfont]
. ad . ad
+9
View File
@@ -109,6 +109,7 @@ struct options {
unsigned int quiet:1; unsigned int quiet:1;
unsigned int no_settle:1; unsigned int no_settle:1;
unsigned int site_id; unsigned int site_id;
unsigned int no_module_load:1;
}; };
/* Makefile converts chzdev_usage.txt into C file which we include here. */ /* 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_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__), OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_SITE = 's', OPT_SITE = 's',
OPT_NO_MODULE_LOAD = (OPT_ANONYMOUS_BASE+__COUNTER__),
}; };
static struct opts_conflict conflict_list[] = { static struct opts_conflict conflict_list[] = {
@@ -260,6 +262,7 @@ static const struct option opt_list[] = {
{ "quiet", no_argument, NULL, OPT_QUIET }, { "quiet", no_argument, NULL, OPT_QUIET },
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE }, { "no-settle", no_argument, NULL, OPT_NO_SETTLE },
{ "site", required_argument, NULL, OPT_SITE }, { "site", required_argument, NULL, OPT_SITE },
{ "no-module-load", no_argument, NULL, OPT_NO_MODULE_LOAD },
{ NULL, no_argument, NULL, 0 }, { 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; opts->no_settle = 1;
break; break;
case OPT_NO_MODULE_LOAD:
/* --no-module-load */
opts->no_module_load = 1;
break;
case OPT_SITE: case OPT_SITE:
/* --site */ /* --site */
/* 1. User can specify only site-ids from 0 to 9 /* 1. User can specify only site-ids from 0 to 9
@@ -3177,6 +3185,7 @@ int main(int argc, char *argv[])
dryrun = opts.dryrun; dryrun = opts.dryrun;
udev_no_settle = opts.no_settle; udev_no_settle = opts.no_settle;
path_set_base(opts.base); path_set_base(opts.base);
module_load_suppress(opts.no_module_load);
if (dryrun) if (dryrun)
info("Starting dry-run, configuration will not be changed\n"); info("Starting dry-run, configuration will not be changed\n");
+1
View File
@@ -57,6 +57,7 @@ OPTIONS
--dry-run Display changes without applying --dry-run Display changes without applying
--base PATH Use PATH as base for accessing files --base PATH Use PATH as base for accessing files
--no-settle Do not wait for udev to settle --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 --auto-conf Apply changes to auto-configuration only
-s, --site ID Apply changes to the specified site only -s, --site ID Apply changes to the specified site only
--is-owner FILE Examine whether the file is generated by zdev --is-owner FILE Examine whether the file is generated by zdev
+61 -3
View File
@@ -29,6 +29,7 @@
#include "export.h" #include "export.h"
#include "firmware.h" #include "firmware.h"
#include "misc.h" #include "misc.h"
#include "path.h"
#include "qeth.h" #include "qeth.h"
#include "subtype.h" #include "subtype.h"
#include "zfcp_host.h" #include "zfcp_host.h"
@@ -143,6 +144,13 @@ struct fw_qeth {
char settings[]; char settings[];
} __packed; } __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. */ /* Emit a warning that refers to a position in a firmware file. */
static void fwwarn(struct fw_file *f, const char *fmt, ...) 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 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. */ /* Parse a DASD device entry. */
static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config, static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config,
struct util_list *objects) struct util_list *objects)
{ {
struct fw_dasd *dasd = (struct fw_dasd *) de; struct fw_dasd *dasd = (struct fw_dasd *) de;
struct ccw_devid devid; struct ccw_devid devid;
struct device *dev_eckd, *dev_fba; struct device *dev_eckd = NULL, *dev_fba = NULL;
char *id; char *id;
if (!check_de_size(f, de, sizeof(struct fw_dasd))) 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 */ /* Could be either dasd_eckd or dasd_fba - add both entries */
io_to_ccw(&devid, &dasd->id); io_to_ccw(&devid, &dasd->id);
id = ccw_devid_to_str(&devid); 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); free(id);
if (dasd->hdr.len > sizeof(struct fw_dasd)) { if (dasd->hdr.len > sizeof(struct fw_dasd)) {
+1 -1
View File
@@ -28,7 +28,7 @@ static int suppress_module_load;
/* Allow suppression of module loading. */ /* Allow suppression of module loading. */
void module_load_suppress(int state) void module_load_suppress(int state)
{ {
suppress_module_load = 1; suppress_module_load = state;
} }
/* Check if a module is currently loaded. */ /* Check if a module is currently loaded. */
+44 -9
View File
@@ -25,7 +25,7 @@
# generates persistent configuration that can be transferred from initrd to # generates persistent configuration that can be transferred from initrd to
# another root files system such as in a distro installer environment. # 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() { zdev_parse_dasd_list() {
sed 's/,/\n/g' | while read -r _zdev_dasditem; do sed 's/,/\n/g' | while read -r _zdev_dasditem; do
@@ -41,7 +41,7 @@ zdev_parse_dasd_list() {
echo "rd.dasd ...,${_zdev_dasditem},... :" echo "rd.dasd ...,${_zdev_dasditem},... :"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
chzdev dasd --type "${_zdev_dasditem}=1" --persistent \ chzdev dasd --type "${_zdev_dasditem}=1" --persistent \
$zdev_dasd_base_args $zdev_dasd_base_args --force
;; ;;
"") continue ;; # empty range "") continue ;; # empty range
*) # currently no support for a device-spec "ipldev", only devbusid *) # currently no support for a device-spec "ipldev", only devbusid
@@ -72,15 +72,50 @@ EOF
$_zdev_dasd_features $_zdev_dasd_features
EOF EOF
fi fi
# Without dynamic (active) config zdev cannot infer # 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},... :" echo "rd.dasd ...,${_zdev_dasditem},... :"
# shellcheck disable=SC2086
chzdev dasd-eckd --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \ # free the dev-ids from blacklist to analyse the modalias
$zdev_dasd_base_args echo "free $_zdev_dasd_range" > /proc/cio_ignore
# shellcheck disable=SC2086 echo 1 > /proc/cio_settle
chzdev dasd-fba --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \
$zdev_dasd_base_args # 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 esac
done # input redir w/ process substitution causes syntax error in dracut env done # input redir w/ process substitution causes syntax error in dracut env
+3 -14
View File
@@ -33,6 +33,7 @@
#include "lib/libzds.h" #include "lib/libzds.h"
#include "lib/util_libc.h" #include "lib/util_libc.h"
#include "lib/util_str.h"
#include "lib/zt_common.h" #include "lib/zt_common.h"
#define COMP "zdsfs: " #define COMP "zdsfs: "
@@ -1369,18 +1370,6 @@ static void zdsfs_process_device_file(const char *devfile)
free(buffer); 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) static void zdsfs_process_config_file(const char *config)
{ {
char line[MAX_LINE_LENGTH]; char line[MAX_LINE_LENGTH];
@@ -1403,7 +1392,7 @@ static void zdsfs_process_config_file(const char *config)
/* remove all whitespaces */ /* remove all whitespaces */
tmp = util_malloc(strlen(line) + 1); tmp = util_malloc(strlen(line) + 1);
remove_whitespace(line, tmp); util_str_rm_whitespace(line, tmp);
key = strtok(tmp, delimiter); key = strtok(tmp, delimiter);
if (strcmp(key, "restserver") == 0) { if (strcmp(key, "restserver") == 0) {
@@ -1496,7 +1485,7 @@ static int zdsfs_process_dataset_conf(const char *config)
linecount++; linecount++;
/* remove all whitespaces */ /* remove all whitespaces */
tmp = util_malloc(strlen(line) + 1); tmp = util_malloc(strlen(line) + 1);
remove_whitespace(line, tmp); util_str_rm_whitespace(line, tmp);
/* skip empty lines */ /* skip empty lines */
if (*tmp == '\n' || *tmp == '#') { if (*tmp == '\n' || *tmp == '#') {
free(tmp); free(tmp);
+3 -1
View File
@@ -36,8 +36,10 @@ static int open_dump_file(void)
mount_point = util_strdup("/tmp/zdump-ngdump-XXXXXX"); mount_point = util_strdup("/tmp/zdump-ngdump-XXXXXX");
/* Create a mount point directory */ /* 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; goto fail_free;
}
if (mount(l.device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL)) { if (mount(l.device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL)) {
warnx("Could not mount \"%s\" (%s)", l.device, strerror(errno)); warnx("Could not mount \"%s\" (%s)", l.device, strerror(errno));
+19 -3
View File
@@ -42,11 +42,19 @@ static int read_meta_from_file(const char *filename, struct ngdump_meta *meta)
FILE *fp = NULL; FILE *fp = NULL;
char *line = NULL; char *line = NULL;
util_log_print(UTIL_LOG_TRACE,
"%s: Reading meta file \"%s\"\n",
__func__, filename);
memset(meta, 0, sizeof(*meta)); memset(meta, 0, sizeof(*meta));
fp = fopen(filename, "r"); 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; return -1;
}
while (fscanf(fp, "%m[^\n]\n", &line) == 1) { while (fscanf(fp, "%m[^\n]\n", &line) == 1) {
char *ptr, *param = NULL, *value = NULL; 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 * that the given partition is a valid NGDump partition but with no
* dump present. * dump present.
*/ */
if (!meta->file) if (!meta->file) {
util_log_print(UTIL_LOG_TRACE,
"%s: No dump file present\n", __func__);
return 0; return 0;
}
if (!meta->sha256sum) { if (!meta->sha256sum) {
warnx("Invalid NGDump SHA256 checksum"); warnx("Invalid NGDump SHA256 checksum");
return -1; return -1;
@@ -189,13 +200,18 @@ int ngdump_read_meta_from_device(const char *device, struct ngdump_meta *meta)
/* Create a mount point directory */ /* Create a mount point directory */
if (mkdtemp(mount_point) == NULL) { if (mkdtemp(mount_point) == NULL) {
warnx("Could not create directory \"%s\"", mount_point);
rc = -1; rc = -1;
goto out; goto out;
} }
rc = mount(device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL); 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; goto out_rmdir;
}
util_asprintf(&filename, "%s/%s", mount_point, NGDUMP_META_FILENAME); util_asprintf(&filename, "%s/%s", mount_point, NGDUMP_META_FILENAME);
+16 -74
View File
@@ -13,7 +13,8 @@
#include "boot/s390.h" #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 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ /* 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 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
}; };
static unsigned char ebcdic_500[256] = { /* Convert 'ebcdic' table from code page 037 to code page 500 if necessary */
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ void ebcdic_update_table(void)
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, {
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ if (!is_zvm()) {
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, ebcdic[74] = 0x5B; /* ¢ -> [ */
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC ebcdic[79] = 0x21; /* | -> ! */
-ENP ->LF */ ebcdic[90] = 0x5D; /* ! -> ] */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, ebcdic[95] = 0x5E; /* ¬ -> ^ */
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB ebcdic[176] = 0xA2; /* ^ -> ¢ */
-IUS */ ebcdic[186] = 0xAC; /* [ -> ¬ */
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, ebcdic[187] = 0x7C; /* ] -> | */
/* 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
};
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source, void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
unsigned int l) unsigned int l)
{ {
unsigned char *ebc;
unsigned int i; unsigned int i;
ebc = is_zvm() ? ebcdic_037 : ebcdic_500;
for (i = 0; i < l; i++) for (i = 0; i < l; i++)
target[i] = ebc[source[i]]; target[i] = ebcdic[source[i]];
} }
+1
View File
@@ -14,6 +14,7 @@
#ifndef __ASSEMBLER__ #ifndef __ASSEMBLER__
void ebcdic_update_table(void);
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source, void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
unsigned int l); unsigned int l);
+1 -1
View File
@@ -363,7 +363,7 @@ static char *number(char *buf, char *end, unsigned long val,
/* /*
* vsnprintf - Format string and place in a buffer * 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. * C standard, i.e.
* specifiers: * specifiers:
* * %s (strings) * * %s (strings)
+36 -18
View File
@@ -59,7 +59,7 @@ static int menu_read(void)
if (is_zvm()) if (is_zvm())
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
temp_area[i] = ebcdic_tolower(temp_area[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) && if ((endptr != temp_area) && (value < BOOT_MENU_ENTRIES - 1) &&
(__stage2_params.config[value] != 0)) { (__stage2_params.config[value] != 0)) {
@@ -68,6 +68,7 @@ static int menu_read(void)
} else { } else {
/* no valid config retry */ /* no valid config retry */
printf(msg_econfig); printf(msg_econfig);
memset(temp_area, 0, PAGE_SIZE);
count = 0; count = 0;
} }
} }
@@ -97,6 +98,13 @@ static int menu_list(void)
return 0; return 0;
} }
enum param_result {
NUMBER_FOUND = 0,
PRINT_PROMPT = 1,
NOTHING_FOUND = 2,
SCLP_ERROR = 3,
};
/* /*
* Interpret loadparm * Interpret loadparm
* *
@@ -107,15 +115,20 @@ static int menu_list(void)
* 0 - found number to boot, stored in value * 0 - found number to boot, stored in value
* 1 - print prompt * 1 - print prompt
* 2 - nothing found * 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 loadparm[PARAM_SIZE];
char *endptr; char *endptr;
int i; int i;
if (!sclp_param(loadparm)) /* try to fetch loadparms from sclp into 'loadparm' */
*value = ebcdic_strtoul(loadparm, &endptr, 10); if (sclp_param(loadparm) != 0)
return SCLP_ERROR;
/* parse number from loadparm */
*value = ebcdic_strtoul(loadparm, &endptr, 10);
/* got number, done */ /* got number, done */
if (endptr != loadparm) if (endptr != loadparm)
@@ -127,39 +140,44 @@ static int menu_param(unsigned long *value)
while ((i < PARAM_SIZE) && ecbdic_isspace(loadparm[i])) while ((i < PARAM_SIZE) && ecbdic_isspace(loadparm[i]))
i++; i++;
if (!strncmp(&loadparm[i], "PROMPT", 6)) { if (!strncmp(&loadparm[i], "PROMPT", 6))
*value = 0;
return PRINT_PROMPT; return PRINT_PROMPT;
}
return NOTHING_FOUND; return NOTHING_FOUND;
} }
int menu(void) int menu(void)
{ {
unsigned long value = 0; enum { DEFAULT_MENU_ENTRY = 0 };
unsigned long value = DEFAULT_MENU_ENTRY;
char *cmd_line_extra; char *cmd_line_extra;
char endstring[15]; char endstring[15];
int rc;
cmd_line_extra = (char *)COMMAND_LINE_EXTRA; cmd_line_extra = (char *)COMMAND_LINE_EXTRA;
memset(cmd_line_extra, 0, COMMAND_LINE_EXTRA_SIZE); memset(cmd_line_extra, 0, COMMAND_LINE_EXTRA_SIZE);
rc = sclp_setup(SCLP_INIT); if (sclp_setup(SCLP_INIT) != 0) {
if (rc)
/* sclp setup failed boot default */ /* sclp setup failed boot default */
goto boot; goto boot;
}
rc = menu_param(&value); switch (menu_param(&value)) {
if (rc == 0) { case NUMBER_FOUND:
/* got number from loadparm, boot it */ /* got number from loadparm, boot it */
goto boot; goto boot;
} else if (rc == 1 && value == 0) { case PRINT_PROMPT:
/* keyword "prompt", show menu */ /* print menu */
} else if (__stage2_params.flag == 0) { break;
/* menu disabled, boot default */ case SCLP_ERROR:
value = 0; /* failed to read from sclp, boot default */
printf("SCLP_ERROR\n");
goto boot; goto boot;
case NOTHING_FOUND:
if (__stage2_params.flag == 0) {
/* menu disabled, boot default */
goto boot;
}
break;
} }
/* print banner */ /* print banner */
-4
View File
@@ -18,10 +18,6 @@
#define PARAM_SIZE 8 #define PARAM_SIZE 8
#define TEXT_OFFSET 4 #define TEXT_OFFSET 4
#define NUMBER_FOUND 0
#define PRINT_PROMPT 1
#define NOTHING_FOUND 2
struct boot_stage2_params { struct boot_stage2_params {
uint16_t flag; uint16_t flag;
uint16_t timeout; uint16_t timeout;
+8 -2
View File
@@ -507,6 +507,8 @@ void start(void)
if (secure_boot_enabled()) if (secure_boot_enabled())
verify_secure_boot(); verify_secure_boot();
ebcdic_update_table();
/* /*
* cut the kernel header * cut the kernel header
*/ */
@@ -525,8 +527,12 @@ void start(void)
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr; *(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len; *(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(); kdump_stage3();
+2 -1
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 # Create a file containing meta information for zgetdump
# #
save_meta save_meta
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
save_logs
cleanup cleanup
exit 0 exit 0
+39 -26
View File
@@ -68,24 +68,33 @@ typedef enum {
undefined undefined
} definition_t; } definition_t;
/* Disk information type */ /* Physical disk information type */
struct disk_info { struct disk_info {
dev_t disk;
disk_type_t type; disk_type_t type;
dev_t device; /* logical device for bootmap creation */
dev_t partition; dev_t partition;
int devno; int devno;
int partnum; int partnum;
int phy_block_size; int phy_block_size;
int fs_block_size;
uint64_t phy_blocks; uint64_t phy_blocks;
struct hd_geometry geo; struct hd_geometry geo;
char* name;
char* drv_name;
definition_t targetbase_def; definition_t targetbase_def;
int is_nvme; 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 { struct file_range {
@@ -95,51 +104,55 @@ struct file_range {
struct job_target_data; struct job_target_data;
int disk_get_info(const char *device, struct job_target_data *target, int device_get_info(const char *device, struct job_target_data *target,
struct disk_info **info); struct device_info **info);
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_id);
int disk_is_tape(const char *device); int disk_is_tape(const char *device);
int disk_type_is_scsi(struct disk_ext_type *ext_type); 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_eckd_ldl(struct disk_ext_type *ext_type);
int disk_type_is_nvme(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_type_is_eckd(disk_type_t type);
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 *info);
int disk_get_info_from_file(const char* filename, int device_get_info_from_file(const char *filename,
struct job_target_data* target, struct job_target_data *target,
struct disk_info** info); struct device_info **info);
void disk_free_info(struct disk_info* info); void device_free_info(struct device_info *info);
char* disk_get_type_name(disk_type_t type); char *disk_get_type_name(disk_type_t type);
char *disk_get_ipl_type(disk_type_t type, int is_dump); char *disk_get_ipl_type(disk_type_t type, int is_dump);
int disk_is_large_volume(struct disk_info* info); int disk_is_large_volume(struct disk_info* info);
int disk_cyl_from_blocknum(blocknum_t blocknum, 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_head_from_blocknum(blocknum_t blocknum, struct disk_info* info);
int disk_sec_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, void disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
struct disk_info* info); struct disk_info *info);
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount, int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
disk_blockptr_t *block, struct disk_info *info); 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, blocknum_t disk_write_block_buffer(struct misc_fd *fd, int fd_is_basedisk,
const void* buffer, size_t bytecount, const void* buffer, size_t bytecount,
disk_blockptr_t** blocklist, 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, blocknum_t disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
const void *buffer, size_t bytecount, const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist, disk_blockptr_t **blocklist,
int fs_block_size,
struct disk_info *info, int align, struct disk_info *info, int align,
off_t *offset); off_t *offset);
void disk_print_devt(dev_t d); void disk_print_devt(dev_t d);
void disk_print_devname(dev_t d); void disk_print_devname(dev_t d);
void prepare_footnote_ptr(int source, char *ptr); void prepare_footnote_ptr(int source, char *ptr);
void print_footnote_ref(int source, const char *prefix); void print_footnote_ref(int source, const char *prefix);
void disk_print_info(struct disk_info *info, int source); 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); 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, 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, blocknum_t disk_get_blocklist_from_file(const char* filename,
struct file_range *reg, struct file_range *reg,
disk_blockptr_t** blocklist, disk_blockptr_t **blocklist,
struct disk_info* pinfo); int fs_block_size,
struct disk_info *info);
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name); 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); int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
+45 -10
View File
@@ -16,6 +16,7 @@
#include "job.h" #include "job.h"
#include "misc.h" #include "misc.h"
#include "zipl.h" #include "zipl.h"
#include "boot.h"
#include "boot/boot_defs.h" #include "boot/boot_defs.h"
#define NR_PROGRAM_TABLES NR_BLKPTR_FORMATS #define NR_PROGRAM_TABLES NR_BLKPTR_FORMATS
@@ -52,6 +53,11 @@ struct component_loc {
struct component_footer { struct component_footer {
component_type type; component_type type;
const char *desc; 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 { struct program_component {
@@ -66,18 +72,25 @@ struct program_table {
blocknum_t stage1b_count; blocknum_t stage1b_count;
}; };
/* Bootloader Installation Set */ /* A part of Bootloader Installation Set specific for an individual mirror */
struct install_set { struct install_set_mirror {
struct program_table tables[NR_PROGRAM_TABLES]; struct program_table tables[NR_PROGRAM_TABLES];
struct program_component *components[NR_PROGRAM_COMPONENTS]; 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; int nr_menu_entries;
struct misc_fd mfd; 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; char *filename;
unsigned int skip_prepare_device:1;
unsigned int tmp_filename_created:1; unsigned int tmp_filename_created:1;
unsigned int skip_prepare:1; struct device_info *info;
unsigned int print_details:1;
struct disk_info *info;
disk_blockptr_t scsi_dump_sb_blockptr; 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; 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, static inline struct program_component *get_component(struct install_set *bis,
int mirror_id,
int i, int j) 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 prepare_bootloader(struct job_data *job, struct install_set *bis);
int install_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); 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, int install_tapeloader(const char* device, const char* image,
const char* parmline, const char* ramdisk, const char* parmline, const char* ramdisk,
address_t image_addr, address_t parm_addr, 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, int install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_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, int install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, blocknum_t *stage1b_count,
disk_blockptr_t *stage2_list, 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); int rewind_tape(int fd);
#endif /* INSTALL_H */ #endif /* INSTALL_H */
+1
View File
@@ -34,6 +34,7 @@ int misc_asprintf(char **out, const char *fmt, ...);
void* misc_calloc(size_t n, size_t size); void* misc_calloc(size_t n, size_t size);
char* misc_strdup(const char* s); char* misc_strdup(const char* s);
int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate); 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(int fd, void* buffer, size_t count);
int misc_read_file(const char* filename, char** buffer, size_t* size, int misc_read_file(const char* filename, char** buffer, size_t* size,
int nil_terminate); int nil_terminate);
+1
View File
@@ -44,6 +44,7 @@
#define MAX_DUMP_VOLUMES 32 #define MAX_DUMP_VOLUMES 32
#define MAX_TARGETS 32 #define MAX_TARGETS 32
#define FIRST_MIRROR_ID 0
#define SECURE_BOOT_UNDEFINED -1 #define SECURE_BOOT_UNDEFINED -1
#define SECURE_BOOT_DISABLED 0 #define SECURE_BOOT_DISABLED 0
@@ -104,5 +104,7 @@ logsave "$MNTDIR/makedumpfile.log" makedumpfile -f -E --message-level 7 -d 31 "$
# Create a file containing meta information for zgetdump # Create a file containing meta information for zgetdump
# #
save_meta save_meta
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
save_logs
quit quit
+422 -242
View File
File diff suppressed because it is too large Load Diff
+191 -194
View File
@@ -104,12 +104,13 @@ read_block_by_offset(int fd, int blksize, uint64_t offset, char *buffer)
return 0; 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) const struct stat *stats)
{ {
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
int fd, rc, shift, sb;
char *device; char *device;
char *buffer; char *buffer;
int fd, rc, shift, sb;
rc = 0; rc = 0;
buffer = (char *) malloc(data->phy_block_size); 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) { if (data->geo.heads == 15) {
/* assume DASD */ /* assume DASD */
data->partnum = stats->st_rdev & DASD_PARTN_MASK; 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) if (rc)
goto out_err; goto out_err;
@@ -167,7 +168,7 @@ static int determine_virtblk_type(struct disk_info *data,
} else { } else {
data->type = disk_type_scsi; data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK; 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: out_err:
@@ -195,6 +196,8 @@ static int set_target_parameters(FILE *fh, struct job_target_data *td)
char *error; char *error;
int i; int i;
/* reset array of target parameters */
memset(td->targets, 0, sizeof(td->targets));
/** /**
* Process a stream of 'key=value' pairs and distribute * Process a stream of 'key=value' pairs and distribute
* them into groups. * them into groups.
@@ -284,65 +287,28 @@ error:
return -1; 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 * Set disk info using ready target parameters provided either by
* user, or by script * user, or by script
*/ */
static int disk_set_info_by_hint(struct job_target_data *td, static int device_set_info_by_hint(struct job_target_data *td,
struct disk_info *data, int fd) struct device_info *data, int fd)
{ {
int majnum, minnum; int majnum, minnum;
struct stat stats; struct stat stats;
int i; 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); assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) { /* set devno, type, phy_block_size */
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;
for (i = 0; i < td->nr_targets; i++) { 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), if (sscanf(get_targetbase(td, i),
"%d:%d", &majnum, &minnum) == 2) { "%d:%d", &majnum, &minnum) == 2) {
data->basedisks[i] = makedev(majnum, minnum); data->base[i].disk = makedev(majnum, minnum);
defined_as = defined_as_device; data->base[i].targetbase_def = defined_as_device;
} else { } else {
if (stat(get_targetbase(td, i), &stats)) { if (stat(get_targetbase(td, i), &stats)) {
error_reason(strerror(errno)); error_reason(strerror(errno));
@@ -356,47 +322,51 @@ static int disk_set_info_by_hint(struct job_target_data *td,
get_targetbase(td, i)); get_targetbase(td, i));
return -1; return -1;
} }
data->basedisks[i] = stats.st_rdev; data->base[i].disk = stats.st_rdev;
defined_as = defined_as_name; data->base[i].targetbase_def = defined_as_name;
} }
if (data->targetbase_def != undefined && if (data->base[i].targetbase_def !=
data->targetbase_def != defined_as) { data->base[0].targetbase_def) {
error_reason("Target base disks are defined by different ways"); error_reason("Target base disks are defined by different ways");
return -1; 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; return 0;
} }
/** /**
* Calculate target parameters in the case when no hints were provided * 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) const struct stat *stats, int fd)
{ {
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
struct dasd_information dasd_info; struct dasd_information dasd_info;
if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) { if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) {
error_reason("Could not get blocksize"); error_reason("Could not get blocksize");
return -1; return -1;
} }
if (!data->drv_name) { if (!dev_info->drv_name) {
/* Driver name cannot be read */ /* Driver name cannot be read */
if (ioctl(fd, BIODASDINFO, &dasd_info)) { if (ioctl(fd, BIODASDINFO, &dasd_info)) {
data->devno = -1; data->devno = -1;
if (data->geo.start) { if (data->geo.start) {
/* SCSI partition */ /* SCSI partition */
data->type = disk_type_scsi; data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK; data->partnum =
data->device = stats->st_rdev & ~SCSI_PARTN_MASK; stats->st_rdev & SCSI_PARTN_MASK;
dev_info->device =
stats->st_rdev & ~SCSI_PARTN_MASK;
} else { } else {
/* SCSI disk */ /* SCSI disk */
data->type = disk_type_scsi; data->type = disk_type_scsi;
data->partnum = 0; data->partnum = 0;
data->device = stats->st_rdev; dev_info->device = stats->st_rdev;
} }
} else { } else {
/* DASD */ /* DASD */
@@ -404,9 +374,9 @@ static int disk_set_info_auto(struct disk_info *data,
if (disk_determine_dasd_type(data, dasd_info)) if (disk_determine_dasd_type(data, dasd_info))
return -1; return -1;
data->partnum = stats->st_rdev & DASD_PARTN_MASK; 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' */ /* Driver name is 'dasd' */
if (ioctl(fd, BIODASDINFO, &dasd_info)) { if (ioctl(fd, BIODASDINFO, &dasd_info)) {
error_reason("Could not determine DASD type"); 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)) if (disk_determine_dasd_type(data, dasd_info))
return -1; return -1;
data->partnum = stats->st_rdev & DASD_PARTN_MASK; 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_SD) == 0) { } else if (strcmp(dev_info->drv_name, UTIL_PROC_DEV_ENTRY_SD) == 0) {
/* Driver name is 'sd' */ /* Driver name is 'sd' */
data->devno = -1; data->devno = -1;
data->type = disk_type_scsi; data->type = disk_type_scsi;
data->partnum = stats->st_rdev & SCSI_PARTN_MASK; 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' */ /* Driver name is 'virtblk' */
if (ioctl(fd, HDIO_GETGEO, &data->geo) != 0) if (ioctl(fd, HDIO_GETGEO, &data->geo) != 0)
perror("Could not retrieve disk geometry information."); perror("Could not retrieve disk geometry information.");
if (ioctl(fd, BLKSSZGET, &data->phy_block_size) != 0) if (ioctl(fd, BLKSSZGET, &data->phy_block_size) != 0)
perror("Could not retrieve blocksize information."); 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 " error_reason("Virtblk device type not clearly "
"determined."); "determined.");
return -1; 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) { ioctl(fd, NVME_IOCTL_ID) >= 0) {
/* NVMe path, driver name is 'blkext' */ /* NVMe path, driver name is 'blkext' */
data->devno = -1; data->devno = -1;
@@ -444,18 +416,20 @@ static int disk_set_info_auto(struct disk_info *data,
data->is_nvme = 1; data->is_nvme = 1;
if (util_sys_dev_is_partition(stats->st_rdev)) { 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; return -1;
data->partnum = util_sys_get_partnum(stats->st_rdev); data->partnum = util_sys_get_partnum(stats->st_rdev);
if (data->partnum == -1) if (data->partnum == -1)
return -1; return -1;
} else { } else {
data->device = stats->st_rdev; dev_info->device = stats->st_rdev;
data->partnum = 0; data->partnum = 0;
} }
} else { } else {
/* Driver name is unknown */ /* 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 -1;
} }
return 0; return 0;
@@ -488,7 +462,7 @@ static void set_source_type(struct job_target_data *td,
td->source = source_auto; 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; 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 * Note: geo.start contains a sector number offset measured in
* physical blocks, not sectors (512 bytes) * physical blocks, not sectors (512 bytes)
*/ */
static int disk_set_geometry_by_hint(struct job_target_data *td, static int device_set_geometry_by_hint(struct job_target_data *td,
struct disk_info *data) struct device_info *data)
{ {
int i; 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); assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) { for (i = 0; i < td->nr_targets; i++) {
if (data->geo.heads != get_targetheads(td, i) || data->base[i].geo.heads = get_targetheads(td, i);
data->geo.sectors != get_targetsectors(td, i) || data->base[i].geo.sectors = get_targetsectors(td, i);
data->geo.cylinders != get_targetcylinders(td, i) || data->base[i].geo.cylinders = get_targetcylinders(td, i);
data->geo.start != get_targetoffset(td, i)) { data->base[i].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;
}
} }
return 0; return 0;
} }
@@ -603,7 +564,6 @@ static int disk_set_info_complete(struct job_target_data *td,
struct disk_info *data, struct disk_info *data,
struct stat *stats, int fd) struct stat *stats, int fd)
{ {
struct util_proc_part_entry part_entry;
long devsize; long devsize;
/* Get size of device in sectors (512 byte) */ /* 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); data->geo.start / (data->phy_block_size / 512);
if (data->partnum != 0) if (data->partnum != 0)
data->partition = stats->st_rdev; 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 */ /* Try to get device name */
if (util_proc_part_get_entry(data->device, &part_entry) == 0) { if (util_proc_part_get_entry(dev_info->device, &part_entry) == 0) {
data->name = misc_strdup(part_entry.name); dev_info->name = misc_strdup(part_entry.name);
util_proc_part_free_entry(&part_entry); util_proc_part_free_entry(&part_entry);
if (data->name == NULL) if (!dev_info->name)
return -1; return -1;
} }
/* Initialize file system block size with invalid value */ /* Initialize file system block size with invalid value */
data->fs_block_size = -1; dev_info->fs_block_size = -1;
return 0; 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 * To exclude any user assumptions about the DEVICE, this function
* should be called with TD pointing to a zeroed structure. * 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. * file system.
*/ */
int disk_get_info(const char *device, struct job_target_data *td, int device_get_info(const char *device, struct job_target_data *td,
struct disk_info **info) struct device_info **info)
{ {
char *script_file = NULL; char *script_file = NULL;
struct disk_info *data; struct device_info *data;
struct stat stats; struct stat stats;
int fd; int fd;
int i;
if (stat(device, &stats)) { if (stat(device, &stats)) {
error_reason(strerror(errno)); error_reason(strerror(errno));
@@ -683,10 +651,10 @@ int disk_get_info(const char *device, struct job_target_data *td,
error_reason(strerror(errno)); error_reason(strerror(errno));
return -1; return -1;
} }
data = (struct disk_info *)misc_malloc(sizeof(struct disk_info)); data = (struct device_info *)misc_malloc(sizeof(struct device_info));
if (!data) if (!data)
goto error; 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_driver_name(fd, data, stats.st_rdev);
set_source_type(td, data->drv_name, &script_file); set_source_type(td, data->drv_name, &script_file);
switch (td->source) { 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 */ /* target parameters were set by the script output */
assert(target_parameters_are_set(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; goto error;
if (disk_set_info_by_hint(td, data, fd)) if (device_set_info_by_hint(td, data, fd))
goto error; goto error;
data->device = stats.st_rdev; data->device = stats.st_rdev;
break; break;
@@ -709,20 +677,20 @@ int disk_get_info(const char *device, struct job_target_data *td,
*/ */
assert(target_parameters_are_set(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; goto error;
if (disk_set_info_by_hint(td, data, fd)) if (device_set_info_by_hint(td, data, fd))
goto error; goto error;
/* /*
* multiple base disks are not supported * multiple base disks are not supported
* with this source type * with this source type
*/ */
assert(td->nr_targets == 1); assert(td->nr_targets == 1);
data->device = data->basedisks[0]; data->device = data->base[FIRST_MIRROR_ID].disk;
break; break;
case source_auto: case source_auto:
/* no ready target parameters are available */ /* 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; goto error;
if (disk_set_info_auto(data, &stats, fd)) if (disk_set_info_auto(data, &stats, fd))
goto error; goto error;
@@ -730,13 +698,17 @@ int disk_get_info(const char *device, struct job_target_data *td,
* multiple base disks are not supported * multiple base disks are not supported
* with this source type * with this source type
*/ */
data->basedisks[0] = data->device; data->base[FIRST_MIRROR_ID].disk = data->device;
td->nr_targets = 1; td->nr_targets = 1;
break; break;
default: default:
assert(0); 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; goto error;
free(script_file); free(script_file);
close(fd); close(fd);
@@ -772,17 +744,20 @@ disk_is_tape(const char* device)
* partition, etc). In case of success the resulted disk type is * partition, etc). In case of success the resulted disk type is
* stored in EXT_TYPE. * 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 job_target_data tmp = {.source = source_unknown};
struct device_info *dev_info;
struct disk_info *info; struct disk_info *info;
if (disk_get_info(device, &tmp, &info)) if (device_get_info(device, &tmp, &dev_info))
return -1; return -1;
info = &dev_info->base[disk_idx];
ext_type->type = info->type; ext_type->type = info->type;
ext_type->is_nvme = info->is_nvme; ext_type->is_nvme = info->is_nvme;
disk_free_info(info); device_free_info(dev_info);
free_target_data(&tmp); free_target_data(&tmp);
return 0; 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 * 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 blocksize;
int fd; int fd;
@@ -830,7 +805,7 @@ int disk_info_set_fs_block(const char *filename, struct disk_info *info)
filename); filename);
return -1; return -1;
} }
info->fs_block_size = blocksize; dinfo->fs_block_size = blocksize;
return 0; 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 * Retrieve disk info of the device which contains FILENAME
* and set the filesystem block size * and set the filesystem block size
*/ */
int disk_get_info_from_file(const char *filename, int device_get_info_from_file(const char *filename,
struct job_target_data *target, struct job_target_data *target,
struct disk_info **info) struct device_info **info)
{ {
struct stat stats; struct stat stats;
char *device; char *device;
@@ -851,15 +826,15 @@ int disk_get_info_from_file(const char *filename,
} }
if (misc_temp_dev(stats.st_dev, 1, &device)) if (misc_temp_dev(stats.st_dev, 1, &device))
return -1; return -1;
if (disk_get_info(device, target, info)) { if (device_get_info(device, target, info)) {
misc_free_temp_dev(device); misc_free_temp_dev(device);
return -1; return -1;
} }
misc_free_temp_dev(device); 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) if (!info)
return; return;
@@ -877,28 +852,29 @@ void disk_free_info(struct disk_info *info)
* otherwise. */ * otherwise. */
static int static int
disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, 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 phy_per_fs;
blocknum_t mapped; blocknum_t mapped;
int subblock; int subblock;
/* No file system: partition or raw disk */ /* No file system: partition or raw disk */
if (info->fs_block_size == -1) { if (fs_block_size == -1) {
if (fd_is_basedisk) if (fd_is_basedisk)
*physical = logical; *physical = logical;
else else
*physical = logical + info->geo.start; *physical = logical + disk_info->geo.start;
return 0; return 0;
} }
/* /*
* Get mapping in file system blocks * 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; subblock = logical % phy_per_fs;
if (fs_map(fd, logical * info->phy_block_size, if (fs_map(fd, logical * disk_info->phy_block_size,
&mapped, info->fs_block_size) != 0) &mapped, fs_block_size) != 0)
return -1; return -1;
if (mapped == 0) { if (mapped == 0) {
/* This is a hole in the file */ /* 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 */ /* Convert file system block to physical */
*physical = mapped * phy_per_fs + subblock; *physical = mapped * phy_per_fs + subblock;
/* Add partition start */ /* Add partition start */
*physical += info->geo.start; *physical += disk_info->geo.start;
} }
return 0; 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 /* Return the cylinder on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */ * CHS device identified by INFO. */
int 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); 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 /* Return the head on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */ * CHS device identified by INFO. */
int 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; 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 /* Return the sector on which the block number BLOCKNUM is stored on the
* CHS device identified by INFO. */ * CHS device identified by INFO. */
int 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; return blocknum % info->geo.sectors + 1;
} }
@@ -945,7 +921,7 @@ disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
* layout. */ * layout. */
void void
disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum, disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
struct disk_info* info) struct disk_info *info)
{ {
switch (info->type) { switch (info->type) {
case disk_type_scsi: 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 * otherwise. On success OFFSET contains offset of the first written byte
*/ */
static int static int
disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void *data, disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk,
size_t bytecount, disk_blockptr_t *block, const void *data, size_t bytecount,
disk_blockptr_t *block, int fs_block_size,
struct disk_info *info, int align, off_t *offset) struct disk_info *info, int align, off_t *offset)
{ {
blocknum_t current_block; 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) { if (block != NULL) {
/* Store block pointer */ /* Store block pointer */
if (disk_get_blocknum(mfd->fd, is_base_disk, current_block, if (disk_get_blocknum(mfd->fd, is_base_disk, current_block,
&blocknum, info)) &blocknum, fs_block_size, info))
return -1; return -1;
disk_blockptr_from_blocknum(block, blocknum, info); 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; return 0;
} }
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount, int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
disk_blockptr_t *block, struct disk_info *info) 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, 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. * otherwise.
*/ */
blocknum_t blocknum_t
disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer, disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
size_t bytecount, disk_blockptr_t **blocklist, const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info *info, int align, off_t *offset) struct disk_info *info, int align, off_t *offset)
{ {
blocknum_t count; 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; count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
*blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) * *blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) *
count); count);
if (*blocklist == NULL) { if (*blocklist == NULL) {
close(mfd->fd); close(mfd->fd);
return 0; 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, rc = disk_write_block_aligned_base(mfd, fd_is_basedisk,
VOID_ADD(buffer, written), VOID_ADD(buffer, written),
chunk_size, &(*blocklist)[i], chunk_size, &(*blocklist)[i],
fs_block_size,
info, info,
i == 0 ? align : info->phy_block_size, i == 0 ? align : info->phy_block_size,
&pos); &pos);
@@ -1085,12 +1067,14 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
} }
blocknum_t blocknum_t
disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer, disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk,
size_t bytecount, disk_blockptr_t **blocklist, const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info *info) struct disk_info *info)
{ {
return disk_write_block_buffer_align(mfd, fd_is_basedisk, buffer, 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); 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. */ /* Return non-zero for ECKD large volumes. */
int int
disk_is_large_volume(struct disk_info* info) disk_is_large_volume(struct disk_info *info)
{ {
return (info->type == disk_type_eckd_ldl || return (info->type == disk_type_eckd_ldl ||
info->type == disk_type_eckd_cdl) && info->type == disk_type_eckd_cdl) &&
info->geo.cylinders == 0xfffe; info->geo.cylinders == 0xfffe;
} }
static void disk_print_info(struct disk_info *info, int source)
/* Print textual representation of INFO contents. */
void disk_print_info(struct disk_info *info, int source)
{ {
const char *prefix = " ";
char footnote[4] = ""; char footnote[4] = "";
prepare_footnote_ptr(source, footnote); prepare_footnote_ptr(source, footnote);
printf(" Device..........................: "); printf("%sDisk............................: ", prefix);
disk_print_devt(info->device); disk_print_devt(info->disk);
if (info->targetbase_def == defined_as_device) if (info->targetbase_def == defined_as_device)
printf("%s", footnote); printf("%s", footnote);
printf("\n"); printf("\n");
if (info->partnum != 0) { if (info->partnum != 0) {
printf(" Partition.......................: "); printf("%sPartition.......................: ", prefix);
disk_print_devt(info->partition); disk_print_devt(info->partition);
printf("\n"); 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) || if (((info->type == disk_type_fba) ||
(info->type == disk_type_diag) || (info->type == disk_type_diag) ||
(info->type == disk_type_eckd_ldl) || (info->type == disk_type_eckd_ldl) ||
(info->type == disk_type_eckd_cdl)) && (info->type == disk_type_eckd_cdl)) &&
(source == source_auto)) { (source == source_auto)) {
printf(" DASD device number..............: %04x\n", printf("%sDASD device number..............: %04x\n",
info->devno); prefix, info->devno);
} }
printf(" Type............................: disk %s\n", printf("%sType............................: disk %s\n",
(info->partnum != 0) ? "partition" : "device"); prefix, (info->partnum != 0) ? "partition" : "device");
printf(" Disk layout.....................: %s%s\n", printf("%sDisk layout.....................: %s%s\n",
disk_get_type_name(info->type), footnote); prefix, disk_get_type_name(info->type), footnote);
if (disk_type_is_eckd(info->type)) { if (disk_type_is_eckd(info->type)) {
printf(" Geometry - heads................: %d%s\n", printf("%sGeometry - heads................: %d%s\n",
info->geo.heads, footnote); prefix, info->geo.heads, footnote);
printf(" Geometry - sectors..............: %d%s\n", printf("%sGeometry - sectors..............: %d%s\n",
info->geo.sectors, footnote); prefix, info->geo.sectors, footnote);
if (disk_is_large_volume(info)) { if (disk_is_large_volume(info)) {
/* ECKD large volume. There is not enough information /* ECKD large volume. There is not enough information
* available in INFO to calculate disk cylinder size. */ * available in INFO to calculate disk cylinder size. */
printf(" Geometry - cylinders............: > 65534\n"); printf("%sGeometry - cylinders............: > 65534\n",
prefix);
} else { } else {
printf(" Geometry - cylinders............: %d%s\n", printf("%sGeometry - cylinders............: %d%s\n",
info->geo.cylinders, footnote); prefix, info->geo.cylinders, footnote);
} }
} }
printf(" Geometry - start................: %ld%s\n", printf("%sGeometry - start................: %ld%s\n",
info->geo.start, footnote); prefix, info->geo.start, footnote);
if (info->fs_block_size >= 0) printf("%sPhysical block size.............: %d%s\n",
printf(" File system block size..........: %d\n", prefix, info->phy_block_size, footnote);
info->fs_block_size); printf("%sDisk size in physical blocks....: %ld\n",
printf(" Physical block size.............: %d%s\n", prefix, (long)info->phy_blocks);
info->phy_block_size, footnote); print_footnote_ref(source, prefix);
printf(" Device size in physical blocks..: %ld\n", }
(long) info->phy_blocks);
print_footnote_ref(source, " "); 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. /* 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. */ * Return non-zero if BLOCK is a zero block, 0 otherwise. */
int 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) { switch (info->type) {
case disk_type_scsi: 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. */ * blocks can be merged, 0 otherwise. */
static int static int
can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second, can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
struct disk_info* info) struct disk_info *info)
{ {
int max_count; int max_count;
@@ -1315,7 +1311,7 @@ can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
* type. */ * type. */
static void static void
merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second, merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
struct disk_info* info) struct disk_info *info)
{ {
switch (info->type) { switch (info->type) {
case disk_type_scsi: case disk_type_scsi:
@@ -1338,7 +1334,7 @@ merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
* number of elements in the list. */ * number of elements in the list. */
blocknum_t blocknum_t
disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count, disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
struct disk_info* info) struct disk_info *info)
{ {
blocknum_t i; blocknum_t i;
blocknum_t last; blocknum_t last;
@@ -1365,8 +1361,8 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
*/ */
blocknum_t blocknum_t
disk_get_blocklist_from_file(const char *filename, struct file_range *reg, disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
disk_blockptr_t **blocklist, disk_blockptr_t **blocklist, int fs_block_size,
struct disk_info* info) struct disk_info *info)
{ {
struct stat stats; struct stat stats;
int fd; int fd;
@@ -1419,7 +1415,8 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
} }
/* Build list */ /* Build list */
for (i = 0; i < blk_count; i++) { 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); close(fd);
return 0; return 0;
} }
+102 -77
View File
@@ -172,7 +172,8 @@ install_fba_ccw(int fd, disk_blockptr_t *program_table,
int int
install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list, install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_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; struct boot_eckd_stage1b *stage1b;
int stage1b_size, rc = -1; 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)) if (boot_init_eckd_stage1b(stage1b, stage2_list, stage2_count))
goto out_free_stage1b; goto out_free_stage1b;
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size, *stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
stage1b_list, info); stage1b_list, fs_block_size,
info);
if (*stage1b_count == 0) if (*stage1b_count == 0)
goto out_free_stage1b; goto out_free_stage1b;
rc = 0; rc = 0;
@@ -291,7 +293,7 @@ static int install_eckd_cdl_ld(int fd, disk_blockptr_t *br,
return -1; return -1;
rc = DRY_RUN_FUNC(misc_write(fd, &vl, sizeof(vl))); rc = DRY_RUN_FUNC(misc_write(fd, &vl, sizeof(vl)));
if (rc) if (rc)
error_text("Could not update volume lablel 'vol1'"); error_text("Could not update volume label 'vol1'");
return 0; 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) int install_bootloader(struct job_data *job, struct install_set *bis)
{ {
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr; disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
struct disk_info *info = bis->info;
char footnote[4]; char footnote[4];
int rc; int rc;
int i; int i;
if (!info) if (!bis->info)
return 0; return 0;
prepare_footnote_ptr(job->target.source, footnote); prepare_footnote_ptr(job->target.source, footnote);
/* Inform user about what we're up to */ /* Inform user about what we're up to */
printf("Preparing boot device for %s%s: ", printf("Preparing boot device: ");
disk_get_ipl_type(info->type, if (bis->info->name)
job->id == job_dump_partition), printf("%s", bis->info->name);
job->id == job_dump_partition ? "dump" : "IPL"); else
if (info->name) { disk_print_devt(bis->info->device);
printf("%s", info->name); printf(".\n");
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 */
/* Install independently on each physical target base */
for (i = 0; i < job_get_nr_targets(job); i++) { for (i = 0; i < job_get_nr_targets(job); i++) {
struct disk_info *info;
int fd; int fd;
info = &bis->info->base[i];
if (verbose) { if (verbose) {
printf("Installing on base disk: "); printf("Installing on base disk: ");
disk_print_devname(info->basedisks[i]); disk_print_devname(info->disk);
printf("%s.\n", footnote); printf("%s.\n", footnote);
} }
/* Open device file */ /* Open device file */
fd = open(bis->basetmp[i], O_RDWR); fd = open(bis->mirrors[i].basetmp, O_RDWR);
if (fd == -1) { if (fd == -1) {
error_reason(strerror(errno)); error_reason(strerror(errno));
error_text("Could not open temporary device file '%s'", error_text("Could not open temporary device file '%s'",
bis->basetmp[i]); bis->mirrors[i].basetmp);
return -1; return -1;
} }
/* Ensure that potential cache inconsistencies between disk and /* 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) * picture in comments above)
*/ */
if (job->id == job_dump_partition) { 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, scsi_dump_sb_blockptr,
job_dump_is_ngdump(job), job_dump_is_ngdump(job),
fd); fd);
} else { } else {
rc = install_bootloader_ipl(bis->tables, info, rc = install_bootloader_ipl(bis->mirrors[i].tables,
info,
fd); fd);
} }
if (fsync(fd)) if (fsync(fd))
error_text("Could not sync device file '%s'", error_text("Could not sync device file '%s'",
bis->basetmp[i]); bis->mirrors[i].basetmp);
if (close(fd)) if (close(fd))
error_text("Could not close device file '%s'", error_text("Could not close device file '%s'",
bis->basetmp[i]); bis->mirrors[i].basetmp);
if (rc) if (rc)
break; 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) if (verbose)
print_footnote_ref(job->target.source, ""); 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; 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); error_text("Could not rewind tape device '%s' to tape", device);
rc = -1; rc = -1;
} }
if (!dry_run && fsync(fd))
error_text("Could not sync device file '%s'", device);
close(fd); close(fd);
return rc; 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 /* Write 64k null bytes with dump signature at offset 512 to
* start of dump partition */ * start of dump partition */
static int 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; int rc;
unsigned int bytes = 65536; 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 * Force block size of 4KiB because otherwise there is not enough space
* to write the dump tool. * 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; 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; param->bpt = info->geo.sectors;
} }
static int install_svdump_eckd_ldl(struct misc_fd *mfd, struct disk_info *info, static int
const struct stage2dump_parm_tail *stage2dump_parms) 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; disk_blockptr_t *stage2_list, *stage1b_list;
blocknum_t stage2_count, stage1b_count; 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)) if (misc_seek(mfd->fd, info->geo.start * info->phy_block_size))
goto out_free_stage2; goto out_free_stage2;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size, stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info); &stage2_list,
fs_block_size, info);
if (stage2_count == 0) if (stage2_count == 0)
goto out_free_stage2_list; goto out_free_stage2_list;
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count, 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; goto out_free_stage2_list;
/* Install stage 0 - afterwards we are at stage 1 position*/ /* Install stage 0 - afterwards we are at stage 1 position*/
boot_init_eckd_ldl_stage0(&stage0); boot_init_eckd_ldl_stage0(&stage0);
@@ -909,8 +912,9 @@ out:
return rc; return rc;
} }
static int install_dump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info, static int install_dump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
void *stage2, size_t stage2_size, int mvdump, struct disk_info *info, void *stage2,
size_t stage2_size, int mvdump,
int force) int force)
{ {
blocknum_t count, stage2_count, stage1b_count; 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)) if (misc_seek(mfd->fd, ECKD_CDL_DUMP_REC * info->phy_block_size))
goto out; goto out;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size, stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info); &stage2_list, fs_block_size,
info);
if (stage2_count == 0) if (stage2_count == 0)
goto out; goto out;
/* Install stage 1b behind stage 2*/ /* Install stage 1b behind stage 2*/
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count, 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; goto out_free_stage2_list;
/* Install stage 0 */ /* Install stage 0 */
boot_init_eckd_cdl_stage0(&stage0_cdl); boot_init_eckd_cdl_stage0(&stage0_cdl);
@@ -966,7 +972,8 @@ out:
} }
static int 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) const struct stage2dump_parm_tail *stage2dump_parms)
{ {
size_t stage2_size; 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)) if (boot_get_eckd_dump_stage2(&stage2, &stage2_size, stage2dump_parms))
return -1; 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); free(stage2);
return rc; return rc;
} }
static int 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 stage2dump_parm_tail *stage2dump_parms,
const struct mvdump_parm_table *mv_parm_table) 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, if (boot_get_eckd_mvdump_stage2(&stage2, &stage2_size, stage2dump_parms,
mv_parm_table)) mv_parm_table))
return -1; 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); stage2dump_parms->mvdump_force);
free(stage2); free(stage2);
return rc; return rc;
@@ -1002,7 +1012,8 @@ install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
int int
install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list, install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
blocknum_t *stage1b_count, disk_blockptr_t *stage2_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; struct boot_fba_stage1b *stage1b;
int stage1b_size, rc = -1; 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)) if (boot_init_fba_stage1b(stage1b, stage2_list, stage2_count))
goto out_free_stage1b; goto out_free_stage1b;
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size, *stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
stage1b_list, info); stage1b_list, fs_block_size,
info);
if (*stage1b_count == 0) if (*stage1b_count == 0)
goto out_free_stage1b; goto out_free_stage1b;
rc = 0; rc = 0;
@@ -1028,7 +1040,8 @@ out:
} }
static int 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) const struct stage2dump_parm_tail *stage2dump_parms)
{ {
blocknum_t stage1b_count, stage2_count, blk; 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)) if (misc_seek(mfd->fd, blk * info->phy_block_size))
goto out_free_stage2; goto out_free_stage2;
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size, stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
&stage2_list, info); &stage2_list, fs_block_size,
info);
if (stage2_count == 0) if (stage2_count == 0)
goto out_free_stage2; goto out_free_stage2;
/* Install stage 1b in front of stage 2 */ /* 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)) if (misc_seek(mfd->fd, blk * info->phy_block_size))
goto out_free_stage2_list; goto out_free_stage2_list;
if (install_fba_stage1b(mfd, &stage1b_list, &stage1b_count, 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; goto out_free_stage2_list;
/* Install stage 0/1 fill in dump partition parameter */ /* Install stage 0/1 fill in dump partition parameter */
if (boot_init_fba_stage0(&stage0, stage1b_list, stage1b_count)) 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 stage2dump_parm_tail stage2dump_parms = {0};
struct misc_fd mfd = {0}; struct misc_fd mfd = {0};
struct disk_info* info; struct device_info *dev_info;
struct disk_info *info;
uint64_t part_size; uint64_t part_size;
char *tempdev; char *tempdev;
int rc; 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 " error_text("Could not install dump record on tape "
"device '%s'", device); "device '%s'", device);
} else { } else {
if (verbose) { if (!misc_fsync(&mfd, device) && verbose) {
printf("Dump record successfully installed on " printf("Dump record successfully installed on "
"tape device '%s'.\n", device); "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); close(mfd.fd);
/* This is a disk device */ /* This is a disk device */
rc = disk_get_info(device, target, &info); rc = device_get_info(device, target, &dev_info);
if (rc) { if (rc) {
error_text("Could not get information for dump target " error_text("Could not get information for dump target "
"'%s'", device); "'%s'", device);
return rc; return rc;
} }
info = &dev_info->base[0];
if (info->partnum == 0) { if (info->partnum == 0) {
error_reason("Dump target '%s' is not a disk partition", error_reason("Dump target '%s' is not a disk partition",
device); device);
disk_free_info(info); device_free_info(dev_info);
return -1; return -1;
} }
if (verbose) { if (verbose) {
printf("Target device information\n"); 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) { if (rc) {
disk_free_info(info); device_free_info(dev_info);
return -1; return -1;
} }
if (misc_open_device(tempdev, &mfd, dry_run) == -1) { if (misc_open_device(tempdev, &mfd, dry_run) == -1) {
error_text("Could not open temporary device node '%s'", error_text("Could not open temporary device node '%s'",
tempdev); tempdev);
misc_free_temp_dev(tempdev); misc_free_temp_dev(tempdev);
disk_free_info(info); device_free_info(dev_info);
return -1; return -1;
} }
switch (info->type) { 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)); disk_get_type_name(info->type));
} }
if (info->type == disk_type_eckd_ldl) 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) 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 else
rc = install_svdump_fba(&mfd, info, &stage2dump_parms); rc = install_svdump_fba(&mfd, dev_info->fs_block_size,
info, &stage2dump_parms);
break; break;
case disk_type_scsi: case disk_type_scsi:
error_reason("%s: Unsupported disk type '%s' (try --dumptofs)", 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; break;
} }
misc_free_temp_dev(tempdev); misc_free_temp_dev(tempdev);
disk_free_info(info); device_free_info(dev_info);
if (fsync(mfd.fd)) if (fsync(mfd.fd))
error_text("Could not sync device file '%s'", device); error_text("Could not sync device file '%s'", device);
if (close(mfd.fd)) if (close(mfd.fd))
@@ -1232,8 +1253,9 @@ int
install_mvdump(char* const device[], struct job_target_data* target, int count, install_mvdump(char* const device[], struct job_target_data* target, int count,
uint64_t mem, uint8_t force) 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 stage2dump_parm_tail stage2dump_parms = {0};
struct disk_info *info[MAX_DUMP_VOLUMES] = {0};
struct mvdump_parm_table mvdump_parms; struct mvdump_parm_table mvdump_parms;
uint64_t total_size = 0; uint64_t total_size = 0;
struct timeval time; struct timeval time;
@@ -1267,12 +1289,13 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
} }
close(mfd.fd); close(mfd.fd);
/* This is a disk device */ /* 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) { if (rc) {
error_text("Could not get information for dump target " error_text("Could not get information for dump target "
"'%s'", device[i]); "'%s'", device[i]);
goto out; goto out;
} }
info[i] = &dev_info[i]->base[0];
if (info[i]->partnum == 0) { if (info[i]->partnum == 0) {
error_reason("Dump target '%s' is not a disk partition", error_reason("Dump target '%s' is not a disk partition",
device[i]); device[i]);
@@ -1321,7 +1344,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
if (verbose) { if (verbose) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
printf("Multi-volume dump target %d:\n", i + 1); 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"); printf("-------------------------------------------\n");
} }
} }
@@ -1343,7 +1366,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
struct misc_fd mfd = {0}; 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) { if (rc) {
rc = -1; rc = -1;
goto out; goto out;
@@ -1358,7 +1381,9 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
if (verbose) if (verbose)
printf("Installing dump record on target partition " printf("Installing dump record on target partition "
"'%s'\n", device[i]); "'%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); misc_free_temp_dev(tempdev);
if (fsync(mfd.fd)) if (fsync(mfd.fd))
@@ -1372,6 +1397,6 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
out: out:
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
if (info[i] != NULL) if (info[i] != NULL)
disk_free_info(info[i]); device_free_info(dev_info[i]);
return rc; return rc;
} }
+11 -1
View File
@@ -131,6 +131,16 @@ int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate)
return mfd->fd; 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 /* 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, * memory at location BUFFER. Return 0 when all bytes were successfully read,
* non-zero otherwise. */ * 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) static int misc_simulate_write(int fd, size_t count)
{ {
+1 -1
View File
@@ -1200,7 +1200,7 @@ scan_check_target_data(char* keyword[], int* line)
return -1; return -1;
case target_type_scsi: case target_type_scsi:
case target_type_fba: case target_type_fba:
if ((keyword[(int) scan_keyword_targetgeometry] == 0)) if (keyword[(int) scan_keyword_targetgeometry] == 0)
break; break;
if (line != NULL) if (line != NULL)
error_reason("Line %d: keyword " error_reason("Line %d: keyword "
+7 -9
View File
@@ -174,7 +174,8 @@ main(int argc, char* argv[])
/* Do it */ /* Do it */
switch (job->id) { switch (job->id) {
case job_dump_partition: 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) if (rc)
break; break;
job_dump_check_set_ngdump(job, &ext_type); job_dump_check_set_ngdump(job, &ext_type);
@@ -210,16 +211,16 @@ main(int argc, char* argv[])
case job_menu: case job_menu:
rc = prepare_bootloader(job, &bis); rc = prepare_bootloader(job, &bis);
if (rc) { if (rc) {
free_bootloader(&bis); free_bootloader(&bis, job);
break; break;
} }
rc = install_bootloader(job, &bis); rc = install_bootloader(job, &bis);
if (rc) { if (rc) {
free_bootloader(&bis); free_bootloader(&bis, job);
break; break;
} }
rc = post_install_bootloader(job, &bis); rc = post_install_bootloader(job, &bis);
free_bootloader(&bis); free_bootloader(&bis, job);
break; break;
case job_ipl_tape: case job_ipl_tape:
rc = install_tapeloader(job->data.ipl_tape.device, rc = install_tapeloader(job->data.ipl_tape.device,
@@ -244,10 +245,6 @@ main(int argc, char* argv[])
} }
switch (rc) { switch (rc) {
case 0: /* Operation completed successfully */ case 0: /* Operation completed successfully */
if (verbose)
printf("Syncing disks...\n");
if (!dry_run)
sync();
printf("Done.\n"); printf("Done.\n");
break; break;
case -2: /* Operation canceled by user */ case -2: /* Operation canceled by user */
@@ -306,7 +303,8 @@ struct component_footer component_footers[NR_PROGRAM_COMPONENTS] = {
}, },
[COMPONENT_ID_ENVBLK] = { [COMPONENT_ID_ENVBLK] = {
.type = COMPONENT_TYPE_LOAD, .type = COMPONENT_TYPE_LOAD,
.desc = "environment blk" .desc = "environment blk",
.fs_block_aligned = 1
}, },
[COMPONENT_ID_SEGMENT_FILE] = { [COMPONENT_ID_SEGMENT_FILE] = {
.type = COMPONENT_TYPE_EXECUTE, .type = COMPONENT_TYPE_EXECUTE,
+5
View File
@@ -1045,6 +1045,8 @@ static int get_table(dev_t dev, struct util_list **table)
&start, &length, &type, &args) < 4) { &start, &length, &type, &args) < 4) {
ERR("Unrecognized device-mapper table format for device '%s'\n", ERR("Unrecognized device-mapper table format for device '%s'\n",
devname); devname);
free(type);
free(args);
goto out; goto out;
} }
tops = find_target_ops(type); 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 " ERR("Unsupported setup: Unsupported device-mapper "
"target type '%s' for device '%s'\n", "target type '%s' for device '%s'\n",
type, devname); type, devname);
free(type);
free(args);
goto out;
} }
data = tops->get_target_data(devname, args); data = tops->get_target_data(devname, args);
free(type); free(type);
+3 -2
View File
@@ -3292,8 +3292,9 @@ int kms_configure(const kms_handle_t handle,
opts.cert_digest, opts.cert_digest,
#ifdef EKMF_SUPPORTS_RSA_PSS_CERTIFICATES #ifdef EKMF_SUPPORTS_RSA_PSS_CERTIFICATES
opts.cert_rsa_pss, opts.cert_rsa_pss,
#endif #else
false); false);
#endif
else else
rc = _error_gen_csr_sscert_opts(ph, &opts); rc = _error_gen_csr_sscert_opts(ph, &opts);
if (rc != 0) if (rc != 0)
@@ -3937,7 +3938,7 @@ static int _restrict_key(struct plugin_handle *ph, unsigned char *key_blob,
return rc; 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) { if (rc != 0) {
warnx("The secure key retrieved from EKMF Web might not be " warnx("The secure key retrieved from EKMF Web might not be "
"secure"); "secure");
+6
View File
@@ -121,6 +121,12 @@ typedef struct {
#define CKR_OK 0x00000000 #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_init_t) (void);
typedef int (*m_add_module_t) (XCP_Module_t module, target_t *target); 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); typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target);
+39 -22
View File
@@ -1983,6 +1983,9 @@ out:
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @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 * @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL) * 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 * @param[in] pkey_fd the file descriptor of /dev/pkey
* *
* @returns 0 for success or a negative errno in case of an error * @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, size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type, const char *clear_key_file, const char *volume_type,
const char *key_type, bool gen_passphrase, 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 key_filenames file_names = { 0 };
struct properties *key_props = NULL; struct properties *key_props = NULL;
@@ -2043,6 +2047,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
file_names.skey_filename, file_names.skey_filename,
keybits, xts, key_type, keybits, xts, key_type,
(const char **)apqn_list, (const char **)apqn_list,
exportable, wrap_with_trusted,
keystore->verbose); keystore->verbose);
else else
rc = generate_secure_key_clear(pkey_fd, 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, keybits, xts, clear_key_file,
key_type, key_type,
(const char **)apqn_list, (const char **)apqn_list,
exportable, wrap_with_trusted,
keystore->verbose); keystore->verbose);
if (rc != 0) if (rc != 0)
goto out_free_props; 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] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase * @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL) * for LUKS2 (optional, can be NULL)
* @param[in] exportable if true the key shall be exportable
* @param[in] lib the external library struct * @param[in] lib the external library struct
* *
* @returns 0 for success or a negative errno in case of an error * @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 *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size, const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase, 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 key_filenames file_names = { 0 };
struct properties *key_props = NULL; struct properties *key_props = NULL;
@@ -2375,17 +2383,22 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
goto out_free_props; goto out_free_props;
} }
rc = restrict_key_export(lib->cca, secure_key, secure_key_size, if (!exportable) {
keystore->verbose); rc = restrict_key_export(lib->cca, secure_key,
if (rc != 0) { secure_key_size,
warnx("Failed to export-restrict the imported secure " keystore->verbose);
"key: %s", strerror(-rc)); if (rc != 0) {
if (!selected) warnx("Failed to export-restrict the imported "
print_msg_for_cca_envvars("secure AES key"); "secure key: %s", strerror(-rc));
goto out_free_props; 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) { if (rc != 0) {
warnx("The secure key to import might not be secure"); warnx("The secure key to import might not be secure");
printf("%s: Do you want to import it anyway [y/N]? ", 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] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase * @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL) * for LUKS2 (optional, can be NULL)
* @param[in] exportable if true the key shall be exportable
* @param[in] lib the external library struct * @param[in] lib the external library struct
* *
* @returns 0 for success or a negative errno in case of an error * @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 *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type, const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file, bool gen_passphrase, const char *passphrase_file,
struct ext_lib *lib) bool exportable, struct ext_lib *lib)
{ {
size_t secure_key_size; size_t secure_key_size;
u8 *secure_key; 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, rc = keystore_import(keystore, secure_key, secure_key_size, name,
description, volumes, apqns, noapqncheck, description, volumes, apqns, noapqncheck,
sector_size, volume_type, gen_passphrase, sector_size, volume_type, gen_passphrase,
passphrase_file, lib); passphrase_file, exportable, lib);
if (secure_key != NULL) if (secure_key != NULL)
free(secure_key); 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] key_type the type of the key to convert it to
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
* existence and type. * 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] pkey_fd the file descriptor of /dev/pkey
* @param[in] lib the external library struct * @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, int keystore_convert_key(struct keystore *keystore, const char *name,
const char *key_type, bool noapqncheck, bool quiet, 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 }; struct key_filenames file_names = { 0 };
u8 output_key[2 * MAX_SECURE_KEY_SIZE]; u8 output_key[2 * MAX_SECURE_KEY_SIZE];
@@ -4941,14 +4956,16 @@ int keystore_convert_key(struct keystore *keystore, const char *name,
goto out; goto out;
} }
rc = restrict_key_export(lib->cca, output_key, output_key_size, if (!exportable) {
keystore->verbose); rc = restrict_key_export(lib->cca, output_key, output_key_size,
if (rc != 0) { keystore->verbose);
warnx("Export restricting the converted secure key '%s' has " if (rc != 0) {
"failed", name); warnx("Export restricting the converted secure key "
if (!selected) "'%s' has failed", name);
print_msg_for_cca_envvars("secure AES key"); if (!selected)
goto out; print_msg_for_cca_envvars("secure AES key");
goto out;
}
} }
rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true); rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true);
+6 -4
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, size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type, const char *clear_key_file, const char *volume_type,
const char *key_type, bool gen_passphrase, 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, int keystore_generate_key_kms(struct keystore *keystore, const char *name,
const char *description, const char *volumes, 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 *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size, const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase, 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, int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes, const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size, const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type, const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file, 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, int keystore_change_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes, 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, int keystore_convert_key(struct keystore *keystore, const char *name,
const char *key_type, bool noapqncheck, bool quiet, 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, int keystore_kms_keys_set_property(struct keystore *keystore,
const char *key_type, const char *key_type,
+4 -4
View File
@@ -745,7 +745,7 @@ static int remove_directory_recursively(const char *directory)
if (rmdir(directory) != 0) { if (rmdir(directory) != 0) {
rc = -errno; rc = -errno;
warnx("Failed to remove '%s': %s", filename, strerror(-rc)); warnx("Failed to remove '%s': %s", directory, strerror(-rc));
goto out; goto out;
} }
@@ -2448,7 +2448,7 @@ int set_kms_key_properties(struct kms_info *kms_info,
ADD_KMS_PROPS(kms_props, num_kms_props, ADD_KMS_PROPS(kms_props, num_kms_props,
KMS_KEY_PROP_SECTOR_SIZE, sector_size); KMS_KEY_PROP_SECTOR_SIZE, sector_size);
if (passphrase_file != NULL) { if (passphrase_file != NULL) {
/* *passphrase_file is NULL to remove the propoerty */ /* *passphrase_file is NULL to remove the property */
if (*passphrase_file != NULL) { if (*passphrase_file != NULL) {
passphrase = passphrase =
read_passphrase_as_base64(*passphrase_file, 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. * @param[in] name_filter the name filter. Can contain wild cards.
* NULL means no name filter. * NULL means no name filter.
* @param[in] volume_filter the volume filter. Can contain wild cards, and * @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' * If the filter does not contain the ':dm-name'
* part, then the volumes are matched without the * part, then the volumes are matched without the
* dm-name part. If the filter contains 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. * @param[in] name_filter the name filter. Can contain wild cards.
* NULL means no name filter. * NULL means no name filter.
* @param[in] volume_filter the volume filter. Can contain wild cards, and * @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' * If the filter does not contain the ':dm-name'
* part, then the volumes are matched without the * part, then the volumes are matched without the
* dm-name part. If the filter contains the * dm-name part. If the filter contains the
+85 -11
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 * 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 * 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 * 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] key_type the type of the key
* @param[in] apqns a zero terminated array of pointers to APQN-strings, * @param[in] apqns a zero terminated array of pointers to APQN-strings,
* or NULL for AUTOSELECT * 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 * @param[in] verbose if true, verbose messages are printed
* *
* @returns 0 on success, a negative errno in case of an error * @returns 0 on success, a negative errno in case of an error
*/ */
int generate_secure_key_random(int pkey_fd, const char *keyfile, int generate_secure_key_random(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, const char *key_type, 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; struct pkey_genseck2 genseck2;
size_t secure_key_size, size; size_t secure_key_size, size;
@@ -960,6 +1003,9 @@ retry:
genseck2.key = secure_key; genseck2.key = secure_key;
genseck2.keylen = size; genseck2.keylen = size;
genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable,
wrap_with_trusted);
rc = pkey_genseck2(pkey_fd, &genseck2, verbose); rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) { if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) {
/* /*
@@ -979,6 +1025,9 @@ retry:
} }
if (rc != 0) { if (rc != 0) {
warnx("Failed to generate a secure key: %s", strerror(-rc)); 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; goto out;
} }
if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) { if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) {
@@ -1016,10 +1065,17 @@ retry:
genseck2.key = secure_key + size; genseck2.key = secure_key + size;
genseck2.keylen = size; genseck2.keylen = size;
genseck2.keygenflags = flags_for_pkey_type(genseck2.type,
exportable,
wrap_with_trusted);
rc = pkey_genseck2(pkey_fd, &genseck2, verbose); rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
if (rc != 0) { if (rc != 0) {
warnx("Failed to generate a secure key: %s", warnx("Failed to generate a secure key: %s",
strerror(-rc)); strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out; goto out;
} }
} }
@@ -1048,6 +1104,9 @@ out:
* @param[in] key_type the type of the key * @param[in] key_type the type of the key
* @param[in] apqns a zero terminated array of pointers to APQN-strings, * @param[in] apqns a zero terminated array of pointers to APQN-strings,
* or NULL for AUTOSELECT * 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 * @param[in] verbose if true, verbose messages are printed
* *
* @returns 0 on success, a negative errno in case of an error * @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, int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, size_t keybits, bool xts,
const char *clearkeyfile, const char *key_type, 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; struct pkey_clr2seck2 clr2seck2;
size_t secure_key_size; size_t secure_key_size;
@@ -1123,6 +1183,9 @@ retry:
clr2seck2.key = secure_key; clr2seck2.key = secure_key;
clr2seck2.keylen = size; clr2seck2.keylen = size;
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable,
wrap_with_trusted);
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) { if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) {
/* /*
@@ -1142,6 +1205,9 @@ retry:
} }
if (rc != 0) { if (rc != 0) {
warnx("Failed to generate a secure key: %s", strerror(-rc)); 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; goto out;
} }
if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) { if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) {
@@ -1182,10 +1248,17 @@ retry:
clr2seck2.key = secure_key + size; clr2seck2.key = secure_key + size;
clr2seck2.keylen = size; clr2seck2.keylen = size;
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type,
exportable,
wrap_with_trusted);
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
if (rc != 0) { if (rc != 0) {
warnx("Failed to generate a secure key: %s", warnx("Failed to generate a secure key: %s",
strerror(-rc)); strerror(-rc));
if (exportable &&
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
print_error_for_ep11_exportable();
goto out; 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 the secure key token
* @param[in] key_size the size of the secure key * @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 * @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; struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
bool mismatch = false; bool mismatch = false;
@@ -2072,22 +2146,22 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
mismatch = true; mismatch = true;
} }
if (cipherkey->kmf1 & 0x8000) { if (!exportable && (cipherkey->kmf1 & 0x8000)) {
printf("WARNING: The secure key can be exported using a " printf("WARNING: The secure key can be exported using a "
"symmetric key\n"); "symmetric key\n");
mismatch = true; mismatch = true;
} }
if (cipherkey->kmf1 & 0x4000) { if (!exportable && (cipherkey->kmf1 & 0x4000)) {
printf("WARNING: The secure key can be exported using an " printf("WARNING: The secure key can be exported using an "
"unauthenticated asymmetric key\n"); "unauthenticated asymmetric key\n");
mismatch = true; mismatch = true;
} }
if (cipherkey->kmf1 & 0x2000) { if (!exportable && (cipherkey->kmf1 & 0x2000)) {
printf("WARNING: The secure key can be exported using an " printf("WARNING: The secure key can be exported using an "
"authenticated asymmetric key\n"); "authenticated asymmetric key\n");
mismatch = true; mismatch = true;
} }
if (cipherkey->kmf1 & 0x1000) { if (!exportable && (cipherkey->kmf1 & 0x1000)) {
printf("WARNING: The secure key can be exported using a RAW " printf("WARNING: The secure key can be exported using a RAW "
"key\n"); "key\n");
mismatch = true; mismatch = true;
@@ -2097,17 +2171,17 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
"CPACF protected key\n"); "CPACF protected key\n");
mismatch = true; mismatch = true;
} }
if ((cipherkey->kmf1 & 0x0080) == 0) { if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) {
printf("WARNING: The secure key can be exported using a DES " printf("WARNING: The secure key can be exported using a DES "
"key\n"); "key\n");
mismatch = true; mismatch = true;
} }
if ((cipherkey->kmf1 & 0x0040) == 0) { if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) {
printf("WARNING: The secure key can be exported using an AES " printf("WARNING: The secure key can be exported using an AES "
"key\n"); "key\n");
mismatch = true; mismatch = true;
} }
if ((cipherkey->kmf1 & 0x0008) == 0) { if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) {
printf("WARNING: The secure key can be exported using an RSA " printf("WARNING: The secure key can be exported using an RSA "
"key\n"); "key\n");
mismatch = true; mismatch = true;
@@ -2161,7 +2235,7 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
"value\n"); "value\n");
mismatch = true; mismatch = true;
} }
if ((cipherkey->kmf3 & 0x00FF) == 0x0012) { if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) {
printf("WARNING: The secure key was converted from a CCA " printf("WARNING: The secure key was converted from a CCA "
"key-token that had no export control attributes\n"); "key-token that had no export control attributes\n");
mismatch = true; mismatch = true;
+5 -3
View File
@@ -330,12 +330,14 @@ int open_pkey_device(bool verbose);
int generate_secure_key_random(int pkey_fd, const char *keyfile, int generate_secure_key_random(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, const char *key_type, 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, int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts, size_t keybits, bool xts,
const char *clearkeyfile, const char *key_type, 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, u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
bool verbose); 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); 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); 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); 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 { enum reencipher_method {
REENCIPHER_OLD_TO_CURRENT = 1, REENCIPHER_OLD_TO_CURRENT = 1,
+1 -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, rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token,
sizeof(build_blob_data.token), name, description, sizeof(build_blob_data.token), name, description,
volumes, NULL, false, sector_size, volume_type, volumes, NULL, false, sector_size, volume_type,
gen_passphrase, passphrase_file, NULL); gen_passphrase, passphrase_file, false, NULL);
return rc; return rc;
} }
+43
View File
@@ -100,6 +100,8 @@ key repository.
.IR clear\-key\-file ] .IR clear\-key\-file ]
.RB [ \-\-key\-type | \-K .RB [ \-\-key\-type | \-K
.IR type ] .IR type ]
.RB [ \-\-exportable ]
.RB [ \-\-wrap\-with\-trusted ]
.RB [ \-\-verbose | \-V ] .RB [ \-\-verbose | \-V ]
. .
.PP .PP
@@ -129,6 +131,8 @@ key repository.
.RB [ \-\-gen\-dummy\-passphrase ] .RB [ \-\-gen\-dummy\-passphrase ]
.RB [ \-\-set\-dummy\-passphrase .RB [ \-\-set\-dummy\-passphrase
.IR passphrase\-file ] .IR passphrase\-file ]
.RB [ \-\-exportable ]
.RB [ \-\-wrap\-with\-trusted ]
.RB [ KMS-plugin\ specific\ options ] .RB [ KMS-plugin\ specific\ options ]
.RB [ \-\-verbose | \-V ] .RB [ \-\-verbose | \-V ]
.PP .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. 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 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. 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" .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 [ \-\-gen\-dummy\-passphrase ]
.RB [ \-\-set\-dummy\-passphrase .RB [ \-\-set\-dummy\-passphrase
.IR passphrase\-file ] .IR passphrase\-file ]
.RB [ \-\-exportable ]
.RB [ \-\-verbose | \-V ] .RB [ \-\-verbose | \-V ]
. .
.PP .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. to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
For the supported environments and downloads, see: For the supported environments and downloads, see:
\fIhttp://www.ibm.com/security/cryptocards\fP \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" .SS "Export AES secure keys from the secure key repository"
. .
@@ -798,6 +833,7 @@ same way as with \fBcryptsetup\fP.
.IR type .IR type
.RB [ \-\-no\-apqn\-check ] .RB [ \-\-no\-apqn\-check ]
.RB [ \-\-force | \-F ] .RB [ \-\-force | \-F ]
.RB [ \-\-exportable ]
.RB [ \-\-verbose | \-V ] .RB [ \-\-verbose | \-V ]
. .
.PP .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 to be installed. The required CCA IBM cryptographic adapter firmware version
is 6.3.27 or later. For the supported environments and downloads, see: is 6.3.27 or later. For the supported environments and downloads, see:
\fIhttp://www.ibm.com/security/cryptocards\fP \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 .SH COMMANDS FOR KEY MANAGEMENT SYSTEM INTEGRATION
+109 -14
View File
@@ -83,6 +83,8 @@ static struct zkey_globals {
bool gen_passphrase; bool gen_passphrase;
char *passphrase_file; char *passphrase_file;
bool remove_passphrase; bool remove_passphrase;
bool exportable;
bool wrap_with_trusted;
bool kms_bound; bool kms_bound;
bool run; bool run;
bool batch_mode; bool batch_mode;
@@ -173,6 +175,8 @@ static struct zkey_globals {
#define OPT_GEN_DUMMY_PASSPHRASE 265 #define OPT_GEN_DUMMY_PASSPHRASE 265
#define OPT_SET_DUMMY_PASSPHRASE 266 #define OPT_SET_DUMMY_PASSPHRASE 266
#define OPT_REMOVE_DUMMY_PASSPHRASE 267 #define OPT_REMOVE_DUMMY_PASSPHRASE 267
#define OPT_EXPORTABLE 268
#define OPT_WRAP_WITH_TRUSTED 269
/* /*
* Configuration of command line options * Configuration of command line options
@@ -313,6 +317,28 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT, .flags = UTIL_OPT_FLAG_NOSHORT,
.command = COMMAND_GENERATE, .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, .flags = UTIL_OPT_FLAG_SECTION,
@@ -496,6 +522,19 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT, .flags = UTIL_OPT_FLAG_NOSHORT,
.command = COMMAND_IMPORT, .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, .flags = UTIL_OPT_FLAG_SECTION,
@@ -969,6 +1008,14 @@ static struct util_opt opt_vec[] = {
"key", "key",
.command = COMMAND_CONVERT, .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, .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, rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg,
g.keybits, g.xts, g.keybits, g.xts,
g.clearkeyfile, g.key_type, g.clearkeyfile, g.key_type,
NULL, g.verbose); NULL, g.exportable, g.wrap_with_trusted,
g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; 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, rc = generate_secure_key_random(g.pkey_fd, g.pos_arg,
g.keybits, g.xts, g.key_type, 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; return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }
@@ -1941,13 +1990,24 @@ static int command_generate_repository(void)
} }
if (g.clearkeyfile != NULL) { 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, " "generating a key in a KMS-bound repository, "
"unless option '--local|-L' is also specified"); "unless option '--local|-L' is also specified");
util_prg_print_parse_error(); util_prg_print_parse_error();
return EXIT_FAILURE; 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); rc = perform_kms_login(&g.kms_info, g.verbose);
if (rc != 0) if (rc != 0)
rc = EXIT_FAILURE; rc = EXIT_FAILURE;
@@ -1966,11 +2026,25 @@ static int command_generate_repository(void)
if (g.key_type == NULL) if (g.key_type == NULL)
g.key_type = KEY_TYPE_CCA_AESDATA; 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, rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes,
g.apqns, g.noapqncheck, g.sector_size, g.apqns, g.noapqncheck, g.sector_size,
g.keybits, g.xts, g.clearkeyfile, g.keybits, g.xts, g.clearkeyfile,
g.volume_type, g.key_type, g.gen_passphrase, 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: out:
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
@@ -2007,6 +2081,18 @@ static int command_generate(void)
"pvsecret import' instead", g.key_type); "pvsecret import' instead", g.key_type);
return -EXIT_FAILURE; 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.pos_arg != NULL) {
if (g.volumes != NULL) { if (g.volumes != NULL) {
warnx("Option '--volumes|-l' is not valid for " 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, rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes,
g.apqns, g.noapqncheck, g.sector_size, g.apqns, g.noapqncheck, g.sector_size,
g.pos_arg, g.volume_type, g.gen_passphrase, 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; return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }
@@ -2816,14 +2902,17 @@ static int command_convert_file(void)
goto out; goto out;
} }
rc = restrict_key_export(&g.cca, output_key, output_key_size, if (!g.exportable) {
g.verbose); rc = restrict_key_export(&g.cca, output_key, output_key_size,
if (rc != 0) { g.verbose);
warnx("Export restricting the converted secure key has failed"); if (rc != 0) {
if (!selected) warnx("Export restricting the converted secure key "
print_msg_for_cca_envvars("secure AES key"); "has failed");
rc = EXIT_FAILURE; if (!selected)
goto out; print_msg_for_cca_envvars("secure AES key");
rc = EXIT_FAILURE;
goto out;
}
} }
pr_verbose("Secure key was converted successfully"); 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, 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; return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }
@@ -3519,6 +3608,12 @@ int main(int argc, char *argv[])
case OPT_REMOVE_DUMMY_PASSPHRASE: case OPT_REMOVE_DUMMY_PASSPHRASE:
g.remove_passphrase = 1; g.remove_passphrase = 1;
break; break;
case OPT_EXPORTABLE:
g.exportable = 1;
break;
case OPT_WRAP_WITH_TRUSTED:
g.wrap_with_trusted = 1;
break;
case 'A': case 'A':
g.all = 1; g.all = 1;
break; break;
+7 -7
View File
@@ -88,7 +88,7 @@ Without any options zmemtopo displays the tree view with nesting level 3.
$ zmemtopo $ zmemtopo
.nf .nf
.ft CW .ft CR
LPAR/LEVEL SIZE LPAR/LEVEL SIZE
LPAR003 8G LPAR003 8G
└LEVEL4_0 8G └LEVEL4_0 8G
@@ -126,7 +126,7 @@ Display table format, by default nesting level 3 is displayed.
$ zmemtopo -t $ zmemtopo -t
.nf .nf
.ft CW .ft CR
LEVEL 4 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 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 LEVEL 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
NR LPAR SUM NR LPAR SUM
@@ -150,7 +150,7 @@ Display reverse tree with nesting level 4.
$ zmemtopo -r -l 4 $ zmemtopo -r -l 4
.nf .nf
.ft CW .ft CR
LEVEL/LPAR SIZE LEVEL/LPAR SIZE
LEVEL4_0 388G LEVEL4_0 388G
│ ├LPAR003 8G │ ├LPAR003 8G
@@ -178,7 +178,7 @@ Display reverse full tree. Entries that have no memory increments are visible.
$ zmemtopo -rf $ zmemtopo -rf
.nf .nf
.ft CW .ft CR
.ft .ft
LEVEL/LPAR SIZE LEVEL/LPAR SIZE
LEVEL4_0 388G LEVEL4_0 388G
@@ -240,7 +240,7 @@ Display tree with entries which have no memory increments.
$ zmemtopo -f $ zmemtopo -f
.nf .nf
.ft CW .ft CR
LPAR/LEVEL SIZE LPAR/LEVEL SIZE
LPAR086 2G LPAR086 2G
├LEVEL4_0 - ├LEVEL4_0 -
@@ -287,7 +287,7 @@ Display json format.
$ zmemtopo --format=json $ zmemtopo --format=json
.nf .nf
.ft CW .ft CR
{ {
"meta": { "meta": {
"api_level": "1", "api_level": "1",
@@ -349,7 +349,7 @@ Display csv format.
$ zmemtopo --format=csv $ zmemtopo --format=csv
.nf .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" "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","8"
"0x0e082f818b066ce82000","74","1073741824","3","LPAR003","4","0","3","0","2" "0x0e082f818b066ce82000","74","1073741824","3","LPAR003","4","0","3","0","2"
+2 -1
View File
@@ -10,8 +10,9 @@ include ../common.mak
all: zpwr all: zpwr
OBJECTS = zpwr.o OBJECTS = zpwr.o
LDLIBS += -lm
zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a -lm zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a
install: all install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
+3 -3
View File
@@ -154,7 +154,7 @@ CPU, storage or I/O resources to partitions.
.SH "EXAMPLES" .SH "EXAMPLES"
1. Display power readings in human readable format. 1. Display power readings in human readable format.
.nf .nf
.ft CW .ft CR
# zpwr # zpwr
LPAR CPU: 140.00 W LPAR CPU: 140.00 W
LPAR Storage: 1.72 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 2. Perform two power measurements with a delay of 10 seconds and output the
data in json format. data in json format.
.nf .nf
.ft CW .ft CR
# zpwr --format json --delay 10 --count 2 # zpwr --format json --delay 10 --count 2
{ {
"meta": { "meta": {
@@ -223,7 +223,7 @@ data in json format.
.sp 1 .sp 1
3. Display power readings in csv format and in stream mode. 3. Display power readings in csv format and in stream mode.
.nf .nf
.ft CW .ft CR
# zpwr --format csv --stream # zpwr --format csv --stream
"iteration","time","time_epoch_sec","time_epoch_nsec","update_interval","cpu","storage","io","total","unassigned_resources","infrastructure" "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" "0","2025-01-08 07:17:05+0100","1736317025","592784684","10000000000","143000000","1708200","40000000","15550000000","7659162700","5337186700"