Compare commits

...

59 Commits

Author SHA1 Message Date
Jan Höppner
d93645d690 New release s390-tools-2.18.0
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 17:45:29 +02:00
Jan Höppner
c3bb6307b8 gitignore: Add zdev_id and zipl-editenv
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 15:44:16 +02:00
Steffen Maier
6208c0626a dbginfo.sh: collect udev rules from DPM device auto-configuration
Complements v2.5.0 commit fe68ec513d
("zdev: Add support for handling auto-configuration data").

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Peter Oberparleiter
3949c62f6f zdev: Add auto-config for PCI and crypto devices
PCI and crypto devices defined using the IBM Z Dynamic Partition Manager
(DPM) will start in an offline/unconfigured state that requires manual
intervention before the associated Linux function can be used. This
results for example in PCI networking interfaces being unavailable in a
distribution installer system.

Fix this by providing a udev rule and initial RAM-disk logic that
automatically enables PCI and crypto devices either during boot, or when
they are defined at run-time. This processing can be suppressed by
specifying the "rd.zdev=no-auto" parameter on the kernel command line.

Auto-configuration is limited to Linux running in DPM LPARs because
PCI-functions and crypto devices defined by DPM are always intended for
use by a single LPAR only.

For Linux running in classic-mode LPARs or virtual machines, leaving PCI
and crypto devices in an offline state may be useful to allow defining a
device as available to multiple systems, where only the system that
intends to make use of the device should enable it.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Peter Oberparleiter
8c6c2ef30e zdev: Add udev rule helper tool
Add an internal tool intended to be used by udev rules to determine
zdev and hypervisor related system information in key=value format.

Supported keys are:

  ZDEV_NEST_LEVEL=n
    Virtualization nesting level of running system

  ZDEV_HYPERVISOR_<n>=LPAR|z/VM|KVM/Linux
    Type of hypervisor that provides virtualization at nesting level <n>

  ZDEV_IS_DPM=0|1
    Indicator if top-level LPAR is managed by Dynamic Partition Manager

  ZDEV_NO_AUTO=0|1
    Indicator if auto-configuration is requested

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Joern Siglen
12c84469fd dbginfo.sh: exclude reading page_idle/bitmap sysfs attribute
Due to issues with reading the /sys/kernel/mm/page_idle/bitmap sysfs
attribute that can lead to a system hang, reading this particular
attribute is excluded.

Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1904884
Reviewed-by: Mario Held <mario.held@linux.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Marc Hartmayer
0a8e726d05 zgetdump: return -EINVAL for user space core dumps
zgetdump is for analyzing VM coredumps and not user space coredumps therefore
return an error in case of a user space coredump.

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Peter Oberparleiter
533df585fd scripts: Add tool for parsing sclp s390dbf logs
Add new tool named 'sclpdbf'. This script displays the contents of
s390dbf sclp debug logs in human-readable format.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
c4eb2d5fba zipl-editenv: added a manpage for the new zipl-editenv tool
update manpages of zipl(8), zipl.conf(5) w/ zipl environment stuff

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
35029ad010 zipl: add environment editor
Added a tool to operate with zIPL environment, currently
installed in the boot record. All modifying operations result
in atomic update of the installed environment block, bypassing
the file system driver.

Operations currently supported by zIPL environment editor:
. set/unset environment variables,
. print/reset environment

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
296079f70a zipl: move logical-to-physical block mapping logic
from disk.c to a dedicated source file fs-map.c, so that the new
zipl-editenv tool will be also able to use it.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
4f1c73d592 zipl: add environment interpretation by stage3 of boot
Before starting the kernel:

1. parse environment block (if any) and create a hash table
   of all found pairs (NAME, VALUE);
2. scan the command line, which already contains extra-parameters
   (if any), and for each found ${NAME} replace it with VALUE, as
   found in the hash table by NAME.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
1fb859729e zipl: add basic support of environment block by zipl(8)
When installing a boot record for "ipl" and "menu" job, always add
an environment block as a boot component located in bootmap file
at offset alinged on file system block size boundary. When adding,
first try to import environment from a file at location specified
by newly introduced "--environment" zipl option, or by default at
"/etc/ziplenv". If nothing was imported, then add a blank environment
block. Optionally print the content of the environment block.

Store environment block size and address (as of boot component) in
stage3_parms.

Change interface of add_ipl_program(): add 2 additional arguments:
a pointer to bootmap file name and a predicate indicating if we
need to add environment block as a boot component.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
bbeb0f8445 zipl: expand interface of disk_write_block_aligned_base()
Provide helper functions disk_write_block_buffer_align()
and add_component_buffer_align() to align offset of the record
on specified block size boundary and to save the offset, where
the record was made at.

This will be used by a later patch.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Eduard Shishkin
2ae44cb794 zipl: expand interface of disk_get_blocklist_from_file
. expand interface of disk_get_blocklist_from_file() to get
  block pointers for a specific range of data within a file.
. provide a helper function add_component_file_range() to add a
  specific range of data within a file as a boot component.

This will be used by a later patch.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Marc Hartmayer
a0b8033088 zdump: dfi_mem_range_valid: check that unsigned int operation don't wrap
Validate that the addition of the parameters @addr and @len given to
`dfi_mem_range_valid()` does not overflow 64bit unsigned integer type.

This fixes the following segmentation fault:

[#0] 0x2aa000084fc → mem_read(mem=0x2aa00021b68 <l+152>, addr=0xffffffffffffffff, buf=0x3ffffffec64, cnt=0xc)
[#1] 0x2aa00009964 → dfi_mem_read(addr=0xfffffffffffffffa, buf=0x3ffffffec64, cnt=0xc)
[#2] 0x2aa00009c86 → dfi_mem_read_rc(addr=0xfffffffffffffffa, buf=0x3ffffffec64, cnt=0xc)
[#3] 0x2aa0000ba42 → dfi_vmcoreinfo_init()
[#4] 0x2aa0000b496 → dfi_init()
[#5] 0x2aa00005aa6 → do_dump_info()
[#6] 0x2aa00005c82 → main(argc=<optimized out>, argv=0x3fffffff118)

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Marc Hartmayer
9cc74839b0 zgetdump: initalize sigact to 0
This fixes the following Valgrind finding:

==28040== Syscall param rt_sigaction(act->sa_flags) points to uninitialised byte(s)
==28040==    at 0x48C98B0: __libc_sigaction (sigaction.c:58)
==28040==    by 0x10D507: sig_handler_init (zgetdump.c:58)
==28040==    by 0x10D507: main (zgetdump.c:179)
==28040==  Address 0x1ffefffc84 is on thread 1's stack
==28040==  in frame #0, created by __libc_sigaction (sigaction.c:43)

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Marc Hartmayer
b7b7002855 gitignore: Ignore coverage data
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Peter Oberparleiter
55e428a4d7 dump2tar: Fix (null) output on help text
Fix the following incorrect help text line:

 -T, --file-timeout (null)  Stop reading file after SEC seconds

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Alexander Egorenkov
cb09223a56 zdump/dfi_elf: Fix stack buffer overflow in nt_read()
Sanity check ELF notes descriptor size before reading it. This prevents
stack buffer overflows in case a dump contains invalid ELF notes.
Instead of reading a note's descriptor into a temporary buffer on stack,
read it directly into the buffer given to nt_read() but also provide
a maximum length of the given buffer to nt_read() in order to prevent
overflows.

This problem was found with valgrind and AFL fuzzing + ASAN.

AFL +  ASAN findings:

[root@t83lp49 s390-tools]# ./zdump/zgetdump -iVVVVV ~/zgetdump-fuzzing/findings/crashes/id\:000008\,sig\:06\,src\:000007\,op\:arith8\,pos\:67\,val\:+3
TRACE: DFI initialization
DEBUG: DFI trying s390tape
DEBUG: DFI s390tape returned with rc -19
DEBUG: DFI trying devmem
DEBUG: DFI devmem returned with rc -19
DEBUG: DFI trying s390mv_ext
DEBUG: DFI s390mv_ext returned with rc -19
DEBUG: DFI trying s390mv
DEBUG: DFI s390mv returned with rc -19
DEBUG: DFI trying s390_ext
DEBUG: DFI S390 extended initialization
DEBUG: DFI s390_ext returned with rc -19
DEBUG: DFI trying s390
DEBUG: DFI S390 initialization
DEBUG: DFI s390 returned with rc -19
DEBUG: DFI trying lkcd
DEBUG: DFI lkcd returned with rc -19
DEBUG: DFI trying elf
DEBUG: DFI ELF initialization
DEBUG: DFI ELF e_phnum 11
DEBUG: DFI ELF p_type[0] 0x4
DEBUG: DFI ELF n_type 0x0
AddressSanitizer:DEADLYSIGNAL
=================================================================
==208548==ERROR: AddressSanitizer: stack-overflow on address 0x03ffef05d000 (pc 0x0000010051b0 bp 0x03fff107dc40 sp 0x03ffef05dac8 T0)
    #0 0x10051b0  (/root/s390-tools/zdump/zgetdump+0x10051b0)

SUMMARY: AddressSanitizer: stack-overflow (/root/s390-tools/zdump/zgetdump+0x10051b0)
==208548==ABORTING

valgrind findings:

	==56423== Source and destination overlap in memcpy(0x4a86d38, 0x4a875e0, 4096)
	==56423==    at 0x4839F86: memcpy (in /usr/lib/s390x-linux-gnu/valgrind/vgpreload_memcheck-s390x-linux.so)
	==56423==    by 0x114253: memcpy (string_fortified.h:34)
	==56423==    by 0x114253: nt_read (dfi_elf.c:95)
	==56423==    by 0x1145FF: nt_s390_prefix_read (dfi_elf.c:195)
	==56423==    by 0x1145FF: pt_notes_add (dfi_elf.c:259)
	==56423==    by 0x1145FF: dfi_elf_init (dfi_elf.c:326)
	==56423==    by 0x112A57: dfi_init (dfi.c:1212)
	==56423==    by 0x10D663: do_dump_info (zgetdump.c:127)
	==56423==    by 0x10D663: main (zgetdump.c:182)
	==56423==
	==56423== Invalid write of size 8
	==56423==    at 0x4839E28: memcpy (in /usr/lib/s390x-linux-gnu/valgrind/vgpreload_memcheck-s390x-linux.so)
	==56423==    by 0x114253: memcpy (string_fortified.h:34)
	==56423==    by 0x114253: nt_read (dfi_elf.c:95)
	==56423==    by 0x1145FF: nt_s390_prefix_read (dfi_elf.c:195)
	==56423==    by 0x1145FF: pt_notes_add (dfi_elf.c:259)
	==56423==    by 0x1145FF: dfi_elf_init (dfi_elf.c:326)
	==56423==    by 0x112A57: dfi_init (dfi.c:1212)
	==56423==    by 0x10D663: do_dump_info (zgetdump.c:127)
	==56423==    by 0x10D663: main (zgetdump.c:182)
	==56423==  Address 0x4a86ee0 is 0 bytes after a block of size 912 alloc'd
	==56423==    at 0x483675E: calloc (in /usr/lib/s390x-linux-gnu/valgrind/vgpreload_memcheck-s390x-linux.so)
	==56423==    by 0x10E71D: zg_alloc (zg.c:93)
	==56423==    by 0x114793: nt_prstatus_read (dfi_elf.c:123)
	==56423==    by 0x114793: pt_notes_add (dfi_elf.c:234)
	==56423==    by 0x114793: dfi_elf_init (dfi_elf.c:326)
	==56423==    by 0x112A57: dfi_init (dfi.c:1212)
	==56423==    by 0x10D663: do_dump_info (zgetdump.c:127)
	==56423==    by 0x10D663: main (zgetdump.c:182)

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Suggested-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reported-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Alexander Egorenkov
28db3523d0 zdump/dfi: Fix illegal memory access in mem_chunk_has_addr()
Verify that the mem chunk_cache pointer is valid before using it.
This prevents potential illegal memory accesses.

This problem was found with AFL fuzzing and ASAN.

./zdump/zgetdump -iVVVVV ~/zgetdump-fuzzing/findings/crashes/id\:000007\,sig\:06\,src\:000007\,op\:flip1\,pos\:37
TRACE: DFI initialization
DEBUG: DFI trying s390tape
DEBUG: DFI s390tape returned with rc -19
DEBUG: DFI trying devmem
DEBUG: DFI devmem returned with rc -19
DEBUG: DFI trying s390mv_ext
DEBUG: DFI s390mv_ext returned with rc -19
DEBUG: DFI trying s390mv
DEBUG: DFI s390mv returned with rc -19
DEBUG: DFI trying s390_ext
DEBUG: DFI S390 extended initialization
DEBUG: DFI s390_ext returned with rc -19
DEBUG: DFI trying s390
DEBUG: DFI S390 initialization
DEBUG: DFI s390 returned with rc -19
DEBUG: DFI trying lkcd
DEBUG: DFI lkcd returned with rc -19
DEBUG: DFI trying elf
DEBUG: DFI ELF initialization
DEBUG: DFI ELF e_phnum 11
DEBUG: DFI ELF p_type[0] 0x6060606
DEBUG: DFI ELF p_type[1] 0x6060606
DEBUG: DFI ELF p_type[2] 0x6060606
DEBUG: DFI ELF p_type[3] 0x6060606
DEBUG: DFI ELF p_type[4] 0x6060606
DEBUG: DFI ELF p_type[5] 0x6060606
DEBUG: DFI ELF p_type[6] 0x6060606
DEBUG: DFI ELF p_type[7] 0x6060606
DEBUG: DFI ELF p_type[8] 0x6060606
DEBUG: DFI ELF p_type[9] 0x6060606
DEBUG: DFI ELF p_type[10] 0x6060606
TRACE: DFI kdump initialization
AddressSanitizer:DEADLYSIGNAL
=================================================================
==206692==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000001016a12 bp 0x03ffcc57eae0 sp 0x03ffcc57eae0 T0)
==206692==The signal is caused by a UNKNOWN memory access.
==206692==Hint: address points to the zero page.
    #0 0x1016a12 in mem_chunk_has_addr /root/s390-tools/zdump/dfi.c:308
    #1 0x1016a12 in mem_chunk_find /root/s390-tools/zdump/dfi.c:318
    #2 0x1016a12 in dfi_mem_chunk_find /root/s390-tools/zdump/dfi.c:513
    #3 0x1016a12 in dfi_mem_range_valid /root/s390-tools/zdump/dfi.c:208
    #4 0x1016a12 in kdump_init /root/s390-tools/zdump/dfi.c:1100
    #5 0x1016a12 in dfi_init /root/s390-tools/zdump/dfi.c:1253
    #6 0x1006d3d in do_dump_info /root/s390-tools/zdump/zgetdump.c:127
    #7 0x1006d3d in main /root/s390-tools/zdump/zgetdump.c:182
    #8 0x3ff9e0abe03 in __libc_start_main (/lib64/libc.so.6+0x2be03)
    #9 0x1007d7d  (/root/s390-tools/zdump/zgetdump+0x1007d7d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /root/s390-tools/zdump/dfi.c:308 in mem_chunk_has_addr
==206692==ABORTING

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Alexander Egorenkov
f15e32cdb1 zdump/dfi: Fix illegal memory access in dfi_cpu_add_from_lc()
Check that a CPU's lowcore address falls within a valid memory region
before accessing it. This prevents potential illegal memory accesses
in case a dump contains invalid CPU lowcore addresses.

This bug was found with AFL fuzzing and ASAN.

Starting program: /root/s390-tools/zdump/zgetdump -iVVVVV /root/zgetdump-fuzzing/findings/crashes/id:000004,sig:06,src:000005,op:flip32,pos:3055
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
TRACE: DFI initialization
DEBUG: DFI trying s390tape
DEBUG: DFI s390tape returned with rc -19
DEBUG: DFI trying devmem
DEBUG: DFI devmem returned with rc -19
DEBUG: DFI trying s390mv_ext
DEBUG: DFI s390mv_ext returned with rc -19
DEBUG: DFI trying s390mv
DEBUG: DFI s390mv returned with rc -19
DEBUG: DFI trying s390_ext
DEBUG: DFI S390 extended initialization
DEBUG: DFI s390_ext returned with rc -19
DEBUG: DFI trying s390
DEBUG: DFI S390 initialization
 INFO: DFI S390 version 5
DEBUG: DFI S390 mem_size 0x00000000000dbba0
DEBUG: DFI add vol mem chunk start 0x0000000000000000 size 0x00000000000dbba0 volnr 0

[snip]

TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x00000000000000ff size 0x0000000000002000
TRACE: DFI virt mem read addr 0x00000000ffffff00 size 0x0000000000002000

Program received signal SIGSEGV, Segmentation fault.
0x000000000100fbb0 in mem_read (cnt=<optimized out>, buf=0x3ffffffc7d0, addr=4294967040, mem=0x104b218 <l+152>) at dfi.c:339
339                     size = MIN(cnt - copied, mem_chunk->end - addr + 1);
Missing separate debuginfos, use: dnf debuginfo-install fuse-libs-2.9.9-11.fc34.s390x glibc-2.33-20.1.ibm.fc34.s390x libasan-11.0.1-0.3.1.ibm.fc34.s390x libgcc-11.0.1-0.3.1.ibm.fc34.s390x libstdc++-11.0.1-0.3.1.ibm.fc34.s390x zlib-1.2.11-26.fc34.s390x
(gdb) bt

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00
Steffen Maier
094057e265 zfcpdbf: fix missing "FCP rsp IU all" fields if PAY record wrapped away
Had a confusing decoded trace record:

Timestamp      : ...
Area           : SCSI
...
Tag            : rsl_err
...
SCSI result    : 0x00000002
SCSI opcode    : 1a...
FCP rsp inf cod: 0x00
FCP rsp IU     : 00000000 00000000 00000a02 000000ff
                 00000012 00000000
                       ^^

and was wondering where that sense data would be or if the
corresponding PAY record "fcp_riu" was lost due to trace area wrapping.
So the fix here adds the following two lines to the same trace record:

FCP rsp IU len : 42
FCP rsp IU all : record not available anymore.

Similarly, the following LUN reset Task Management Function response
was missing the last two lines regarding the full FCP response IU
and its optional part with the FCP response info:

Timestamp      : ...
Area           : SCSI
...
Tag            : lr_okay
...
FCP rsp inf cod: 0x00
FCP rsp IU     : 00000000 00000000 00000100 00000000
                 00000000 00000008
                                ^^
FCP rsp IU len : 32
FCP rsp IU all : record not available anymore.

As of today, depending on the kernel version we have
either fcp_sns or fcp_riu PAY trace records. See Linux kernel v4.14 commit
12c3e5754c80 ("scsi: zfcp: fix payload with full FCP_RSP IU in SCSI trace
records").
Always print non-zero payload length independent of payload type.
Actually this also fixes a regression for wrapped away fcp_sns.
If there is no fcp_sns it either wrapped away or it's a kernel with fcp_riu.
Only print fcp_riu if it contains more content than the preceding
unconditional fixed-size field "FCP rsp IU".

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Fedor Loshakov <loshakov@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:58:17 +02:00
Alexander Egorenkov
025a2198a4 zdump/dfi_s390: Fix use of uninitialized stack value in mem_chunks_add_ext()
If the while loop in mem_chunks_add_ext() is never executed, then
the stack variable containing the dump segment header will never be
initialized.

clang's static code analyzer reports the following problem:

$ make CC="clang --analyze" -C zdump

dfi_s390.c:157:6: warning: Branch condition evaluates to a garbage value [core.uninitialized.Branch]
        if (!dump_segm.stop_marker)
            ^~~~~~~~~~~~~~~~~~~~~~

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reported-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Alexander Egorenkov
2b938b78aa zdump: Introduce multi-level message logging
Use util_log from libutil to output various log messages that can be helpful
during problem analysis.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Alexander Egorenkov
de36fc5259 zdump/dfi: Fix format string specifiers in dfi_info_print()
Replace the incorrect %d format specifier with %u one for unsigned integer
types.

This issue was found with AFL fuzzing.

$ ./zdump/zgetdump -i ~/input.bin
General dump info:
  Dump format........: s390
  Version............: -2147483647
  System arch........: s390x (64 bit)
  CPU count (online).: 32768
  Dump memory range..: 1 MB

Memory map:
  0000000000000000 - 00000000000f423f (1 MB)

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Alexander Egorenkov
e3e5b6422a zdump/dfi_s390: Fix out-of-bounds array access in df_s390_cpu_info_add()
Verify that a s390 dump header contains a valid CPU count value.

This bug was found with an input file produced by AFL + ASAN.

$ ./zdump/zgetdump -i ~/input.bin
=================================================================
==3928488==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000001043e90 at pc 0x000001025dca bp 0x03ffe96fe128 sp 0x03ffe96fe120
READ of size 4 at 0x000001043e90 thread T0
    #0 0x1025dc9 in df_s390_cpu_info_add /root/s390-tools/zdump/df_s390.c:57
    #1 0x101bb59 in dfi_s390_init_gen /root/s390-tools/zdump/dfi_s390.c:169
    #2 0x101bb59 in dfi_s390_init_gen /root/s390-tools/zdump/dfi_s390.c:156
    #3 0x1015d23 in dfi_init /root/s390-tools/zdump/dfi.c:1216
    #4 0x1006a0d in do_dump_info /root/s390-tools/zdump/zgetdump.c:127
    #5 0x1006a0d in main /root/s390-tools/zdump/zgetdump.c:182
    #6 0x3ffb93abe03 in __libc_start_main (/lib64/libc.so.6+0x2be03)
    #7 0x10077bd  (/root/s390-tools/zdump/zgetdump+0x10077bd)

0x000001043e91 is located 0 bytes to the right of global variable 'l' defined in 'dfi_s390.c:30:3' (0x1042e80) of size 4113
SUMMARY: AddressSanitizer: global-buffer-overflow /root/s390-tools/zdump/df_s390.c:57 in df_s390_cpu_info_add
Shadow bytes around the buggy address:
  0x10000000208780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000000208790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100000002087a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100000002087b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100000002087c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100000002087d0: 00 00[01]f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
  0x100000002087e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100000002087f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000000208800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000000208810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000000208820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==3928488==ABORTING

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Joern Siglen
38343be0fa dbginfo.sh: update the order of collection steps
We found situations, where the debug commands from dbginfo.sh
did overwrite some debug files (e.g. OSA s390dbf) - so we change
the collection order

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Alexander Egorenkov
957e612720 zdump/dfi_vmcoreinfo: Fix illegal memory access in dfi_vmcoreinfo_init()
Before reading data in dfi_vmcoreinfo_init(), check the validity
of the memory range. Otherwise this can result in a segmentation fault when
zgetdump is given a very small dump, e.g. S390 DASD single-volume dump of
size 0x10 bytes.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:14 +02:00
Jan Höppner
1d2316caef zdump/dfi_vmcoreinfo: Fix potential illegal memory access in os_info_get()
Before reading data in os_info_get(), check the validity of the memory
range. Otherwise this can result in a segmentation fault when zgetdump is
given a very small dump, e.g. S390 DASD single-volume dump of size 0x10
bytes.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Alexander Egorenkov
64539853ca zdump/dfi: Fix potential illegal memory access in kdump_init()
Before reading data at addresses 0x10418 and 0x10420 in kdump_init(),
validate the validity of the memory range. Otherwise this can result in
a segmentation fault when zgetdump is given a very small dump, e.g.
S390 DASD single-volume dump of size 0x1000 bytes.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Alexander Egorenkov
686c331b69 zdump/dfi_vmcoreinfo: Sanity checks for n_namesz in ELF Notes header
The dfi_vmcoreinfo_init() function might be called on a dump of a non-ELF
format because the DFI goes through all supported dump formats when first
trying to identify of what dump format the given input is. Therefore,
we must be very careful in interpreting read data and ensure that it
makes sense before accessing or using it.

This commit prevents a potential overflow of a stack buffer in
dfi_vmcoreinfo_init() if note.n_namesz is bigger than the stack buffer.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Joern Siglen
01551f98d5 dbginfo.sh: code rework
- adding new print function for simpler output customizing
- consolidate some output files
- sync structures and use of checks
- some formatting updates

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Alexander Egorenkov
0e2c5907e0 zdump/dfi_s390: Fix memory leaks in mem_chunks_add_ext()
The function mem_chunks_add_ext() allocates but does not release memory
if it encounters unexpected errors during dump segment reading.

Allocate memory passed to dfi_mem_chunk_add() right before calling
the function. This way there is no need for a cleanup anymore.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reported-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Joern Siglen
68ff79e4c3 dbginfo.sh: cleanup 2.x kernel & adding timeout
- clenaup outdated kernel - we no longer have 2.x kenrel in support
- add timeout on command excution - ensure to not miss all data for a
  single command to hang

Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:53:13 +02:00
Ingo Franzki
5b96d86538 zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled
When integrity support is enabled for a LUKS2 volume, then the LUKS2
volume key may include the integrity key concatenated to the secure key
(dependent on the integrity type).

Get the integrity key size from the LUKS2 header (if integrity support
is enabled) and calculate the size of the secure key when performing
operations with the secure key only.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:21:32 +02:00
Alexander Egorenkov
4043137cd7 zdump: Fix handling of PT_LOAD segments with p_filesz < p_memsz in ELF DFI
According to the ELF specification, the file size of a PT_LOAD
memory segment might be less than its memory size. In that case,
the remaining bytes must be filled with zeros. The ELF DFI implementation
didn't handle this case correctly. Such ELF core files might be produced
by the makedumpfile tool.

From elf.5 man page:
------------------------------------------------------
PT_LOAD
       The array element specifies a loadable segment,
       described by p_filesz and p_memsz.  The bytes
       from the file are mapped to the beginning of the
       memory segment.  If the segment's memory size
       p_memsz is larger than the file size p_filesz,
       the "extra" bytes are defined to hold the value
       0 and to follow the segment's initialized area.
       The file size may not be larger than the memory
       size.  Loadable segment entries in the program
       header table appear in ascending order, sorted
       on the p_vaddr member.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:21:32 +02:00
Alexander Egorenkov
735d8f5493 zdump: Fix reading of ELF program headers in ELF DFI
Before attempting to read ELF program headers, the file must be positioned
at the offset of the first program header as specified in the ELF header.

Furthermore, read all ELF program headers with one call to zg_read(). This
has the advantage of not having to restore the file offset every time
after it is changed in pt_notes_add() and other functions which need to
read the same file as well.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:21:32 +02:00
Alexander Egorenkov
28efa7d120 zdump: Disable warnings about unaligned access to packed struct members
This is a false positive because IBM Z architecture supports unaligned
memory accesses.

dfo_s390.c: In function ‘df_s390_dump_init’:
dfo_s390.c:218:29: warning: taking address of packed member of ‘struct df_s390_hdr’ may result in an unaligned pointer value [-Waddress-of-packed-member]
  218 |                 timeval2tod(&dh->tod, dfi_attr_time());
      |                             ^~~~~~~~
dfo_s390.c:219:29: warning: taking address of packed member of ‘struct df_s390_em’ may result in an unaligned pointer value [-Waddress-of-packed-member]
  219 |                 timeval2tod(&em->tod, dfi_attr_time());
      |                             ^~~~~~~~
dfo_s390.c:222:29: warning: taking address of packed member of ‘struct df_s390_em’ may result in an unaligned pointer value [-Waddress-of-packed-member]
  222 |                 timeval2tod(&em->tod, dfi_attr_time_end());
      |                             ^~~~~~~~

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:21:29 +02:00
Eduard Shishkin
ebde75eeda zipl: add an ability to specify components location
When creating a bootmap, look for zipl components (stage3.bin and
zipl helpers) in a directory specified by S390TOOLS_LIBDIR shell
environment variable. If that variable is not set, then look for
them at the default location.

In verbose mode print the name of s390-tools library directory,
where the search was implemented.

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>
2021-09-30 17:20:52 +02:00
Eduard Shishkin
a645ec0fc0 libutil: make TOOLS_LIBDIR and TOOLS_DATADIR configurable
Add generic functions

util_sysdir();
util_sysdir_path()

and their special cases supplied for users:

util_libdir();
util_libdir_path();
util_datadir();
util_datadir_path()

The function util_sysdir() determines the absolute name of a
s390-tools system directory. It could be data, or library directory.

The function util_sysdir_path() determines the absolute name of a
file installed in the s390-tools system directory.

The function util_libdir() determines the absolute name of a
s390-tools library directory. By default that name is defined
by the compile-time macro TOOLS_LIBDIR (/lib/s390-tools).
Users can specify an override by setting environment variable
S390TOOLS_LIBDIR.

The function util_libdir_path() determines the absolute name of a
file installed in the s390-tools library directory.

The function util_datadir() determines the absolute name of a
s390-tools system data directory. By default the name is defined
by the compile-time macro TOOLS_DATADIR (/usr/share/s390-tools/).
Users can specify an override by setting environment variable
S390TOOLS_DATADIR.

The function util_datadir_path() determines the absolute name of a
file installed in the s390-tools data directory.

The ability to override the setting for TOOLS_LIBDIR and
TOOLS_DATADIR is required for implementing tests on tools that are
not installed in their default system path locations.

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>
2021-09-30 17:20:52 +02:00
Vineeth Vijayan
761a185938 zconf/lscss: show device number from pmcw
lscss shows device number as 'None' if the corresponding device in the
subchannel is non-operational or not connected. Instead, show the
device-no derived from the new dev_busid attribute which provides the
value of device_id irrespective of the device availability.

i.e the current lscss shows

Device   Subchan.  DevType CU Type Use  PIM PAM POM  CHPIDs
----------------------------------------------------------------------
0.0.0000 0.0.0000  3390/0c 3990/e9      f0  a0  ff   32333435 00000000
none     0.0.00c7                       f0  a0  5f   32333435 00000000
none     0.0.00c8                       f0  a0  5f   32333435 00000000

here the subchannels 0.0.00c7 and 0.0.00c8 do not have an operational
device on them. Hence the device is shown as none.

With this patch,

Device   Subchan.  DevType CU Type Use  PIM PAM POM  CHPIDs
----------------------------------------------------------------------
0.0.0000 0.0.0000  3390/0c 3990/e9      f0  a0  ff   32333435 00000000
0.0.74c8 0.0.00c7                       f0  a0  5f   32333435 00000000
0.0.74c9 0.0.00c8                       f0  a0  5f   32333435 00000000

The device-no is shown as 0.0.74c8; Where the DevType field still
indicates that the device is not present.

Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00
Harald Freudenberger
890b15d158 lszcrypt: add support for vfio-ap status field
lszcrypt now evaluates and displays the 'status' in the
'STATUS' column if the vfio provides this sysfs attribute.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00
Harald Freudenberger
07677668a3 lszcrypt: online is status and now tracked as status
The 'STATUS' field was within the code used as 'online'
record because it showed the online state. But now it
displays some more info like 'deconfig' and maybe even
more in the future. So now the code also uses a 'status'
record for this info.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00
Harald Freudenberger
fde914fd1c lszcrypt: Improved output for deconfig cards and queues
Slight rework of the lszcrypt code with:
- a queue or card in deconfigured mode is displayed
  as 'deconfig' regardless which driver is bound to
  the device.
- a queue or card in 'deconfig' mode does not show
  any counters any more but displays a '-' instead.
- a queue not bound or bound to something else as
  the 'default' zcrypt dd does not show any counters
  but displays a '-' instead.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00
Thomas Richter
35c97a07aa vmur/vmur.cpp: Fix error handling on transfer failure
The command

 # vmur punch -r rhcos-live-kernel-s390x -u etpgxku -N kernel.img
 Reader file with spoolid E YO created and transferred to ETPGXKU.
 #

did not indicate an error when executed. However the file was not
transferred and this error message went unnoticed:
RPIMGR032E YOU ARE NOT AUTHORIZED TO CLOSE TO TATAC05.ETPGXKU
HCPCSL007E Invalid userid - ETPGXKU

Fix this by checking the CP return code and print the error message
returned by CP. This is the behavior now:
 # vmur punch -r rhcos-live-kernel-s390x -u etpgxku -N kernel.img
 vmur: CP command failed with rc=7
 RPIMGR032E YOU ARE NOT AUTHORIZED TO CLOSE TO TATAC05.ETPGXKU
 HCPCSL007E Invalid userid - ETPGXKU
 #

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/119
Reported-by: Vance Morris <vmorris@us.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Tested-by: Vance Morris <vmorris@us.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00
Steffen Maier
45e3f016f4 lszfcp: add linkdown case to host marker of extended output
Complements v2.9.0 commit 4036e80b26 ("lszfcp: add new output marker
for non-good FCP devices (hosts)").

Otherwise "linkdown" shows as "failed" because linkdown happens to be
the one special case that also sets the failed status flag of the
FCP device (host). Linkdown is kind of benign compared to other cases
where the FCP device is in failed status.

Examples:
$ lszfcp -He
0.0.194a host3 linkdown

$ lszfcp -HeV
/sys/devices/css0/0.0.0004/0.0.194a linkdown
/sys/class/fc_host/host3
/sys/class/scsi_host/host3

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Fedor Loshakov <loshakov@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:55:02 +02:00
Alexander Egorenkov
38b520ab4c zdump/dfi_elf: Don't accept ELF input with zero-sized PT_LOAD segments
The DFI memory chunk manager cannot handle PT_LOAD segments of size 0.
This can lead to a NULL pointer access in mem_chunk_find() in case
zgetdump is given an invalid VMCORE ELF file as input which contains
only zero-sized ELF PT_LOAD segments. Instead of crashing, show an error
message and terminate gracefully.

The TELA test dump_with_zero_sized_segments.sh can reproduce this bug.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:55:02 +02:00
Fabrice Fontaine
3b6920271e Makefile: add {CURL,XML2}_CONFIG
Add CURL_CONFIG and XML2_CONFIG to allow the user to configure the
binaries used in check-dep-libkmipclient

This will allow the user to avoid the following build failures raised
since version 2.17.0 and
56fecf1832:

/bin/sh: 1: curl-config: not found
Error: libcurl is not built with the OpenSSL backend
make[2]: *** [Makefile:54: check-dep-libekmfweb] Error 1

Fixes:
 - http://autobuild.buildroot.org/results/cfb46d7547c22f0a23aca2286dc5a1d2c20aadfc

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/121
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:55:02 +02:00
Steffen Maier
a3146409da dbginfo.sh: collect zipl boot menu entries from boot loader specification
Zipl.conf is collected via /etc/*.conf in CONFIGFILES but BLS snippets
do not appear in /etc/zipl.conf.

Complements v2.5.0 commit d280e5b1fa ("zipl: Add BootLoaderSpec support")

Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:55:02 +02:00
Ingo Franzki
76aa8e4a52 Makefile: Fix order of build of libraries for parallel builds
Some libraries depend on other libraries, so split the libraries
into two groups: base libraries that do not depend on any others, and
the remaining libraries, that may depend on the base libraries.

Ensure that the base libraries are built first, before starting to
build the remaining libraries. The tools are only built once the base
libraries and the remaining libraries have been built.

This fixes a possible build problem with libseckey when parallel build
is used. The libseckey library is used by libekmfweb and the zkey KMIP
plugin (zkey-kmip). With parallel build both are built in parallel,
and thus both trigger the build of libseckey. This can lead to the
situation that libseckey is built twice at the same time, which can lead
to build failures (corrupted archive, etc).

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:54:59 +02:00
Jan Höppner
f5e6f4a8cb gitignore: Add hsavmcore/check-dep-fuse
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 16:52:38 +02:00
Alexander Egorenkov
80cb1553a3 hsavmcore: Avoid recompilation of overlay during install step
overlay.o was being recompiled during the install step because
it depended on the target check-dep-fuse which is phony and,
therefore, always outdated. The solution is to create an empty file
for the target check-dep-fuse after its successful completion. This
prevents make from rebuilding overlay.o during installation.

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/118
Fixes: 5a7d2a58c8 ("hsavmcore: Fix fuse dependency checking")
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Suggested-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:48:45 +02:00
Thomas Richter
706f59b9d9 cpumf/lshwc.c: Fix sparse warning and replace 0 by NULL
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Suggested-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
Thomas Richter
02cc902b90 cpumf/lswhc.c: Fix sparse warning and add missing static
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Suggested-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
Alexander Egorenkov
ff2fb42868 cpumf/lshwc: Fix compile errors due to use of non-standard __BITS_PER_LONG
Use LONG_BIT provided by C standard in <limits.h>.

Fixes the following compile errors with buildroot:
lshwc.c: In function ‘parse_cpulist’:
lshwc.c:295:15: error: ‘__BITS_PER_LONG’ undeclared (first use in this function)
  295 |    no_a = i % __BITS_PER_LONG;
      |               ^~~~~~~~~~~~~~~

Fixes: 27a562da0a ("cpumf/lshwc: Program to extract complete counter sets")
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
Alexander Egorenkov
451bf7fbc6 cpumf/lshwc: Fix compile errors due to use of non-standard 32/64 bit types
Use standard 32- and 64-bit C types.

Fixes the following compile errors with buildroot:
In file included from lshwc.c:41:
lshwc.h:61:2: error: unknown type name ‘__u64’
   61 |  __u64 version;   /* Version of interface */
      |  ^~~~~
lshwc.h:62:2: error: unknown type name ‘__u64’
   62 |  __u64 data_bytes;  /* # of bytes required */
      |  ^~~~~
lshwc.h:63:2: error: unknown type name ‘__u64’
   63 |  __u64 cpumask_len;  /* Length of CPU mask in bytes */
      |  ^~~~~
lshwc.h:64:2: error: unknown type name ‘__u64’
   64 |  __u64 *cpumask;   /* Pointer to CPU mask */
      |  ^~~~~
lshwc.h:65:2: error: unknown type name ‘__u64’
   65 |  __u64 counter_sets;  /* Bit mask of counter set to get */
      |  ^~~~~
lshwc.h:69:2: error: unknown type name ‘__u32’
   69 |  __u32 set;   /* Counter set number */
      |  ^~~~~

Fixes: 27a562da0a ("cpumf/lshwc: Program to extract complete counter sets")
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
Ingo Franzki
d2a4a8b0f3 libkmipclient: Fix parsing of hex values for XML and JSON encoding
KMIP values of type BYTE-STRING are represented as hex values when XML
or JSON encoding is used. Do not drop any leading zero bytes, if the
value has them.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
Jan Höppner
fe5753d34d Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-27 11:46:39 +02:00
72 changed files with 4673 additions and 953 deletions

7
.gitignore vendored
View File

@@ -9,6 +9,10 @@
tags
TAGS
# Ignore coverage data
*.gcda
*.gcno
#
# Ignore generated executables and other generated files
#
@@ -25,6 +29,7 @@ dasdview/dasdview
dump2tar/src/dump2tar
fdasd/fdasd
hmcdrvfs/hmcdrvfs
hsavmcore/check-dep-fuse
hsavmcore/hsavmcore
hyptop/hyptop
ip_watcher/xcec-bridge
@@ -98,7 +103,9 @@ zipl/boot/*.bin
zipl/boot/*.exec
zipl/boot/data.h
zipl/src/chreipl_helper.device-mapper
zdev/src/zdev_id
zipl/src/zipl
zipl/src/zipl-editenv
zipl/src/zipl_helper.device-mapper
zkey/check-dep-zkey
zkey/check-dep-zkey-cryptsetup

View File

@@ -1,5 +1,34 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.18.0 (2021-10-01)__
For Linux kernel version: 5.14
Add new tools:
- scripts: Add tool for parsing sclp s390dbf logs
- zdev: Add udev rule helper tool
- zipl-editenv: Add tool to operate with zIPL environment installed in the boot record
Changes of existing tools:
- Makefile: Fix order of build of libraries for parallel builds
- dbginfo.sh: Add collection in area of timedate, coredump and --check option
- dbginfo.sh: Add exception on dump2tar for /sys/kernel/mm/page_idle/bitmap
- dbginfo.sh: Cleanup of outdated sections and general code rework
- dbginfo.sh: Collect zipl boot menu entries from boot loader specification
- lszcrypt: Add support for vfio-ap status field
- lszcrypt: Improved output for deconfig cards and queues
- lszfcp: Add linkdown case to host marker of extended output
- zdev: Add auto-config for PCI and crypto devices
- zdump: Introduce multi-level message logging
- zipl: Add support for environment block interpretation
- zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled
Bug Fixes:
- hsavmcore: Avoid recompilation of overlay during install step
- libkmipclient: Fix parsing of hex values for XML and JSON encoding
- vmur/vmur.cpp: Fix error handling on transfer failure
- zdump: Lots of smaller fixes across the board
* __v2.17.0 (2021-07-07)__
For Linux kernel version: 5.12 / 5.13

View File

@@ -3,8 +3,14 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
# Include common definitions
include common.mak
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb \
libseckey libkmipclient
#
# BASELIBS: Libraries that have no dependency to other libraries in s390-tools
# LIBS: Libraries that can have a dependency to base libraries
# TOOLS: Tools that can have a dependency to base libraries or libraries
#
BASELIB_DIRS = libutil libseckey
LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \
libkmipclient
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
@@ -12,7 +18,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
genprotimg lsstp hsci hsavmcore
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
all: $(TOOL_DIRS)
clean: $(TOOL_DIRS)
@@ -26,9 +32,10 @@ MAKECMDGOALS = all
endif
#
# We have to build the libraries before the tools are built. Otherwise
# the tools would trigger parallel "make -C" builds for libraries in
# case of "make -j".
# We have to build the base libraries before the other libraries are built,
# and then build the other libraries before the tools are built. Otherwise the
# other libraries and tools would trigger parallel "make -C" builds for the
# base libraries and the other libraries in case of "make -j".
#
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
# "foreach" to generate a ";" separated list of "make -C <target>".
@@ -45,7 +52,12 @@ $(TOOL_DIRS): $(LIB_DIRS)
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
.PHONY: $(TOOL_DIRS)
$(LIB_DIRS):
$(LIB_DIRS): $(BASELIB_DIRS)
$(foreach goal,$(MAKECMDGOALS), \
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
.PHONY: $(LIB_DIRS)
$(BASELIB_DIRS):
$(foreach goal,$(MAKECMDGOALS), \
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
.PHONY: $(BASELIB_DIRS)

View File

@@ -109,6 +109,9 @@ Package contents
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
several traces, make it more readable etc.
* sclpdbf:
Display debug data for the sclp kernel component.
* scsi_logging_level:
Create, get or set the logging level for the SCSI logging facility.

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 = 17
RELEASE = 18
PATCHLEVEL = 0
DISTRELEASE = build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)

View File

@@ -17,6 +17,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/limits.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -53,7 +54,7 @@ static unsigned char *ioctlbuffer;
static bool allcpu;
static unsigned int max_possible_cpus; /* No of possible CPUs */
struct ctrname { /* List of defined counters */
static struct ctrname { /* List of defined counters */
char *name; /* Counter name */
bool hitcnt; /* Counter number read from ioctl() */
unsigned long total; /* Total counter value */
@@ -240,7 +241,7 @@ static char *show_ctrset(unsigned long set)
/* Parse CPU list and counter sets */
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
{
__u64 *words = start->cpumask;
uint64_t *words = start->cpumask;
unsigned long i, no_a, no_b;
char *cp, *tokens[16]; /* Used to parse command line params */
char cpubuf[256];
@@ -275,8 +276,8 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
show_ctrset(noton));
}
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")) != 0;
++i, parm = 0) {
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
++i, parm = NULL) {
cp = strchr(tokens[i], '-'); /* Range character? */
if (cp) {
no_a = getnumber(tokens[i], *cp);
@@ -292,15 +293,15 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
/* Convert the CPU list to a bitmask for kernel cpumask_t */
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
if (check[i].cpu_req) {
no_a = i % __BITS_PER_LONG;
no_b = i / __BITS_PER_LONG;
no_a = i % LONG_BIT;
no_b = i / LONG_BIT;
words[no_b] |= 1ULL << no_a;
}
}
/* no_b is highest used index, swap array */
start->cpumask_len = (no_b + 1) * 8;
for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
__u64 tmp = words[no_a];
uint64_t tmp = words[no_a];
words[no_a] = words[no_b];
words[no_b] = tmp;
@@ -326,7 +327,7 @@ static bool check_setpossible(void)
}
parm = cpubuf;
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
++i, parm = 0) {
++i, parm = NULL) {
cp = strchr(tokens[i], '-');
if (cp) { /* Range */
no_a = getnumber(tokens[i], *cp);
@@ -525,7 +526,7 @@ static int test_read(struct s390_hwctr_read *read)
}
/* Iterate over all counters in each set */
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
__u64 value;
uint64_t value;
void *addr = base + offset;
size_t idx = ctrset_offset(sp->set) + k;
@@ -608,8 +609,8 @@ static int do_it(char *s)
int rc;
memset(&start, 0, sizeof(start));
rc = max_possible_cpus / sizeof(__u64);
start.cpumask = alloca(max_possible_cpus / sizeof(__u64));
rc = max_possible_cpus / sizeof(uint64_t);
start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t));
memset(start.cpumask, 0, rc);
parse_cpulist(s, &start);
errno = 0;

View File

@@ -13,6 +13,7 @@
#ifndef LSHWC_H
#define LSHWC_H
#include <stdint.h>
#include <sys/ioctl.h>
enum {
@@ -58,27 +59,27 @@ enum {
*/
struct s390_hwctr_start { /* Set CPUs to operate on */
__u64 version; /* Version of interface */
__u64 data_bytes; /* # of bytes required */
__u64 cpumask_len; /* Length of CPU mask in bytes */
__u64 *cpumask; /* Pointer to CPU mask */
__u64 counter_sets; /* Bit mask of counter set to get */
uint64_t version; /* Version of interface */
uint64_t data_bytes; /* # of bytes required */
uint64_t cpumask_len; /* Length of CPU mask in bytes */
uint64_t *cpumask; /* Pointer to CPU mask */
uint64_t counter_sets; /* Bit mask of counter set to get */
};
struct s390_hwctr_setdata { /* Counter set data */
__u32 set; /* Counter set number */
__u32 no_cnts; /* # of counters stored in cv[] */
__u64 cv[0]; /* Counter values (variable length) */
uint32_t set; /* Counter set number */
uint32_t no_cnts; /* # of counters stored in cv[] */
uint64_t cv[0]; /* Counter values (variable length) */
};
struct s390_hwctr_cpudata { /* Counter set data per CPU */
__u32 cpu_nr; /* Counter set number */
__u32 no_sets; /* # of counters sets in data[] */
uint32_t cpu_nr; /* Counter set number */
uint32_t no_sets; /* # of counters sets in data[] */
struct s390_hwctr_setdata data[0];
};
struct s390_hwctr_read { /* Structure to get all ctr sets */
__u64 no_cpus; /* Total # of CPUs data taken from */
uint64_t no_cpus; /* Total # of CPUs data taken from */
struct s390_hwctr_cpudata data[0];
};

View File

@@ -110,6 +110,7 @@ static struct util_opt dump2tar_opts[] = {
{
.option = { "file-timeout", required_argument, NULL, 'T' },
.desc = "Stop reading file after SEC seconds",
.argument = "SEC",
},
{
.option = { "file-max-size", required_argument, NULL, 'M' },

View File

@@ -69,6 +69,7 @@ check-dep-fuse:
"fuse.h", \
"fuse-devel or libfuse-dev", \
"HAVE_FUSE=0")
touch check-dep-fuse
install: all
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
@@ -81,6 +82,6 @@ install: all
endif # HAVE_FUSE
clean:
rm -f hsavmcore $(objects)
rm -f hsavmcore $(objects) check-dep-fuse
.PHONY: all install clean

View File

@@ -20,6 +20,10 @@ void util_hexdump(FILE *fh, const char *tag, const void *data, int cnt);
void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group,
int cnt, int indent);
void util_print_indented(const char *str, int indent);
const char *util_libdir(void);
const char *util_libdir_path(const char *filename);
const char *util_datadir(void);
const char *util_datadir_path(const char *filename);
static inline void util_ptr_vec_free(void **ptr_vec, int count)
{

View File

@@ -52,6 +52,7 @@
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
#define TOOLS_BINDIR STRINGIFY (S390_TOOLS_BINDIR)
#define TOOLS_DATADIR STRINGIFY (S390_TOOLS_DATADIR)
#define __noreturn __attribute__((noreturn))
#define __packed __attribute__((packed))

View File

@@ -47,6 +47,8 @@ detect-openssl-version.dep:
echo "}" >> $(TMPFILE)
mv $(TMPFILE) $@
CURL_CONFIG ?= curl-config
check-dep-libekmfweb: detect-openssl-version.dep
$(call check_dep, \
"libekmfweb", \
@@ -64,8 +66,8 @@ check-dep-libekmfweb: detect-openssl-version.dep
"curl/curl.h", \
"libcurl-devel", \
"HAVE_LIBCURL=0" \
`curl-config --cflags` `curl-config --libs`)
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
$(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
touch check-dep-libekmfweb
skip-libekmfweb-openssl:
@@ -83,8 +85,8 @@ ekmfweb.o: check-dep-libekmfweb ekmfweb.c utilities.h cca.h $(rootdir)include/ek
utilities.o: check-dep-libekmfweb utilities.c utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
cca.o: check-dep-libekmfweb cca.c cca.h utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `curl-config --cflags`
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `curl-config --libs` -ldl
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `$(CURL_CONFIG) --cflags`
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(CURL_CONFIG) --libs` -ldl
libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM)
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs)

View File

@@ -50,6 +50,9 @@ detect-openssl-version.dep:
echo "}" >> $(TMPFILE)
mv $(TMPFILE) $@
CURL_CONFIG ?= curl-config
XML2_CONFIG ?= xml2-config
check-dep-libkmipclient: detect-openssl-version.dep
$(call check_dep, \
"libkmipclient", \
@@ -67,14 +70,14 @@ check-dep-libkmipclient: detect-openssl-version.dep
"libxml/tree.h", \
"libxml2-devel", \
"HAVE_LIBXML2=0", \
`xml2-config --cflags` `xml2-config --libs`)
`$(XML2_CONFIG) --cflags` `$(XML2_CONFIG) --libs`)
$(call check_dep, \
"libkmipclient", \
"curl/curl.h", \
"libcurl-devel", \
"HAVE_LIBCURL=0" \
`curl-config --cflags` `curl-config --libs`)
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
$(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
touch check-dep-libkmipclient
skip-libkmipclient-openssl:
@@ -104,8 +107,8 @@ tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient
names.o: check-dep-libkmipclient names.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
utils.o: check-dep-libkmipclient utils.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `xml2-config --cflags` `curl-config --cflags`
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `xml2-config --libs` `curl-config --libs`
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `$(XML2_CONFIG) --cflags` `$(CURL_CONFIG) --cflags`
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(XML2_CONFIG) --libs` `$(CURL_CONFIG) --libs`
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM)
libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \

View File

@@ -125,7 +125,7 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
{
unsigned char *buf;
BIGNUM *b = NULL;
int len, rc;
int len;
if (str == NULL)
return -EINVAL;
@@ -133,18 +133,20 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
if (has_prefix && strncmp(str, "0x", 2) != 0)
return -EBADMSG;
rc = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
if (rc <= 0)
len = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
if (len <= 0)
return -EBADMSG;
if (len < (int)strlen(str) - (has_prefix ? 2 : 0))
return -EBADMSG;
len = BN_num_bytes(b);
len = len / 2 + (len % 2 > 0 ? 1 : 0);
buf = calloc(1, len);
if (buf == NULL) {
BN_free(b);
return -ENOMEM;
}
if (BN_bn2bin(b, buf) != len) {
if (BN_bn2binpad(b, buf, len) != len) {
BN_free(b);
free(buf);
return -EIO;

View File

@@ -9,6 +9,8 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <err.h>
#include <limits.h>
#include <string.h>
#include "lib/util_base.h"
@@ -91,3 +93,91 @@ void util_print_indented(const char *str, int indent)
printf("\n");
free(desc_ptr);
}
/**
* Determines the absolute name of an s390-tools system directory
* (it could be data, or library directory)
*/
static const char *util_sysdir(const char *env_var, const char *default_dir)
{
return secure_getenv(env_var) ?: default_dir;
}
/**
* Determines the absolute name of a file installed in the s390-tools
* system directory
*/
static const char *util_sysdir_path(const char *filename, const char *dirname)
{
static char libdir_pathname[PATH_MAX];
int ret;
ret = snprintf(libdir_pathname, sizeof(libdir_pathname),
"%s/%s", dirname, filename);
if (ret < 0 || ret >= (int)sizeof(libdir_pathname))
errx(EXIT_FAILURE,
"Could not compose absolute pathname of %s and %s",
dirname, filename);
return libdir_pathname;
}
/**
* Determines the absolute name of a s390-tools library directory
*
* Resources are handled by the library
*
* @returns Pointer to a buffer, which contains the null-terminated name
*/
const char *util_libdir(void)
{
return util_sysdir("S390TOOLS_LIBDIR", TOOLS_LIBDIR);
}
/**
* Determines the absolute name of a file installed in the s390-tools
* library directory by its relative name.
*
* Resources are handled by the library. A second call of this function
* overwrites previously returned data. Care must be taken when attempting
* to do a second call.
*
* @param[in] filename Null-terminated file name relative to the s390-tools
* library directory
* @returns Pointer to a buffer of PATH_MAX size, which contains the
* null-terminated absolute name
*/
const char *util_libdir_path(const char *filename)
{
return util_sysdir_path(filename, util_libdir());
}
/**
* Determines the absolute name of a s390-tools data directory
*
* Resources are handled by the library
*
* @returns Pointer to a buffer which contains the null-terminated name
*/
const char *util_datadir(void)
{
return util_sysdir("S390TOOLS_DATADIR", TOOLS_DATADIR);
}
/**
* Determines the absolute name of a file installed in the s390-tools
* data directory by its relative name.
*
* Resources are handled by the library. A second call of this function
* overwrites previously returned data. Care must be taken when attempting
* to do a second call.
*
* @param[in] filename Null-terminated file name relative to the s390-tools
* data directory
* @returns Pointer to a buffer of PATH_MAX size, which contains the
* null-terminated absolute name
*/
const char *util_datadir_path(const char *filename)
{
return util_sysdir_path(filename, util_datadir());
}

View File

@@ -1,6 +1,7 @@
include ../common.mak
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg scsi_logging_level
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg scsi_logging_level
SCRIPTS += sclpdbf
MAN_PAGES = dbginfo.sh.1 zfcpdbf.1 zipl-switch-to-blscfg.1
all:

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
.TH DBGINFO.SH 1 "April 2021" "s390-tools"
.TH DBGINFO.SH 1 "September 2021" "s390-tools"
.SH NAME
dbginfo.sh \- collect runtime, configuration and trace information
@@ -14,8 +14,10 @@ for debugging Linux on IBM Z
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.
The debug information is written to a file
/tmp/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
/<DIRECTORY>/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
where [date] and [time] are the date and time when the debug data was
collected. [hostname] indicates the hostname of the system the data was
collected from. The [processorid] is taken from the processor 0 and indicates
@@ -32,7 +34,12 @@ Print version information, then exit.
.TP
\fB\-d <DIRECTORY>\fP, \fB\-\-directory <DIRECTORY>\fP
Specify the DIRECTORY where the data collection stores the temporary data and the final archive. The specified directory must already exist. If this parameter is not specified, /tmp is used by default.
Specify the DIRECTORY where the data collection stores the temporary data and the final archive.
The specified directory must already exist. If this parameter is not specified, /tmp is used by default.
.TP
\fB\-c\fP, \fB\-\-check\fP
Print online quick check (not saving any data) - can be combind with a preceding -d option.
.SH FILES
A .tgz file of the form
@@ -56,48 +63,57 @@ Copyright IBM Corp. 2002, 2021
.PP
Hardware platform = s390x
.br
Kernel version = 5.4.0 (5.4.0-70-generic)
.br
Runtime environment = z/VM
.br
Kernel version = 4.18.0 (4.18.0-305.el8.s390x)
.br
OS version / distro = Red Hat Enterprise Linux 8.4 (Ootpa)
.br
Date and time of info = 2021-08-26-15-27-58
.PP
1 of 16: Collecting command output
1 of 18: Collecting sysfs
.PP
2 of 16: Collecting z/VM command output
2 of 18: Collecting procfs
.PP
3 of 16: Collecting procfs
3 of 18: Collecting config files
.PP
4 of 16: Collecting sysfs
4 of 18: Collecting command output
.PP
5 of 16: Collecting log files
5 of 18: Collecting hyptop for z/VM - 5s output
.PP
6 of 16: Collecting config files
6 of 18: Collecting z/VM output
.PP
7 of 16: Collecting osa oat output
7 of 18: Collecting network output
.PP
8 of 16: Collecting ethtool output
8 of 18: Collecting osa oat output
.PP
9 of 16: Collecting tc output
9 of 18: Collecting ethtool output
.PP
10 of 16: Collecting bridge output
10 of 18: Collecting Trafic Control output
.PP
11 of 16: Collecting OpenVSwitch output
11 of 18: Collecting bridge output
.PP
12a of 16: Collecting docker container
12b of 16: Collecting docker network
12 of 18: Skip OpenVSwitch: ovs-vsctl not available
.PP
13 of 16: Collecting nvme output
13 of 18: Skip KVM: no virsh command
.PP
14 of 16: Collecting KVM data
14 of 18: Collecting docker output
.PP
15 of 16: Postprocessing
15 of 18: Collecting NVME storage output
.PP
16 of 16: Finalizing: Creating archive with collected data
16 of 18: Collecting log files
.PP
0 logfiles over 50 MB
.PP
17 of 18: Postprocessing
.PP
18 of 18: Finalizing: Creating archive with collected data
.PP
Collected data was saved to:
.br
>> /data\-collection/DBGINFO\-2021\-04\-20\-14\-00\-07\-host\-012345.tgz <<
>> /data\-collection/DBGINFO\-2021\-08\-26\-15\-27\-58\-host\-012345.tgz <<
.br
Review the collected data before sending to your service organization.
Please review all collected data before sending to your service organization.
.SH HINTS
Run the script with root authority.
.br

1172
scripts/sclpdbf Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -887,14 +887,13 @@ sub print_scsi_common
print "FCP rsp inf cod: 0x", substr($rec->[2], 44, 2), "\n";
print "FCP rsp IU : ", payload_format(substr($rec->[2], 114, 48));
if (defined($pay_sns)) {
print "Sense len : ", $payload_length, "\n"
if ($payload_length);
print_payload($payload_length, $pay_sns, "Sense info")
}
if (defined($pay_riu) && ($payload_length > FCP_RESP_WITH_EXT)) {
if ($payload_length && defined($pay_sns)) {
print "Sense len : ", $payload_length, "\n";
print_payload($payload_length, $pay_sns, "Sense info");
} elsif ($payload_length) {
print "FCP rsp IU len : ", $payload_length, "\n";
print_payload($payload_length, $pay_riu, "FCP rsp IU all");
print_payload($payload_length, $pay_riu, "FCP rsp IU all")
if ($payload_length > FCP_RESP_WITH_EXT);
}
print "\n";
}

View File

@@ -1863,7 +1863,6 @@ static void close_ur_device(struct vmur *info)
}
cpcmd_cs(cmd, &response, &cprc, 0);
memcpy(spoolid, &response[9], 4);
free(response);
if (cprc == 439) {
if (info->action == PUNCH)
sprintf(cmd, "PURGE * PUN %s", spoolid);
@@ -1872,7 +1871,10 @@ static void close_ur_device(struct vmur *info)
cpcmd(cmd, NULL, NULL, 0);
ERR_EXIT("User %s spool fileid limit exceeded.\n"
, info->user);
} else if (cprc) {
ERR_EXIT("CP command failed with rc=%i\n%s\n", cprc, response);
}
free(response);
if (info->rdr_specified) {
if (info->node_specified)
printf("Reader file with spoolid %s created and "

View File

@@ -463,7 +463,15 @@ static int fill_io_devid(struct util_rec *rec, char *path)
util_scandir_free(de_vec, count);
return 1;
}
strncpy(buf, "none", sizeof(buf));
if (util_file_read_line(buf, sizeof(buf), "%s/dev_busid",
path) == 0) {
if (cmd.opt_short) {
if (strncmp(buf, "0.0.", PREFIX_ID_LENGTH) != 0)
return 1;
memmove(buf, buf + PREFIX_ID_LENGTH,
(sizeof(buf) - PREFIX_ID_LENGTH));
}
}
fill_device_info(rec, NULL, NULL);
}
if (cmd.opt_uppercase)

View File

@@ -203,6 +203,7 @@ $(readlink -e $HOST_PATH)/-"
read ONLINE < $ADAPTER_PATH/online
read AVAILABILITY < $ADAPTER_PATH/availability
[ -r "$SYSFS/class/fc_host/$SCSI_HOST/port_state" ] && read PORT_STATE < "$SYSFS/class/fc_host/$SCSI_HOST/port_state"
[ -r $ADAPTER_PATH/failed ] && read FAILED < $ADAPTER_PATH/failed
DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid
DEFUNCT=${DEFUNCT##*/} # basename
@@ -212,6 +213,8 @@ $(readlink -e $HOST_PATH)/-"
HOSTMARKER=" defunct"
elif [ "$ONLINE" = "0" ]; then
HOSTMARKER=" offline"
elif [ "$PORT_STATE" = "Linkdown" ]; then
HOSTMARKER=" linkdown"
elif [ "$FAILED" = "1" ]; then
HOSTMARKER=" failed"
elif [ "$AVAILABILITY" != "good" ]; then

View File

@@ -115,6 +115,11 @@ Displays version information and exits.
Here is an explanation of the columns displayed. Please note that some
of the columns show up in verbose mode only.
.TP
.B CARD.DOM
The crypto card number in hexadecimal for a crypto card line or
the crypto card number and the domain id both in hex separated by a single
dot for a queue line.
.TP
.B TYPE and HWTYPE
The HWTYPE is a numeric value showing which type of hardware the zcrypt
device driver presumes that this crypto card is. The currently known values
@@ -145,9 +150,16 @@ may be adjusted on the HMC or SE. The chzcrypt application can also
trigger this state with the --config-on and --config-off options.
.br
lszcrypt shows 'online' when a card or queue is available for
cryptograhic operations. 'offline' is displayed when a card or queue
cryptographic operations. 'offline' is displayed when a card or queue
is switched to (software) offline. If a card is 'deconfigured' via
HMC, SE or chzcrypt the field shows 'deconfig'.
.br
If a queue is not bound to a device driver there is no detailed
information available and thus the status shows only '-'.
.br
If a queue is bound to the vfio-ap device driver it is up to this driver
to give some status information and what exactly this means. So lszcrypt
shows the text retrieved from the underlying sysfs attribute here.
.TP
.B REQUESTS
This is the counter value of successful processed requests on card or queue
@@ -168,7 +180,7 @@ S - APSC available: card/queue can handle requests with the special bit
enabled.
.br
M - Accelerator card/queue with support for RSA ME with up to 4k key size.
.br
.Sr
C - Accelerator card/queue with support for RSA CRT with up to 4k key size.
.br
D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).

View File

@@ -22,6 +22,8 @@
#include "lib/util_scandir.h"
#include "lib/zt_common.h"
#include "misc.h"
/*
* Private data
*/
@@ -331,13 +333,49 @@ static void show_capability(const char *id_str)
printf("\n");
}
/*
* Read driver entry in dir or in dir/subdir and store driver into buf.
* Returns:
* 0 if there is no driver link (no driver bound to this device or error)
* 1 if one of the cexXcard or cexXqueue driver is bound to this device
* 2 if the vfio is bound to this device
*/
static int read_driver(const char *dir, const char *subdir, char *buf, size_t buflen)
{
char drvlink[PATH_MAX], linktarget[PATH_MAX];
char *p, driver[256];
if (subdir)
snprintf(drvlink, sizeof(drvlink), "%s/%s/driver", dir, subdir);
else
snprintf(drvlink, sizeof(drvlink), "%s/driver", dir);
drvlink[sizeof(drvlink) - 1] = '\0';
memset(linktarget, 0, sizeof(linktarget));
if (readlink(drvlink, linktarget, sizeof(linktarget)) > 0) {
p = strrchr(linktarget, '/');
if (p) {
strncpy(driver, p + 1, sizeof(driver));
driver[sizeof(driver) - 1] = '\0';
strncpy(buf, driver, buflen);
if (misc_regex_match(driver, "cex[0-9](card|queue)"))
return 1;
else if (misc_regex_match(driver, "vfio.ap"))
return 2;
}
}
return 0;
}
/*
* Read subdevice default attributes
*/
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
const char *sub_dev)
{
long value;
long config = -1, online = -1;
char buf[256];
unsigned long facility;
@@ -346,24 +384,29 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
else
util_rec_set(rec, "type", buf);
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev)) {
util_file_read_l(&value, 10, "%s/%s/online", grp_dev, sub_dev);
if (value > 0)
util_rec_set(rec, "online", "online");
else {
/* device is offline, check config (if available) */
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev)) {
util_file_read_l(&value, 10, "%s/%s/config", grp_dev, sub_dev);
if (value > 0)
util_rec_set(rec, "online", "offline");
else
util_rec_set(rec, "online", "deconfig");
} else
util_rec_set(rec, "online", "offline");
}
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev))
util_file_read_l(&config, 10, "%s/%s/config", grp_dev, sub_dev);
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
util_file_read_l(&online, 10, "%s/%s/online", grp_dev, sub_dev);
util_rec_set(rec, "status", "-");
if (config == 0) {
util_rec_set(rec, "status", "deconfig");
} else {
/* no online attribute */
util_rec_set(rec, "online", "-");
if (online > 0)
util_rec_set(rec, "status", "online");
else if (online == 0)
util_rec_set(rec, "status", "offline");
else {
/* no online attribute, maybe use status attribute */
if (util_path_is_readable("%s/%s/status",
grp_dev, sub_dev)) {
util_file_read_line(buf, sizeof(buf),
"%s/%s/status",
grp_dev, sub_dev);
util_rec_set(rec, "status", buf);
}
}
}
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
@@ -376,9 +419,13 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
else
util_rec_set(rec, "mode", "Unknown");
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
grp_dev, sub_dev);
util_rec_set(rec, "requests", buf);
if (config == 0) {
util_rec_set(rec, "requests", "-");
} else {
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
grp_dev, sub_dev);
util_rec_set(rec, "requests", buf);
}
}
/*
@@ -387,19 +434,29 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
const char *sub_dev)
{
int i;
int i, drvinfo;
unsigned long facility;
char buf[256], afile[PATH_MAX];
long depth, pending1, pending2;
char buf[256];
long depth, pending1, pending2, config = -1;
if (l.verbose == 0)
return;
util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
grp_dev, sub_dev);
util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
grp_dev, sub_dev);
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
drvinfo = read_driver(grp_dev, sub_dev, buf, sizeof(buf));
util_rec_set(rec, "driver", drvinfo > 0 ? buf : "-no-driver-");
if (util_path_is_readable("%s/config", grp_dev))
util_file_read_l(&config, 10, "%s/config", grp_dev);
if (config == 0 || drvinfo != 1) {
util_rec_set(rec, "pending", "-");
} else {
util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
grp_dev, sub_dev);
util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
grp_dev, sub_dev);
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
}
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
util_rec_set(rec, "hwtype", buf);
@@ -412,14 +469,6 @@ static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-';
buf[i] = '\0';
util_rec_set(rec, "facility", buf);
snprintf(afile, sizeof(afile), "%s/%s/driver", grp_dev, sub_dev);
afile[sizeof(afile) - 1] = '\0';
memset(buf, 0, sizeof(buf));
if (readlink(afile, buf, sizeof(buf)) > 0)
util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
else
util_rec_set(rec, "driver", "-no-driver-");
}
/*
@@ -467,7 +516,7 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev)
*/
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
{
long value;
long config = -1, online = -1;
char buf[256];
unsigned long facility;
@@ -486,24 +535,27 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
else
util_rec_set(rec, "mode", "Unknown");
if (util_path_is_readable("%s/online", grp_dev)) {
util_file_read_l(&value, 10, "%s/online", grp_dev);
if (value > 0)
util_rec_set(rec, "online", "online");
else {
if (util_path_is_readable("%s/config", grp_dev)) {
util_file_read_l(&value, 10, "%s/config", grp_dev);
if (value > 0)
util_rec_set(rec, "online", "offline");
else
util_rec_set(rec, "online", "deconfig");
} else
util_rec_set(rec, "online", "offline");
}
if (util_path_is_readable("%s/config", grp_dev))
util_file_read_l(&config, 10, "%s/config", grp_dev);
if (util_path_is_readable("%s/online", grp_dev))
util_file_read_l(&online, 10, "%s/online", grp_dev);
if (config == 0) {
util_rec_set(rec, "status", "deconfig");
} else {
if (online > 0)
util_rec_set(rec, "status", "online");
else if (online == 0)
util_rec_set(rec, "status", "offline");
else
util_rec_set(rec, "status", "-");
}
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
util_rec_set(rec, "requests", buf);
if (config == 0) {
util_rec_set(rec, "requests", "-");
} else {
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
util_rec_set(rec, "requests", buf);
}
}
/*
@@ -513,15 +565,22 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
{
int i;
unsigned long facility;
char buf[256], afile[PATH_MAX];
long depth, pending1, pending2;
char buf[256];
long depth, pending1, pending2, config = -1;
if (l.verbose == 0)
return;
util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
if (util_path_is_readable("%s/config", grp_dev))
util_file_read_l(&config, 10, "%s/config", grp_dev);
if (config == 0) {
util_rec_set(rec, "pending", "-");
} else {
util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
}
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
util_rec_set(rec, "hwtype", buf);
@@ -535,13 +594,8 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
buf[i] = '\0';
util_rec_set(rec, "facility", buf);
snprintf(afile, sizeof(afile), "%s/driver", grp_dev);
afile[sizeof(afile) - 1] = '\0';
memset(buf, 0, sizeof(buf));
if (readlink(afile, buf, sizeof(buf)) > 0)
util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
else
util_rec_set(rec, "driver", "-no-driver-");
i = read_driver(grp_dev, NULL, buf, sizeof(buf));
util_rec_set(rec, "driver", i > 0 ? buf : "-no-driver-");
}
/*
@@ -581,10 +635,10 @@ out_free:
*/
static void define_rec_default(struct util_rec *rec)
{
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN");
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 8, "CARD.DOM");
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 8, "STATUS");
util_rec_def(rec, "status", UTIL_REC_ALIGN_LEFT, 10, "STATUS");
util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS");
}

View File

@@ -7,6 +7,7 @@ all:
install: all
$(MAKE) -C src install
$(MAKE) -C man install
$(MAKE) -C udev install
$(MAKE) -C dracut install
$(MAKE) -C initramfs install

View File

@@ -20,7 +20,7 @@ check() {
[ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1
# Ensure that required tools are available
require_binaries chzdev lszdev || return 1
require_binaries chzdev lszdev /lib/s390-tools/zdev_id || return 1
return 0
}
@@ -40,11 +40,14 @@ install() {
local _tempfile
# Ensure that required tools are available
inst_multiple chzdev lszdev vmcp
inst_multiple chzdev lszdev vmcp /lib/s390-tools/zdev_id
# Hook to parse zdev kernel parameter
inst_hook cmdline 95 "$moddir/parse-zdev.sh"
# Rule to automatically enable devices when running in DPM
inst_rules "81-dpm.rules"
# Obtain early + root device configuration
_tempfile=$(mktemp --tmpdir dracut-zdev.XXXXXX)
chzdev --export "$_tempfile" --persistent --by-path / --quiet \

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright IBM Corp. 2017
# Copyright IBM Corp. 2017,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.
@@ -15,11 +15,14 @@
# where
#
# no-auto: Indicates that firmware-provided I/O configuration data
# should not be applied.
# should not be applied. This also affects automatic
# activation of PCI and crypto devices when running in a DPM
# LPAR.
#
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
zdev_id="/lib/s390-tools/zdev_id"
if [ -e "$zdev_fw_file" ] ; then
zdev_auto=1
@@ -35,4 +38,22 @@ done
if [ $zdev_auto -eq 1 ] ; then
chzdev --import "$zdev_fw_file" $zdev_base_args
# Get information about DPM environment
for line in $($zdev_id) ; do
eval "$line"
done
if [ "$ZDEV_IS_DPM,$ZDEV_NEST_LEVEL,$ZDEV_HYPERVISOR_0" = "1,1,LPAR" ] ; then
# Manually iterate over existing PCI devices - there is a udev rule
# that handles this for PCI devices added at runtime but that doesn't
# work for PCI devices defined before boot because there is no coldplug
# trigger for /sys/bus/pci/slots
for slot in /sys/bus/pci/slots/* ; do
read power < "$slot/power"
if [ "$power" = "0" ] ; then
echo 1 > "$slot/power"
fi
done
fi
fi

View File

@@ -38,6 +38,9 @@ done
copy_exec /sbin/chzdev
copy_exec /sbin/lszdev
copy_exec /sbin/vmcp
copy_exec /lib/s390-tools/zdev_id
cp -p /usr/lib/udev/rules.d/81-dpm.rules "$DESTDIR/usr/lib/udev/rules.d/"
_tempfile=$(mktemp --tmpdir initramfs-zdev.XXXXXX)

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright IBM Corp. 2017
# Copyright IBM Corp. 2017,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.
@@ -15,7 +15,9 @@
# where
#
# no-auto: Indicates that firmware-provided I/O configuration data
# should not be applied.
# should not be applied. This also affects automatic
# activation of PCI and crypto devices when running in a DPM
# LPAR.
#
PREREQ="udev"
@@ -36,6 +38,7 @@ esac
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
zdev_id="/lib/s390-tools/zdev_id"
if [ -e "$zdev_fw_file" ] ; then
zdev_auto=1
@@ -64,4 +67,22 @@ if [ $zdev_auto -eq 1 ] ; then
udevadm trigger --type=subsystems --action=add
udevadm trigger --action=add
udevadm settle || true
# Get information about DPM environment
for line in $($zdev_id) ; do
eval "$line"
done
if [ "$ZDEV_IS_DPM,$ZDEV_NEST_LEVEL,$ZDEV_HYPERVISOR_0" = "1,1,LPAR" ] ; then
# Manually iterate over existing PCI devices - there is a udev
# rule that handles this for PCI devices added at runtime but
# that doesn't work for PCI devices defined before boot because
# there is no coldplug trigger for /sys/bus/pci/slots
for slot in /sys/bus/pci/slots/* ; do
read power < "$slot/power"
if [ "$power" = "0" ] ; then
echo 1 > "$slot/power"
fi
done
fi
fi

View File

@@ -73,7 +73,7 @@ lszdev_objects += lcs.o lcs_auto.o
# Generic CCW devtype
lszdev_objects += generic_ccw.o
all: chzdev lszdev
all: chzdev lszdev zdev_id
chzdev_usage.c: chzdev_usage.txt
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
@@ -95,16 +95,17 @@ libs = $(rootdir)/libutil/libutil.a
chzdev: $(chzdev_objects) $(libs)
lszdev: $(lszdev_objects) $(libs)
install: chzdev
install: chzdev zdev_id
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -c chzdev $(DESTDIR)$(BINDIR)
$(INSTALL) -c lszdev $(DESTDIR)$(BINDIR)
$(INSTALL) -c zdev_id $(DESTDIR)$(TOOLS_LIBDIR)
ifeq ($(HAVE_DRACUT),1)
$(INSTALL) -m 755 zdev-root-update.dracut \
$(DESTDIR)$(TOOLS_LIBDIR)/zdev-root-update
endif
clean:
@rm -f *.o chzdev lszdev chzdev_usage.c lszdev_usage.c gmon.out *.gcda *.gcno
@rm -f *.o chzdev lszdev chzdev_usage.c lszdev_usage.c gmon.out *.gcda *.gcno zdev_id
.PHONY: all install clean

154
zdev/src/zdev_id.c Normal file
View File

@@ -0,0 +1,154 @@
/*
* zdev_id - Determine system characteristics required by zdev udev rules
*
* Copyright IBM Corp. 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SYSINFO "/proc/sysinfo"
#define CMDLINE "/proc/cmdline"
#define WHITESPACE " \t\n"
static void array_add(char ***array_p, int *num_p, const char *str)
{
char **array = *array_p, *copy;
int num = *num_p + 1;
array = realloc(array, num * sizeof(char *));
copy = strdup(str);
if (!array || !copy)
err(1, "Could not allocate memory");
array[num - 1] = copy;
*array_p = array;
*num_p = num;
}
static void array_free(char **array, int num)
{
int i;
for (i = 0; i < num; i++)
free(array[i]);
free(array);
}
static void process_sysinfo(const char *filename)
{
char *line = NULL, *substr, **cps = NULL;
int is_dpm = 0, num_cps = 0, level, i;
FILE *fd;
size_t n;
fd = fopen(filename, "r");
if (!fd)
err(1, "Could not open sysinfo file '%s'", filename);
while (getline(&line, &n, fd) != -1) {
if (sscanf(line, "LPAR %m[^:]", &substr) == 1) {
if (strcmp(substr, "UUID") == 0) {
/* Heuristic: only DPM LPARs have UUIDs. */
is_dpm = 1;
} else if (strcmp(substr, "Number") == 0) {
array_add(&cps, &num_cps, "LPAR");
}
free(substr);
} else if (sscanf(line, "VM%*d Control Program: %ms ",
&substr) == 1) {
array_add(&cps, &num_cps, substr);
free(substr);
}
}
free(line);
fclose(fd);
/*
* ZDEV_NEST_LEVEL=n
*
* Virtualization nesting level of running system:
* 1: LPAR
* 2: first level VM
* 3: second level VM
*/
printf("ZDEV_NEST_LEVEL=%d\n", num_cps);
/*
* ZDEV_HYPERVISOR_<n>=LPAR|z/VM|KVM/Linux
*
* Type of hypervisor that provides virtualization at nesting level <n>.
*/
for (i = 0; i < num_cps; i++) {
/* Sysinfo lists VMs in reverse order .*/
level = (i == 0) ? i : (num_cps - i);
printf("ZDEV_HYPERVISOR_%d=\"%s\"\n", i, cps[level]);
}
array_free(cps, num_cps);
/*
* ZDEV_IS_DPM=0|1
*
* Indicator if top-level LPAR is managed by Dynamic Partition
* Manager (DPM):
* 0: Classic mode
* 1: DPM mode
*/
printf("ZDEV_IS_DPM=%d\n", is_dpm);
}
static void process_cmdline(const char *filename)
{
char *line = NULL, *substr, *next = NULL, *value;
int no_auto = 0;
size_t n;
FILE *fd;
fd = fopen(filename, "r");
if (!fd)
err(1, "Could not open cmdline file '%s'", filename);
if (getline(&line, &n, fd) == -1)
goto out;
for (substr = strtok_r(line, WHITESPACE, &next); substr;
substr = strtok_r(NULL, WHITESPACE, &next)) {
if (sscanf(substr, "rd.zdev=%ms ", &value) != 1)
continue;
if (strstr(value, "no-auto"))
no_auto = 1;
free(value);
}
out:
free(line);
fclose(fd);
/*
* ZDEV_NO_AUTO=0|1
*
* Indicator if auto-configuration is requested.
* 0: Perform auto-configuration
* 1: Skip auto-configuration
*/
printf("ZDEV_NO_AUTO=%d\n", no_auto);
}
int main(int argc, char *argv[])
{
char *sysinfo, *cmdline;
sysinfo = argc < 2 ? SYSINFO : argv[1];
cmdline = argc < 3 ? CMDLINE : argv[2];
process_sysinfo(sysinfo);
process_cmdline(cmdline);
return 0;
}

40
zdev/udev/81-dpm.rules Normal file
View File

@@ -0,0 +1,40 @@
# Automatically activate certain s390-specific device types that are attached
# in offline state when running in an LPAR managed by the Dynamic Partition
# Manager (DPM).
#
# Note that processing is restricted to DPM as there may be use cases for
# not enabling these device types in non-DPM mode.
#
# To deactivate this processing, specify the following on the kernel command
# line:
#
# rd.zdev=no-auto
ACTION!="add", GOTO="end_dpm"
# Handle newly added PCI devices in offline state
SUBSYSTEM=="slots", ATTR{power}=="0", GOTO="check_dpm"
# Handle newly added crypto devices in offline state
SUBSYSTEM=="ap", ENV{DEVTYPE}=="ap_card", ATTR{config}=="0", GOTO="check_dpm"
GOTO="end_dpm"
LABEL="check_dpm"
IMPORT{program}="/lib/s390-tools/zdev_id"
# Check for off-switch
ENV{ZDEV_NO_AUTO}=="1", GOTO="end_dpm"
# Check for DPM LPAR
ENV{ZDEV_IS_DPM}!="1", GOTO="end_dpm"
ENV{ZDEV_NEST_LEVEL}!="1", GOTO="end_dpm"
ENV{ZDEV_HYPERVISOR_0}!="LPAR", GOTO="end_dpm"
# Enable PCI device
SUBSYSTEM=="slots", ATTR{power}="1"
# Enable crypto device
SUBSYSTEM=="ap", ATTR{config}="1"
# End of rule
LABEL="end_dpm"

9
zdev/udev/Makefile Normal file
View File

@@ -0,0 +1,9 @@
# Common definitions
include ../../common.mak
UDEVDIR := /usr/lib/udev/rules.d/
RULES := 81-dpm.rules
install:
$(INSTALL) -m 755 -d $(DESTDIR)$(UDEVDIR)
$(INSTALL) -m 644 $(RULES) $(DESTDIR)$(UDEVDIR)/

View File

@@ -1,5 +1,7 @@
include ../common.mak
ALL_CFLAGS += -Wno-address-of-packed-member
#
# HAVE_FUSE: Allow to build zgetdump without mount support
#

View File

@@ -37,9 +37,10 @@ static int check_addr_max(struct df_s390_hdr *hdr, u64 addr_max)
/*
* Convert lowcore information into internal CPU representation
*/
void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max)
int df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max)
{
unsigned int i;
int rc;
if (hdr->version < 5 && hdr->magic == DF_S390_MAGIC) {
/* No Prefix registers in header */
@@ -53,8 +54,13 @@ void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max)
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
}
for (i = 0; i < hdr->cpu_cnt; i++)
dfi_cpu_add_from_lc(hdr->lc_vec[i]);
for (i = 0; i < hdr->cpu_cnt; i++) {
rc = dfi_cpu_add_from_lc(hdr->lc_vec[i]);
if (rc)
return rc;
}
return 0;
}
/*

View File

@@ -127,7 +127,7 @@ struct df_s390_dumper {
*/
extern void df_s390_hdr_add(struct df_s390_hdr *hdr);
extern void df_s390_em_add(struct df_s390_em *em);
extern void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max);
extern int df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max);
extern int df_s390_em_verify(struct df_s390_em *em, struct df_s390_hdr *hdr);
extern void df_s390_dumper_read(struct zg_fh *fh, int32_t blk_size,
struct df_s390_dumper *dumper);

View File

@@ -10,6 +10,8 @@
*/
#include <time.h>
#include "lib/util_log.h"
#include "zgetdump.h"
#define TIME_FMT_STR "%a, %d %b %Y %H:%M:%S %z"
@@ -160,7 +162,7 @@ static void mem_map_print(void)
/*
* Print each memory chunk if verbose specified
*/
if (g.opts.verbose_specified) {
if (g.opts.verbose) {
dfi_mem_chunk_iterate(mem_chunk) {
zero_str = "";
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
@@ -202,6 +204,10 @@ int dfi_mem_range_valid(u64 addr, u64 len)
struct dfi_mem_chunk *mem_chunk;
u64 addr_end = addr + len;
/* check for unsigned wrap */
if (addr_end < addr)
return 0;
do {
mem_chunk = dfi_mem_chunk_find(addr);
if (!mem_chunk)
@@ -236,7 +242,7 @@ void dfi_info_print(void)
STDERR("General dump info:\n");
STDERR(" Dump format........: %s\n", l.dfi->name);
if (l.attr.dfi_version)
STDERR(" Version............: %d\n", *l.attr.dfi_version);
STDERR(" Version............: %u\n", *l.attr.dfi_version);
date_print();
if (l.attr.dump_method)
STDERR(" Dump method........: %s\n", l.attr.dump_method);
@@ -248,15 +254,15 @@ void dfi_info_print(void)
STDERR(" UTS kernel version.: %s\n", l.attr.utsname->version);
}
if (l.attr.vol_nr)
STDERR(" Volume number......: %d\n", *l.attr.vol_nr);
STDERR(" Volume number......: %u\n", *l.attr.vol_nr);
if (l.attr.build_arch)
STDERR(" Build arch.........: %s\n",
dfi_arch_str(*l.attr.build_arch));
STDERR(" System arch........: %s\n", dfi_arch_str(l.arch));
if (l.cpus.cnt)
STDERR(" CPU count (online).: %d\n", l.cpus.cnt);
STDERR(" CPU count (online).: %u\n", l.cpus.cnt);
if (l.attr.real_cpu_cnt)
STDERR(" CPU count (real)...: %d\n", *l.attr.real_cpu_cnt);
STDERR(" CPU count (real)...: %u\n", *l.attr.real_cpu_cnt);
if (dfi_mem_range())
STDERR(" Dump memory range..: %lld MB\n",
TO_MIB(dfi_mem_range()));
@@ -313,7 +319,7 @@ static struct dfi_mem_chunk *mem_chunk_find(struct mem *mem, u64 addr)
{
struct dfi_mem_chunk *mem_chunk;
if (mem_chunk_has_addr(mem->chunk_cache, addr))
if (mem->chunk_cache && mem_chunk_has_addr(mem->chunk_cache, addr))
return mem->chunk_cache;
util_list_iterate(&mem->chunk_list, mem_chunk) {
if (mem_chunk_has_addr(mem_chunk, addr)) {
@@ -391,6 +397,11 @@ void dfi_mem_chunk_virt_add(u64 start, u64 size, void *data,
dfi_mem_chunk_read_fn read_fn,
dfi_mem_chunk_free_fn free_fn)
{
util_log_print(UTIL_LOG_DEBUG,
"DFI add %svirt mem chunk start 0x%016lx size 0x%016lx\n",
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
start, size);
if (size == 0)
return;
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn);
@@ -404,6 +415,11 @@ void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
dfi_mem_chunk_free_fn free_fn,
u32 volnr)
{
util_log_print(UTIL_LOG_DEBUG,
"DFI add %svol mem chunk start 0x%016lx size 0x%016lx volnr %u\n",
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
start, size, volnr);
if (size == 0)
return;
mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn);
@@ -605,6 +621,9 @@ struct util_list *dfi_cpu_list(void)
*/
void dfi_mem_read(u64 addr, void *buf, size_t cnt)
{
util_log_print(UTIL_LOG_TRACE,
"DFI virt mem read addr 0x%016lx size 0x%016lx\n",
addr, cnt);
mem_read(&l.mem_virt, addr, buf, cnt);
}
@@ -613,6 +632,9 @@ void dfi_mem_read(u64 addr, void *buf, size_t cnt)
*/
void dfi_mem_phys_read(u64 addr, void *buf, size_t cnt)
{
util_log_print(UTIL_LOG_TRACE,
"DFI phys mem read addr 0x%016lx size 0x%016lx\n",
addr, cnt);
mem_read(&l.mem_phys, addr, buf, cnt);
}
@@ -917,7 +939,7 @@ static void lc2cpu_32(struct dfi_cpu_32 *cpu, struct dfi_lowcore_32 *lc)
* Note: When this function is called, the memory chunks have to be already
* defined by the DFI dump specific code.
*/
void dfi_cpu_add_from_lc(u32 lc_addr)
int dfi_cpu_add_from_lc(u32 lc_addr)
{
struct dfi_cpu *cpu = dfi_cpu_alloc();
@@ -930,12 +952,14 @@ void dfi_cpu_add_from_lc(u32 lc_addr)
if (l.arch == DFI_ARCH_32) {
struct dfi_cpu_32 cpu_32;
struct dfi_lowcore_32 lc;
dfi_mem_read(lc_addr, &lc, sizeof(lc));
if (dfi_mem_read_rc(lc_addr, &lc, sizeof(lc)))
return -EINVAL;
lc2cpu_32(&cpu_32, &lc);
cpu_32_to_64(cpu, &cpu_32);
} else {
struct dfi_lowcore_64 lc;
dfi_mem_read(lc_addr, &lc, sizeof(lc));
if (dfi_mem_read_rc(lc_addr, &lc, sizeof(lc)))
return -EINVAL;
lc2cpu_64(cpu, &lc);
}
break;
@@ -943,6 +967,7 @@ void dfi_cpu_add_from_lc(u32 lc_addr)
ABORT("dfi_cpu_add_from_lc() called for CONTENT_NONE");
}
dfi_cpu_add(cpu);
return 0;
}
/*
@@ -1074,6 +1099,12 @@ static void kdump_init(void)
{
unsigned long base, size;
util_log_print(UTIL_LOG_TRACE, "DFI kdump initialization\n");
if (!dfi_mem_range_valid(0x10418, sizeof(base)))
return;
if (!dfi_mem_range_valid(0x10420, sizeof(size)))
return;
dfi_mem_phys_read(0x10418, &base, sizeof(base));
dfi_mem_phys_read(0x10420, &size, sizeof(size));
if (base == 0 || size == 0)
@@ -1084,6 +1115,9 @@ static void kdump_init(void)
return;
l.kdump_base = base;
l.kdump_size = size;
util_log_print(UTIL_LOG_INFO,
"DFI found valid kdump base 0x%016lx size 0x%016lx\n",
l.kdump_base, l.kdump_size);
/*
* For dumped kdump and user has selected "prod" we swap
* the crashkernel memory with old memory. If user selected "kdump",
@@ -1146,6 +1180,8 @@ static void utsname_init(void)
unsigned long ptr;
char buf[1024];
util_log_print(UTIL_LOG_TRACE, "DFI utsname initialization\n");
if (dfi_vmcoreinfo_symbol(&ptr, "init_uts_ns"))
return;
if (dfi_mem_read_rc(ptr, buf, sizeof(buf)))
@@ -1156,6 +1192,8 @@ static void utsname_init(void)
if (strncmp(utsname->sysname, "Linux", sizeof(utsname->version)) != 0)
return;
dfi_attr_utsname_set(utsname);
util_log_print(UTIL_LOG_INFO, "DFI utsname release %s version %s\n",
utsname->release, utsname->version);
}
/*
@@ -1165,6 +1203,8 @@ static void livedump_init(void)
{
u64 magic;
util_log_print(UTIL_LOG_TRACE, "DFI livedump initialization\n");
if (dfi_mem_read_rc(0, &magic, sizeof(magic)))
return;
if (magic == dfi_live_dump_magic)
@@ -1201,12 +1241,15 @@ int dfi_init(void)
struct dfi *dfi;
int i = 0, rc;
util_log_print(UTIL_LOG_TRACE, "DFI initialization\n");
l.arch = DFI_ARCH_UNKNOWN;
mem_init(&l.mem_virt);
mem_init(&l.mem_phys);
attr_init();
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
while ((dfi = dfi_vec[i])) {
util_log_print(UTIL_LOG_DEBUG, "DFI trying %s\n", dfi->name);
l.dfi = dfi;
g.fh = dfi_dump_open(g.opts.device);
rc = dfi->init();
@@ -1218,6 +1261,8 @@ int dfi_init(void)
utsname_init();
livedump_init();
}
util_log_print(UTIL_LOG_DEBUG, "DFI %s returned with rc %d\n",
dfi->name, rc);
if (rc == 0 || rc == -EINVAL)
return rc;
zg_close(g.fh);
@@ -1231,6 +1276,8 @@ int dfi_init(void)
*/
void dfi_exit(void)
{
util_log_print(UTIL_LOG_TRACE, "DFI exit\n");
if (l.dfi && l.dfi->exit)
l.dfi->exit();
}

View File

@@ -142,7 +142,7 @@ extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
extern void dfi_cpu_add(struct dfi_cpu *cpu);
extern unsigned int dfi_cpu_cnt(void);
extern enum dfi_cpu_content dfi_cpu_content(void);
extern void dfi_cpu_add_from_lc(u32 lc_addr);
extern int dfi_cpu_add_from_lc(u32 lc_addr);
#define DFI_VX_SA_SIZE (32 * 16)
extern int dfi_cpu_lc_has_vx_sa(void *lc);

View File

@@ -16,6 +16,8 @@
#include <string.h>
#include <unistd.h>
#include "lib/util_libc.h"
#include "lib/util_log.h"
#include "zgetdump.h"
/*
@@ -37,56 +39,68 @@ 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 == 0) {
/* Add zero memory chunk */
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, NULL,
dfi_mem_chunk_read_zero, NULL);
} else {
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_memsz, off_ptr,
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;
}
/*
* Skip name of note
*/
static void nt_name_skip(Elf64_Nhdr *note)
{
zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
}
/*
* Read note
*/
static int nt_read(Elf64_Nhdr *note, void *buf)
static int nt_read(const Elf64_Nhdr *note, void *buf, size_t buf_len)
{
off_t buf_len = ROUNDUP(note->n_descsz, 4);
char tmp_buf[buf_len];
ssize_t nread;
nt_name_skip(note);
if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len)
/* We cannot read more than the current note provides */
if (note->n_descsz < buf_len)
return -EINVAL;
if (buf)
memcpy(buf, tmp_buf, note->n_descsz);
/* 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 int nt_skip(Elf64_Nhdr *note)
static void nt_skip(const Elf64_Nhdr *note)
{
return nt_read(note, NULL);
/* 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);
}
/*
@@ -107,7 +121,7 @@ static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
struct dfi_cpu *cpu = dfi_cpu_alloc();
struct nt_prstatus_64 nt_prstatus;
if (nt_read(note, &nt_prstatus))
if (nt_read(note, &nt_prstatus, sizeof(nt_prstatus)))
return NULL;
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
@@ -126,7 +140,7 @@ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
struct nt_fpregset_64 nt_fpregset;
check_cpu(cpu, "FPREGSET");
if (nt_read(note, &nt_fpregset))
if (nt_read(note, &nt_fpregset, sizeof(nt_fpregset)))
return -EINVAL;
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
@@ -140,7 +154,7 @@ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TIMER");
return nt_read(note, &cpu->timer);
return nt_read(note, &cpu->timer, sizeof(cpu->timer));
}
/*
@@ -149,7 +163,7 @@ static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TODCMP");
return nt_read(note, &cpu->todcmp);
return nt_read(note, &cpu->todcmp, sizeof(cpu->todcmp));
}
/*
@@ -158,7 +172,7 @@ static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_TODPREG");
return nt_read(note, &cpu->todpreg);
return nt_read(note, &cpu->todpreg, sizeof(cpu->todpreg));
}
/*
@@ -167,7 +181,7 @@ static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_CTRS");
return nt_read(note, &cpu->ctrs);
return nt_read(note, &cpu->ctrs, sizeof(cpu->ctrs));
}
/*
@@ -176,7 +190,7 @@ static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_PREFIX");
return nt_read(note, &cpu->prefix);
return nt_read(note, &cpu->prefix, sizeof(cpu->prefix));
}
/*
@@ -185,7 +199,7 @@ static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_VXRS_LOW");
return nt_read(note, &cpu->vxrs_low);
return nt_read(note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
}
/*
@@ -194,7 +208,7 @@ static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
{
check_cpu(cpu, "S390_VXRS_HIGH");
return nt_read(note, &cpu->vxrs_high);
return nt_read(note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
}
/*
@@ -202,7 +216,6 @@ static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
*/
static int pt_notes_add(Elf64_Phdr *phdr)
{
u64 start_off = zg_tell(g.fh, ZG_CHECK);
struct dfi_cpu *cpu_current = NULL;
u64 notes_start_off;
Elf64_Nhdr note;
@@ -214,6 +227,8 @@ static int pt_notes_add(Elf64_Phdr *phdr)
rc = zg_read(g.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);
@@ -255,12 +270,10 @@ static int pt_notes_add(Elf64_Phdr *phdr)
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
break;
default:
if (nt_skip(&note))
return -EINVAL;
nt_skip(&note);
break;
}
}
zg_seek(g.fh, start_off, ZG_CHECK);
return 0;
}
@@ -287,9 +300,11 @@ static int read_elf_hdr(Elf64_Ehdr *ehdr)
static int dfi_elf_init(void)
{
Elf64_Ehdr ehdr;
Elf64_Phdr phdr;
Elf64_Phdr *phdr;
int i;
util_log_print(UTIL_LOG_DEBUG, "DFI ELF initialization\n");
if (read_elf_hdr(&ehdr) != 0)
return -ENODEV;
@@ -297,21 +312,32 @@ static int dfi_elf_init(void)
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++) {
zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK);
switch (phdr.p_type) {
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))
if (pt_load_add(&phdr[i])) {
free(phdr);
return -EINVAL;
}
break;
case PT_NOTE:
if (pt_notes_add(&phdr))
if (pt_notes_add(&phdr[i])) {
free(phdr);
return -EINVAL;
}
break;
default:
break;
}
}
free(phdr);
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
return 0;
}

View File

@@ -267,19 +267,25 @@ static int mem_init(void)
/*
* Initialize CPU information
*/
static void cpu_init(void)
static int cpu_init(void)
{
unsigned int i;
int rc;
if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) {
/* Old LKCD dump without asm header */
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
return;
return 0;
}
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
for (i = 0; i < l.hdr_asm.cpu_cnt; i++)
dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[i]);
for (i = 0; i < l.hdr_asm.cpu_cnt; i++) {
rc = dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[i]);
if (rc)
return rc;
}
return 0;
}
/*
@@ -321,7 +327,8 @@ static int dfi_lkcd_init(void)
return -ENODEV;
if (mem_init() != 0)
return -EINVAL;
cpu_init();
if (cpu_init() != 0)
return -EINVAL;
return 0;
}

View File

@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
#include "lib/util_log.h"
#include "zgetdump.h"
/*
@@ -68,6 +69,9 @@ static int read_s390_hdr(void)
return -ENODEV;
if (l.hdr.magic != magic_number)
return -ENODEV;
if (l.hdr.cpu_cnt > DF_S390_CPU_MAX)
return -ENODEV;
util_log_print(UTIL_LOG_INFO, "DFI S390 version %u\n", l.hdr.version);
df_s390_hdr_add(&l.hdr);
return 0;
}
@@ -95,6 +99,9 @@ static int mem_chunks_add(void)
{
u64 rc;
util_log_print(UTIL_LOG_DEBUG, "DFI S390 mem_size 0x%016lx\n",
l.hdr.mem_size);
/* Single memory chunk for non-extended dump format */
dfi_mem_chunk_add(0, l.hdr.mem_size, NULL,
dfi_s390_mem_chunk_read,
@@ -112,39 +119,43 @@ static int mem_chunks_add(void)
*/
static int mem_chunks_add_ext(void)
{
struct df_s390_dump_segm_hdr dump_segm;
u64 rc, *off_ptr, old = 0, dump_size = 0;
struct df_s390_dump_segm_hdr dump_segm = { 0 };
u64 rc, off, old = 0, dump_size = 0;
off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = zg_seek(g.fh, DF_S390_HDR_SIZE, ZG_CHECK_NONE);
if (*off_ptr != DF_S390_HDR_SIZE)
off = zg_seek(g.fh, DF_S390_HDR_SIZE, ZG_CHECK_NONE);
if (off != DF_S390_HDR_SIZE)
return -EINVAL;
while (*off_ptr < DF_S390_HDR_SIZE + l.hdr.mem_size - PAGE_SIZE) {
while (off < DF_S390_HDR_SIZE + l.hdr.mem_size - PAGE_SIZE) {
rc = zg_read(g.fh, &dump_segm, PAGE_SIZE, ZG_CHECK_ERR);
if (rc != PAGE_SIZE)
return -EINVAL;
*off_ptr += PAGE_SIZE;
util_log_print(UTIL_LOG_DEBUG,
"DFI S390 dump segment start 0x%016lx size 0x%016lx stop marker %d\n",
dump_segm.start, dump_segm.len, dump_segm.stop_marker);
off += PAGE_SIZE;
/* Add zero memory chunk */
dfi_mem_chunk_add(old, dump_segm.start - old, NULL,
dfi_mem_chunk_read_zero, NULL);
/* Add memory chunk for a dump segment */
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = off;
dfi_mem_chunk_add(dump_segm.start, dump_segm.len, off_ptr,
dfi_s390_ext_mem_chunk_read, zg_free);
off_ptr = NULL;
old = dump_segm.start + dump_segm.len;
dump_size += dump_segm.len;
off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
off = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
if (dump_segm.stop_marker)
break;
}
/* Check if the last dump segment found */
if (!dump_segm.stop_marker)
return -EINVAL;
/* Add zero memory chunk at the end */
dfi_mem_chunk_add(old, l.hdr.mem_size - old, NULL,
dfi_mem_chunk_read_zero, NULL);
/* Set the actual size of the dump file */
dfi_attr_file_size_set(dump_size);
/* Check if the last dump segment found */
if (!dump_segm.stop_marker)
return -EINVAL;
/* Read and verify the end marker */
return read_s390_em();
}
@@ -156,6 +167,9 @@ int dfi_s390_init_gen(bool extended)
{
int rc;
util_log_print(UTIL_LOG_DEBUG, "DFI S390 %sinitialization\n",
extended ? "extended " : "");
l.extended = extended;
if (read_s390_hdr() != 0)
return -ENODEV;
@@ -165,7 +179,9 @@ int dfi_s390_init_gen(bool extended)
rc = mem_chunks_add_ext();
if (rc)
return rc;
df_s390_cpu_info_add(&l.hdr, l.hdr.mem_size);
rc = df_s390_cpu_info_add(&l.hdr, l.hdr.mem_size);
if (rc)
return rc;
zg_seek(g.fh, sizeof(l.hdr), ZG_CHECK);
return 0;
}

View File

@@ -574,6 +574,8 @@ static void set_magic_numbers(void)
*/
int dfi_s390mv_init_gen(bool extended)
{
int rc;
l.extended = extended;
set_magic_numbers();
if (open_dump() != 0)
@@ -590,7 +592,9 @@ int dfi_s390mv_init_gen(bool extended)
return -EINVAL;
if (l.dump_incomplete)
return -EINVAL;
df_s390_cpu_info_add(&l.hdr, l.hdr.mem_end);
rc = df_s390_cpu_info_add(&l.hdr, l.hdr.mem_end);
if (rc)
return rc;
df_s390_em_add(&l.em);
return 0;
}

View File

@@ -166,7 +166,9 @@ static int vol_init(void)
/* Init memory read & CPU info */
mem_read_init();
df_s390_cpu_info_add(&hdr, hdr.mem_size - 1);
rc = df_s390_cpu_info_add(&hdr, hdr.mem_size - 1);
if (rc)
return rc;
return 0;
}

View File

@@ -12,6 +12,7 @@
#include <elf.h>
#include "lib/zt_common.h"
#include "lib/util_log.h"
#include "zgetdump.h"
@@ -62,7 +63,10 @@ static struct os_info *os_info_get(void)
static struct os_info os_info;
unsigned long addr;
dfi_mem_read(LC_OS_INFO, &addr, sizeof(addr));
util_log_print(UTIL_LOG_TRACE, "DFI get osinfo\n");
if (dfi_mem_read_rc(LC_OS_INFO, &addr, sizeof(addr)))
return NULL;
if (addr % 0x1000)
return NULL;
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
@@ -83,17 +87,23 @@ void dfi_vmcoreinfo_init(void)
Elf64_Nhdr note;
char str[128];
util_log_print(UTIL_LOG_TRACE, "DFI vmcoreinfo initialization\n");
l.os_info = os_info_get();
if (l.os_info && l.os_info->vmcoreinfo_size) {
util_log_print(UTIL_LOG_DEBUG, "DFI found valid osinfo\n");
addr = l.os_info->vmcoreinfo_addr;
size = l.os_info->vmcoreinfo_size;
} else {
dfi_mem_read(LC_VMCORE_INFO, &addr, sizeof(addr));
if (dfi_mem_read_rc(LC_VMCORE_INFO, &addr, sizeof(addr)))
return;
if (addr == 0)
return;
if (dfi_mem_read_rc(addr, &note, sizeof(note)))
return;
if (note.n_namesz == 0 || note.n_namesz > sizeof(str))
return;
memset(str, 0, sizeof(str));
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
return;
@@ -102,6 +112,9 @@ void dfi_vmcoreinfo_init(void)
size = note.n_descsz;
addr += 24;
}
util_log_print(UTIL_LOG_DEBUG,
"DFI vmcoreinfo addr 0x%016lx size 0x%016lx\n",
addr, size);
l.vmcoreinfo = zg_alloc(size + 1);
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
zg_free(l.vmcoreinfo);
@@ -109,6 +122,7 @@ void dfi_vmcoreinfo_init(void)
return;
}
l.vmcoreinfo[size] = 0;
util_log_print(UTIL_LOG_INFO, "DFI found valid vmcoreinfo\n");
}
/*

View File

@@ -15,6 +15,7 @@
#include <string.h>
#include "lib/zt_common.h"
#include "lib/util_log.h"
#include "zgetdump.h"
/*
@@ -46,7 +47,10 @@ static char help_text[] =
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
"-d, --device Print DUMPDEV (dump device) information\n"
"-v, --version Print version information, then exit\n"
"-V, --verbose Show detailed layout of memory map on printing DUMP information\n"
"-V, --verbose Print verbose messages to stdout. Repeat this option\n"
" for increased verbosity from just error messages to\n"
" also include warning, information, debug, and trace\n"
" 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";
@@ -71,6 +75,9 @@ static void init_defaults(void)
g.opts.fmt = "s390";
#endif
dfo_set(g.opts.fmt);
/* Verbose logging */
g.opts.verbose = UTIL_LOG_ERROR;
util_log_set_level(g.opts.verbose);
}
/*
@@ -265,7 +272,8 @@ void opts_parse(int argc, char *argv[])
case 'v':
print_version_exit();
case 'V':
g.opts.verbose_specified = 1;
g.opts.verbose++;
util_log_set_level(g.opts.verbose);
break;
case 'i':
action_set(ZG_ACTION_DUMP_INFO);

View File

@@ -49,7 +49,7 @@ static void sig_exit(int sig)
*/
static void sig_handler_init(void)
{
struct sigaction sigact;
struct sigaction sigact = { 0 };
/* Ignore signals SIGUSR1 and SIGUSR2 */
if (sigemptyset(&sigact.sa_mask) < 0)

View File

@@ -35,7 +35,7 @@ struct options {
int argc_fuse;
const char *select;
int select_specified;
int verbose_specified;
int verbose;
};
extern const char *OPTS_SELECT_KDUMP;

View File

@@ -130,6 +130,37 @@ int strncmp(const char *s1, const char *s2, unsigned long count)
return 0;
}
/**
* Find the first occurrence of character in string.
* STR: address of the string, which is different from NULL!
*/
char *strchr(const char *str, char c)
{
while (*str) {
if (*str == c)
return (char *)str;
str++;
}
return NULL;
}
/**
* Minimalistic hash function on strings.
* It doesn't provide perfect quality of distribution.
*
* STR: null-terminated string
* HASH_SIZE: number of buckets
*/
unsigned int strhash(unsigned char *str, unsigned int hash_size)
{
unsigned int sum = 0;
while (*str)
sum += 5 * *(str++);
return sum % hash_size;
}
static int skip_atoi(const char **c)
{
int i = 0;

View File

@@ -54,6 +54,8 @@ char *strcat(char *, const char *);
int strncmp(const char *, const char *, unsigned long);
int strlen(const char *);
char *strcpy(char *, const char *);
char *strchr(const char *str, char c);
unsigned int strhash(unsigned char *str, unsigned int hash_size);
unsigned long get_zeroed_page(void);
void free_page(unsigned long);
void initialize(void);

View File

@@ -131,6 +131,205 @@ secure_boot_enabled()
return rc;
}
#define ZIPL_ENVBLK_SIGNATURE "# zIPL Environment Block\n"
#define STR_HASH_SIZE (16)
struct env_hash_entry {
unsigned int next;
unsigned int name;
};
/**
* ITEMS: pointer to pre-allocated page, where the new item will be allocated
* NAME: null-terminated name
*/
static void hash_table_add(struct env_hash_entry *items,
struct env_hash_entry **buckets,
unsigned int *new_idx,
unsigned long name)
{
struct env_hash_entry *new;
unsigned int hash;
new = &items[*new_idx];
new->name = name;
hash = strhash((unsigned char *)name, STR_HASH_SIZE);
new->next = (unsigned long)buckets[hash];
buckets[hash] = new;
(*new_idx)++;
}
struct env_hash_entry *hash_table_find(struct env_hash_entry **buckets,
char *name)
{
struct env_hash_entry *item;
if (strlen(name) == 0)
return NULL;
item = buckets[strhash((unsigned char *)name, STR_HASH_SIZE)];
while (item) {
if (strlen((char *)(unsigned long)item->name) ==
strlen(name) &&
!strncmp((char *)(unsigned long)item->name,
name, strlen(name)))
return item;
item = (struct env_hash_entry *)(unsigned long)item->next;
}
return NULL;
}
/**
* Format of environment block:
*
* magic'\n'name1=value1'\n'...nameN=valueN'\n'zero-padding
*/
static void parse_envblk(struct env_hash_entry *items,
struct env_hash_entry **buckets,
unsigned int *nr)
{
char sgn[] = ZIPL_ENVBLK_SIGNATURE;
unsigned int len = 0;
unsigned int off = 0;
char *name;
char *value;
ebcdic_to_ascii((unsigned char *)sgn,
(unsigned char *)sgn, sizeof(sgn) - 1);
if (strncmp((char *)_stage3_parms.envblk_addr, sgn, sizeof(sgn) - 1)) {
printf("Bad envblk\n");
return;
}
/* we rely that environment block is consistent */
name = (char *)_stage3_parms.envblk_addr + sizeof(sgn) - 1;
/*
* calculate significant length of the environment block
* (excluding trailing zeros)
*/
while (len < _stage3_parms.envblk_len) {
if (name[len] == 0)
break;
len++;
}
while (off < len) {
value = strchr(name, 0x3D /* = */) + 1;
/* null-terminate the name */
*(value - 1) = 0;
hash_table_add(items, buckets, nr, (unsigned long)name);
off += (value - name); /* offset of the value */
name = strchr(value, 0x0A /* /n */) + 1;
/* null-terminate the value */
*(name - 1) = 0;
off += (name - value); /* offset of the next name (if any) */
}
}
/**
* Find all instances of the pattern ${NAME} in the command line, and replace
* each one with corresponding VALUE as found in the hash table by NAME
*
* CMDL_LEN: length of the command line to be processed
* BUCKETS: hash table of pairs (NAME, VALUE)
*/
void process_parm_line(struct env_hash_entry **buckets, unsigned int cmdl_len)
{
struct env_hash_entry *item;
char empty_str = 0;
char *cmdl_end;
char *start;
char *end;
char *val;
int len;
start = (char *)COMMAND_LINE;
cmdl_end = start + cmdl_len;
while (start < cmdl_end) {
start = strchr(start, 0x24 /* $ */);
if (start == NULL || start + 3 >= cmdl_end)
/* no more instances */
break;
if (*(start + 1) != 0x7B /* { */) {
/* skip "$" without braces */
start += 1;
continue;
}
end = strchr(start, 0x7D /* } */);
if (end == NULL || end >= cmdl_end)
/* no more instances */
break;
/* terminate the NAME and find it in the hash table */
*end = 0;
item = hash_table_find(buckets, start + 2);
if (!item) {
/*
* Item to replace with not found.
* The case of "${}" (empty NAME) also gets here!
*
* Assign the empty string to remove the instance
* from the command line
*/
val = &empty_str;
} else {
val = (char *)(unsigned long)item->name +
strlen((char *)(unsigned long)item->name) + 1;
}
/*
* try to replace the instance by VALUE as found
* in the hash table by NAME
*/
len = strlen(val);
if (cmdl_len + len - (end - start + 1) >= COMMAND_LINE_SIZE)
/* VALUE doesn't fit */
break;
/*
* make a room with the beginning at "$" by moving
* the rest of parm line at the right of "}" to @dst,
* which is (offset of "$" + size of the VALUE")
*/
memmove(start + len, end + 1, cmdl_end - (end + 1));
/*
* copy VALUE to the room
*/
memcpy(start, val, len);
cmdl_end += (len - (end - start + 1));
start += len;
}
if (cmdl_len > cmdl_end - (char *)COMMAND_LINE)
memset(cmdl_end, 0,
cmdl_len - (cmdl_end - (char *)COMMAND_LINE));
}
/**
* LEN: length of the command line to be processed
*/
static void handle_environment(unsigned int len)
{
struct env_hash_entry *buckets[STR_HASH_SIZE];
struct env_hash_entry *items;
unsigned int nr_items = 0;
if (_stage3_parms.envblk_addr == 0 ||
_stage3_parms.envblk_addr == UNSPECIFIED_ADDRESS)
return;
memset(buckets, 0, sizeof(buckets));
items = (struct env_hash_entry *)get_zeroed_page();
/*
* scan in-memory environment block and populate hash table
*/
parse_envblk(items, buckets, &nr_items);
/*
* find environment variables in the command line and
* replace them with their values as found in the hash table
*/
process_parm_line(buckets, len);
free_page((unsigned long)items);
}
void start(void)
{
unsigned int subchannel_id;
@@ -171,6 +370,9 @@ void start(void)
/* terminate \0 */
cmdline[COMMAND_LINE_SIZE - 1] = 0;
}
/* determine length of original parm line */
cmdline_len = MIN(strlen((const char *)cmdline),
COMMAND_LINE_SIZE - 1);
/* convert extra parameter to ascii */
if (!_stage3_parms.extra_parm || !*cextra)
@@ -187,11 +389,6 @@ void start(void)
cextra++;
cextra_len--;
}
/* determine length of original parm line */
cmdline_len = MIN(strlen((const char *)cmdline),
COMMAND_LINE_SIZE - 1);
/*
* if extra parm string starts with '=' replace original string,
* else append
@@ -214,8 +411,9 @@ void start(void)
/* terminate 0 */
cmdline[cmdline_len + cextra_len] = 0;
}
noextra:
handle_environment(cmdline_len + cextra_len);
/* copy initrd start address and size intop new kernle space */
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;

View File

@@ -36,8 +36,10 @@ struct stage3_parms {
unsigned long long flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
unsigned long long image_len; /* length of kernel */
unsigned long long image_addr; /* target address of kernel */
unsigned long long envblk_addr; /* address of environment block */
unsigned long long envblk_len; /* size of environment block */
};
STATIC_ASSERT(sizeof(struct stage3_parms) == 8 * 8)
STATIC_ASSERT(sizeof(struct stage3_parms) == 8 * 10)
extern struct stage3_parms _stage3_parms;
extern void kdump_stage3();

View File

@@ -296,7 +296,8 @@ int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job);
int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
address_t initrd_addr, size_t initrd_len,
address_t entry, int extra_parm, uint64_t flags,
address_t image_addr, size_t image_len);
address_t image_addr, size_t image_len,
address_t envblk_addr, size_t envblk_len);
int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
address_t initrd_addr, address_t image_addr);
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);

View File

@@ -17,10 +17,20 @@
#include "disk.h"
#include "job.h"
#include "zipl.h"
#include "stddef.h"
#define BOOTMAP_HEADER_VERSION 1
#define SIGNATURE_MAGIC "~Module signature appended~\n"
#define PKCS7_FORMAT 0x01
struct bootmap_header {
char header_text[48];
u64 version;
u64 envblk_offset;
char reserved[448];
};
struct signature_header {
uint8_t format;
uint8_t reserved[3];
@@ -50,6 +60,9 @@ struct file_signature {
#define PKEY_ID_PKCS7 0x02
int bootmap_header_init(int fd);
int bootmap_header_read(int fd, struct bootmap_header *bh);
int bootmap_header_write(int fd, struct bootmap_header *bh);
int bootmap_create(struct job_data* job, disk_blockptr_t* program_table,
disk_blockptr_t *scsi_dump_sb_blockptr,
disk_blockptr_t** stage2_list, blocknum_t* stage2_count,

View File

@@ -90,6 +90,11 @@ struct disk_info {
definition_t targetbase;
};
struct file_range {
off_t offset;
size_t len;
};
struct job_target_data;
int disk_get_info(const char* device, struct job_target_data* target,
@@ -112,16 +117,23 @@ int disk_write_block_aligned(int fd, const void* data, size_t bytecount,
blocknum_t disk_write_block_buffer(int fd, int fd_is_basedisk,
const void* buffer, size_t bytecount,
disk_blockptr_t** blocklist,
struct disk_info* info);
struct disk_info *info);
blocknum_t disk_write_block_buffer_align(int fd, int fd_is_basedisk,
const void *buffer, size_t bytecount,
disk_blockptr_t **blocklist,
struct disk_info *info, int align,
off_t *offset);
void disk_print_devt(dev_t d);
void disk_print_info(struct disk_info* info);
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
struct disk_info* info);
blocknum_t disk_get_blocklist_from_file(const char* filename,
struct file_range *reg,
disk_blockptr_t** blocklist,
struct disk_info* pinfo);
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
void disk_print_geo(struct disk_info *data);
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
#endif /* not DISK_H */

34
zipl/include/envblk.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* s390-tools/zipl/include/bootmap.h
* Functions to handle environment block.
*
* Copyright IBM Corp. 2020
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*/
#include "boot/s390.h" /* for PAGE_SIZE */
#define ZIPL_ENVBLK_SIGNATURE "# zIPL Environment Block\n"
#define ENVBLK_DEFAULT_IMPORT_SOURCE "/etc/ziplenv"
/*
* The following limit can be increased. It requires modifications
* of the code, which is responsible for handling environment in
* stage3, which will increase its memory footprint.
*/
#define ENVBLK_MAX_LINES (PAGE_SIZE / 8 /* sizeof(struct env_hash_entry */)
#define ENVBLK_MAX_IMPORT_SIZE(envblk_size) \
(envblk_size - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1)
int envblk_offset_get(int fd, off_t *off);
int envblk_offset_set(int fd, off_t off);
int envblk_size_get(int fd, int *size);
int envblk_check_name(char *name, int len);
int envblk_import(char *from, char *to, int size);
char *envblk_next_line(char *s, const char *end);
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name));
void envblk_print(char *envblk, unsigned int envblk_size);
void envblk_create_blank(char *envblk, int envblk_len);

View File

@@ -46,9 +46,15 @@ struct job_ipl_data {
address_t image_addr;
address_t parm_addr;
address_t ramdisk_addr;
address_t envblk_addr;
int is_kdump;
};
struct job_envblk_data {
int size;
char *buf;
};
struct job_segment_data {
char* segment;
address_t segment_addr;
@@ -108,6 +114,7 @@ struct job_menu_data {
struct job_data {
enum job_id id;
struct job_target_data target;
struct job_envblk_data envblk;
char* name;
union {
struct job_ipl_data ipl;

View File

@@ -14,6 +14,7 @@
#include <stdint.h>
#include "lib/zt_common.h"
#include "lib/util_base.h"
#include "boot/loaders_layout.h"
#define ZIPL_MAGIC "zIPL"
@@ -35,7 +36,7 @@
#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf"
#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf"
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin"
#define ZIPL_STAGE3_PATH util_libdir_path("stage3.bin")
#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure"
#define MENU_DEFAULT_PROMPT 0

View File

@@ -9,10 +9,14 @@ install:
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \
> $(DESTDIR)$(MANDIR)/man8/zipl.8
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl-editenv.8.in \
> $(DESTDIR)$(MANDIR)/man8/zipl-editenv.8
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \
> $(DESTDIR)$(MANDIR)/man5/zipl.conf.5
chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \
$(DESTDIR)$(MANDIR)/man8/zipl-editenv.8 \
$(DESTDIR)$(MANDIR)/man5/zipl.conf.5
clean:

156
zipl/man/zipl-editenv.8.in Normal file
View File

@@ -0,0 +1,156 @@
.\" Copyright 2020 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH ZIPL-EDITENV 8 "Feb 2020" "s390-tools"
.SH NAME
zipl-editenv \- zIPL environment editor.
.SH SYNOPSIS
.B zipl-editenv
[OPTION] [ARGUMENT]
.SH DESCRIPTION
.B zipl-editenv
is a tool to manipulate with zIPL environment of partitions, prepared for
initial program load (IPL) by
.B zipl(8)
utility. This tool performs atomic updates of boot data without modifying the
boot record referencing that data. The following operations are supported:
.IP " -"
Print environment
.IP " -"
Assign a specified value to any environment variable
.IP " -"
Remove a specified variable from the environment
.IP " -"
Remove all variables from the environment
.PP
.B zIPL environment
(don't confuse with shell environment!)
is a set of variables with their values. All the variables are identified by
their unique names. Names of zIPL environment variables have to satisfy POSIX
requirements for shell environment variables (IEEE Std 1003.1-2001).
That is, such names should consist solely of uppercase letters, digits, and
the '_' (underscore) from the characters defined in Portable Character Set and
should not begin with a digit.
zIPL environment is defined per boot partition by a special boot component
called
.B zIPL environment block.
This component is used for evaluation of variables in another boot component,
kernel command line.
zIPL environment variables can be used in kernel parameter string, where they
should be specified by their names as ${NAME}. During boot, such variables
are replaced with their current values, as defined by the installed environment.
Maximum number of zIPL environment variables per boot partition is 512.
zIPL environment is installed every time when preparing a device for initial
program load (IPL) by
.B zipl(8)
utility. At installation time the environment by default is imported from
user-editable file
.B '/etc/ziplenv'.
That default location can be overridden by respective
.B zipl(8)
option.
Installed zIPL environment (AKA zIPL environment block) can be accessed
(modified, printed) by this
.B zipl-editenv(8)
tool. All modifications made with this tool don't affect the file
.B '/etc/ziplenv'.
Hence, those modifications will be lost after the next boot record
re-installation, unless
.B '/etc/ziplenv'
was updated respectively.
.SH OPTIONS
.TP
.BR "\-h" " or " "\-\-help"
Print usage information, then exit.
.TP
.BR "\-v" " or " "\-\-version"
Print version information, then exit.
.TP
.BR "\-t <TARGET DIRECTORY>" " or " "\-\-target <TARGET DIRECTORY>"
Specify a directory, where the environment was installed. This directory should
contain boot data (bootmap file). Default value is "/boot". A similar option
with the same name exists also for
.B zipl(8)
utility.
.TP
.BR "\-l" " or " "\-\-list"
Print zIPL environment, that is a list of all zIPL environment variables with
their current values.
.TP
.BR "\-s <NAME=VALUE>" " or " "\-\-set <NAME=VALUE>"
Assign value
.B VALUE
to the variable with name
.B NAME.
.B NAME
has to satisfy the requirements above.
.B VALUE
may consist of any printable characters different from the new line symbol.
If variable with such name didn't exist in the environment,
then it will be added.
.TP
.BR "\-u <NAME>" " or " "\-\-unset <NAME>"
Remove the variable with name
.B NAME
from zIPL environment.
.TP
.BR "\-r" " or " "\-\-reset"
Remove all variables from zIPL environment.
.SH FILES
.B /etc/ziplenv
- "initial environment" (or "environment file").
A regular file containing zIPL environment to be installed every time when
preparing a device for initial program load (IPL) by
.B zipl(8)
utility.
Don't confuse environment file with environment block!
In contrast with environment block, environment file is not a boot component
and is not used at boot time by any boot processes.
This file contains pairs
.B NAME=VALUE
delimited by new line symbols (i.e. each pair on a separate line).
.B NAME
is identified as a sequence of characters before the leftmost '=' in the line.
.B NAME
has to satisfy the requirements above. Lines beginning from "#" are ignored.
Lines with not identified
.B NAME
are ignored. If lines contain identical names,
then only the last one takes an effect. If the file defines more than 512
variables, or if the environment defined by that file doesn't fit to the
environment block, then
.B zipl(8)
will fail to import such file.
This file can be modified, using any suitable text editor.
.SH NOTES
Any installed zIPL environment (environment block) should be accessed only
by this
.B zipl-editenv
tool. Using other ways to access the environment block is not allowed.
.TP
.SH SEE ALSO
.BR zipl (8),
.BR zipl.conf (5)

View File

@@ -138,6 +138,11 @@ the target directory. Supported devices are DASD and SCSI disks.
It is not possible to specify both this parameter and the name of a menu
or configuration section on the command line at the same time.
.TP
.BR "\-\-environment=<FILE>"
Import zIPL environment from specified <FILE>. If none is supplied,
it will be imported from "/etc/ziplenv".
.TP
.BR "\-\-targetbase=<BASE DEVICE>"
Install the actual boot loader on the device node specified by BASE DEVICE.
@@ -229,6 +234,9 @@ and <PARMLINE> are appended to form the resulting command line.
It is not possible to specify both this parameter and the name of a menu
section on the command line at the same time.
The command line can contain zIPL environment variables (see
.BR zipl-editenv(8)).
.TP
.BR "\-k auto" " or " "\-\-kdump=auto"
Install a kdump kernel that can be used as a stand-alone dump tool. You
@@ -401,4 +409,5 @@ for example via the "/dev/disk/by-path/" device nodes.
.SH SEE ALSO
.BR zipl.conf (5),
.BR zipl-editenv (8),
.BR kexec (8)

View File

@@ -457,6 +457,8 @@ This option cannot be used together with either
Use this option to specify a parameter line which will be passed to the Linux
kernel at boot time. Note that the parameter line may be enclosed in quotes
(" or ') to preserve leading and trailing spaces.
The parameter line can contain zIPL environment variables (see
.B zipl-editenv(8)).
.PP
.B parmfile
@@ -710,4 +712,5 @@ for example via the "/dev/disk/by-path/" device nodes.
.SH SEE ALSO
.BR zipl (8),
.BR zipl-editenv (8),
.BR zgetdump (8)

View File

@@ -9,14 +9,14 @@ ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
libs = $(rootdir)/libutil/libutil.a
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
install.o zipl.o $(rootdir)/zipl/boot/data.o
objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \
bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
zipl_stage3 = ../boot/stage3.bin
all: zipl $(chreipl_helpers) $(zipl_stage3)
all: zipl zipl-editenv $(chreipl_helpers) $(zipl_stage3)
zipl: $(objects) $(libs)
@@ -24,19 +24,23 @@ zipl_helper.device-mapper: $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libvtoc/libvtoc.a \
$(libs) zipl_helper.device-mapper.o
zipl-editenv: $(libs) misc.o error.o fs-map.o bootmap_header.o \
envblk.o zipl-editenv.o
chreipl_helper.%: zipl_helper.%
ln -f -s $< $@
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl-editenv $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
$(DESTDIR)$(TOOLS_LIBDIR)
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
$(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR)
clean:
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl zipl-editenv
.PHONY: all install clean

View File

@@ -82,7 +82,8 @@ int
boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
address_t initrd_addr, size_t initrd_len,
address_t entry, int extra_parm, uint64_t flags,
address_t image_addr, size_t image_len)
address_t image_addr, size_t image_len,
address_t envblk_addr, size_t envblk_len)
{
struct stage3_parms params;
void* data;
@@ -106,6 +107,8 @@ boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
params.flags = flags;
params.image_len = (uint64_t) image_len;
params.image_addr = (uint64_t) image_addr;
params.envblk_addr = (uint64_t) envblk_addr;
params.envblk_len = (uint64_t) envblk_len;
/* Initialize buffer */
memcpy(data, &params, sizeof(params));
*buffer = data;

View File

@@ -17,6 +17,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include "lib/zt_common.h"
#include "lib/util_part.h"
@@ -26,19 +27,15 @@
#include "boot.h"
#include "bootmap.h"
#include "envblk.h"
#include "disk.h"
#include "error.h"
#include "install.h"
#include "misc.h"
/* Header text of the bootmap file */
static const char header_text[] = "zSeries bootmap file\n"
"created by zIPL\n";
/* Pointer to dedicated empty block in bootmap. */
disk_blockptr_t empty_block;
/* Get size of a bootmap block pointer for disk with given INFO. */
static int
get_blockptr_size(struct disk_info* info)
@@ -291,10 +288,11 @@ struct component_loc {
};
static int
add_component_file(int fd, const char* filename, address_t load_address,
size_t trailer, void *component, int add_files,
struct disk_info* info, struct job_target_data* target,
struct component_loc *location)
add_component_file_range(int fd, const char *filename, struct file_range *reg,
address_t load_address,
size_t trailer, void *component, int add_files,
struct disk_info *info, struct job_target_data *target,
struct component_loc *location)
{
struct disk_info* file_info;
struct component_loc loc;
@@ -306,6 +304,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
int rc;
if (add_files) {
assert(reg == NULL); /* not implemented */
/* Read file to buffer */
rc = misc_read_file(filename, &buffer, &size, 0);
if (rc) {
@@ -332,8 +331,8 @@ add_component_file(int fd, const char* filename, address_t load_address,
return -1;
}
/* Get block list from existing file */
count = disk_get_blocklist_from_file(filename, &list,
file_info);
count = disk_get_blocklist_from_file(filename, reg,
&list, file_info);
disk_free_info(file_info);
if (count == 0)
return -1;
@@ -358,9 +357,22 @@ add_component_file(int fd, const char* filename, address_t load_address,
}
static int
add_component_buffer(int fd, void* buffer, size_t size, component_data data,
void* component, struct disk_info* info,
struct component_loc *location, int type)
add_component_file(int fd, const char *filename, address_t load_address,
size_t trailer, void *component, int add_files,
struct disk_info *info, struct job_target_data *target,
struct component_loc *location)
{
return add_component_file_range(fd, filename, NULL, load_address,
trailer, component, add_files,
info, target, location);
}
static int
add_component_buffer_align(int fd, void *buffer, size_t size,
component_data data, void *component,
struct disk_info *info,
struct component_loc *location, int type,
int align, off_t *offset)
{
struct component_loc loc;
disk_blockptr_t segment;
@@ -369,7 +381,8 @@ add_component_buffer(int fd, void* buffer, size_t size, component_data data,
int rc;
/* Write buffer */
count = disk_write_block_buffer(fd, 0, buffer, size, &list, info);
count = disk_write_block_buffer_align(fd, 0, buffer, size, &list, info,
align, offset);
if (count == 0) {
error_text("Could not write to bootmap file");
return -1;
@@ -396,6 +409,15 @@ add_component_buffer(int fd, void* buffer, size_t size, component_data data,
return rc;
}
static int
add_component_buffer(int fd, void *buffer, size_t size, component_data data,
void *component, struct disk_info *info,
struct component_loc *location, int type)
{
return add_component_buffer_align(fd, buffer, size, data, component,
info, location, type,
info->phy_block_size, NULL);
}
static int
add_dummy_buffer(int fd, size_t size, address_t addr, void *component,
@@ -440,7 +462,7 @@ print_components(const char *name[], struct component_loc *loc, int num)
}
static int
extract_signature(char *filename, void **ret_signature,
extract_signature(const char *filename, void **ret_signature,
struct signature_header *sig_head)
{
struct file_signature *file_sig;
@@ -496,8 +518,9 @@ check_remaining_filesize(size_t filesize, size_t signature_size,
}
}
static int
add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
static int add_ipl_program(int fd, char *filename,
bool add_envblk, struct job_envblk_data *envblk,
struct job_ipl_data *ipl, disk_blockptr_t *program,
int verbose, int add_files, component_header_type type,
struct disk_info* info, struct job_target_data* target,
int is_secure)
@@ -507,12 +530,13 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
size_t ramdisk_size, image_size;
bool secure_boot_supported;
size_t stage3_params_size;
const char *comp_name[10];
const char *comp_name[11];
size_t signature_size;
int offset;
uint64_t flags = 0;
void *stage3_params;
struct stat stats;
off_t envblk_off;
void *signature;
int comp_nr = 0;
void *table;
@@ -650,7 +674,8 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
ipl->is_kdump ? IMAGE_ENTRY_KDUMP :
IMAGE_ENTRY,
(info->type == disk_type_scsi) ? 0 : 1,
flags, ipl->image_addr, image_size);
flags, ipl->image_addr, image_size,
ipl->envblk_addr, envblk->size);
if (rc) {
free(table);
return rc;
@@ -743,8 +768,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
comp_name[comp_nr] = "parmline";
comp_nr++;
}
/* finally add ramdisk */
/* add ramdisk */
if (ipl->ramdisk != NULL) {
signature_size = extract_signature(ipl->ramdisk, &signature,
&sig_head);
@@ -789,6 +813,65 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
comp_name[comp_nr] = "initial ramdisk";
comp_nr++;
}
if (add_envblk == true) {
/*
* finally add environment block
*/
rc = envblk_offset_get(fd, &envblk_off);
if (rc) {
free(table);
return rc;
}
if (envblk_off == 0) {
/*
* write with fs_block_size alignment to make sure that the
* logical environment block will get to single file system
* block
*/
rc = add_component_buffer_align(fd,
envblk->buf, envblk->size,
(component_data)ipl->envblk_addr,
VOID_ADD(table, offset),
info, &comp_loc[comp_nr],
component_load,
info->fs_block_size,
&envblk_off);
if (rc) {
error_text("Could not add environment block");
free(table);
return rc;
}
assert(envblk_off % info->fs_block_size == 0);
/*
* store environment block location
* in the bootmap header
*/
rc = envblk_offset_set(fd, envblk_off);
if (rc) {
error_text("Could not store environment block location");
free(table);
return rc;
}
} else {
struct file_range reg;
reg.offset = envblk_off;
reg.len = envblk->size;
rc = add_component_file_range(fd, filename, &reg,
ipl->envblk_addr, 0,
VOID_ADD(table, offset),
0, info, target,
&comp_loc[comp_nr]);
if (rc) {
error_text("Could not add environment block");
free(table);
return rc;
}
}
offset += sizeof(struct component_entry);
comp_name[comp_nr] = "environment blk";
comp_nr++;
}
if (verbose)
print_components(comp_name, comp_loc, comp_nr);
/* Terminate component table */
@@ -834,7 +917,7 @@ add_segment_program(int fd, struct job_segment_data* segment,
rc = add_component_file(fd, segment->segment, segment->segment_addr, 0,
VOID_ADD(table, offset), add_files, info,
target, &comp_loc[0]);
if (rc) {
if (rc) {
error_text("Could not add segment file '%s'",
segment->segment);
free(table);
@@ -936,7 +1019,7 @@ add_dump_program(int fd, struct job_dump_data* dump,
if (rc)
return rc;
ipl.parm_addr = dump->parm_addr;
return add_ipl_program(fd, &ipl, program, verbose, 1,
return add_ipl_program(fd, NULL, false, NULL, &ipl, program, verbose, 1,
type, info, target, SECURE_BOOT_DISABLED);
}
@@ -944,8 +1027,8 @@ add_dump_program(int fd, struct job_dump_data* dump,
/* Build a program table from job data and set pointer to program table
* block upon success. */
static int
build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
struct disk_info* info)
build_program_table(int fd, char *filename, struct job_data *job,
disk_blockptr_t *pointer, struct disk_info *info)
{
disk_blockptr_t* table;
int entries, component_header;
@@ -959,6 +1042,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
entries);
if (table == NULL)
return -1;
memset((void *) table, 0, sizeof(disk_blockptr_t) * entries);
/* Add programs */
switch (job->id) {
@@ -972,8 +1056,9 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
component_header = component_header_dump;
else
component_header = component_header_ipl;
rc = add_ipl_program(fd, &job->data.ipl, &table[0],
verbose || job->command_line,
rc = add_ipl_program(fd, filename,
true, &job->envblk, &job->data.ipl,
&table[0], verbose || job->command_line,
job->add_files, component_header,
info, &job->target, job->is_secure);
break;
@@ -1026,7 +1111,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
else
is_secure =
job->data.menu.entry[i].is_secure;
rc = add_ipl_program(fd,
rc = add_ipl_program(fd, filename,
true, &job->envblk,
&job->data.menu.entry[i].data.ipl,
&table[job->data.menu.entry[i].pos],
verbose || job->command_line,
@@ -1060,6 +1146,9 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
rc = -1;
break;
}
if (job->envblk.buf && verbose)
envblk_print(job->envblk.buf, job->envblk.size);
if (rc == 0) {
/* Add program table block */
rc = add_program_table(fd, table, entries, pointer, info);
@@ -1218,9 +1307,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
scsi_sb.dump_size = unused_size;
}
/* Write bootmap header */
if (misc_write(fd, header_text, sizeof(header_text))) {
error_text("Could not write to file '%s'", filename);
/* Initialize bootmap header */
if (bootmap_header_init(fd)) {
error_text("Could not init bootmap header at '%s'", filename);
goto out_misc_free_temp_dev;
}
/* Write empty block to be read in place of holes in files */
@@ -1229,7 +1318,7 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
goto out_misc_free_temp_dev;
}
/* Build program table */
if (build_program_table(fd, job, program_table, info))
if (build_program_table(fd, filename, job, program_table, info))
goto out_misc_free_temp_dev;
if (job->id == job_dump_partition) {
scsi_sb.magic = SCSI_DUMP_SB_MAGIC;

64
zipl/src/bootmap_header.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to manipulate with bootmap header
*
* Copyright IBM Corp. 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <string.h>
#include "bootmap.h"
#include "error.h"
#include "misc.h"
int bootmap_header_init(int fd)
{
struct bootmap_header bh = {
.header_text =
"zSeries bootmap file\n"
"created by zIPL\n",
.version = BOOTMAP_HEADER_VERSION
};
return misc_write(fd, &bh, sizeof(bh));
}
static int bootmap_header_access(int fd, struct bootmap_header *bh, int read)
{
off_t cur_off;
int ret;
cur_off = lseek(fd, 0, SEEK_CUR);
if (cur_off == -1) {
error_reason(strerror(errno));
return -1;
}
if (lseek(fd, 0, SEEK_SET) == -1) {
error_reason(strerror(errno));
ret = -1;
goto out;
}
if (read)
ret = misc_read(fd, bh, sizeof(*bh));
else
ret = misc_write(fd, bh, sizeof(*bh));
out:
if (lseek(fd, cur_off, SEEK_SET) == -1)
return -1;
return ret;
}
int bootmap_header_read(int fd, struct bootmap_header *bh)
{
return bootmap_header_access(fd, bh, 1);
}
int bootmap_header_write(int fd, struct bootmap_header *bh)
{
return bootmap_header_access(fd, bh, 0);
}

View File

@@ -23,10 +23,10 @@
#include <unistd.h>
#include <linux/fs.h>
#include <linux/fiemap.h>
#include <assert.h>
#include "lib/util_proc.h"
#include "lib/util_sys.h"
#include "disk.h"
#include "error.h"
#include "install.h"
@@ -198,7 +198,7 @@ disk_get_info(const char* device, struct job_target_data* target,
int fd;
long devsize;
FILE *fh;
char *script_pre = TOOLS_LIBDIR "/zipl_helper.";
const char *script_pre = util_libdir_path("/zipl_helper.");
char *script_file = NULL;
char *ppn_cmd = NULL;
char buffer[80];
@@ -554,14 +554,6 @@ disk_free_info(struct disk_info* info)
}
#ifndef REISERFS_SUPER_MAGIC
#define REISERFS_SUPER_MAGIC 0x52654973
#endif /* not REISERFS_SUPER_MAGIC */
#ifndef REISERFS_IOC_UNPACK
#define REISERFS_IOC_UNPACK _IOW(0xCD,1,long)
#endif /* not REISERFS_IOC_UNPACK */
/* Retrieve the physical blocknumber (block on disk) of the specified logical
* block (block in file). FD provides the file descriptor, LOGICAL is the
* logical block number. Upon success, return 0 and store the physical
@@ -571,14 +563,9 @@ int
disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
blocknum_t* physical, struct disk_info* info)
{
struct statfs buf;
blocknum_t phy_per_fs;
blocknum_t mapped;
int block;
int subblock;
int fiemap_size;
int map_offset;
struct fiemap *fiemap;
/* No file system: partition or raw disk */
if (info->fs_block_size == -1) {
@@ -588,70 +575,15 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
*physical = logical + info->geo.start;
return 0;
}
/* Get file system type */
if (fstatfs(fd, &buf)) {
error_reason(strerror(errno));
return -1;
}
/* Files on ReiserFS need unpacking */
if (buf.f_type == REISERFS_SUPER_MAGIC) {
if (ioctl(fd, REISERFS_IOC_UNPACK, 1)) {
error_reason("Could not unpack ReiserFS file");
return -1;
}
}
/* Get mapping in file system blocks */
/*
* Get mapping in file system blocks
*/
phy_per_fs = info->fs_block_size / info->phy_block_size;
subblock = logical % phy_per_fs;
/* First try FIEMAP, more complicated to set up */
fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
fiemap = misc_malloc(fiemap_size);
if (!fiemap)
if (fs_map(fd, logical * info->phy_block_size,
&mapped, info->fs_block_size) != 0)
return -1;
memset(fiemap, 0, fiemap_size);
fiemap->fm_extent_count = 1;
fiemap->fm_flags = FIEMAP_FLAG_SYNC;
/* fm_start, fm_length in bytes; logical is in physical block units */
fiemap->fm_start = logical * info->phy_block_size;
fiemap->fm_length = info->phy_block_size;
if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
/* FIEMAP failed, fall back to FIBMAP */
block = logical / phy_per_fs;
if (ioctl(fd, FIBMAP, &block)) {
error_reason("Could not get file mapping");
free(fiemap);
return -1;
}
mapped = block;
} else {
if (fiemap->fm_mapped_extents) {
if (fiemap->fm_extents[0].fe_flags &
FIEMAP_EXTENT_ENCODED) {
error_reason("File mapping is encoded");
free(fiemap);
return -1;
}
/*
* returned extent may start prior to our request
*/
map_offset = fiemap->fm_start -
fiemap->fm_extents[0].fe_logical;
mapped = fiemap->fm_extents[0].fe_physical +
map_offset;
/* set mapped to fs block units */
mapped = mapped / info->fs_block_size;
} else {
mapped = 0;
}
}
free(fiemap);
if (mapped == 0) {
/* This is a hole in the file */
*physical = 0;
@@ -728,22 +660,26 @@ disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
}
}
/* Write BYTECOUNT bytes of data from memory at location DATA as a block to
* the file identified by file descriptor FD. Make sure that the data is
* aligned on a block size boundary and that at most INFO->PHY_BLOCK_SIZE
* bytes are written. INFO provides information about the disk layout. Upon
* success, return 0 and store the pointer to the resulting disk block to BLOCK
* (if BLOCK is not NULL). Return non-zero otherwise. */
/**
* Write BYTECOUNT bytes of data from memory at location DATA as a block to
* the file identified by file descriptor FD at the current position in that
* file aligned on ALIGN block size boundary and make sure that at most
* INFO->PHY_BLOCK_SIZE bytes are written. INFO provides information about
* the disk layout. Upon success, store the pointer to the resulting disk
* block to BLOCK (if BLOCK is not NULL) and return 0. Return non-zero
* otherwise. On success OFFSET contains offset of the first written byte
*/
static int
disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
size_t bytecount, disk_blockptr_t* block,
struct disk_info* info)
struct disk_info *info, int align, off_t *offset)
{
blocknum_t current_block;
blocknum_t blocknum;
off_t current_pos;
int align;
if (align == 0)
align = info->phy_block_size;
current_pos = lseek(fd, 0, SEEK_CUR);
if (current_pos == -1) {
@@ -751,18 +687,18 @@ disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
return -1;
}
/* Ensure block alignment of current file pos */
align = info->phy_block_size;
if (current_pos % align != 0) {
current_pos = lseek(fd, align - current_pos % align, SEEK_CUR);
if (current_pos == -1) {
if (current_pos == -1) {
error_text(strerror(errno));
return -1;
}
}
current_block = current_pos / align;
current_block = current_pos / info->phy_block_size;
/* Ensure maximum size */
if (bytecount > (size_t) align)
bytecount = align;
if (bytecount > (size_t)info->phy_block_size)
bytecount = info->phy_block_size;
/* Write data block */
if (misc_write(fd, data, bytecount))
return -1;
@@ -773,33 +709,38 @@ disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
return -1;
disk_blockptr_from_blocknum(block, blocknum, info);
}
if (offset)
*offset = current_pos;
return 0;
}
int
disk_write_block_aligned(int fd, const void* data, size_t bytecount,
disk_blockptr_t* block, struct disk_info* info)
int disk_write_block_aligned(int fd, const void *data, size_t bytecount,
disk_blockptr_t *block, struct disk_info *info)
{
return disk_write_block_aligned_base(fd, 0, data, bytecount, block,
info);
info, info->phy_block_size, NULL);
}
/* Write BYTECOUNT bytes from memory at location BUFFER to the file identified
* by file descriptor FD and return the list of pointers to the disk blocks
* that make up the respective part of the file. Upon success return the number
* of blocks and set BLOCKLIST to point to the uncompressed list. Return zero
* otherwise. Note that the data is written to a file position which is aligned
* on a block size boundary. */
/**
* Write BYTECOUNT bytes from memory at location BUFFER to the file identified
* by file descriptor FD at the current position in that file aligned on ALIGN
* block size boundary and return the list of pointers to the disk blocks that
* make up the respective part of the file. Upon success return the number of
* blocks, set BLOCKLIST to point to the uncompressed list, and store offset of
* the first written byte in OFFSET (if OFFSET is not NULL). Return zero
* otherwise.
*/
blocknum_t
disk_write_block_buffer(int fd, int fd_is_basedisk, const void* buffer,
size_t bytecount, disk_blockptr_t** blocklist,
struct disk_info* info)
disk_write_block_buffer_align(int fd, int fd_is_basedisk, const void *buffer,
size_t bytecount, disk_blockptr_t **blocklist,
struct disk_info *info, int align, off_t *offset)
{
disk_blockptr_t* list;
blocknum_t count;
blocknum_t i;
size_t written;
size_t chunk_size;
off_t pos;
int rc;
count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
@@ -816,18 +757,30 @@ disk_write_block_buffer(int fd, int fd_is_basedisk, const void* buffer,
if (chunk_size > (size_t) info->phy_block_size)
chunk_size = info->phy_block_size;
rc = disk_write_block_aligned_base(fd, fd_is_basedisk,
VOID_ADD(buffer, written),
chunk_size,
&list[i], info);
VOID_ADD(buffer, written),
chunk_size, &list[i], info,
i == 0 ? align : info->phy_block_size,
&pos);
if (rc) {
free(list);
return 0;
}
if (offset != NULL && i == 0)
*offset = pos;
}
*blocklist = list;
return count;
}
blocknum_t
disk_write_block_buffer(int fd, int fd_is_basedisk, const void *buffer,
size_t bytecount, disk_blockptr_t **blocklist,
struct disk_info *info)
{
return disk_write_block_buffer_align(fd, fd_is_basedisk, buffer,
bytecount, blocklist, info,
info->phy_block_size, NULL);
}
/* Print device node. */
void
@@ -1064,19 +1017,26 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
return last + 1;
}
/* Retrieve a list of pointers to the disk blocks that make up the file
* specified by FILENAME. Upon success, return the number of blocks and set
* BLOCKLIST to point to the uncompacted list. INFO provides information
* about the device which contains the file. Return zero otherwise. */
/**
* Retrieve a list of pointers to the disk blocks that make up a continuous
* region REG in a file specified by FILENAME. If REG is NULL, then retrieve
* a list of pointers for the whole file.
* Upon success, return the number of blocks and set BLOCKLIST to point to
* the uncompacted list. INFO provides information about the device which
* contains the file. Return zero otherwise
*/
blocknum_t
disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
disk_blockptr_t **blocklist,
struct disk_info* info)
{
disk_blockptr_t* list;
struct stat stats;
int fd;
blocknum_t count;
off_t off;
size_t count;
blocknum_t blk_off;
blocknum_t blk_count;
blocknum_t i;
blocknum_t blocknum;
@@ -1093,24 +1053,37 @@ disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
close (fd);
return 0;
}
/* Get number of blocks */
count = ((blocknum_t) stats.st_size +
info->phy_block_size - 1) / info->phy_block_size;
if (count == 0) {
error_reason("Could not read empty file '%s'", filename);
close(fd);
return 0;
if (reg) {
/*
* case of not block-aligned offsets is not implemented
*/
assert(reg->offset % info->phy_block_size == 0);
off = reg->offset;
count = reg->len;
} else {
off = 0;
count = stats.st_size;
}
assert(off < stats.st_size);
if (off + count > (size_t)stats.st_size)
count = stats.st_size - off;
blk_off = off / info->phy_block_size;
blk_count = ((blocknum_t) count +
info->phy_block_size - 1) / info->phy_block_size;
list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) *
count);
blk_count);
if (list == NULL) {
close(fd);
return 0;
}
memset((void *) list, 0, sizeof(disk_blockptr_t) * count);
memset((void *) list, 0, sizeof(disk_blockptr_t) * blk_count);
/* Build list */
for (i=0; i < count; i++) {
if (disk_get_blocknum(fd, 0, i, &blocknum, info)) {
for (i = 0; i < blk_count; i++) {
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum, info)) {
free(list);
close(fd);
return 0;
@@ -1119,7 +1092,7 @@ disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
}
close(fd);
*blocklist = list;
return count;
return blk_count;
}
/* Check whether input device is in subchannel set 0.

452
zipl/src/envblk.c Normal file
View File

@@ -0,0 +1,452 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to manipulate with environment block
*
* Copyright IBM Corp. 2020
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bootmap.h"
#include "envblk.h"
#include "error.h"
#include "misc.h"
/* from linux/fs.h */
#define FIGETBSZ _IO(0x00, 2)
void envblk_create_blank(char *envblk, int envblk_len)
{
memcpy(envblk, ZIPL_ENVBLK_SIGNATURE,
sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
}
/**
* Find out environment block location.
* FD: bootmap file descriptor.
* On success the searched location is saved in variable pointed out by OFF
*/
int envblk_offset_get(int fd, off_t *off)
{
struct bootmap_header bh;
if (bootmap_header_read(fd, &bh)) {
error_reason("Could not read bootmap_header");
return -1;
}
*off = bh.envblk_offset;
return 0;
}
/**
* Save environment block location specified by OFF
* FD: bootmap file descriptor.
* On success the location is saved in the bootmap header.
*/
int envblk_offset_set(int fd, off_t off)
{
struct bootmap_header bh;
if (bootmap_header_read(fd, &bh)) {
error_reason("Could not read bootmap header");
return -1;
}
bh.envblk_offset = off;
if (bootmap_header_write(fd, &bh)) {
error_reason("Could not write bootmap header");
return -1;
}
return 0;
}
/**
* Find out environment block size.
* FD: bootmap file descriptor.
* On success the searched size is stored in RESULT
*/
int envblk_size_get(int fd, int *result)
{
if (ioctl(fd, FIGETBSZ, result) == -1) {
error_reason(strerror(errno));
return -1;
}
return 0;
}
#define LINE_HASH_SIZE (16)
/**
* Calculate hash function of "environment line"
*
* LINE: pointer to a string, containing NANE=VALUE
* LEN: length of the NAME with trailing delimiter "="
*/
static unsigned int linehash(char *line, int len, unsigned int hash_size)
{
unsigned int sum = 0;
while (len) {
sum += 5 * *(line++);
len--;
}
return sum % hash_size;
}
struct line_hash_entry {
struct line_hash_entry *next;
char *line; /* pointer to an "environment line",
* a string containing NAME=VALUE with
* optional trailing end-of-line symbol
*/
};
/**
* Check if @name meets the requirements for shell environment variables
* (IEEE Std 1003.1-2001), mitigated with lowercase letter allowance
*
* Return 0, if it meets, and 1 otherwise
*/
int envblk_check_name(char *name, int len)
{
int i;
if (len == 0)
return 1;
if (!isalpha(name[0]) && name[0] != '_')
return 1;
for (i = 1; i < len; i++)
if (!isalpha(name[i]) && !isdigit(name[i]) && name[i] != '_')
return 1;
return 0;
}
/**
* Add environment line to hash table
*
* LINE: pointer to a string containing NAME=VALUE
* LEN: length of NAME with trailing delimiter "="
*/
static int hash_table_add(struct line_hash_entry **table, char *line, int len)
{
unsigned int hash;
struct line_hash_entry *new;
new = malloc(sizeof(*new));
if (!new)
return -ENOMEM;
memset(new, 0, sizeof(*new));
new->line = misc_strdup(line);
if (!new->line) {
free(new);
return -ENOMEM;
}
hash = linehash(new->line, len, LINE_HASH_SIZE);
new->next = table[hash];
table[hash] = new;
return 0;
}
/**
* Find environment line in the hash table by name
*
* LINE: string contailing NAME=VALUE
* LEN: length of the NAME with trailing delimiter "="
*/
struct line_hash_entry *hash_table_find(struct line_hash_entry **table,
char *name, size_t len)
{
struct line_hash_entry *item;
item = table[linehash(name, len, LINE_HASH_SIZE)];
while (item) {
if (!strncmp(item->line, name, len))
return item;
item = item->next;
}
return NULL;
}
static int hash_entry_replace(struct line_hash_entry *item, char *name,
int *offset)
{
char *dup;
dup = misc_strdup(name);
if (!dup)
return -ENOMEM;
*offset -= strlen(item->line);
free(item->line);
item->line = dup;
*offset += strlen(item->line);
return 1;
}
/**
* Scan hash table TABLE, and call ACTOR for each its entry.
* The ACTOR is allowed even to release the entry.
*/
static void hash_table_scan(struct line_hash_entry **table,
void (*actor)(struct line_hash_entry *entry))
{
int i;
for (i = 0; i < LINE_HASH_SIZE; i++) {
struct line_hash_entry *item = table[i];
while (item) {
struct line_hash_entry *next = item->next;
actor(item);
item = next;
}
}
}
static void hash_entry_free(struct line_hash_entry *this)
{
free(this->line);
free(this);
}
static void hash_table_free(struct line_hash_entry **table)
{
hash_table_scan(table, hash_entry_free);
}
/**
* Scan hash table TABLE, and for each its entry copy its line to the
* in-memory environment block specified by DST at the offset OFF.
* Insert end-of-line symbol, if needed. The caller should guarantee
* enough space in the buffer DST
*/
static void hash_table_flush(struct line_hash_entry **table, char *dst,
int off)
{
int i;
for (i = 0; i < LINE_HASH_SIZE; i++) {
struct line_hash_entry *item = table[i];
while (item) {
int str_len;
str_len = strlen(item->line);
memcpy(dst + off, item->line, str_len);
off += str_len;
if (dst[off - 1] != '\n') {
dst[off] = '\n';
off += 1;
}
item = item->next;
}
}
}
/**
* Put a zIPL environment line into hash table.
*
* Identify a name in the line THIS which contains a pair NAME=VALUE, and
* look for an entry in the hash table TABLE with a similar name. If such
* entry exists, then replace the line in that entry with the new one
* specified by THIS. Othewise, insert a new entry to the hash table.
*
* THIS: pointer to a NULL-terminated string, which contains the line to
* be imported. If the line was successfully imported, and no line with the
* same NAME was added before, then return 0.
* If the line should be ignored, or it replaced previously added line with
* the same NAME, then return > 0. On errors return < 0.
*/
static int import_one_line(char *this, struct line_hash_entry **table,
int *offset)
{
struct line_hash_entry *item;
char *p;
if (*this == '#')
/* Ignore "commented" line */
return 1;
p = strchr(this, '=');
if (p == NULL || p == this)
/* Could not identify NAME in the line. Ignore */
return 1;
if (envblk_check_name(this, p - this)) {
error_reason("Unacceptable name '%.*s'", p - this, this);
return -EINVAL;
}
/*
* Try to find an entry with similar NAME.
*/
item = hash_table_find(table, this, p - this + 1);
return item != NULL ? hash_entry_replace(item, this, offset) :
hash_table_add(table, this, p - this + 1);
}
/**
* Import environment line-by-line from a regular file specified
* by its name FROM_FILE to a pre-allocated buffer TO_BUF. Ignore
* malformed, or "commented" lines. If the file doesn't end with
* end-of-line symbol, then append that symbol in the destination
* buffer. Return 0 on success.
*
* ENVBLK_SIZE: size of the destination buffer TO_BUF.
*/
int envblk_import(char *from_file, char *to_buf, int envblk_size)
{
const char *err_prefix = "Failed to import environment file";
struct line_hash_entry *table[LINE_HASH_SIZE];
unsigned int lines_imported = 0;
char *line = NULL;
size_t len = 0;
FILE *stream;
ssize_t read;
int ret = 0;
int offset;
stream = fopen(from_file, "r");
if (stream == NULL)
return 0;
memset(table, 0, LINE_HASH_SIZE*sizeof(void *));
offset = sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
while ((read = getline(&line, &len, stream)) != -1) {
if (read > envblk_size - offset) {
error_reason("%s %s: maximal size (%d) exceeded",
err_prefix, from_file,
ENVBLK_MAX_IMPORT_SIZE(envblk_size));
ret = -EINVAL;
break;
}
if (read < 2)
/* malformed line. Ignored */
continue;
if (line[read - 1] != '\n' && read == envblk_size - offset) {
/*
* file's tail doesn't contain end-of-line symbol at
* the end, whereas there is no enough space in the
* destination buffer to insert that symbol, which is
* mandatory according to the environment block format
*/
error_reason("%s %s: maximal size (%d) exceeded",
err_prefix, from_file,
ENVBLK_MAX_IMPORT_SIZE(envblk_size));
ret = -EINVAL;
break;
}
ret = import_one_line(line, table, &offset);
if (ret < 0) {
/* error */
error_text("%s %s", err_prefix, from_file);
break;
} else if (ret > 0) {
/*
* either this line was ignored, or previously
* imported line was overwritten by it
*/
ret = 0;
continue;
}
if (lines_imported >= ENVBLK_MAX_LINES) {
error_reason("%s %s: maximum number of lines (%d) exceeded",
err_prefix, from_file, ENVBLK_MAX_LINES);
ret = -EINVAL;
break;
}
offset += read;
lines_imported++;
}
hash_table_flush(table, to_buf, sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
hash_table_free(table);
free(line);
fclose(stream);
return ret;
}
char *envblk_next_line(char *s, const char *end)
{
while (s < end) {
if (*s == '\n')
break;
s++;
}
return s + 1;
}
/**
* Scan installed environment block and call ACTOR for each found NAME.
* Return -1 on corrupted environment blocks. Otherwise, return 0
*/
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name))
{
unsigned int lines_scanned = 0;
const char *reason;
char *s, *end;
char *name;
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
while (s < end && *s != 0) {
if (lines_scanned > ENVBLK_MAX_LINES) {
reason = "maximum number of lines exceeded";
goto corrupted;
}
name = s;
while (s < end && *s != '=')
s++;
if (s == end) {
/* delimiter "=" not found */
reason = "missed delimiter";
goto corrupted;
}
/* here @s points to "=" */
s++;
while (s < end && *s != '\n')
s++;
if (s == end) {
/* end of line not found */
reason = "missed EOL";
goto corrupted;
}
*s = '\0';
actor(name);
*s = '\n';
lines_scanned++;
s = envblk_next_line(s, end);
}
return 0;
corrupted:
error_reason("Found corrupted environment block (%s) - please run zipl",
reason);
return -1;
}
static void print_name_value(char *this)
{
printf(" %s\n", this);
}
void envblk_print(char *envblk, unsigned int envblk_size)
{
printf("Environment block content:\n");
envblk_scan(envblk, envblk_size, print_name_value);
}

127
zipl/src/fs-map.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to map logical addresses to physical ones
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/fiemap.h>
#include "disk.h"
#include "error.h"
#include "misc.h"
#ifndef REISERFS_SUPER_MAGIC
#define REISERFS_SUPER_MAGIC 0x52654973
#endif /* not REISERFS_SUPER_MAGIC */
#ifndef REISERFS_IOC_UNPACK
#define REISERFS_IOC_UNPACK _IOW(0xCD, 1, long)
#endif /* not REISERFS_IOC_UNPACK */
/**
* Filesystem-specific hook.
* Do whatever needed before mapping logical address to physical one
*/
static int fs_premap_hook(int fd)
{
struct statfs buf;
/* Get file system type */
if (fstatfs(fd, &buf)) {
error_reason(strerror(errno));
return -1;
}
switch (buf.f_type) {
case REISERFS_SUPER_MAGIC:
/* Files on ReiserFS need unpacking */
if (ioctl(fd, REISERFS_IOC_UNPACK, 1)) {
error_reason("Could not unpack ReiserFS file");
return -1;
}
break;
default:
break;
}
return 0;
}
/**
* Find a disk address for a file system block,
* where a specified byte of the file body is located
*
* FD: file descriptor
* OFFSET: offset of the byte in the file body
* MAPPED: resulted disk address
* FS_BLOCK_SIZE : file system block size in bytes
*/
int fs_map(int fd, uint64_t offset, blocknum_t *mapped,
int fs_block_size)
{
struct fiemap *fiemap;
int fiemap_size;
int map_offset;
int block;
if (fs_premap_hook(fd))
return -1;
/*
* First try FIEMAP, more complicated to set up
*/
fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
fiemap = misc_malloc(fiemap_size);
if (!fiemap)
return -1;
memset(fiemap, 0, fiemap_size);
fiemap->fm_extent_count = 1;
fiemap->fm_flags = FIEMAP_FLAG_SYNC;
fiemap->fm_start = offset;
fiemap->fm_length = fs_block_size;
if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
/* FIEMAP failed, fall back to FIBMAP */
block = offset / fs_block_size;
if (ioctl(fd, FIBMAP, &block)) {
error_reason("Could not get file mapping");
free(fiemap);
return -1;
}
*mapped = block;
} else {
if (fiemap->fm_mapped_extents) {
if (fiemap->fm_extents[0].fe_flags &
FIEMAP_EXTENT_ENCODED) {
error_reason("File mapping is encoded");
free(fiemap);
return -1;
}
/*
* returned extent may start prior to our request
*/
map_offset = fiemap->fm_start -
fiemap->fm_extents[0].fe_logical;
*mapped = fiemap->fm_extents[0].fe_physical +
map_offset;
/* set mapped to fs block units */
*mapped = *mapped / fs_block_size;
} else {
*mapped = 0;
}
}
free(fiemap);
return 0;
}

View File

@@ -14,6 +14,7 @@
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -26,6 +27,7 @@
#include "misc.h"
#include "scan.h"
#include "zipl.h"
#include "envblk.h"
/* Command line options */
static struct option options[] = {
@@ -37,6 +39,7 @@ static struct option options[] = {
{ "targetgeometry", required_argument, NULL, 0xac},
{ "targetblocksize", required_argument, NULL, 0xad},
{ "targetoffset", required_argument, NULL, 0xae},
{ "environment", required_argument, NULL, 0xaf},
{ "image", required_argument, NULL, 'i'},
{ "ramdisk", required_argument, NULL, 'r'},
{ "parmfile", required_argument, NULL, 'p'},
@@ -73,6 +76,7 @@ static const char *zipl_conf[] = {
struct command_line {
char* data[SCAN_KEYWORD_NUM];
char* config;
char *envblk_import_hint;
char *blsdir;
char* menu;
char* section;
@@ -87,7 +91,6 @@ struct command_line {
enum scan_section_type type;
};
static int
store_option(struct command_line* cmdline, enum scan_keyword_id keyword,
char* value)
@@ -211,6 +214,13 @@ get_command_line(int argc, char* argv[], struct command_line* line)
rc = store_option(&cmdline, scan_keyword_targetoffset,
optarg);
break;
case 0xaf:
if (cmdline.envblk_import_hint != NULL) {
error_reason("Option 'environment' specified more than once");
rc = -1;
} else
cmdline.envblk_import_hint = optarg;
break;
case 'T':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_tape,
@@ -351,6 +361,12 @@ free_target_data(struct job_target_data* data)
free(data->targetbase);
}
static void
free_envblk_data(struct job_envblk_data *data)
{
free(data->buf);
}
static void
free_ipl_data(struct job_ipl_data* data)
{
@@ -444,6 +460,7 @@ job_free(struct job_data* job)
if (job->target.bootmap_dir != NULL)
free(job->target.bootmap_dir);
free_target_data(&job->target);
free_envblk_data(&job->envblk);
if (job->name != NULL)
free(job->name);
switch (job->id) {
@@ -480,7 +497,7 @@ struct component_loc {
};
static int
set_cl_element(struct component_loc *cl, char *name, char *filename,
set_cl_element(struct component_loc *cl, char *name, const char *filename,
address_t *addrp, size_t size, off_t off, off_t align)
{
struct stat stats;
@@ -528,17 +545,16 @@ sort_cl_array(struct component_loc* cl, int elements)
}
}
}
static int
get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
int *nump)
int *nump, struct job_envblk_data *envblk)
{
struct component_loc *cl;
int num;
int rc;
/* Get memory for image, parmline, ramdisk */
cl = misc_calloc(3, sizeof(struct component_loc));
/* Get memory for image, parmline, ramdisk and environment block */
cl = misc_calloc(4, sizeof(struct component_loc));
if (cl == NULL)
return -1;
/* Fill in component data */
@@ -561,6 +577,12 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
if (rc)
goto error;
}
rc = set_cl_element(&cl[num++], "environment block",
NULL,
&ipl->envblk_addr, envblk->size, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
*clp = cl;
*nump = num;
@@ -753,7 +775,8 @@ finalize_component_address_data(struct component_loc *cl, int num,
static int
finalize_ipl_address_data(struct job_ipl_data* ipl, char *name)
finalize_ipl_address_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk)
{
unsigned long address_limit;
struct component_loc *cl;
@@ -762,7 +785,7 @@ finalize_ipl_address_data(struct job_ipl_data* ipl, char *name)
address_limit = ipl->is_kdump ?
MIN(util_arch_hsa_maxsize(), ADDRESS_LIMIT) : ADDRESS_LIMIT;
rc = get_ipl_components(ipl, &cl, &num);
rc = get_ipl_components(ipl, &cl, &num, envblk);
if (rc)
return rc;
sort_cl_array(cl, num);
@@ -828,7 +851,8 @@ out_free:
static int
check_job_ipl_data(struct job_ipl_data *ipl, char* name)
check_job_ipl_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk)
{
int rc;
@@ -856,7 +880,7 @@ check_job_ipl_data(struct job_ipl_data *ipl, char* name)
return rc;
}
}
return finalize_ipl_address_data(ipl, name);
return finalize_ipl_address_data(ipl, name, envblk);
}
@@ -935,7 +959,7 @@ check_job_dump_images(struct job_dump_data* dump, char* name)
static int
check_job_menu_data(struct job_menu_data* menu)
check_job_menu_data(struct job_menu_data *menu, struct job_envblk_data *envblk)
{
int rc;
int i;
@@ -944,7 +968,8 @@ check_job_menu_data(struct job_menu_data* menu)
switch (menu->entry[i].id) {
case job_ipl:
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
menu->entry[i].name);
menu->entry[i].name,
envblk);
if (rc)
return rc;
break;
@@ -1110,10 +1135,11 @@ check_job_data(struct job_data* job)
rc = 0;
break;
case job_ipl:
rc = check_job_ipl_data(&job->data.ipl, job->name);
rc = check_job_ipl_data(&job->data.ipl, job->name,
&job->envblk);
break;
case job_menu:
rc = check_job_menu_data(&job->data.menu);
rc = check_job_menu_data(&job->data.menu, &job->envblk);
break;
case job_segment:
rc = check_job_segment_data(&job->data.segment, job->name);
@@ -1372,6 +1398,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
&job->data.ipl.parm_addr, section);
if (rc)
return rc;
/* Fill in environment block */
job->data.ipl.envblk_addr = UNSPECIFIED_ADDRESS;
/* Fill in name and address of ramdisk file */
if (data[(int) scan_keyword_ramdisk] != NULL) {
job->data.ipl.ramdisk =
@@ -1898,6 +1927,45 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
return rc;
}
static int get_job_envblk_data(struct job_data *job, char *import_hint)
{
struct job_envblk_data *data = &job->envblk;
int fd;
switch (job->id) {
case job_ipl:
case job_menu:
break;
default:
return 0;
}
fd = open(job->target.bootmap_dir, O_RDONLY);
if (fd < 0) {
error_reason(strerror(errno));
error_text("Could not open bootmap dir");
return -1;
}
if (envblk_size_get(fd, &data->size)) {
close(fd);
error_text("Could not get environment block size");
return -1;
}
close(fd);
data->buf = misc_malloc(data->size);
if (data->buf == NULL) {
error_text("Could not allocate environment block");
return -1;
}
envblk_create_blank(data->buf, data->size);
if (envblk_import(import_hint ?: ENVBLK_DEFAULT_IMPORT_SOURCE,
data->buf, data->size)) {
free(data->buf);
data->buf = NULL;
return -1;
}
return 0;
}
int
job_get(int argc, char* argv[], struct job_data** data)
@@ -1920,6 +1988,9 @@ job_get(int argc, char* argv[], struct job_data** data)
job->data.mvdump.force = cmdline.force;
job->dry_run = cmdline.dry_run;
job->is_secure = SECURE_BOOT_UNDEFINED;
if (job->verbose)
printf("Looking for components in '%s'\n", util_libdir());
/* Get job data from user input */
if (cmdline.help) {
job->command_line = 1;
@@ -1943,6 +2014,11 @@ job_get(int argc, char* argv[], struct job_data** data)
else if (job->id != job_menu && job->is_secure == SECURE_BOOT_UNDEFINED)
job->is_secure = SECURE_BOOT_AUTO;
rc = get_job_envblk_data(job, cmdline.envblk_import_hint);
if (rc) {
job_free(job);
return -1;
}
/* Check job data for validity */
rc = check_job_data(job);
if (rc) {

611
zipl/src/zipl-editenv.c Normal file
View File

@@ -0,0 +1,611 @@
/*
* zipl-editenv: Environment Editor for zSeries Initial Program Loader
*
* Copyright IBM Corp. 2020
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <assert.h>
#include "disk.h"
#include "error.h"
#include "misc.h"
#include "bootmap.h"
#include "envblk.h"
#include "lib/util_prg.h"
#include "lib/util_opt.h"
#include "lib/util_part.h"
#include "lib/util_path.h"
#include "lib/util_proc.h"
/* from linux/fs.h */
#define FIBMAP _IO(0x00, 1)
#define FIGETBSZ _IO(0x00, 2)
static const struct util_prg prg = {
.desc = "zipl-editenv: Environment Editor for zSeries Initial Program Loader",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2021,
},
UTIL_PRG_COPYRIGHT_END
}
};
int verbose;
static char *bootmap_dir;
enum op_id {
INVALID_OP_ID,
SET_OP_ID,
UNSET_OP_ID,
RESET_OP_ID,
LIST_OP_ID,
LAST_OP_ID
};
static char *op_desc[LAST_OP_ID] = {"invalid", "set", "unset", "reset", "list"};
struct zipl_envblk {
int fd;
char *buf;
off_t offset;
int size;
unsigned int need_update:1;
};
static char *opcode2desc(enum op_id opcode)
{
return op_desc[opcode];
}
static void zipl_envblk_init(struct zipl_envblk *zeb)
{
memset(zeb, 0, sizeof(*zeb));
zeb->fd = -1;
}
static void print_name_value(char *this)
{
printf("%s\n", this);
}
static int do_list(char *envblk, unsigned int envblk_size)
{
return envblk_scan(envblk, envblk_size, print_name_value);
}
static int do_set(char *envblk, unsigned int envblk_size,
const char *name, const char *new_val)
{
unsigned int name_len, new_val_len;
char *fss; /* free space start */
unsigned int lines_scanned = 0;
int name_found = 0;
char *s, *end;
name_len = strlen(name);
new_val_len = strlen(new_val);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
/*
* find the start of free space
*/
for (fss = end - 1; *fss == '\0'; fss--)
;
if (*fss != '\n') {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
fss++;
while (fss - s > name_len) {
if (lines_scanned >= ENVBLK_MAX_LINES) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (memcmp(s, name, name_len) == 0 && s[name_len] == '=') {
unsigned int cur_val_len;
/*
* such name exists, replace its current value
*/
s += (name_len + 1);
cur_val_len = 0;
while (s + cur_val_len < end && s[cur_val_len] != '\n')
cur_val_len++;
if (s + cur_val_len >= end) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (new_val_len > cur_val_len &&
end - fss < new_val_len - cur_val_len) {
error_reason("Not enough space for new value");
return -1;
}
/*
* make a precise-sized room for the new value
*/
if (new_val_len < cur_val_len) {
memmove(s + new_val_len, s + cur_val_len,
end - (s + cur_val_len));
memset(fss + cur_val_len - new_val_len, '\0',
cur_val_len - new_val_len);
} else
memmove(s + new_val_len, s + cur_val_len,
end - (s + new_val_len));
name_found = 1;
break;
}
lines_scanned++;
s = envblk_next_line(s, end);
}
assert(lines_scanned <= ENVBLK_MAX_LINES);
if (!name_found) {
if (lines_scanned == ENVBLK_MAX_LINES) {
error_reason("Maximum number of lines reached");
return -1;
}
/*
* append a new variable
*/
if (end - fss < name_len + new_val_len + 2) {
error_reason("Not enough space in environment block");
return -1;
}
memcpy(fss, name, name_len);
s = fss + name_len;
*s++ = '=';
}
/*
* copy the new value and terminate it with a new line symbol
*/
memcpy(s, new_val, new_val_len);
s[new_val_len] = '\n';
return 0;
}
static int do_unset(char *envblk, int envblk_len, const char *name)
{
unsigned int name_len;
char *s, *end;
name_len = strlen(name);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_len;
while (end - s >= name_len + 2 /* minimal length of
* pattern "name=foo"
*/) {
if (memcmp(s, name, name_len) == 0 && s[name_len] == '=') {
/*
* name was found. Locate the whole
* "named" line and cut it including
* the trailing "\n"
*/
unsigned int cut_len = name_len + 1;
while (s + cut_len < end) {
if (s[cut_len] == '\n')
break;
cut_len++;
}
if (s + cut_len >= end) {
/*
* trailing "\n" not found
*/
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
cut_len++;
memmove(s, s + cut_len, end - (s + cut_len));
memset(end - cut_len, '\0', cut_len);
return 0;
}
s = envblk_next_line(s, end);
}
error_reason("Name %s not found", name);
return -1;
}
/**
* sync the content of the environment block with disk
*/
static int envblk_update(struct zipl_envblk *zeb)
{
struct util_proc_part_entry part_entry;
struct stat info;
blocknum_t blknr;
char *dev_name;
int dev_fd;
if (fstat(zeb->fd, &info))
return -1;
if (util_proc_part_get_entry(info.st_dev, &part_entry) != 0)
return -1;
dev_name = misc_make_path("/dev", part_entry.name);
if (!dev_name) {
error_reason("Could not make path for %s", part_entry.name);
return -1;
}
dev_fd = open(dev_name, O_RDWR | O_DIRECT);
if (dev_fd < 0) {
error_reason("Could not open file %s", dev_name);
free(dev_name);
goto error;
}
free(dev_name);
/*
* det disk address of the environment block
*/
if (fs_map(zeb->fd, zeb->offset, &blknr, zeb->size) != 0)
goto error_close;
if (lseek64(dev_fd, blknr * (uint64_t)zeb->size, SEEK_SET) < 0) {
error_reason(strerror(errno));
goto error_close;
}
if (write(dev_fd, zeb->buf, zeb->size) != zeb->size) {
error_reason("Could not update physical block %ld on %s",
blknr, part_entry.name);
goto error_close;
}
if (verbose)
printf("Physical block %ld on %s got updated\n",
blknr, part_entry.name);
util_proc_part_free_entry(&part_entry);
return 0;
error_close:
close(dev_fd);
error:
util_proc_part_free_entry(&part_entry);
return -1;
}
static int envblk_close(struct zipl_envblk *zeb)
{
if (zeb->buf != NULL) {
free(zeb->buf);
zeb->buf = NULL;
}
if (zeb->fd < 0)
return 0;
if (close(zeb->fd) == 0) {
zeb->fd = -1;
return 0;
}
return -1;
}
/**
* Open a regular file, which contains environment block, and
* read that block to a buffer. Fill in environment block info
* ZEB with valid values
*/
static int envblk_open(struct zipl_envblk *zeb)
{
char *bootmap_file;
if (zeb->fd >= 0)
/* it was opened before */
return 0;
bootmap_file =
misc_make_path(bootmap_dir != NULL ? bootmap_dir : "/boot",
"bootmap");
if (!bootmap_file) {
error_reason("Could not make path for bootmap file");
return -1;
}
zeb->fd = open(bootmap_file, O_RDONLY);
if (zeb->fd < 0) {
error_reason("Could not open bootmap file %s: %s",
bootmap_file, strerror(errno));
free(bootmap_file);
bootmap_file = NULL;
return -1;
}
if (verbose)
printf("Processing bootmap file at %s\n", bootmap_file);
if (envblk_size_get(zeb->fd, &zeb->size)) {
error_reason("Could not get environment block size");
goto error;
}
if (envblk_offset_get(zeb->fd, &zeb->offset)) {
error_reason("Could not get environment block location");
goto error;
}
if (zeb->offset == 0) {
error_reason("Environment not installed - please run zipl");
goto error;
}
/* reopen for direct operations */
close(zeb->fd);
zeb->fd = open(bootmap_file, O_RDWR | O_DIRECT);
if (zeb->fd < 0) {
error_reason("Could not open environment block at %s",
bootmap_file);
goto error;
}
free(bootmap_file);
bootmap_file = NULL;
zeb->buf = aligned_alloc(zeb->size, zeb->size);
if (!zeb->buf) {
error_reason("Could not allocate aligned memory region");
goto error;
}
if (lseek(zeb->fd, zeb->offset, SEEK_SET) < 0) {
error_reason(strerror(errno));
goto error;
}
if (read(zeb->fd, zeb->buf, zeb->size) != zeb->size) {
error_reason("Could not read environment block");
goto error;
}
if (memcmp(zeb->buf, ZIPL_ENVBLK_SIGNATURE,
sizeof(ZIPL_ENVBLK_SIGNATURE) - 1) != 0) {
error_reason("Found corrupted environment block - please run zipl");
goto error;
}
return 0;
error:
if (bootmap_file)
free(bootmap_file);
envblk_close(zeb);
return -1;
}
static int env_list(struct zipl_envblk *zeb)
{
if (envblk_open(zeb))
return -1;
return do_list(zeb->buf, strnlen(zeb->buf, zeb->size));
}
static int env_set(struct zipl_envblk *zeb, char *arg)
{
char *name, *value;
if (envblk_open(zeb))
return -1;
/*
* parse the argument, locate name and value
*/
name = strdup(arg);
if (!name)
return -1;
value = strchr(name, '=');
if (!value || value == name) {
/* name is not identified */
error_reason("Invalid argument %s", arg);
goto error;
}
*value = '\0';
value++;
if (envblk_check_name(name, strlen(name))) {
error_reason("Unacceptable name '%s'", name);
goto error;
}
if (do_set(zeb->buf, zeb->size, name, value) == 0) {
free(name);
zeb->need_update = 1;
return 0;
}
error:
free(name);
return -1;
}
static int env_unset(struct zipl_envblk *zeb, char *name)
{
if (envblk_open(zeb))
return -1;
/*
* check name for validness
*/
if (strchr(name, '=') != NULL) {
error_reason("Invalid argument %s", name);
return -1;
}
if (do_unset(zeb->buf, strnlen(zeb->buf, zeb->size), name) == 0) {
zeb->need_update = 1;
return 0;
}
return -1;
}
static void env_blank(char *envblk, int envblk_len)
{
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
}
static int env_reset(struct zipl_envblk *zeb)
{
if (envblk_open(zeb))
return -1;
env_blank(zeb->buf, zeb->size);
zeb->need_update = 1;
return 0;
}
static int save_op(enum op_id *dst, enum op_id src)
{
if (*dst != INVALID_OP_ID) {
error_reason("Can't perform more than one operation (%s and %s) at a time\n",
opcode2desc(*dst), opcode2desc(src));
return -EINVAL;
}
*dst = src;
return 0;
}
static int save_op_arg(enum op_id *dst, enum op_id src,
char **dst_arg, char *src_arg)
{
int ret;
ret = save_op(dst, src);
if (ret)
return ret;
*dst_arg = strdup(src_arg);
if (*dst_arg == NULL) {
error_reason("Couldn't copy the argument\n");
return -ENOMEM;
}
return 0;
}
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS WITHOUT ARGUMENTS"),
{
.option = { "list", no_argument, NULL, 'l'},
.desc = "print list of zIPL environment variables with their values",
},
{
.option = { "reset", no_argument, NULL, 'r'},
.desc = "remove all variables from zIPL environment",
},
{
.option = { "verbose", no_argument, NULL, 'V'},
.desc = "provide more information",
},
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
{
.option = { "target", required_argument, NULL, 't'},
.argument = "DIR",
.desc = "specify directory, where bootmap file is located",
},
{
.option = { "set", required_argument, NULL, 's'},
.argument = "NAME=VALUE",
.desc = "assign value VALUE to variable NAME",
},
{
.option = { "unset", required_argument, NULL, 'u'},
.argument = "NAME",
.desc = "remove variable NAME from zIPL environment",
},
UTIL_OPT_SECTION("STANDARD OPTIONS"),
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END,
};
int main(int argc, char *argv[])
{
enum op_id opcode = INVALID_OP_ID;
struct zipl_envblk zeb;
char *saved_arg = NULL;
int ret = 0;
int c;
zipl_envblk_init(&zeb);
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
if (argc < 2) {
fprintf(stderr,
"%s: Missing command\nUse 'zipl-editenv --help' for more information\n",
argv[0]);
return EXIT_FAILURE;
}
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'h':
util_prg_print_help();
util_opt_print_help();
goto out;
case 'v':
util_prg_print_version();
goto out;
case 'V':
verbose = 1;
break;
case 't':
bootmap_dir = optarg;
break;
case 'r':
ret = save_op(&opcode, RESET_OP_ID);
break;
case 'l':
ret = save_op(&opcode, LIST_OP_ID);
break;
case 's':
ret = save_op_arg(&opcode, SET_OP_ID,
&saved_arg, optarg);
break;
case 'u':
ret = save_op_arg(&opcode, UNSET_OP_ID,
&saved_arg, optarg);
break;
default:
util_opt_print_parse_error(c, argv);
ret = -1;
}
if (ret)
goto out;
}
if (optind < argc) {
fprintf(stderr, "%s: Unknown command", argv[0]);
while (optind < argc)
printf(" %s", argv[optind++]);
fprintf(stderr, "\n");
goto out;
}
switch (opcode) {
case SET_OP_ID:
ret = env_set(&zeb, saved_arg);
break;
case UNSET_OP_ID:
ret = env_unset(&zeb, saved_arg);
break;
case RESET_OP_ID:
ret = env_reset(&zeb);
break;
case LIST_OP_ID:
ret = env_list(&zeb);
break;
default:
break;
}
out:
if (zeb.need_update)
ret = envblk_update(&zeb);
envblk_close(&zeb);
if (saved_arg != NULL)
free(saved_arg);
if (ret) {
error_print();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -1370,11 +1370,18 @@ static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
* Open a keyslot and get a secure key from a key slot. Optionally returns the
* key and password used to unlock the keyslot. You can either open a specific
* key slot, or let it choose based on the password (keyslot=CRYPT_ANY_SLOT).
* If integrity support is enabled for the LUKS2 device, the returned key may
* include the integrity key concatenated to the secure key (dependent on the
* integrity type). The returned keysize is the full size of both, and the
* returned integrity_keysize is the size of the integrity key only (which may
* be 0). The size of the secure key is keysize minus integrity_keysize.
*/
static int open_keyslot(int keyslot, char **key, size_t *keysize,
size_t *integrity_keysize,
char **password, size_t *password_len,
const char *prompt)
{
struct crypt_params_integrity ip = { 0 };
#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
struct crypt_pbkdf_type pbkdf;
#endif
@@ -1388,7 +1395,20 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
vkeysize = crypt_get_volume_key_size(g.cd);
pr_verbose("Volume key size: %lu", vkeysize);
rc = check_keysize_and_cipher_mode(NULL, vkeysize);
rc = crypt_get_integrity_info(g.cd, &ip);
if (rc == 0) {
/*
* If integrity support is enabled for the LUKS2 device, the
* LUKS2 volume key may include the integrity key concatenated
* to the secure key (dependent on the integrity type).
*/
pr_verbose("Integrity: '%s'",
ip.integrity != NULL ? ip.integrity : "none");
pr_verbose("Integrity key size: %u", ip.integrity_key_size);
}
rc = check_keysize_and_cipher_mode(NULL,
vkeysize - ip.integrity_key_size);
if (rc != 0)
return rc;
@@ -1460,6 +1480,8 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
vkey = NULL;
if (keysize != NULL)
*keysize = vkeysize;
if (integrity_keysize != NULL)
*integrity_keysize = ip.integrity_key_size;
if (password != NULL)
*password = pw;
else
@@ -1482,24 +1504,31 @@ out:
* Validate and get a secure key from a key slot. Optionally returns the key
* and password used to unlock the keyslot. You can either validate a specific
* key slot, or let it choose based on the password (keyslot=CRYPT_ANY_SLOT).
* If integrity support is enabled for the LUKS2 device, the returned key may
* include the integrity key concatenated to the secure key (dependent on the
* integrity type). The returned keysize is the full size of both, and the
* returned integrity_keysize is the size of the integrity key only (which may
* be 0). The size of the secure key is keysize minus integrity_keysize.
*/
static int validate_keyslot(int keyslot, char **key, size_t *keysize,
size_t *intgrity_keysize,
char **password, size_t *password_len,
int *is_old_mk, size_t *clear_keysize,
const char *prompt, const char *invalid_msg)
{
size_t vkeysize = 0;
size_t vkeysize = 0, ikeysize = 0;
char *vkey = NULL;
int rc, is_old;
rc = open_keyslot(keyslot, &vkey, &vkeysize, password, password_len,
prompt);
rc = open_keyslot(keyslot, &vkey, &vkeysize, &ikeysize,
password, password_len, prompt);
if (rc < 0)
return rc;
keyslot = rc;
rc = validate_secure_key(g.pkey_fd, (u8 *)vkey, vkeysize, clear_keysize,
rc = validate_secure_key(g.pkey_fd, (u8 *)vkey,
vkeysize - ikeysize, clear_keysize,
&is_old, NULL, g.verbose);
if (rc != 0) {
if (invalid_msg != NULL)
@@ -1520,6 +1549,8 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
vkey = NULL;
if (keysize != NULL)
*keysize = vkeysize;
if (intgrity_keysize != NULL)
*intgrity_keysize = ikeysize;
if (is_old_mk != NULL)
*is_old_mk = is_old;
@@ -1539,8 +1570,10 @@ out:
static int reencipher_prepare(int token)
{
struct reencipher_token reenc_tok;
size_t integrity_keysize = 0;
struct vp_token vp_tok;
char *password = NULL;
size_t securekeysize;
size_t password_len;
char *key = NULL;
size_t keysize;
@@ -1569,8 +1602,9 @@ static int reencipher_prepare(int token)
}
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password,
&password_len, &is_old_mk, NULL, prompt, NULL);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
&integrity_keysize, &password, &password_len,
&is_old_mk, NULL, prompt, NULL);
free(prompt);
if (rc < 0)
goto out;
@@ -1581,7 +1615,9 @@ static int reencipher_prepare(int token)
if (rc != 0)
goto out;
rc = generate_key_verification_pattern((u8 *)key, keysize,
securekeysize = keysize - integrity_keysize;
rc = generate_key_verification_pattern((u8 *)key, securekeysize,
reenc_tok.verification_pattern,
sizeof(reenc_tok.verification_pattern),
g.verbose);
@@ -1624,7 +1660,7 @@ static int reencipher_prepare(int token)
}
if (g.fromold) {
rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize,
rc = reencipher_secure_key(&g.lib, (u8 *)key, securekeysize,
NULL, REENCIPHER_OLD_TO_CURRENT,
&selected, g.verbose);
if (rc != 0) {
@@ -1637,7 +1673,7 @@ static int reencipher_prepare(int token)
warnx("Failed to re-encipher the secure volume "
"key for device '%s'\n", g.pos_arg);
if (!selected &&
!is_ep11_aes_key((u8 *)key, keysize))
!is_ep11_aes_key((u8 *)key, securekeysize))
print_msg_for_cca_envvars(
"secure AES volume key");
rc = -EINVAL;
@@ -1647,7 +1683,7 @@ static int reencipher_prepare(int token)
}
if (g.tonew) {
rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize,
rc = reencipher_secure_key(&g.lib, (u8 *)key, securekeysize,
NULL, REENCIPHER_CURRENT_TO_NEW,
&selected, g.verbose);
if (rc != 0) {
@@ -1660,7 +1696,7 @@ static int reencipher_prepare(int token)
warnx("Failed to re-encipher the secure volume "
"key for device '%s'\n", g.pos_arg);
if (!selected &&
!is_ep11_aes_key((u8 *)key, keysize))
!is_ep11_aes_key((u8 *)key, securekeysize))
print_msg_for_cca_envvars(
"secure AES volume key");
rc = -EINVAL;
@@ -1727,8 +1763,10 @@ out:
static int reencipher_complete(int token)
{
char vp[VERIFICATION_PATTERN_LEN];
size_t integrity_keysize = 0;
struct reencipher_token tok;
char *password = NULL;
size_t securekeysize;
size_t password_len;
char *key = NULL;
size_t keysize;
@@ -1751,8 +1789,10 @@ static int reencipher_complete(int token)
g.pos_arg);
util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ",
tok.original_keyslot, g.pos_arg);
rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize, &password,
&password_len, &is_old_mk, NULL, prompt, msg);
rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize,
&integrity_keysize, &password, &password_len,
&is_old_mk, NULL,
prompt, msg);
free(msg);
free(prompt);
if (rc < 0)
@@ -1762,6 +1802,8 @@ static int reencipher_complete(int token)
if (rc != 0)
goto out;
securekeysize = keysize - integrity_keysize;
if (is_old_mk) {
util_asprintf(&msg, "The re-enciphered secure volume key "
"of device '%s' is enciphered with the "
@@ -1781,7 +1823,7 @@ static int reencipher_complete(int token)
goto out;
}
rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize,
rc = reencipher_secure_key(&g.lib, (u8 *)key, securekeysize,
NULL, REENCIPHER_OLD_TO_CURRENT,
&selected, g.verbose);
if (rc != 0) {
@@ -1794,7 +1836,7 @@ static int reencipher_complete(int token)
warnx("Failed to re-encipher the secure volume "
"key for device '%s'\n", g.pos_arg);
if (!selected &&
!is_ep11_aes_key((u8 *)key, keysize))
!is_ep11_aes_key((u8 *)key, securekeysize))
print_msg_for_cca_envvars(
"secure AES volume key");
rc = -EINVAL;
@@ -1823,7 +1865,7 @@ static int reencipher_complete(int token)
}
rc = generate_key_verification_pattern((u8 *)key, keysize, vp,
rc = generate_key_verification_pattern((u8 *)key, securekeysize, vp,
sizeof(vp), g.verbose);
if (rc != 0) {
warnx("Failed to generate the verification pattern: %s",
@@ -1920,11 +1962,13 @@ static int command_validate(void)
{
int reenc_pending = 0, vp_tok_avail = 0, is_valid = 0, is_old_mk = 0;
struct reencipher_token reenc_tok;
size_t integrity_keysize = 0;
struct vp_token vp_tok;
const char *key_type;
u8 mkvp[MKVP_LENGTH];
size_t clear_keysize;
size_t keysize = 0;
size_t seckeysize;
char *key = NULL;
char *prompt;
char *msg;
@@ -1932,7 +1976,8 @@ static int command_validate(void)
int rc;
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL, prompt);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &integrity_keysize,
NULL, NULL, prompt);
free(prompt);
if (rc < 0)
goto out;
@@ -1941,8 +1986,10 @@ static int command_validate(void)
if (rc != 0)
goto out;
rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize,
&is_old_mk, NULL, g.verbose);
seckeysize = keysize - integrity_keysize;
rc = validate_secure_key(g.pkey_fd, (u8 *)key, seckeysize,
&clear_keysize, &is_old_mk, NULL, g.verbose);
is_valid = (rc == 0);
token = find_token(g.cd, PAES_REENC_TOKEN_NAME);
@@ -1959,7 +2006,7 @@ static int command_validate(void)
vp_tok_avail = 1;
}
rc = get_master_key_verification_pattern((u8 *)key, keysize,
rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
mkvp, g.verbose);
if (rc != 0) {
warnx("Failed to get the master key verification pattern: %s",
@@ -1967,13 +2014,13 @@ static int command_validate(void)
goto out;
}
key_type = get_key_type((u8 *)key, keysize);
key_type = get_key_type((u8 *)key, seckeysize);
printf("Validation of secure volume key of device '%s':\n", g.pos_arg);
printf(" Status: %s\n", is_valid ? "Valid" : "Invalid");
printf(" Secure key size: %lu bytes\n", keysize);
printf(" Secure key size: %lu bytes\n", seckeysize);
printf(" XTS type key: %s\n",
is_xts_key((u8 *)key, keysize) ? "Yes" : "No");
is_xts_key((u8 *)key, seckeysize) ? "Yes" : "No");
printf(" Key type: %s\n", key_type);
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
@@ -2033,6 +2080,7 @@ out:
*/
static int command_setvp(void)
{
size_t integrity_keysize = 0;
struct vp_token vp_tok;
size_t keysize = 0;
char *key = NULL;
@@ -2041,8 +2089,9 @@ static int command_setvp(void)
int rc;
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL,
NULL, NULL, prompt, NULL);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
&integrity_keysize, NULL, NULL, NULL, NULL,
prompt, NULL);
free(prompt);
if (rc < 0)
goto out;
@@ -2053,7 +2102,8 @@ static int command_setvp(void)
token = find_token(g.cd, PAES_VP_TOKEN_NAME);
rc = generate_key_verification_pattern((const u8 *)key, keysize,
rc = generate_key_verification_pattern((const u8 *)key,
keysize - integrity_keysize,
vp_tok.verification_pattern,
sizeof(vp_tok.verification_pattern),
g.verbose);
@@ -2084,7 +2134,9 @@ out:
*/
static int command_setkey(void)
{
struct crypt_params_integrity ip = { 0 };
char vp[VERIFICATION_PATTERN_LEN];
size_t integrity_keysize = 0;
size_t password_len = 0;
struct vp_token vp_tok;
size_t newkey_size = 0;
@@ -2108,11 +2160,17 @@ static int command_setkey(void)
if (newkey == NULL)
return EXIT_FAILURE;
rc = check_keysize_and_cipher_mode(newkey, newkey_size);
rc = crypt_get_integrity_info(g.cd, &ip);
if (rc == 0)
integrity_keysize = ip.integrity_key_size;
rc = check_keysize_and_cipher_mode(newkey,
newkey_size - integrity_keysize);
if (rc != 0)
goto out;
rc = validate_secure_key(g.pkey_fd, newkey, newkey_size, NULL,
rc = validate_secure_key(g.pkey_fd, newkey,
newkey_size - integrity_keysize, NULL,
&is_old_mk, NULL, g.verbose);
if (rc != 0) {
warnx("The secure key in file '%s' is not valid",
@@ -2137,21 +2195,33 @@ static int command_setkey(void)
}
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password,
&password_len, prompt);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &integrity_keysize,
&password, &password_len, prompt);
free(prompt);
if (rc < 0)
goto out;
if (keysize == newkey_size && memcmp(newkey, key, keysize) == 0) {
if (keysize - integrity_keysize == newkey_size - integrity_keysize &&
memcmp(newkey, key, keysize - integrity_keysize) == 0) {
warnx("The secure key in file '%s' is equal to the current "
"volume key, setkey is ignored", g.master_key_file);
rc = 0;
goto out;
}
if (integrity_keysize > 0 &&
memcmp(newkey + newkey_size - integrity_keysize,
key + keysize - integrity_keysize, integrity_keysize) != 0) {
warnx("The secure key in file '%s' contains a different "
"integrity key (i.e. the last %lu bytes of the key) than "
"the current volume key.", g.master_key_file,
integrity_keysize);
rc = -EINVAL;
goto out;
}
rc = generate_key_verification_pattern(newkey, newkey_size, vp,
sizeof(vp), g.verbose);
rc = generate_key_verification_pattern(newkey,
newkey_size - integrity_keysize,
vp, sizeof(vp), g.verbose);
if (rc != 0) {
warnx("Failed to generate the verification pattern: %s",
strerror(-rc));