Compare commits

...

87 Commits

Author SHA1 Message Date
Jan Höppner
c8d1aa55b4 New release s390-tools-2.24.0
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-09 17:11:48 +01:00
Matthew Rosato
25a70ac5a8 libap: use util_lockfile and remove liblockfile dependency
Now that we have a utility library for file locking, remove all
calls to liblockfile functions from libap and remove all links to
the library from the current users of the libap liblockfile
implementation (ap_tools/ap-check and zdev).

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/142
Suggested-by: Luca BRUNO <luca.bruno@coreos.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-09 16:09:19 +01:00
Matthew Rosato
6235a51d7c libutil: add example for util_lockfile
Adds util_lockfile_example.c, which can be used to sample
util_lockfile support.

To acquire a lock using a parent PID (e.g. your shell instance):
util_lockfile_exmample -f <path> -l <retries>

To release the lock using the parent PID:
util_lockfile_example -f <path> -r

To acquire the lock, sleep briefly, and then release the lock
using the PID of the util_lockfile_example process:
util_lockfile_example -f <path> -L <retries>

In each example, the <path> is the location of the desired lockfile
and <retries> is the number of times to retry acquiring the lock
if it fails.

GitHub-ID: https://github.com/ibm-s390-linux/s390-tools/issues/142
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-09 16:09:19 +01:00
Matthew Rosato
e1aec24e84 libutil: introduce util_lockfile
Implement simple file-locking routines that use process PIDs for stale
lock detection.  The implementation is meant to be a simplified subset of
what liblockfile was previously being used for by libap, allowing the
external dependency to be removed.

This initial implementation provides a series of functions that allow for
creating/release file locks using either the current process PID or the
PID of the current process parent.  When creating a file lock, first a
temporary file is created and the appropriate PID (either this process
PID or the parent process PID) is placed in the file to specify the owner
of the lock.  Then an attempt is made to link that file to the desired
file location; if this succeeds, the lock is now held on behalf of the
specified PID.  If it fails, this implies the file already exists
(meaning the lock is already held).  In this case, stale lock detection
is performed by reading the PID from the file and ensuring that the
associated process still exists -- if it does not, then the lock is
presumed stale and destroyed.  If the process still exists, then either
the lock request fails or the caller will sleep and retry, depending on
an optional retry setting.

A lock remains valid until either 1) it is released via the corresponding
util_lockfile function, which will delete the corresponding file 2) the
associated PID no longer exists, which leaves the file in-place but will
cause it to be destroyed the next time a different process attempts to
lock that file or 3) the file is directly removed (e.g. rm).

A typical usecase for such support would be to provide a means for
multiple invocations of the same (or different) tools to ensure that they
do not access the same shared resource simultaneously.  For example,
ap-check, chzdev and lszdev all have a need to view and/or modify the AP
and vfio-ap configuration files; util_lockfile can be used to ensure that
only one instance of any of these utilities do that at a time by ensuring
they all use the same lockfile.
Additionally, providing the ability to specify the parent PID rather than
the current PID allows for a general purpose tool (like mdevctl) to
invoke a sub-program (ap-check) to acquire and release a lockfile as
necssary while allowing stale lock detection to be controlled by that
parent PID, allowing the lock to remain held over multiple sub-program
invocations.

Note that this implementation is sufficient for our current usage (e.g.
lockfiles placed in tmpfs) but does not take into consideration things
like NFS, which a more complete lockfile solution like liblockfile does.

GitHub-ID: https://github.com/ibm-s390-linux/s390-tools/issues/142
Suggested-by: Luca BRUNO <luca.bruno@coreos.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-09 16:09:19 +01:00
Eduard Shishkin
d9a6fb3ccf zipl/src: fix secure boot status check
Set up the secure_boot_supported global variable at the
function bootmap_create() instead of bootmap_create_device()
to make sure that the secure boot status is checked for all
relevant jobs.

Fixes: 7b369318dd ("zipl: check for secure boot once")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-09 16:09:19 +01:00
Steffen Eiden
a47dd4570a README: update zgetdump information
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
8fa1b5a00b zdump: dfi: add support to read Protected Virtualization dumps
Sometimes dumping a virtual machine from the outside is the only way to
get the data that is needed. This can be the case if a dumping mechanism
like kdump hasn't been configured or data needs to be fetched at a
specific point. Dumping a protected guest from the outside without help
from FW/HW doesn't yield sufficient data to be useful. Hence we have
introduced Protected Virtualization (PV) dump support - also named
confidential dump support.

The confidential dump support works by integrating the firmware into the
dump process. New Ultravisor calls (UVC) are used to initiate the dump
process, dump CPU data, dump memory state and lastly complete the dump
process. The guest's data is fully encrypted and can only be decrypted
by the entity that owns the customer communication key (CCK) for the
dumped guest. The output format is very similar the normal s390 vmcore
ELF format, it's only enriched by new sections where the returned data
from the UVC "Complete Configuration Dump" and the UVC "Dump
Configuration Storage State" is stored. The encrypted CPU data is stored
in a new note type `NT_S390_PV_CPU_DATA`. The old note types do still
exists but without any confidential data stored. The memory data is
stored in the LOAD segment as usual but for PV dumps it's fully AES-XTS
encrypted.

This commit adds support for reading/decrypting PV guest dumps to
zgetdump by introducing a new DFI input module (`dfi_pv_elf.c`). For
specifying the customer communication key a new command line option
`--key` is added.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
e537ab902e zdump: Makefile: add basic libpv support
The functionalities of libpv are used in the next patch. Libpv itself
requires glib2 and libcrypto.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
f0fb4a180a zdump: refactor usage print
The function will be reused in a later patch.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
f957d895cd zdump: consolidate error reporting
Consolidate error reporting to make sure that error messages are used
consistently. In addition, don't call `zg_exit()` directly, but instead
return the value 1 in `main()`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
73c3a9e684 libpv: fix memory leak in pv_get_openssl_error
While at it, improve documentation and adapt libpv code.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Marc Hartmayer
2ee1387f34 libpv/genprotimg/pvattest: use glib/gi18n.h
Replace `<glib/gi18n-lib.h>` with `<glib/gi18n.h>` since `<glib/gi18n-lib.h>` should only be used
for libraries (see https://docs.gtk.org/glib/i18n.html).

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00
Eduard Shishkin
10cefb3337 zipl/src/zipl_helper.device-mapper: Fix bug in error path
Prevent double free/close that happens in get_multipath_status()
when the output of "dmsetup status XXX" is empty

Fixes: 599b141 ("zipl: Rewrite helper script in C")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:09 +01:00
Dan Horák
7b369318dd zipl: check for secure boot once
Check for secure boot status only once when starting to create the
bootmap.

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/133
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Dan Horák
cf2fb296f0 zipl: be verbose about secure boot support
Report the details about the state of secure boot on the system when running
zipl in verbose mode.

GitHub-ID: https://github.com/ibm-s390-linux/s390-tools/pull/133
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
6cbd81f997 zdump: zg.h: consolidate PAGE_SIZE macros
Reuse `PAGE_SIZE` defined in `boot/s390.h`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
2025cf751f zdump: man: fix long form of abbreviation
ELF stands for "Executable and Linkable Format" (see
https://refspecs.linuxfoundation.org/elf/TIS1.1.pdf).

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
5a052103ae zdump: fix signatures of the wrappers zg_alloc, zg_realloc and zg_ioctl
Adapt the wrappers `zg_alloc`, `zg_realloc` and `zg_ioctl` to match the
signatures used by `calloc`, `realloc` and `ioctl`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Thomas Richter
2ced5853b4 cpumf/lshwc: fix incremented counter output
lshwc displays counter values extracted from the /dev/hwctr device.
The counter values are retrieved using a loop and incremented instead
of just displayed on output.
This leads to the situation where a counter is incremented in large
numbers while the counter triggering program is active. When this
program terminates, the counter should not increase but remain steady.

This is not the case as this example shows:

 # lshwc -i 2 -l90 -a :a | fgrep Total  | \
	awk -F ',' '{print $2 "," $152 "," $157}'
 Report time and values of DFLT_ACCESS and DFLT_CC counters
 sleep 10
 # DFLTCC=1 python3 -c "import zlib; zlib.compress(b'A'*6000000000);
 Increment counter DFLT_ACCESS and DFLT_CC, runs about 45 seconds

Output before:
15:24:20,0,0
15:24:22,0,0
15:24:24,0,0
15:24:26,0,0
15:24:28,0,0
15:24:30,0,0
15:24:32,2067027,592996481      <--- python3 program start
15:24:34,6953101,2039012580
15:24:36,14494585,4264340060
15:24:38,24759568,7346128396
....
15:24:56,232929262,70526362509
15:24:58,269037963,81464038124
15:25:00,307757248,93171809261
15:25:02,348900718,105620730492
15:25:04,392646075,118841031269  <--- python3 program stop
15:25:06,438956016,132853968029  <-- Value column two increased by 49148217
15:25:08,488104233,147709011658  <-- Similar high increase

The reason is that lshwc takes the last read counter value and keeps
adding it to the total count, even if there zero delta to the previous
read value. Fix this by clearing the total count value between ioctl()
read operations.

Output after:
15:39:26,0,0
15:39:28,0,0
15:39:30,0,0
15:39:32,0,0
15:39:34,0,0
15:39:36,1367250,380721127      <--- python3 program start
15:39:38,4104791,1108181711
15:39:40,6740994,1858843730
15:39:42,9277545,2646351211
15:39:44,11895180,3444375804
....
15:40:16,54060774,16315924126
15:40:18,57830349,17513712210
15:40:20,61628170,18690791102
15:40:22,65410470,19864086006
15:40:24,69070765,21028646754   <--- python3 program stop
15:40:26,70429758,21472772340   <-- No increase of value in column two
15:40:28,70429758,21472772340
15:40:30,70429758,21472772340
15:40:32,70429758,21472772340
15:40:34,70429758,21472772340
15:40:36,70429758,21472772340
15:40:38,70429758,21472772340

Fixes: 27a562da0a ("cpumf/lshwc: Program to extract complete counter sets")
Reported-by: Axel Busch <Axel.Busch@ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Acked-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Thomas Richter
13edbb7d5e cpumf/pai: fix core dump when summary flag set
pai dumps core when invoked with summary flag -S and
certain painnpa.XXXXX data files:

   # ~/s390-tools/cpumf/pai -r -S
   Segmentation fault (core dumped)
   #

Fix this and check the existence of event raw data before
reading it. This avoid accessing NULL pointer trying to read raw
data when no counter values have been appended as raw data by the
kernel.

Fixes: dc73c77d73 ("cpumf/pai: Omit detailed event information when summary flag selected")
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
1643af4578 zdev: dracut: modifiy the module-setup.sh
Export and import the configurations with zdev:early on the
site-specific configurations as well. Filter the site-specific
configurations by attribute "zdev:early" and import the output
to the corresponding sites.

Note that --site parameter does not work with the device type.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
c7ac1e63bd zdev: add export/import support for site
Add export and import support for site-specific settings. For each
site-specific configuration, a new site-block will be generated as
below. The same format can be interpreted by the import function and use
the values on the device's site-specific configuration.

Typical example of a site-block in the export file:

[site3 dasd-fba 0.0.f001]
online=1
cmb_enable=0

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
fa3dd0ec20 zdev: add --deconfigure-all function for chzdev
Introduce --deconfigure-all feature, which will remove all available
settings from the mentioned device. A typical device can have 10
different site-specific settings, active settings, persistent
settings and sometimes auto-configured settings configured in it.
--deconfigure-all can be used to remove all of them with a single
command.

usage: 	chzdev -D <device-id>
	chzdev --deconfigure-all <device-id>

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
34146c3f92 zdev: modify man-pages to update the site information
Add the new site parameter help information in the man-pages of both
chzdev and lszdev.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
298ede17f5 zdev: provide site-specific information in lszdev
lszdev must be modified to share the site-specific configuration
information from the persistent settings. The persistent configuration
array will be read and tabularized based on the site-id input from the
user.

This patch also accommodates the site specific information in the
lszdev --info command. New information about the site-ids are integrated
with the command. For example,

DEVICE dasd-eckd 0.0.f001
  Names              : -
  Modules            : dasd_eckd_mod dasd_mod
  Online             : no
  Exists             : yes
  Persistent         : yes
  Sites              : 1,2

  ATTRIBUTE                     ACTIVE    PERSISTENT  SITE1  SITE2
  cmb_enable                    "0"       "0"         "1"    -
  eer_enabled                   "0"       -           -      -
  erplog                        "0"       -           -      -
  failfast                      "0"       -           -      -
  last_known_reservation_state  "none"    -           -      -
  online                        "0"       "1"         "1"    "1"
  raw_track_access              "0"       -           -      -
  readonly                      "0"       -           -      -
  reservation_policy            "ignore"  -           -      -
  use_diag                      "0"       -           -      -

Each available site specific configuration will be shown as column. The
"Sites" parameter in the above output indicates the available site
configurations and the SITE1 and SITE2 colums indicates the detailed
configuration for the respective sites.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
d66ace8121 zdev: add functionality to read site-specific settings
Modify the dev_get_setting_list function to get the site-specific
attributes of the device. The new site_id parameter for this function
must be less than SITE_FALLBACK to read the site-specific attributes.
As SITE_FALLBACK setting is same as persistent setting, we do not need
a separate read function for it.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
2e89722ef0 zdev: make site specific udev-rule for ccw
The udev-rules must be modified to make it site-specific. The site
specific part of the rule should be executed only on the sites where the
devices belong. Change the generic udev rule to support the site-specific
udev-rule.

During boot, the current site-id is read from the LOADPARM which is
located at /sys/firmware/ipl. The zdev_id command reads this LOADPARM
and prints the SITE_ID based on the value. This SITE_ID is the
current site where the udev-rule is executing. Based on this assumption,
the rule is divided in to multiple blocks where each site block is executed
only for the corresponding sites.

The rule executes based on the value read from the LOADPARM.
1. When the LOADPARM specifies a site-id and if the current device has
   an associated configuration settings, it will be used.
2. When the LOADPARM specifies a site, and the current device does not
   have any associated configuration set, the udev rule uses the
   fallback configuration settings.
3. If the device does not have a fallback-configuration settings or any
   site-specific settings, no configurations will be used.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
c8ad5f57d0 zdev: modify zdev_id to read the site_id from loadparm
The currently active site can be found from the loadparm attribute which
is located at /sys/firmware/ipl/loadparm. Read the value of loadparm and
extract the site_id.

The loadparm is an 8 byte alphanumerical IPL parameter. The content of
loadparm can be of different forms to indicate different boot menu
options, which are used by Linux for selecting the respective boot menu
entries. To indicate the site-id, loadparm adds a 'S' character. Where,
the following character after 'S' indicates either the site-id or
indivcation to derive the site-id from SSID.
For example,
If the loadparm value is Sn,'n' is the integer which could be one of the
valid site_ids from 0 to 9; i.e when booted on site-1, loadparm value will
be S1.
If loadparm value is "SS", zdev_id extracts the site_id from the SSID of
the current ipl device. For ccw and zfcp devices, the current ipl device-id
can be found at /sys/firmware/ipl/device.

In any case, if the loadparm contains invalid value, or empty, the
site_id will be default to the common-site.

After the first invocation of the zdev_id from udev-rule, the site-id
information is stored in /run/zdev_id.env file. This will reduce the
number of further invocation of zdev_id from within udev-rules.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
d2317943c0 zdev: add site specific states for devices
Currently zdev supports the configuration and maintenance of one single
persistent device state. With the introduction of sites, zdev should be
able to store, configure and maintaine NUM_SITES number of states in
the persistent configuration. This patch modifies the struct device and
adds a new site_specific device_state array, which stores the
persistent configuration for all the available sites.

Total number of available sites are configured with NUM_SITES. Where,
10 site-specific configurations and 1 common configuration.

1. during read, all site data is always read and update the
   site_specific[] array.
2. dev->persistent is a copy of dev->site_specific[global_site_id]
3. modifications are only done on dev->persistent, and during write
   dev->persistent overlays dev->site_specific[global_site_id]

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Vineeth Vijayan
198c9fc052 zdev: onboard --site option on zdev commands
In some high-availability setups, root and boot disks of a Linux
installation are copied to remote sites that can take over execution
of a Linux workload in case of an outage of the original site.
A site in this context is an execution environment such as an LPAR
or z/VM guest.

Each site may provide a different set of devices, or require different
parameters to be applied per device. chzdev supports site-specific
device configuration for up to 10 sites. Each site is identified by
a number in the range 0 to 9. You can provide a separate set of persistent
device configuration for each site. The IPL Load parameter value specified
during boot determines the currently active site. Only the device
configuration of the active site is applied during boot and when new
devices become available. A common configuration can be provided that is
applied when no site-specific configuration is available for a device in
the active site.

User can use the --site parameter to configure devices for a specific
site only. Configuration actions without a site parameter apply to the
common configuration.

During the boot, the active site configuration is chosed based on the
IPL LOADPARM variable. If the LOADPARM contains no valid site-id (0 to 9),
the fallback-site id will be used. The fallback-site-id can also be
configured with the zdev tools by not specifying the --site parameter.

Some of the new chzdev/lszdev commands with site-parameter can be as
follows

1. chzdev -ep f001 --site 3

This command is used to configure the device f001 for the site 3. During
boot, if the LOADPARM value is S3, this device configuration will be
used.

2. lszdev --site 3

This command lists all the devices which are configured for site 3.

3. chzdev -ep f001

Above command does not have a --site parameter. This device settings
will be used for the following conditions

1. When the loadparm specifies a site, (e.g S3), and the device does not
   have an associated config-set, zdev uses fallback config set.
2. When the loadparm does not specify any site information, then the
   fallback config set will be used.

When the loadparm specifies a site, and the device does not have an
associated config set, nor a fallback site, nothing is configured for this
device.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
b3551f50a8 Provide codespell configuration
While at it, add a list with words to ignore. This suppresses the following codespell finding:

$ codespell libcpumf/libcpumf_cpuset.c
libcpumf/libcpumf_cpuset.c:15: parm ==> param, pram, parma
libcpumf/libcpumf_cpuset.c:17: parm ==> param, pram, parma

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
4ff360e621 Provide CheckPatch configuration
Provie a CheckPatch configuration file and mention it in the coding style
guidelines (this feature was introduced with CheckPatch version 0.32 ~11 years
ago).

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
87e8fa18cf CODINGSTYLE.md: mention EditorConfig configuration
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Marc Hartmayer
4bd135c3c7 Add project-wide .clang-format
Add a .clang-format configuration and mention it in the coding guidelines.

Usage example:

 $ clang-format -i **/*.[ch]

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00
Matthew Rosato
f69350e8bd ap_tools/ap-check: reject start for control domains without usage
When configuring a vfio-ap device, specifying only control domains
without any usage domains results in a useless configuration in that
both adapters and usage domains are required to make cryptographic
resources available to the guest.  Without that, the control domains
are useless.  While the kernel allows this, let's reject this
combination with a message.

Suggested-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Reviewed-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Reviewed-by: Tony Krowiak <akrowiak@linux.ibm.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:22 +02:00
Peter Oberparleiter
5768d55a08 zipl/boot: add secure boot trailer
This patch enhances the zipl stage3 loader image adding a trailer as
required for secure boot by future firmware versions.

Note: with the change in this patch the padding via objcopy command line
options is replaced by padding via linker script directives with the
same effect.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Holger Dengler
538264a8cd lszcrypt: fix parameter handling for device list
lszcrypt allows the explicit definition of a list of devices. Continue
the parameter processing if multiple devices are specified.

Signed-off-by: Holger Dengler <dengler@linux.ibm.com>
Reviewed-By: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
7f09f0e4ad zdump: refactor pt_load_add
In an upcoming patch the refactored function `pt_load_add` is reused. While at
it, add a wrap-around check to `pt_load_add`, replace `g.opts.device` access
with `fh->path` (this makes this function easier testable) and improve the
documentation.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
e36084d7f2 libpv/pvattest: consolidate __PV_BIT and BIT macros
Consolidate `__PV_BIT` and `BIT` macros.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
bd18166a21 zdump: opts: declare print_usage_exit in header file
Remove `static` attribute from `print_usage_exit` and declare it in the header
file `opts.h`. The function will be reused in a later patch.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
a6f2c38891 zdump: constify dfi_vmcoreinfo_get
Caller of `dfi_vmcoreinfo_get` must not modify the returned value, therefore
return a `const char *` value for `dfi_vmcoreinfo_get`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
029a3f8f52 zdump: add guarded storage support for ELF input and output format
Add guarded storage registers support for the ELF input and the ELF output
format. See `man 2 s390_guarded_storage` for details about guarded storage.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
dc87aef335 zdump: dfo_elf: replace HDR_PER_CPU_SIZE with get_max_note_size_per_cpu
The maximum size of all supported ELF note entries for a CPU can be 0x4a4 bytes
and not 0x4a0. Use a function for the calculation so it's easier to maintain.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
d712806b39 lib/zt_common.h: add macro for determining field size
The macro `sizeof_field` is used in the next patch.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
2734724432 zdump: df_elf: add multiple ELF section helper
Preparation for upcoming patches.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
6176509076 zdump: df_elf: refactor check_elf_hdr
The function will be reused by upcoming patches.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
1780efae07 zdump: df_elf: ehdr_is_elf_object: add ELF version check
Currently, only the ELF version `1` is defined by the ELF specification. See
`man 5 elf` for details. Therefore of course, we do only support this version.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
922e8cbb2f zdump: since we create ELF version 1 files use this value explicitly
The value of `EV_CURRENT` might change. The ELF specification says: "The value
of EV_CURRENT, though given as 1 above, will change as necessary to reflect the
current version number." [1]

[1] https://refspecs.linuxfoundation.org/elf/elf.pdf

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
eef0dd4fa4 zdump: df_elf: add missing endianness check
A s390x ELF file must use big-endian encoding.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
f93f437f8b zdump: df_elf: read_elf_hdr: return Elf64_Ehdr struct
This makes the API of `read_elf_hdr` consistent with `read_elf_phdrs`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
4e6d43e2ac zdump: dfi_elf: refactor nt_* functions
The functions are refactored so the `struct zg_fh` value is now passed as
argument to the `nt_*` functions instead of accessing the global variable `g.fh`
from within of the functions. This makes the functions reusable and easier to
test. While at it, improve the documentation of `nt_read`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
922d29ee49 zdump: dfi_elf_init: introduce temporary variable
Introduce variable `phdrs` and shrink the scope of `phdr`. This removes
duplicated code `&phdr[i]` and makes the code easier to read. While at it,
constify the parameter of `pt_notes_add`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
25475bdf42 zdump: read_elf_phdrs: fix -Wconversion issue
`ehdr->e_phoff` has the type `Elf64_Off` and this maps to `uint64_t`, but `off_t` is signed. Let's
add a check for this.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
09c413b1be zdump: read_elf_phdrs: handle no program header case
Handle the case where no program headers are defined (see `man 5 elf`).

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
8944c2f60d zdump: dfi_elf: refactor read_elf_(ehdr|phdrs)
Make the function unit testable and easier to reuse - no functional change.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
ff3fe4bf43 zdump: constify struct zg_fh *fh parameter in the zg file APIs
Constify the `struct zg_fh *fh` parameter in the file APIS, e.g. `zg_path()`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Joern Siglen
5fe2e19277 dbginfo.sh: fix man page
add missing option in man page

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Joern Siglen
b8cf861aea dbginfo.sh: add kuberentes (OCP) data collection
generalize the collection step name from "docker" to "container host"

initial version of adding data collection for OCP.

all log file content might be too much for most installations

Reviewed-by: Alexander Klein <alkl@de.ibm.com>
Reviewed-by: Mario Held <mario.held@de.ibm.com>
Reviewed-by: Mike Storzer <mstorzer@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Joern Siglen
6ca3576dbf dbginfo.sh: fix typo
typo in KVM section

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
7ea425db5e pvattest/exchange_format.c: use proper endianess conversion macro for hdr->version
GUINT32_TO_BE and GUINT32_FROM_BE do exactly the same in case of glib2, just the
naming differs. However, at this point the conversion is from be to system
endianness therefore `GUINT32_FROM_BE` is the proper macro.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
330ddb7adb libpv: add copied parameter to pv_gbytes_memcpy and adapt pvattest
It's often useful to know how much data was actually copied, therefore let's
introduce an nullable parameter `@copied` to `pv_gbytes_memcpy`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
d69b9fab4a pvattest: build_attestation_v1_ioctl: make the code more robust
...by adding a compile time assertion about the buffer size. While at it, report
the actual user data size in case of an error.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
4e02dc8751 pvattest: uvio.c: remove endianness handling
The endianness handling was only partially implemented for uvio.c. Let's remove
it since doesn't make much sense here, because the IOCTL must be build on the
same system where they're actually executed.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Joern Siglen
0c8be405ce dbginfo.sh: fix vmcp buffer resizing
the buffer sizing did not work any longer
add explaining comments and rework the variable names for claritiy

refering commit f7cc14753cc9d930af804e2ac3ce108a12145db6

Reported-by: Mike Storzer <MSTORZER@de.ibm.com>
Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Steffen Eiden
24677dc561 libpv: remove wrong parameter attribute
`error` is actually used therefore let's remove the `G_GNUC_UNUSED` attribute.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Steffen Eiden
7e7c38755d libpv: improve error reporting
Use `PV_CERT_ERROR` error domain whenever useful. Currently, it's only used
internally in libpv and therefore there is no problem in changing it.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
229cd6d194 zdump: don't modify passed program header in pt_load_add()
There should be no problem with this change since the changed value was never
used. Therefore, let's remove the assignment and declare the parameter as const
to prevent such a behavior in the future.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
776e5d41d7 zdump: dfi_elf: factor out some ELF functionalities
This makes them reuseable and testable.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
8d7e8a87b5 zdump: df_elf: move function documentation to function declarations
It's more common to have the function documentation next to the function
declarations.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
e24d8c936c zdump: move NOTE_NAME_* definitions to df_elf.h
This allows the reuse in other source files.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
39b6c4a273 zdump: introduce ELF_NOTE_ROUNDUP macro
Introduce and use `ELF_NOTE_ROUNDUP` macro.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Marc Hartmayer
da9b96fb12 lib/zt_common.h: consolidate ROUNDUP macro
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00
Alexander Egorenkov
7d855acd07 util_arch: Fix max HSA size for Z16 machine
The machine types are not strictly increasing anymore since Z16, therefore,
we cannot use numerical comparison to find out the correct HSA size
of a machine.

Fixes: 2515832469 ("util_arch: Add IBM z16 as known machine")
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Marc Hartmayer
f961a2bf90 common.mak: fix compiler command line for afl-clang
`afl-clang` requires a white space between a option name and the option value.
Therefore let's fix this for `-xc` by adding a white space.

How to reproduce:

$ make -C zdump CC=afl-clang-fast LINK=afl-clang-fast

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Joern Siglen
9f93af614f dbginfo.sh: ensure compatibility with /bin/dash
some versions of /bin/dash do interpreted our commands different, which
causes a variable failure and termination of the script.

e.g. a new line char "\n" will split the cmd_type to a multiple line variable
not working with the followup commands. adding the head command does avoid
multiline variables now.

Problem found in Ubuntu 20.4 and some older Ubuntu versions

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Joern Siglen
d529ba07d3 dbginfo.sh: update message on not beeing root
add a hint that dbginfo.sh -c can check some basic stuff even as non-root

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Joern Siglen
0fffb05d01 dbginfo.sh: add tool check for testing
move tool checking to the top of the script and add the output to
the check function (-c) for easy verifivation and testing

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Joern Siglen
92b8409207 dbginfo.sh: ensure type commands compatible with dash
All debian distors use the dash shell as default, where type is not supprting "-t" parameter

Remove of "-t" where not important and change type check not using -t

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:34 +02:00
Steffen Maier
bc3134ba5a dbginfo.sh: collect config and logs of Dandified YUM (DNF)
Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:31 +02:00
Steffen Maier
c7f55a182f dbginfo.sh: collect YaST log files of successful installation
Cf. /usr/sbin/save_y2logs of yast2-logs.rpm.

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:28 +02:00
Steffen Maier
5078be45b8 dbginfo.sh: collect generated unattended installation kickstart files
anaconda-7.2.91-1
c4249bbe06 (diff-86b9da41b3aa75b36055aa04a2effff06c0decadc7b80f6efd8a513fe265746eR49)
("merge dispatch to HEAD")

anaconda-24.7-1
1c4af3b432
("Include original kickstart in /root/original-ks.cfg (#1227939)")
https://github.com/rhinstaller/anaconda/pull/447
https://bugzilla.redhat.com/show_bug.cgi?id=1227939

Cf. https://anaconda-installer.readthedocs.io/en/latest/boot-options.html#inst-nosave
from anaconda-25.2-1
b7d699a547
("Make it possible to skip saving of kickstarts and logs (#1285519)")

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:24 +02:00
Steffen Maier
2418ea93fc dbginfo.sh: collect anaconda log files of successful installation
Since anaconda-15.2-1
7e2754473e (diff-3b740ff14364c6d399b86ef7a6ca044e17b334ab351988600058d9af0e7fa0bc)

See also /usr/libexec/anaconda/log-capture of anaconda-core.rpm.

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:26:21 +02:00
Steffen Maier
13cac5de44 dbginfo.sh: also collect "local" multipath-tools config
Easier to read for a first glimpse than the (still required) full config.

$ man multipathd
       list|show config local
              Show the currently used configuration like show config, but lim‐
              iting  the  devices  section  to those devices that are actually
              present in the system.

Works as of multipath-tools 0.7.8 commit
c23924dc74ee ("multipathd: implement "show config local"")

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:17:31 +02:00
Joern Siglen
b6687925cc dbginfo.sh: cleanup outdated comment and formating
remove spaces and outdated comment line

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:17:31 +02:00
Marc Hartmayer
7ee51182bf zdump: fix memory leak of path
==660622== 385 bytes in 7 blocks are definitely lost in loss record 30 of 37
==660622==    at 0x484417C: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-s390x-linux.so)
==660622==    by 0x4E93BD7: strdup (strdup.c:42)
==660622==    by 0x11249F: zg_strdup (zg.c:115)
==660622==    by 0x11249F: zg_open (zg.c:164)
==660622==    by 0x114FBF: dfi_dump_open (dfi.c:750)
==660622==    by 0x114FBF: dfi_init (dfi.c:772)
==660622==    by 0x111465: do_copy (zgetdump.c:192)
==660622==    by 0x111465: main (zgetdump.c:216)

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:17:31 +02:00
Siglen
ec3e5b4a75 dbginfo.sh: save dbginfo.sh version to dbginfo.log
we do miss a simple way to see the dbginfo.sh version used by the
customer for the debug generation. saving the version now to dbginfo.log

Reported-by: Mike Storzer <MSTORZER@de.ibm.com>
Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:17:31 +02:00
Jan Höppner
0b5aac3d79 Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-08-18 15:33:16 +02:00
91 changed files with 4948 additions and 602 deletions

12
.checkpatch.conf Normal file
View File

@@ -0,0 +1,12 @@
--max-line-length=100
--strict
# Not Linux therefore don't expect a Linux tree
--no-tree
# Ignore the following
--ignore PREFER_KERNEL_TYPES
--ignore EXECUTE_PERMISSIONS
--ignore FILE_PATH_CHANGES
--ignore NEW_TYPEDEFS
--ignore SPDX_LICENSE_TAG

134
.clang-format Normal file
View File

@@ -0,0 +1,134 @@
# Must be compatible with clang-format 14
---
AccessModifierOffset: "-4"
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: AcrossComments
AlignConsecutiveMacros: AcrossComments
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BitFieldColonSpacing: Both
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: BeforeComma
BreakConstructorInitializersBeforeComma: false
BreakStringLiterals: false
ColumnLimit: "100"
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: "8"
ContinuationIndentWidth: "8"
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
# git grep -h '^#define [^[:space:]]*\(for_each\|iterate\)[^[:space:]]*(' \
# | sed "s,^#define \([^[:space:]]*\(for_each\|iterate\)[^[:space:]]*\)(.*$, - '\1'," \
# | LC_ALL=C sort -u
ForEachMacros:
- "dfi_cpu_iterate"
- "dfi_mem_chunk_iterate"
- "dfo_chunk_iterate"
- "for_each_rb_entry"
- "ns_range_for_each"
- "sd_cpu_item_enable_iterate"
- "sd_cpu_item_iterate"
- "sd_cpu_iterate"
- "sd_cpu_type_iterate"
- "sd_sys_item_enable_iterate"
- "sd_sys_item_iterate"
- "sd_sys_iterate"
- "table_col_iterate"
- "table_iterate_mark_keys"
- "util_list_iterate"
- "util_list_iterate_safe"
- "util_opt_iterate"
- "util_rec_iterate"
IncludeBlocks: Preserve
# Leave imports as is
IncludeCategories:
- Regex: .*
Priority: 1
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: "8"
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: "1"
NamespaceIndentation: Inner
PackConstructorInitializers: CurrentLine
PenaltyBreakAssignment: "10"
PenaltyBreakBeforeFirstCallParameter: "30"
PenaltyBreakComment: "10"
PenaltyBreakFirstLessLess: "0"
PenaltyBreakString: "10"
PenaltyExcessCharacter: "100"
PenaltyReturnTypeOnItsOwnLine: "60"
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptForEachMacros
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: "1"
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
StatementMacros:
- "STATIC_ASSERT"
TabWidth: "8"
TypenameMacros:
- "STACK_OF"
UseTab: Always
WhitespaceSensitiveMacros:
- "STRINGIFY"

5
.codespellrc Normal file
View File

@@ -0,0 +1,5 @@
[codespell]
ignore-words-list = parm,parms
skip = ''
count = ''
quiet-level = 3

View File

@@ -1,6 +1,29 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.24.0 (2022-11-09)__
For Linux kernel version: 6.0
Add new tools / libraries:
- Provide config files for checkpatch, codespell, and clang-format
Changes of existing tools:
- dbginfo.sh: Collect log from various distro tools (YaST, DNF, Anaconda)
- dbginfo.sh: add Kubernetes data collection
- libutil: Introduce util_lockfile
- zdev: Add site-aware device configuration
- zdump: Add support to read Protected Virtualization dumps
- zipl/boot: Add secure boot trailer
Bug Fixes:
- ap_tools/ap-check: Reject start for control domains without usage
- cpumf/lshwc: Fix incremented counter output
- cpumf/pai: Fix core dump when summary flag set
- dbginfo.sh: Ensure compatibility with /bin/dash shell
- dbginfo.sh: Save dbginfo.sh version to dbginfo.log
- zipl/src/zipl_helper.device-mapper: Fix bug in error path
* __v2.23.0 (2022-08-18)__
For Linux kernel version: 5.19
@@ -24,7 +47,6 @@ Release history for s390-tools (MIT version)
- pvattest: Fix dependency checking
- zipl: Fix segmentation fault when no parmline is provided
* __v2.22.0 (2022-06-20)__
For Linux kernel version: 5.18

View File

@@ -4,8 +4,10 @@ Coding guidelines for s390-tools
For s390-tools the preferred language is C. We provide libraries, e.g.
[`libutil`](libutil) that should be used by all tools if possible.
The coding style is based on the Linux [kernel guidelines]. Therefore, use
the [checkpatch] tool for verification before you submit a patch.
The coding style is based on the Linux [kernel guidelines]. Therefore, use the
[checkpatch] tool for verification before you submit a patch. s390-tools
provides a CheckPatch configuration for this - see
[`.checkpatch.conf`](.checkpatch.conf).
[kernel guidelines]: https://www.kernel.org/doc/html/latest/process/coding-style.html
[checkpatch]: https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl
@@ -17,6 +19,39 @@ This package started in 2001 and has a long "tradition" - therefore, older tools
might not follow all recommendations. Note that when changing existing code,
consistency could have priority over applying rules.
Automatic Code Formatting
-------------------------
> **NOTE:** clang-format is a helpful tool but please don't use it blindly!
s390-tools provides a ClangFormat (https://clang.llvm.org/docs/ClangFormat.html)
configuration file - see [`.clang-format`](.clang-format). It can be used to
format your C/C++ code automatically.
Clang-format can format a single file or multiple files at once. For example, to
format `main.c` in place, run the following command in a terminal:
```bash
clang-format -i main.c
```
In order to format only your current staged changes use the clang-format git
plugin:
```bash
git clang-format --staged
```
See also `git clang-format -h`.
Automatic Editor Configuration
------------------------------
s390-tools provides a EditorConfig (https://editorconfig.org/) configuration
file - see [`.editorconfig`](.editorconfig). EditorConfig defines coding style
rules (such as indentation size, use of tabs, etc.) and is supported by most
common editors natively or via plugins.
Standard abbreviations
----------------------

View File

@@ -60,6 +60,7 @@ Package contents
* zgetdump:
Retrieve system dumps from either tapes, DASDs, SCSIs or NVMes.
Decrypt Protected Virtualization (PV) dumps from IBM Secure Execution guests.
* qetharp:
Read and flush the ARP cache on OSA Express network cards.
@@ -309,16 +310,15 @@ build options:
| net-snmp | `HAVE_SNMP` | osasnmpd |
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
| | | libkmipclient, pvattest |
| | | libkmipclient, pvattest, zgetdump |
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
| | | libkmipclient |
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest |
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest, zgetdump |
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
| | | pvattest |
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
| systemd | `HAVE_SYSTEMD` | hsavmcore |
| liblockfile | `HAVE_LOCKFILE` | ap-check |
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
This table lists additional build or install options:
@@ -376,6 +376,13 @@ the different tools are provided:
- Packages: blktrace, multipath-tools, sg3-utils
- Tools: rsync, tar, lsscsi
* zgetdump
For building zgetdump you need OpenSSL version 1.1.0 or newer
installed (openssl-devel.rpm). Also required is glib2
(glib2-devel.rpm) and zlib (zlib-devel.rpm).
Tip: you may skip the zgetdump build by adding
`HAVE_OPENSSL=0`, `HAVE_GLIB2=0`, or `HAVE_ZLIB=0`.
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
FUSE is provided by installing the fuse3 and libfuse3 packages and by a
@@ -505,8 +512,7 @@ the different tools are provided:
GNU awk for the build process.
* ap-check:
For building the ap-check mdevctl callout utility you need liblockfile
version 1.14 or newer installed (liblockfile-devel.rpm). Also required is
json-c version 0.13 or newer (json-c-devel.rpm).
Tip: you may skip ap-check build by adding `HAVE_LOCKFILE=0` or `HAVE_JSONC=0`
to the make invocation.
For building the ap-check mdevctl callout utility you need json-c version
0.13 or newer (json-c-devel.rpm).
Tip: you may skip ap-check build by adding `HAVE_JSONC=0` to the make
invocation.

View File

@@ -7,21 +7,14 @@ MDEVCTL_CALLOUTS = /etc/mdevctl.d/scripts.d/callouts/
libs = $(rootdir)/libap/libap.a \
$(rootdir)/libutil/libutil.a
ifeq (${HAVE_LOCKFILE},0)
all:
$(SKIP) HAVE_LOCKFILE=0
install:
$(SKIP) HAVE_LOCKFILE=0
else ifeq (${HAVE_JSONC},0)
ifeq (${HAVE_JSONC},0)
all:
$(SKIP) HAVE_JSONC=0
install:
$(SKIP) HAVE_JSONC=0
else
LDLIBS += -llockfile -ljson-c
LDLIBS += -ljson-c
all: ap-check

View File

@@ -133,7 +133,7 @@ static void ap_check_cleanup(struct ap_check_anchor *anc)
if (anc->dev)
vfio_ap_device_free(anc->dev);
if (anc->cleanup_lock)
ap_release_lock();
ap_release_lock_callout();
}
/*
@@ -685,6 +685,14 @@ static int ap_check_handle_start(struct ap_check_anchor *anc)
goto out;
}
/* Ensure device with control domains also has usage domains */
if (util_list_is_empty(anc->dev->domains) &&
!util_list_is_empty(anc->dev->controls)) {
fprintf(stderr, "At least one usage domain must be specified\n");
rc = -1;
goto out;
}
/* Check against all other active vfio-ap devices */
rc = check_other_mdevs_sysfs(anc);
/* Check against the system sysfs values for apmask/aqmask */
@@ -767,7 +775,7 @@ out:
*/
static int ap_check_handle_post(void)
{
return ap_release_lock();
return ap_release_lock_callout();
}
/* For the specified device, print the attributes to stdout in JSON format */

View File

@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION = 2
RELEASE = 23
RELEASE = 24
PATCHLEVEL = 0
DISTRELEASE = build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
@@ -120,7 +120,7 @@ endif
define check_header_prereq
$(shell printf "#include <%s>\n int main(void) {return 0;}\n" $1 | \
( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) \
$2 -o /dev/null -xc - ) >/dev/null 2>&1 && echo -n yes)
$2 -o /dev/null -x c - ) >/dev/null 2>&1 && echo -n yes)
endef
#
@@ -133,7 +133,7 @@ endef
# $5: Additional compiler & linker options (optional)
#
check_dep=\
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -xc - ) > /dev/null 2>&1; \
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -x c - ) > /dev/null 2>&1; \
if [ $$? != 0 ]; \
then \
printf " REQCHK %s (%s)\n" $1 $2; \

View File

@@ -356,6 +356,8 @@ static void line(char *header)
putchar(',');
printf("%ld", ctrname[i].total);
comma = true;
ctrname[i].total = 0;
ctrname[i].hitcnt = false;
}
putchar('\n');
}
@@ -446,7 +448,7 @@ static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
ctrname[idx].ccv = calloc(max_possible_cpus,
sizeof(unsigned long));
if (ctrname[idx].ccv)
ctrname[idx].ccv[cpu] += value;
ctrname[idx].ccv[cpu] = value;
ctrname[idx].total += value;
ctrname[idx].hitcnt = true;
return true;
@@ -458,10 +460,8 @@ static int test_read(struct s390_hwctr_read *read)
size_t offset = 0;
/* Clear previous hit counters */
for (unsigned int i = 0; i < max_possible_cpus; ++i) {
check[i].sets_hit = 0;
for (unsigned int i = 0; i < max_possible_cpus; ++i)
check[i].cpu_hit = false;
}
/* Iterate over all CPUs */
for (unsigned int i = 0; i < read->no_cpus; ++i) {

View File

@@ -426,7 +426,7 @@ static const char *evt_selector(struct perf_event_attr *pa)
static void evt_show(__u64 evtnum, const char *evtsel, struct pai_event_out *ev)
{
if (summary) {
if (ev->type == PERF_RECORD_SAMPLE)
if (ev->type == PERF_RECORD_SAMPLE && ev->raw)
evtraw_show(evtnum, ev->raw);
return;
}

View File

@@ -10,7 +10,7 @@
#define GETTEXT_PACKAGE "genprotimg"
#include <glib.h>
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include "boot/linux_layout.h"
#include "boot/s390.h"

View File

@@ -90,5 +90,6 @@ void ap_list_remove_all(struct util_list *list);
int ap_get_lock(void);
int ap_get_lock_callout(void);
int ap_release_lock(void);
int ap_release_lock_callout(void);
#endif /* LIB_AP_H */

View File

@@ -0,0 +1,26 @@
/**
* @defgroup util_lockfile_h util_lockfile: File locking utility
* @{
* @brief Create file-based locks
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef LIB_UTIL_LOCKFILE_H
#define LIB_UTIL_LOCKFILE_H
#define UTIL_LOCKFILE_OK 0 /* Lock acquired/released successfully */
#define UTIL_LOCKFILE_LOCK_FAIL 1 /* Lock already held, ran out of retries */
#define UTIL_LOCKFILE_RELEASE_NONE 2 /* Lock not held */
#define UTIL_LOCKFILE_RELEASE_FAIL 3 /* Lock not held by specified pid */
#define UTIL_LOCKFILE_ERR 4 /* Other, unexpected error conditions */
int util_lockfile_lock(char *lockfile, int retries);
int util_lockfile_parent_lock(char *lockfile, int retries);
int util_lockfile_release(char *lockfile);
int util_lockfile_parent_release(char *lockfile);
#endif /** LIB_UTIL_LOCKFILE_H @} */

View File

@@ -32,6 +32,10 @@
#ifndef __ASSEMBLER__
#ifndef sizeof_field
#define sizeof_field(type, field) (sizeof((type *)0)->field)
#endif
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
@@ -50,6 +54,10 @@
#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#ifndef ROUNDUP
# define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#endif
#define RELEASE_STRING STRINGIFY (S390_TOOLS_RELEASE)
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
@@ -62,6 +70,7 @@
#define __may_alias __attribute__((may_alias))
#define __section(x) __attribute__((__section__(#x)))
#define __noinline __attribute__((__noinline__))
#define __big_endian
/* The Linux kernel (in stddef.h) and glibc (sys/cdefs.h) define
* __always_inline. Therefore undefine it first to allow the headers
* to be included first.

View File

@@ -68,6 +68,7 @@ typedef enum {
PV_CERT_ERROR_MALFORMED_ROOT_CA,
PV_CERT_ERROR_NO_CRL,
PV_CERT_ERROR_NO_CRLDP,
PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND,
PV_CERT_ERROR_NO_PUBLIC_KEY,
PV_CERT_ERROR_READ_CERTIFICATE,

View File

@@ -15,7 +15,7 @@
*/
#include "libpv/glib-helper.h"
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include "libpv/openssl-compat.h"
#include "libpv/macros.h"

View File

@@ -42,11 +42,14 @@ enum PvCryptoMode {
PV_DECRYPT,
};
/** pv_get_openssl_error:
/** pv_get_openssl_errors:
*
* Returns: (transfer full): String representing the error.
* Returns the last OpenSSL error messages.
* Caller is responsible to free the returned value.
*
* Returns: String representing the error.
*/
const char *pv_get_openssl_error(void);
char *pv_get_openssl_errors(void);
/**
* pv_BIO_reset:
@@ -181,7 +184,6 @@ typedef enum {
PV_CRYPTO_ERROR_KEYGENERATION,
PV_CRYPTO_ERROR_RANDOMIZATION,
PV_CRYPTO_ERROR_READ_FILE,
PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
PV_CRYPTO_ERROR_NO_MATCH_TAG,
} PvCryptoErrors;

View File

@@ -102,9 +102,12 @@ FILE *pv_file_open(const char *filename, const char *mode, GError **error);
/** pv_gbytes_memcpy:
*
* memcpy with size check.
* memcpy with size check. In case @dst_size is smaller than the size of @src
* the error value %NULL is returned and @copied and @dst are left unchanged.
*
* If @dst and @src data overlap, the behavior is undefined.
*/
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src);
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied);
#define PV_GLIB_HELPER_ERROR g_quark_from_static_string("pv-glib-helper_error-quark")
typedef enum {

View File

@@ -10,7 +10,12 @@
#ifndef LIBPV_MACROS_H
#define LIBPV_MACROS_H
#include <stdint.h>
#define PV_NONNULL(...)
#define DO_PRAGMA(x) _Pragma(#x)
/* Most significant bit */
#define PV_MSB(idx) ((uint64_t)1 << (63 - (idx)))
#endif /* LIBPV_MACROS_H */

View File

@@ -15,6 +15,7 @@
#include "boot/s390.h"
#include "libpv/crypto.h"
#include "libpv/macros.h"
/* Magic number which is used to identify the file containing the PV
* header
@@ -22,20 +23,17 @@
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
#define PV_VERSION_1 0x00000100U
/* Internal helper macro */
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
/* Plaintext control flags */
/* dumping of the configuration is allowed */
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34)
#define PV_PCF_ALLOW_DUMPING PV_MSB(34)
/* prevent Ultravisor decryption during unpack operation */
#define PV_PCF_NO_DECRYPTION __PV_BIT(35)
#define PV_PCF_NO_DECRYPTION PV_MSB(35)
/* PCKMO encrypt-DEA/TDEA-key functions allowed */
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56)
#define PV_PCF_PCKMO_DEA_TDEA PV_MSB(56)
/* PCKMO encrypt-AES-key functions allowed */
#define PV_PCF_PCKMO_AES __PV_BIT(57)
#define PV_PCF_PCKMO_AES PV_MSB(57)
/* PCKMO encrypt-ECC-key functions allowed */
#define PV_PCF_PCKM_ECC __PV_BIT(58)
#define PV_PCF_PCKM_ECC PV_MSB(58)
/* maxima for the PV version 1 */
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE

View File

@@ -2,17 +2,6 @@ include ../common.mak
lib = libap.a
check-dep-lock:
touch check-dep-lock
ifneq (${HAVE_LOCKFILE},0)
$(call check_dep, \
"libap", \
"lockfile.h", \
"liblockfile-devel", \
"HAVE_LOCKFILE=0")
ALL_CPPFLAGS += -DHAVE_LOCKFILE
endif
check-dep-json:
touch check-dep-json
ifneq (${HAVE_JSONC},0)
@@ -29,7 +18,7 @@ objects = ap.o
$(lib): $(objects)
$(objects): check-dep-lock check-dep-json
$(objects): check-dep-json
install: all

View File

@@ -22,13 +22,10 @@
#include <json-c/json.h>
#endif /* HAVE_JSONC */
#ifdef HAVE_LOCKFILE
#include <lockfile.h>
#endif /* HAVE_LOCKFILE */
#include "lib/ap.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_lockfile.h"
#include "lib/util_panic.h"
#include "lib/util_path.h"
#include "lib/util_udev.h"
@@ -701,33 +698,28 @@ void ap_list_remove_all(struct util_list *list)
}
}
#ifdef HAVE_LOCKFILE
static int ap_lockfile_create(int flags)
{
return lockfile_create(AP_LOCKFILE, AP_LOCK_RETRIES, flags);
}
/**
* Acquire the ap config lock using this process PID (L_PID)
* Acquire the ap config lock using this Process ID
*
* @retval 0 Lock acquired on behalf of this process
*
* @retval 0 Lock successfully acquired on behalf of L_PID
* @retval != 0 Error, lock was not obtained
*/
int ap_get_lock(void)
{
return ap_lockfile_create(L_PID);
return util_lockfile_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
}
/**
* Acquire the ap config lock using the parent process PID (L_PPID) -- intended
* for use by the mdevctl callout ap-check utility
* Acquire the ap config lock using the Parent Process ID -- intended for use
* by the mdevctl callout ap-check utility
*
* @retval 0 Lock successfully acquired on behalf of L_PPID
* @retval 0 Lock acquired on behalf of parent process
* @retval != 0 Error, lock was not obtained
*/
int ap_get_lock_callout(void)
{
return ap_lockfile_create(L_PPID);
return util_lockfile_parent_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
}
/**
@@ -738,23 +730,10 @@ int ap_get_lock_callout(void)
*/
int ap_release_lock(void)
{
return lockfile_remove(AP_LOCKFILE);
}
#else
/* If no liblockfile, actions are performed without acquiring the file lock */
int ap_get_lock(void)
{
return 0;
return util_lockfile_release(AP_LOCKFILE);
}
int ap_get_lock_callout(void)
int ap_release_lock_callout(void)
{
return 0;
return util_lockfile_parent_release(AP_LOCKFILE);
}
int ap_release_lock(void)
{
return 0;
}
#endif /* HAVE_LOCKFILE */

View File

@@ -290,7 +290,9 @@ static char *pv_X509_NAME_oneline(const X509_NAME *name)
long len;
if (X509_NAME_print_ex(key_bio, name, 0, XN_FLAG_RFC2253) == -1) {
g_warning(_("Cannot receive X509-NAME from CRL: %s"), pv_get_openssl_error());
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
g_warning(_("Cannot receive X509-NAME from CRL: %s"), openssl_err_msg);
return NULL;
}
@@ -790,7 +792,7 @@ int pv_store_set_verify_param(X509_STORE *store, GError **error)
return 0;
error:
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
_("X509 store initialization failed"));
return -1;
}
@@ -1375,14 +1377,14 @@ int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error)
ca_x509_subject = X509_get_subject_name(ca);
if (!ca_x509_subject) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
_("subject of the root CA cannot be retrieved"));
return -1;
}
ca_subject = pv_X509_NAME_oneline(ca_x509_subject);
if (!ca_subject) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
_("subject name of the root CA cannot be retrieved"));
return -1;
}
@@ -1476,11 +1478,11 @@ static STACK_OF_X509 *get_ibm_signing_certs(STACK_OF_X509 *certs, GError **error
ibm_signing_certs = pv_remove_ibm_signing_certs(certs);
ibm_signing_certs_count = sk_X509_num(ibm_signing_certs);
if (ibm_signing_certs_count < 1) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
_("Specify at least one IBM Z signing key"));
return NULL;
} else if (ibm_signing_certs_count > 1) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
_("Specify only one IBM Z signing key"));
return NULL;
}
@@ -1511,7 +1513,7 @@ static gboolean download_crls(X509_STORE *trusted, PvCertWithPathList *host_key_
X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i);
if (X509_STORE_add_crl(trusted, crl) != 1) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
_("failed to load CRL"));
return FALSE;
}

View File

@@ -19,18 +19,18 @@
#include "libpv/glib-helper.h"
#include "libpv/hash.h"
const char *pv_get_openssl_error(void)
char *pv_get_openssl_errors(void)
{
const char *ret;
BIO *bio;
char *ret;
char *buf;
BIO *bio;
long len;
bio = BIO_new(BIO_s_mem());
ERR_print_errors(bio);
len = BIO_get_mem_data(bio, &buf);
if (len < 0)
ret = "Cannot receive OpenSSL error message.";
if (len <= 0 || !buf)
ret = g_strdup("Cannot receive OpenSSL error message.");
else
ret = g_strndup(buf, (size_t)len);
BIO_free(bio);
@@ -276,7 +276,7 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP
}
GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info,
const EVP_MD *md, G_GNUC_UNUSED GError **error)
const EVP_MD *md, GError **error)
{
const unsigned char *salt_data, *key_data, *info_data;
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
@@ -354,7 +354,6 @@ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *
if (EVP_PKEY_derive(ctx, derived_key, &derived_key_len) != 1) {
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL,
"FAILED to derive key via HKDF");
printf("%s\n", pv_get_openssl_error());
return NULL;
}

View File

@@ -168,12 +168,14 @@ GBytes *pv_file_get_content_as_secure_bytes(const char *filename)
return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size);
}
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src)
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied)
{
size_t src_size;
const void *src_data = g_bytes_get_data(src, &src_size);
if (dst_size < src_size)
return NULL;
if (copied)
*copied = src_size;
return memcpy(dst, src_data, src_size);
}

View File

@@ -97,8 +97,10 @@ HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error)
key_data = g_bytes_get_data(key, &key_size);
if (HMAC_Init_ex(ctx, key_data, (int)key_size, md, NULL) != 1) {
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
"unable to create HMAC context: %s", pv_get_openssl_error());
"unable to create HMAC context: %s", openssl_err_msg);
return NULL;
}
return g_steal_pointer(&ctx);
@@ -110,8 +112,10 @@ int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *buf, size_t size, GError *
return 0;
if (HMAC_Update(ctx, buf, size) != 1) {
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
"unable to add data to HMAC context: %s", pv_get_openssl_error());
"unable to add data to HMAC context: %s", openssl_err_msg);
return -1;
}
return 0;
@@ -139,8 +143,10 @@ GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error)
hmac = g_malloc0((unsigned int)md_size);
if (HMAC_Final(ctx, hmac, &hmac_size) != 1) {
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
"unable to calculate HMAC: %s", pv_get_openssl_error());
"unable to calculate HMAC: %s", openssl_err_msg);
return NULL;
}
return g_bytes_new_take(g_steal_pointer(&hmac), hmac_size);

View File

@@ -107,13 +107,12 @@ const char *util_arch_machine_type_to_str(int type)
*/
unsigned long util_arch_hsa_maxsize(void)
{
unsigned long size = HSA_SIZE_32M;
int type;
type = util_arch_machine_type();
if (type != UTIL_ARCH_MACHINE_TYPE_UNKNOWN &&
type >= UTIL_ARCH_MACHINE_TYPE_Z15)
size = HSA_SIZE_512M;
return size;
switch (util_arch_machine_type()) {
case UTIL_ARCH_MACHINE_TYPE_Z15:
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
case UTIL_ARCH_MACHINE_TYPE_Z16:
return HSA_SIZE_512M;
default:
return HSA_SIZE_32M;
}
}

300
libutil/util_lockfile.c Normal file
View File

@@ -0,0 +1,300 @@
/*
* util - Utility function library
*
* Created file-based locks
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "lib/util_libc.h"
#include "lib/util_lockfile.h"
#include "lib/util_panic.h"
#define WAITPID 120 /* Time to wait for pid to be written */
#define WAITINC 5 /* Additional time to wait each retry */
#define MAXWAIT 60 /* Maximum wait between retries */
#define BUFSIZE 40 /* Buffer must be large enough to fit pid string */
/**
* Check if there is a process that exists with the specified identifier.
*
* @param[in] pid Process Identifier to check
*
* @retval true Process exists or we lack privelege to check
* @retval false Process does not exist
*/
static bool pid_exists(int pid)
{
int rc;
/* Use sig 0 to determine if the owner is still alive */
rc = kill(pid, 0);
if (rc != 0) {
switch (errno) {
case EPERM:
/* Privilege issue, just assume PID exists */
break;
case ESRCH:
/* PID does not exist, this lock is stale */
return false;
default:
util_assert(false, "Unexpected return from kill: %d\n", rc);
break;
}
}
return true;
}
/**
* Check for an existing lock that is deemed stale (either the associated PID
* is gone or no PID has been written to the file in a reasonable timeframe).
* In the case a stale lock is found, remove it.
*
* @param[in] lockfile Path to the lock file
*
* @retval 0 Either no lock or stale lock was found and removed
* @retval 1 Lock is held by another PID and is not stale
*/
static int handle_stale_lock(char *lockfile)
{
int fd, rc, pid, len;
struct stat info;
char buf[BUFSIZE];
time_t curr;
fd = open(lockfile, O_RDONLY);
if (fd >= 0) {
/* Lock exists, see who owns it */
len = read(fd, buf, sizeof(buf));
if (len > 0) {
buf[len] = 0; /* Ensure null terminated string */
pid = atoi(buf);
if (!pid_exists(pid)) {
/* Stale lock detected unlink and retry now */
close(fd);
unlink(lockfile);
return 0;
} else if (pid != 0) {
/* Lock is held by an active pid, delay */
close(fd);
return 1;
}
/*
* If we reach this point, the PID was 0 which is
* unexpected. Proceed under the assumption that the
* proper PID hasn't been written yet.
*/
}
/*
* PID hasn't been written yet? Either a bad lock or a very
* new one.
*/
time(&curr);
rc = fstat(fd, &info);
close(fd);
if (rc != 0) {
/* Can't read file anymore, retry now */
return 0;
}
if (curr > info.st_mtime + WAITPID) {
/*
* PID should be in the file within 2 minutes,
* something went wrong. Treat as stale.
*/
unlink(lockfile);
return 0;
}
/* Otherwise, assume file was newly created and delay */
return 1;
}
/* Couldn't open, try again immediately */
return 0;
}
/**
* Attempt to create a lockfile at the specified path.
*
* @param[in] lockfile Path to the lock file
* @param[in] retries Number of times to retry if lock fails initially
* @param[in] pid PID to use for lock ownership
*
* @retval 0 Lock created with PID as owner
* @retval !=0 Lock was not created
*/
static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid)
{
int fd, plen, len, rc = 0, snooze = 0;
unsigned int tries = retries + 1;
char buf[BUFSIZE];
char *tpath;
if (!lockfile)
return UTIL_LOCKFILE_ERR;
plen = snprintf(buf, sizeof(buf), "%d\n", pid);
if (plen < 0 || (plen > ((int)sizeof(buf) - 1)))
return UTIL_LOCKFILE_ERR;
/* Allocate temporary lock file with a sufficiently unique path */
len = util_asprintf(&tpath, "%s%05d%02x", lockfile, getpid(),
(unsigned int)time(NULL) & 255);
if (len < 0)
return UTIL_LOCKFILE_ERR;
/* Open the temporary lockfile, write the specified pid */
fd = open(tpath, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
if (fd < 0) {
rc = UTIL_LOCKFILE_ERR;
goto out;
}
len = write(fd, buf, plen);
if (close(fd) != 0) {
rc = UTIL_LOCKFILE_ERR;
goto cleanup;
}
if (len != plen) {
/* Failed to write the temp lockfile, bail out */
rc = UTIL_LOCKFILE_ERR;
goto cleanup;
}
/* Link the temprorary file to the real path */
do {
rc = link(tpath, lockfile);
if (rc == 0) {
/* Lock successfully acquired */
rc = UTIL_LOCKFILE_OK;
goto cleanup;
}
/* Lock already held - check for stale lock */
rc = handle_stale_lock(lockfile);
/* Only wait if the lock was not stale */
if (rc != 0) {
tries--;
if (tries > 0) {
snooze += WAITINC;
snooze = (snooze > MAXWAIT) ? MAXWAIT : snooze;
sleep(snooze);
}
}
} while (tries > 0);
/* Exhausted specified number of retries, exit on failure */
rc = UTIL_LOCKFILE_LOCK_FAIL;
cleanup:
unlink(tpath);
free(tpath);
out:
return rc;
}
/**
* Attempt to release a lockfile at the specified path.
*
* @param[in] lockfile Path to the lock file
* @param[in] pid PID that should own the lock
*
* @retval 0 Lock released
* @retval !=0 Lock was not released or did not exist
*/
static int do_lockfile_release(char *lockfile, int pid)
{
int fd, len, lpid;
char buf[BUFSIZE];
if (!lockfile)
return UTIL_LOCKFILE_ERR;
/* Open lockfile, read the owning pid if it exists */
fd = open(lockfile, O_RDONLY);
if (fd < 0)
return UTIL_LOCKFILE_RELEASE_NONE;
len = read(fd, buf, sizeof(buf));
close(fd);
if (len <= 0)
return UTIL_LOCKFILE_RELEASE_FAIL;
buf[len] = 0;
lpid = atoi(buf);
/* Only release the lock if its held by the right pid */
if (pid != lpid)
return UTIL_LOCKFILE_RELEASE_FAIL;
unlink(lockfile);
return 0;
}
/**
* Attempt to create a lockfile owned by this process at the specified path.
*
* @param[in] lockfile Path to the lock file
* @param[in] retries Number of times to retry if lock fails initially
*
* @retval 0 Lock created
* @retval !=0 Lock was not created
*/
int util_lockfile_lock(char *lockfile, int retries)
{
return do_lockfile_lock(lockfile, retries, getpid());
}
/**
* Attempt to create a lockfile owned by the parent of this process at the
* specified path.
*
* @param[in] lockfile Path to the lock file
* @param[in] retries Number of times to retry if lock fails initially
*
* @retval 0 Lock created
* @retval !=0 Lock was not created
*/
int util_lockfile_parent_lock(char *lockfile, int retries)
{
return do_lockfile_lock(lockfile, retries, getppid());
}
/**
* Attempt to release a lockfile owned by this process at the specified path.
*
* @param[in] lockfile Path to the lock file
*
* @retval 0 Lock released
* @retval !=0 Lock was not released or did not exist
*/
int util_lockfile_release(char *lockfile)
{
return do_lockfile_release(lockfile, getpid());
}
/**
* Attempt to release a lockfile owned by the parent of this process at the
* specified path.
*
* @param[in] lockfile Path to the lock file
*
* @retval 0 Lock released
* @retval !=0 Lock was not released or did not exist
*/
int util_lockfile_parent_release(char *lockfile)
{
return do_lockfile_release(lockfile, getppid());
}

View File

@@ -0,0 +1,232 @@
/**
* util_lockfile_example - Example program for util_lockfile
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
//! [code]
#include <stdio.h>
#include <stdlib.h>
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "lib/util_lockfile.h"
static const struct util_prg prg = {
.desc = "Example for util_lockfile.",
.copyright_vec = { {
.owner = "IBM Corp.",
.pub_first = 2022,
.pub_last = 2022,
},
UTIL_PRG_COPYRIGHT_END }
};
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS"),
{
.option = { "file", required_argument, NULL, 'f' },
.argument = "PATH",
.desc = "Use the specified path for a lockfile.",
},
{
.option = { "lock", required_argument, NULL, 'l' },
.argument = "RETRIES",
.desc = "Acquire the specified file lock using the parent "
"process id of this process. If not immediately "
"successful, retry for the specified number of times",
},
{
.option = { "release", 0, NULL, 'r' },
.desc = "Release the specified lock file using the parent "
"process id",
},
{
.option = { "lock-and-release", required_argument, NULL, 'L' },
.argument = "RETRIES",
.desc = "Acquire the specified file lock using this process "
"id, then release it. If not immediately successful, "
"retry for the specified number of times.",
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
};
enum prg_action {
ACTION_NONE = 0,
ACTION_LOCK,
ACTION_RELEASE,
ACTION_LOCK_AND_RELEASE,
};
static void print_single_action_error(void)
{
warnx("Only a single action (--lock, --lock-parent, --release, "
"--release-parent) is allowed");
}
int main(int argc, char *argv[])
{
enum prg_action action_id = ACTION_NONE;
int opt, rc, retries = 0;
char *endp, *path = NULL;
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
while (1) {
opt = util_opt_getopt_long(argc, argv);
if (opt == -1)
break;
switch (opt) {
case 'f':
path = optarg;
break;
case 'L':
if (action_id != ACTION_NONE) {
print_single_action_error();
return EXIT_FAILURE;
}
retries = strtol(optarg, &endp, 0);
if (*optarg == '\0' || *endp != '\0' || retries < 0) {
warnx("Invalid retry value for "
"--lock-and-release: '%s'",
optarg);
util_prg_print_parse_error();
return EXIT_FAILURE;
}
action_id = ACTION_LOCK_AND_RELEASE;
break;
case 'l':
if (action_id != ACTION_NONE) {
print_single_action_error();
return EXIT_FAILURE;
}
retries = strtol(optarg, &endp, 0);
if (*optarg == '\0' || *endp != '\0' || retries < 0) {
warnx("Invalid retry value for "
"--parent-lock: '%s'",
optarg);
util_prg_print_parse_error();
return EXIT_FAILURE;
}
action_id = ACTION_LOCK;
break;
case 'r':
if (action_id != ACTION_NONE) {
print_single_action_error();
return EXIT_FAILURE;
}
action_id = ACTION_RELEASE;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
exit(EXIT_SUCCESS);
case 'v':
util_prg_print_version();
exit(EXIT_SUCCESS);
default:
util_opt_print_parse_error(opt, argv);
return EXIT_FAILURE;
}
}
if (!path) {
warnx("--file is required, see --help for more information");
return EXIT_FAILURE;
}
if (action_id == ACTION_NONE) {
warnx("One of the following actions must be specified: "
"--lock, --release, --lock-and-release");
return EXIT_FAILURE;
}
/* Determine which util_lockfile function to call */
switch (action_id) {
case ACTION_LOCK:
rc = util_lockfile_parent_lock(path, retries);
switch (rc) {
case UTIL_LOCKFILE_OK:
printf("lock acquired successfully\n");
break;
case UTIL_LOCKFILE_LOCK_FAIL:
warnx("lock was not acquired; already held");
return EXIT_FAILURE;
case UTIL_LOCKFILE_ERR:
warnx("lock was not acquired; file error");
return EXIT_FAILURE;
default:
warnx("Unknown util_lockfile rc %d", rc);
return EXIT_FAILURE;
}
break;
case ACTION_RELEASE:
rc = util_lockfile_parent_release(path);
switch (rc) {
case UTIL_LOCKFILE_OK:
printf("lock released successfully\n");
break;
case UTIL_LOCKFILE_RELEASE_NONE:
warnx("lock file did not exist");
return EXIT_FAILURE;
case UTIL_LOCKFILE_RELEASE_FAIL:
warnx("lock was not held by the specified pid");
return EXIT_FAILURE;
case UTIL_LOCKFILE_ERR:
warnx("lock was not released; file error");
return EXIT_FAILURE;
default:
warnx("Unknown util_lockfile rc %d", rc);
return EXIT_FAILURE;
}
break;
case ACTION_LOCK_AND_RELEASE:
rc = util_lockfile_lock(path, retries);
switch (rc) {
case UTIL_LOCKFILE_OK:
printf("lock acquired successfully, holding for 2 seconds\n");
break;
case UTIL_LOCKFILE_LOCK_FAIL:
warnx("lock was not acquired; already held");
return EXIT_FAILURE;
case UTIL_LOCKFILE_ERR:
warnx("lock was not acquired; file error");
return EXIT_FAILURE;
default:
warnx("Unknown util_lockfile rc %d", rc);
return EXIT_FAILURE;
}
/* Briefly sleep while holding the lock */
sleep(2);
rc = util_lockfile_release(path);
switch (rc) {
case UTIL_LOCKFILE_OK:
printf("lock released successfully\n");
break;
case UTIL_LOCKFILE_RELEASE_NONE:
warnx("lock file did not exist");
return EXIT_FAILURE;
case UTIL_LOCKFILE_RELEASE_FAIL:
warnx("lock was not held by the specified pid");
return EXIT_FAILURE;
case UTIL_LOCKFILE_ERR:
warnx("lock was not released; file error");
return EXIT_FAILURE;
default:
warnx("Unknown util_lockfile rc %d", rc);
return EXIT_FAILURE;
}
break;
default:
warnx("Unknown util_lockfile action");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
//! [code]

View File

@@ -171,9 +171,9 @@ int arcb_v1_add_key_slot(arcb_v1_t *arcb, EVP_PKEY *evp_host, GError **error)
g_assert(g_bytes_get_size(phkh) == sizeof(key_slot->phkh));
key_slot = g_malloc0(sizeof(*key_slot));
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk);
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag);
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh);
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk, NULL);
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag, NULL);
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh, NULL);
arcb->host_key_slots = g_slist_prepend(arcb->host_key_slots, g_steal_pointer(&key_slot));
return 0;
@@ -246,7 +246,7 @@ GBytes *arcb_v1_serialize(const arcb_v1_t *arcb, GError **error)
/* copy plain data to contiguous memory */
hdr.arl = GUINT32_TO_BE((uint32_t)att_req_len);
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv);
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv, NULL);
hdr.nks = (uint8_t)nks;
hdr.sea = GUINT32_TO_BE((uint32_t)sea);
ecdh_cpk = pv_evp_pkey_to_ecdh_pub_key(arcb->evp_cust_pub_key, error);

View File

@@ -15,6 +15,7 @@
#include "lib/zt_common.h"
#include "libpv/crypto.h"
#include "libpv/macros.h"
#include "types.h"
@@ -28,18 +29,17 @@
#define ARCB_V1_IV_SIZE 12
#define ARCB_V1_PHKH_SIZE 32
#define BIT(bit) ((uint64_t)1 << (63 - (bit)))
/* Optional nonce in ARCB */
#define ARCB_V1_PAF_NONCE BIT(1)
#define ARCB_V1_PAF_NONCE PV_MSB(1)
/* Public host key hash used to unseal SE header added to additional data to be measured */
#define ARCB_V1_PAF_AAD_PHKH_HEADER BIT(2)
#define ARCB_V1_PAF_AAD_PHKH_HEADER PV_MSB(2)
/* Public host key hash used to unseal this attestation added to additional data to be measured */
#define ARCB_V1_PAF_AAD_PHKH_ATTEST BIT(3)
#define ARCB_V1_PAF_AAD_PHKH_ATTEST PV_MSB(3)
/* Temporary backup-host-key use allowed */
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED BIT(62)
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED PV_MSB(62)
/* Global not-host-specific key allowed */
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED BIT(63)
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED PV_MSB(63)
#define ARCB_V1_PAF_ALL \
(ARCB_V1_PAF_NONCE | ARCB_V1_PAF_AAD_PHKH_HEADER | ARCB_V1_PAF_AAD_PHKH_ATTEST | \

View File

@@ -130,7 +130,7 @@ void att_add_uid(att_meas_ctx_t *meas_ctx, GBytes *config_uid)
pv_wrapped_g_assert(config_uid);
g_assert(g_bytes_get_size(config_uid) == ATT_CONFIG_UID_SIZE);
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid);
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid, NULL);
}
gboolean att_verify_measurement(const GBytes *calculated_measurement,

View File

@@ -11,7 +11,7 @@
/* Must be included before any other header */
#include "config.h"
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include <stdio.h>
#include "libpv/glib-helper.h"

View File

@@ -191,7 +191,7 @@ exchange_format_ctx_t *exchange_ctx_from_file(const char *filename, GError **err
}
ctx->req_meas_size = GUINT32_FROM_BE(hdr_v1->measurement.size);
ctx->req_add_size = GUINT32_FROM_BE(hdr_v1->additional_data.size);
ctx->version = GUINT32_TO_BE(hdr->version);
ctx->version = GUINT32_FROM_BE(hdr->version);
return g_steal_pointer(&ctx);
}

View File

@@ -9,6 +9,7 @@
*/
/* Must be included before any other header */
#include "config.h"
#include "lib/zt_common.h"
#ifdef PVATTEST_COMPILE_PERFORM
#include <stdio.h>
@@ -42,26 +43,29 @@ uvio_attest_t *build_attestation_v1_ioctl(GBytes *serialized_arcb, GBytes *user_
uvio_attest = g_malloc0(sizeof(*uvio_attest));
uvio_attest->arcb_addr = PTR_TO_U64(g_steal_pointer(&arcb));
g_assert_cmpuint(arcb_size, <, UINT32_MAX);
uvio_attest->arcb_len = GUINT32_TO_BE((uint32_t)arcb_size);
uvio_attest->arcb_len = (uint32_t)arcb_size;
/* transferred the local ownership of the arcb from this function to uvio_attest; nullify pointer */
g_steal_pointer(&serialized_arcb);
if (user_data) {
if (g_bytes_get_size(user_data) > sizeof(uvio_attest->user_data)) {
size_t copied_data_size;
if (pv_gbytes_memcpy(uvio_attest->user_data, sizeof(uvio_attest->user_data),
user_data, &copied_data_size) == NULL) {
g_set_error(error, ATT_ERROR, ATT_ERR_INVALID_USER_DATA,
_("User data larger than %li bytes"),
sizeof(uvio_attest->user_data));
_("User data %li bytes is larger than %li bytes"),
g_bytes_get_size(user_data), sizeof(uvio_attest->user_data));
return NULL;
}
uvio_attest->user_data_len = GUINT16_TO_BE((uint16_t)g_bytes_get_size(user_data));
pv_gbytes_memcpy(uvio_attest->user_data, uvio_attest->user_data_len, user_data);
STATIC_ASSERT(sizeof(uvio_attest->user_data) <= UINT16_MAX);
uvio_attest->user_data_len = (uint16_t)copied_data_size;
}
uvio_attest->meas_len = GUINT32_TO_BE(measurement_size);
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(uvio_attest->meas_len));
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(measurement_size));
uvio_attest->meas_len = measurement_size;
uvio_attest->add_data_len = GUINT32_TO_BE(add_data_size);
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(uvio_attest->add_data_len));
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(add_data_size));
uvio_attest->add_data_len = add_data_size;
return g_steal_pointer(&uvio_attest);
}
@@ -83,7 +87,7 @@ GBytes *uvio_get_measurement(const uvio_attest_t *attest)
if (attest->meas_addr == (__u64)0)
return NULL;
return g_bytes_new(U64_TO_PTR(attest->meas_addr), GUINT32_FROM_BE(attest->meas_len));
return g_bytes_new(U64_TO_PTR(attest->meas_addr), attest->meas_len);
}
GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
@@ -92,8 +96,7 @@ GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
if (attest->add_data_addr == (__u64)0)
return NULL;
return g_bytes_new(U64_TO_PTR(attest->add_data_addr),
GUINT32_FROM_BE(attest->add_data_len));
return g_bytes_new(U64_TO_PTR(attest->add_data_addr), attest->add_data_len);
}
GBytes *uvio_get_config_uid(const uvio_attest_t *attest)
@@ -127,7 +130,7 @@ uint16_t uvio_ioctl(const int uv_fd, const unsigned int cmd, const uint32_t flag
g_set_error(error, UVIO_ERROR, UVIO_ERR_UV_NOT_OK,
_("Ultravisor call returned '%#x' (%s)"), uv_ioctl->uv_rc,
uvio_uv_rc_to_str(uv_ioctl->uv_rc));
return GUINT16_FROM_BE(uv_ioctl->uv_rc);
return uv_ioctl->uv_rc;
}
uint16_t uvio_ioctl_attest(const int uv_fd, uvio_attest_t *attest, GError **error)

View File

@@ -17,7 +17,8 @@ export LC_ALL
readonly SCRIPTNAME="${0##*/}" # general name of this script
#
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
readonly DOCKER=$(if type -t docker >/dev/null; then echo "YES"; else echo "NO"; fi)
readonly DOCKER=$(if type docker >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly DUMP2TAR_OK=$(if type dump2tar >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly HW="$(uname -i 2>/dev/null)"
# retrieve and split kernel version
readonly KERNEL_BASE="$(uname -r 2>/dev/null)"
@@ -25,7 +26,9 @@ readonly KERNEL_VERSION=$(echo ${KERNEL_BASE} | cut -d'.' -f1 )
readonly KERNEL_MAJOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f2 )
readonly KERNEL_MINOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f3 | sed 's/[^0-9].*//g')
readonly KERNEL_INFO=${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION}
readonly KVM=$(if type -t virsh >/dev/null; then echo "YES"; else echo "NO"; fi)
readonly KUBERNETES=$(if type kubectl >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly KUBERNETES_LOG_LINES=50
readonly KVM=$(if type virsh >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
# The file to indicate that another instance of the script is already running
readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock"
# check limits for logfiles like /var/log/messages
@@ -43,14 +46,20 @@ readonly PROCESSORVERSION="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then
RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo | \
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
else
RUNTIME_ENVIRONMENT="LPAR"
fi
readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)" # hostname of system being analysed
readonly TERMINAL="$(tty 2>/dev/null)"
# The processor version for the first processor and resulting vitrtualization RUNTIME
readonly TOS=15 # timeout seconds for command execution
# timeout seconds TOS / kill timeout TOKS
readonly TOS=15
readonly TOKS=30
readonly TIMEOUT=$(if type timeout >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly TIMEOUT_OK=$(if test "x${TIMEOUT}" = "xYES"; then
if test $(timeout -k ${TOKS} ${TOS} uname 2>/dev/null) = $(uname); then
echo "YES"; else echo "WRONG_VERSION"; fi
else echo "NO"; fi)
readonly ZDEV_CONF=$(lszdev --configured 2>/dev/null | wc -l)
readonly ZDEV_OFF=$(lszdev --offline 2>/dev/null | wc -l)
readonly ZDEV_ONL=$(lszdev --online 2>/dev/null | wc -l)
@@ -79,7 +88,7 @@ This script collects runtime, configuration and trace information on
a Linux on IBM Z installation for debugging purposes.
It also traces information about z/VM if the Linux runs under z/VM.
KVM or DOCKER data ist collected on a host serving this.
Virtualization platform data is collected on a host serving this.
Default location for data collection and final tar file is "/tmp/".
The collected information is written to a TAR archive named
@@ -96,7 +105,7 @@ Options:
stores the temporary data and the final archive.
-h|--help print this help
-v|--version print version information
-c|--check online quick check (no data collection)
-c|--check online base system information (no data collection)
Please report bugs to: linux390@de.ibm.com
@@ -150,7 +159,7 @@ $(cat /proc/sysinfo | grep 'Name')
Kernel version = ${KERNEL_INFO}
OS version / distro = ${OS_NAME}
KVM host = ${KVM}
DOCKER host = ${DOCKER}
container host Kubernetes: ${KUBERNETES} - docker: ${DOCKER}
Current user = $(whoami) (must be root for data collection)
Date and time = $(date)
@@ -162,6 +171,9 @@ Log file check =$(logfile_checker "/var/log*")
Working directory = $(ls -d ${paramWORKDIR_BASE} 2>&1 && df -k ${paramWORKDIR_BASE})
$(ls -ltr ${paramWORKDIR_BASE}/DBGINFO*tgz 2>/dev/null | tail -2)
$(ls ${LOCKFILE} 2>/dev/null && echo " Warning: dbginfo running since: $(cat ${LOCKFILE})")
Tool/command dependency check successful:
timeout = ${TIMEOUT_OK}
dump2tar = ${DUMP2TAR_OK}
This is a console output only - no data was saved using option -c !
@@ -213,7 +225,8 @@ done
# finally verification to run as root
if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then
echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!"
echo "${SCRIPTNAME}: Error: You must be user root to run data collection \"${SCRIPTNAME}\"!"
echo "${SCRIPTNAME}: Hint: for base system information you may run \"${SCRIPTNAME} -c\""
exit 1
fi
@@ -249,6 +262,7 @@ readonly OUTPUT_FILE_ISW="${WORKPATH}installed_sw.out"
readonly OUTPUT_FILE_TC="${WORKPATH}network.out"
readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
# Base file names for different output files - no extension !
readonly OUTPUT_FILE_KUBERNETES="${WORKPATH}kubernetes"
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}network"
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfs"
@@ -268,7 +282,7 @@ ALL_STEPS="\
collect_bridge\
collect_ovs\
collect_kvm\
collect_docker\
collect_container\
collect_nvme\
collect_logfiles\
post_processing\
@@ -329,14 +343,18 @@ fi
########################################
LOGFILES="\
/root/anaconda-ks.cfg\
/root/original-ks.cfg\
/run/containerd/events.log\
/run/docker/libcontainerd/containerd/events.log\
/run/udev/chreiplzfcpmp-[0-9][0-9][a-z][a-z][a-z][a-z]-*\
/var/log/anaconda\
/var/log/anaconda.*\
/var/log/audit\
/var/log/boot*\
/var/log/cron*\
/var/log/dmesg*\
/var/log/dnf.*\
/var/log/dracut.log*\
/var/log/IBMtape.trace\
/var/log/IBMtape.errorlog\
@@ -346,9 +364,12 @@ LOGFILES="\
/var/log/messages*\
/var/log/openvswitch/ovs-vswitchd.log\
/var/log/openvswitch/ovsdb-server.log\
/var/log/pbl.log\
/var/log/sa\
/var/log/syslog*\
/var/log/yum.log\
/var/log/YaST2\
/var/log/zypp*\
"
########################################
@@ -368,6 +389,7 @@ CONFIGFILES="\
/etc/crypttab\
/etc/default\
/etc/depmod.d\
/etc/dnf/dnf.conf\
/etc/docker\
/etc/dracut.conf.d\
/etc/exports\
@@ -468,6 +490,7 @@ CMDS="${CMDS}\
:dmsetup table --target multipath\
:dmsetup status\
:multipathd -k'show config'\
:multipathd -k'show config local'\
:multipathd -k'show maps'\
:multipathd -k'show topo'\
:multipathd -k'show paths'\
@@ -557,6 +580,18 @@ DOCKER_CMDS="docker version\
:systemctl status docker.service\
"
########################################
KUBERNETES_CMDS="kubectl version\
:oc version # get also OCP version if installed\
:kubectl top pod\
:kubectl top node\
:kubectl describe pod -A\
:kubectl describe node -A\
:kubectl describe endpoints\
:ls -l /var/log/containers # list of logs is sufficent\
:kubectl cluster-info dump >>${OUTPUT_FILE_KUBERNETES}.dmp\
"
########################################
VM_CMDS="q userid\
:q users\
@@ -676,18 +711,18 @@ collect_vmcmdsout() {
local vm_userid
local module_loaded=1
local ifs_orig="${IFS}"
local cp_buffer_size=2
local rc_buffer_size=2
local cp_buffer_size
local rc_buffer_check
if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then
pr_collect_output "z/VM"
if type -t vmcp >/dev/null; then
if type vmcp >/dev/null; then
cp_command="vmcp"
if ! lsmod 2>/dev/null | grep -q vmcp && modinfo vmcp >/dev/null 2>&1; then
modprobe vmcp && module_loaded=0 && sleep 2
fi
elif type -t hcp >/dev/null; then
elif type hcp >/dev/null; then
cp_command="hcp"
if ! lsmod 2>/dev/null | grep -q cpint; then
modprobe cpint && module_loaded=0 && sleep 2
@@ -703,11 +738,14 @@ collect_vmcmdsout() {
IFS=:
for vm_command in ${vm_cmds}; do
IFS="${ifs_orig}"
while test ${rc_buffer_size} -eq 2 && test ${cp_buffer_size} -lt 1024; do
# initialize buffer values for automatic resizing
cp_buffer_size=2
rc_buffer_check=2
while test ${rc_buffer_check} -eq 2 && test ${cp_buffer_size} -lt 1024; do
cp_buffer_size=$(( cp_buffer_size * 2 ))
eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" \
>/dev/null 2>&1
rc_buffer_size=$?
rc_buffer_check=$?
done
call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" \
"${OUTPUT_FILE_VMCMD}"
@@ -781,7 +819,7 @@ collect_sysfs() {
# files known to block on read (-x). Stop reading a file that takes
# more than 5 seconds (-T 5) such as an active ftrace buffer.
# error messages are not written to the log
if type -t dump2tar >/dev/null; then
if test "x${DUMP2TAR_OK}" = "xYES"; then
dump2tar /sys -z -o "${OUTPUT_FILE_SYSFS}.tgz" \
-x '*/tracing/trace_pipe*' \
-x '*/page_idle/bitmap*' \
@@ -859,7 +897,7 @@ collect_osaoat() {
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g')
if type -t qethqoat >/dev/null; then
if type qethqoat >/dev/null; then
if test -n "${network_devices}"; then
pr_collect_output "osa oat"
for network_device in ${network_devices}; do
@@ -882,7 +920,7 @@ collect_ethtool() {
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type -t ethtool >/dev/null; then
if type ethtool >/dev/null; then
if test -n "${network_devices}"; then
pr_collect_output "ethtool"
for network_device in ${network_devices}; do
@@ -923,7 +961,7 @@ collect_tc() {
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type -t tc >/dev/null; then
if type tc >/dev/null; then
if test -n "${network_devices}"; then
pr_collect_output "Trafic Control"
for network_device in ${network_devices}; do
@@ -944,7 +982,7 @@ collect_bridge() {
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type -t bridge >/dev/null; then
if type bridge >/dev/null; then
if test -n "${network_devices}"; then
pr_collect_output "bridge"
for network_device in ${network_devices}; do
@@ -976,7 +1014,7 @@ collect_ovs() {
:ovs-vsctl -t 5 show\
:ovsdb-client dump\
"
if type -t ovs-vsctl >/dev/null; then
if type ovs-vsctl >/dev/null; then
pr_collect_output "OpenVSwitch"
IFS=:
for ovscmd in ${ovscmds}; do
@@ -1001,14 +1039,21 @@ collect_ovs() {
}
########################################
collect_docker() {
collect_container() {
local container_list
local network_list
local item
# check if docker command exists
# check if container environment exists
if test "x${DOCKER}" = "xYES" || test "x${KUBERNETES}" = "xYES"; then
pr_collect_output "container host"
else
pr_skip "container host: not found"
fi
# for docker command exists
if [ "x${DOCKER}" = "xYES" ]; then
pr_collect_output "docker"
pr_syslog_stdout " docker ..."
container_list=$(docker ps -qa)
network_list=$(docker network ls -q)
ifs_orig="${IFS}"
@@ -1030,8 +1075,25 @@ collect_docker() {
"${OUTPUT_FILE_DOCKER}"
done
fi
else
pr_skip "docker: not available"
fi
# for kubectl command exists
if [ "x${KUBERNETES}" = "xYES" ]; then
pr_syslog_stdout " Kubernetes ..."
container_list=$(kubectl top pod | grep -v "MEMORY(bytes)" | cut -d' ' -f1)
ifs_orig="${IFS}"
IFS=:
for item in ${KUBERNETES_CMDS}; do
IFS=${ifs_orig} call_run_command "${item}" "${OUTPUT_FILE_KUBERNETES}.out"
done
IFS="${ifs_orig}"
if test -n "${container_list}"; then
for item in ${container_list}; do
call_run_command "kubectl logs --tail=${KUBERNETES_LOG_LINES} ${item}"\
"${OUTPUT_FILE_KUBERNETES}.log"
done
fi
fi
}
@@ -1039,7 +1101,7 @@ collect_docker() {
collect_nvme() {
local device
if type -t nvme >/dev/null; then
if type nvme >/dev/null; then
pr_collect_output "NVME storage"
call_run_command "nvme list" "${OUTPUT_FILE_NVME}"
for device in /dev/nvme[0-9]*; do
@@ -1080,7 +1142,7 @@ collect_kvm() {
call_run_command "virsh domstats ${domain}" "${OUTPUT_FILE_KVM}"
done
else
echo "no KVM doamins found" | tee -a ${OUTPUT_FILE_KVM}
echo "no KVM domains found" | tee -a ${OUTPUT_FILE_KVM}
fi
else
pr_skip "KVM: no virsh command"
@@ -1139,23 +1201,23 @@ call_run_command() {
local rc=0
local cmd="${1}"
local logfile="${2}"
# extract the raw_command and set cmd_type
local raw_cmd=$(echo "${cmd}" | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
local cmd_type=$(type -t ${raw_cmd})
# timeout_ok - like a boolean - is not empty if command exists
local timeout_ok=$(type -t timeout)
# extract the raw_command and set cmd_type, as some shell might split into
# several lines restrict the cmd echo to the first line only
local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
local cmd_type=$(type ${raw_cmd} | cut -d' ' -sf4,5)
echo "#######################################################" >> "${logfile}"
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
# check calling command type
if [ "X${cmd_type}" = "Xbuiltin" ]; then
if [ "X${cmd_type}" = "Xshell builtin" ]; then
# command is a builtin (no use of timeout possible)
eval "${cmd}" >> ${logfile} 2>&1
rc=$?
elif [ "X${cmd_type}" != "X" ]; then
if [ "X${timeout_ok}" = "Xfile" ]; then
eval timeout -k ${TOS} ${TOS} "${cmd}" >> ${logfile} 2>&1
elif [ "X${cmd_type}" != "Xnot found" ]; then
if [ "x${TIMEOUT_OK}" = "xYES" ]; then
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
rc=$?
else
# fall back - call all existing commands without timeout
@@ -1347,6 +1409,9 @@ print_version
# saving stdout/stderr and redirecting stdout/stderr into log file
exec 8>&1 9>&2 >"${LOGFILE}" 2>&1
# run print_version again to be logged in dbginfo.log
print_version
# trap on SIGHUP=1 SIGINT=2 SIGTERM=15
trap emergency_exit SIGHUP SIGINT SIGTERM

View File

@@ -1,4 +1,4 @@
.TH DBGINFO.SH 8 "May 2022" "s390-tools"
.TH DBGINFO.SH 8 "Sep 2022" "s390-tools"
.SH NAME
dbginfo.sh \- collect runtime, configuration and trace information
@@ -8,13 +8,13 @@ for debugging Linux on IBM Z
.br
\fBdbginfo.sh\fP [OPTIONS]
.br
\fBdbginfo.sh\fP {\-h|\-v}
\fBdbginfo.sh\fP {\-h|\-c|\-v}
.SH DESCRIPTION
This script collects runtime, configuration and trace information that can
be used to debug a Linux on IBM Z instance.
For Linux on z/VM, the script also traces information about the z/VM system.
KVM or DOCKER data ist collected on a host serving this.
Virtualization platform data is collected on a host serving this.
The debug information is written to a file
/<DIRECTORY>/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
@@ -59,7 +59,7 @@ Sample invocation:
.br
dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
.br
Copyright IBM Corp. 2002, 2021
Copyright IBM Corp. 2002, 2022
.PP
Hardware platform = s390x
.br
@@ -97,8 +97,10 @@ Date and time of info = 2021-08-26-15-27-58
.PP
13 of 18: Skip KVM: no virsh command
.PP
14 of 18: Collecting docker output
14 of 18: Collecting container host output
.PP
Kubernetes ...
.br
15 of 18: Collecting NVME storage output
.PP
16 of 18: Collecting log files

View File

@@ -872,7 +872,7 @@ static void show_devices_argv(char *argv[])
show_device(rec, card);
free(card);
}
return;
continue;
}
if (sscanf(argv[i]+1, "%x", &dom) == 1) {
/* list specific domains of all adapters */
@@ -897,7 +897,7 @@ static void show_devices_argv(char *argv[])
}
free(path);
}
return;
continue;
}
printf("Invalid adpater id!\n");
}

View File

@@ -55,9 +55,22 @@ install() {
chzdev --export - --persistent --by-attrib "zdev:early=1" --quiet \
--type 2>/dev/null >> "$_tempfile"
# Obtain early device configuration for site-specific settings
for (( site=0; site<10; site++ ))
do
chzdev --export - --persistent --by-attrib "zdev:early=1" --site $site \
--quiet 2>/dev/null >> "$_tempfile"
done
# Apply via --import to prevent other devices from being configured
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
--yes --quiet --no-root-update --force >/dev/null
# Apply site-specific configurations via --import
for (( site=0; site<10; site++ ))
do
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
--site $site --yes --quiet --no-root-update --force >/dev/null
done
rm -f "$_tempfile"

View File

@@ -19,6 +19,20 @@
struct subtype;
struct setting_list;
/**
* Currently zdev supports 10 sites. Which means, zdev support 10 different
* set of attributes which are specific to each site. When the user does
* not provide any site information, the common set will be used which is
* not specific to any site. So, total we have 11 persistent attribute sets
* Where,
* 0- 9: Site specific attributes
* 10: Common attributes which do not belong to any sites
*/
#define NUM_SITES 11
#define NUM_USER_SITES (NUM_SITES - 1)
#define SITE_FALLBACK NUM_USER_SITES
/**
* struct device_state - Represent the state of a device in a configuration
* @settings: List of attribute settings
@@ -61,6 +75,7 @@ struct device {
struct device_state active;
struct device_state persistent;
struct device_state autoconf;
struct device_state site_specific[NUM_SITES];
unsigned int processed:1;
};
@@ -97,8 +112,11 @@ struct device *device_list_find(struct device_list *, const char *,
struct device *);
void device_list_print(struct device_list *, int);
struct setting_list *device_get_setting_list(struct device *dev,
config_t config);
config_t config,
int site_id);
config_t device_get_config(struct device *dev);
char *device_get_sites(struct device *dev);
bool is_dev_pers(struct device *dev);
#endif /* DEVICE_H */

View File

@@ -38,6 +38,7 @@ extern struct devtype *devtypes[];
* devtypes with empty title will not be shown with --list-types.
* @devname: Short name for devices of this device type
* @modules: (Optional) Array of kernel modules required for this devtype
* @site_support: Support for site-specific configuration for the device-type
* @subtypes: Array of subtypes
* @type_attribs: Array of device type attributes (may point to empty array)
* @unknown_type_attribs: Allow specification of unknown device type attributes
@@ -59,6 +60,7 @@ struct devtype {
const char title[DEVTYPE_TITLE_LEN + 1];
const char *devname;
const char **modules;
unsigned int site_support:1;
struct subtype **subtypes;
struct attrib **type_attribs;

View File

@@ -103,6 +103,8 @@ extern int delayed_warnings;
extern unsigned long longrun_total;
extern unsigned long longrun_current;
extern int global_site_id;
void misc_exit(void);
void indent(unsigned int, const char *, ...);
void error(const char *, ...);
@@ -239,6 +241,7 @@ void longrun_stop(void);
char *skip_comp(char *);
void byte_swap(uint8_t *, unsigned int *, unsigned);
bool valid_hex(const char *);
bool is_valid_site(const char *);
void debug_init(int, char **);
#endif /* MISC_H */

View File

@@ -18,6 +18,7 @@
#define MODPROBE_PREFIX "s390x"
#define UDEV_PREFIX "41"
#define UDEV_SUFFIX ".rules"
#define ZDEV_PREFIX "40"
#define PATH_MODPROBE_CONF "/etc/modprobe.d"
#define PATH_MODPROBE "/sbin/modprobe"
@@ -65,6 +66,7 @@ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *);
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *);
char *path_get_scsi_hctl_dev(const char *);
char *path_get_bus_attr(const char *, const char *);
char *path_get_zdev_rule(const char *name);
exit_code_t path_for_each(const char *,
exit_code_t (*callback)(const char *, const char *,

View File

@@ -12,6 +12,6 @@
#include "exit_code.h"
exit_code_t initrd_check(bool all_pers);
exit_code_t initrd_check(bool all_pers, int site_validate);
#endif /* ROOT_H */

View File

@@ -19,6 +19,9 @@ struct setting_list;
extern int udev_need_settle;
extern int udev_no_settle;
#define SITE_BLOCK_START "# site_start"
#define SITE_BLOCK_END "# site_end"
bool udev_file_is_empty(struct util_udev_file *file);
void udev_get_device_ids(const char *type, struct util_list *list,
@@ -31,4 +34,7 @@ void udev_add_internal_from_entry(struct setting_list *list,
struct util_udev_entry_node *entry,
struct attrib **attribs);
exit_code_t udev_write_site_rule(void);
bool is_legacy_rule(struct util_udev_file *file);
#endif /* UDEV_H */

15
zdev/include/zdev_id.h Normal file
View File

@@ -0,0 +1,15 @@
/*
* zdev - Modify and display the persistent configuration of devices
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef ZDEV_ID_H
#define ZDEV_ID_H
#define ZDEV_SITE_ID_FILE "/run/zdev_id.env"
#endif /* ZDEV_ID_H */

View File

@@ -82,6 +82,42 @@ when a device driver is loaded.
.PP
By default chzdev applies changes to both the active and the persistent
configuration.
.
.SS "Site-specific configurations"
In some high-availability setups, root and boot disks of a Linux installation are
copied to remote sites that can take over execution of a Linux workload in case of
an outage of the original site. A
.B site
in this context is an execution environment such as an
.B LPAR or
.B z/VM guest.
Each site may provide a different set of devices, or require different parameters to be
applied per device.
chzdev supports site-specific device configuration for up to
.B 10
sites. Each site is identified by a number in the range 0 to 9. You can provide a separate
set of persistent device configuration for each site. The IPL Load Parameter value specified
during boot determines the currently active site. Only the device configuration of the active
site is applied during boot and when new devices become available. A common configuration can
be provided that is applied when no site-specific configuration is available for a device in
the active site.
You can use the
.B --site
parameter to configure devices for a specific site only. Configuration actions without a site
parameter apply to the common configuration.
Linux supports the following IPL Load Parameter values for selecting the active site:
.B S<n>
Site with number <n> becomes the active site. Example: "S2" specifies site 2 as active site.
.B SS
The Subchannel-Set ID (SSID) of the IPL device specifies the number of the active site.
If no site is specified in the Load Parameter, only the common configuration is applied.
.PP
.
.SS "Tool actions"
@@ -392,6 +428,10 @@ Removes configuration files and settings associated with the
selected device.
.PP
.
.OD disable-all "D" ""
Disable device in all configurations.
.PP
.
.OD enable "e" ""
Enable device.
@@ -583,6 +623,34 @@ Apply changes to persistent configuration only.
The persistent configuration is put into effect when the system boots, or
when a device becomes available, or when a device driver is loaded.
.PP
.
.OD site "s" "SITE-ID"
Apply the persistent configuration to the specified site only.
This option can be used to configure the device in such a way that, the
same configuration can be used on multiple different sites with different
device hardwares, without any modification. With the site information, we
can add different configurations which are specific to different sites. Where
each site represents different systems or different hardware devices which
are even located in different locations.
For example, having
the configuration of main-site or failover-site in the same place, but these
configurations used only on the respective sites. Currently a user can define
10 different configurations for 10 different sites with
.I SITE-ID
0 to 9.
The configuration settings is put into effect only on the system with the
site number which is mentioned in
.I SITE-ID.
This parameter is only effective only for persistent configurations and for
dasd device-types.
.B Example:
.CL chzdev -ep 0.0.f001 --site 3
.PP
.
.OD quiet "q" ""

View File

@@ -74,9 +74,43 @@ of the currently running system, the persistent configuration stored in
configuration files, and the auto-configuration that is provided by some
machine types to automatically enable I/O devices.
.PP
Persistent configuration data is of two different types. Site-specific configuration
and common-configuration. Common configuration refers to the persistent configuration
which is not bound to any site. In many use cases, this configuration is being
used as a fallback configuration.
Refer the
.B site
section below to learn more about site-specific configuration.
.PP
By default lszdev displays information from both the active and the persistent
configuration, and, if available, from the auto-configuration.
.PP
.SS Output Format
In list format, lszdev uses values with the following meaning for the persistent colums (PERS):
.B yes
.CL A persistent, non-site specific configuration exists; No site specific configurations available.
.B no
.CL No persistent configurations is available.
.B auto
.CL Machine-provided auto-configuration is available.
.B s<n>
.CL A site-specific configuration is available for site-id n.
.B s+
.CL Site specific configurations are available for multiple sites.
.B Example
.CL TYPE ID ON PERS NAMES
.CL dasd-eckd 0.0.f001 no s+ dasda
.CL dasd-eckd 0.0.f002 no s4 dasdb
.CL dasd-eckd 0.0.f003 no no
.CL qeth 0.0.1200:0.0.1201:0.0.1202 no yes encbdf0
.
.
.SH TYPE
@@ -123,6 +157,7 @@ comma (,).
.PP
.
.
.SH SELECTION
In addition to specifying devices by their ID, you can also select devices by
their state or by the Linux functions they provide.
@@ -292,6 +327,7 @@ Display detailed information.
Displays detailed information about the configuration of the selected device
or device type. Specifying \-\-info twice will display additional device
information.
.PP
.
.OD list-columns "l" ""
@@ -392,6 +428,25 @@ escaped by placing a slash in front of it.
List information from the persistent configuration only.
Restricts output to information obtained from configuration files.
.PP
.
.OD site "s" "SITE-ID"
List the persistent configuration from the specified site only.
The resulted persistent configuration details are filtered based on the
.ID SITE-ID
value. The site specific information can be found in a basic lszdev output.
For example, the PERS column may show "s+" which indicates that the device
has multiple configurations for multiple sites, and "sn" where 'n' is an
number between 0 to 9, which indicates that the persistent configuration
for the device is available only for site 'n'.
.B Example:
.CL lszdev --site 3
.CL lszdev -i --site 2
.CL lszdev f001 --site 9
.PP
.
.OD quiet "q" ""

View File

@@ -81,9 +81,6 @@ lszdev_objects += generic_ccw.o
all: chzdev lszdev zdev_id
ifneq (${HAVE_LOCKFILE}, 0)
LDLIBS += -llockfile
endif
ifneq (${HAVE_JSONC}, 0)
LDLIBS += -ljson-c
endif

View File

@@ -1593,7 +1593,7 @@ static exit_code_t ccw_st_deconfigure_active(struct subtype *st,
static exit_code_t ccw_st_deconfigure_persistent(struct subtype *st,
struct device *dev)
{
return udev_remove_rule(st->name, dev->id, false);
return ccw_st_configure_persistent(st, dev);
}
/**

View File

@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "lib/util_path.h"
#include "lib/zt_common.h"
@@ -44,6 +45,12 @@
#include "udev.h"
#include "zfcp_lun.h"
/* current site-id in action. By default, operations are always on fallback
* site; We need the current site-id as global variable to avoid excessive
* diff of simple function parameter modifications to pass this value.
*/
int global_site_id = SITE_FALLBACK;
/* Main program action. */
typedef enum {
ACT_CONFIGURE,
@@ -73,6 +80,7 @@ struct options {
/* Actions */
unsigned int enable:1;
unsigned int deconfigure:1;
unsigned int deconfigure_all:1;
unsigned int list_attribs:1;
unsigned int list_types:1;
unsigned int help_attribs:1;
@@ -97,6 +105,7 @@ struct options {
unsigned int verbose:1;
unsigned int quiet:1;
unsigned int no_settle:1;
unsigned int site_id;
};
/* Makefile converts chzdev_usage.txt into C file which we include here. */
@@ -119,6 +128,7 @@ enum {
OPT_TYPE = 't',
OPT_ENABLE = 'e',
OPT_DECONFIGURE = 'd',
OPT_DECONFIGURE_ALL = 'D',
OPT_LIST_ATTRIBS = 'l',
OPT_HELP_ATTRIBS = 'H',
OPT_LIST_TYPES = 'L',
@@ -140,43 +150,52 @@ enum {
OPT_QUIET = 'q',
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_SITE = 's',
};
static struct opts_conflict conflict_list[] = {
OPTS_CONFLICT(OPT_ENABLE,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE, 0),
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_DECONFIGURE,
OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_TYPE, 0),
OPT_REMOVE_ALL, OPT_TYPE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_LIST_ATTRIBS,
OPT_DECONFIGURE, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_LIST_TYPES,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_EXPORT,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, 0),
OPT_REMOVE_ALL, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_IMPORT,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_EXPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_BY_PATH,
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE, 0),
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_APPLY,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_ACTIVE, 0),
OPT_REMOVE_ALL, OPT_ACTIVE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_ONLINE,
OPT_OFFLINE),
OPTS_CONFLICT(OPT_SITE,
OPT_TYPE, 0),
OPTS_CONFLICT(OPT_QUIET,
OPT_VERBOSE),
OPTS_CONFLICT(OPT_AUTO_CONF,
@@ -202,6 +221,7 @@ static const struct option opt_list[] = {
/* Actions. */
{ "enable", no_argument, NULL, OPT_ENABLE },
{ "disable", no_argument, NULL, OPT_DECONFIGURE },
{ "disable-all", no_argument, NULL, OPT_DECONFIGURE_ALL },
{ "list-attributes", no_argument, NULL, OPT_LIST_ATTRIBS },
{ "help-attribute", no_argument, NULL, OPT_HELP_ATTRIBS },
{ "list-types", no_argument, NULL, OPT_LIST_TYPES },
@@ -225,11 +245,12 @@ static const struct option opt_list[] = {
{ "verbose", no_argument, NULL, OPT_VERBOSE },
{ "quiet", no_argument, NULL, OPT_QUIET },
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
{ "site", required_argument, NULL, OPT_SITE },
{ NULL, no_argument, NULL, 0 },
};
/* Command line abbreviations. */
static const char opt_str[] = ":edlHLapr:RfyhvVqt";
static const char opt_str[] = ":edlHLapr:RfyhvVqts:D";
/* Count of persistently modified devices. */
static int pers_mod_devs;
@@ -246,6 +267,8 @@ static void init_options(struct options *opts)
opts->settings = strlist_new();
opts->remove = strlist_new();
opts->base = strlist_new();
/* Default operations are on fallback site*/
opts->site_id = SITE_FALLBACK;
}
/* Release memory used in options data structure. */
@@ -838,6 +861,12 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->deconfigure = 1;
break;
case OPT_DECONFIGURE_ALL:
/* --disable all the settings*/
opts->deconfigure = 1;
opts->deconfigure_all = 1;
break;
case OPT_LIST_ATTRIBS:
/* --list-attributes */
opts->list_attribs = 1;
@@ -955,6 +984,28 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->no_settle = 1;
break;
case OPT_SITE:
/* --site */
/* 1. User can specify only site-ids from 0 to 9
* 2. only one --site parameter is accepted
*/
if (!is_valid_site(optarg)) {
syntax("Unsupported site ID\n");
return EXIT_USAGE_ERROR;
}
if (opts->site_id != SITE_FALLBACK) {
syntax("Cannot specify '--site' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
opts->site_id = atoi(optarg);
global_site_id = opts->site_id;
opts->persistent = 1;
break;
case ':':
/* Missing option argument. */
syntax("Option '%s' requires an argument\n",
@@ -987,7 +1038,9 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
goto out;
/* Determine configuration set. */
if (!opts->active && !opts->persistent && !opts->auto_conf) {
if (opts->deconfigure_all) {
opts->config = config_all;
} else if (!opts->active && !opts->persistent && !opts->auto_conf) {
/* Default configuration targets are active + persistent */
opts->config = config_active | config_persistent;
} else {
@@ -1794,6 +1847,24 @@ static void unblacklist_ranges(struct selected_dev_node *sel,
*param_ptr = param;
}
/* Currently the site-specific configurations are supported only
* for some device types. Make sure that user get an error message when
* try to configure other device-types with site parameter.
*/
static exit_code_t is_site_supported(struct util_list *selected)
{
struct selected_dev_node *sel;
util_list_iterate(selected, sel) {
if (sel->dt && !sel->dt->site_support) {
error("Site specific configuration is not supported"
" for %s devices\n", sel->dt->devname);
return EXIT_USAGE_ERROR;
}
}
return EXIT_OK;
}
/* Handle device configuration. */
static exit_code_t configure_devices(struct options *opts, int specified,
int *found_ptr)
@@ -1827,6 +1898,9 @@ static exit_code_t configure_devices(struct options *opts, int specified,
error("No device was selected!\n");
rc = EXIT_EMPTY_SELECTION;
goto out;
} else if (opts->site_id != SITE_FALLBACK) {
if (is_site_supported(selected))
goto out;
}
/* Work on selected devices. */
@@ -1913,6 +1987,18 @@ static exit_code_t check_in_use(struct device *dev)
return EXIT_OK;
}
static bool is_site_modified(struct device *dev)
{
int i;
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].modified)
return true;
}
return false;
}
/* Deconfigure specified device. */
static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
struct options *opts, int try,
@@ -1922,6 +2008,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
exit_code_t rc;
struct device *dev;
config_t config, read_config;
int i, site_available = 0;
/* Reset processed flag. */
if (proc_ptr)
@@ -1954,7 +2041,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
dev->processed = 1;
if (try && !(dev->active.exists || dev->active.definable ||
dev->persistent.exists || dev->autoconf.exists)) {
is_dev_pers(dev) || dev->autoconf.exists)) {
/* Attempt to deconfigure this device will fail. */
return EXIT_DEVICE_NOT_FOUND;
}
@@ -1964,7 +2051,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
* dev. */
if (SCOPE_ACTIVE(config) &&
!(dev->active.exists || dev->active.definable)) {
if (!((SCOPE_PERSISTENT(config) && dev->persistent.exists) ||
if (!((SCOPE_PERSISTENT(config) && is_dev_pers(dev)) ||
(SCOPE_AUTOCONF(config) && dev->autoconf.exists)))
return EXIT_DEVICE_NOT_FOUND;
}
@@ -1985,16 +2072,48 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
} else if (subtype_online_get(st, dev, config_active) == 1)
dev->active.modified = 1;
}
if (SCOPE_PERSISTENT(config) && dev->persistent.exists) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
if (SCOPE_PERSISTENT(config)) {
if (dev->persistent.exists) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
} else if (is_dev_pers(dev) && global_site_id != SITE_FALLBACK) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
}
}
if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) {
dev->autoconf.deconfigured = 1;
dev->autoconf.modified = 1;
}
/* In case of deconfigure_all command, make sure that site specific
* settings are also removed.
*/
if (opts->deconfigure_all) {
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].exists) {
site_available++;
/* To remove site-specific settings, simply
* make the deconfigured = 1
*/
dev->site_specific[i].deconfigured = 1;
dev->site_specific[i].modified = 1;
}
}
}
if (site_available) {
/* set persistent.exists = 1 to let the
* drivers know that we need to modify the
* udev-rule
*/
dev->persistent.exists = 1;
dev->persistent.modified = 1;
dev->persistent.deconfigured = 1;
}
if (!dev->active.modified && !dev->persistent.modified &&
!dev->autoconf.modified)
!dev->autoconf.modified && !is_site_modified(dev))
return EXIT_OK;
/* Pre-write check. */
@@ -2036,6 +2155,9 @@ static exit_code_t deconfigure_devices(struct options *opts)
error("No device was selected!\n");
rc = EXIT_EMPTY_SELECTION;
goto out;
} else if (opts->site_id != SITE_FALLBACK) {
if (is_site_supported(selected))
goto out;
}
/* Work on selected devices. */
@@ -2558,8 +2680,13 @@ static void action_note(const char *msg, config_t config)
{
if (config == config_active)
info("%s the active configuration only\n", msg);
else if (config == config_persistent)
info("%s the persistent configuration only\n", msg);
else if (config == config_persistent) {
if (global_site_id == SITE_FALLBACK)
info("%s the persistent configuration only\n", msg);
else
info("%s the site %d configuration only\n", msg,
global_site_id);
}
else if (config == config_autoconf)
info("%s the auto-configuration only\n", msg);
}
@@ -3034,7 +3161,7 @@ int main(int argc, char *argv[])
!dryrun) {
/* If the root device/device type or early devices have been
* modified, additional work might be necessary. */
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD);
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD, opts.deconfigure_all);
if (rc && !drc)
drc = rc;
}

View File

@@ -33,7 +33,8 @@ SETTINGS
ACTIONS
-e, --enable Enable device
-d, --disable Disable device
-d, --disable Disable specified setting of the device
-D, --disable-all Disable all the settings of the device
-l, --list-attributes List attributes
-L, --list-types List supported device types
-H, --help-attribute Show detailed help on specified attribute
@@ -56,5 +57,6 @@ OPTIONS
--base PATH Use PATH as base for accessing files
--no-settle Do not wait for udev to settle
--auto-conf Apply changes to auto-configuration only
-s, --site ID Apply changes to the specified site only
-V, --verbose Print additional run-time information
-q, --quiet Print only minimal run-time information

View File

@@ -688,6 +688,7 @@ struct devtype dasd_devtype = {
"(DASDs)",
.devname = "DASD",
.modules = STRING_ARRAY(DASD_MOD_NAME),
.site_support = 1,
.subtypes = SUBTYPE_ARRAY(
&dasd_subtype_eckd,

View File

@@ -29,6 +29,7 @@ struct device *device_new(struct subtype *st, const char *id)
{
struct device *dev;
struct namespace *ns = st->namespace;
int i;
dev = misc_malloc(sizeof(struct device));
dev->subtype = st;
@@ -44,12 +45,17 @@ struct device *device_new(struct subtype *st, const char *id)
dev->persistent.settings = setting_list_new();
dev->autoconf.settings = setting_list_new();
for (i = 0; i < NUM_SITES; i++)
dev->site_specific[i].settings = setting_list_new();
return dev;
}
/* Release all resources associated with the specified device. */
void device_free(struct device *dev)
{
int i;
if (!dev)
return;
free(dev->id);
@@ -57,12 +63,18 @@ void device_free(struct device *dev)
setting_list_free(dev->active.settings);
setting_list_free(dev->persistent.settings);
setting_list_free(dev->autoconf.settings);
for (i = 0; i < NUM_SITES; i++)
setting_list_free(dev->site_specific[i].settings);
free(dev);
}
/* Used for debugging. */
void device_print(struct device *dev, int level)
{
int i;
printf("%*sdevice at %p:\n", level, "", (void *) dev);
if (!dev)
return;
@@ -96,6 +108,36 @@ void device_print(struct device *dev, int level)
setting_list_print(dev->autoconf.settings, level + 8);
else
printf("%*s<none>\n", level + 8, "");
printf("%*spersistent-site-specific:\n", level + 4, "");
for (i = 0; i < NUM_SITES; i++) {
printf("%*sexists=%d mod=%d deconf=%d\n",
level + 8, "", dev->site_specific[i].exists,
dev->site_specific[i].modified,
dev->site_specific[i].deconfigured);
if (dev->site_specific[i].settings)
setting_list_print(dev->site_specific[i].settings, level + 8);
else
printf("%*s<none>\n", level + 8, "");
}
}
char *device_get_sites(struct device *dev)
{
struct util_list *names;
char *str;
int i;
names = strlist_new();
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].exists)
strlist_add(names, "%d", i);
}
str = strlist_flatten(names, ",");
strlist_free(names);
return str;
}
static const void *device_hash_get_id(void *dev_ptr)
@@ -627,10 +669,16 @@ exit_code_t device_check_settings(struct device *dev, config_t config,
}
struct setting_list *device_get_setting_list(struct device *dev,
config_t config)
config_t config,
int site_id)
{
struct setting_list *settings = NULL;
if (site_id < SITE_FALLBACK) {
settings = dev->site_specific[site_id].settings;
goto out;
}
if (config == config_active)
settings = dev->active.settings;
else if (config == config_persistent)
@@ -638,6 +686,7 @@ struct setting_list *device_get_setting_list(struct device *dev,
else if (config == config_autoconf)
settings = dev->autoconf.settings;
out:
return settings;
}
@@ -657,3 +706,21 @@ config_t device_get_config(struct device *dev)
return config;
}
/* Determine if persistent configuration available for any of the sites*/
bool is_dev_pers(struct device *dev)
{
int i;
if (global_site_id != SITE_FALLBACK &&
!dev->site_specific[global_site_id].exists)
return false;
else if (dev->persistent.exists)
return true;
for (i = 0; i < NUM_SITES; i++)
if (dev->site_specific[i].exists)
return true;
return false;
}

View File

@@ -21,6 +21,8 @@
#include "setting.h"
#include "subtype.h"
static int import_site_id = SITE_FALLBACK;
struct export_header {
export_t type;
config_t config;
@@ -28,13 +30,28 @@ struct export_header {
char *id;
};
static bool header_str_to_config(const char *config_str, config_t *config)
{
int site;
if (strstr(config_str, "site")) {
if (sscanf(config_str, "site%d", &site) == 1) {
import_site_id = site;
*config = config_persistent;
return true;
}
}
import_site_id = SITE_FALLBACK;
return str_to_config(config_str, config);
}
static struct export_header *header_new(const char *config_str,
const char *type, const char *id)
{
config_t config;
struct export_header *hdr;
if (!str_to_config(config_str, &config))
if (!header_str_to_config(config_str, &config))
return NULL;
hdr = misc_malloc(sizeof(struct export_header));
if (!id)
@@ -130,6 +147,14 @@ static void export_write_comment(FILE *fd)
fprintf(fd, "# Generated by chzdev\n");
}
static const char *site_specific_header(config_t config)
{
if (global_site_id != SITE_FALLBACK)
return misc_asprintf("site%d", global_site_id);
else
return config_to_str(config);
}
/* Write an export file header. */
static void write_header(FILE *fd, config_t config, const char *type,
const char *id, int *first_ptr)
@@ -143,9 +168,9 @@ static void write_header(FILE *fd, config_t config, const char *type,
}
if (id)
fprintf(fd, "[%s %s %s]\n", config_to_str(config), type, id);
fprintf(fd, "[%s %s %s]\n", site_specific_header(config), type, id);
else
fprintf(fd, "[%s %s]\n", config_to_str(config), type);
fprintf(fd, "[%s %s]\n", site_specific_header(config), type);
}
static int count_exportable(struct device *dev, config_t config)
@@ -154,7 +179,7 @@ static int count_exportable(struct device *dev, config_t config)
struct setting *s;
int count;
list = device_get_setting_list(dev, config);
list = device_get_setting_list(dev, config, global_site_id);
if (!list)
return 0;
count = 0;
@@ -408,7 +433,11 @@ static exit_code_t handle_header(const char *filename, int lineno,
dev->active.exists = 1;
}
if (SCOPE_PERSISTENT(hdr->config)) {
setting_list_clear(dev->persistent.settings);
if (import_site_id != SITE_FALLBACK)
setting_list_clear(dev->site_specific[import_site_id].settings);
else
setting_list_clear(dev->persistent.settings);
dev->persistent.exists = 1;
}
if (SCOPE_AUTOCONF(hdr->config)) {
@@ -462,7 +491,7 @@ static exit_code_t handle_setting(const char *filename, int lineno,
} else if (dev) {
/* We're inside a device section. */
attribs = dev->subtype->dev_attribs;
list = device_get_setting_list(dev, config);
list = device_get_setting_list(dev, config, SITE_FALLBACK);
} else
return EXIT_OK;
@@ -502,7 +531,8 @@ static bool empty_device(struct device *dev)
return false;
if (util_list_is_empty(&dev->active.settings->list) &&
util_list_is_empty(&dev->persistent.settings->list) &&
util_list_is_empty(&dev->autoconf.settings->list))
util_list_is_empty(&dev->autoconf.settings->list) &&
import_site_id == SITE_FALLBACK)
return true;
return false;
}
@@ -574,8 +604,9 @@ exit_code_t export_read(FILE *fd, const char *filename,
ptrlist_add(objects, object_new(export_devtype,
dt));
} else if (dev) {
ptrlist_add(objects, object_new(export_device,
dev));
if (global_site_id == import_site_id)
ptrlist_add(objects, object_new(export_device,
dev));
}
} else if (parse_setting(l, &key, &value)) {
/* Found <key>=<value>. */
@@ -584,8 +615,10 @@ exit_code_t export_read(FILE *fd, const char *filename,
"valid section\n");
rc = EXIT_FORMAT_ERROR;
} else {
rc = handle_setting(filename, lineno, key,
value, dt, dev, config);
if (global_site_id == import_site_id ||
import_site_id == SITE_FALLBACK)
rc = handle_setting(filename, lineno, key,
value, dt, dev, config);
}
free(key);
free(value);

View File

@@ -348,7 +348,7 @@ static void _add_setting(const char *filename, struct device *dev,
struct attrib *a;
struct setting_list *list;
list = device_get_setting_list(dev, config);
list = device_get_setting_list(dev, config, SITE_FALLBACK);
a = attrib_find(dev->subtype->dev_attribs, key);
if (!a) {
warnx("%s: Applying unknown device setting %s=%s", filename,

View File

@@ -32,6 +32,12 @@
#include "table.h"
#include "table_types.h"
/* current site-id in action. By default, operations are always on fallback
* site; We need the current site-id as global variable to avoid excessive
* diff of simple function parameter modifications to pass this value.
*/
int global_site_id = SITE_FALLBACK;
/* Main program action. */
typedef enum {
ACT_LIST,
@@ -66,6 +72,7 @@ struct options {
unsigned int pairs:1;
unsigned int verbose:1;
unsigned int quiet:1;
unsigned int site_id;
};
/* Makefile converts lszdev_usage.txt into C file which we include here. */
@@ -100,6 +107,7 @@ enum {
OPT_QUIET = 'q',
OPT_PAIRS = 'P',
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_SITE = 's',
};
static struct opts_conflict conflict_list[] = {
@@ -120,6 +128,8 @@ static struct opts_conflict conflict_list[] = {
OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE, OPT_OFFLINE,
OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE, OPT_FAILED,
0),
OPTS_CONFLICT(OPT_SITE,
OPT_TYPE),
OPTS_CONFLICT(OPT_ONLINE,
OPT_OFFLINE),
OPTS_CONFLICT(OPT_QUIET,
@@ -159,11 +169,12 @@ static const struct option opt_list[] = {
{ "pairs", no_argument, NULL, OPT_PAIRS },
{ "verbose", no_argument, NULL, OPT_VERBOSE },
{ "quiet", no_argument, NULL, OPT_QUIET },
{ "site", required_argument, NULL, OPT_SITE },
{ NULL, no_argument, NULL, 0 },
};
/* Command line abbreviations. */
static const char opt_str[] = ":tilLhvapPc:nVq";
static const char opt_str[] = ":tilLhvapPc:nVqs:";
/* Initialize options data structure. */
static void init_options(struct options *opts)
@@ -172,6 +183,8 @@ static void init_options(struct options *opts)
opts->select = select_opts_new();
opts->columns = strlist_new();
opts->base = strlist_new();
/* Default operations are on fallback site*/
opts->site_id = SITE_FALLBACK;
}
/* Release memory used in options data structure. */
@@ -212,6 +225,41 @@ static action_t get_action(struct options *opts)
return ACT_LIST;
}
/* Determine the site information from the device */
static char *get_site_from_pers(struct device *dev)
{
int site, i, num = 0;
for (i = 0; i < NUM_SITES; i++) {
if (dev->site_specific[i].exists) {
site = i;
num++;
if (site == global_site_id && site != SITE_FALLBACK)
return misc_asprintf("s%d", site);
}
}
/* Incase of devices which do not have --site support, all the
* read configurations will be in the dev->persistent and not
* in dev->site_specific.
*/
if (num == 0 && dev->persistent.exists == 1 &&
global_site_id == SITE_FALLBACK)
return misc_asprintf("yes");
/* Found more site-specific information ? return s+ */
if (num > 1)
return misc_asprintf("s+");
if (num == 1)
if (site == SITE_FALLBACK)
return misc_asprintf("yes");
else
return misc_asprintf("s%d", site);
else
return misc_asprintf("no");
}
/* Check options data structure for syntax errors. */
static exit_code_t check_options(struct options *opts,
int specified[OPTS_MAX + 1], int op)
@@ -661,6 +709,27 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->quiet = 1;
break;
case OPT_SITE:
/* --site */
/* User can specify only site-ids from 0 to 9 */
if (!is_valid_site(optarg)) {
syntax("Unsupported site ID\n");
return EXIT_USAGE_ERROR;
}
if (opts->site_id != SITE_FALLBACK) {
syntax("Cannot specify '--site' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
/* site information is a persistent configuration.
* set opts->persistent here.
*/
opts->persistent = 1;
opts->site_id = atoi(optarg);
break;
case ':':
/* Missing option argument. */
syntax("Option '%s' requires an argument\n",
@@ -718,7 +787,8 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->select->all = 1;
else if (opts->config == config_active)
opts->select->existing = 1;
else if (opts->config == config_persistent)
else if (opts->config == config_persistent ||
opts->site_id != SITE_FALLBACK)
opts->select->configured = 1;
}
break;
@@ -782,34 +852,53 @@ static struct column *dev_table = COLUMN_ARRAY(
);
static char *merge_str(const char *act, const char *pers, const char *ac,
config_t config)
config_t config, char **site, int max_sites)
{
char *str;
size_t len;
int i;
struct util_list *str_list;
str_list = strlist_new();
act = act ? act : "-";
pers = pers ? pers : "-";
ac = ac ? ac : "-";
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0)
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0 &&
max_sites == 1)
return misc_strdup(act);
len = strlen(act) + strlen(pers) + strlen(ac) + /* 3 * / + NUL */ 4;
str = misc_malloc(len);
if (SCOPE_ACTIVE(config))
strcat(str, act);
strlist_add(str_list, act);
if (SCOPE_PERSISTENT(config)) {
if (*str)
strcat(str, "/");
strcat(str, pers);
if (global_site_id == SITE_FALLBACK) {
strlist_add(str_list, pers);
} else {
strlist_add(str_list, site[global_site_id]);
goto out;
}
}
if (SCOPE_AUTOCONF(config)) {
if (*str)
strcat(str, "/");
strcat(str, ac);
strlist_add(str_list, ac);
}
/* Do not show site-specific information when user specifically
* asked for persistent configuration setting.
*/
if (SCOPE_PERSISTENT(config) && config != config_persistent) {
for (i = 0; i <= max_sites; i++) {
if (site[i])
strlist_add(str_list, site[i]);
else
if (str_list)
strlist_add(str_list, "-");
}
}
out:
str = strlist_flatten(str_list, "/");
strlist_free(str_list);
return str;
}
@@ -848,12 +937,13 @@ static char *dev_table_get_modules(struct device *dev)
return str;
}
static char *get_attr(struct device *dev, const char *name, config_t config)
static char *get_attr(struct device *dev, const char *name, config_t config,
int site_id)
{
struct setting_list *list;
struct setting *s;
list = device_get_setting_list(dev, config);
list = device_get_setting_list(dev, config, site_id);
if (!list)
return NULL;
s = setting_list_find(list, name);
@@ -868,30 +958,61 @@ static char *get_attr(struct device *dev, const char *name, config_t config)
return misc_strdup(s->value);
}
/* When lszdev --info, make sure that the output contains only the
* valid site information. Check if the site is valid and then add
* the new SITEn column. For SITE_FALLBACK, the PERSISTENT column will
* be used.
*/
static bool is_site_show(int site_id, int current_site)
{
if (site_id == SITE_FALLBACK || site_id == current_site)
return true;
return false;
}
/* Return string for attribute with specified @name for device @dev. */
static char *dev_table_get_attr(struct device *dev, const char *attr,
config_t config)
{
char *act = NULL, *pers = NULL, *ac = NULL, *str;
const char *name;
char *site[NUM_USER_SITES];
int i;
static int max_sites;
name = strchr(attr, ':');
if (!name)
return NULL;
name++;
/* To get the default settings on any configuration, make sure that
* the site_id is specified as SITE_FALLBACK. Any value of site_id
* less than SITE_FALLBACK will endup providing site-specific attribute
* settings.
*/
if (SCOPE_ACTIVE(config))
act = get_attr(dev, name, config_active);
if (SCOPE_PERSISTENT(config))
pers = get_attr(dev, name, config_persistent);
act = get_attr(dev, name, config_active, SITE_FALLBACK);
if (SCOPE_PERSISTENT(config)) {
pers = get_attr(dev, name, config_persistent, SITE_FALLBACK);
for (i = 0; i < NUM_USER_SITES; i++)
site[i] = get_attr(dev, name, config, i);
}
if (SCOPE_AUTOCONF(config))
ac = get_attr(dev, name, config_autoconf);
ac = get_attr(dev, name, config_autoconf, SITE_FALLBACK);
str = merge_str(act, pers, ac, config);
for (i = 0; i < NUM_USER_SITES; i++)
if (dev->site_specific[i].exists)
max_sites = (max_sites < i) ? i : max_sites;
str = merge_str(act, pers, ac, config, site, max_sites);
free(act);
free(pers);
free(ac);
if (SCOPE_PERSISTENT(config))
for (i = 0; i < NUM_USER_SITES; i++)
free(site[i]);
return str;
}
@@ -938,9 +1059,9 @@ static char *dev_table_get_value(void *item, int id, const char *heading,
return misc_strdup(YESNO(dev->active.exists ||
dev->active.definable));
case dev_pers:
if (!dev->persistent.exists && dev->autoconf.exists)
if (!is_dev_pers(dev) && dev->autoconf.exists)
return misc_strdup("auto");
return misc_strdup(YESNO(dev->persistent.exists));
return get_site_from_pers(dev);
case dev_auto:
return misc_strdup(YESNO(dev->autoconf.exists));
case dev_online:
@@ -1032,11 +1153,17 @@ static struct util_list *dev_table_build(struct options *opts,
/* Only process existing devices. */
active = dev->active.exists || dev->active.definable;
persistent = dev->persistent.exists;
persistent = (int)is_dev_pers(dev);
autoconf = dev->autoconf.exists;
if (!active && !persistent && !autoconf)
continue;
/* with --site option, we consider only the persistent
* configurations available on the specified site-id.
* ignore active and autoconf here
*/
if (!persistent && global_site_id != SITE_FALLBACK)
continue;
ptrlist_add(devices, dev);
}
@@ -1107,6 +1234,16 @@ enum settings_table_id {
settings_active,
settings_persistent,
settings_autoconf,
settings_site0,
settings_site1,
settings_site2,
settings_site3,
settings_site4,
settings_site5,
settings_site6,
settings_site7,
settings_site8,
settings_site9,
};
static struct column *settings_table = COLUMN_ARRAY(
@@ -1114,17 +1251,29 @@ static struct column *settings_table = COLUMN_ARRAY(
COLUMN("READONLY", align_left, settings_readonly, 1, ""),
COLUMN("ACTIVE", align_left, settings_active, 1, ""),
COLUMN("PERSISTENT", align_left, settings_persistent, 1, ""),
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, "")
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, ""),
COLUMN("SITE0", align_left, settings_site0, 0, ""),
COLUMN("SITE1", align_left, settings_site1, 0, ""),
COLUMN("SITE2", align_left, settings_site2, 0, ""),
COLUMN("SITE3", align_left, settings_site3, 0, ""),
COLUMN("SITE4", align_left, settings_site4, 0, ""),
COLUMN("SITE5", align_left, settings_site5, 0, ""),
COLUMN("SITE6", align_left, settings_site6, 0, ""),
COLUMN("SITE7", align_left, settings_site7, 0, ""),
COLUMN("SITE8", align_left, settings_site8, 0, ""),
COLUMN("SITE9", align_left, settings_site9, 0, "")
);
static struct util_list *settings_table_build(struct setting_list *active,
struct setting_list *persistent,
struct setting_list *autoconf,
struct device *dev,
bool readonly)
{
struct util_list *names, *items;
struct setting *s;
struct strlist_node *str;
int i;
/* Get a list of all configured attributes. */
names = strlist_new();
@@ -1139,6 +1288,11 @@ static struct util_list *settings_table_build(struct setting_list *active,
if (persistent && !readonly) {
util_list_iterate(&persistent->list, s)
strlist_add(names, s->name);
if (dev) {
for (i = 0; i < NUM_SITES; i++)
util_list_iterate(&dev->site_specific[i].settings->list, s)
strlist_add(names, s->name);
}
}
if (autoconf && !readonly) {
util_list_iterate(&autoconf->list, s)
@@ -1159,6 +1313,7 @@ struct settings_table_data {
struct setting_list *active;
struct setting_list *persistent;
struct setting_list *autoconf;
struct setting_list *sites[NUM_SITES];
int pairs;
bool readonly;
};
@@ -1184,6 +1339,37 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
case settings_autoconf:
list = stdata->autoconf;
break;
case settings_site0:
list = stdata->sites[0];
break;
case settings_site1:
list = stdata->sites[1];
break;
case settings_site2:
list = stdata->sites[2];
break;
case settings_site3:
list = stdata->sites[3];
break;
case settings_site4:
list = stdata->sites[4];
break;
case settings_site5:
list = stdata->sites[5];
break;
case settings_site6:
list = stdata->sites[6];
break;
case settings_site7:
list = stdata->sites[7];
break;
case settings_site8:
list = stdata->sites[8];
break;
case settings_site9:
list = stdata->sites[9];
break;
default:
break;
}
@@ -1208,16 +1394,18 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
static void settings_table_print(struct setting_list *active,
struct setting_list *persistent,
struct setting_list *autoconf,
struct device *dev,
struct options *opts, int ind, bool readonly,
bool neednl)
{
struct util_list *items;
struct settings_table_data data;
int i;
items = settings_table_build(
SCOPE_ACTIVE(opts->config) ? active : NULL,
SCOPE_PERSISTENT(opts->config) ? persistent : NULL,
autoconf, readonly);
autoconf, dev, readonly);
if (util_list_is_empty(items)) {
if (!opts->pairs && !readonly)
indent(ind, "%sNo settings found\n",
@@ -1232,14 +1420,28 @@ static void settings_table_print(struct setting_list *active,
table_set_default(settings_table, settings_active,
SCOPE_ACTIVE(opts->config));
table_set_default(settings_table, settings_persistent,
is_site_show(opts->site_id, SITE_FALLBACK) &&
SCOPE_PERSISTENT(opts->config) && !readonly ? 1 : 0);
table_set_default(settings_table, settings_autoconf,
(SCOPE_AUTOCONF(opts->config) || autoconf) &&
!readonly ? 1 : 0);
if (dev) {
for (i = 0; i < NUM_USER_SITES; i++)
table_set_default(settings_table, settings_site0 + i,
SCOPE_PERSISTENT(opts->config) &&
is_site_show(opts->site_id, i) &&
dev->site_specific[i].exists &&
!readonly ? 1 : 0);
for (i = 0; i < NUM_SITES; i++)
data.sites[i] = dev->site_specific[i].settings;
}
data.active = active;
data.persistent = persistent;
data.autoconf = autoconf;
data.pairs = opts->pairs;
table_print(settings_table, settings_table_get_value, &data, items,
NULL, 1, opts->pairs, ind, 0);
@@ -1320,7 +1522,7 @@ static exit_code_t do_info_type(struct options *opts)
}
} else {
settings_table_print(dt->active_settings,
dt->persistent_settings, NULL, opts,
dt->persistent_settings, NULL, NULL, opts,
INFO_INDENT, false, false);
}
@@ -1346,7 +1548,7 @@ static void do_list_columns(struct options *opts)
static void do_info_one_device(struct device *dev, struct options *opts)
{
struct subtype *st = dev->subtype;
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key;
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key, *sites;
const char *id, *type, *exists, *persist, *ac, *prefix;
struct util_list *resources, *errors;
struct strlist_node *s;
@@ -1367,6 +1569,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
exists = YESNO(dev->active.exists || dev->active.definable);
persist = YESNO(dev->persistent.exists);
ac = YESNO(dev->autoconf.exists);
sites = device_get_sites(dev);
/* Print information. */
if (opts->pairs) {
@@ -1384,6 +1587,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
print_pair("ONLINE", online);
print_pair("EXISTS", exists);
print_pair("PERSISTENT", persist);
if (*sites)
print_pair("SITES", sites);
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
print_pair("AUTOCONF", ac);
if (errors) {
@@ -1413,6 +1619,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
print_info("Persistent", persist);
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
print_info("Auto-configured", ac);
if (*sites)
print_info("Sites", sites);
if (errors) {
first = true;
util_list_iterate(errors, s) {
@@ -1453,17 +1662,17 @@ static void do_info_one_device(struct device *dev, struct options *opts)
}
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
dev->persistent.exists ? dev->persistent.settings :
is_dev_pers(dev) ? dev->persistent.settings :
NULL,
dev->autoconf.exists ? dev->autoconf.settings :
NULL,
NULL, dev,
opts, INFO_INDENT, false, !opts->pairs);
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
dev->persistent.exists ? dev->persistent.settings :
is_dev_pers(dev) ? dev->persistent.settings :
NULL,
dev->autoconf.exists ? dev->autoconf.settings :
NULL,
NULL, dev,
opts, INFO_INDENT, true, !opts->pairs);
strlist_free(errors);
@@ -1474,6 +1683,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
free(cdevs);
free(bdevs);
free(names);
free(sites);
}
/* Perform --info on devices. */
@@ -1518,10 +1728,18 @@ static exit_code_t do_info_devices(struct options *opts)
/* Only process existing devices. */
active = dev->active.exists || dev->active.definable;
persistent = dev->persistent.exists;
persistent = (int)is_dev_pers(dev);
autoconf = dev->autoconf.exists;
if (!active && !persistent && !autoconf)
continue;
/* with --site option, we consider only the persistent
* configurations available on the specified site-id.
* ignore active and autoconf here
*/
if (!persistent && global_site_id != SITE_FALLBACK)
continue;
if (found > 0)
printf("\n");
do_info_one_device(dev, opts);
@@ -1558,6 +1776,7 @@ int main(int argc, char *argv[])
/* Set globals. */
verbose = opts.verbose;
quiet = opts.quiet;
global_site_id = opts.site_id;
path_set_base(opts.base);
if (opts.pairs)
set_stdout_data();

View File

@@ -42,5 +42,6 @@ OPTIONS
--base PATH Use PATH as base for accessing files
--pairs Produce output in KEY="VALUE" format
--auto-conf Only show auto-configuration data
-s, --site ID Only show data configured for the specified site
-V, --verbose Print additional run-time information
-q, --quiet Print only minimal run-time information

View File

@@ -1695,3 +1695,20 @@ char *misc_strrstr(const char *haystack, const char *needle)
return result;
}
/* Check if the site mentioned is valid */
bool is_valid_site(const char *str)
{
char *ptr = NULL;
long site_id;
site_id = strtol(str, &ptr, 10);
if (*ptr)
return false;
if (site_id < 0 || site_id >= NUM_USER_SITES)
return false;
return true;
}

View File

@@ -404,3 +404,13 @@ char *path_get_bus_attr(const char *bus, const char *attr)
{
return path_get("/sys/bus/%s/%s", bus, attr);
}
/* Return path will be with ZDEV_PREFIX oon PATH_UDEV_RULES */
char *path_get_zdev_rule(const char *name)
{
const char *path = PATH_UDEV_RULES;
/* Create file. */
return path_get("%s/%s-%s%s", path,
ZDEV_PREFIX, name, UDEV_SUFFIX);
}

View File

@@ -83,23 +83,58 @@ static bool is_zdev_early_0(struct selected_dev_node *sel)
{
struct setting *s;
struct device *dev;
int i;
dev = device_list_find(sel->st->devices, sel->id, NULL);
if (!dev)
return false;
s = setting_list_find(dev->persistent.settings,
internal_attr_early.name);
if (!s)
for (i = 0; i < NUM_SITES; i++) {
s = setting_list_find(dev->site_specific[i].settings,
internal_attr_early.name);
if (!s)
return false;
if (s->specified && strcmp(s->value, "0") == 0)
return true;
}
return false;
}
/* When site-specific settings has zdev:early configured, it is not
* detected with the --by-attr functionality of select_devices. Instead
* we will have to iterate through each configuration and check if there
* is a zdev:early set in the device. This is usually applicable only in
* the case of deconfigure-all command, because, for all other commands
* we copy the settings-in-action to the persistent settings and do the
* processing there.
*/
static bool is_zdev_early_site(struct selected_dev_node *sel)
{
struct setting *s;
struct device *dev;
int i;
if (global_site_id != SITE_FALLBACK)
return false;
if (s->specified && strcmp(s->value, "0") == 0)
return true;
dev = device_list_find(sel->st->devices, sel->id, NULL);
if (!dev)
return false;
for (i = 0; i < NUM_SITES; i++) {
s = setting_list_find(dev->site_specific[i].settings,
internal_attr_early.name);
if (s)
return true;
}
return false;
}
/* Determine if initial RAM-disk needs updating. If so, run the corresponding
* scripts if available. */
exit_code_t initrd_check(bool all_pers)
exit_code_t initrd_check(bool all_pers, int site_validate)
{
struct util_list *selected, *params, *mod = strlist_new();
struct selected_dev_node *sel;
@@ -142,7 +177,12 @@ exit_code_t initrd_check(bool all_pers)
}
/* Finally add devices with zdev:early=1. */
select = select_opts_new();
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
/* If we need information from multiple site, we cannot use the by-attr
* functionality of select_devices function. Instead we have to iterate
* through individual devices and site settings to find the key.
*/
if (!site_validate)
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
select_devices(select, selected, 1, 0, 0,
config_active | config_persistent, scope_mandatory,
err_ignore);
@@ -162,7 +202,12 @@ check_mod:
/* Check devices. */
dev = device_list_find(sel->st->devices, sel->id, NULL);
if (dev && dev->persistent.exists &&
(device_needs_writing(dev, config_persistent) || force)) {
(device_needs_writing(dev, config_persistent) || force) &&
!site_validate) {
strlist_add(mod, "%s %s", dev->subtype->devname,
dev->id);
} else if (site_validate && is_zdev_early_site(sel) &&
device_needs_writing(dev, config_persistent)) {
strlist_add(mod, "%s %s", dev->subtype->devname,
dev->id);
}

View File

@@ -23,6 +23,7 @@
#include "path.h"
#include "setting.h"
#include "udev.h"
#include "zdev_id.h"
int udev_need_settle = 0;
int udev_no_settle;
@@ -137,3 +138,61 @@ void udev_add_internal_from_entry(struct setting_list *list,
out:
free(copy);
}
/*
* Write the UDEV rule that determines the active SITE_ID
*/
exit_code_t udev_write_site_rule(void)
{
char *path, *name = "zdev-id";
exit_code_t rc = EXIT_OK;
FILE *fd;
/* Create file. */
path = path_get_zdev_rule(name);
if (!util_path_exists(path)) {
rc = path_create(path);
if (rc)
goto out;
}
fd = misc_fopen(path, "w");
if (!fd) {
error("Could not write to file %s: %s\n", path,
strerror(errno));
rc = EXIT_RUNTIME_ERROR;
goto out;
}
fprintf(fd, "# Generated by chzdev\n");
fprintf(fd, "\n");
/* We have to make sure that we are not trying to determine the SITE_ID
* for all the corresponding uevent. Check if the SITE_ID is already
* determined by reading the file /run/zdev_site_id; and if it is not
* available, execute the zdev_id program and determine the SITE_ID and
* create the new zdev_site_id file for next run.
*/
fprintf(fd, "TEST!=\"%s\", IMPORT{program}="
"\"%s/zdev_id\",GOTO=\"end\"\n", ZDEV_SITE_ID_FILE,
TOOLS_LIBDIR);
fprintf(fd, "IMPORT{file}=\"%s\"\n", ZDEV_SITE_ID_FILE);
fprintf(fd, "LABEL=\"end\"\n");
if (misc_fclose(fd))
warn("Could not close file %s: %s\n", path, strerror(errno));
out:
free(path);
return rc;
}
bool is_legacy_rule(struct util_udev_file *file)
{
struct util_udev_line_node *line;
util_list_iterate(&file->lines, line)
if (starts_with(line->line, SITE_BLOCK_START))
return false;
return true;
}

View File

@@ -26,6 +26,8 @@
#include "udev.h"
#include "udev_ccw.h"
#define SITE_FALLBACK_SUFFIX "fb"
/* Check if a udev rule for the specified ccw device exists. */
bool udev_ccw_exists(const char *type, const char *id, bool autoconf)
{
@@ -79,10 +81,68 @@ out:
free(copy);
}
static void get_site_from_line(char *line, int *site, bool *in_site)
{
char site_str[2];
if (starts_with(line, SITE_BLOCK_START)) {
memcpy(site_str, line + sizeof(SITE_BLOCK_START), 2);
if (strcmp(site_str, "fb") != 0)
*site = atoi(site_str);
else
*site = SITE_FALLBACK;
*in_site = true;
} else if (starts_with(line, SITE_BLOCK_END)) {
memcpy(site_str, line + sizeof(SITE_BLOCK_END), 2);
if (strcmp(site_str, "fb") != 0)
*site = atoi(site_str);
else
*site = SITE_FALLBACK;
*in_site = false;
}
}
static void dev_state_copy(struct device_state *dst, struct device_state *src)
{
setting_list_free(dst->settings);
dst->settings = setting_list_copy(src->settings);
dst->exists = src->exists;
dst->modified = src->modified;
dst->deconfigured = src->deconfigured;
dst->blacklisted = src->blacklisted;
dst->definable = src->definable;
}
/* Extract per-site CCW device settings from a CCW device udev rule file. */
static void _udev_file_get_settings_new(struct util_udev_file *file,
struct attrib **attribs,
struct device *dev)
{
struct util_udev_line_node *line;
struct util_udev_entry_node *entry;
int site_id = SITE_FALLBACK;
struct setting_list *list;
bool in_site = false;
util_list_iterate(&file->lines, line) {
get_site_from_line(line->line, &site_id, &in_site);
if (!in_site || site_id >= NUM_SITES || site_id < 0)
continue;
entry = util_list_start(&line->entries);
if (!entry)
continue;
list = dev->site_specific[site_id].settings;
add_setting_from_entry(list, entry, attribs);
dev->site_specific[site_id].exists = 1;
if (site_id == global_site_id)
dev_state_copy(&dev->persistent, &dev->site_specific[site_id]);
}
}
/* Extract CCW device settings from a CCW device udev rule file. */
static void udev_file_get_settings(struct util_udev_file *file,
struct attrib **attribs,
struct setting_list *list)
static void _udev_file_get_settings_legacy(struct util_udev_file *file,
struct attrib **attribs,
struct setting_list *list)
{
struct util_udev_line_node *line;
struct util_udev_entry_node *entry;
@@ -95,6 +155,22 @@ static void udev_file_get_settings(struct util_udev_file *file,
}
}
static void udev_file_get_settings(struct util_udev_file *file,
struct attrib **attribs,
struct device *dev,
bool autoconf)
{
struct device_state *state = autoconf ? &dev->autoconf :
&dev->persistent;
if (is_legacy_rule(file) || autoconf) {
_udev_file_get_settings_legacy(file, attribs, state->settings);
state->exists = 1;
} else {
_udev_file_get_settings_new(file, attribs, dev);
}
}
/* Read the persistent configuration of a CCW device from a udev rule. */
exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
{
@@ -113,8 +189,7 @@ exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
warn_once("Warning: Invalid udev rule: %s\n", path);
state->exists = 0;
} else {
udev_file_get_settings(file, st->dev_attribs, state->settings);
state->exists = 1;
udev_file_get_settings(file, st->dev_attribs, dev, autoconf);
}
util_udev_free_file(file);
@@ -141,8 +216,32 @@ static char *get_label_id(const char *prefix, const char *type,
return id;
}
/* Write the persistent configuration of a CCW device to a udev rule. */
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
static void write_attr_to_file(FILE *fd, struct device_state *state, const char *id)
{
struct ptrlist_node *p;
struct setting *s;
struct util_list *list = NULL;
/* Apply attributes in correct order. */
list = setting_list_get_sorted(state->settings);
util_list_iterate(list, p) {
s = p->ptr;
if (s->removed)
continue;
if ((s->attrib && s->attrib->internal) ||
internal_by_name(s->name)) {
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
internal_get_name(s->name), s->value);
} else {
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
s->value);
}
}
ptrlist_free(list, 0);
}
static exit_code_t udev_ccw_write_device_legacy(struct device *dev, bool autoconf)
{
struct subtype *st = dev->subtype;
struct ccw_subtype_data *data = st->data;
@@ -150,22 +249,16 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
struct device_state *state = autoconf ? &dev->autoconf :
&dev->persistent;
char *path, *cfg_label = NULL, *end_label = NULL;
struct util_list *list;
struct ptrlist_node *p;
struct setting *s;
exit_code_t rc = EXIT_OK;
FILE *fd;
if (!state->exists)
return udev_remove_rule(type, id, autoconf);
return udev_remove_rule(type, id, true);
cfg_label = get_label_id("cfg", type, id);
end_label = get_label_id("end", type, id);
/* Apply attributes in correct order. */
list = setting_list_get_sorted(state->settings);
path = path_get_udev_rule(type, id, autoconf);
path = path_get_udev_rule(type, id, true);
debug("Writing %s udev rule file %s\n", type, path);
if (!util_path_exists(path)) {
rc = path_create(path);
@@ -196,22 +289,8 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
}
fprintf(fd, "GOTO=\"%s\"\n", end_label);
fprintf(fd, "\n");
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
/* Write settings. */
util_list_iterate(list, p) {
s = p->ptr;
if (s->removed)
continue;
if ((s->attrib && s->attrib->internal) ||
internal_by_name(s->name)) {
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
internal_get_name(s->name), s->value);
} else {
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
s->value);
}
}
write_attr_to_file(fd, state, id);
/* Write udev rule epilog. */
fprintf(fd, "\n");
@@ -221,7 +300,6 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
warn("Could not close file %s: %s\n", path, strerror(errno));
out:
ptrlist_free(list, 0);
free(end_label);
free(cfg_label);
free(path);
@@ -229,6 +307,150 @@ out:
return rc;
}
/* Write the persistent configuration of a CCW device to a udev rule. */
static exit_code_t udev_ccw_write_device_new(struct device *dev)
{
struct subtype *st = dev->subtype;
struct ccw_subtype_data *data = st->data;
const char *type = st->name, *drv = data->any_driver ? "*" : data->ccwdrv, *id = dev->id;
char *path, *cfg_label = NULL, *end_label = NULL;
exit_code_t rc = EXIT_OK;
FILE *fd;
int i, configured = 0;
/*
* Remove the previous file; The new udev rule will be created based on the
* current configuration settings
*/
udev_remove_rule(type, id, 0);
/*
* Copy the dev->persistent to the site_specific array; While writing the new
* udev-rule we consider the site_specific array only
*/
dev_state_copy(&dev->site_specific[global_site_id], &dev->persistent);
for (i = 0; i < NUM_SITES; i++) {
configured += dev->site_specific[i].exists;
configured -= dev->site_specific[i].deconfigured;
}
/*
* If there is no configuration settings available, do not create the new
* udev-rule file; exit here
*/
if (!configured)
return rc;
cfg_label = get_label_id("cfg", type, id);
end_label = get_label_id("end", type, id);
path = path_get_udev_rule(type, id, false);
debug("Writing %s udev rule file %s\n", type, path);
if (!util_path_exists(path)) {
rc = path_create(path);
if (rc)
goto out;
}
fd = misc_fopen(path, "w");
if (!fd) {
error("Could not write to file %s: %s\n", path,
strerror(errno));
rc = EXIT_RUNTIME_ERROR;
goto out;
}
/* Write udev rule prolog. */
fprintf(fd, "# Generated by chzdev\n");
if (drv) {
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", id,
drv, cfg_label);
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
"KERNEL==\"%s\", TEST==\"[ccw/%s]\", "
"GOTO=\"%s\"\n", drv, id, cfg_label);
} else {
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
"KERNEL==\"%s\", GOTO=\"%s\"\n", id, cfg_label);
}
fprintf(fd, "GOTO=\"%s\"\n", end_label);
fprintf(fd, "\n");
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
/* site comparison block */
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].exists &&
!dev->site_specific[i].deconfigured) {
fprintf(fd, "ENV{ZDEV_SITE_ID}==\"%d\",GOTO=\"%s_site%d\"\n", i,
cfg_label, i);
}
}
/*
* If we have a generic configuration available, then use that setting as a
* fail-over incase of no site-id information in LOADPARM
*/
if (dev->site_specific[SITE_FALLBACK].exists &&
!dev->site_specific[SITE_FALLBACK].deconfigured)
fprintf(fd, "GOTO=\"%s_site_fb\"\n", cfg_label);
else
fprintf(fd, "GOTO=\"%s\"\n", end_label);
fprintf(fd, "\n");
/* Write the site blocks for all the available configurations */
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].exists &&
!dev->site_specific[i].deconfigured) {
fprintf(fd, "# site_start_%d\n", i);
fprintf(fd, "LABEL=\"%s_site%d\"\n", cfg_label, i);
write_attr_to_file(fd, &dev->site_specific[i], id);
/* Write udev rule epilog. */
fprintf(fd, "GOTO=\"%s\"\n", end_label);
fprintf(fd, "# site_end_%d\n", i);
fprintf(fd, "\n");
}
}
if (dev->site_specific[SITE_FALLBACK].exists &&
!dev->site_specific[SITE_FALLBACK].deconfigured) {
fprintf(fd, "# site_start_fb\n");
fprintf(fd, "LABEL=\"%s_site_fb\"\n", cfg_label);
write_attr_to_file(fd, &dev->site_specific[i], id);
/* Write udev rule epilog. */
fprintf(fd, "GOTO=\"%s\"\n", end_label);
fprintf(fd, "# site_end_fb\n");
fprintf(fd, "\n");
}
/* Write udev rule epilog. */
fprintf(fd, "\n");
fprintf(fd, "LABEL=\"%s\"\n", end_label);
if (misc_fclose(fd))
warn("Could not close file %s: %s\n", path, strerror(errno));
rc = udev_write_site_rule();
if (rc)
goto out;
out:
free(end_label);
free(cfg_label);
free(path);
return rc;
}
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
{
if (autoconf)
return udev_ccw_write_device_legacy(dev, autoconf);
else
return udev_ccw_write_device_new(dev);
}
#define MARKER "echo free "
static char *read_cio_ignore(const char *path)
@@ -300,6 +522,7 @@ exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf)
/* Write udev rule. */
fprintf(fd, "# Generated by chzdev\n");
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"subsystem\", "
"KERNEL==\"ccw\", RUN{program}+=\"/bin/sh -c "
"'echo free %s > /proc/cio_ignore'\"\n", id_list);

View File

@@ -11,9 +11,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "zdev_id.h"
#define SYSINFO "/proc/sysinfo"
#define CMDLINE "/proc/cmdline"
#define LOADPARM "/sys/firmware/ipl/loadparm"
#define IPL_DEV_ID "/sys/firmware/ipl/device"
#define IPL_DEV_TYPE "/sys/firmware/ipl/ipl_type"
#define SITE_FALLBACK 10
#define WHITESPACE " \t\n"
static void array_add(char ***array_p, int *num_p, const char *str)
@@ -140,15 +148,139 @@ out:
printf("ZDEV_NO_AUTO=%d\n", no_auto);
}
/*
* If the erraneous loadparm provides a site_id which is not valid,
* default it to the common-site id.
*/
static int validate_site(int site_id)
{
if (site_id < SITE_FALLBACK)
return site_id;
return SITE_FALLBACK;
}
/* get the ipl device and extract the SSID */
static int get_site_id(void)
{
FILE *fd;
char *line;
size_t n;
int site_id = SITE_FALLBACK, ssid;
fd = fopen(IPL_DEV_ID, "r");
if (!fd)
goto fail_safe;
if (getline(&line, &n, fd) == -1)
goto out;
if (sscanf(line, "%*d.%d.%*d", &ssid) == 1)
site_id = validate_site(ssid);
out:
free(line);
fclose(fd);
fail_safe:
return site_id;
}
/*
* Find the ipl type and see if it belongs to ccw or zfcp, if different,
* default it to common-site.
*/
static int read_ssid(void)
{
FILE *fd;
char *line = NULL;
size_t n;
int site_id = SITE_FALLBACK;
fd = fopen(IPL_DEV_TYPE, "r");
if (!fd)
goto out;
if (getline(&line, &n, fd) != -1) {
if (strcmp(line, "ccw") == 0 || strcmp(line, "zfcp"))
site_id = get_site_id();
}
free(line);
fclose(fd);
out:
return site_id;
}
static void write_zdev_site_id(int site_id)
{
FILE *fd;
fd = fopen(ZDEV_SITE_ID_FILE, "w");
if (!fd)
err(1, "Could not write to zdev_site_id file");
if (site_id == SITE_FALLBACK)
fprintf(fd, "ZDEV_SITE_ID=\n");
else
fprintf(fd, "ZDEV_SITE_ID=%d\n", site_id);
fclose(fd);
}
/* Read the loadparm and extract the current site_id.
* loadparm can contains either Sn or "Ss". Sn indicate the site_id, where
* 'n' is the integer which could be one of the valid site_ids from 0 to 9.
* When loadparm value is "Ss", zdev_id extracts the site_id from the SSID
* of the current ipl device. For ccw and zfcp devices, the current ipl
* device-id can be found at /sys/firmware/ipl/device.
* For all other invalid cases, set ZDEV_SITE_ID to NULL.
*/
static void process_loadparm(const char *filename)
{
size_t n;
FILE *fd;
char *line = NULL, *substr;
int site_id = SITE_FALLBACK;
fd = fopen(filename, "r");
if (!fd)
goto out;
/*
* We expect the value here to be either SX, where X is the site-id
* or SS,in which case, the site-id will be derived from the SSID
*/
if ((getline(&line, &n, fd)) != -1) {
substr = strchr(line, 'S');
if (!substr)
goto out;
if (isdigit(substr[1]))
site_id = validate_site(atoi(&substr[1]));
else if (substr[1] == 'S')
site_id = read_ssid();
}
free(line);
fclose(fd);
out:
write_zdev_site_id(site_id);
if (site_id == SITE_FALLBACK)
printf("ZDEV_SITE_ID=\n");
else
printf("ZDEV_SITE_ID=%d\n", site_id);
}
int main(int argc, char *argv[])
{
char *sysinfo, *cmdline;
char *sysinfo, *cmdline, *loadparm;
sysinfo = argc < 2 ? SYSINFO : argv[1];
cmdline = argc < 3 ? CMDLINE : argv[2];
loadparm = argc < 4 ? LOADPARM : argv[3];
process_sysinfo(sysinfo);
process_cmdline(cmdline);
process_loadparm(loadparm);
return 0;
}

View File

@@ -20,31 +20,52 @@ check_dep_fuse:
"-DFUSE_USE_VERSION=30")
endif
#
# HAVE_ZLIB: Allow skip zgetdump build, when no zlib-devel is available
#
ifeq (${HAVE_ZLIB},0)
.detect_openssl.dep.c:
echo "#include <openssl/evp.h>" > $@
echo "#if OPENSSL_VERSION_NUMBER < 0x10100000L" >> $@
echo " #error At least OpenSSL version 1.1.0 is required" >> $@
echo "#endif" >> $@
echo "static void __attribute__((unused)) test(void) {" >> $@
echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@
echo " EVP_MD_CTX_free(ctx);" >> $@
echo "}" >> $@
all:
$(SKIP) HAVE_ZLIB=0
install:
$(SKIP) HAVE_ZLIB=0
else
check_dep_zlib:
.check_dep_zgetdump: .detect_openssl.dep.c check_dep_fuse
$(call check_dep, \
"zgetdump", \
"zlib.h", \
"zlib-devel or libz-dev", \
"HAVE_ZLIB=0")
"zgetdump", \
"zlib.h", \
"zlib-devel or libz-dev", \
"HAVE_ZLIB=0")
$(call check_dep, \
"zgetdump", \
"glib.h", \
"glib2-devel / libglib2.0-dev", \
"HAVE_GLIB2=0")
$(call check_dep, \
"zgetdump", \
$<, \
"openssl-devel / libssl-dev version >= 1.1.0", \
"HAVE_OPENSSL=0", \
"-I.")
touch $@
all: check_dep_fuse check_dep_zlib zgetdump
#
# Allow skip of zgetdump build, when no zlib-devel, openssl-devel, glib2-devel is available
#
BUILD_TARGETS :=skip-zgetdump
INSTALL_TARGETS := skip-zgetdump
ifneq (${HAVE_ZLIB},0)
ifneq (${HAVE_OPENSSL},0)
ifneq (${HAVE_GLIB2},0)
BUILD_TARGETS := zgetdump
INSTALL_TARGETS := install-zgetdump
endif
endif
endif
OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
dfi.o dfi_mem_chunk.o dfi_vmcoreinfo.o \
dfi_lkcd.o dfi_elf.o \
dfi_lkcd.o dfi_elf.o dfi_elf_common.o dfi_pv_elf.o \
dfi_s390.o dfi_s390_ext.o\
dfi_s390mv.o dfi_s390mv_ext.o \
dfi_s390tape.o dfi_kdump.o \
@@ -54,7 +75,27 @@ OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
dt.o dt_s390sv.o dt_s390sv_ext.o \
dt_s390mv.o dt_s390mv_ext.o \
dt_scsi.o output.o \
ngdump.o dt_ngdump.o dfi_ngdump.o
ngdump.o dt_ngdump.o dfi_ngdump.o \
pv_utils.o
ifneq ($(shell sh -c 'command -v pkg-config'),)
GLIB2_CFLAGS := $(shell pkg-config --silence-errors --cflags glib-2.0)
GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0)
LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto)
LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto)
else
GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
GLIB2_LIBS := -lglib-2.0
LIBCRYPTO_CFLAGS :=
LIBCRYPTO_LIBS := -lcrypto
endif
LIBPV = $(rootdir)/libpv/libpv.a
LIBPV_CFLAGS := -DOPENSSL_API_COMPAT=0x10100000L \
$(GLIB2_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(NULL)
LIBPV_LIBS := $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
ifeq ("$(HAVE_FUSE)","0")
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
@@ -66,23 +107,30 @@ else
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
FUSE_LDLIBS = -lfuse3
endif
LDLIBS += -lz $(FUSE_LDLIBS)
ALL_CFLAGS += $(FUSE_CFLAGS)
LDLIBS += -lz $(FUSE_LDLIBS) $(LIBPV_LIBS)
ALL_CFLAGS += $(FUSE_CFLAGS) $(LIBPV_CFLAGS)
ifneq ("$(HAVE_FUSE)","0")
OBJECTS += zfuse.o
endif
libs = $(rootdir)/libutil/libutil.a
zgetdump: $(OBJECTS) $(libs)
libs = $(rootdir)/libutil/libutil.a $(LIBPV)
install: all
all: $(BUILD_TARGETS)
zgetdump: .check_dep_zgetdump $(OBJECTS) $(libs)
skip-zgetdump:
echo " SKIP zgetdump due to unresolved dependencies"
install-zgetdump: zgetdump
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 zgetdump $(DESTDIR)$(BINDIR)
$(INSTALL) -m 644 zgetdump.8 $(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o *~ zgetdump core.*
endif
install: $(INSTALL_TARGETS)
.PHONY: all install clean check_dep_fuse check_dep_zlib
clean:
rm -f -- *.o *~ zgetdump core.* .detect_openssl.dep.c .check_dep_zgetdump
.PHONY: all install clean check_dep_fuse check_dep_zlib skip-zgetdump install-zgetdump

View File

@@ -7,27 +7,23 @@
#include <string.h>
#include "lib/util_libc.h"
#include "lib/util_log.h"
#include "df_elf.h"
#define NOTE_NAME_CORE "CORE"
#define NOTE_NAME_LINUX "LINUX"
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
/*
* Initialize ELF header
*/
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
{
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
ehdr->e_ident[EI_VERSION] = ELF_VERSION_1;
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
ehdr->e_ident[EI_ABIVERSION] = 0;
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
ehdr->e_type = ET_CORE;
ehdr->e_machine = EM_S390;
ehdr->e_version = EV_CURRENT;
ehdr->e_version = ELF_VERSION_1;
ehdr->e_entry = 0;
ehdr->e_phoff = sizeof(Elf64_Ehdr);
ehdr->e_shoff = 0;
@@ -41,9 +37,188 @@ void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
return ehdr + 1;
}
/*
* Initialize ELF note
*/
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr)
{
return (ehdr->e_version == ELF_VERSION_1) && (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0);
}
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr)
{
return ehdr->e_type == ET_CORE;
}
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr)
{
return ehdr->e_machine == EM_S390 && ehdr->e_ident[EI_DATA] == ELFDATA2MSB &&
ehdr->e_ident[EI_CLASS] == ELFCLASS64;
}
int ehdr_check_s390x(const Elf64_Ehdr *ehdr)
{
if (!ehdr_is_elf_object(ehdr) || !ehdr_is_vmcore(ehdr))
return -ENODEV;
if (!ehdr_is_s390x(ehdr))
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
return 0;
}
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh)
{
Elf64_Ehdr *ehdr;
const size_t ehdr_size = sizeof(*ehdr);
if (zg_size(fh) < ehdr_size)
return NULL;
ehdr = util_malloc(ehdr_size);
zg_read(fh, ehdr, ehdr_size, ZG_CHECK);
return ehdr;
}
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *phdr_count)
{
const Elf64_Half phnum = ehdr->e_phnum;
size_t phdrs_size;
Elf64_Phdr *phdrs;
if (!phnum) {
*phdr_count = phnum;
return NULL;
}
if (ehdr->e_phoff > OFF_T_MAX)
ERR_EXIT("Unsupported offset");
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
phdrs_size = sizeof(*phdrs) * phnum;
phdrs = util_malloc(phdrs_size);
zg_seek(fh, (off_t)ehdr->e_phoff, ZG_CHECK);
zg_read(fh, phdrs, phdrs_size, ZG_CHECK);
*phdr_count = phnum;
return phdrs;
}
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *shdr_count)
{
const Elf64_Half shnum = ehdr->e_shnum;
size_t shdrs_size;
Elf64_Shdr *shdrs;
if (!shnum) {
*shdr_count = shnum;
return NULL;
}
if (ehdr->e_shoff > OFF_T_MAX)
ERR_EXIT("Unsupported offset");
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
shdrs_size = sizeof(*shdrs) * shnum;
shdrs = util_malloc(shdrs_size);
zg_seek(fh, (off_t)ehdr->e_shoff, ZG_CHECK);
zg_read(fh, shdrs, shdrs_size, ZG_CHECK);
*shdr_count = shnum;
return shdrs;
}
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size)
{
const size_t sh_size = shdr->sh_size;
unsigned char *ret;
if (!sh_size) {
*size = sh_size;
return NULL;
}
if (shdr->sh_offset > OFF_T_MAX)
ERR_EXIT("Unsupported offset");
ret = util_malloc(sh_size);
zg_seek(fh, (off_t)shdr->sh_offset, ZG_CHECK);
zg_read(fh, ret, sh_size, ZG_CHECK);
*size = sh_size;
return ret;
}
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
const unsigned int shnum, size_t *shstrtab_size)
{
const size_t shstrndx = ehdr->e_shstrndx;
Elf64_Xword tmp_shstrtab_size;
Elf64_Off shstrndx_off;
char *shstrtab;
/* file has no section name string table */
if (shstrndx == SHN_UNDEF) {
*shstrtab_size = 0;
return NULL;
}
/* Invalid ELF file */
if (shstrndx >= shnum) {
util_log_print(UTIL_LOG_ERROR, "Invalid ELF file: invalid string table index\n");
*shstrtab_size = 0;
return NULL;
}
shstrndx_off = shdrs[shstrndx].sh_offset;
if (shstrndx_off > OFF_T_MAX)
ERR_EXIT("Unsupported offset");
tmp_shstrtab_size = shdrs[shstrndx].sh_size;
shstrtab = util_malloc(tmp_shstrtab_size);
zg_seek(fh, (off_t)shstrndx_off, ZG_CHECK);
zg_read(fh, shstrtab, tmp_shstrtab_size, ZG_CHECK);
*shstrtab_size = tmp_shstrtab_size;
return shstrtab;
}
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
const char *const shstrtab, const size_t shstrtab_size,
const char *name)
{
const Elf64_Shdr *shdr = shdrs;
unsigned int idx;
for (idx = 0; idx < shnum; idx++, shdr++) {
/* Invalid ELF file */
if (shdr->sh_name >= shstrtab_size) {
util_log_print(UTIL_LOG_ERROR,
"Invalid ELF file: section name is too large\n");
return NULL;
}
if (strcmp(name, &shstrtab[shdr->sh_name]) == 0)
return shdr;
}
/* Not found */
return NULL;
}
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len)
{
ssize_t nread;
/* We cannot read more than the current note provides */
if (note->n_descsz < buf_len)
return -EINVAL;
/* Skip note's name and position file at note's descriptor */
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz), ZG_CHECK);
/* Read note's descriptor */
nread = zg_read(fh, buf, buf_len, ZG_CHECK_ERR);
if (nread < 0 || (size_t)nread != buf_len)
return -EINVAL;
/* Skip the rest of note's descriptor until the next note */
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_descsz) - buf_len, ZG_CHECK);
return 0;
}
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note)
{
/* Skip note's name + descriptor and position file at the next note */
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz) + ELF_NOTE_ROUNDUP(note->n_descsz),
ZG_CHECK);
}
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
const char *name)
{
@@ -57,17 +232,14 @@ void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
len = sizeof(Elf64_Nhdr);
memcpy(buf + len, name, note->n_namesz);
len = ROUNDUP(len + note->n_namesz, 4);
len = ELF_NOTE_ROUNDUP(len + note->n_namesz);
memcpy(buf + len, desc, note->n_descsz);
len = ROUNDUP(len + note->n_descsz, 4);
len = ELF_NOTE_ROUNDUP(len + note->n_descsz);
return PTR_ADD(buf, len);
}
/*
* Initialize prstatus note
*/
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
{
struct nt_prstatus_64 nt_prstatus;
@@ -84,9 +256,6 @@ void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
NOTE_NAME_CORE);
}
/*
* Initialize fpregset (floating point) note
*/
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
{
struct nt_fpregset_64 nt_fpregset;
@@ -99,72 +268,53 @@ void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
NOTE_NAME_CORE);
}
/*
* Initialize timer note
*/
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
NOTE_NAME_LINUX);
}
/*
* Initialize TOD clock comparator note
*/
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
}
/*
* Initialize TOD programmable register note
*/
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
}
/*
* Initialize control register note
*/
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
NOTE_NAME_LINUX);
}
/*
* Initialize prefix register note
*/
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
sizeof(cpu->prefix), NOTE_NAME_LINUX);
}
/*
* Initialize vxrs_low register note
*/
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
}
/*
* Initialize vxrs_high register note
*/
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
}
/*
* Initialize prpsinfo note
*/
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu)
{
return nt_init(ptr, NT_S390_GS_CB, &cpu->gscb, sizeof(cpu->gscb), NOTE_NAME_LINUX);
}
void *nt_prpsinfo(void *ptr)
{
struct nt_prpsinfo_64 prpsinfo;
@@ -179,9 +329,6 @@ void *nt_prpsinfo(void *ptr)
NOTE_NAME_CORE);
}
/*
* Initialize vmcoreinfo note
*/
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
{
if (!vmcoreinfo)
@@ -189,3 +336,22 @@ void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
NOTE_NAME_VMCOREINFO);
}
/* Keep in sync with `struct dfi_cpu` */
size_t get_max_note_size_per_cpu(void)
{
size_t size = 0;
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_prstatus_64));
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_fpregset_64));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, timer));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todcmp));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todpreg));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, ctrs));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, prefix));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_low));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_high));
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, gscb));
return size;
}

View File

@@ -14,12 +14,17 @@
#include <elf.h>
#include <linux/types.h>
#include <stdbool.h>
#include "lib/zt_common.h"
#include "zg.h"
#include "dfi.h"
#ifndef ELF_VERSION_1
#define ELF_VERSION_1 0x1
#endif
/*
* S390 CPU timer note (u64)
*/
@@ -69,6 +74,29 @@
#define NT_S390_VXRS_HIGH 0x30a
#endif
/*
* S390 guarded-storage registers note (4 * u64)
*/
#ifndef NT_S390_GS_CB
#define NT_S390_GS_CB 0x30b
#endif
/*
* S390 confidential CPU data from secure guest.
*/
#ifndef NT_S390_PV_CPU_DATA
#define NT_S390_PV_CPU_DATA 0x30e
#endif
#define NOTE_NAME_CORE "CORE"
#define NOTE_NAME_LINUX "LINUX"
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
#define ELF_NOTE_ROUNDUP(size) ROUNDUP(size, 4)
#define ELF_NOTE_NAME_SIZE(name) ELF_NOTE_ROUNDUP(name ? strlen(name) + 1 : 0)
#define ELF64_NOTE_SIZE(name, desc_size) \
(sizeof(Elf64_Nhdr) + ELF_NOTE_NAME_SIZE(name) + ELF_NOTE_ROUNDUP(desc_size))
/*
* prstatus ELF Note
*/
@@ -116,20 +144,150 @@ static inline void df_elf_ensure_s390x(void)
#endif
}
/*
* Initialize ELF header
*/
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum);
/*
* Checks whether @ehdr is a ELF object
*/
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr);
/*
* Checks whether @ehdr is a vmcore
*/
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr);
/*
* Checks whether @ehdr is a s390x header
*/
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr);
/*
* Verify that given header is a s390x ELF header
*/
int ehdr_check_s390x(const Elf64_Ehdr *ehdr);
/*
* Read ELF header at current offset
*/
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh);
/*
* Read ELF program headers
*
* To read the program headers the offset of @fh is changed.
*/
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
unsigned int *phdr_count);
/*
* Read ELF section headers
*
* To read the section headers the offset of @fh is changed.
*/
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
unsigned int *shdr_count);
/*
* Read ELF section content
*
* To read the section content the offset of @fh is changed.
*/
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size);
/*
* Read ELF section header string table
*
* To read the section header string table the offset of @fh is changed.
*/
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
const unsigned int shnum, size_t *shstrtab_size);
/*
* Find ELF section header by section name
*/
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
const char *shstrtab, const size_t shstrtab_size,
const char *name);
/*
* Read note and store the note description in @buf
*/
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len);
/*
* Skip note
*/
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note);
/*
* Initialize ELF note
*/
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
const char *name);
/*
* Initialize prstatus note
*/
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize fpregset (floating point) note
*/
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize timer note
*/
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize TOD clock comparator note
*/
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize TOD programmable register note
*/
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize control register note
*/
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize prefix register note
*/
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize vxrs_low register note
*/
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize vxrs_high register note
*/
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize guarded-storage registers note
*/
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu);
/*
* Initialize prpsinfo note
*/
void *nt_prpsinfo(void *ptr);
/*
* Initialize vmcoreinfo note
*/
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo);
size_t get_max_note_size_per_cpu(void);
#endif /* DF_ELF_H */

View File

@@ -26,6 +26,7 @@
* DFI vector - ensure that tape is the first in the list and devmem the second!
*/
static struct dfi *dfi_vec[] = {
/* clang-format off */
&dfi_s390tape,
&dfi_devmem,
&dfi_s390mv_ext,
@@ -33,11 +34,13 @@ static struct dfi *dfi_vec[] = {
&dfi_s390_ext,
&dfi_s390,
&dfi_lkcd,
&dfi_pv_elf,
&dfi_elf,
&dfi_kdump,
&dfi_kdump_flat,
&dfi_ngdump,
NULL,
/* clang-format on */
};
/*
@@ -168,6 +171,11 @@ struct dfi_cpu *dfi_cpu_alloc(void)
return zg_alloc(sizeof(struct dfi_cpu));
}
void dfi_cpu_free(struct dfi_cpu *cpu)
{
zg_free(cpu);
}
/*
* Add DFI CPU
*/
@@ -780,7 +788,7 @@ int dfi_init(void)
}
util_log_print(UTIL_LOG_DEBUG, "DFI %s returned with rc %d\n",
dfi->name, rc);
if (rc == 0 || rc == -EINVAL)
if (rc == 0 || rc == -EINVAL || rc == -ENOKEY)
return rc;
zg_close(g.fh);
i++;

View File

@@ -99,6 +99,15 @@ struct dfi_cpu {
u32 todpreg;
u64 vxrs_low[16];
struct dfi_vxrs vxrs_high[16];
union {
u64 gscb[4];
struct {
u64 reserved;
u64 gsd;
u64 gssm;
u64 gs_epl_a;
};
};
u16 cpu_id;
};
@@ -127,7 +136,8 @@ enum dfi_cpu_content {
DFI_CPU_CONTENT_ALL, /* Complete register information available */
};
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
#define DFI_CPU_CONTENT_FAC_GS 0x00000002
int dfi_cpu_content_fac_check(int flags);
void dfi_cpu_content_fac_add(int flags);
@@ -137,6 +147,7 @@ void dfi_cpu_content_fac_add(int flags);
struct util_list *dfi_cpu_list(void);
void dfi_cpu_info_init(enum dfi_cpu_content content);
struct dfi_cpu *dfi_cpu_alloc(void);
void dfi_cpu_free(struct dfi_cpu *cpu);
struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
void dfi_cpu_add(struct dfi_cpu *cpu);
unsigned int dfi_cpu_cnt(void);
@@ -238,6 +249,7 @@ extern struct dfi dfi_s390mv_ext;
extern struct dfi dfi_s390;
extern struct dfi dfi_s390_ext;
extern struct dfi dfi_lkcd;
extern struct dfi dfi_pv_elf;
extern struct dfi dfi_elf;
extern struct dfi dfi_kdump;
extern struct dfi dfi_kdump_flat;

View File

@@ -24,6 +24,7 @@
#include "df_elf.h"
#include "dfi.h"
#include "dfi_mem_chunk.h"
#include "dfi_elf_common.h"
/*
* Read memory for given memory chunk
@@ -37,77 +38,6 @@ static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
zg_read(g.fh, buf, cnt, ZG_CHECK);
}
/*
* Add load (memory chunk) to DFI dump
*/
static int pt_load_add(Elf64_Phdr *phdr)
{
u64 *off_ptr;
util_log_print(UTIL_LOG_DEBUG,
"DFI ELF p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
phdr->p_paddr, phdr->p_vaddr, phdr->p_offset,
phdr->p_filesz, phdr->p_memsz);
if (phdr->p_paddr != phdr->p_vaddr) {
phdr->p_paddr = phdr->p_vaddr;
STDERR("Dump file \"%s\" is a user space core dump\n",
g.opts.device);
return -EINVAL;
}
if (phdr->p_memsz == 0)
return -EINVAL;
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
return -EINVAL;
if (phdr->p_filesz > phdr->p_memsz)
return -EINVAL;
if (phdr->p_filesz > 0) {
off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = phdr->p_offset;
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, off_ptr,
dfi_elf_mem_chunk_read_fn, zg_free);
}
if (phdr->p_memsz - phdr->p_filesz > 0) {
/* Add zero memory chunk */
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz,
phdr->p_memsz - phdr->p_filesz, NULL,
dfi_mem_chunk_read_zero, NULL);
}
return 0;
}
/*
* Read note
*/
static int nt_read(const Elf64_Nhdr *note, void *buf, size_t buf_len)
{
ssize_t nread;
/* We cannot read more than the current note provides */
if (note->n_descsz < buf_len)
return -EINVAL;
/* Skip note's name and position file at note's descriptor */
zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
/* Read note's descriptor */
nread = zg_read(g.fh, buf, buf_len, ZG_CHECK_ERR);
if (nread < 0 || (size_t)nread != buf_len)
return -EINVAL;
/* Skip the rest of note's descriptor until the next note */
zg_seek_cur(g.fh, ROUNDUP(note->n_descsz, 4) - buf_len, ZG_CHECK);
return 0;
}
/*
* Skip note
*/
static void nt_skip(const Elf64_Nhdr *note)
{
/* Skip note's name + descriptor and position file at the next note */
zg_seek_cur(g.fh,
ROUNDUP(note->n_namesz, 4) + ROUNDUP(note->n_descsz, 4),
ZG_CHECK);
}
/*
* Ensure that CPU is already defined by prstatus note
*/
@@ -121,12 +51,12 @@ static void check_cpu(struct dfi_cpu *cpu, const char *note_str)
/*
* Read prstatus note and return new DFI CPU
*/
static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
static struct dfi_cpu *nt_prstatus_read(const struct zg_fh *fh, const Elf64_Nhdr *note)
{
struct dfi_cpu *cpu = dfi_cpu_alloc();
struct nt_prstatus_64 nt_prstatus;
if (nt_read(note, &nt_prstatus, sizeof(nt_prstatus)))
if (nt_read(fh, note, &nt_prstatus, sizeof(nt_prstatus)))
return NULL;
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
@@ -140,12 +70,12 @@ static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
/*
* Read fpregset note
*/
static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_fpregset_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
struct nt_fpregset_64 nt_fpregset;
check_cpu(cpu, "FPREGSET");
if (nt_read(note, &nt_fpregset, sizeof(nt_fpregset)))
if (nt_read(fh, note, &nt_fpregset, sizeof(nt_fpregset)))
return -EINVAL;
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
@@ -156,195 +86,208 @@ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
/*
* Read s390 timer note
*/
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_timer_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TIMER");
return nt_read(note, &cpu->timer, sizeof(cpu->timer));
return nt_read(fh, note, &cpu->timer, sizeof(cpu->timer));
}
/*
* Read s390 todcmp note
*/
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_todcmp_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TODCMP");
return nt_read(note, &cpu->todcmp, sizeof(cpu->todcmp));
return nt_read(fh, note, &cpu->todcmp, sizeof(cpu->todcmp));
}
/*
* Read s390 todpreg note
*/
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_todpreg_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TODPREG");
return nt_read(note, &cpu->todpreg, sizeof(cpu->todpreg));
return nt_read(fh, note, &cpu->todpreg, sizeof(cpu->todpreg));
}
/*
* Read s390 ctrs note
*/
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_ctrs_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_CTRS");
return nt_read(note, &cpu->ctrs, sizeof(cpu->ctrs));
return nt_read(fh, note, &cpu->ctrs, sizeof(cpu->ctrs));
}
/*
* Read s390 prefix note
*/
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_prefix_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_PREFIX");
return nt_read(note, &cpu->prefix, sizeof(cpu->prefix));
return nt_read(fh, note, &cpu->prefix, sizeof(cpu->prefix));
}
/*
* Read s390 vxrs_low note
*/
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_vxrs_low_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_VXRS_LOW");
return nt_read(note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
return nt_read(fh, note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
}
/*
* Read s390 vxrs_high note
*/
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_vxrs_high_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_VXRS_HIGH");
return nt_read(note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
return nt_read(fh, note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
}
/*
* Read s390 gs_cb note
*/
static int nt_s390_gs_cb_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_GSCB");
return nt_read(fh, note, &cpu->gscb, sizeof(cpu->gscb));
}
/*
* Add all notes for notes phdr
*/
static int pt_notes_add(Elf64_Phdr *phdr)
static int pt_notes_add(const Elf64_Phdr *phdr)
{
struct dfi_cpu *cpu_current = NULL;
u64 notes_start_off;
Elf64_Nhdr note;
const struct zg_fh *fh = g.fh;
int rc;
zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
notes_start_off = zg_tell(g.fh, ZG_CHECK);
while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
rc = zg_read(g.fh, &note, sizeof(note), ZG_CHECK_ERR);
zg_seek(fh, phdr->p_offset, ZG_CHECK);
while (zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
Elf64_Nhdr note;
rc = zg_read(fh, &note, sizeof(note), ZG_CHECK_ERR);
if (rc != sizeof(note))
return -EINVAL;
util_log_print(UTIL_LOG_DEBUG, "DFI ELF n_type 0x%x\n",
note.n_type);
switch (note.n_type) {
case NT_PRSTATUS:
cpu_current = nt_prstatus_read(&note);
cpu_current = nt_prstatus_read(fh, &note);
if (!cpu_current)
return -EINVAL;
break;
case NT_FPREGSET:
if (nt_fpregset_read(cpu_current, &note))
if (nt_fpregset_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TIMER:
if (nt_s390_timer_read(cpu_current, &note))
if (nt_s390_timer_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TODCMP:
if (nt_s390_todcmp_read(cpu_current, &note))
if (nt_s390_todcmp_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TODPREG:
if (nt_s390_todpreg_read(cpu_current, &note))
if (nt_s390_todpreg_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_CTRS:
if (nt_s390_ctrs_read(cpu_current, &note))
if (nt_s390_ctrs_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_PREFIX:
if (nt_s390_prefix_read(cpu_current, &note))
if (nt_s390_prefix_read(fh, cpu_current, &note))
return -EINVAL;
break;
case NT_S390_VXRS_LOW:
if (nt_s390_vxrs_low_read(cpu_current, &note))
if (nt_s390_vxrs_low_read(fh, cpu_current, &note))
return -EINVAL;
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
break;
case NT_S390_VXRS_HIGH:
if (nt_s390_vxrs_high_read(cpu_current, &note))
if (nt_s390_vxrs_high_read(fh, cpu_current, &note))
return -EINVAL;
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
break;
case NT_S390_GS_CB:
if (nt_s390_gs_cb_read(fh, cpu_current, &note))
return -EINVAL;
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_GS);
break;
default:
nt_skip(&note);
nt_skip(fh, &note);
break;
}
}
return 0;
}
/*
* Read ELF header
*/
static int read_elf_hdr(Elf64_Ehdr *ehdr)
{
if (zg_size(g.fh) < sizeof(*ehdr))
return -ENODEV;
zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
return -ENODEV;
if (ehdr->e_type != ET_CORE)
return -ENODEV;
if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
return 0;
}
/*
* Initialize ELF input dump format
*/
static int dfi_elf_init(void)
{
Elf64_Ehdr ehdr;
Elf64_Phdr *phdr;
int i;
unsigned int phnum, i;
Elf64_Phdr *phdrs;
Elf64_Ehdr *ehdr;
int rc = -ENODEV;
util_log_print(UTIL_LOG_DEBUG, "DFI ELF initialization\n");
if (read_elf_hdr(&ehdr) != 0)
ehdr = read_elf_hdr(g.fh);
if (!ehdr)
return -ENODEV;
if (ehdr_check_s390x(ehdr) < 0)
goto free_ehdr;
df_elf_ensure_s390x();
dfi_arch_set(DFI_ARCH_64);
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
phdr = util_malloc(sizeof(*phdr) * ehdr.e_phnum);
zg_seek(g.fh, ehdr.e_phoff, ZG_CHECK);
zg_read(g.fh, phdr, sizeof(*phdr) * ehdr.e_phnum, ZG_CHECK);
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", ehdr.e_phnum);
for (i = 0; i < ehdr.e_phnum; i++) {
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n",
i, phdr[i].p_type);
switch (phdr[i].p_type) {
case PT_LOAD:
if (pt_load_add(&phdr[i])) {
free(phdr);
return -EINVAL;
phdrs = read_elf_phdrs(g.fh, ehdr, &phnum);
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", phnum);
for (i = 0; i < phnum; i++) {
const Elf64_Phdr *phdr = &phdrs[i];
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n", i, phdr->p_type);
switch (phdr->p_type) {
case PT_LOAD: {
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = phdr->p_offset;
if (pt_load_add(g.fh, phdr, (void **)&off_ptr, dfi_elf_mem_chunk_read_fn,
free) < 0) {
free(off_ptr);
rc = -EINVAL;
goto free_phdrs;
}
break;
} break;
case PT_NOTE:
if (pt_notes_add(&phdr[i])) {
free(phdr);
return -EINVAL;
if (pt_notes_add(phdr)) {
rc = -EINVAL;
goto free_phdrs;
}
break;
default:
break;
}
}
free(phdr);
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
return 0;
dfi_attr_version_set(ehdr->e_ident[EI_VERSION]);
rc = 0;
free_phdrs:
free(phdrs);
free_ehdr:
free(ehdr);
return rc;
}
/*

61
zdump/dfi_elf_common.c Normal file
View File

@@ -0,0 +1,61 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* Common ELF core dump input format definitions
*
* Copyright IBM Corp. 2001, 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "dfi_elf_common.h"
#include <assert.h>
#include "lib/util_log.h"
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn)
{
assert(fh);
assert(phdr);
assert(data);
assert(read_fn);
assert(free_fn);
util_log_print(
UTIL_LOG_DEBUG,
"%s p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
__func__, phdr->p_paddr, phdr->p_vaddr, phdr->p_offset, phdr->p_filesz,
phdr->p_memsz);
if (phdr->p_paddr != phdr->p_vaddr) {
STDERR("Dump file \"%s\" is a user space core dump\n", fh->path);
return -EINVAL;
}
if (phdr->p_memsz == 0)
return -EINVAL;
if (phdr->p_offset + phdr->p_filesz > zg_size(fh))
return -EINVAL;
if (phdr->p_filesz > phdr->p_memsz)
return -EINVAL;
/* check for wrap-around */
if (phdr->p_paddr + phdr->p_filesz < phdr->p_paddr)
return -EINVAL;
if (phdr->p_filesz > 0) {
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, *data, read_fn, free_fn);
*data = NULL;
} else {
/* Free @data directly as it's not used */
free_fn(*data);
*data = NULL;
}
if (phdr->p_memsz - phdr->p_filesz > 0) {
/* Add zero memory chunk */
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz, phdr->p_memsz - phdr->p_filesz,
NULL, dfi_mem_chunk_read_zero, NULL);
}
return 0;
}

36
zdump/dfi_elf_common.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* Common ELF core dump input format definitions
*
* Copyright IBM Corp. 2001, 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef DFI_ELF_COMMON_H
#define DFI_ELF_COMMON_H
#include <elf.h>
#include "dfi_mem_chunk.h"
#include "zg.h"
/**
* pt_load_add:
* @fh: (not nullable): open input file
* @phdr: (not nullable): program ELF header of the load segment to add
* @data: (not nullable): arbitrary pointer passed to the read callback @read_fn
* @read_fn: (not nullable): callback used for reading the data of a memory chunk
* @free_fn: (not nullable): function called to cleanup @data
*
* Add load (memory chunk) to DFI dump. After a successful call, @data belongs
* to the mem_chunk structure created and the @data pointer is set to %NULL.
*
* Returns: %0 on success, returns < 0 in case of an error
*/
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn);
#endif /* DFI_ELF_COMMON_H */

337
zdump/dfi_pv_elf.c Normal file
View File

@@ -0,0 +1,337 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* PV ELF core dump input format
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <assert.h>
#include <elf.h>
#include <error.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <elf.h>
#include <errno.h>
#include <glib.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include "lib/util_log.h"
#include "libpv/glib-helper.h"
#include "pv_defs.h"
#include "pv_utils.h"
#include "zgetdump.h"
#include "zg.h"
#include "df_elf.h"
#include "dfi.h"
#include "dfi_mem_chunk.h"
#include "dfi_elf_common.h"
#include "opts.h"
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Phdr, free);
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Shdr, free);
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Ehdr, free);
/**
* read_elf_section_data_as_gbytes:
* @fh: (not nullable): open input file
* @shdr: (not nullable): section header of the section to read
*
* Try to read section data and return it as #GBytes.
*
* Returns: #GBytes on success, %NULL if an error occurred
*/
static GBytes *read_elf_section_data_as_gbytes(const struct zg_fh *fh, const Elf64_Shdr *shdr)
{
unsigned char *data;
size_t size;
data = read_elf_section_data(fh, shdr, &size);
if (!data)
return NULL;
return g_bytes_new_with_free_func(data, size, free, data);
}
/**
* dfi_pv_elf_mem_chunk_read_fn:
* @chunk: (not nullable): memory chunk to read from
* @chunk_off: offset in the memory chunk to read from
* @dst: (not nullable): destination buffer
* @size: size in bytes to read
*
* Memory chunk callback that attempts to read and decrypt up to @size
* bytes encrypted memory from @chunk.
*/
static void dfi_pv_elf_mem_chunk_read_fn(struct dfi_mem_chunk *chunk, u64 chunk_off, void *dst,
u64 size)
{
const pv_elf_ctx_t *ctx = chunk->data;
g_autoptr(GError) error = NULL;
g_assert_nonnull(ctx);
if (pv_elf_read(ctx, chunk_off, dst, size, &error) < 0)
ERR_EXIT(_("Reading encrypted memory failed:" ERR_NEWLINE "%s"), error->message);
}
/**
* nt_s390_pv_cpu_data_read:
* @fh: (not nullable): open input ELF file
* @note_hdr: (not nullable): note header of the note to read
* @version: expected PV CPU dump version
* @dump_key: (not nullable): key to be used to decrypt data stored in the note
* @error: return location for a #GError
*
* Read and decrypt PV CPU data of the note using @dump_key.
*
* Returns: #dfi_cpu_t struct on success, %NULL if an error occurred.
*/
static dfi_cpu_t *nt_s390_pv_cpu_data_read(const struct zg_fh *fh, const Elf64_Nhdr *note_hdr,
const unsigned int version, GBytes *dump_key,
GError **error)
{
const size_t note_descsz = note_hdr->n_descsz;
g_autofree uint8_t *note_data = NULL;
g_autoptr(GBytes) note = NULL;
g_assert(dump_key);
note_data = g_malloc(note_descsz);
if (nt_read(fh, note_hdr, note_data, note_descsz) < 0) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
_("Unable to read confidential CPU data. Dump probably corrupted."));
return NULL;
}
note = g_bytes_new_take(g_steal_pointer(&note_data), note_descsz);
return pv_decrypt_cpu_note_data(version, note, dump_key, error);
}
/**
* pt_notes_add:
* @fh: (not nullable): open input file
* @phdr: (not nullable): program header of the note segment to add
* @version: expected PV CPU dump version
* @dump_key: (not nullable): key to be used to decrypt data stored in the notes
* @error: return location for a #GError
*
* Read all ELF notes for @phdr.
*
* Returns: %0 on success, -EINVAL if an error occurred.
*/
static int pt_notes_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, const unsigned int version,
GBytes *dump_key, GError **error)
{
if (phdr->p_offset > OFF_T_MAX) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
_("Confidential CPU data has too large offset. Dump probably corrupted."));
return -EINVAL;
}
zg_seek(fh, (off_t)phdr->p_offset, ZG_CHECK);
/* Cast to `size_t` is safe because we're using `ZG_CHECK` here. This
* means an error in `zg_tell` leads to an exit and therefore `zg_tell`
* cannot return an negative value.
*/
while ((size_t)zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
g_autoptr(dfi_cpu_t) cpu_current = NULL;
Elf64_Nhdr note;
ssize_t rc;
rc = zg_read(fh, &note, sizeof(note), ZG_CHECK_ERR);
if (rc != sizeof(note)) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
_("Confidential CPU data could not be read. Dump probably corrupted."));
return -EINVAL;
}
switch (note.n_type) {
case NT_S390_PV_CPU_DATA:
cpu_current = nt_s390_pv_cpu_data_read(fh, &note, version, dump_key, error);
if (!cpu_current)
return -EINVAL;
dfi_cpu_add(g_steal_pointer(&cpu_current));
break;
default:
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-Note %#x"), note.n_type);
__attribute__((fallthrough));
case NT_PRSTATUS:
case NT_FPREGSET:
case NT_S390_TIMER:
case NT_S390_TODCMP:
case NT_S390_TODPREG:
case NT_S390_CTRS:
case NT_S390_PREFIX:
case NT_S390_VXRS_LOW:
case NT_S390_VXRS_HIGH:
case NT_S390_GS_CB:
/* In case of PV ignore all these note types */
nt_skip(fh, &note);
break;
}
}
return 0;
}
/**
* dfi_pv_elf_init:
*
* Initialize Protected Virtualization ELF input dump format for @g.fh.
*
* Returns: %0 on success, -ENODEV in case it's not a PV ELF dump and
* -EINVAL in case of an error.
*/
static int dfi_pv_elf_init(void)
{
const Elf64_Shdr *completion_shdr = NULL, *storage_state_shdr = NULL;
g_autoptr(GBytes) key = NULL, dump_key = NULL, completion_sdata = NULL;
g_autoptr(storage_state_mmap_t) storage_state_data = NULL;
const pv_dump_completion_v1_t *cpl_conf_v1_decr = NULL;
g_autoptr(pv_dump_completion_t) cpl_conf_decr = NULL;
const char *key_path = g.opts.key_path;
g_autoptr(Elf64_Phdr) phdrs = NULL;
g_autoptr(Elf64_Shdr) shdrs = NULL;
g_autoptr(Elf64_Ehdr) ehdr = NULL;
g_autofree char *shstrtab = NULL;
g_autoptr(GError) error = NULL;
unsigned int cpu_dump_version;
const struct zg_fh *fh = g.fh;
unsigned int shnum, phnum;
bool load_found = false;
size_t shstrtab_size;
unsigned int i;
util_log_print(UTIL_LOG_DEBUG, _("DFI %s initialization\n"), dfi_pv_elf.name);
ehdr = read_elf_hdr(fh);
if (!ehdr)
return -ENODEV;
if (ehdr_check_s390x(ehdr) < 0)
return -ENODEV;
shdrs = read_elf_shdrs(fh, ehdr, &shnum);
if (!shdrs)
return -ENODEV;
shstrtab = read_elf_shstrtab(fh, ehdr, shdrs, shnum, &shstrtab_size);
if (!shstrtab)
return -ENODEV;
if (!pv_is_pv_elf(shdrs, shnum, shstrtab, shstrtab_size))
return -ENODEV;
df_elf_ensure_s390x();
dfi_arch_set(DFI_ARCH_64);
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
/* Try to read the customer communication key */
if (!key_path)
return -ENOKEY;
util_log_print(UTIL_LOG_DEBUG, _("Key path: %s\n"), key_path);
key = pv_file_get_content_as_secure_bytes(key_path);
if (!key)
return -ENOKEY;
/* Find PV completion configuration data and storage state data sections */
completion_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
PV_ELF_SECTION_NAME_COMPL);
if (!completion_shdr)
return -EINVAL;
storage_state_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
PV_ELF_SECTION_NAME_TWEAKS);
if (!storage_state_shdr)
return -EINVAL;
/* Read the PV completion configuration section data */
completion_sdata = read_elf_section_data_as_gbytes(fh, completion_shdr);
if (!completion_sdata)
return -EINVAL;
/* Derive and store the dump key in `@dump_key`. This key is used to AES-GCM decrypt the
* completion configuration data and store it in `@cpl_conf_decr`. In addition, mmap the
* configuration storage state area and use the `tweak_nonce`.
*/
if (pv_process_section_data(fh->fh, completion_sdata, storage_state_shdr->sh_offset,
storage_state_shdr->sh_size, key, &cpl_conf_decr, &dump_key,
&storage_state_data, &error) < 0) {
ERR(_("Unable to read decryption information:" ERR_NEWLINE "%s."), error->message);
return -EINVAL;
}
g_assert_nonnull(cpl_conf_decr);
g_assert_cmpuint(cpl_conf_decr->version, ==, PV_COMPL_DATA_VERSION_1);
cpl_conf_v1_decr = (pv_dump_completion_v1_t *)cpl_conf_decr;
cpu_dump_version = PV_SEC_CPU_DATA_VERSION_1;
phdrs = read_elf_phdrs(fh, ehdr, &phnum);
util_log_print(UTIL_LOG_DEBUG, _("DFI %s e_phnum %u\n"), dfi_pv_elf.name, phnum);
for (i = 0; i < phnum; i++) {
const Elf64_Phdr *phdr = &phdrs[i];
util_log_print(UTIL_LOG_DEBUG, _("DFI %s p_type[%d] 0x%lx\n"), dfi_pv_elf.name, i,
phdr->p_type);
switch (phdr->p_type) {
case PT_LOAD: {
/* Initialize callback data for the `dfi_pv_elf_mem_chunk_read_fn` function
*/
g_autoptr(pv_elf_ctx_t) elf_ctx =
pv_elf_ctx_new(fh->fh, &cpl_conf_v1_decr->data.confidential_area,
storage_state_data, phdr->p_offset, &error);
if (!elf_ctx) {
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
return -EINVAL;
}
if (load_found) {
ERR(_("Reading dump failed:" ERR_NEWLINE
"Multiple PT_LOAD segments are not supported."));
return -EINVAL;
}
if (pt_load_add(fh, phdr, (void **)&elf_ctx, dfi_pv_elf_mem_chunk_read_fn,
(dfi_mem_chunk_free_fn)pv_elf_ctx_free) < 0)
return -EINVAL;
load_found = true;
break;
}
case PT_NOTE:
/* Add CPU information (decrypts the CPU data) */
if (pt_notes_add(fh, phdr, cpu_dump_version, dump_key, &error)) {
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
return -EINVAL;
}
break;
default:
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-PHDR type %#x"), phdr->p_type);
break;
}
}
dfi_attr_version_set(cpl_conf_decr->version);
return 0;
}
static void dfi_pv_elf_cleanup(void)
{
/* nothing to do here */
}
/*
* PV ELF DFI operations
*/
struct dfi dfi_pv_elf = {
.exit = dfi_pv_elf_cleanup,
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
.init = dfi_pv_elf_init,
.name = "ELF (protected virtualization dump)",
};

View File

@@ -132,7 +132,7 @@ void dfi_vmcoreinfo_init(void)
/*
* Return vmcoreinfo data
*/
char *dfi_vmcoreinfo_get(void)
const char *dfi_vmcoreinfo_get(void)
{
return l.vmcoreinfo;
}

View File

@@ -9,7 +9,7 @@
#define DFI_VMCOREINFO_H
void dfi_vmcoreinfo_init(void);
char *dfi_vmcoreinfo_get(void);
const char *dfi_vmcoreinfo_get(void);
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);

View File

@@ -23,7 +23,6 @@
#include "dfi_vmcoreinfo.h"
#include "dfo.h"
#define HDR_PER_CPU_SIZE 0x4a0
#define HDR_PER_MEMC_SIZE 0x100
#define HDR_BASE_SIZE 0x2000
@@ -81,6 +80,8 @@ static void *notes_init(Elf64_Phdr *phdr, void *segment_start, u64 elf_offset)
ptr = nt_s390_vxrs_low(ptr, cpu);
ptr = nt_s390_vxrs_high(ptr, cpu);
}
if (dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_GS))
ptr = nt_s390_gs_cb(ptr, cpu);
}
out:
ptr = nt_vmcoreinfo(ptr, dfi_vmcoreinfo_get());
@@ -133,9 +134,8 @@ static void dfo_elf_init(void)
u64 hdr_off;
ensure_s390x();
alloc_size = HDR_BASE_SIZE +
dfi_cpu_cnt() * HDR_PER_CPU_SIZE +
dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
alloc_size = HDR_BASE_SIZE + dfi_cpu_cnt() * get_max_note_size_per_cpu() +
dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
buf = zg_alloc(alloc_size);
/* Init elf header */
ptr = ehdr_init(buf, dfi_mem_chunk_cnt() + 1);

View File

@@ -20,9 +20,11 @@
#include "opts.h"
static struct option long_opts[] = {
/* clang-format off */
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"info", no_argument, NULL, 'i'},
{"key", required_argument, NULL, 'k'},
{"device", no_argument, NULL, 'd'},
{"mount", no_argument, NULL, 'm'},
{"umount", no_argument, NULL, 'u'},
@@ -30,19 +32,20 @@ static struct option long_opts[] = {
{"select", required_argument, NULL, 's'},
{"debug", no_argument, NULL, 'X'},
{"verbose", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0 }
{NULL, 0, NULL, 0 },
/* clang-format on */
};
static const char optstr[] = "hvVidmus:f:X";
static const char optstr[] = "hvVidmuk:s:f:X";
/*
* Text for --help option
*/
static const char help_text[] =
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
" DUMP [-s SYS] [-f FMT] DUMP_FILE\n"
" -m DUMP [-s SYS] [-f FMT] DIR\n"
" -i DUMP [-s SYS]\n"
"Usage: zgetdump DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE\n"
" DUMP [-s SYS] [-f FMT] [-k KEY] DUMP_FILE\n"
" -m DUMP [-s SYS] [-f FMT] [-k KEY] DIR\n"
" -i DUMP [-s SYS] [-k KEY]\n"
" -d DUMPDEV\n"
" -u DIR\n"
"\n"
@@ -58,6 +61,7 @@ static const char help_text[] =
"In the syntax description, DUMP specifies a dump device or dump file to be\n"
"read. The following options are available:\n"
"\n"
"-k, --key Specify the key KEY to decrypt the protected virtualization dump\n"
"-m, --mount Mount DUMP to mount point DIR\n"
"-u, --umount Unmount dump from mount point DIR\n"
"-i, --info Print DUMP information\n"
@@ -71,7 +75,7 @@ static const char help_text[] =
" messages. This option is intended for debugging\n"
"-h, --help Print this help, then exit\n";
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2018";
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2022";
/*
* Select option strings
@@ -88,6 +92,7 @@ static void init_defaults(struct options *opts)
opts->prog_name = "zgetdump";
opts->action = ZG_ACTION_COPY;
opts->output_path = NULL;
opts->key_path = NULL;
#ifdef __s390x__
opts->fmt = "elf";
#else
@@ -101,10 +106,15 @@ static void init_defaults(struct options *opts)
/*
* Print "help" hint
*/
static void __noreturn print_usage_exit(const char *prog_name)
void __noreturn print_usage_exit(const char *prog_name)
{
opts_print_usage(prog_name);
zg_exit(1);
}
void opts_print_usage(const char *prog_name)
{
STDERR("Try '%s --help' for more information.\n", prog_name);
zg_exit(1);
}
/*
@@ -176,6 +186,16 @@ static void output_set(struct options *opts, const char *path)
opts->output_path = zg_strdup(path);
}
/*
* Set customer communication key (CCK)
*/
static void key_set(struct options *opts, const char *key)
{
assert(!opts->key_path);
opts->key_path = zg_strdup(key);
}
/*
* Set FUSE debug options
*/
@@ -313,6 +333,9 @@ void opts_parse(int argc, char *argv[], struct options *opts)
case 's':
select_set(opts, optarg);
break;
case 'k':
key_set(opts, optarg);
break;
case 'X':
opts->debug_specified = 1;
break;

View File

@@ -20,6 +20,7 @@ struct options {
char *device;
/* If `output_path == NULL` the output is written to `stdout` */
const char *output_path;
const char *key_path;
char *mount_point;
int fmt_specified;
const char *fmt;
@@ -36,5 +37,7 @@ extern const char *OPTS_SELECT_PROD;
extern const char *OPTS_SELECT_ALL;
void opts_parse(int argc, char *argv[], struct options *opts);
void opts_print_usage(const char *prog_name);
void __noreturn print_usage_exit(const char *prog_name);
#endif /* OPTS_H */

218
zdump/pv_defs.h Normal file
View File

@@ -0,0 +1,218 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* Secure execution/PV guest dump definitions.
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef PV_DEFS_H
#define PV_DEFS_H
#include <stdint.h>
#include "lib/zt_common.h"
/* Special ELF section names used for Protected Virtualization Dumps */
#define PV_ELF_SECTION_NAME_COMPL "pv_compl"
#define PV_ELF_SECTION_NAME_TWEAKS "pv_mem_meta"
#define PV_COMPL_DATA_VERSION_1 ((uint32_t)1)
#define PV_SEC_CPU_DATA_VERSION_1 ((uint32_t)1)
/**
* pv_tweak_nonce_t:
*
* Struct representing a tweak nonce.
*/
typedef struct {
uint8_t value[16];
} pv_tweak_nonce_t __big_endian;
/**
* pv_dump_completion_aad_v1_t:
*
* Struct representing the authenticated part of a PV dump completion v1
*/
typedef struct {
uint32_t version; /* 0000 */
uint32_t len; /* 0004 */
uint64_t res_0x8; /* 0008 */
uint8_t seed[64]; /* 0016 */
uint8_t iv[12]; /* 0080 */
uint32_t res_0x92; /* 0092 */
} pv_dump_completion_aad_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_dump_completion_aad_v1_t) == 96)
/**
* pv_dump_completion_confidential_area_v1_t:
*
* Struct representing the encrypted part of a PV dump completion v1
*/
typedef struct {
pv_tweak_nonce_t nonce; /* 0096 */
union {
uint8_t key[64]; /* 0112 */
struct {
uint8_t key1[32]; /* 0112 */
uint8_t key2[32]; /* 0144 */
};
};
uint8_t res_0x176[96]; /* 0176 */
} pv_dump_completion_confidential_area_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_dump_completion_confidential_area_v1_t) == 176)
/**
* pv_dump_completion_data_v1_t
*
* Struct to interpret the data returned by the `Complete Configuration Dump` UVC.
*/
typedef struct {
pv_dump_completion_aad_v1_t aad; /* 0000 */
pv_dump_completion_confidential_area_v1_t confidential_area; /* 0096 */
uint8_t tag[16]; /* 0272 */
/* 0288 */
} pv_dump_completion_data_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_dump_completion_data_v1_t) == 288)
/* Container data structures used to implement polymorphic behavior */
#define PV_STRUCT_NAME(_struct, _version) _struct##_v##_version##_t
#define PV_GET_STRUCT_DATA(_struct, _version, _container) \
_container->version != _version ? \
NULL : \
&(((PV_STRUCT_NAME(_struct, _version) *)_container)->data)
#define PV_GET_DUMP_DATA_V1(_container) PV_GET_STRUCT_DATA(pv_dump_completion, 1, _container)
typedef struct {
unsigned int version;
} pv_dump_completion_t;
typedef struct {
pv_dump_completion_t super;
pv_dump_completion_data_v1_t data;
} PV_STRUCT_NAME(pv_dump_completion, 1);
/**
* pv_cpu_vector_register_t:
*
* Struct representing the PV CPU vector register
*/
typedef struct {
uint64_t low;
uint64_t high;
} pv_cpu_vector_register_t __big_endian;
STATIC_ASSERT(sizeof(pv_cpu_vector_register_t) == 16)
/**
* pv_cpu_dump_aad_v1_t:
*
* Struct representing the authenticated part of a PV cpu dump v1
*/
typedef struct {
uint32_t version; /* 0000 */
uint32_t len; /* 0004 */
uint8_t iv[12]; /* 0008 */
uint8_t res_0x20[12]; /* 0020 */
} pv_cpu_dump_aad_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_cpu_dump_aad_v1_t) == 32)
/**
* pv_cpu_dump_confidential_area_v1_t:
*
* Struct to interpret the Secure CPU Dump Area UVC.
*/
typedef struct {
uint8_t gprs[128]; /* 0032 */
uint8_t psw[16]; /* 0160 */
uint8_t res_0x176[8]; /* 0176 */
uint32_t prefix; /* 0184 */
uint32_t fpc; /* 0188 */
uint8_t res_0x192[4]; /* 0192 */
uint32_t todpreg; /* 0196 */
uint64_t timer; /* 0200 */
uint64_t todcmp; /* 0208 */
uint8_t res_0x216[8]; /* 0216 */
uint8_t acrs[64]; /* 0224 */
uint8_t ctrs[128]; /* 0288 */
struct {
pv_cpu_vector_register_t vector_register_low[16];
uint8_t vector_register_high[256];
}; /* 0416 */
uint8_t res_0x928[512]; /* 0928 */
uint8_t zeros_2[8]; /* 1440 */
uint64_t gsd; /* 1448 */
uint64_t gssm; /* 1456 */
uint64_t gs_epl_a; /* 1464 */
uint8_t res_0x1472[64]; /* 1472 */
union {
struct {
uint16_t has_osii : 1;
uint16_t reserved : 15;
};
uint16_t dump_flags; /* 1536 */
};
uint8_t res_0x1538[6]; /* 1538 */
uint8_t res_0x1544[8]; /* 1544 */
} pv_cpu_dump_confidential_area_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_cpu_dump_confidential_area_v1_t) == 1520)
/**
* pv_cpu_dump_v1_t:
*
* Struct representing a PV CPU dump v1 returned by the `Dump CPU state` UVC.
*/
typedef struct {
pv_cpu_dump_aad_v1_t aad; /* 0000 */
pv_cpu_dump_confidential_area_v1_t confidential_area; /* 0032 */
uint8_t tag[16]; /* 1552 */
} pv_cpu_dump_v1_t __big_endian;
STATIC_ASSERT(sizeof(pv_cpu_dump_v1_t) == 1568)
/* PV special tweak component indicator */
#define PV_SPECIAL_INDICATOR 0xFFFFFFFFUL
/**
* pv_special_tweak_component_layout_t:
*
* Struct representing the layout of a special tweak component.
*/
typedef struct {
uint32_t indicator;
uint8_t reserved[10];
uint8_t flag_reserved1;
uint8_t flag_reserved2 : 5;
uint8_t is_mapped_page : 1;
uint8_t is_shared_page : 1;
uint8_t is_zero_page : 1;
} pv_special_tweak_component_layout_t __big_endian;
STATIC_ASSERT(sizeof(pv_special_tweak_component_layout_t) == 16)
/**
* pv_tweak_component_t:
*
* Struct representing a tweak component returned by `Dump Configuration Storage State` UVC.
*/
typedef union {
uint8_t value[16];
pv_special_tweak_component_layout_t special;
} pv_tweak_component_t __big_endian;
STATIC_ASSERT(sizeof(pv_tweak_component_t) == 16)
/**
* pv_tweak_t:
*
* Struct representing one tweak value.
*/
typedef struct {
uint8_t value[16];
} pv_tweak_t;
STATIC_ASSERT(sizeof(pv_tweak_t) == 16)
STATIC_ASSERT(sizeof_field(pv_tweak_t, value) == sizeof_field(pv_tweak_component_t, value))
STATIC_ASSERT(sizeof_field(pv_tweak_t, value) == sizeof_field(pv_tweak_nonce_t, value))
#endif /* PV_DEFS_H */

980
zdump/pv_utils.c Normal file
View File

@@ -0,0 +1,980 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* Utilities to decrypt secure execution guest dumps.
*
* Copyright IBM Corp. 2001, 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "pv_utils.h"
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <errno.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/kdf.h>
#include <openssl/crypto.h>
#include "lib/zt_common.h"
#include "lib/util_log.h"
#include "libpv/crypto.h"
#include "libpv/se-hdr.h"
#include "pv_defs.h"
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Phdr, free)
/* Definitions for decrypting memory and deriving dump key */
#define PV_CSS_PAGESIZE 0x1000U /* Configuration storage state page size */
#define PV_DUMP_V1_HKDF_INFO "IBM Z Ultravisor Dump"
#define PV_DUMP_V1_HKDF_LEN 32
#define PV_DUMP_V1_HKDF_FUN EVP_sha512()
#define PV_DUMP_V1_CIPHER EVP_aes_256_gcm()
static gboolean u64_checked_add(u64 *res, u64 lhs, u64 rhs)
{
guint64 _res = 0;
gboolean success = g_uint64_checked_add(&_res, (guint64)lhs, (guint64)rhs);
*res = (u64)_res;
return success;
}
static u64 page_offset(u64 addr)
{
return addr % PV_CSS_PAGESIZE;
}
static u64 page_index(u64 addr)
{
return addr / PV_CSS_PAGESIZE;
}
static u64 page_start_addr(u64 page_idx, GError **error)
{
gboolean success;
uint64_t ret;
success = g_uint64_checked_mul(&ret, page_idx, PV_CSS_PAGESIZE);
if (G_UNLIKELY(!success)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGE_START_ADDR_OVERFLOW,
_("UInt overflow detected: %s: pageidx: %#llx"), __func__, page_idx);
}
return ret;
}
static u64 page_end_addr(u64 page_idx, GError **error)
{
gboolean success;
uint64_t ret;
/* (page_idx + 1) * PV_CSS_PAGESIZE - 1; */
success = g_uint64_checked_add(&ret, page_idx, 1);
success &= g_uint64_checked_mul(&ret, ret, PV_CSS_PAGESIZE);
ret -= 1;
if (G_UNLIKELY(!success)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGE_END_ADDR_OVERFLOW,
_("UInt overflow detected: %s: pageidx: %#llx"), __func__, page_idx);
}
return ret;
}
static void pv_dump_completion_v1_free(pv_dump_completion_v1_t *cpl)
{
pv_dump_completion_free((pv_dump_completion_t *)cpl);
}
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_dump_completion_v1_t, pv_dump_completion_v1_free)
void pv_dump_completion_free(pv_dump_completion_t *cpl)
{
if (!cpl)
return;
if (cpl->version == PV_COMPL_DATA_VERSION_1) {
pv_dump_completion_v1_t *cpl_v1 = (pv_dump_completion_v1_t *)cpl;
OPENSSL_cleanse(&cpl_v1->data, sizeof(cpl_v1->data));
}
g_free(cpl);
}
GBytes *pv_derive_dump_key_v1(const pv_dump_completion_data_v1_t *cpl_data, GBytes *cck,
GError **error)
{
g_autoptr(GBytes) salt = NULL, info = NULL;
size_t cck_size;
size_t exp_cck_size = sizeof_field(struct pv_hdr_encrypted, cust_comm_key);
assert(cpl_data->aad.version == PV_COMPL_DATA_VERSION_1);
cck_size = g_bytes_get_size(cck);
if (cck_size != exp_cck_size) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_CCK_SIZE,
_("Wrong key size: expected %lu != actual %lu"), exp_cck_size,
cck_size);
return NULL;
}
salt = g_bytes_new(&cpl_data->aad.seed, sizeof(cpl_data->aad.seed));
info = g_bytes_new(PV_DUMP_V1_HKDF_INFO, strlen(PV_DUMP_V1_HKDF_INFO));
return pv_hkdf_extract_and_expand(PV_DUMP_V1_HKDF_LEN, cck, salt, info, PV_DUMP_V1_HKDF_FUN,
error);
}
pv_dump_completion_v1_t *pv_decrypt_dump_completion_v1(const pv_dump_completion_data_v1_t *cpl_data,
GBytes *dump_key, GError **error)
{
g_autoptr(GBytes) encr = NULL, aad = NULL, tag = NULL, decr = NULL, iv = NULL;
g_autoptr(pv_dump_completion_v1_t) cpl = NULL;
PvCipherParms params;
size_t copied;
void *tmp;
assert(cpl_data->aad.version == PV_COMPL_DATA_VERSION_1);
encr = g_bytes_new(&cpl_data->confidential_area, sizeof(cpl_data->confidential_area));
aad = g_bytes_new(&cpl_data->aad, sizeof(cpl_data->aad));
tag = g_bytes_new(&cpl_data->tag, sizeof(cpl_data->tag));
iv = g_bytes_new(&cpl_data->aad.iv, sizeof(cpl_data->aad.iv));
params.cipher = PV_DUMP_V1_CIPHER;
params.key = dump_key;
params.iv = iv;
params.tag_size = g_bytes_get_size(tag);
if (pv_gcm_decrypt(encr, aad, tag, &params, &decr, error) < 0)
return NULL;
cpl = g_malloc(sizeof(*cpl));
cpl->super.version = PV_COMPL_DATA_VERSION_1;
tmp = pv_gbytes_memcpy(&cpl->data.aad, sizeof(cpl->data.aad), aad, &copied);
if (!tmp || copied != sizeof(cpl->data.aad)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_AAD_SIZE,
_("Wrong AAD size"));
return NULL;
}
tmp = pv_gbytes_memcpy(&cpl->data.confidential_area, sizeof(cpl->data.confidential_area),
decr, &copied);
if (!tmp || copied != sizeof(cpl->data.confidential_area)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_CONFIDENTIAL_SIZE,
_("Wrong confidential data size"));
return NULL;
}
tmp = pv_gbytes_memcpy(&cpl->data.tag, sizeof(cpl->data.tag), tag, &copied);
if (!tmp || copied != sizeof(cpl->data.tag)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_TAG_SIZE,
_("Wrong tag size"));
return NULL;
}
return g_steal_pointer(&cpl);
}
static long pv_cpu_note_data_get_version(GBytes *note, GError **error)
{
size_t size;
const uint32_t *version = g_bytes_get_data(note, &size);
STATIC_ASSERT(offsetof(pv_cpu_dump_aad_v1_t, version) == 0);
/* check whether we can dereference @version */
if (sizeof(*version) > size) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE, "%s",
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
return -1;
}
return *version;
}
dfi_cpu_t *pv_decrypt_cpu_note_data(const unsigned int expected_version, GBytes *cpu_note,
GBytes *dump_key, GError **error)
{
long version = pv_cpu_note_data_get_version(cpu_note, error);
if (version < 0)
return NULL;
if (version != expected_version) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
_("Wrong NT_S390_PV_CPU_DATA version (%ld)"), version);
return NULL;
}
switch (version) {
case PV_SEC_CPU_DATA_VERSION_1: {
const pv_cpu_dump_confidential_area_v1_t *pv_cpu;
const pv_cpu_dump_v1_t *cpu_encrypted;
size_t cpu_note_size, cpu_decrypted_size;
g_autoptr(GBytes) cpu_decrypted = NULL;
cpu_encrypted = g_bytes_get_data(cpu_note, &cpu_note_size);
if (sizeof(*cpu_encrypted) > cpu_note_size) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE,
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
return NULL;
}
cpu_decrypted = pv_decrypt_cpu_dump_area_v1(cpu_encrypted, dump_key, error);
if (!cpu_decrypted) {
g_prefix_error(
error,
_("Unable to authenticate confidential CPU data. Dump probably corrupted:" ERR_NEWLINE));
return NULL;
}
pv_cpu = g_bytes_get_data(cpu_decrypted, &cpu_decrypted_size);
if (cpu_decrypted_size != sizeof(*pv_cpu)) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE,
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
return NULL;
}
/* Check dump flags */
if (pv_cpu->has_osii)
util_log_print(UTIL_LOG_WARN,
_("CPU state may contain partial instruction results"));
return pv_dfi_cpu_from_pv_cpu(pv_cpu);
}
default:
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
_("Unsupported NT_S390_PV_CPU_DATA version (%ld)"), version);
return NULL;
}
}
GBytes *pv_decrypt_cpu_dump_area_v1(const pv_cpu_dump_v1_t *cpu_dump_area, GBytes *dump_key,
GError **error)
{
g_autoptr(GBytes) encr = NULL, aad = NULL, tag = NULL;
g_autoptr(GBytes) iv = NULL, out = NULL;
PvCipherParms params;
encr = g_bytes_new(&cpu_dump_area->confidential_area,
sizeof(cpu_dump_area->confidential_area));
aad = g_bytes_new(&cpu_dump_area->aad, sizeof(cpu_dump_area->aad));
tag = g_bytes_new(&cpu_dump_area->tag, sizeof(cpu_dump_area->tag));
iv = g_bytes_new(&cpu_dump_area->aad.iv, sizeof(cpu_dump_area->aad.iv));
params.cipher = PV_DUMP_V1_CIPHER;
params.key = dump_key;
params.iv = iv;
params.tag_size = g_bytes_get_size(tag);
if (pv_gcm_decrypt(encr, aad, tag, &params, &out, error) < 0)
return NULL;
return g_steal_pointer(&out);
}
dfi_cpu_t *pv_dfi_cpu_from_pv_cpu(const pv_cpu_dump_confidential_area_v1_t *pv_cpu)
{
g_autoptr(dfi_cpu_t) ret = dfi_cpu_alloc();
STATIC_ASSERT(sizeof(ret->gprs) == sizeof(pv_cpu->gprs));
(void)memcpy(ret->gprs, pv_cpu->gprs, sizeof(ret->gprs));
STATIC_ASSERT(sizeof(ret->psw) == sizeof(pv_cpu->psw));
(void)memcpy(ret->psw, pv_cpu->psw, sizeof(ret->psw));
ret->prefix = pv_cpu->prefix;
ret->fpc = pv_cpu->fpc;
ret->todpreg = pv_cpu->todpreg;
ret->timer = pv_cpu->timer;
ret->todcmp = pv_cpu->todcmp;
STATIC_ASSERT(sizeof(ret->acrs) == sizeof(pv_cpu->acrs));
(void)memcpy(ret->acrs, pv_cpu->acrs, sizeof(ret->acrs));
STATIC_ASSERT(sizeof(ret->ctrs) == sizeof(pv_cpu->ctrs));
(void)memcpy(ret->ctrs, pv_cpu->ctrs, sizeof(ret->ctrs));
/* Copy floating point register and the high part of the first 16 vector
* register
*/
STATIC_ASSERT(ARRAY_SIZE(ret->fprs) == ARRAY_SIZE(pv_cpu->vector_register_low));
STATIC_ASSERT(ARRAY_SIZE(pv_cpu->vector_register_low) == ARRAY_SIZE(ret->vxrs_low));
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
for (unsigned int i = 0; i < ARRAY_SIZE(ret->fprs); i++) {
ret->fprs[i] = pv_cpu->vector_register_low[i].low;
ret->vxrs_low[i] = pv_cpu->vector_register_low[i].high;
}
STATIC_ASSERT(sizeof(ret->vxrs_high) == sizeof(pv_cpu->vector_register_high));
(void)memcpy(ret->vxrs_high, pv_cpu->vector_register_high, sizeof(ret->vxrs_high));
/* Set guarded storage registers */
ret->reserved = 0;
ret->gsd = pv_cpu->gsd;
ret->gssm = pv_cpu->gssm;
ret->gs_epl_a = pv_cpu->gs_epl_a;
/* Add GS facility */
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_GS);
/* NOTE: In the future it might be useful to store `@pv_cpu->dump_flags`
* in the `struct dfi_cpu`. Currently, we don't have any use case for
* it.
*/
return g_steal_pointer(&ret);
}
/* Utilities for decrypting the memory */
struct _pv_crypto_ctx {
EVP_CIPHER_CTX *cipher_ctx;
BIO *input;
BIO *filter;
/* To be allocated/deallocated using OpenSSL malloc and clear+free */
pv_tweak_nonce_t *nonce;
/* scratch area */
pv_tweak_t tweak_scratch;
};
static BIO *pv_BIO_cipher_new(const EVP_CIPHER *cipher, const unsigned char *key, size_t key_len,
enum PvCryptoMode mode, GError **error)
{
bool encrypt = mode == PV_ENCRYPT;
EVP_CIPHER_CTX *ctx = NULL;
g_autoptr(BIO) ret = NULL;
ENGINE *engine = NULL;
ret = BIO_new(BIO_f_cipher());
if (!ret) {
abort();
return NULL;
}
if (BIO_get_cipher_ctx(ret, &ctx) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
_("BIO_get_cipher_ctx failed"));
return NULL;
}
g_assert(ctx);
if (EVP_CipherInit_ex(ctx, cipher, engine, NULL, NULL, encrypt) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
_("EVP_Cipher_init failed"));
return NULL;
}
/* Check key length */
if (EVP_CIPHER_CTX_key_length(ctx) != (int)key_len) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_KEY,
_("Passed key has incorrect size: %ld != %d"), key_len,
EVP_CIPHER_key_length(cipher));
return NULL;
}
/* Set key */
if (EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, -1) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
_("EVP_Cipher_init set_key failed"));
return NULL;
}
return g_steal_pointer(&ret);
}
pv_crypto_ctx_t *pv_crypto_ctx_new(BIO *input, const unsigned char *key, size_t key_size,
const pv_tweak_nonce_t *nonce, enum PvCryptoMode mode,
GError **error)
{
g_autoptr(pv_crypto_ctx_t) ret = NULL;
g_autoptr(BIO) xts_filter = NULL;
g_assert(input);
STATIC_ASSERT(sizeof_field(pv_crypto_ctx_t, nonce) == sizeof(nonce));
ret = g_new0(pv_crypto_ctx_t, 1);
xts_filter = pv_BIO_cipher_new(EVP_aes_256_xts(), key, key_size, mode, error);
if (!xts_filter) {
g_prefix_error(error,
_("Initializing the zdump crypto context failed" ERR_NEWLINE));
return NULL;
}
if (BIO_get_cipher_ctx(xts_filter, &ret->cipher_ctx) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CRYPTO_CTX,
_("Initializing the zdump crypto context failed"));
return NULL;
}
g_assert(ret->cipher_ctx);
/* set-up BIO chain for the encryption/decryption */
ret->filter = BIO_push(g_steal_pointer(&xts_filter), input);
g_assert(ret->filter);
if (BIO_up_ref(input) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CRYPTO_CTX,
_("Initializing the zdump crypto context failed"));
return NULL;
}
ret->input = input;
ret->nonce = OPENSSL_malloc(sizeof(*ret->nonce));
if (!ret->nonce)
abort();
(void)memcpy(ret->nonce, nonce, sizeof(*ret->nonce));
return g_steal_pointer(&ret);
}
void pv_crypto_ctx_free(pv_crypto_ctx_t *ctx)
{
if (!ctx)
return;
g_clear_pointer(&ctx->input, BIO_vfree);
g_clear_pointer(&ctx->filter, BIO_vfree);
/* It's intentional that we don't free @ctx->cipher_ctx since it's not
* owned by us, but the BIO chain */
ctx->cipher_ctx = NULL;
OPENSSL_clear_free(ctx->nonce, sizeof(*ctx->nonce));
g_free(ctx);
}
void calculate_tweak(const pv_tweak_component_t *tweak, const pv_tweak_nonce_t *nonce,
pv_tweak_t *out)
{
for (size_t i = 0; i < ARRAY_SIZE(out->value); i++)
out->value[i] = tweak->value[i] | nonce->value[i];
}
int pv_read_page(BIO *input, BIO *output, GError **error)
{
char data[PAGE_SIZE];
int rc;
rc = BIO_read(input, data, ARRAY_SIZE(data));
if (rc != ARRAY_SIZE(data)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO, _("BIO_read failed"));
return -1;
}
rc = BIO_write(output, data, ARRAY_SIZE(data));
if (rc != ARRAY_SIZE(data)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO, _("BIO_write failed"));
return -1;
}
return rc;
}
unsigned long pv_page_state(const pv_tweak_component_t *comp)
{
unsigned long ret = PV_INVAL_PAGE_STATE;
if (comp->special.indicator != PV_SPECIAL_INDICATOR)
return PV_ENCRYPTED_PAGE;
if (comp->special.flag_reserved1 || comp->special.flag_reserved2 ||
comp->special.is_zero_page + comp->special.is_shared_page +
comp->special.is_mapped_page <
1)
return PV_INVAL_PAGE_STATE;
if (comp->special.is_zero_page)
ret |= PV_ZERO_PAGE;
if (comp->special.is_shared_page)
ret |= PV_SHARED_PAGE;
if (comp->special.is_mapped_page)
ret |= PV_MAPPED_PAGE;
return ret;
}
static const unsigned char NULL_DATA[PV_CSS_PAGESIZE] = { 0x0 };
static bool pv_BIO_is_seekable(BIO *bio)
{
const int type = BIO_method_type(bio);
return type == BIO_TYPE_FD || type == BIO_TYPE_FILE;
}
static int pv_BIO_seek(BIO *bio, long long offset)
{
/* The documentation of @BIO_seeks says @offset is a `int` but the
* source code actually shows it's a long. Therefore add these
* additional checks here to detect in case something changes in
* OpenSSL.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wconversion"
#pragma GCC diagnostic error "-Wsign-conversion"
return BIO_seek(bio, offset);
#pragma GCC diagnostic pop
}
static int update_tweak(pv_crypto_ctx_t *crypto_ctx, GError **error)
{
EVP_CIPHER_CTX *ctx = crypto_ctx->cipher_ctx;
u8 *tweak = crypto_ctx->tweak_scratch.value;
/* Check tweak length */
if (EVP_CIPHER_CTX_iv_length(ctx) != ARRAY_SIZE(crypto_ctx->tweak_scratch.value)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_TWEAK,
_("Tweak has wrong size"));
return -1;
}
/* set the new tweak IV */
if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, tweak, -1) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_TWEAK,
_("Initializing tweaks failed"));
return -1;
}
return 0;
}
ssize_t pv_process_pglist(pv_crypto_ctx_t *ctx, BIO *output,
const pv_tweak_component_t *tweak_components, size_t tweak_components_len,
long input_off, GError **error)
{
bool is_output_seekable = pv_BIO_is_seekable(output);
long cur_in_off = input_off, cur_out_off = 0;
ssize_t page_idx;
int rc;
assert(ctx->input);
assert(ctx->filter);
assert(output);
assert(tweak_components_len <= SSIZE_MAX);
/* See https://www.openssl.org/docs/man1.1.0/man3/BIO_seek.html */
if (!pv_BIO_is_seekable(ctx->input)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
_("Input source is not seekable"));
return -1;
}
if (pv_BIO_seek(ctx->input, cur_in_off) == -1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
_("BIO_seek failed"));
return -1;
}
if (tweak_components_len > LONG_MAX / PV_CSS_PAGESIZE) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
_("Possible long overflow detected: Try to read %li pages"),
tweak_components_len);
}
for (page_idx = 0; page_idx < (ssize_t)tweak_components_len; page_idx++) {
const pv_tweak_component_t *tweak_comp = &tweak_components[page_idx];
unsigned long page_state;
BIO *input = NULL;
g_assert_nonnull(tweak_comp);
page_state = pv_page_state(tweak_comp);
if (page_state & PV_INVAL_PAGE_STATE) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
_("Invalid page state"));
return -1;
}
/* g_assert(page_state & PV_MAPPED_PAGE); */
page_state &= ~PV_MAPPED_PAGE;
if (page_state & PV_ZERO_PAGE) {
/* Nothing to do here for BIO_FILE because a sparse file
* is filled with zeros by default. Therefore we can
* simply calculate the new output offset. For a
* BIO_s_mem BIO_seek doesn't work therefore we've to
* work around.
*/
cur_out_off += PV_CSS_PAGESIZE;
cur_in_off += PV_CSS_PAGESIZE;
if (G_UNLIKELY(pv_BIO_seek(ctx->input, cur_in_off) == -1)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
_("BIO_seek failed"));
return -1;
}
if (is_output_seekable) {
if (G_UNLIKELY(pv_BIO_seek(output, cur_out_off) == -1)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR,
ZDUMP_ERR_PGLIST_BIO, _("BIO_seek failed"));
return -1;
}
} else {
if (G_UNLIKELY(
BIO_write(output, NULL_DATA, ARRAY_SIZE(NULL_DATA)) !=
ARRAY_SIZE(NULL_DATA))) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR,
ZDUMP_ERR_PGLIST_BIO, _("BIO_write failed"));
return -1;
}
}
g_assert(!(page_state & ~PV_ZERO_PAGE));
continue;
}
if (page_state & PV_SHARED_PAGE) {
/* shared pages are not encrypted */
input = ctx->input;
g_assert(!(page_state & ~PV_SHARED_PAGE));
} else if (page_state & PV_ENCRYPTED_PAGE) {
input = ctx->filter;
calculate_tweak(tweak_comp, ctx->nonce, &ctx->tweak_scratch);
/* set new tweak */
if (update_tweak(ctx, error) < 0)
return -1;
g_assert(!(page_state & ~PV_ENCRYPTED_PAGE));
} else {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_INVAL_STATE,
_("Invalid page state. page-idx: %#lx, state: %#lx"), page_idx,
page_state);
return -1;
}
rc = pv_read_page(input, output, error);
if (rc != PV_CSS_PAGESIZE)
return -1;
/* adapt the offsets */
cur_in_off += rc;
cur_out_off += rc;
}
return page_idx;
}
bool pv_is_pv_elf(const Elf64_Shdr *shdrs, const unsigned int shnum, const char *shstrtab,
const size_t shstrtab_size)
{
return find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
PV_ELF_SECTION_NAME_COMPL) != NULL;
}
struct _storage_state_mmap {
void *first_page_ptr;
size_t mapped_size;
pv_tweak_component_t *tweak_components;
size_t num_tweaks;
gatomicrefcount ref_count;
};
storage_state_mmap_t *storage_state_mmap_new(const int fd, const u64 offset, const u64 size,
GError **error)
{
size_t tweak_components_cnt, start_addr, in_page_offset, mmapped_size;
g_autoptr(storage_state_mmap_t) ret = NULL;
int saved_errno = 0;
u8 *ptr;
if (size == 0 || size % sizeof(pv_tweak_component_t) != 0) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP,
_("StorageState MMAP: size (%#llx) not a multiple of (%#lx)"), size,
sizeof(pv_tweak_component_t));
return NULL;
}
start_addr = page_start_addr(page_index(offset), error);
if (*error)
return NULL;
tweak_components_cnt = size / sizeof(pv_tweak_component_t);
in_page_offset = page_offset(offset);
mmapped_size = size + in_page_offset;
if (start_addr > SSIZE_MAX) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP,
_("StorageState MMAP: page start address is too large (%#lx)"),
start_addr);
return NULL;
}
ptr = mmap(NULL, mmapped_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, fd,
(ssize_t)start_addr);
saved_errno = errno;
if (ptr == MAP_FAILED) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP, _("mmap failed: %s"),
g_strerror(saved_errno));
return NULL;
}
ret = g_new0(typeof(*ret), 1);
ret->first_page_ptr = ptr;
ret->tweak_components = (pv_tweak_component_t *)(ptr + in_page_offset);
ret->num_tweaks = tweak_components_cnt;
ret->mapped_size = mmapped_size;
g_atomic_ref_count_init(&ret->ref_count);
return g_steal_pointer(&ret);
}
storage_state_mmap_t *storage_state_mmap_ref(storage_state_mmap_t *storage_state)
{
g_assert(storage_state);
g_atomic_ref_count_inc(&storage_state->ref_count);
return storage_state;
}
void storage_state_mmap_unref(storage_state_mmap_t *storage_state)
{
if (!storage_state)
return;
if (storage_state->ref_count && !g_atomic_ref_count_dec(&storage_state->ref_count))
return;
if (storage_state->first_page_ptr) {
int rc = munmap(storage_state->first_page_ptr, storage_state->mapped_size);
if (rc != 0)
util_log_print(UTIL_LOG_WARN, _("munmap has failed"));
}
g_free(storage_state);
}
pv_elf_ctx_t *pv_elf_ctx_new(const int fd,
const pv_dump_completion_confidential_area_v1_t *cpl_conf,
storage_state_mmap_t *storage_state_data, const u64 elf_load_offset,
GError **error)
{
g_autoptr(pv_elf_ctx_t) ret = NULL;
g_autoptr(BIO) input = NULL;
input = BIO_new_fd(fd, BIO_NOCLOSE);
if (!input) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_CTX_BIO,
_("cannot open file"));
return NULL;
}
ret = g_new0(typeof(*ret), 1);
ret->pv_ctx = pv_crypto_ctx_new(input, cpl_conf->key, sizeof(cpl_conf->key),
&cpl_conf->nonce, PV_DECRYPT, error);
if (!ret->pv_ctx)
return NULL;
ret->storage_state_data = storage_state_mmap_ref(storage_state_data);
ret->elf_load_off = elf_load_offset;
return g_steal_pointer(&ret);
}
void pv_elf_ctx_free(pv_elf_ctx_t *p)
{
if (!p)
return;
g_clear_pointer(&p->pv_ctx, pv_crypto_ctx_free);
g_clear_pointer(&p->storage_state_data, storage_state_mmap_unref);
g_clear_pointer(&p->output, BIO_vfree);
g_free(p);
}
const pv_tweak_component_t *pv_get_tweak_components(storage_state_mmap_t *storage_state_data,
u64 page_idx, u64 page_cnt)
{
u64 last_page_idx;
g_assert_cmpuint(page_cnt, >=, 1);
if (!u64_checked_add(&last_page_idx, page_idx, page_cnt - 1))
return NULL;
if (last_page_idx >= storage_state_data->num_tweaks)
return NULL;
return &storage_state_data->tweak_components[page_idx];
}
/* Return version number if possible */
static long completion_data_get_version(GBytes *cpl_data, GError **error)
{
size_t size;
const uint32_t *version = g_bytes_get_data(cpl_data, &size);
STATIC_ASSERT(offsetof(pv_dump_completion_aad_v1_t, version) == 0);
/* check whether we can dereference @version */
if (sizeof(*version) > size) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_COMPL_VER,
_("Unsupported " PV_ELF_SECTION_NAME_COMPL " section size (%#lx)"),
size);
return -1;
}
return *version;
}
int pv_process_section_data(const int fd, GBytes *completion_sec, const u64 storage_state_offset,
const size_t storage_state_size, GBytes *cck,
pv_dump_completion_t **completion_decr, GBytes **dump_key,
storage_state_mmap_t **storage_state, GError **error)
{
g_autoptr(pv_dump_completion_t) _completion_decr = NULL;
g_autoptr(storage_state_mmap_t) _storage_state_data = NULL;
g_autoptr(GBytes) _dump_key = NULL;
g_assert(completion_sec);
long version;
version = completion_data_get_version(completion_sec, error);
if (version < 0)
return -1;
switch (version) {
case PV_COMPL_DATA_VERSION_1: {
const pv_dump_completion_data_v1_t *ccd;
size_t size;
ccd = g_bytes_get_data(completion_sec, &size);
if (sizeof(*ccd) > size) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_COMPL_DATA,
_("Corrupted completion configuration data"));
return -1;
}
if (ccd->aad.len != sizeof(*ccd)) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_COMPL_DATA,
_("Incorrect completion configuration data length"));
return -1;
}
_dump_key = pv_derive_dump_key_v1(ccd, cck, error);
if (!_dump_key) {
g_prefix_error(error, _("Unable to derive dump key: "));
return -1;
}
_completion_decr = (pv_dump_completion_t *)pv_decrypt_dump_completion_v1(
ccd, _dump_key, error);
if (!_completion_decr) {
g_prefix_error(error,
_("Unable to decrypt completion configuration data: "));
return -1;
}
break;
}
default:
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_COMPL_VER,
_("Unsupported dump completion version (%#lx) found in section " PV_ELF_SECTION_NAME_COMPL),
version);
return -1;
}
_storage_state_data =
storage_state_mmap_new(fd, storage_state_offset, storage_state_size, error);
if (!_storage_state_data)
return -1;
*dump_key = g_steal_pointer(&_dump_key);
*completion_decr = g_steal_pointer(&_completion_decr);
*storage_state = g_steal_pointer(&_storage_state_data);
return 0;
}
int pv_elf_read(const pv_elf_ctx_t *elf_ctx, const u64 start_addr, void *dst, const u64 size,
GError **error)
{
u64 pglist_size, pglist_start_idx, pglist_end_idx, pglist_num_pages;
const pv_tweak_component_t *pglist_tweak_components;
u64 pglist_start_addr, pglist_end_addr, pglist_elf_off, page_off;
const unsigned char *data = NULL;
g_autoptr(BIO) output = NULL;
gssize num_processed_pages;
long data_size;
u64 end_addr;
/* nothing to do then */
if (size == 0)
return 0;
end_addr = start_addr + size - 1;
if (end_addr < start_addr) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ_END_ADDR_OVERFLOW,
_("UInt Overflow during reading memory detected. start_addr %p, size: %#llx"),
(void *)start_addr, size);
return -1;
}
page_off = page_offset(start_addr);
pglist_start_idx = page_index(start_addr);
pglist_start_addr = page_start_addr(pglist_start_idx, error);
/* Could never happen here */
if (*error)
return -1;
pglist_end_idx = page_index(end_addr);
pglist_end_addr = page_end_addr(pglist_end_idx, error);
if (*error)
return -1;
pglist_size = pglist_end_addr - pglist_start_addr + 1;
pglist_num_pages = pglist_end_idx - pglist_start_idx + 1;
g_assert(IS_ALIGNED(pglist_start_addr, PV_CSS_PAGESIZE));
g_assert(IS_ALIGNED(pglist_end_addr + 1, PV_CSS_PAGESIZE));
g_assert(IS_ALIGNED(pglist_size, PV_CSS_PAGESIZE));
/* must be true, is max UINT64_T/0x1000 + 1 < SSIZE_MAX */
g_assert(pglist_num_pages <= SSIZE_MAX);
g_assert(pglist_num_pages > 0);
pglist_tweak_components = pv_get_tweak_components(elf_ctx->storage_state_data,
pglist_start_idx, pglist_num_pages);
if (!pglist_tweak_components) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
_("Page tweaks were not found idx %llu num %llu"), pglist_start_idx,
pglist_num_pages);
return -1;
}
output = BIO_new(BIO_s_mem());
if (!output)
abort();
if (!u64_checked_add(&pglist_elf_off, elf_ctx->elf_load_off, pglist_start_addr)) {
g_set_error(
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
_("UInt overflow detected: ELF load offset %#llx page start address %#llx"),
elf_ctx->elf_load_off, pglist_start_addr);
return -1;
}
if (pglist_elf_off > LONG_MAX) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
_("ELF load offset is too large"));
return -1;
}
/* Process the pages - in this case do the decryption */
num_processed_pages = pv_process_pglist(elf_ctx->pv_ctx, output, pglist_tweak_components,
pglist_num_pages, (long)pglist_elf_off, error);
if (num_processed_pages < 0)
return -1;
if (num_processed_pages != (gssize)pglist_num_pages) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
_("Processed page count isn't correct"));
return -1;
}
if (BIO_flush(output) != 1) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ, _("BIO_flush failed"));
return -1;
}
data_size = BIO_get_mem_data(output, &data);
if (data_size < 0 || !data) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
_("BIO_get_mem_data failed"));
return -1;
}
if ((u64)data_size != pglist_size) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
_("Decrypting memory failed"));
return -1;
}
/* NOTE previous assertions/overflow checks assure that this can never happen.
* We keep this to be extra sure and protect the following memcpy from
* malicious copying.
*/
if (page_off + size > (u64)data_size) {
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ, _("%s: Illegal state"),
__func__);
return -1;
}
(void)memcpy(dst, data + page_off, size);
return 0;
}

347
zdump/pv_utils.h Normal file
View File

@@ -0,0 +1,347 @@
/*
* zgetdump - Tool for copying and converting System z dumps
*
* Utilities to decrypt secure execution guest dumps.
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef PV_UTILS_H
#define PV_UTILS_H
#include <stdbool.h>
#include <openssl/evp.h>
#include "lib/util_log.h"
#include "libpv/crypto.h"
#include "df_elf.h"
#include "dfi.h"
#include "pv_defs.h"
#include <stdint.h>
#define PV_INVAL_PAGE_STATE 0UL
#define PV_ENCRYPTED_PAGE (1UL << 0)
#define PV_SHARED_PAGE (1UL << 1)
#define PV_ZERO_PAGE (1UL << 2)
#define PV_MAPPED_PAGE (1UL << 3)
#define FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(type) type##_flat_struct_cleanse_free
#define DEFINE_FLAT_STRUCT_CLEANSE_FREE(type, free_func) \
static void FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(type)(type * obj) \
{ \
if (!obj) \
return; \
OPENSSL_cleanse(obj, sizeof(type)); \
free_func(obj); \
}
/* The typedef is required to be able to define a autoptr cleanup
* function using glib2.
*/
typedef struct dfi_cpu dfi_cpu_t;
DEFINE_FLAT_STRUCT_CLEANSE_FREE(dfi_cpu_t, dfi_cpu_free)
DEFINE_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_data_v1_t, g_free)
DEFINE_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_confidential_area_v1_t, g_free)
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(dfi_cpu_t, FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(dfi_cpu_t))
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(
pv_dump_completion_data_v1_t,
FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_data_v1_t))
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(
pv_dump_completion_confidential_area_v1_t,
FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_confidential_area_v1_t))
typedef struct _pv_crypto_ctx pv_crypto_ctx_t;
typedef struct _storage_state_mmap storage_state_mmap_t;
struct _pv_elf_ctx {
BIO *output;
u64 elf_load_off;
pv_crypto_ctx_t *pv_ctx;
storage_state_mmap_t *storage_state_data;
};
typedef struct _pv_elf_ctx pv_elf_ctx_t;
void pv_dump_completion_free(pv_dump_completion_t *cpl);
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_dump_completion_t, pv_dump_completion_free)
/**
* pv_derive_dump_key_v1:
* @cpl_data: v1 dump completion data
* @cck: Customer communication key
* @error: return location for a #GError
*
* Derives an 32 byte AES-256-GCM key using the key derivation function HDKF
* together with the customer communication key (CCK) @cck as input keying
* material, and the seed from the completion configuration dump @cpl_data as salt.
*
* Returns: 32 byte AES-256-GCM key on success, %NULL if an error occurred
*/
GBytes *pv_derive_dump_key_v1(const pv_dump_completion_data_v1_t *cpl_data, GBytes *cck,
GError **error);
/**
* pv_decrypt_dump_completion_v1:
* @cpl_data: v1 dump completion data
* @dump_key: Dump key
* @error: return location for a #GError
*
* Decrypts and authenticates the completion configuration dump data @cpl_data
* using the AES-256-GCM @dump_key.
*
* Returns: Decrypted confidential area of the completion configuration dump on success,
* %NULL if an error occurred
*/
pv_dump_completion_v1_t *pv_decrypt_dump_completion_v1(const pv_dump_completion_data_v1_t *cpl_data,
GBytes *dump_key, GError **error);
dfi_cpu_t *pv_decrypt_cpu_note_data(const unsigned int expected_version, GBytes *cpu_note,
GBytes *dump_key, GError **error);
/**
* pv_decrypt_cpu_dump_area_v1:
* @cpu_dump_area: a v1 CPU dump area
* @dump_key: Dump key
* @error: return location for a #GError
*
* Decrypts and authenticates a secure CPU dump using AES-256-GCM with specified
* dump_key.
*
* Returns: Decrypted part of the CPU dump on success, %NULL if an error occurred
*/
GBytes *pv_decrypt_cpu_dump_area_v1(const pv_cpu_dump_v1_t *cpu_dump_area, GBytes *dump_key,
GError **error);
/**
* pv_dfi_cpu_from_pv_cpu:
* @pv_cpu: PV CPU data
*
* Convert PV CPU data to internal dfi_cpu struct.
*
* Returns: #dfi_cpu struct
*/
dfi_cpu_t *pv_dfi_cpu_from_pv_cpu(const pv_cpu_dump_confidential_area_v1_t *pv_cpu);
/**
* pv_crypto_ctx_new:
* @input: a #BIO input
* @key: (array length=key_size): byte data of the key
* @key_size: size of @key in bytes
* @nonce: The PV tweak nonce
* @mode: encrypt or decrypt mode
* @error: return location for a #GError
*
* Create a PV crypto context using @input as the input, @key as the cipher key
* and @nonce as the nonce for deriving the tweaks.
*
* Returns: a new #pv_crypto_ctx_t
*/
pv_crypto_ctx_t *pv_crypto_ctx_new(BIO *input, const unsigned char *key, size_t key_size,
const pv_tweak_nonce_t *nonce, enum PvCryptoMode mode,
GError **error);
/**
* pv_crypto_ctx_free:
* @ctx: a #pv_crypto_ctx_t
*
* Free all resources and the memory allocated for the #pv_crypto_ctx_t
*/
void pv_crypto_ctx_free(pv_crypto_ctx_t *ctx);
/**
* pv_process_pglist:
* @ctx: a #pv_crypto_ctx_t context
* @output: Output
* @tweaks_components: (array length=tweaks_len) (element-type pv_tweak_component_t): tweaks
* components to be used for the decryption
* @tweaks_components_len: the length of @tweaks and the length of the page list to be processed
* @input_off: the file offset of the encrypted memory passed in @ctx to decrypt
* @error: return location for a #GError
*
* Decrypts a page block (e.g. 256 pages).
*
* Returns: number of processed pages on success, -1 if an error occurred
*/
ssize_t pv_process_pglist(pv_crypto_ctx_t *ctx, BIO *output,
const pv_tweak_component_t *tweak_components, size_t tweak_components_len,
long input_off, GError **error);
/**
* calculate_tweak:
*
* calculates the bitwise or of tweak component and nonce.
*/
void calculate_tweak(const pv_tweak_component_t *tweak, const pv_tweak_nonce_t *nonce,
pv_tweak_t *out);
/**
* pv_read_page:
* @output: Input
* @output: Output
*
* Read one page data from @input and write it to @output.
*
* Returns: The number of bytes read for success, -1 if an error occurred
*/
int pv_read_page(BIO *input, BIO *output, GError **error);
/**
* pv_page_state:
*
* Get the page state of the tweak component.
*/
unsigned long pv_page_state(const pv_tweak_component_t *comp);
/**
* pv_get_tweak_components:
* @storage_state_data: input data
* @page_idx: Page index of the fist page
* @page_cnt: number of tweak components that can be at least accessed
*
* get a pointer to the page states of the given pages.
*
* Returns: pointer to page states or NULL in case of an error
*
*/
const pv_tweak_component_t *pv_get_tweak_components(storage_state_mmap_t *storage_state_data,
u64 page_idx, u64 page_cnt);
/**
* pv_elf_ctx_new:
* fd: file descriptor of elf file
* @completion_confidential_decr: content of ection pv_meta size must be
* sizeof(pv_dump_completion_confidential_area_t)
* @storage_state_data: mmap to pv_mem_meta
* @load_offset: offset of LOAD segment in fd
* @error: return value for GError
*
* creates a new elf context for processing the elf file
*
* Returns: new elf context or NULL in case of error
*/
pv_elf_ctx_t *
pv_elf_ctx_new(const int fd,
const pv_dump_completion_confidential_area_v1_t *completion_confidential_decr,
storage_state_mmap_t *storage_state_data, const u64 load_offset, GError **error);
/**
* pv_info_free:
* frees the elf context
*/
void pv_elf_ctx_free(pv_elf_ctx_t *p);
/**
* pv_is_pv_elf:
* @shdrs: list of section headers
* @shnum: size of shdrs list
* @shstrtab: content of shstrtab (section hdr string table)
* @shstrtab_size: size of shstrtab
*
* Returns: true in case it is a PV ELF vmcore dump, false otherwise.
*/
bool pv_is_pv_elf(const Elf64_Shdr *shdrs, const unsigned int shnum, const char *shstrtab,
const size_t shstrtab_size);
/**
* pv_process_section_data:
*
* Uses the ELF sections data to derive dump key, using the provided
* customer-communication-key. It copies the decrypted completion
* data into @completion_decr and mmaps storage state tweaks to storage
* state data.
*
* The caller is responsible for cleanup and overwriting.
*
* Returns: 0 in case of success, -1 otherwise.
*/
int pv_process_section_data(const int fd, GBytes *completion_sec, const u64 storage_state_offset,
const size_t storage_state_size, GBytes *cck,
pv_dump_completion_t **completion_decr, GBytes **dump_key,
storage_state_mmap_t **storage_state_data, GError **error);
/**
* pv_elf_read:
*
* elf_ctx: elf context
* @start_addr: first address to read from
* @dst: target
* @size: num bytes to read
* @error: return value for GError
*
* Read size bytes from start addr in elf_ctx and save it into dest.
* Will decrypt pages if defined by page state.
*
* Returns: 0 in case of success, -1 otherwise.
*/
int pv_elf_read(const pv_elf_ctx_t *elf_ctx, const u64 start_addr, void *dst, const u64 size,
GError **error);
/**
* storage_state_mmap_new:
* @fd: file descriptor for which to create the mapping
* @offset: offset in fd to pv_mem_meta section
* @size: size of mapping (and pv_mem_meta section)
* @error: return value for GError
*
* Returns: new storage_state_mmap context
*/
storage_state_mmap_t *storage_state_mmap_new(const int fd, const u64 offset, const u64 size,
GError **error);
/**
* storage_state_ref:
*
* increase the reference counter by one.
*
* Returns: pointer to storage_state
*/
storage_state_mmap_t *storage_state_mmap_ref(storage_state_mmap_t *storage_state);
/**
* storage_state_unref:
*
* Decreases the reference counter by one.
* If the counter is now zero storage_state will be unmapped and freed.
* If the munmap fails, an error will be logged.
*/
void storage_state_mmap_unref(storage_state_mmap_t *storage_state);
#define ZDUMP_PV_UTILS_ERROR g_quark_from_static_string("zdump-pv-utils")
typedef enum {
ZDUMP_ERR_BIO,
ZDUMP_ERR_BIO_FAIL,
ZDUMP_ERR_BIO_KEY,
ZDUMP_ERR_CORRUPTED_COMPL_DATA,
ZDUMP_ERR_CORRUPTED_NOTE,
ZDUMP_ERR_CRYPTO_CTX,
ZDUMP_ERR_ELF_CTX,
ZDUMP_ERR_ELF_CTX_BIO,
ZDUMP_ERR_ELF_READ,
ZDUMP_ERR_ELF_READ_END_ADDR_OVERFLOW,
ZDUMP_ERR_MMAP,
ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
ZDUMP_ERR_PAGE_END_ADDR_OVERFLOW,
ZDUMP_ERR_PAGE_START_ADDR_OVERFLOW,
ZDUMP_ERR_PGLIST_BIO,
ZDUMP_ERR_PGLIST_INVAL,
ZDUMP_ERR_PGLIST_INVAL_STATE,
ZDUMP_ERR_PROC_SECT,
ZDUMP_ERR_TWEAK,
ZDUMP_ERR_UNSUPP_COMPL_VER,
ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
ZDUMP_ERR_WRONG_AAD_SIZE,
ZDUMP_ERR_WRONG_CCK_SIZE,
ZDUMP_ERR_WRONG_CONFIDENTIAL_SIZE,
ZDUMP_ERR_WRONG_NOTE_SIZE,
ZDUMP_ERR_WRONG_TAG_SIZE,
} zdump_pv_utils_error_e;
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_crypto_ctx_t, pv_crypto_ctx_free)
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_elf_ctx_t, pv_elf_ctx_free)
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(storage_state_mmap_t, storage_state_mmap_unref)
#endif /* PV_UTILS_H */

View File

@@ -88,7 +88,7 @@ void __noreturn zg_exit(int rc)
/*
* Alloc memory and check for errors
*/
void *zg_alloc(unsigned int size)
void *zg_alloc(size_t size)
{
void *ptr = calloc(size, 1);
if (!ptr)
@@ -99,7 +99,7 @@ void *zg_alloc(unsigned int size)
/*
* Realloc memory and check for errors
*/
void *zg_realloc(void *ptr, unsigned int size)
void *zg_realloc(void *ptr, size_t size)
{
void *new_ptr = realloc(ptr, size);
if (!new_ptr)
@@ -130,7 +130,7 @@ void zg_free(void *ptr)
/*
* Return path name of open file
*/
const char *zg_path(struct zg_fh *zg_fh)
const char *zg_path(const struct zg_fh *zg_fh)
{
return zg_fh->path;
}
@@ -138,7 +138,7 @@ const char *zg_path(struct zg_fh *zg_fh)
/*
* Return stat buffer of open file
*/
const struct stat *zg_stat(struct zg_fh *zg_fh)
const struct stat *zg_stat(const struct zg_fh *zg_fh)
{
return &zg_fh->sb;
}
@@ -183,13 +183,14 @@ fail:
void zg_close(struct zg_fh *zg_fh)
{
close(zg_fh->fh);
free((void *)zg_fh->path);
free(zg_fh);
}
/*
* Read file
*/
ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
ssize_t zg_read(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
{
size_t copied = 0;
ssize_t rc;
@@ -215,7 +216,7 @@ ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
/*
* Read line
*/
ssize_t zg_gets(struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
ssize_t zg_gets(const struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
{
size_t copied = 0;
char *buf = ptr;
@@ -241,7 +242,7 @@ ssize_t zg_gets(struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
/*
* Return file size
*/
u64 zg_size(struct zg_fh *zg_fh)
u64 zg_size(const struct zg_fh *zg_fh)
{
return zg_fh->sb.st_size;
}
@@ -249,7 +250,7 @@ u64 zg_size(struct zg_fh *zg_fh)
/*
* Return file position
*/
off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check)
off_t zg_tell(const struct zg_fh *zg_fh, enum zg_check check)
{
off_t rc;
@@ -263,7 +264,7 @@ off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check)
/*
* Seek to "off" relative to END
*/
off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check)
off_t zg_seek_end(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
{
off_t rc;
@@ -276,7 +277,7 @@ off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check)
/*
* Seek to "off" in file
*/
off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
off_t zg_seek(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
{
off_t rc;
@@ -294,7 +295,7 @@ off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
/*
* Seek from current position
*/
off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check)
off_t zg_seek_cur(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
{
off_t rc;
@@ -307,7 +308,7 @@ off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check)
/*
* Do ioctl and exit in case of an error
*/
int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
int zg_ioctl(const struct zg_fh *zg_fh, unsigned long rq, void *data, const char *op,
enum zg_check check)
{
int rc;
@@ -322,10 +323,10 @@ int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
/*
* Return file type
*/
enum zg_type zg_type(struct zg_fh *zg_fh)
enum zg_type zg_type(const struct zg_fh *zg_fh)
{
struct mtop mtop;
struct stat *sb = &zg_fh->sb;
const struct stat *sb = &zg_fh->sb;
if (S_ISREG(sb->st_mode))
return ZG_TYPE_FILE;

View File

@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
@@ -24,6 +25,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "boot/s390.h"
#include "lib/util_base.h"
#include "lib/zt_common.h"
@@ -31,6 +33,8 @@
#define U32_MAX ((u32) -1)
#define U16_MAX ((u16) -1)
#define U8_MAX ((u8) -1)
#define OFF_T_MAX LONG_MAX
STATIC_ASSERT(sizeof(off_t) == sizeof(long))
/*
* IEC definitions
@@ -45,8 +49,8 @@
/*
* Memory functions
*/
void *zg_alloc(unsigned int size);
void *zg_realloc(void *ptr, unsigned int size);
void *zg_alloc(size_t size);
void *zg_realloc(void *ptr, size_t size);
void zg_free(void *ptr);
char *zg_strdup(const char *str);
@@ -81,6 +85,7 @@ void zg_abort(const char *fmt, ...);
#define ERR_EXIT(fmt, ...) zg_err_exit(fmt, ## __VA_ARGS__)
#define ERR_EXIT_ERRNO(fmt, ...) zg_err_exit_errno(fmt, ## __VA_ARGS__)
#define ABORT(fmt, ...) zg_abort(fmt, ## __VA_ARGS__)
#define ERR_NEWLINE "\n "
void zg_stderr(const char *fmt, ...);
void zg_stderr_pr(const char *fmt, ...);
@@ -92,9 +97,9 @@ void zg_stdout(const char *fmt, ...);
/*
* Misc
*/
#define PAGE_SIZE 4096UL
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define IS_ALIGNED(addr, size) (!((addr) & (size - 1)))
#define IS_PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)(addr), PAGE_SIZE)
static inline u32 zg_csum_partial(const void *buf, int len, u32 sum)
{
@@ -138,22 +143,20 @@ enum zg_check {
ZG_CHECK_NONE,
};
const char *zg_path(struct zg_fh *zg_fh);
const struct stat *zg_stat(struct zg_fh *zg_fh);
const char *zg_path(const struct zg_fh *zg_fh);
const struct stat *zg_stat(const struct zg_fh *zg_fh);
struct zg_fh *zg_open(const char *path, int flags, enum zg_check check);
void zg_close(struct zg_fh *zg_fh);
ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt,
enum zg_check check);
ssize_t zg_gets(struct zg_fh *zg_fh, void *buf, size_t cnt,
enum zg_check check);
u64 zg_size(struct zg_fh *zg_fh);
off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check);
off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check);
off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check);
off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check);
int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
ssize_t zg_read(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check);
ssize_t zg_gets(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check);
u64 zg_size(const struct zg_fh *zg_fh);
off_t zg_tell(const struct zg_fh *zg_fh, enum zg_check check);
off_t zg_seek(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
off_t zg_seek_end(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
off_t zg_seek_cur(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
int zg_ioctl(const struct zg_fh *zg_fh, unsigned long rq, void *data, const char *op,
enum zg_check check);
enum zg_type zg_type(struct zg_fh *zg_fh);
enum zg_type zg_type(const struct zg_fh *zg_fh);
/*
* zgetdump actions

View File

@@ -1,19 +1,19 @@
.\" Copyright 2018 IBM Corp.
.\" Copyright 2022 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH ZGETDUMP 8 "April 2012" "s390-tools"
.TH ZGETDUMP 8 "October 2022" "s390-tools"
.SH NAME
zgetdump \- Tool for copying and converting System z dumps
.SH SYNOPSIS
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] > DUMP_FILE
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE
.br
DUMP [-s SYS] [-f FMT] DUMP_FILE
DUMP [-s SYS] [-f FMT] [-k KEY] DUMP_FILE
.br
-m DUMP [-s SYS] [-f FMT] DIR
-m DUMP [-s SYS] [-f FMT] [-k KEY] DIR
.br
-i DUMP [-s SYS]
-i DUMP [-s SYS] [-k KEY]
.br
-d DUMPDEV
.br
@@ -37,6 +37,10 @@ Print usage information, then exit.
.BR "\-v" " or " "\-\-version"
Print version information, then exit.
.TP
.BR "\-k <KEY>" " or " "\-\-key <KEY>"
Specify the file KEY to use as key for the decryption of a protected virtualization dump.
.TP
.BR "\-m <DUMP> <DIR>" " or " "\-\-mount <DUMP> <DIR>"
Mount the source dump DUMP to mount point DIR and generate a virtual target
@@ -73,7 +77,7 @@ Use the specified target dump format FMT when writing or mounting the dump.
The following target dump formats are supported:
.BR "- elf:"
Executable and Linking Format core dump (default)
Executable and Linkable Format core dump (default)
.BR "- s390:"
s390 dump
@@ -115,6 +119,8 @@ NVMe partition device node (e.g. /dev/nvme0n1p1)
Tape device node (e.g. /dev/ntibm0)
.IP " -" 12
Device node for live system (/dev/mem or /dev/crash)
.IP " -" 12
Protected virtualization dump file (e.g. /dumps/pv.elf)
.PP
.IP " " 8
Note: For DASD multi-volume dump it is sufficient to specify only one of the
@@ -173,9 +179,12 @@ change the target format. The following dump formats are supported for
target and source dump:
.TP
.BR "elf"
Executable and Linking Format core dump. This dump format is also used for
Executable and Linkable Format core dump. This dump format is also used for
Linux user space core dumps.
.TP
.BR "ELF (protected virtualization dump):"
Protected virtualization vmcore dump in Executable and Linkable Format
.TP
.BR "s390"
This dump format is System z specific and is used for DASD and tape dumps.
.TP

View File

@@ -26,6 +26,8 @@
#include <time.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "opts.h"
#include "zgetdump.h"
#include "dt.h"
#include "dfi.h"
@@ -105,6 +107,20 @@ static void kdump_select_check(void)
ERR_EXIT("%s", msg);
}
static int dfi_init_error(int rc)
{
if (rc == -ENOKEY) {
if (g.opts.key_path)
ERR("Cannot read key: '%s'", g.opts.key_path);
else
ERR("No key for dump decryption provided.");
opts_print_usage(g.opts.prog_name);
} else {
STDERR("Dump cannot be processed (is not complete)\n");
}
return 1;
}
/*
* Run "--umount" action
*/
@@ -129,10 +145,13 @@ static int do_device_info(void)
*/
static int do_dump_info(void)
{
if (dfi_init() != 0) {
int rc;
rc = dfi_init();
if (rc != 0) {
dfi_info_print();
STDERR("\nERROR: Dump is not complete\n");
zg_exit(1);
STDERR("\n");
return dfi_init_error(rc);
}
kdump_select_check();
dfi_info_print();
@@ -147,8 +166,9 @@ static int do_mount(void)
{
int rc;
if (dfi_init() != 0)
ERR_EXIT("Dump cannot be processed (is not complete)");
rc = dfi_init();
if (rc != 0)
return dfi_init_error(rc);
dfo_init();
kdump_select_check();
rc = zfuse_mount_dump();
@@ -189,8 +209,9 @@ static int do_copy(const char *output)
FILE *stream;
int rc;
if (dfi_init() != 0)
ERR_EXIT("Dump cannot be processed (is not complete)");
rc = dfi_init();
if (rc != 0)
return dfi_init_error(rc);
dfo_init();
kdump_select_check();
stream = open_file_for_writing(output);

View File

@@ -106,7 +106,7 @@ stage3.bin: stage3.exec
--only-section=.stage2dump.tail \
--only-section=.eckd2dump_mv.tail \
--only-section=.fixup \
--pad-to=0xe000 \
--only-section=.sb.trailer \
$< $@
data.o: $(FILES)

View File

@@ -14,6 +14,7 @@
*/
#include "boot/loaders_layout.h"
#include "boot/s390.h"
SECTIONS
{
@@ -46,6 +47,15 @@ SECTIONS
.rodata : {*(.rodata) }
.data : { *(.data) }
/* Trailer needed for Secure Boot */
. = COMMAND_LINE_EXTRA - 32;
.sb.trailer : {
QUAD(0x0000c00000000000)
QUAD(STAGE3_ENTRY + PSW_LOAD)
QUAD(STAGE3_ENTRY)
QUAD(0x000000207a49504c)
}
. = COMMAND_LINE_EXTRA;
.cmdline_extra : {
. += COMMAND_LINE_EXTRA_SIZE;

View File

@@ -54,6 +54,5 @@ void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to);
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to);
unsigned int misc_check_secure_boot(void);
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#endif /* not MISC_H */

View File

@@ -40,6 +40,9 @@
/* Pointer to dedicated empty block in bootmap. */
static disk_blockptr_t empty_block;
/* State of secure boot in the system */
static bool secure_boot_supported;
/* Get size of a bootmap block pointer for disk with given INFO. */
static int
get_blockptr_size(struct disk_info* info)
@@ -128,16 +131,27 @@ check_secure_boot_support(void)
unsigned int val;
FILE *fp;
if (verbose)
printf("Secure boot support: ");
fp = fopen(ZIPL_SIPL_PATH, "r");
if (!fp)
if (!fp) {
if (verbose)
printf("not available\n");
return false;
}
if (fscanf(fp, "%d", &val) != 1) {
if (verbose)
printf("error\n");
fclose(fp);
return false;
}
fclose(fp);
if (verbose)
printf("%s\n", val ? "yes" : "no");
return val ? true : false;
}
@@ -508,7 +522,6 @@ static int add_ipl_program(int fd, char *filename,
struct component_loc comp_loc[10];
struct signature_header sig_head;
size_t ramdisk_size, image_size;
bool secure_boot_supported;
size_t stage3_params_size;
const char *comp_name[11];
size_t signature_size;
@@ -597,7 +610,6 @@ static int add_ipl_program(int fd, char *filename,
return -1;
}
image_size = stats.st_size;
secure_boot_supported = check_secure_boot_support();
signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature,
&sig_head);
if (signature_size &&
@@ -1637,6 +1649,8 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count,
char **new_device, struct disk_info **new_info)
{
secure_boot_supported = check_secure_boot_support();
if (job->id == job_dump_partition) {
if (is_ngdump_enabled(job->data.dump.device, &job->target))
return bootmap_create_device_ngdump(job, program_table,

View File

@@ -611,9 +611,6 @@ static struct util_list *get_multipath_status(const char *devname)
}
}
free(line);
pclose(fp);
len = util_list_len(status);
if (len == 0) {
ERR("No paths found for '%s'\n", devname);
@@ -625,15 +622,14 @@ static struct util_list *get_multipath_status(const char *devname)
WARN("There are one or more failed paths for device '%s'\n",
devname);
}
return status;
out:
goto success;
out:
status_list_free(status);
status = NULL;
success:
free(line);
pclose(fp);
status_list_free(status);
return NULL;
return status;
}
static struct util_list *get_multipath_data(const char *devname, char *args)